Verified against MiniMax Code 3.1.6.
A Mini App is a MiniMax Plugin whose package.json declares a Mini App payload. MiniMax Code
enforces the manifest, payload, and path rules below when the package is installed. npm run check
enforces the same rules here, plus this repository's own requirements: README.md and LICENSE,
a real image file behind icon, lifecycle limited to on-demand, no hard links, no committed
node_modules, routes outside the Host-reserved /__mavis/ prefix, the Node entry conventions in
docs/runtime.md, a stated minimum client version for newer features (see
Minimum client version), and code that only calls what the manifest
declares (see Declared access must match the code).
<plugin-id>/
.minimax-plugin/plugin.json Plugin manifest: identity, icon, category, declared capabilities
package.json Mini App declaration (see below)
icon.png Plugin icon (PNG, JPEG, or WebP)
miniapp/
miniapp.json Payload roots, Node entry, page route
client/ Runtime payload: files served to the page
node/ Runtime payload: the Node entry and what it imports
README.md Required by this repository
LICENSE Required by this repository
README.zh-CN.md Optional
tests/ Optional; keep outside miniapp/
skills/<name>/SKILL.md Optional plugin capability
agents/<name>.md Optional plugin capability (MiniMax Code 3.1.2 or later)
*.mcp.json Optional plugin capability
bindings/<name>.binding.json Optional plugin capability
The directory name, plugin.json.name, and the installed plugin ID are the same string. It must be
unique across this repository because the author directory is dropped at install time.
Git does not keep empty directories. Every path listed under artifacts must contain at least one
committed file.
| Field | Rule |
|---|---|
$schema |
Optional string. |
schemaVersion |
The number 1. |
name |
At most 80 characters, matches ^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$, equals the directory name. |
displayName |
Optional non-empty string. |
version |
SemVer 2.0, at most 128 characters. |
description, author |
Non-empty strings. |
icon |
Relative path with a lowercase .png, .jpg, .jpeg, or .webp extension; the file must exist and be a real image. |
darkIcon |
Optional; same rules as icon. |
category |
One of Office, Studio, Design & Sites, Code, Business, Sales, Productivity, Science & Healthcare, Education, Other. |
exampleQueries |
Array of non-empty strings. Provide at least one; the Agent uses them to open the Mini App by name. |
apps |
Array of *.app.json paths. Locally installed packages ignore this field; use []. |
mcpServers |
Array of *.mcp.json paths; each file must exist. These are MCP servers the plugin offers to the Agent, distinct from mcpEndpoints in miniapp.json. |
skills |
Array of skills/<name>/SKILL.md paths; each file must exist. |
hooks |
Optional array of *.json paths; each file must exist. Contents are validated by MiniMax Code at install time. |
hostBindings |
Optional array of bindings/<name>.binding.json paths; each file must exist. Contents are validated at install time. |
agents |
Optional array of agents/<name>.md paths (<name> matches ^[a-z][a-z0-9_-]*$); each file must exist. Subagents the plugin adds; contents are validated at install time. Requires MiniMax Code 3.1.2 or later. |
hostCapabilities |
Rejected. Host capabilities come only from Host Bindings. |
| Any other field | Rejected. MiniMax Code 3.1.2 and later ignore unknown fields, but earlier clients reject the whole package. |
Use [] for apps, mcpServers, and skills when the package has none.
The mcode field must be exactly:
{
"mcode": {
"schemaVersion": 2,
"miniApp": "./miniapp/miniapp.json"
}
}No other keys are allowed inside mcode. Other top-level keys (name, type, scripts) are fine.
{
"schemaVersion": 1,
"artifacts": {
"client": ["./miniapp/client"],
"node": ["./miniapp/node"]
},
"runtime": {
"kind": "process",
"entry": "./miniapp/node/server.mjs",
"lifecycle": "on-demand"
},
"surface": { "path": "/dashboard" },
"mcpEndpoints": []
}schemaVersionis1. Unknown top-level fields are rejected: MiniMax Code 3.1.5 and later reportMINIAPP_MANIFEST_FIELD_UNSUPPORTEDnaming the field, which on an installed client means the client is older than the package; earlier clients reportMINIAPP_MANIFEST_INVALID.artifacts.clientandartifacts.nodeare non-empty arrays of unique paths underminiapp/. Each path must exist. These are the runtime payload roots: exactly what MiniMax Code hashes and installs. Anynode_modulesdirectory is excluded from payloads.runtime.kindisprocess.runtime.entryends in.js,.mjs, or.cjs, exists, and lies inside one ofartifacts.node. Use.mjs: this repository checks the entry for an ESMstartexport (seedocs/runtime.md).runtime.lifecycleison-demandor omitted.surface.pathis the route the Node entry serves the page on. It is relative to the Host and must not contain an origin, query, fragment, or backslash. A missing leading/is added.surface.windowis optional; seesurface.window.mcpEndpointsis an array of{ "server": string, "path": string }.servermatches^[a-zA-Z0-9_-]{1,128}$and must name a server declared throughplugin.json.mcpServers; MiniMax Code checks that reference at install time.serverandpathare each unique. Use[]when there are none.- No route (
surface.pathor an MCP endpointpath) may be/__mavisor start with/__mavis/. The Host owns that prefix and answers some of it itself. hostConnectorAccessis optional:{ "providers": string[] }, each matching^[a-z0-9_-]{1,64}$and unique. Declared providers are granted by the Host at install time; this repository does not document their use.agentAccessis optional: an array of"session.run"(run Agent turns) and"session.read"(list, read, and observe the user's own conversations). Neither implies the other; declare both when the Mini App needs both. Absent or[]means no Agent access. Seedocs/agent.mdbefore declaring it. Requires MiniMax Code 3.1.2 or later;"session.read"requires 3.1.5 or later.agentConcurrencyis optional: an integer from 1 to 32, default 4. It caps how many Agent turns of this Mini App run at once; it has no effect withoutagentAccess. Requires MiniMax Code 3.1.2 or later.toolAccessis optional. The only allowed value is["call"], which gives the Node entrycontext.toolto call the MCP and Connector tools installed on the user's MiniMax Code, each call judged by the user's permission rules. Seedocs/tool.md. Requires MiniMax Code 3.1.5 or later.deviceAccessis optional. The only allowed value is["microphone"], which lets the page callgetUserMedia({ audio })andwindow.mcode.miniapp.speech.transcribe(seedocs/client.md). Only audio-only requests from the page's main frame are granted: a request that also asks for video, or comes from an iframe, is still denied. Requires MiniMax Code 3.1.5 or later.enginesis optional:{ "mcode": ">=MAJOR.MINOR.PATCH" }, nothing else. A client at 3.1.5 or later but older than the bound keeps the plugin with its Skills and MCP servers, and withholds the Mini App and itsmcpEndpoints; publishing such a package from a workspace fails withMINIAPP_HOST_TOO_OLD. Clients before 3.1.5 do not know the field and reject the whole package, so the bound only matters above 3.1.5. Write it only when the package needs a specific newer client; it is not required for the features in the table below. Requires MiniMax Code 3.1.5 or later.
From MiniMax Code 3.1.5 the user can switch off, item by item in the plugin details, what the
manifest declares: each hostConnectorAccess provider, each agentAccess value, toolAccess
"call", and deviceAccess "microphone". Everything starts on, with no first-use prompt. A switch
applies at once and does not restart the Mini App: context.agent and context.tool stay present,
and their calls fail until the user switches the item back on:
| Switched off | What fails |
|---|---|
session.run |
Running turns end as aborted; run, reply, answer, cancel, open, models, agents, create fail with NOT_PERMITTED (not_permitted on the page). |
session.read |
Observers end; sessions(), observe({ all: true }), and reading or observing the user's sessions fail the same way. |
tool:call |
context.tool.call fails with TOOL_NOT_PERMITTED; list() reports every tool as deny. |
microphone |
The next getUserMedia fails with NotAllowedError and speech.transcribe with not_permitted; a recording already open keeps its stream until the page stops it. |
Handle these errors where the call is made and explain in the page what the user can turn back on.
surface.window flags are not switches.
Optional. It declares the window MiniMax Code opens when the user pops the page out of the side panel into its own window. Requires MiniMax Code 3.1.2 or later.
{
"surface": {
"path": "/dashboard",
"window": {
"width": 960,
"height": 700,
"minWidth": 480,
"minHeight": 360,
"resizable": true,
"frame": true,
"transparent": false,
"alwaysOnTop": false,
"skipTaskbar": false,
"clickThrough": false
}
}
}| Field | Rule | Default when omitted |
|---|---|---|
width, height |
Integer from 200 to 10000. | The page's last size in the side panel. |
minWidth, minHeight |
Integer from 200 to 10000; not larger than width / height when both are given. |
480 / 360 |
resizable |
Boolean. | true |
frame |
Boolean. | true |
transparent |
Boolean. true requires frame: false. |
false |
alwaysOnTop |
Boolean. Also the ceiling for turning it on from the page at runtime. | false |
skipTaskbar |
Boolean. | false |
clickThrough |
Boolean. Mouse events pass through to whatever is underneath; the page still receives mouse moves. Also the ceiling for turning it on from the page at runtime. | false |
| Any other field | Rejected. Title, position, parent window, and close behaviour are always set by the Host. |
The window never opens at launch: it opens when the user pops the page out, or when the page
switches to it from a user gesture through window.mcode.miniapp.window (see docs/client.md).
Closing it, or Cmd/Ctrl+W, returns the page to the side panel.
Clients older than the version below reject the whole package when it declares the feature, so the package cannot be installed there at all.
| Feature | Declared in | Minimum MiniMax Code |
|---|---|---|
agents |
plugin.json |
3.1.2 |
agentAccess |
miniapp/miniapp.json |
3.1.2 |
agentAccess with "session.read" |
miniapp/miniapp.json |
3.1.5 |
agentConcurrency |
miniapp/miniapp.json |
3.1.2 |
deviceAccess |
miniapp/miniapp.json |
3.1.5 |
engines |
miniapp/miniapp.json |
3.1.5, or its own bound when higher |
surface.window |
miniapp/miniapp.json |
3.1.2 |
toolAccess |
miniapp/miniapp.json |
3.1.5 |
A 3.1.4 preview build accepted "session.read", but the released 3.1.4 rejects it.
The package's minimum is the highest entry it declares. When it has one, name a MiniMax Code
version at or above it under ## Tested environment in its README, for example
- MiniMax Code 3.1.5 on macOS 15. npm run check warns until it finds one there.
To use a newer page or Node API only where it exists, without raising the minimum, feature-detect
it: capabilities?.has('agent.observe') (MiniMax Code 3.1.5 and later), or test that the method
exists. A manifest field cannot be feature-detected: declaring it is what raises the minimum.
npm run check scans the files that run in the page or in Node (.js, .mjs, .cjs, .html,
.htm under artifacts.client and artifacts.node, outside node_modules) for calls that need a
grant the manifest does not make, and reports them as ACCESS_UNDECLARED.
| Call | Needs | Level |
|---|---|---|
context.agent / page agent run, answer, reply, cancel, open, models, agents, create |
agentAccess "session.run" |
error |
agent.sessions(), agent.observe({ all: true }), page agent.session() without an id |
agentAccess "session.read" |
error |
agent.session(id), agent.observe({ sessionId }) on the Mini App's own sessions |
either agentAccess value |
error |
context.tool.*, page tool.request() |
toolAccess: ["call"] |
error |
speech.transcribe() |
deviceAccess: ["microphone"] |
error |
getUserMedia() |
deviceAccess: ["microphone"] |
error when the package's minimum is 3.1.5 or later; warning otherwise |
The Agent rows are reported once per file, naming every missing value. The first five rows never
work without the grant, on any client. getUserMedia worked without a grant
before MiniMax Code 3.1.5, and 3.1.5 and later deny it with NotAllowedError. A package whose
minimum is below 3.1.5 still installs on those earlier clients, so the check only warns; declaring
deviceAccess fixes the call and raises the minimum to 3.1.5. Camera, geolocation, notifications,
and every other permission a browser would prompt for are denied to Mini App pages on 3.1.5 and
later and cannot be declared yet.
The scan matches source text, so it misses calls through a renamed variable and can flag dormant code, such as an engine's audio input that the app never turns on. Explain a warning in the pull request instead of declaring a permission the app does not use.
Every path inside the package, and every path written in a manifest, must be portable:
- ASCII only; each segment matches
[A-Za-z0-9._-]+and does not end with.. - No
.or..segments, no backslashes, no leading or trailing/, no control characters. - Each segment at most 128 bytes; the whole path at most 512 bytes and at most 16 segments.
- A segment's part before its first
.must not be a Windows reserved name (con,prn,aux,nul,com1–com9,lpt1–lpt9). - No symbolic links or hard links anywhere in the package, and the package directory itself is not a symbolic link.
At most 1024 files, 16 MiB per file, and 64 MiB in total.
Only artifacts.client and artifacts.node become the runtime payload. Keep tests, docs, and
source-only files outside miniapp/. Vendor any third-party runtime code inside a payload root;
node_modules is never committed to this repository.