Skip to main content
Glama
beaconfire-projects

mcp-oauth-test

FastMCP OIDC Server

Dies ist ein MCP-Server, der mit FastMCP geschrieben und durch OIDC-Login geschützt ist. Er verwendet den OIDCProxy von FastMCP: Der MCP-Client authentifiziert sich über die vom Server bereitgestellten OAuth-Metadaten, während der eigentliche Login und Token-Austausch an den QA-OIDC-Provider weitergeleitet werden.

Derzeit ist die QA MGT OpenAPI angebunden, die MCP-Tools für Trainee, Bestellungen, Produkte, Kunden, Campus-Recruiting und internationale Rekrutierung generiert.

Die OIDC-Discovery-Adresse ist standardmäßig wie folgt konfiguriert:

https://auth-qa.drillinsight.com/.well-known/openid-configuration

Authentifizierungsvorbereitung

Registrieren Sie zuerst eine OAuth-Anwendung unter auth-qa.drillinsight.com und fügen Sie die folgende Callback-Adresse zur Whitelist hinzu:

http://localhost:8000/auth/callback

Wenn Sie auf einer anderen Adresse bereitstellen, ersetzen Sie http://localhost:8000 durch den Wert von BASE_URL. Die Callback-Adresse muss exakt mit dem BASE_URL von FastMCP übereinstimmen.

Lokale Ausführung

cp .env.example .env
# 编辑 .env,至少填写 OIDC_CLIENT_ID 和 OIDC_CLIENT_SECRET
uv sync
uv run mcp-oidc-server

Sie können das Modul auch direkt ausführen:

uv run python -m oidc_mcp_server.server

Der Dienst lauscht standardmäßig auf http://127.0.0.1:8000. Wenn der MCP-Client auf einer anderen Maschine oder in einem Container läuft, setzen Sie eine für den Client erreichbare BASE_URL und eine geeignete HOST-Adresse (z. B. 0.0.0.0).

Claude Code Plugin

Das Repository enthält einen privaten Claude-Code-Marketplace und ein MCP-Plugin:

.claude-plugin/marketplace.json
└── plugins/mcp-oauth-test/
    ├── .claude-plugin/plugin.json
    ├── .mcp.json
    └── README.md

Das Plugin verbindet Claude Code lediglich mit dem bereits bereitgestellten Remote-MCP-Dienst und startet keinen lokalen Python-Dienst. Für Entwicklungs- und Testzwecke kann es direkt geladen werden:

claude --plugin-dir ./plugins/mcp-oauth-test

Das Plugin ist fest mit dem MCP-Server der Entwicklungsumgebung verbunden:

https://api-mcp-oauth-dev.beaconfireinc.com/mcp

Es kann auch über den privaten Marketplace installiert werden:

/plugin marketplace add /path/to/mcp-oauth-test
/plugin install mcp-oauth-test@authsome-internal

Das aktuelle Marketplace-Root-Verzeichnis ist das Repository-Root. Bitte halten Sie diesen Marketplace in einem privaten GitHub-Repository des Unternehmens und übermitteln Sie ihn nicht an einen öffentlichen Marketplace. Für gemeinsame Umgebungen sollten Sie HTTPS-Adressen verwenden und den Zugriff auf Unternehmensbenutzer auf der Seite des IdP und des MCP-Servers beschränken.

Warum OIDCProxy

Der Upstream auth-qa.drillinsight.com muss kein DCR oder CIMD unterstützen. OIDCProxy ist genau für dieses Szenario gedacht:

ChatGPT ── MCP OAuth / CIMD ──> FastMCP OIDCProxy
                                      │
                                      └── 固定 client_id/client_secret ──> auth-qa.drillinsight.com

Im Upstream muss nur die OAuth-Anwendung FastMCP vorab registriert werden, und ${BASE_URL}/auth/callback muss konfiguriert werden. Das von ChatGPT verwendete CIMD wird von der FastMCP-Proxy-Schicht verarbeitet und nicht an den Upstream-OAuth-Server weitergeleitet.

ChatGPT CIMD-Konfiguration

Wählen Sie beim Erstellen eines benutzerdefinierten MCP in ChatGPT unter den erweiterten OAuth-Einstellungen für „Client-Registrierung“ die folgende Option:

客户端标识元数据文档(CIMD)

Die vom aktuellen ChatGPT-Connector generierten Informationen sind:

CIMD Client ID / 客户端元数据 URL:
https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json

ChatGPT Callback URL:
https://chatgpt.com/connector/oauth/0Buhw3sHVv1-

Die CIMD-URL selbst ist die client_id, die ChatGPT beim Zugriff auf den FastMCP-OAuth-Proxy verwendet. Sie muss nicht und sollte nicht beim Upstream auth-qa.drillinsight.com registriert werden.

Dieses Projekt enthält zwei verschiedene Ebenen von OAuth-Client-IDs:

OAuth-Kette

client_id

Konfigurationsort

ChatGPT → FastMCP OIDCProxy

https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json

Von ChatGPT automatisch bereitgestellt; nach Auswahl von CIMD keine manuelle Eingabe erforderlich

FastMCP OIDCProxy → auth-qa.drillinsight.com

app_74a4b555-5b87-4212-9dda-d584fa78caf8

OIDC_CLIENT_ID des MCP-Servers

Der entsprechende Datenfluss ist:

ChatGPT
  │ client_id=https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json
  ▼
FastMCP OIDCProxy
  │ client_id=app_74a4b555-5b87-4212-9dda-d584fa78caf8
  ▼
auth-qa.drillinsight.com

Die Umgebungsvariablen des MCP-Servers:

OIDC_CLIENT_ID=app_74a4b555-5b87-4212-9dda-d584fa78caf8
OIDC_CLIENT_SECRET=<上游 OAuth Server 颁发的客户端密钥>

Der Upstream-OAuth-Server muss nur die FastMCP-Callback-Adresse für die app_...-Anwendung konfigurieren:

https://heroic-verbally-crawdad.ngrok-free.app/auth/callback

Konfigurieren Sie die ChatGPT-Callback-Adresse https://chatgpt.com/connector/oauth/... nicht am Upstream-OAuth-Server; diese Adresse wird von der FastMCP-Proxy-Schicht nach Abschluss der Authentifizierung verwendet.

Zu Beginn der Authentifizierung sollte im normalen Log zuerst die CIMD-Client-ID von ChatGPT erscheinen:

CIMD document fetched and validated
GET /authorize?client_id=https://chatgpt.com/oauth/.../client.json ... 302

Anschließend verwendet FastMCP app_74a4b555-..., um zum Upstream-OAuth-Server weiterzuleiten.

MCP-Client-Konfiguration

Konfigurieren Sie die MCP-Adresse wie folgt:

http://localhost:8000/mcp

FastMCP stellt die folgenden Authentifizierungs-Discovery-Adressen bereit:

http://localhost:8000/.well-known/oauth-authorization-server
http://localhost:8000/.well-known/oauth-protected-resource/mcp

Der Client sollte diese MCP/OAuth-Discovery-Endpunkte automatisch lesen. Nach erfolgreichem Login können zwei geschützte Tools aufgerufen werden:

  • ping: Gesundheitscheck.

  • who_am_i: Gibt die client_id, Scopes und Claims zurück, die FastMCP aus dem aktuellen Authentifizierungstoken extrahiert.

Konfigurationsoptionen

Umgebungsvariable

Erforderlich

Standardwert

Beschreibung

OIDC_CLIENT_ID

Ja

-

Upstream-OIDC-Client-ID

OIDC_CLIENT_SECRET

Eines von beiden

-

Geheimnis des vertraulichen Clients

JWT_SIGNING_KEY

Eines von beiden

-

Signaturschlüssel für öffentliche PKCE-Clients oder FastMCP-Token in der Produktion

OIDC_CONFIG_URL

Nein

QA-Discovery-URL

OIDC-Discovery-Adresse

BASE_URL

Nein

http://localhost:8000

Öffentliche Adresse des MCP-Servers

OIDC_REQUIRED_SCOPES

Nein

openid

Scope für OAuth-Autorisierungsanfragen/Standardwerbung; nicht für die Access-Token-Scope-Validierung

OIDC_TOKEN_ISSUER

Nein

OIDC-Discovery-Issuer

JWT-iss-Validierungswert; wird gesetzt, wenn die Auth-Middleware den Issuer umschreibt

OIDC_JWKS_URI

Nein

https://auth-qa.drillinsight.com/oauth/jwks

JWKS-Adresse für benutzerdefinierte Token-Issuer

OIDC_TOKEN_AUDIENCE

Nein

-

Optionaler JWT-aud-Validierungswert

HOST

Nein

127.0.0.1

Lauschadresse

PORT

Nein

8000

Lauschport

MGT_API_BASE_URL

Nein

QA-MGT-Adresse

Basis-URL für tatsächliche MGT-API-Aufrufe

MGT_OPENAPI_SPEC_PATH

Nein

specs/mgt-qa-openapi.json

Pfad zur lokalen OpenAPI-Spezifikation

Setzen Sie in der Produktion explizit einen zufälligen JWT_SIGNING_KEY und verwenden Sie eine HTTPS-BASE_URL. Committen Sie weder .env noch Client-Secrets in Git.

Benutzerdefinierter Token-Issuer

Wenn der iss des Tokens nicht der vom OIDC-Discovery zurückgegebene Issuer ist, sondern von der Auth-Middleware in eine Mandantenadresse umgeschrieben wird, z. B.:

实际 token iss:
https://api-authsome-qa.drillinsight.com/auth-middleware/t_adecdb63-afab-4346-a1aa-b50bbbae7aee/

Setzen Sie:

OIDC_TOKEN_ISSUER=https://api-authsome-qa.drillinsight.com/auth-middleware/t_adecdb63-afab-4346-a1aa-b50bbbae7aee/
OIDC_JWKS_URI=https://auth-qa.drillinsight.com/oauth/jwks

OIDC_CONFIG_URL wird weiterhin für die OAuth-Login- und Autorisierungs-Endpunkt-Discovery verwendet; OIDC_TOKEN_ISSUER dient nur zur Validierung des iss des JWT-Access-Tokens. Beide können unterschiedlich sein. OIDC_TOKEN_ISSUER muss exakt mit dem iss im Token übereinstimmen, einschließlich des abschließenden /.

Das aktuelle Projekt validiert weder den scope- noch den scp-Claim des Access-Tokens, da historische MGT-Tokens ein nicht standardkonformes Scope-Format verwenden. OIDC_REQUIRED_SCOPES wird weiterhin für OAuth-Autorisierungsanfragen verwendet, blockiert jedoch keine gültigen Tokens ohne standardkonforme Scope-Claims. Signatur-, Issuer-, Audience-, Ablaufzeit- und JWKS-Validierung bleiben erhalten.

QA MGT OpenAPI-Anbindung

Die QA-OpenAPI-Spezifikation wurde fest unter folgendem Pfad gespeichert:

specs/mgt-qa-openapi.json

MCP greift zur Laufzeit nicht auf die Online-/api-docs zu, sodass die zukünftige Produktionsumgebung ohne offene API-Dokumentation weiterhin funktioniert. Über MGT_API_BASE_URL kann die tatsächliche API-Adresse umgeschaltet werden. Das Docker-Image kopiert specs/mgt-qa-openapi.json nach /app/specs/mgt-qa-openapi.json und setzt MGT_OPENAPI_SPEC_PATH automatisch.

Der in der ersten Version freigegebene API-Umfang:

/api/v1/user/current
/course/list
/batch/list
/batch/trainee/list
/equity/userequity/give
/api/v1/order/**
/api/v1/item/**
/api/v1/open/getSku*
/api/v1/customers
/api/v1/campus-recruitment/**(排除 export)
/api/v1/recruitment-info/**(排除 export)

Die Schnittstelle für Bestellungs-Zahlungslink wird gemäß den aktuellen Anforderungen angebunden:

/api/v1/order/queryPayLink
/api/v1/order/reGenaratePayLink

Weiterhin ausgeschlossen sind Rückerstattungs-, Zahlungs-Callback- und Kundendatenexport-Schnittstellen:

/mall/v1/order/refund
/alipay/**
/stripe/**
/weixin/refund/**
/api/v1/customers/export
/api/v1/campus-recruitment/export
/api/v1/recruitment-info/export

Bei jedem MGT-Aufruf holt der OpenAPI-Client das Upstream-OAuth-Access-Token des Benutzers aus der aktuellen FastMCP-Anfrage und sendet:

Authorization: Bearer <user access token>
X-Application-Id: <token.app_id>

Dabei muss X-Application-Id nicht zusätzlich konfiguriert werden; es wird direkt aus dem app_id-Claim des validierten JWT gelesen. Tokens ohne app_id werden abgelehnt, um unvollständige Anfragen an MGT zu vermeiden.

Daher muss MGT den von auth-qa.drillinsight.com ausgestellten Benutzer-Tokens vertrauen und die Berechtigungssteuerung basierend auf der Benutzeridentität durchführen.

Fehlerbehebung bei ChatGPT CIMD-Timeout

Wenn das Log Folgendes enthält:

CIMD fetch failed for https://chatgpt.com/.../client.json: Timeout fetching
Unregistered client_id=https://chatgpt.com/.../client.json

bedeutet dies, dass FastMCP nicht direkt auf die von ChatGPT gehosteten Client-Metadaten zugreifen kann. Wenn die aktuelle Maschine für den Zugriff auf das Internet über einen vertrauenswürdigen ausgehenden Proxy gehen muss, konfigurieren Sie:

FASTMCP_SSRF_TRUST_PROXY=true
HTTPS_PROXY=http://127.0.0.1:7897

Stoppen und starten Sie den Dienst dann vollständig neu. Das Programm lädt automatisch die .env im Projektstamm, bevor FastMCP importiert wird; FastMCP führt standardmäßig DNS-Validierung und IP-Pinning für CIMD/JWKS-Anfragen durch und verwendet daher nicht automatisch normale Proxy-Umgebungsvariablen. Wenn diese Option aktiviert ist, wird die SSRF-Schutzverantwortung an den angegebenen Proxy übertragen und NO_PROXY ignoriert. Aktivieren Sie sie nur für vertrauenswürdige Proxys.

Das erste POST /mcp 401 im Log ist der Client, der vor der Authentifizierung geschützte Ressourcen abtastet; die 404-Antworten auf mehrere /.well-known/...-Adressen sind ebenfalls Kompatibilitätstests von ChatGPT. Solange /.well-known/oauth-authorization-server 200 zurückgibt, sind sie nicht die Ursache dieses Fehlers.

Wenn das Log Unregistered client_id=app_... oder eine andere Nicht-URL-Client-ID anzeigt, bedeutet dies, dass ChatGPT eine alte DCR-Registrierung zwischengespeichert hat, die aus dem Server-Speicher verloren gegangen ist. Fixieren Sie JWT_SIGNING_KEY, starten Sie den Dienst neu und löschen Sie dann das benutzerdefinierte MCP in ChatGPT und erstellen Sie es neu, damit es /register erneut aufruft. Ein erneuter Login-Versuch stellt die dem Server unbekannte alte Client-ID nicht wieder her.

Tests

uv run pytest

Related MCP Connectors