Zum Hauptinhalt springen

MCP-Authentifizierung

Der MCP Server spricht OAuth 2.1 mit PKCE über einen HTTP-Transport (Streamable HTTP). Er ist stateless: keine In-Memory-Session-Map, keine Sticky Sessions, kein Per-Client-Transport-State. Der State liegt in Firestore, der Client trägt ein Bearer-JWT.

Workspace Gate

Nur Google-Identitäten aus der cloudpilots.com-Workspace dürfen sich einloggen. Das Gate hängt am hd-Claim (Hosted Domain) des Google-ID- Tokens und wird gegen MCP_ALLOWED_DOMAIN geprüft (Default cloudpilots.com). Ein Email-Suffix-Check kommt bewusst nicht zum Einsatz — Service-Account-Tokens haben keinen hd-Claim und werden korrekt abgelehnt.

Token-Form

  • Access Token: RS256 JWT, 15 Minuten TTL. iss und aud zeigen beide auf MCP_PUBLIC_URL. Public Keys liegen unter /.well-known/jwks.json (1 h Cache auf Verifier-Seite).
  • Refresh Token: opak, 30 Tage TTL mit einem 30-Sekunden-Rotation- Grace-Window (das expiresAt des alten Tokens wird beim Rotate geschrumpft, nicht gelöscht — damit ein gleichzeitig fliegender Request nicht abschmiert).
  • Resource Indicator (RFC 8707): jeder /authorize- und /token- Request muss resource=<MCP_PUBLIC_URL> mitschicken. Ein Trailing Slash wird toleriert.

Discovery-Endpoints

Alle direkt unter der Server-Root:

EndpointLiefert
/.well-known/oauth-authorization-serverRFC-8414-Metadata (authorization_endpoint, etc.)
/.well-known/oauth-protected-resourceRFC-9728-Resource-Metadata
/.well-known/jwks.jsonAktive Signing-Keys (RS256)

Audit Log

Jeder Tool-Aufruf schreibt eine strukturierte JSON-Zeile nach stdout mit channel: "mcp_audit". Drin stehen uid, clientId, Tool-Name, Start / Ende, Dauer, Success / Failure und bei Erfolg die Pipeline-IDs. Was nicht drin steht: User-Prompt-Inhalte — die wären PII, und ein Routine-Log ist nicht der richtige Ort dafür.

Quoten

Per-uid Counter in Firestore (usage/${uid}_${YYYY-MM-DD}), pro Tool-Call in einer Firestore-Transaktion inkrementiert.

LimitDefaultEnv Var
Stündliche Tool-Calls60MCP_HOURLY_QUOTA
Tägliche Tool-Calls300MCP_DAILY_QUOTA

Ein Tool-Call = eine Einheit. generate_image_async mit iterations: 4 zählt weiterhin nur als 1 Einheit — Iterationen multiplizieren die Quote nicht. Gezählt werden propose_concepts, generate_image und generate_image_async; get_image ist frei (read-only).

Quota-Errors kommen als Tool-Errors zurück, z. B. Daily generation limit (300) exceeded. Try again tomorrow. Der Claude-Client sollte sie dem User anzeigen, statt still im Hintergrund nachzuziehen.