Extensions Overview Beta
Mantis extensions are trusted packages that can add workspace UI, background behavior, commands, and optional Python-backed actions without changing the Mantis core app.
Looking for the browser add-on?
“Mantis Connection” is a separate product: a Chrome extension that turns web pages into Mantis spaces. It has nothing to do with the platform documented here. See Mantis Connection browser extension.
The extension system now has two browser-side layers:
- Extension host: a background Web Worker that loads an extension’s
mainfile, callsactivate(context), owns command registration, keeps subscriptions alive, and runs until the extension is disabled, uninstalled, reloaded, or the workspace changes. - Extension panels: sandboxed iframe UI surfaces contributed by the extension. Panels render the user interface and call Mantis through
window.mantis, but they are no longer the whole extension runtime.
Extensions may also include a Python backend. Backend code is stored separately from browser assets and runs through the Mantis API in a Docker container with timeouts and network disabled unless the manifest explicitly requests it.
Mantis notebooks can also act as a lightweight development environment for vertical panels. They are useful for quick prototyping: write Python vertical cells, add one UI/TSX cell, test the panel in place, then export it as a .mantisx extension package. See Notebook-Based Extension Development.
Package Shape
A typical extension package looks like this:
my-extension/
mantis.extension.json
dist/
extension.js
panel.js
styles.css
backend/
main.pydist/extension.js is the extension host entry. dist/panel.js is panel UI code. backend/main.py is optional Python backend code.
That layout is a convention, not a requirement. Every path in the manifest is just a relative package path, so you can lay the files out however your build tool prefers. The Yeoman generator emits a flatter tree (extension.js at the root, panels under panel/) along with a packaging script.
Manifest
The manifest declares identity, lifecycle, contributions, permissions, and optional backend behavior.
{
"manifestVersion": 1,
"apiVersion": "1.0.0",
"id": "demo.sample-panel",
"name": "Sample Panel Extension",
"version": "0.1.0",
"main": "dist/extension.js",
"activationEvents": [
"onStartup",
"onPanel:samplePanel",
"onCommand:demo.sample-panel.hello"
],
"permissions": ["maps:read", "selection:read", "commands:execute"],
"contributes": {
"commands": [
{ "id": "demo.sample-panel.hello", "title": "Sample Extension: Hello" }
],
"panels": [
{
"id": "samplePanel",
"title": "Sample Extension",
"entry": "dist/panel.js",
"styles": ["dist/styles.css"]
}
]
}
}Extension Host Lifecycle
If an extension declares main, Mantis can activate it in the background. The host entry should export activate(context) and may export deactivate().
exports.activate = async function activate(context) {
const count = await context.workspaceState.get('activationCount', 0);
await context.workspaceState.update('activationCount', count + 1);
context.subscriptions.push(
await mantis.commands.registerCommand('demo.sample-panel.hello', async () => ({
message: 'hello from the extension host',
activationCount: count + 1
}))
);
};
exports.deactivate = function deactivate() {};The context object includes:
extensionIdextensionUrimanifestapiVersionsubscriptionsworkspaceStateglobalState
Disposables pushed into context.subscriptions are cleaned up when the extension host is stopped.
Activation Events
Activation events decide when the extension host starts.
Supported events:
onStartuponPanel:<panelId>onCommand:<commandId>onMapsChangedonActiveMapChangedonSelectionChangedonBagsChanged*
For example, onCommand:demo.sample-panel.hello means Mantis activates the extension before trying to execute that command.
Panels
Panels are contributed through contributes.panels and appear in the Verticals menu. Opening a panel activates onPanel:<panelId> before rendering the iframe.
Panel code runs in a sandboxed iframe and receives window.mantis. It can call the SDK, invoke commands, subscribe to events, and call the extension backend if permissions allow it.
Panel messages use a private per-panel channel, so multiple extension panels can run independently without sharing a generic message path.
With panels:write, an extension host can also open a panel contributed by the same extension:
await mantis.panels.open('samplePanel');This is useful for background workflows, such as activating on a selection or bag event and surfacing the extension UI only when it becomes relevant.
Commands
Extensions can contribute commands in the manifest and register handlers in activate(context).
Command handlers now live in the extension host, not only inside an open panel. This means a command can be available even when the panel is closed, as long as the extension has been activated or can be activated through onCommand:<commandId>.
Panel code may call:
await window.mantis.commands.execute('demo.sample-panel.hello');If the command has an activation event, Mantis activates the host first, then invokes the registered handler.
Permissions
Extensions must declare permissions before using protected APIs.
Supported permissions:
maps:readselection:readbags:writepanels:writecommands:executebackend:invoke
maps:read covers map list, active map metadata, points, cluster metadata, and opening or focusing map panels. Permissions are enforced in the SDK bridge and the backend invocation path.
selection:write is not installable
The SDK defines a selection:write permission and a selection.set() method, but the server-side install validator does not accept the permission. A package that declares it is rejected outright, so the whole extension fails to install rather than losing one capability. Leave it out of your manifest. Extensions cannot write the point selection today.
commands:execute reaches commands registered by extensions. There is no native Mantis command an extension can call: the native allowlist is currently empty.
Python Backend
Extensions may declare a Python backend:
{
"backend": {
"runtime": "python",
"entry": "backend/main.py",
"requirements": "humanize==4.10.0\n",
"network": false,
"actions": ["ping", "dependencyCheck"]
}
}Browser code invokes backend actions through:
await window.mantis.backend.invoke('ping', { sentAt: new Date().toISOString() });Backend invocation requires backend:invoke. Declared actions are allowlisted. Python runs in a container with execution timeouts, dependency caching, audit logging, and no network unless network is true.
Trust Model
Only install extensions you trust. Extensions can run JavaScript in the browser extension host and panel iframe, access Mantis data covered by granted permissions, register commands, subscribe to workspace changes, store extension state, and invoke declared backend code.
The host and panel are isolated from each other structurally, but extensions are still trusted code. Treat extension installation like installing an IDE extension: review the publisher, source, requested permissions, backend settings, and network access before installing.
When To Build An Extension
Build an extension when you want reusable workspace functionality outside the Mantis core app. Good use cases include:
- a custom point inspector
- a lab-specific vertical panel
- a command-driven analysis workflow
- a panel that reacts to selection changes
- a Python-backed analysis tool
- a domain-specific assistant or data review surface