# Sake v0.4.3 - Table of Contents > MCP for Elixir and Phoenix: a server for both protocol eras and stdio, with tasks, MCP Apps, middleware and proxying; an OAuth 2.1 authorization server on your own login, or your own; and a client that authorizes itself. ## Pages - [Sake](readme.md) - [Changelog](changelog.md) - [Security policy](security-2.md) - Guides - [Quickstart](quickstart.md) - [Tools](tools.md) - [Tasks](tasks.md) - [MCP Apps](apps.md) - [Resources and prompts](resources_and_prompts.md) - [Authorization](authorization.md) - [Bring your own authorization server](external_auth.md) - [Multi-tenant endpoints](tenants.md) - [Middleware](middleware.md) - [Composing servers](composing.md) - [Shipping a server in a library](libraries.md) - [The client](client.md) - [Stdio](stdio.md) - [Observability](observability.md) - [Testing](testing.md) - Reference - [Protocol versions](protocol_versions.md) - [Security](security-1.md) - [Running on several nodes](clustering.md) ## Modules - [SakeDev.Server](SakeDev.Server.md): The example server of the development server and the tests. - Server - [Sake](Sake.md): Sake is MCP for Elixir and Phoenix. - [Sake.Content](Sake.Content.md): A content block of a tool result, a resource read, or a prompt message. - [Sake.Context](Sake.Context.md): What a component learns about the request it serves. - [Sake.Elicit](Sake.Elicit.md): Asks the person for input from inside a component. - [Sake.Input](Sake.Input.md): Asks the client several things at once. - [Sake.Middleware](Sake.Middleware.md): Host code around every MCP call: audit, rate limits, feature flags and plans, argument rewriting, redaction. A middleware is a module with `init/1` and `call/2`, as a Plug is, declared on the server for the events it handles - [Sake.Plug](Sake.Plug.md): The MCP endpoint. Mount it once per server - [Sake.Plug.Headers](Sake.Plug.Headers.md): The request headers that mirror the body on the modern Streamable HTTP transport: `Mcp-Method`, `Mcp-Name` (the tool or prompt name, the resource URI, or the task ID) and `Mcp-Param-*`, with the Base64 sentinel encoding for values that are not plain ASCII. - [Sake.Plug.Streams](Sake.Plug.Streams.md): The registry of open streams on this node: `subscriptions/listen` streams, legacy GET streams, and handlers waiting for a legacy client's answer to a server request (elicitation, sampling, roots). Started by the `Sake` supervisor. With a `pubsub:` option (a `Phoenix.PubSub` name) every node takes part: notifications reach the streams on every node, and a legacy session's subscriptions and answers reach its GET stream and its waiting handler on whichever node holds them, wherever the client's POST landed. - [Sake.Progress](Sake.Progress.md): Progress and log notifications from inside a component. - [Sake.Prompt](Sake.Prompt.md): A prompt template: `arg` lines (strings only) and `get/2`, which returns messages as `{:user | :assistant, text | %Sake.Content{}}`. `scope:` (one or many) gates it by the token's scopes, and `authentication:` (`[acr: ["mfa"], max_age: 300]`) by how the person signed in (see "Step-up authentication" in `Sake.Server`). - [Sake.Prompt.Spec](Sake.Prompt.Spec.md): The compiled definition of a prompt. - [Sake.Proxy](Sake.Proxy.md): A server whose tools, resources and prompts are another MCP server's: every call is forwarded to that upstream with `Sake.Client`, and the proxy adds what the upstream lacks, your login and scopes, middleware (access control, audit, rate limits), on one approved endpoint. - [Sake.Resource](Sake.Resource.md): A resource: a fixed `uri:` or a template with `{name}` placeholders, and `read/2`. A template resource may implement `list/1` to enumerate concrete resources. `subscribe: true` lets clients subscribe to updates, which the host announces with `Sake.Server.resource_updated/2`. `scope:` (one or many) gates it by the token's scopes, and `authentication:` (`[acr: ["mfa"], max_age: 300]`) by how the person signed in, for a read (see "Step-up authentication" in `Sake.Server`). - [Sake.Resource.Spec](Sake.Resource.Spec.md): The compiled definition of a resource. - [Sake.Roots](Sake.Roots.md): Asks the client for its roots — the directories or other locations it lets the server work in (`roots/list`). - [Sake.Router](Sake.Router.md): Router macros for Phoenix hosts - [Sake.Sampling](Sake.Sampling.md): Asks the client's model to complete a conversation from inside a component (`sampling/createMessage`). - [Sake.Sampling.Result](Sake.Sampling.Result.md): The client's answer to `Sake.Sampling.create_message/4`: the `role` of the message (`"assistant"`), its `content` as `Sake.Content` blocks, `text` (every text block joined), the `model` that produced it, the `stop_reason`, and the `raw` result map, which also holds content Sake has no struct for (such as tool use). - [Sake.Server](Sake.Server.md): The server module lists the components and their scopes. - [Sake.Stdio](Sake.Stdio.md): Serves a `Sake.Server` over standard input and output, for a client that launches the server as a subprocess - [Sake.Tool](Sake.Tool.md): A tool: `use Sake.Tool` with options, `arg` lines, an optional `output` block, and `execute/2`. - [Sake.Tool.Spec](Sake.Tool.Spec.md): The compiled definition of a tool. - [Sake.UI](Sake.UI.md): An MCP App: an HTML document the host renders in a sandboxed iframe when a tool that links it is called (the `io.modelcontextprotocol/ui` extension). On the wire it is a resource with a `ui://` URI and the `text/html;profile=mcp-app` MIME type, so it is listed on a server with the other resources - Tasks - [Sake.Tasks](Sake.Tasks.md): Tools that run as tasks: the `io.modelcontextprotocol/tasks` extension of the modern protocol. - [Sake.Tasks.Migration](Sake.Tasks.Migration.md): The table of `Sake.Tasks.Store.Ecto`. In your app - [Sake.Tasks.Record](Sake.Tasks.Record.md): A task as a `Sake.Tasks.Store` keeps it. - [Sake.Tasks.Store](Sake.Tasks.Store.md): Where tasks live between the request that creates them and the requests that poll, answer or cancel them. Implemented by `Sake.Tasks.Store.ETS` (one node) and `Sake.Tasks.Store.Ecto` (any number of nodes). - [Sake.Tasks.Store.ETS](Sake.Tasks.Store.ETS.md): The default task store: in memory, on one node. Tasks are lost on restart, and a task created on one node is unknown to the others; use `Sake.Tasks.Store.Ecto` when the endpoint runs on several nodes. - [Sake.Tasks.Store.Ecto](Sake.Tasks.Store.Ecto.md): The task store for an endpoint on several nodes: `tasks: [store: {Sake.Tasks.Store.Ecto, repo: MyApp.Repo}]` with the table from `Sake.Tasks.Migration`. Every node reads and writes the same rows, so any node serves any task's `tasks/get`, `tasks/update` and `tasks/cancel`. Tested on Postgres. - Authorization - [Sake.Auth](Sake.Auth.md): Authorization for a Sake endpoint: the host app is the OAuth 2.1 authorization server, and the endpoint is the resource server. - [Sake.Auth.Authorize](Sake.Auth.Authorize.md): The authorization endpoint: `GET` validates the request and shows the consent page (or redirects at once when consent is remembered); `POST` records the decision. Mount it inside the pipeline that requires a signed-in person; Sake reads the person through `Sake.Auth.Subject`. - [Sake.Auth.Challenge](Sake.Auth.Challenge.md): An authorization challenge: what a `WWW-Authenticate` header says, on either side of a request. - [Sake.Auth.Client](Sake.Auth.Client.md): An OAuth client: from a Client ID Metadata Document, dynamically registered, or registered by the host. - [Sake.Auth.Clients](Sake.Auth.Clients.md): Clients the host registers itself (for its own CLIs or partners). - [Sake.Auth.Code](Sake.Auth.Code.md): An authorization code (stored hashed). `dpop_jkt` is the thumbprint of the DPoP key the code is bound to (RFC 9449 §10), or nil: the token request that redeems a bound code must carry a proof made with that key. `acr` and `auth_time` are how and when the person signed in when they approved it (`c:Sake.Auth.Subject.authentication/1`), or nil, which its tokens carry (RFC 9470). `nonce` is the authorization request's OpenID Connect `nonce`, or nil, which the ID token issued for the code carries. - [Sake.Auth.Config](Sake.Auth.Config.md): The authorization configuration given to `{Sake, auth: [...]}`. - [Sake.Auth.Consent](Sake.Auth.Consent.md): Renders the consent page and the authorization error page. The default, `Sake.Auth.Consent.Default`, ships its own small style sheet. A host replaces it with `consent: MyAppWeb.MCPConsent` in the auth config. - [Sake.Auth.Consent.Default](Sake.Auth.Consent.Default.md): The consent, code entry and error pages that ship with Sake. - [Sake.Auth.DeviceGrant](Sake.Auth.DeviceGrant.md): A device authorization grant (RFC 8628), stored hashed: `hash` is the device code's, `user_code_hash` the user code's. `status` is `:pending` until the person decides, then `:approved` (with `subject_id` and the `scopes` they granted) or `:denied`, and `:issued` once the client has its tokens. `last_polled_at` is when the client last asked the token endpoint, against `interval`. - [Sake.Auth.DeviceVerification](Sake.Auth.DeviceVerification.md): The device verification page (RFC 8628 §3.3), at `{oauth_path}/device`: where a person enters the user code a CLI or a device shows them, then approves or denies its request on the consent page. Mount it, like `Sake.Auth.Authorize`, inside the pipeline that requires a signed-in person; Sake reads the person through `Sake.Auth.Subject`. It answers 404 without `device:` in the auth config. - [Sake.Auth.External](Sake.Auth.External.md): An endpoint whose tokens come from an authorization server Sake does not run: your app's own OAuth provider (ExOauth2Provider, Boruta, a hand-rolled one) or an external identity provider (Keycloak, Auth0, Okta, Entra ID, Cognito, WorkOS). Sake is then only the resource server: it checks the tokens that server issues and enforces the scopes. - [Sake.Auth.Migration](Sake.Auth.Migration.md): The tables of `Sake.Auth.Store.Ecto`. In your app - [Sake.Auth.PushedRequest](Sake.Auth.PushedRequest.md): A pushed authorization request (RFC 9126), stored by the hash of its `request_uri` until the authorize endpoint consumes it, once, or it expires. The other fields are the validated request's: `extra` holds the parameters a later feature adds (`"dpop_jkt"`, the thumbprint of the DPoP key the code will be bound to), with string keys and JSON values, so a store can keep it as JSON. It holds `"acr_values"` and `"max_age"` too, when the client asks how the person must have signed in (RFC 9470). - [Sake.Auth.RouterPlug](Sake.Auth.RouterPlug.md): The token, registration, revocation, device authorization and pushed authorization request endpoints, and with `openid:` the JWKS and userinfo endpoints, as one Plug router for hosts without Phoenix - [Sake.Auth.Store](Sake.Auth.Store.md): Where clients, codes, pushed authorization requests, device grants, tokens and consents live. Implemented by `Sake.Auth.Store.ETS` and `Sake.Auth.Store.Ecto`. - [Sake.Auth.Store.ETS](Sake.Auth.Store.ETS.md): An in-memory store for development and tests on one node. Everything is lost on restart. - [Sake.Auth.Store.Ecto](Sake.Auth.Store.Ecto.md): The Ecto store: `store: {Sake.Auth.Store.Ecto, repo: MyApp.Repo}` with the tables from `Sake.Auth.Migration`. Tested on Postgres. - [Sake.Auth.Subject](Sake.Auth.Subject.md): How Sake finds the person behind a request. The host implements it - [Sake.Auth.TokenRecord](Sake.Auth.TokenRecord.md): An access or refresh token (stored hashed). `family_id` groups the tokens born from one authorization code. `jkt` is the RFC 7638 thumbprint of the DPoP key the token is bound to (its `cnf.jkt`), or nil for a bearer token. `acr` and `auth_time` are how and when the person signed in to approve the grant (RFC 9470), or nil when unknown (and always for a machine client); a refresh keeps them, since it is no new sign-in. - [Sake.Auth.TrustedIssuer](Sake.Auth.TrustedIssuer.md): An issuer whose signed JWTs the token endpoint accepts as authorization grants (`urn:ietf:params:oauth:grant-type:jwt-bearer`, RFC 7523). Give them to `{Sake, auth: [trusted_issuers: [...]]}` - [Sake.Auth.WellKnown](Sake.Auth.WellKnown.md): Serves `/.well-known/oauth-protected-resource` (also under the endpoint's path) and `/.well-known/oauth-authorization-server`. The protected resource metadata of an endpoint on an external authorization server (`Sake.Auth.External`) names that server; the authorization server metadata is only `Sake.Auth`'s, and so is `/.well-known/openid-configuration`, OpenID Connect Discovery's, which is there with `openid:` alone. Mount at the root - Client - [Sake.Client](Sake.Client.md): A client for MCP servers over Streamable HTTP or stdio, on both protocol eras. - [Sake.Client.Auth](Sake.Client.Auth.md): The MCP authorization flow, run by `Sake.Client` itself. Turn it on with `auth:` on `Sake.Client.connect/2` - [Sake.Client.Auth.Browser](Sake.Client.Auth.Browser.md): Authorizes in the person's browser: opens the authorization URL with the system's opener (`open` on macOS, `xdg-open` elsewhere on Unix, `rundll32` on Windows) and waits for the redirect on a one-shot HTTP listener at the redirect URI's loopback address and port (RFC 8252 §7.3). The default in `Sake.Client.Auth`. - [Sake.Client.Auth.Device](Sake.Client.Auth.Device.md): The interactive step of the device authorization grant (RFC 8628), `grant: :device` in `Sake.Client.Auth`: the client shows the person a user code and where to enter it, on any device, and polls the token endpoint until they decide there. - [Sake.Client.Auth.Headless](Sake.Client.Auth.Headless.md): Authorizes without a person: GETs the authorization URL, does not follow the redirect it answers with, and reads the authorization response from its `Location` header. - [Sake.Client.Auth.Store](Sake.Client.Auth.Store.md): Keeps a client's tokens and dynamic registrations across sessions. Pass `store: {module, arg}` in `Sake.Client.connect/2`'s `auth:` options; `arg` is handed to every callback. - [Sake.Client.Session](Sake.Client.Session.md): The state of a client connection; every `Sake.Client` call returns an updated one. - [Sake.Client.ToolResult](Sake.Client.ToolResult.md): The result of `Sake.Client.call_tool/3`. - Testing - [Sake.Test](Sake.Test.md): Tests for a Sake server: a client connected to the server in memory, the MCP counterpart of `Phoenix.ConnTest`. - [Sake.Test.Client](Sake.Test.Client.md): A client of `Sake.Test`, from `Sake.Test.connect/2`. - Protocol - [Sake.JSONRPC](Sake.JSONRPC.md): JSON-RPC 2.0 messages as MCP uses them: requests with a string or integer id, notifications without one, results, errors, and batches (legacy revision 2025-03-26 only). - [Sake.Protocol](Sake.Protocol.md): The protocol revisions Sake speaks, the `_meta` keys the protocol reserves, and the identifiers of the authorization extensions. - [Sake.Schema](Sake.Schema.md): Builds JSON Schema 2020-12 from `arg` declarations and validates arguments against it. - [Sake.Server.Session](Sake.Server.Session.md): Legacy session IDs are signed tokens that carry the negotiated revision, the client capabilities, the log level `logging/setLevel` last set, and a random nonce. The server stores nothing per session. ## Mix Tasks - [mix sake.stdio](Mix.Tasks.Sake.Stdio.md): Serves a `Sake.Server` module over standard input and output, for a client that launches the server as a subprocess