Your API, as tools for AI agents
Mark the endpoints an agent may call. Courier serves them over MCP to Claude Code, Cursor and every MCP client — typed from your schemas, run through your middleware, and confirmed by a person before anything is undone.
cwd ~/shop · MCP shop (courier-mcp) connected, 2 tools
? for shortcuts
STUDIO · TIMELINE
From a definition to an agent's tools in three steps
- STEP 1
Mark what an agent may call
One line on the endpoints you already defined. Everything else stays out of reach.
.agent({ description: "Every open order", access: "read", })
- STEP 2
Add it to the agent
Your editor's agent starts it over stdio. Your app serves it over HTTP to the rest.
claude mcp add courier -- npx @noverax/courier-mcp - STEP 3
Watch every call
Each call runs through your middleware and lands in Studio, marked agent.
npx @noverax/courier-studio
SCOPE
Only what you offer becomes a tool
An agent sees the endpoints you offered — each marked .agent(), or every read in one line of config — and never what your config denies: not the export your nightly job calls, not the admin purge.
- Typed tools from the schemas each endpoint already has.
- Hints a client acts on: read, write or destructive, so it knows when to ask.
- Checked at start: a policy it cannot read, or a write offered as a read, stops the server, not a model.
POLICY
Your middleware decides who may do what
An agent's call runs through the middleware your app's calls run through, with ctx.agent saying which agent asked and for whom. There is no second path to forget to protect.
- One set of rules — auth, rate limits and audit apply to agents as written.
- Tools per caller: one a principal may not use is absent from its list.
- No borrowed sessions: an agent carries no browser's cookies, only what you grant.
if (ctx.agent?.access !== "read"
&& !canWrite(ctx.agent.principal))
return { status: 403, data: { message: "Reads only" } }CONFIRMATION
A person says yes before anything is undone
A destructive tool runs only once the person at the client has confirmed that exact call. The question goes to the person — text an API returns to the model cannot answer it.
- The exact call, its arguments checked by the schema before anyone is asked.
- Signed and bound: a yes counts for one tool, those arguments, five minutes.
- Stateless: under MCP 2026-07-28 it travels with the call, so any replica finishes it.
TO THE MODEL · A TOOL'S ANSWER
TO THE PERSON · OUT OF THE MODEL'S REACH
An agent asks to run shop_orders_cancel
Cancel an order that has not shipped yet. This cannot be undone.
POST shop.orders.cancel with { id: "o_7f2a" }
STUDIO
Every agent call, on the record
Studio shows an agent's calls beside your app's own — the tool, the client, the variables, the middleware, the answer. A refusal is a row, not a gap.
- Marked agent, with one filter to see them alone.
- Refusals included: a declined confirmation, a policy's 403, bad arguments.
- The principal stays home: Studio is told the tool and the client, never who.
SETUP
One line in your editor, one handler in your app
Install it in the project, then add it where your agents are. The command serves your courier.config.ts; the handler serves your app's registered sources.
claude mcp add courier --scope project -- npx @noverax/courier-mcp --dotenv .env.localWrites .mcp.json, so everyone who works on the project gets the tools.
- Your credentials stay yoursLocally the tools run with your environment; in your app, with what authorize grants.
- Nothing leaves unaskedNo account, no cloud relay, no telemetry. The server is a process you run.
- Both eras of MCP2026-07-28, and the 2025 handshake editors speak by default today.
Give your agents the API you already typed
Free and open source, part of Courier. One definition for your app, your clients and your agents.