# MCP Runbook ## Scope This runbook covers the MCP adapter exposed by `app.mcp_main`. ## Endpoint - Streamable HTTP endpoint: `POST /mcp` (mounted under `app.mcp_main`) When using Docker Compose: - Base URL: `http://127.0.0.1:8001/mcp` ## Runtime modes Local: ```bash uv run uvicorn app.mcp_main:app --host 0.0.0.0 --port 8001 ``` Docker Compose service: - `personal-agent-mcp` ## Tool surface Always enabled: - `check_availability` Optional mutation tools (disabled by default): - `scan_mailbox` - `list_unsubscribe_candidates` - `execute_unsubscribe` Enable optional tools with: ```bash MCP_ENABLE_MUTATION_TOOLS=true ``` ## Authorization and scope gates MCP tools call the shared auth backend and read auth headers from request context. MCP auth mode resolution: - `MCP_AUTH_MODE=inherit` (default): use `AUTH_MODE` - `MCP_AUTH_MODE=api_key|jwt|hybrid|oauth`: override only for MCP Supported auth headers: - `X-API-Key` - `Authorization: Bearer ...` For `MCP_AUTH_MODE=oauth`, bearer tokens are validated via OAuth token introspection: - `MCP_RESOURCE_SERVER_URL` (required canonical MCP URL, for RFC 9728 metadata/challenges) - `MCP_OAUTH_INTROSPECTION_URL` (required) - `MCP_OAUTH_CLIENT_ID` / `MCP_OAUTH_CLIENT_SECRET` (optional; use for introspection endpoint auth) - `MCP_OAUTH_ISSUER` (optional strict `iss` match) - `MCP_OAUTH_AUDIENCE` (optional required audience value) - `MCP_OAUTH_TIMEOUT_SECONDS` (default `8`) When OAuth mode is enabled, FastMCP exposes protected resource metadata at: - `/.well-known/oauth-protected-resource/mcp` and returns OAuth-compliant `WWW-Authenticate: Bearer ...` challenges for unauthenticated requests. Required scopes: - `check_availability`: `availability:read` - `scan_mailbox`: `mail:scan` - `list_unsubscribe_candidates`: `unsubscribe:read` - `execute_unsubscribe`: `unsubscribe:execute` ## Tool verification Verify tool list from Python: ```bash uv run python - <<'PY' import asyncio from app.mcp.server import mcp async def main(): tools = await mcp.list_tools() print([t.name for t in tools]) asyncio.run(main()) PY ``` Expected output by mode: - default: `['check_availability']` - with `MCP_ENABLE_MUTATION_TOOLS=true`: all four tools ## Protocol notes - The server uses FastMCP Streamable HTTP. - Basic GET to `/mcp` is not a health endpoint; MCP expects protocol-compliant requests. - In local development, FastMCP may enforce host/origin checks. If you see `421 Misdirected Request`, verify host/port and reverse-proxy headers. ## Troubleshooting If tools fail with auth errors: - Check `AUTH_MODE` and credentials - Check `MCP_AUTH_MODE` override and related `MCP_OAUTH_*` variables - Verify `MCP_RESOURCE_SERVER_URL` matches the public MCP endpoint (for example `https://mcp.example.com/mcp`) - Confirm JWT contains required scopes - For API key mode, verify `AGENT_API_KEY` If tool calls fail with Google errors: - Verify OAuth file mounts in Docker: - `GOOGLE_CLIENT_SECRETS_FILE` - `GOOGLE_TOKEN_FILE`