Extension Manifest
Every extension package must include mantis.extension.json. The manifest tells Mantis what the extension is, what it contributes, and which permissions it needs.
Mantis validates the manifest during import and installation. Invalid packages are rejected before they can be installed.
Minimal manifest
{
"manifestVersion": 1,
"id": "demo.my-extension",
"name": "My Extension",
"version": "0.1.0",
"apiVersion": "1.0.0",
"main": "dist/extension.js",
"activationEvents": ["onPanel:main"],
"permissions": [],
"contributes": {
"panels": [
{
"id": "main",
"title": "My Panel",
"entry": "dist/panel.js"
}
]
}
}Top-level fields
| Field | Required | Description |
|---|---|---|
manifestVersion | No | Must be 1. Defaults to 1. |
apiVersion | No | Extension SDK version requested by this package. Defaults to 1.0.0. |
id | Yes | Stable extension id. Use a unique dotted id like publisher.extension-name. |
name | Yes | Human-readable extension name. |
version | Yes | Extension version string. Letters, numbers, ., _ and - only, because it becomes a directory name on the server. A semver build suffix such as 1.0.0+build.3 is rejected. |
description | No | Short explanation shown to users. |
publisher | No | Publisher or author name. |
homepage | No | HTTP or HTTPS URL. |
main | No | JavaScript file loaded by the background extension host. |
activationEvents | No | Events that start the extension host. |
permissions | No | List of Mantis permissions requested by the extension. |
contributes | No | Static contributions such as panels and commands. |
backend | No | Optional Python backend declaration. |
Id rules
id must start with a letter or number and can contain:
- letters
- numbers
._-
Good ids:
demo.sample-panel
kellis.pathway-tools
my_lab.inspectorAvoid changing ids after users install the extension. Mantis stores installed packages by id and version.
Permissions
Declare only what the extension needs.
| Permission | Allows |
|---|---|
maps:read | Reading map list, active map, map points, cluster metadata, and opening or focusing map panels. |
selection:read | Reading selection and bags. |
bags:write | Creating bags. |
panels:write | Opening and closing panels. |
commands:execute | Executing commands registered by extensions. The native command allowlist is currently empty, so no built-in Mantis command is reachable this way. |
backend:invoke | Calling the extension Python backend. |
If an extension calls an API without the matching permission, Mantis rejects the call.
selection:write is rejected at install
selection:write appears in the frontend permission enum and gates the selection.set() SDK method, but the server’s install validator does not list it as an allowed permission. Declaring it makes the whole package fail validation with “Unknown or unsupported extension permissions”, so it is not a partial loss of function: nothing installs at all. Omit it until the backend allowlist accepts it.
What the validator enforces
Beyond the field rules above, the import path applies hard limits to the package itself:
| Limit | Value |
|---|---|
| Package size | 25 MB |
| Single file size | 5 MB |
| File encoding | UTF-8 text only. Binary assets (images, fonts, wasm) are rejected. |
Non-empty contributes.menus and contributes.settings are also rejected. See Packaging and installation for the full validation list.
Extension host fields
Use main and activationEvents when the extension needs durable commands, background subscriptions, workspace state, or setup work that should not depend on an open panel.
{
"main": "dist/extension.js",
"activationEvents": [
"onStartup",
"onPanel:samplePanel",
"onCommand:demo.sample-panel.refresh"
]
}Supported activation events:
onStartuponPanel:<panelId>onCommand:<commandId>onMapsChangedonActiveMapChangedonSelectionChangedonBagsChanged*
The main file must be present in assets and should export activate(context). It may also export deactivate().
Panel contributions
Panels are listed under contributes.panels:
{
"contributes": {
"panels": [
{
"id": "samplePanel",
"title": "Sample Extension",
"entry": "dist/panel.js",
"scripts": ["dist/helpers.js"],
"styles": ["dist/styles.css"],
"description": "Shows data from the Mantis SDK."
}
]
}
}| Field | Required | Description |
|---|---|---|
id | Yes | Panel id inside this extension. |
title | Yes | Name shown in the Mantis UI. |
entry | Yes | JavaScript file loaded after the SDK bootstrap. |
scripts | No | Extra JavaScript files loaded before entry. |
styles | No | CSS files loaded with the panel. |
icon | No | Reserved for panel icon metadata. |
description | No | Human-readable panel description. |
All paths must be relative package paths. Absolute paths and .. are rejected.
Command contributions
Commands declare callable behavior. The extension host should register durable handlers for contributed commands:
{
"permissions": ["commands:execute"],
"contributes": {
"commands": [
{
"id": "demo.sample-panel.refresh",
"title": "Refresh Sample Panel"
}
]
}
}Command ids should be globally unique. The safest pattern is:
extension.id.commandNameCommands can also be registered dynamically if their id starts with the extension id followed by a dot.
For command-driven activation, add onCommand:<commandId> to activationEvents.
Backend declaration
Python backends are declared with backend:
{
"permissions": ["backend:invoke"],
"backend": {
"runtime": "python",
"entry": "backend/main.py",
"requirements": "humanize==4.10.0\n",
"network": false,
"actions": ["ping", "dependencyCheck"]
}
}| Field | Required | Description |
|---|---|---|
runtime | Yes | Must be python. |
entry | Yes | Python entry file path. |
actions | No | Allowed action names. If empty, any function/action may be invoked. |
requirements | No | Pip requirements text installed into an extension cache. |
network | No | Whether backend execution gets host networking. Defaults to false. |
Unsupported contribution points
These fields are recognized but currently rejected if non-empty:
contributes.menuscontributes.settings
They are reserved for future extension platform work.