Chataway Developers
Concepts

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.tools is 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

Agent Chataway kernel — remote runner 1account → Bearer token, or a Connect card 2files → consent, prepare, accept/maxBytes 3confirm → blocking approval card 4headers → per-app pseudonyms 5call your server (120 s timeout) 6result → text to agent, files to storage 7_meta card · elicitation → form card Your server remote.mcpUrl

Every call from the agent goes through the kernel, in this order:

  1. Account. If the tool declares account, the runner adds Authorization: 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.
  2. Files. For each param in the tool's files, the runner resolves the refs the agent passed, asks for hand-off consent if needed, runs prepare, and checks accept and maxBytes. See Files & hand-off.
  3. Confirm. If the tool declares confirm, a blocking approval card appears. Declined → your server is never called.
  4. Headers. The request carries X-Chataway-Project and X-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.
  5. Call. tools/call is sent to your server.
  6. Result. See below.

#Results

What you return in a CallToolResult, and what Chataway does with it:

You returnChataway
text contentPassed to the agent as-is.
structuredContentPassed 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 blobSame as above — use this for any file type.
resource_link to a URL on one of your remote.domainsDownloaded 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: trueThe 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:

ProviderYour toolsCards
Chataway chat (Agent SDK)YesInline, on the tool call that raised them
Claude CodeYesIn the pending-asks strip above the composer, and in your panel
CodexYesPending-asks strip
GrokYes, when the agent supports HTTP MCPPending-asks strip
CursorYesPending-asks strip
OpenCodeNot 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

StateWhere
Your dataOn your servers, keyed however you like — by your own user ids (from the account token) and, if useful, by the project pseudonym.
Files you returnedYour app's storage on the Mac, per project. The user can add them to the Media library or save them to the project.
SettingsChataway, per the settings schema you declare.
TokensChataway's encrypted vault on the Mac.