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
idstring✅Unique id of the plugin
urlstring—Filled in by the backend from the registered URL
namestring✅Display name in the workbench
descriptionstring✅Shown in the plugin list
iconicon node✅A Lucide icon, converted with convertIcon
defaultboolean—Set 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
idstring✅Language id, unique across all enabled plugins
namestring✅Display name
extensionstring—File extension including the dot. Omit for generated languages that have no files of their own
newFileActionboolean—Show a dialog when a file of this language is created — used by languages that must know their metamodel up front
serverPlugin.importstring✅Path to the ES module exporting the LangiumLanguagePluginProvider
graphicalEditorPluginobject—Omit for languages without a diagram editor
textualEditorPluginobject—Omit for languages without a text editor
iconicon node✅Icon shown for files of this language
isGeneratedboolean✅Marks a language whose files the platform produces rather than the user
documentationUrlstring—Documentation 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.