Bearer keys. Stateless JSON-RPC. Family-scope read tools with an append-only audit log. A caregiver mints a key in the Sandwich app, pastes it into Claude Desktop or Cursor, and the assistant can immediately query their loved ones and recent care events — scoped to their own workspace, nothing more.
The Sandwich MCP server exposes a small, opinionated toolbelt to any AI assistant that speaks the Model Context Protocol. A caregiver mints a key in the Sandwich app, pastes it into Claude Desktop, Cursor, or a custom agent, and the assistant can immediately query their loved ones and recent care events — scoped to their own workspace, nothing more.
Sandwich publishes two servers to the
official MCP Registry
under the com.joinsandwich namespace (DNS-verified on joinsandwich.com):
app.joinsandwich.com/api/mcpwww.joinsandwich.com/api/mcpBrowse all matching listings: registry.modelcontextprotocol.io — search com.joinsandwich. Any MCP client that reads the registry can install Sandwich by name.
Sandwich supports two ways to authenticate external clients. Both reach the same tools and enforce the same per-user ownership; they differ in how the user grants access.
Best for Claude Desktop, Cursor, Codex CLI, and other clients you run yourself with a
long-lived config file. Sign into
app.joinsandwich.com, open
Settings → MCP API keys, mint a key, copy it once (the plaintext is
shown one time, then only its SHA-256 hash is kept), paste it into your client’s
config. Keys look like sk-sand-<random>. Revoke any time from the same page;
the next request after revocation is rejected.
Authorization: Bearer sk-sand-4c2fa8e1b7d9…
Best for hosted assistants such as Claude.ai custom connectors and any third-party app that wants to onboard caregivers without asking them to copy-paste a secret. The client registers itself via Dynamic Client Registration, redirects the user to Sandwich’s consent screen, and receives a short-lived access token plus a rotating refresh token.
S256, Dynamic Client Registration (RFC 7591), Token Revocation (RFC 7009), Authorization Server Metadata (RFC 8414), Protected Resource Metadata (RFC 9728).profile:read (name, email, workspace) and
care_circle:read (list loved ones and read recent care events). Users approve scopes individually on the consent screen.Discovery URLs an OAuth client uses automatically:
/.well-known/oauth-authorization-server/.well-known/oauth-protected-resource/.well-known/jwks.json/oauth/authorize · /oauth/token · /oauth/register · /oauth/revoke
In-app agents running on app.joinsandwich.com or
soft.joinsandwich.com use the signed-in session automatically — no key,
no OAuth round-trip. This page covers external clients.
claude_desktop_config.json) or, once the connector is approved, via the in-app connector flow.https://app.joinsandwich.com/api/mcp in Settings → Connectors → Add custom connector. Claude auto-detects OAuth, walks the consent screen, and connects. Requires a Claude Pro, Max, Team, or Enterprise plan.~/.codex/config.toml — see the snippet at the bottom of this page.2025-03-26. Both auth paths above are RFC-standard so any spec-compliant client should connect.
One endpoint: POST https://app.joinsandwich.com/api/mcp. Standard MCP JSON-RPC 2.0
envelopes in the body, a single JSON-RPC response in the reply. Each POST is stateless —
no session handshake between requests — which keeps the server compatible with Vercel's
serverless routing and matches the
Streamable HTTP
transport. Server-initiated streaming is a roadmap item; v1 does not open long-lived
connections.
POST /api/mcp HTTP/1.1 Host: app.joinsandwich.com Authorization: Bearer sk-sand-… Content-Type: application/json {"jsonrpc":"2.0","id":1,"method":"tools/call", "params":{"name":"list_care_events", "arguments":{"limit":10}}}
Supported JSON-RPC methods:
initialize — server info and capabilities handshake.notifications/initialized — accepted and ignored as a JSON-RPC notification.tools/list — discover the current tool registry.tools/call — invoke a tool by name with { name, arguments }.ping — protocol-level liveness probe (distinct from the ping tool).pong plus the verified caller’s uid, tenant, and auth method. No scope required. No side effects.profile:read.care_circle:read.lovedOneId is given, otherwise aggregated across the workspace. limit is 1–50, applied per loved one when aggregating. Scope: care_circle:read.
Every tool re-derives ownership from the verified caller. Passing a lovedOneId
that belongs to a different caregiver’s workspace returns a clean
not authorized error. Bearer-key callers are not scope-gated today (the key grants
full read access to the owning caregiver’s data). OAuth callers see only the scopes
they approved on the consent screen.
Today, the MCP can:
Today, the MCP cannot:
Retry-After and JSON-RPC error code -32029.
Every authenticated, parsed MCP request writes one row to the internal mcp_audit_log with the verified
uid, tenant, method, tool name, success flag, and duration. Tool arguments and
tool results are not stored — sensitive data stays in the request and in short-term
Vercel / Cloud Run logs. Sandwich handles PHI under a BAA; agents that surface care events
to their own end users are responsible for their own BAA posture downstream.
Retry-After.error.message.Sandwich. URL: https://app.joinsandwich.com/api/mcp.Requires a Claude Pro, Max, Team, or Enterprise plan. No copy-pasting secrets.
{
"mcpServers": {
"sandwich": {
"url": "https://app.joinsandwich.com/api/mcp",
"headers": { "Authorization": "Bearer sk-sand-…" }
}
}
}
In Cursor settings, add a new MCP server with the same URL and Authorization header.
# Expect: "pong from sandwich platform — uid=… tenant=… via=api-key" curl -s https://app.joinsandwich.com/api/mcp \ -H "Authorization: Bearer sk-sand-…" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ping","arguments":{}}}'
# Sandwich MCP — family-scope read tools [mcp_servers.sandwich] url = "https://app.joinsandwich.com/api/mcp" auth_header = "Bearer sk-sand-…"
Looking for the rest of the stack? Sandwich Pipe · Sandwich Soft · All docs