Skip to content
Charming Docs
Esc
navigateopen⌘Jpreview
On this page

Connect to any AI agent

Build your app in one agent, then open, read, and update it from another: Claude, ChatGPT, any agent that speaks MCP or HTTP, all against the same app.

What you can do

Every app is accessible over MCP from the moment it’s created. You don’t add an API, register anything, or write integration code: the operations the app declares are already callable through Charming’s MCP tools (query_app for reads, mutate_app for writes). See Every app is accessible over MCP.

Build with ChatGPT, then pick the work back up in Claude, Claude Code, or Cursor, without losing anything. Every Charming app is one URL and one dataset, so whichever agent you’re in is working on the real app, not a copy of it.

Your agent can read data, change data, and update the app’s code. Other people can point their own agents at the same app and do the same, within whatever access you’ve given them.

Technical

Charming speaks two open interfaces. It’s an MCP server, so any client that supports MCP connectors, Claude, ChatGPT, Cursor, and others, can add Charming directly; setup steps for each client are at AI clients. It’s also a plain HTTP API, so any agent that can make web requests, even just curl, can use your app: give it the app’s id or URL and it has everything it needs.

Copy this prompt for your agent

Connect to my Charming apps. Read
https://charm.ing/docs/capabilities/connect-any-agent.md first. If
you don't have access yet, use the device-pairing flow it describes
and ask me to approve it, then list my apps so I can pick one to
work on.

How an agent performs this job

If the agent needs Charming’s documentation, it can call read_docs({}) for the index or read_docs({ path: "build-mcp.md" }) for the MCP build guide. This uses the existing connection and needs no web access. Follow a returned next_offset with the same path to read the rest of a long page. See Read docs through MCP.

An agent with Charming MCP tools connected calls list_apps to find the app (or uses an id/URL it’s already been given), get_app to see the app’s current callable operations, get_app_source if it needs the actual code, query_app for a read-only operation, mutate_app for one that writes, and update_app if the code itself needs to change.

An agent that only makes HTTP requests does the same job through the matching routes: GET /app lists Apps, GET /app/<id>/describe returns the operation inventory (GET /app/<id>/agent.json is the equivalent no-auth descriptor for a Public App or Listed Template; an enabled unlisted Template stays private without app:read), GET /app/<id>/source exports the code, POST /app/<id>/api/<op> calls one operation (the route’s annotations.readOnlyHint decides whether it’s a read or a write), and PUT /app/<id> ships a code update.

To use the app from a chat assistant, add Charming as a connector in that client first; setup steps are at AI clients. Or hand the job to a code agent that can make HTTP requests instead.

The contract

Two credential families decide what a second agent can touch:

  • chrm_user_* (account-scoped). Minted by signing in and calling POST /api/token, or by device-pairing (POST /api/pair/start, approve at charm.ing/pair, POST /api/pair/poll). Any agent holding this token can list, read, and mutate every app the account owns, not just the one that first created it. Browser-minted tokens default to a 7-day TTL; paired tokens default to 30 days.
  • chrm_app_* (one app). Minted for a specific app at anonymous create. It authorizes mutations and /api/* calls on that one app id only and doesn’t list or touch any other app. If the app has since been claimed by an account, a stale chrm_app_* gets a 401 with a recovery block pointing at the device-pairing flow instead of a silent failure.

Over MCP, the client completes OAuth once when it’s added as a connector; after that, tool calls carry the signed-in account’s scope automatically, so the agent never handles a bearer token directly. Over HTTP, the calling agent supplies Authorization: Bearer <chrm_user_* or chrm_app_*> itself on every request.

Limits, access, and deletion

  • Read-only vs writing. query_app only runs operations the app declares read-only with annotations.readOnlyHint: true; calling a mutating operation through it returns not_read_only. mutate_app requires run access and refuses to run an operation declared read-only.
  • Viewer role. An agent acting for a read-only viewer (holding app:read but not app:run) can call query_app but gets forbidden_write if the underlying handler tries to write, and is denied mutate_app outright.
  • Scope leaks stay closed. A chrm_app_* token can’t list or read the source of any app other than the one it was minted for; those calls 401.
  • Render tokens aren’t for this. The chrm_render_* token embedded in a rendered app’s page is scoped to that page calling its own backend, not to an outside agent driving the app. A second agent uses chrm_app_* or chrm_user_* instead.
  • Deletion. Deleting the app (delete_app / DELETE /app/<id>) removes it for every agent that had access, not just the one that called delete. Over MCP, a client that supports confirmation shows the delete request before it runs. A client without that confirmation flow gets confirmation_required on the first call; the agent must ask you, then retry with confirm: true. Charming can enforce the client confirmation when the client supports it, but it cannot prove that another agent asked you before sending the retry.

Was this page helpful?