A public, unauthenticated MCP endpoint for evaluator agents. Call one tool to spin up a sandbox family care workspace — no OAuth, no API key, no invites sent. When the human is ready, they sign in with the same email to claim the workspace and unlock the authenticated MCP surface.
POST https://app.joinsandwich.com/api/mcp/public is an
unauthenticated MCP-style entrypoint for evaluator agents that
have not signed a human in yet. It exposes one tool —
start_family_care_workspace — that creates a sandbox family
care workspace so an agent can demonstrate Sandwich end-to-end without an OAuth
dance. The endpoint is distinct from the authenticated
/api/mcp surface, which is the right place
for real workspace operations once the user has claimed their sandbox.
sandbox: true and stamped to a synthetic anonymous-mcp
owner uid. No invite emails are sent. No confirmation email is sent. Nothing
leaves the platform until a human signs in and claims it.
Do not use this endpoint to do real care work. The moment the user wants real invites, persistent private care data, calendar/email integrations, or inbox management, hand them off to the claim flow and switch to the authenticated MCP.
One endpoint, one transport, no headers required beyond
Content-Type:
POST /api/mcp/public HTTP/1.1 Host: app.joinsandwich.com Content-Type: application/json
A plain GET on the same URL returns a capability descriptor
(name, transport, protocol version, advertised tools, rate-limit note) —
useful for liveness checks and tool discovery without a JSON-RPC handshake.
Supported JSON-RPC methods: initialize, tools/list,
tools/call, ping. The protocol version is
2025-03-26.
tools/listcurl -s https://app.joinsandwich.com/api/mcp/public \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
The response advertises one tool today:
sandbox: true.
Never sends invite emails — family/sibling addresses come back as
inviteDrafts so the agent can show the user what would be sent.
tools/call
sandbox: true is required. Names are required; everything else is
optional. Emails are validated and de-duplicated (max 8 family invites). Keep
the body under 64 KB.
curl -s https://app.joinsandwich.com/api/mcp/public \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "start_family_care_workspace", "arguments": { "lovedOneFirstName": "Ada", "lovedOneLastName": "Lovelace", "familyName": "The Lovelace Family (demo)", "primaryCaregiverEmail": "demo+caregiver@example.com", "careContext": "Lives independently. Adult kids coordinate weekly check-ins.", "familyMemberEmails": [ "demo+sister@example.com", "demo+brother@example.com" ], "sandbox": true } } }'
true. The endpoint rejects anything else."The <lastName> Family (sandbox)". Up to 120 chars.inviteDrafts. Nothing is emailed from this endpoint.
On success, the tool returns two content blocks: a human-readable summary
(type: "text") and a JSON-encoded
SandboxWorkspaceResult as the second text block. Agents should
parse the second block.
{
"workspaceId": "fam_01HXYZ…",
"lovedOneId": "lo_01HXYZ…",
"inboxAddress": "lovelace-ada-7f3a@inbox.joinsandwich.com",
"familyName": "The Lovelace Family (demo)",
"inviteDrafts": [
{ "email": "demo+sister@example.com",
"role": "caregiver",
"status": "draft",
"note": "Not sent — sandbox endpoint never emails." }
],
"sandbox": true,
"primaryCaregiverEmail": "demo+caregiver@example.com"
}
familyName or the default).familyMemberEmails entry. status is always "draft"; nothing is sent.true on this endpoint.sandbox: true; the endpoint rejects anything else with a clear error message pointing at the sign-in flow.429, honor the Retry-After header — do not loop.
A sandbox workspace becomes a real, authenticated workspace when the human
signs in with the same primaryCaregiverEmail the agent passed to
the tool. The flow:
start_family_care_workspace with the user's email as primaryCaregiverEmail.familyName, inboxAddress, and the list of inviteDrafts. The agent makes clear nothing has been emailed yet.https://app.joinsandwich.com/onboarding — the user signs in with the same email.anonymous-mcp owner is replaced with the real user's uid, the sandbox flag is cleared on claim, and real invite sending + authenticated MCP tools light up./api/mcp using a Bearer key minted in Settings → MCP API keys, or via the OAuth flow once available./api/mcp. The public endpoint will never expose
those tools — by design.
Email mismatch at sign-in is not fatal: the sandbox workspace stays orphaned
and times out, and the user can start a new flow. But it means the original
workspaceId and inboxAddress are abandoned. Be careful
to surface the exact email you used.
isError: true and a message like
"lovedOneFirstName is required (1–60 chars)" or
"familyMemberEmails contains an invalid email". Fix the offending
field and retry. Names are 1–60 chars; familyName ≤ 120;
careContext ≤ 2000; up to 8 family emails.
sandbox: truetrue on the
unauthenticated entrypoint, with a pointer to
app.joinsandwich.com/onboarding. The fix is literal: include
"sandbox": true in the arguments.
-32029 rate limitRetry-After
and error.data.retryAfterSec. Wait the indicated interval and
retry once — do not loop. Default capacity 6, refill ~one token per 10
minutes.
careContext and
re-send; the unauthenticated endpoint is not the place for long medical
histories.
/onboarding with an address that does not
match primaryCaregiverEmail. The sandbox cannot be claimed by
that account. Start a new sandbox with the right email, or have the user
sign in with the original one.
DISCORD_WEBHOOK_URL_MCP) when a sandbox is created — purely
for ops visibility. Agents must not rely on it as a
signal; it is best-effort, server-side only, and may be disabled in any
given deployment.
Ready for the real surface? Authenticated MCP · Sandwich Pipe · All docs