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.

3.0 KiB

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:

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:

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:

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