MCP Server
Connect AI assistants to Coach Watts via the remote Model Context Protocol (MCP) server with OAuth 2.0 and PKCE.
Coach Watts exposes a remote MCP server so tools like Cursor can read and write your training data on your behalf. The endpoint uses OAuth 2.0 with PKCE and the same permission scopes as the REST API.
Endpoint
Remote MCP URL:
https://coachwatts.com/mcp
For local development:
http://localhost:3099/mcp
Quick start (Cursor)
Dynamic client registration (DCR) is enabled by default. Add the MCP URL to your mcp.json:
{
"mcpServers": {
"coach-watts": {
"url": "https://coachwatts.com/mcp"
}
}
}
Cursor registers via OAuth automatically. When you connect, approve the requested scopes on the consent screen.
OAuth discovery
MCP clients can discover authorization metadata at:
/.well-known/oauth-authorization-server/.well-known/oauth-protected-resource/.well-known/oauth-protected-resource/mcp
Authorization requests must include:
resource={siteUrl}/mcp(exact MCP resource URL)- PKCE with
code_challenge_method=S256 - Scopes from the MCP allowlist (profile, workout, health, nutrition, planning, analysis, memory, and recommendations scopes)
Tool phases
Tools are grouped into read, write, and async phases. All phases are enabled by default in production.
| Phase | Default | Examples |
|---|---|---|
| Read | On | List workouts, read profile, fetch wellness |
| Write | On | Log nutrition, update profile, create planned workouts |
| Async | On | generate_report, sync_data, structure generation |
Server operators can disable phases with environment variables:
| Variable | Default | Effect |
|---|---|---|
NUXT_MCP_READ_ENABLED | true | Expose read tools |
NUXT_MCP_WRITE_ENABLED | true | Expose write tools |
NUXT_MCP_ASYNC_ENABLED | true | Expose async/AI tools |
NUXT_MCP_EXECUTION_ENABLED | true | Kill switch for tool execution |
NUXT_MCP_CLIENT_ALLOWLIST | empty | Comma-separated OAuth client IDs |
NUXT_MCP_DCR_ENABLED | true | Allow MCP clients to register via OAuth DCR |
Write and async mutating tools support idempotency via the Mcp-Idempotency-Key request header.
Async jobs
Async tools return identifiers such as generation_run_id, report_id, or run_id. Poll completion with the get_async_job_status tool:
{
"job_type": "structure_generation",
"job_id": "<generation_run_id>"
}
Supported job_type values: structure_generation, report, trigger_run.
Client registration
When DCR is enabled, Cursor and other MCP clients connect with only the MCP URL. Compatible redirect URIs reuse the shared Cursor MCP OAuth app instead of creating a new client each time.
DCR owner resolution order:
NUXT_MCP_DCR_OWNER_USER_IDNUXT_MCP_DCR_OWNER_EMAIL- First admin user in the database
To disable DCR, set NUXT_MCP_DCR_ENABLED=false. A static auth.CLIENT_ID in mcp.json is only needed when DCR is disabled.
Manual bootstrap (self-hosted)
Pre-create the shared Cursor app explicitly:
pnpm cw:cli oauth create-cursor-mcp-app --owner-email [email protected]
pnpm cw:cli oauth create-cursor-mcp-app --owner-email [email protected] --prod
Local development
MCP is enabled by default locally. Set NUXT_MCP_ENABLED=false to disable the endpoint, or NUXT_MCP_WRITE_ENABLED=false / NUXT_MCP_ASYNC_ENABLED=false to turn off individual phases.
export NUXT_PUBLIC_SITE_URL=http://localhost:3099
Apply database migrations and run MCP-focused unit tests:
pnpm db:deploy
pnpm test:mcp
Operations
- Manifest drift is logged at startup when MCP is enabled.
- Admin dashboard data:
GET /api/admin/mcp/stats(execution summary, latency, error codes, DCR counts). - Revoke MCP access any time from Settings → Apps.
Troubleshooting
401 on /mcp
- Confirm the token was issued with
resource={siteUrl}/mcp. REST developer tokens without a resource are rejected. - Check token expiry and that the user account is active.
- Inspect the
WWW-Authenticatechallenge for the protected-resource metadata URL.
OAuth authorization fails
- MCP clients must send PKCE with
code_challenge_method=S256. - Requested scopes must be in the MCP allowlist.
- Redirect URIs must match exactly (HTTPS required except loopback).
Tool missing from tools/list
- Verify the tool phase flag (
NUXT_MCP_WRITE_ENABLED,NUXT_MCP_ASYNC_ENABLED). - Confirm the token includes the required scopes.
- Nutrition and feature-gated tools only appear when enabled for the user.
- Chat-disabled async tools (
sync_data,generate_report) require the async phase flag.
Async job stuck
- Call
get_async_job_statuswith the id returned by the async tool. - For structure generation use
job_type=structure_generationandgeneration_run_id. - For reports use
job_type=reportandreport_id. - For Trigger.dev-backed sync use
job_type=trigger_runandrun_id.
Security notes
- MCP tokens are bound to the exact
/mcpresource URL. - REST developer API tokens without a resource cannot call
/mcp. - Refresh token rotation is enforced for MCP resource tokens; reuse revokes the grant chain.
