> ## Documentation Index
> Fetch the complete documentation index at: https://agent.minimax.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture overview

> What a Mini App is made of, how its parts talk to each other, and one full call from start to finish.

<div className="code-docs">
  <h2 id="scope">
    Scope
  </h2>

  * **Package format:** a regular MiniMax Plugin package plus a `miniapp.json` declaration; see [Package and manifests](/docs/miniapp/authors/package).
  * **Host runtime:** the part of the MiniMax Code desktop app that publishes, starts, authenticates, and cleans up Mini Apps; see [Running and updating](/docs/miniapp/host/lifecycle).
  * **Host APIs:** `start(context)`, `context.agent`, and `context.hostConnector` on the Node side, and `window.mcode.miniapp` on the page.
  * **Creator Skill:** /miniapp-creator, which guides the Agent through verifying data, writing, publishing, and checking an app; see [Build with the Agent](/docs/miniapp/authors/quickstart).

  <Note>
    Mini Apps define only the runtime environment and the host APIs. The plugin decides which tech stack the page uses and how business data is stored, and the host doesn't isolate the plugin at the operating-system level; see [Security model](/docs/miniapp/host/security).
  </Note>

  <h2 id="concepts">
    Core concepts
  </h2>

  <h3 id="participants">
    Participants
  </h3>

  * **The host:** the MiniMax Code desktop app. It validates and publishes the package, starts the Node process, assigns the port, issues the credential, shows the page in the session panel, and grants Agent and Connector capabilities as declared in the manifest.
  * **The Mini App page:** a web page running in the host's built-in browser, shown in a session panel tab or popped out into its own window. The page talks only to its own Node service.
  * **The Mini App Node service:** the plugin's backend. Each plugin has one process listening on a local port assigned by the host, and it owns business logic, data, and managed MCP tools. Even a single HTML page is served by it.
  * **The Agent:** the Agent in a session calls the Mini App's tools through the host MCP client, and the Mini App can start Agent sessions of its own through the Agent API.

  <Frame caption="The page, the Agent, and host services talk to the Mini App's Node process over a local port or IPC; the host manages credentials.">
    <img src="https://mintcdn.com/agent-en/mM6ovYl9sr4HdU6x/images/miniapp/architecture-en.svg?fit=max&auto=format&n=mM6ovYl9sr4HdU6x&q=85&s=a9af21fee6b3c68d0ad6ce39ea394ac1" alt="Mini App architecture" width="1200" height="520" data-path="images/miniapp/architecture-en.svg" />
  </Frame>

  **For example:** when you `@`-mention the Stocks Mini App in a session, the host starts a Node process for it and opens its page in the panel. When you open it later in another session, the host doesn't start another process; both pages talk to the same service and the same data. See [Mini Apps and sessions](/docs/miniapp/sessions).

  <h3 id="layers">
    Layers
  </h3>

  * **Presentation (the page):** HTML, CSS, and JavaScript for filters, tables, charts, and interaction. The page holds no credentials.
  * **Application (the Node service):** data aggregation, business rules, and persistence. Page APIs and Agent tools reuse the same business services, so people and the Agent change the same data.
  * **Capabilities:** the Agent API, Host Connector, and managed MCP access provided by the host, plus the CLIs, HTTP APIs, and local files the plugin reaches on its own.

  Host management spans all three layers: it decides when the process runs, who can reach the port, and which capabilities are available, while business logic always belongs to the plugin.

  <h3 id="channels">
    How the parts communicate
  </h3>

  | From → to | Channel | How identity is checked |
  | - | - | - |
  | Page → Node service | Local HTTP with relative or same-origin URLs | The host main process checks the page's real origin and adds the run credential |
  | Session Agent → Node service | Managed MCP (Streamable HTTP) | The host MCP client keeps the credential in request headers, never in config |
  | Node service → host | IPC: `context.agent`, `context.hostConnector` | The host checks the process identity and the manifest grants |
  | Page → host | `window.mcode.miniapp`, injected by preload | Checked against the real page Electron reports, never against what the page claims |
  | Node service → outside | Started by the plugin: CLIs, HTTP APIs, files | Handled by the plugin; the host adds no extra gate |

  <h3 id="lifecycle">
    Lifecycle
  </h3>

  Mini Apps start on demand: enabling a plugin doesn't start it; selecting it with `@`, opening its page, publishing, or restarting does. Each plugin has one process at a time, and at most 3 different Mini App services run at once. Updates stop the old process before starting the new one and can roll back; see [Running and updating](/docs/miniapp/host/lifecycle).

  <h2 id="example">
    Example: one full call
  </h2>

  Take a weekly risk dashboard: the user clicks a button on the page, the Node service asks the Agent to summarize the risks, the Agent calls back into this Mini App's own tool to read the data, and the result returns to the page.

  <Frame caption="The page hands the task to its Node service, Node hands it to the Agent, and the Agent calls back into this Mini App's own tools.">
    <img src="https://mintcdn.com/agent-en/mM6ovYl9sr4HdU6x/images/miniapp/call-flow-en.svg?fit=max&auto=format&n=mM6ovYl9sr4HdU6x&q=85&s=2bc8aba0a5f3dab0a755b6b5f0b85cd0" alt="One full call" width="1200" height="600" data-path="images/miniapp/call-flow-en.svg" />
  </Frame>

  <Steps>
    <Step title="Declare capabilities">
      The dashboard needs two host capabilities: a managed MCP server for the Agent, and permission to start Agent sessions. Both are declared in `miniapp.json`:

      ```json miniapp/miniapp.json theme={null}
      {
        "runtime": { "kind": "process", "entry": "./miniapp/node/server.mjs" },
        "surface": { "path": "/risks" },
        "mcpEndpoints": [{ "server": "risk-board", "path": "/mcp/risk-board" }],
        "agentAccess": ["session.run"]
      }
      ```

      From this, the host connects the MCP endpoint at publish time and exposes `context.agent` to this plugin only. Capabilities that aren't declared aren't available at runtime.
    </Step>

    <Step title="Start and become ready">
      The Node entry starts an HTTP server on the port the host assigned, serving the page, the business APIs, and `/mcp/risk-board`. After `start` returns, the host runs an authenticated probe and marks the service ready only when it passes.

      ```javascript miniapp/node/server.mjs (excerpt) theme={null}
      export async function start(context) {
        const server = createServer((req, res) => route(context, req, res));
        await listen(server, context.listen.host, context.listen.port);
        return { dispose: () => close(server) };
      }
      ```
    </Step>

    <Step title="The page sends a request">
      The user clicks "Weekly risks". The page calls only its own Node service with a relative URL; the host adds the credential as the request leaves the page, and page code never sees it.

      ```javascript miniapp/client/app.js theme={null}
      summarizeButton.onclick = async () => {
        const res = await fetch('/api/weekly-risks', { method: 'POST' });
        if (!res.ok) showError(await res.text());
      };
      ```
    </Step>

    <Step title="The Node service hands the task to the Agent">
      The request handler calls `context.agent.run`. The host creates an Agent session for this Mini App; it doesn't appear in the session list, and only this Mini App can continue it.

      ```javascript miniapp/node/server.mjs (excerpt) theme={null}
      async function handleWeeklyRisks(context, res) {
        const result = await context.agent.run({
          text: 'Call list_risks to read this week\'s risks, summarize them by impact, and list what needs my confirmation.',
        });
        if (result.status !== 'finished') return sendJson(res, 502, { error: result.status });
        await saveSummary(context.dataDir, result.text);
        broadcast({ type: 'summary.updated' }); // push to every open page
        sendJson(res, 200, { sessionId: result.sessionId });
      }
      ```

      This call passes no `onEvent`. If a tool call in the turn needs approval and nobody can answer it, the host denies it right away and tells the model why, so the task never hangs; see [Approvals and questionnaires](/docs/miniapp/authors/agent#agent-interaction).
    </Step>

    <Step title="The Agent calls the Mini App's tool">
      The Agent in that session keeps its full capabilities, including this Mini App's own MCP tools. When it calls `list_risks`, the host forwards the request to the same Node service with the run credential:

      ```json POST /mcp/risk-board theme={null}
      {
        "jsonrpc": "2.0",
        "id": 7,
        "method": "tools/call",
        "params": {
          "name": "list_risks",
          "arguments": { "week": "2026-W42" }
        }
      }
      ```

      The Node service reads the risk list from `dataDir` and returns it as the tool result. The page API and this tool call share the same business logic.
    </Step>

    <Step title="The result returns to the page">
      When the turn ends, `run()` resolves with the summary. Node writes the result to `dataDir` first and then notifies the page over SSE; if the push fails, the page simply reads again after reconnecting. If the user wants to see how the Agent worked, the page can call `window.mcode.miniapp.agent.open(sessionId)` to show the session in the chat panel.
    </Step>
  </Steps>
</div>
