You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.0 KiB
Markdown
124 lines
3.0 KiB
Markdown
# 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`
|