Odel

Architecture

What happens between a client's request and your module's code.

Odel runs entirely on Cloudflare's edge. There is no origin server, no region to pick, and no container that stays warm between calls. Understanding the path a request takes explains most of the constraints your module lives under.

One call, end to end

An MCP client — Claude Code, Cursor, whatever — has your module's URL and wants to call a tool.

The client hits mcp.odel.app

Your module's address is mcp.odel.app/@org/module. That host is the MCP proxy, and it is the only thing on the public internet with your module's name on it.

The proxy checks who's asking

Odel is an OAuth 2.1 server. If the client has no valid token the proxy answers with a challenge and metadata describing where to authenticate, and the client registers itself and signs the user in. The proxy ends up with a verified user, not a shared API key.

It gathers that user's configuration

The proxy resolves the calling user's settings for your module, and asks MI6 — the vault service — for the credential values behind them.

It finds your worker

A KV lookup maps @org/module to where the code lives, which is either a dispatch namespace (user modules, including yours) or a direct service binding (Odel's own built-in modules).

Your code runs

The proxy forwards the JSON-RPC call with the identity and secrets attached in params._meta. A V8 isolate spins up, your handler runs, and the response goes back the way it came.

Chat is a second entrance to the same building. Odel's chat writes TypeScript against your tools and executes it in an isolate, which calls the proxy over an internal binding rather than over HTTP. Your module can't tell the difference and doesn't need to.

The pieces

ServiceWhat it does
apex-apiAccounts, organizations, the marketplace catalogue, and the OAuth 2.1 authorization server that MCP clients register against
mcp-proxyThe public front door. Authenticates callers, resolves their config, routes to your code, and relays the response
MI6The keyring. The only service holding secret key material, reachable only over internal bindings, with no network egress of its own
dev-portalBuilds your repository in a container and deploys the result. Not in the request path
your moduleA Worker in a dispatch namespace, invoked per call

Where secrets are, and aren't

This is worth being precise about, because it's the part people assume wrongly.

Credential values live encrypted in MI6 and nowhere else. The proxy has no key material and cannot decrypt anything itself; it asks MI6 over a service binding and receives a value for one specific call. The user configuration rows the proxy reads hold addresses pointing into MI6, never ciphertext and never plaintext.

Your module receives a resolved string, per call, in the request envelope. It never learns whether that value was typed into a form or pointed at a vault item, and there is no way to ask.

What this means for your code

Assume nothing survives between calls. Isolates are created and discarded around individual requests. Module-level state might persist for a moment or vanish immediately, so build a fresh server per request and keep anything durable in a binding.

Read secrets every time. They're resolved per call, so a user who rotates a key is on the new value at their next request — unless your module cached the old one, in which case yours is the only stale copy in the system.

You don't handle authentication. By the time your handler runs, the caller is authenticated and their credentials are resolved. There is no token for you to validate and no key for you to issue.

Gateways take a different back door

A gateway uses the same front door and the same authentication, but instead of routing to a worker Odel hosts, it forwards to an MCP server running somewhere else — yours, or a third party's. Steps two and three are identical — the caller is authenticated the same way, and credentials still come from MI6. Step four is where it diverges: there is no KV lookup, because a /g/… address resolves straight from the gateway's own record. The call then leaves Odel's network entirely.

On this page