Skip to content

Plugin manifest reference

The manifest is the entire external contract of a plugin. The backend fetches it with GET / when the plugin is registered and whenever an administrator refreshes it.

In TypeScript the shape is Plugin from @mdeo/plugin. A service builds a ServicePluginDefinition — the same thing without url and default, which the backend fills in — and @mdeo/service-common serialises it.

Plugin

json
{
  "id": "metamodel-service",
  "name": "Metamodel",
  "description": "Language support for metamodel definitions (.mm files)",
  "icon": [["path", { "d": "M12 2v4" }]],
  "languagePlugins": [ /* … */ ],
  "contributionPlugins": [ /* … */ ]
}
FieldTypeRequiredMeaning
idstringUnique id of the plugin
urlstringFilled in by the backend from the registered URL
namestringDisplay name in the workbench
descriptionstringShown in the plugin list
iconicon nodeA Lucide icon, converted with convertIcon
defaultbooleanSet by an administrator; default plugins are added to new projects
languagePluginsLanguagePlugin[]Languages this plugin provides
contributionPluginsLanguageContributionPlugin[]Extensions to other plugins' languages

Icons

An icon is a serialised Lucide icon — an array of [tag, attributes] pairs. Use convertIcon from @mdeo/language-common on a Lucide import, or hand-write the array when you need custom shapes:

ts
import { Network } from "lucide";
import { convertIcon } from "@mdeo/language-common";

const icon = convertIcon(Network);

LanguagePlugin

json
{
  "id": "metamodel",
  "name": "Metamodel",
  "extension": ".mm",
  "newFileAction": false,
  "isGenerated": false,
  "documentationUrl": "https://mde-optimiser.github.io/mdeo-cloud/plugins/metamodel",
  "icon": [["path", { "d": "M12 2v4" }]],
  "serverPlugin": { "import": "static/language.js" },
  "graphicalEditorPlugin": {
    "import": "static/editor.js",
    "stylesUrl": "static/styles.css",
    "stylesCls": "editor-metamodel"
  },
  "textualEditorPlugin": {
    "languageConfiguration": { },
    "monarchTokensProvider": { }
  }
}
FieldTypeRequiredMeaning
idstringLanguage id, unique across all enabled plugins
namestringDisplay name
extensionstringFile extension including the dot. Omit for generated languages that have no files of their own
newFileActionbooleanShow a dialog when a file of this language is created — used by languages that must know their metamodel up front
serverPlugin.importstringPath to the ES module exporting the LangiumLanguagePluginProvider
graphicalEditorPluginobjectOmit for languages without a diagram editor
textualEditorPluginobjectOmit for languages without a text editor
iconicon nodeIcon shown for files of this language
isGeneratedbooleanMarks a language whose files the platform produces rather than the user
documentationUrlstringDocumentation for the language. The workbench shows a question mark next to the editor title actions which opens it in a new tab; omit it and no such button appears

Relative paths and versioning

Write asset paths relative, as "language.js". buildManifest rewrites them to static/<version>/language.js when SERVICE_VERSION is set, and to static/language.js otherwise. The backend then resolves them against the plugin's URL.

This is why plugins must be refreshed after an upgrade: a stored manifest points at the old version segment.

graphicalEditorPlugin

FieldMeaning
importES module whose default export is a GLSP ContainerConfiguration
stylesUrlStylesheet loaded when the editor opens
stylesClsCSS class applied to the editor container, so styles can be scoped

textualEditorPlugin

FieldMeaning
languageConfigurationMonaco LanguageConfiguration: brackets, comments, auto-closing pairs
monarchTokensProviderMonaco Monarch tokenizer, serialised

Monarch tokenizers contain RegExp objects, which do not survive JSON. Serialise with serializeMonarchTokensProvider before putting one in a manifest; the workbench calls deserializeMonarchTokensProvider on the way back.

The usual case is to take the shared defaults and only replace the keyword list:

ts
textualEditorPlugin: {
    languageConfiguration: defaultLanguageConfiguration,
    monarchTokensProvider: serializeMonarchTokensProvider({
        ...defaultMonarchTokenProvider,
        keywords: ["class", "extends", "abstract", "import", "from", "as", "enum"]
    })
}

LanguageContributionPlugin

json
{
  "languageId": "config",
  "description": "Provides optimization section support for config language",
  "additionalKeywords": ["problem", "goal", "metamodel", "model"],
  "serverContributionPlugins": [ /* language specific payload */ ]
}
FieldTypeMeaning
languageIdstringThe id of the language being extended
descriptionstringShown in the plugin details view
additionalKeywordsstring[]Keywords this contribution introduces, so the target language's syntax highlighting can pick them up
serverContributionPluginsobject[]The payload, interpreted by the target language

additionalKeywords is purely presentational — it feeds the Monarch tokenizer of the target language. The grammar itself comes from the payload.

ServerContributionPlugin

The base type has one field, id. Everything else is defined by the language being extended, which type-guards on a type discriminator:

TargettypePayload type
configconfig-language-contributionConfigContributionPlugin
scriptscript-language-contributionScriptContributionPlugin

Because the payload is plain JSON it travels through the backend without either side knowing the other. A contribution to a language your plugin has never heard of is simply passed along.

Complete example

The Config MDEO plugin's manifest, abridged:

json
{
  "id": "config-mdeo-service",
  "name": "Config MDEO",
  "description": "Language support for config MDEO sections (search and solver)",
  "icon": [["path", { "d": "…" }]],
  "languagePlugins": [
    {
      "id": "config-mdeo",
      "name": "Config MDEO",
      "newFileAction": false,
      "isGenerated": true,
      "serverPlugin": { "import": "static/language.js" },
      "graphicalEditorPlugin": null,
      "textualEditorPlugin": null,
      "icon": [["path", { "d": "…" }]]
    }
  ],
  "contributionPlugins": [
    {
      "languageId": "config",
      "description": "Provides search and solver section support for config language",
      "additionalKeywords": ["search", "solver", "mutations", "algorithm", "…"],
      "serverContributionPlugins": [
        {
          "id": "config-mdeo",
          "type": "config-language-contribution",
          "name": "mdeo",
          "languageKey": "config-mdeo",
          "grammar": { "rules": [], "interfaces": [], "types": [] },
          "sections": [
            { "name": "search",  "ruleName": "SearchSectionContentRule",  "interfaceName": "SearchSection",  "executable": false },
            { "name": "solver",  "ruleName": "SolverSectionContentRule",  "interfaceName": "SolverSection",  "executable": true },
            { "name": "runtime", "ruleName": "RuntimeSectionContentRule", "interfaceName": "RuntimeSection", "executable": false }
          ],
          "dependencies": ["config-optimization"],
          "exportedTypes": [],
          "sectionDependencies": [{ "pluginName": "optimization", "sectionName": "problem" }]
        }
      ]
    }
  ]
}

Released under the terms of the repository licence.