Security model
Tesseron's security model is local-first, user-authorised. The MCP gateway binds to localhost and won't expose any action until a human types a short code out-of-band. These are the two gates.
Gate 1 - origin allowlist
Section titled “Gate 1 - origin allowlist”The WebSocket upgrade is accepted when:
Originstarts withhttp://localhost:orhttp://127.0.0.1:, orOriginappears verbatim inTESSERON_ORIGIN_ALLOWLIST(comma-separated).
Anything else returns HTTP 403. This is defence-in-depth - it prevents a drive-by page on evil.com from spraying tesseron/hello messages at your gateway and enumerating your app's surface.
Gate 2 - the claim code
Section titled “Gate 2 - the claim code”Even from a permitted origin, the session is inert until claimed. The flow:
- App connects and sends
tesseron/hello. - MCP gateway generates a random 6-char code (format
XXXX-YY, excludes visually confusing characters) and returns it inwelcome. - The code is displayed out-of-band: gateway stderr, and wherever your app chooses to render it.
- The user types the code into the agent ("connect Tesseron session AB3X-7K").
- Agent calls
tesseron__claim_session. If the code matches, the session transitions toClaimedandtools/list_changedfires.
The claim code is never sent on the WebSocket from the gateway to the agent - the user carries it across. That's the whole point: it's a human-performed authorisation gesture, not an electronic one.
Strength: ~1.5 billion possible codes (6 positions × ~32 unambiguous alphanumerics). A brute-force attacker would need ~750M tries for a 50% hit rate. Codes are single-use; a failed match does not retry.
Multi-app coexistence
Section titled “Multi-app coexistence”Multiple apps can be connected at once. Every MCP tool is prefixed with the app.id, so shop__addItem and admin__banUser never collide. Internally the gateway routes tools/call name=shop__addItem to the session whose app.id === "shop" - if that session has disconnected, the call errors with -32003 ActionNotFound.
This means you can, without coordinating, keep your dashboard and your customer app both connected to the same agent. Each has its own claim code, its own origin check, its own session.
What Tesseron does NOT defend against
Section titled “What Tesseron does NOT defend against”- Malicious code running in your app's process. If an attacker already executes JS in your tab or on your Node server, they can call your SDK and declare whatever actions they want. Tesseron is no worse - and no better - than the process it's embedded in.
- Malicious MCP clients on the same machine. Any local process can open a WebSocket to
ws://127.0.0.1:7475and sendtesseron/hello. The origin check only fires if the client sends anOriginheader; non-browser clients may not. The claim code is the second gate, and it requires human cooperation. - Prompt injection. If your handler's
descriptionor inputs are attacker-controlled, they can manipulate the agent's plans. Sanitise descriptions you show to the agent the same way you would sanitise HTML you show to users. - Exfiltration via resources. Anything you expose as a resource is readable by the claimed agent. Don't expose credentials, session tokens, or PII you haven't decided the user is okay sharing with Claude.
Operational tips
Section titled “Operational tips”- Never expand
TESSERON_ORIGIN_ALLOWLISTby default. Add origins only for specific agent integrations that need them. - Treat the claim code as short-lived. Don't render it persistently in the UI after the session is claimed.
- Log the
agent.idthat claimed the session - useful for auditing which agent actually ran which action. - For production tools, use per-user app IDs.
shop_kennyvsshop_sarahprevents one user's agent from driving another user's tab, even if both are on the same machine.
That's the end of the Protocol section. The SDK section picks up from here - how to speak this protocol from TypeScript today and from other languages later.