Skip to content
Esc
↑↓navigate↵open⌘Jpreview
On this page

Connect one app over MCP

Give a single app its own MCP URL. The client sees the app's declared routes as tools and calls them by name.

What you can do

Point an MCP client at one app. Every app you own, reach through a team, or have been given access to has an address on the MCP host: https://mcp.charm.ing/<owner>/<app-name>. Opening that URL in a browser shows install instructions for common clients. A client connected there sees that app alone, with each declared route as a tool named after its operation. The client calls addPlant({ bed: "north", plant: "basil" }) with no app id and no operation selector.

Connect a client that can’t sign in. Some clients take only a fixed token in an Authorization header and never run a sign-in flow. Create a token in Account settings → Agents, choose how long it lasts, and paste it into the client as the bearer token.

Keep the shared server for everything else. https://charm.ing/mcp still reaches every app you own, creates apps, and edits code. The per-app URL is the same access at one app’s address, for a client that should work with that app and nothing more.

Copy the URL from settings or the widget. The MCP section of an app’s settings shows the app’s URL with a copy button. The Overview of the Charming widget offers Add as MCP server on every app you own, reach through a team, or have been given access to; it copies the same URL. A team-owned app’s URL carries the team’s handle as <owner>. The app’s owner, members of the team that owns it, and accounts it is shared with can connect at its URL. A public link, or an app open to every signed-in account, does not let a client connect.

Technical

The MCP section of an app’s settings shows the URL to paste into a client. Most clients sign in with Charming’s own sign-in, the same flow the shared server uses. A client without a sign-in flow sends a personal access token instead.

Copy this prompt for your agent

Connect to my Charming app over MCP. Read
https://charm.ing/docs/guides/connect-an-app-over-mcp.md first. I will
give you the app's URL on mcp.charm.ing; add it as an MCP server, sign
in when asked, then list its tools and tell me what the app can do.

What the client needs

The host accepts the 2025-03-26, 2025-06-18, 2025-11-25, and 2026-07-28 MCP protocol revisions. A 2025 client uses the standard initialize handshake; a modern client uses per-request protocol metadata. Both receive the same app tools and use the same access checks.

The transport is stateless. Legacy clients send requests by POST with Accept: application/json, text/event-stream; after initialization, include the negotiated MCP-Protocol-Version header. There is no persistent session or standalone GET event stream; GET and DELETE session requests return 405.

Sign-in follows the standard MCP authorization flow. An unauthenticated request receives a 401 whose WWW-Authenticate header names the app’s protected-resource document at https://mcp.charm.ing/.well-known/oauth-protected-resource/<owner>/<app-name>; that document names https://charm.ing as the authorization server.

Ask for the token with no RFC 8707 resource parameter, or with https://charm.ing/mcp. The authorization server issues tokens for its three shared MCP resources, and the per-app identifier is not one of them: an authorization request that names it is refused with invalid_target. A token issued for a shared resource is accepted here.

Clients without sign-in

A client that accepts only a static header sends a chrm_user_* personal access token on every request:

Authorization: Bearer chrm_user_...

Create one in Account settings → Agents with Create token, or with Create token and expiresInDays. Pick 7, 30, or 90 days, or a custom date up to a year away (expiresInDays from 1 to 365; the API defaults to 7). Only a signed-in session can choose a lifetime other than 7 days. Charming shows the plaintext once. When the token expires, the host answers with the 401 sign-in challenge, which a static-token client cannot follow: create a new token and replace the old one in the client. Disconnect a token on the same Agents page to revoke it immediately.

The token carries your whole account, like a signed-in client. It is not limited to the app at the URL.

What the client receives

A connected client acts with your account’s access to Charming, the same access the shared server grants. The per-app URL narrows what the client sees, not what your account can do. Connect only clients you trust with your account.

Within the app, your access decides what the tools do. Read tools run for every account that can connect. Write tools need run access; a viewer who calls one receives a forbidden error and nothing changes.

Which routes become tools

Every route the app’s active revision declares in export const routes becomes a tool, except a route with public: false. An app with no declared routes connects and lists no tools, and its author can add routes to change that.

  • The tool keeps the operation’s name when that name is a valid MCP tool name (A-Z, a-z, 0-9, _, ., -, up to 128 characters). Any other name is encoded the same way every time. If an encoded name would clash with another tool, the app publishes no tools until its author renames one.
  • The tool’s input schema, output schema, title, description, and examples come from the route. A route whose input is not an object takes its input as { "value": ... }.
  • For a 2025 client, a route whose output is not an object returns structuredContent as { "result": ... }, with its output schema wrapped to match. A modern client receives the authored value directly.
  • The route’s annotations become the tool’s readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. A route marked only readOnlyHint: true publishes destructiveHint: false and idempotentHint: true.

Renames and old URLs

After you rename an app or change your handle, the old URL keeps answering for the same app, and a client connected there keeps working. Settings show the current URL; hand that one to new clients.

Errors

Error Meaning
401 with WWW-Authenticate Sign in first, or replace an expired token. The header names the app’s resource document.
404 not_found No app answers at that address for your account.
forbidden (tool result) The tool changes the app’s state and your access to the app is read-only.
invalid_input / invalid_output (tool result) The arguments or the handler’s result did not match the route’s schema.
revision_mismatch (tool result) The app changed between listing its tools and calling one. List the tools again.

Was this page helpful?