> ## 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.

# Data and services

> All business reads and writes live in the Mini App's Node service; the page only calls business APIs that Node provides.

## Choose an access method

| Method | Good for |
| - | - |
| Host Connector | Third-party apps the user has authorized in MiniMax Code, such as Notion |
| CLI child process | Command-line tools already on the machine, started and cleaned up by the plugin |
| HTTP API / SDK / local files | Services with their own authentication, or local files the user authorized |
| Unified tool entry | Calling the host's mounted MCP tools and Connectors with one API |

Connectors are one option, not a Mini App prerequisite, and a missing Connector doesn't mean the data is unreachable. If the user names a method and it can be verified, use it; otherwise choose by available capabilities, authentication, runtime environment, and the operations you need.

## Verify before you build

Before writing the package, run one read-only query against each data source with the chosen method. Confirm authorization, real parameters, response fields and types, and get a representative record or a real empty result. Write the parser from the shape you observed.

> [!NOTE]
>
> A query that succeeds in the Agent's terminal doesn't prove it works in the Mini App runtime. CLI, API, and file access still need one real run inside the Node service.

## Unified tool entry

See [Node tool API reference](https://agent.minimax.io/docs/miniapp/reference/node-api.md#tools) for signatures, inventory and result types, cancellation, and timeouts. Page-side authorization is covered in [tool.request reference](https://agent.minimax.io/docs/miniapp/reference/client-api.md#tool-request).

The Node service uses `context.tool` to list and call the host's mounted MCP tools and the user's connected Connector providers. Agent built-in file, shell, and browser tools are not included. For managed MCP implemented by this Mini App, reuse your local business functions directly.

```json miniapp/miniapp.json theme={null}
{
  "toolAccess": ["call"]
}
```

`context.capabilities?.has('tool')` checks whether the host implements the interface, not whether the manifest declared it or the user allows calls. Switching tool access off leaves `context.tool` present, but new calls reject with `TOOL_NOT_PERMITTED`.

```javascript theme={null}
// Call from a request handler after activation; verify names, arguments, and results with a real read
async function callVerifiedTool(context, args) {
  const { tools } = await context.tool.list();
  const tool = tools.find(
    (item) => item.kind === 'mcp' && item.server === VERIFIED_SERVER && item.tool === VERIFIED_TOOL_NAME,
  );
  if (!tool || tool.permission === 'deny') throw new Error('tool_unavailable');
  return context.tool.call(tool.name, args);
}
```

* Take `name` from `list()`, matching `kind`, `server` / `provider`, and `tool`; never construct tool names. `list()` returns `{ tools, partial }`; `partial: true` means the inventory is incomplete: a source may be unavailable or some tools may have been omitted because of metadata problems. Listed tools remain callable subject to current permissions.
* `permission` is the current `allow`, `ask`, or `deny` decision; every call is judged again. Rules come from the user's global tool permissions and permission mode, not a particular Agent or session. Host built-in tools retain their existing allowance, and Agent tool hooks do not run on this path.
* When confirmation is needed, the host prompts with **Allow once**, **Always allow**, and **Deny**. Only the user's **Always allow** choice in that host prompt writes a global rule; the Mini App cannot answer it. When nobody can confirm, the call rejects with `TOOL_APPROVAL_REQUIRED`.
* MCP results contain `kind: 'mcp'`, `isError`, and `content`; `isError: true` is a business failure reported by the tool, and the promise still resolves. Connector results contain `kind: 'connector'` and the raw `value`. Reconcile business state before retrying writes after `unknown_after_dispatch`.
* Node's `context.tool.request(names)` and the page's `window.mcode.miniapp.tool.request(names)` can ask for permission ahead of a call; feature-detect `tool.request`. A page request must come from a user gesture, name at most 10 tools, and have the Node service running. The page only requests permission; listing and calling stay in Node. Pass runtime names from Node to the page through your business API.

## Host Connector

See [Host Connector API reference](https://agent.minimax.io/docs/miniapp/reference/node-api.md#connector) for the complete `list`, `call`, tool metadata, and error contracts.

The Node service uses connectors the user has authorized through `context.hostConnector`. Third-party calls use the account the user authorized in MiniMax Code; the Mini App stores no credentials.

```json miniapp/miniapp.json theme={null}
{
  "hostConnectorAccess": { "providers": ["notion"] }
}
```

* Declare only providers the runtime really needs and that you have verified.
* `list()` is always available and returns the allowed tools with their input schemas. Use `call()` only after the service is activated, from a request handler, never in `start`.
* Don't construct or store `toolRef` values, and don't send them to the page. List again after a process restart or a stale reference.
* When the user signs out, the tool list and authorized references are revoked. After the same account signs in again, call `list()` once more to recover, with no republish. If a different account signs in, old references are revoked for good.

```javascript theme={null}
// Call from a request handler; tool names and arguments come from the inventory and schema you verified
async function readRecords(context, args) {
  const { tools } = await context.hostConnector.list();
  const tool = tools.find((t) => t.provider === 'notion' && t.name === VERIFIED_TOOL_NAME);
  if (!tool) throw new Error('connector_tool_unavailable');
  const result = await context.hostConnector.call(tool.toolRef, args);
  return parseRecords(decodeHostConnectorValue(result.value));
}
```

### Error handling

Branch on `code`; don't rely on `instanceof` across processes.

| code | Meaning |
| - | - |
| `TOOL_REF_STALE` / `SERVICE_RESTARTED` | The reference expired or the service restarted; call `list()` again |
| `INVALID_ARGUMENTS` | Arguments don't match the schema; `diagnostic.issues` gives `path`, `constraint`, and `limit`, never the argument values |
| `CONNECTOR_UNAVAILABLE` | The connector is currently unavailable |
| `CONNECTOR_TIMEOUT` / `REQUEST_CANCELLED` | Timed out or cancelled |
| `CONNECTOR_PROVIDER_ERROR` | The third-party service reported an error. If the cause is unknown, don't assume it is an authorization problem |
| `CONNECTOR_OUTCOME_UNKNOWN` | The request went out, but its outcome is unknown |

`disposition` tells you whether the request went out: `not_dispatched`, `provider_reported`, or `unknown_after_dispatch`. For `unknown_after_dispatch`, read the business state to reconcile before deciding whether to retry a write.

### Decoding results

`result.value` is the third-party service's raw result; the host doesn't normalize it. The Skill's `host-connector-value.mjs` unwraps the common single-text-block envelope and passes every other shape through unchanged. Zero parsed items count as success only when the service explicitly returned an empty result; if there is source data but nothing parses, treat it as a parse error, not as "no data".

## State and persistence

* Keep runtime data in `context.dataDir`, not in the workspace or the payload.
* Route every write from the page, MCP calls, and Agent operations through one Node business service. Commit state before pushing SSE or WebSocket events so every open page refreshes to the latest data.

Write business state to `context.dataDir` as it changes; do not use `dispose()` as the only persistence point. On restart, read the saved data and resume work.
