Handshake & claiming
A Tesseron session goes through three states: connected, awaiting claim, claimed. Only claimed sessions can have their actions invoked.
The tesseron/hello request
Section titled “The tesseron/hello request”Sent by the app right after the WebSocket opens.
{ "jsonrpc": "2.0", "id": 1, "method": "tesseron/hello", "params": { "protocolVersion": "1.0.0", "app": { "id": "shop", "name": "Acme Shop", "description": "Product catalog and cart", "origin": "http://localhost:3000", "version": "1.0.0", "iconUrl": "https://shop.example/icon.svg" }, "actions": [ { "name": "searchProducts", "description": "Search the product catalog", "inputSchema": { /* JSON Schema */ }, "outputSchema": { /* JSON Schema, optional */ }, "annotations": { "readOnly": true }, "timeoutMs": 60000 } ], "resources": [ { "name": "currentRoute", "description": "URL the user is viewing", "subscribable": true } ], "capabilities": { "streaming": true, "subscriptions": true, "sampling": true, "elicitation": true } }}Rules:
app.idmust match/^[a-z][a-z0-9_]*$/. It becomes the prefix on every MCP tool this app contributes.app.originis informational; the MCP gateway treats its own origin check (see Transport) as authoritative.- Action
inputSchema/outputSchemaare JSON Schema. The SDK derives them from your Standard Schema validator where possible, or you can pass them explicitly. capabilitiesis what the app can do, not what the agent can do - that comes back inwelcome.
The welcome response
Section titled “The welcome response”{ "jsonrpc": "2.0", "id": 1, "result": { "sessionId": "s_a1b2c3de1234567", "protocolVersion": "1.0.0", "capabilities": { "streaming": true, "subscriptions": true, "sampling": true, "elicitation": true }, "agent": { "id": "pending", "name": "Awaiting agent" }, "claimCode": "AB3X-7K" }}sessionIdis opaque and only meaningful to the gateway - log it for debugging.capabilitieshere is the intersection of app and agent capabilities. If the agent doesn't support sampling, it will befalsehere even if you asked for it.agentstays at{ id: "pending", name: "Awaiting agent" }until a claim happens.claimCodeis a 6-character human-friendly string likeAB3X-7K. Alphanumerics minus visually confusing characters.
Claiming
Section titled “Claiming”The claim code is not sent on the wire to the agent. It's displayed in two places, for the human to transfer out-of-band:
- The gateway prints it to stderr (which Claude Code surfaces).
- The app is free to render it in its UI - e.g. a "Connect Claude" button that reveals the code.
The user then tells the agent:
Claim Tesseron session AB3X-7K
The agent calls the built-in tesseron__claim_session MCP tool with { code: "AB3X-7K" }. The gateway looks up the pending claim, and if it matches:
- Marks the session
claimed: true. - Sets
agenton the session to the agent's identity. - Emits
notifications/tools/list_changedso the agent refreshes its tool list. - From this point,
tools/call <app_id>__<action>is allowed.
If the code doesn't match (expired, wrong app, already used): error -32009 Unauthorized.
Why out-of-band claim?
Section titled “Why out-of-band claim?”Because in-band claim is just security theatre over localhost. Anything running on the user's machine can open a WebSocket to :7475. The claim code is a user-typed confirmation - proof that a human authorised this specific browser tab to be controlled by this specific agent session. It's short enough to read aloud, long enough to resist guessing (~1.5 billion combinations of 6 upper-case alphanumeric minus confusables).
Protocol version mismatch
Section titled “Protocol version mismatch”The MCP gateway parses protocolVersion as major.minor:
- Different major → hard reject with error code
-32000 ProtocolMismatchand the WebSocket is closed. - Different minor → accepted, with a warning logged to gateway stderr. New fields added in later minors may be silently dropped; rebuild the SDK bundle to resync.
- Exact match → no logging.
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32000, "message": "Gateway speaks protocol 1.0.0; SDK sent 2.0.0. Major version mismatch - pin compatible package versions." } }Next: the action model.