MCP server — QA plan
Automated
make test-unit-api covers apps/api/src/mcp/*.unit.spec.ts, apps/api/src/ai/agent/tools/leaves/workouts.tools.unit.spec.ts and apps/api/src/ai/agent/context-builder.unit.spec.ts (see code map). make test-e2e-api runs apps/api/test/e2e/day-grain-coaching.e2e.spec.ts (also Spotter’s tool list and prompt), day-grain-parse.e2e.spec.ts (replayed parses: titles, one-call confirm-and-save) and session-grain-coaching.e2e.spec.ts (integrator flow) over the real POST /mcp.
Manual
curl -i -X POST <api>/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'→401with aWWW-Authenticateheader namingresource_metadata.curl <api>/.well-known/oauth-protected-resource/mcp→authorization_serversis the Clerk Frontend API for this environment.- Add the server as a Claude custom connector → OAuth sign-in via Clerk completes and tools appear.
- As a multi-org owner without
?orgId=→organization_requiredlisting both orgs. With?orgId=→ tools listed. - As a coach → no owner/admin-only tools (e.g. revenue analytics) are listed.
- Ask the client to search members, then create a task → task exists in the app, and an audit log row
task.createis attributed to the user. - Ask the client to delete a workout → it reports the tool is unavailable; nothing is deleted.
- As a member-only user →
403.
Day grain
initializereturns the day-grain instructions;workouts_upsertandworkouts_parseare listed;workouts_create,workouts_set_sectionsandprograms_session_createcarry the day-grain descriptions.- Paste a real coach day into Claude (brief + Warm-up/Lift/Strength/Conditioning/Optional Successory). It sends the text to
workouts_parsewith the date andsave, confirms anyunresolvedmovements in ONE moreworkouts_parsecall withsave.resolutions, and makes ONEprograms_session_createfor the date. In the app the day is one workout named for its date and program (never its Hebrew brief) on the web list and in the mobile athlete view: the brief on top, parts with their clock windows, notes and optional badge, the conditioning score slot only. - Send a Back Squat loaded
percent_1rmunder “Percentages off your Back Squat 3RM” → the write succeeds and returns apercent_base_mismatchwarning. - In PostHog,
mcp.tool.executedfor those calls carriessectionCount,partCount,slotCount,warningsCount(andmcpSessionIdwhen the client sends one) and no coach text. - Paste the same coach day into Spotter in the app → it calls
workouts.parse(save + date), confirms suggestions withsave.resolutions(asking with a picker when several candidates fit), tells you each warning, and publishes once; asking it to hand-writesourceTextis refused. scripts/mcp-programming-sync.mjs call workouts_parse '{...}'on a long day completes (default 180s tool-call timeout);TAIKAN_MCP_TIMEOUT_MS=5000makes the same call fail fast with an abort.