Three channels
Let the Agent call your Mini App: managed MCP
Each MCP server uses exactly one lifecycle:- Ordinary plugin MCP: runs independently (for example over stdio) without the Mini App’s Node service. Don’t list it in
mcpEndpoints. - Mini App-managed MCP: implemented by the Mini App’s Node service and available only while it runs. Use it when tools should share business state with the page.
*.mcp.json, using this fixed placeholder url that the host rewrites after publishing:servers.mcp.json
miniapp.json, and reference servers.mcp.json from mcpServers in plugin.json:miniapp/miniapp.json
- Before
startreturns, the Node entry must implement Streamable HTTP MCP at that path oncontext.listen. The host rewrites only this server’s address; tool discovery, ordering, calls, and errors stay with the plugin MCP runtime. - When the user sends a message that references the Mini App, the host starts the service, connects MCP, and confirms the tools are available before calling the model. If a required managed MCP is unavailable, the request fails instead of letting the model continue without its tools.
- Keep
tools/listlight: return tool definitions only, without importing data, scanning documents, or calling remote services. Put business initialization in the tool calls that need it. - A broken MCP handler doesn’t affect the page, but the Agent can’t use those tools until you fix it and publish again.
Hand off to the message input
Use this when you prepare context and let the user decide whether to send it; see message.append. If the Agent should change tables, cards, or other business objects, give it MCP tools and use stable business IDs in requests and tools. Users can edit or delete the text in the input, so it can’t serve as a correlation ID between a request and a turn.Agent API
A Mini App can start Agent turns directly from the page (window.mcode.miniapp.agent) or from Node (context.agent). The Agent keeps its full capabilities: files, shell, Skills, and MCP, including this Mini App’s own MCP.Enable it
miniapp/miniapp.json
- Without
agentAccess, access is denied. The only valid value is"session.run"; any other value is rejected at publish time withMINIAPP_AGENT_ACCESS_INVALID. agentConcurrencyis optional, from 1 to 32, default 4: how many turns this Mini App runs at once. Extra calls wait in order, in a queue up to four times the concurrency. Each session runs one turn at a time.- Grants take effect with the process: changing
agentAccessand publishing again restarts the service.
Who owns a session
These sessions don’t appear in the user’s conversation list. Each Mini App can continue only sessions it created, and a continued session keeps its agent and directory.
run and answer can pass model to switch models; it must be a model the user can see in models(). See Mini Apps and sessions for how these sessions relate to chats.Methods
Approvals and questionnaires
The host lets an approval wait only when someone can answer it; otherwise it is denied right away.
permission.requested→reply: a tool call waits for approval. Only “allow once” and “deny” exist; there is no “always allow”, because a Mini App never writes the user’s global permission rules.question.requested→answer: the turn paused on a questionnaire (ask_user, a feature enable, or a Plan review). Callanswerafter the pausedrunreturns.- An approval waits only while someone can answer it: the call passed
onEvent, or a page has the session open in the sidebar throughopen. Otherwise it is denied, and the model is told that nobody can approve it and to continue without the tool. - There is no timeout. A waiting call is released by cancellation, process exit, or republishing.
- When the Agent delegates to a child session, the child’s approvals also arrive through
onEvent, with the child’s ownsessionId. Don’t filter events by the root turn, or the call waits forever.
Page example
Node example
context.agent from a request handler, never in start. A service restart cancels turns in flight.Events
Ignore event types you don’t recognize; more may be added.
Design guidance
- Put the task, data scope, required interaction, success criteria, and stop conditions in
text. Page labels, form state, and the host chat’s context aren’t carried over automatically. denyrejects only that one tool request; it doesn’t put the file off-limits from then on. Enforce boundaries that must hold across calls with host permissions or Hook rules.- A root turn’s
finishedstatus doesn’t mean background child tasks are done. If you need a child’s result, use foreground delegation in the business request. AGENT_OUTCOME_UNKNOWNmeans the turn may already have run. Reconcile the session state first; don’t retry automatically.