How apps run
The remote app runner, what happens on every tool call, how results come back, and how each chat provider shows your app.
A Chataway app has no code running on the user's Mac except its sandboxed panel. Chataway runs every app with one generic remote app runner, and that runner is the only thing that talks to your server.
#Lifecycle
Enable. When a user enables your app in a project, Chataway shows your permissions and asks for consent to your grants. If you declared an account with required: true, it also offers Connect.
Activate. The runner starts one instance per project × app. If your server can't be reached, the app shows as failed with the reason, and the runner retries with backoff (up to once a minute); your tools appear as soon as it answers. It connects to remote.mcpUrl over Streamable HTTP, negotiates the protocol version (Mcp-Protocol-Version), and lists your tools. If the MCP session ends, the next call reconnects.
Register. Only tools declared in your manifest are registered. The manifest is what was reviewed, so:
- a tool on your server that isn't in
contributes.toolsis ignored (and logged in the Inspector), - a tool in the manifest that your server doesn't list is skipped until it appears,
- the description the agent reads comes from the manifest, and the input schema comes from your server.
Disable. Turning the app off disposes the runner: the agent loses your tools, pending cards are withdrawn, and nothing is sent to your server again.
#Anatomy of a tool call
Every call from the agent goes through the kernel, in this order:
- Account. If the tool declares
account, the runner addsAuthorization: Bearer <access token>for that connected account, refreshing it first if it's about to expire. Not connected? The user gets a Connect card in the chat; the call waits for it. - Files. For each param in the tool's
files, the runner resolves the refs the agent passed, asks for hand-off consent if needed, runsprepare, and checksacceptandmaxBytes. See Files & hand-off. - Confirm. If the tool declares
confirm, a blocking approval card appears. Declined → your server is never called. - Headers. The request carries
X-Chataway-ProjectandX-Chataway-Chat: opaque per-app pseudonyms (an HMAC over your app id and the real id, with a key kept on the user's Mac). Two apps get different pseudonyms for the same project, and neither can be turned back into a Chataway id. - Call.
tools/callis sent to your server. - Result. See below.
#Results
What you return in a CallToolResult, and what Chataway does with it:
| You return | Chataway |
|---|---|
text content | Passed to the agent as-is. |
structuredContent | Passed to the agent as JSON when the result has no text content. |
image or audio content (base64) | Saved into your app's storage (needs files:return), shown to the user as a gallery, and handed to the agent as a file ref. |
Embedded resource with a blob | Same as above — use this for any file type. |
resource_link to a URL on one of your remote.domains | Downloaded by Chataway, then treated like an embedded resource. Links to other hosts are passed on as plain links. |
_meta["chataway/card"] | A notice or gallery card shown next to the result. See Cards & approvals. |
isError: true | The agent sees your error text and can react to it. |
Without files:return, returned files are dropped and the agent is told why. Stored files appear as a gallery card with Add to Media library and Save to project buttons, and the agent gets one ref per file.
Limits: a response may be up to 50 MB in total — Chataway stops reading a larger one and the call fails. Every request Chataway makes for your app (including redirects and link downloads) must go to your declared hosts over https.
#Timeouts and long work
- 120 seconds per call. After that the call fails with a timeout and the agent is told so. (A call waiting on a card the user hasn't answered yet is not counted against you — the clock starts when your server is called.)
- Long work → job handle. Return a job id quickly and declare a status tool the agent can poll. Keep status responses small; return the finished files from the status tool.
- Rate limit. 60 calls per minute per app, across the user's projects, to protect users from runaway agents. A limited call returns an error the agent can see; it doesn't reach your server.
#Asking the user mid-call (elicitation)
If your server sends an MCP elicitation request (elicitation/create) while handling a call, Chataway turns it into a form card with your app's icon. The user's answer goes back to your server as the elicitation result, and your handler continues. Use it for the one question you can't avoid — "Which of your 3 workspaces?" — not for things that belong in the tool's arguments.
See Cards & approvals for the supported field types.
#Providers
Chataway runs several agent providers. They all get your tools, but they show cards differently:
| Provider | Your tools | Cards |
|---|---|---|
| Chataway chat (Agent SDK) | Yes | Inline, on the tool call that raised them |
| Claude Code | Yes | In the pending-asks strip above the composer, and in your panel |
| Codex | Yes | Pending-asks strip |
| Grok | Yes, when the agent supports HTTP MCP | Pending-asks strip |
| Cursor | Yes | Pending-asks strip |
| OpenCode | Not yet | — |
Your app's instructions — tool descriptions, and for local apps ctx.prompts, ctx.agentContext and skills — reach every one of these providers, OpenCode included, each through its own channel. See Instructions for every agent.
A blocking call (waiting on a confirm or form card) keeps the agent's tool call open until the user answers. If the user takes too long, the tool returns "still waiting for the user" and the answer is given to the agent with the user's next message — so your server may be called later than the agent's turn.
To limit your app to some providers, set requires.providers in the manifest. Most apps shouldn't.
#Where state lives
| State | Where |
|---|---|
| Your data | On your servers, keyed however you like — by your own user ids (from the account token) and, if useful, by the project pseudonym. |
| Files you returned | Your app's storage on the Mac, per project. The user can add them to the Media library or save them to the project. |
| Settings | Chataway, per the settings schema you declare. |
| Tokens | Chataway's encrypted vault on the Mac. |