API reference
The MCP endpoint
POST /mcp serves your registered catalog over the Model Context Protocol. Point an agent at
it instead of at your own tools, and every read is scoped and audited while every write stops
and waits for a person.
Standard JSON-RPC. initialize, tools/list, tools/call, and notifications.
What tools/list returns
Your tenant's own agent-eligible capabilities, each with its schema — never another tenant's. The additional meta-capabilities depend on the credential used for the connection:
| Credential | Meta-capabilities |
|---|---|
Agent key (tag_) | list_pending_actions |
Tenant secret (tth_) | register_capability, validate_catalog, list_pending_actions, confirm_action |
Use the tenant secret only from a trusted onboarding or approval application. The runtime agent
must use an agent key. That separation prevents an agent from changing is_write, risk, or the
catalog entry that governs it, and prevents it from approving its own staged write.
What tools/call does
A read runs and returns its result.
A write does not run. It stages, and the tool result is the pending action:
{
"preview": true,
"action_id": "8f1c…",
"command_type": "refund_order",
"summary": "Refund 899.00 to the card ending 4471 — awaiting confirmation.",
"expires_at": "2026-09-06T16:36:46Z",
"confirmation_required": true
}
Your agent reports the pending action and stops. Confirmation belongs to a trusted application:
use the Actions API, the approval inbox, or your own
approval UI backed by the tenant secret. A trusted MCP client using that secret can call
confirm_action; the runtime tag_ connection is neither offered that capability nor permitted
to call it.
Proxying a server you already run
PUT /v1/upstreams connects an upstream MCP server. Tether lists its tools and stores them as
an unmarked draft.
curl -X PUT $TETHER_URL/v1/upstreams \
-H "X-Api-Key: $TETHER_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "kind": "mcp", "url": "https://tools.yourcompany.com/mcp", "token": "…" }'
| Field | Notes |
|---|---|
url | Required. The upstream MCP server. |
token | Sent to the upstream as Authorization: Bearer …. credential is the same field. Optional. |
kind | Optional. Only mcp is accepted here; for an OpenAPI document use POST /v1/tools/import. |
Tether connects to the upstream with the MCP streamable HTTP transport, protocol version
2025-06-18 or whatever the server negotiates down to. For each list or call it initializes,
sends notifications/initialized, makes the request, and ends the session, carrying
Mcp-Session-Id and MCP-Protocol-Version when the server issues them. Servers that run
stateless and servers that keep sessions both work, and a reply may be plain JSON or an event
stream. A proxied read therefore costs your server three requests (initialize, initialized, the
call), plus a session delete when it keeps sessions, so a server far from Tether adds about three
round trips to each read. The catalog is stored, not fetched again per turn.
The older HTTP+SSE transport from 2024-11-05, which needs a separate event endpoint,
is not supported.
readOnlyHint and destructiveHint prefill suggested_is_write and suggested_risk. They
never set is_write. Nothing is proxied until you declare it — a tool left unmarked is not
served, which is the safe direction to fail in.
Mark a tool by registering it with PUT /v1/tools and "upstream": true, plus is_write and,
for a write, risk. A tool that shares a name with one on your server but lacks "upstream": true
is registered as your own and the response carries a warnings entry naming it.
Once marked, a proxied read is forwarded to the upstream. A proxied write stages exactly as a native one does and needs your approval, but once confirmed it runs at your tool endpoint, not on the upstream server, and staging it needs that endpoint to be set. Registration warns about each such tool.
Related
- Bring your AI — the door this belongs to
- Tools — registering a catalog over HTTP
- Actions — the same staged writes, over HTTP