Skip to main content
Glama
tung2744
by tung2744

test-mcp

Ein minimaler MCP-Ressourcenserver zum manuellen Testen der Unterstützung von Authgear für Dynamic Client Registration (DCR) und Resource Indicators (docs/specs/dcr.md, docs/specs/access-token-audience-binding.md im authgear-server-Repository).

Für sich genommen tut es nichts Interessantes – seine einzige Aufgabe ist es, hinter Authgear als Autorisierungsserver zu sitzen und einem echten MCP-Client zu ermöglichen, den gesamten Ablauf durchzuspielen: Discovery → DCR-Selbstregistrierung → PKCE-Autorisierung und -Einwilligung → Token-Austausch, der an die resource dieses Servers gebunden ist → ein authentifizierter MCP-Toolaufruf.

Wie die Komponenten zusammenwirken

MCP client  --1. GET /mcp (no token)-->  test-mcp
            <--2. 401 + WWW-Authenticate: Bearer resource_metadata="..."--

MCP client  --3. GET /.well-known/oauth-protected-resource-->  test-mcp
            <--4. { resource, authorization_servers: [Authgear] }--

MCP client  --5. GET /.well-known/oauth-authorization-server-->  Authgear
            <--6. { registration_endpoint, authorization_endpoint, ... }--

MCP client  --7. POST /oauth2/register-->  Authgear   (DCR)
MCP client  --8. /oauth2/authorize + consent, resource=<RESOURCE_URI>--> Authgear
MCP client  --9. POST /oauth2/token, resource=<RESOURCE_URI>-->  Authgear
            <--10. JWT access token, aud=[RESOURCE_URI]--

MCP client  --11. POST /mcp, Authorization: Bearer <token>-->  test-mcp
            <--12. tool result (or 401 if scope/audience don't match)--

Die Schritte 1–2 und 11–12 laufen gegen diesen Server. Alles dazwischen ist Authgear, das von jedem spezifikationskonformen MCP-Client automatisch ermittelt wird – du konfigurierst den Client nicht direkt mit der Authgear-URL.

Related MCP server: MCP Server OAuth Toy

Voraussetzungen

  • Eine laufende Authgear-Instanz mit aktiviertem DCR, z. B. in authgear.yaml:

    oauth:
      dynamic_client_registration:
        enabled: true
        initial_access_token_required: false # open registration, for easy testing
  • Eine Ressource, die in diesem Projekt registriert ist und der unten stehenden RESOURCE_URI entspricht, mit access_policy.allow_dynamic_third_party_client_access: true auf der Ressource selbst und auf jedem Scope, den die Testwerkzeuge benötigen – andernfalls erhält eine resource=-Anfrage eines DCR-Clients invalid_target/invalid_scope. Erstelle sie über den Admin-API-GraphQL-Playground (oder admin_api_graphql in einem e2e-Test, falls du das aus dem authgear-server-Repository heraus tust):

    mutation {
      createResource(input: {
        resourceURI: "https://localhost:8090"
        name: "test-mcp"
        accessPolicy: { allowDynamicThirdPartyClientAccess: true }
      }) {
        resource { id }
      }
    }
    
    mutation {
      createScope(input: {
        resourceURI: "https://localhost:8090"
        scope: "read:tools"
        accessPolicy: { allowDynamicThirdPartyClientAccess: true }
      }) {
        scope { id }
      }
    }
    
    mutation {
      createScope(input: {
        resourceURI: "https://localhost:8090"
        scope: "execute:tools"
        accessPolicy: { allowDynamicThirdPartyClientAccess: true }
      }) {
        scope { id }
      }
    }

    https://localhost:8090 muss der unten stehenden RESOURCE_URI bytegenau entsprechen und die echte eigene Origin dieses Servers sein (Schema + Host + Port), kein beliebiger Platzhalter. Zwei unabhängige Bedingungen legen das fest:

    • Authgear verlangt, dass jede Ressourcen-URI https:// ist (pkg/lib/resourcescope/formats.go).

    • Das resource-Feld der Protected-Resource-Metadaten aus RFC 9728 muss mit der URL (oder Origin) übereinstimmen, mit der der Client tatsächlich verbunden ist, und strenge Clients erzwingen das – der MCP Inspector verweigert die Verbindung mit einem Fehler wie Protected resource ... does not match expected ... (or origin), wenn du RESOURCE_URI auf eine nicht zusammenhängende Kennung statt auf die echte Adresse des Servers zeigst.

    Genau diese Kombination ist der Grund, warum dieser Server standardmäßig HTTPS (selbstsigniert) statt einfachem HTTP ausliefert: https://localhost:<PORT> ist gleichzeitig eine gültige Authgear-Ressourcen-URI und die echte Origin dieses Servers. Wenn du PORT änderst, aktualisiere die URI der Ressource (und die RESOURCE_URI unten) entsprechend.

Einrichtung

npm install
npm run setup   # generates a self-signed TLS cert for localhost (see below)

Ausführen

npm start

Umgebungsvariablen (alle optional):

Variable

Standard

Bedeutung

PORT

8090

Port, auf dem dieser Server lauscht.

AUTHGEAR_ENDPOINT

http://localhost:4000

Basis-URL deiner Authgear-Instanz. Verwende http://localhost:3000, wenn du direkt den make start-Prozess ansprichst, oder http://localhost:3100, wenn du den üblichen lokalen Entwicklungs-nginx-Proxy (docker compose up -d proxy) verwendest – in jedem Fall muss dies dort sein, wo /.well-known/openid-configuration tatsächlich aufgelöst wird.

RESOURCE_URI

https://localhost:<PORT>

Die RFC-8707-Ressourcenkennung – muss der oben erstellten Ressource entsprechen und die echte Origin dieses Servers sein (siehe oben).

USE_HTTP

nicht gesetzt

Setze sie auf 1, um einfaches HTTP statt HTTPS auszuliefern. Nicht empfohlen: Mit USE_HTTP=1 kann RESOURCE_URI nicht mehr der echten Origin dieses Servers entsprechen (sie müsste http://... sein, was Authgear als Ressourcen-URI ablehnt), daher schlägt die Ressourcenabgleich-Prüfung eines strengen MCP-Clients fehl. Verwende dies nur mit einem Client, von dem du weißt, dass er diese Prüfung nicht durchsetzt.

Testen mit einem echten MCP-Client

MCP Inspector (empfohlener erster Schritt)

npx @modelcontextprotocol/inspector

Öffne die ausgegebene lokale URL, setze die Server-URL auf https://localhost:8090/mcp und verbinde dich – das Panel „Auth“ des Inspectors führt Schritt für Schritt durch Discovery, DCR und den Authorize-/Token-Austausch, sodass du genau sehen kannst, was jede Antwort enthält.

Da das Zertifikat selbstsigniert ist, musst du Node möglicherweise anweisen, ihm für die ausgehenden Anfragen des Inspectors zu vertrauen:

NODE_EXTRA_CA_CERTS=$(pwd)/certs/localhost.crt npx @modelcontextprotocol/inspector

(Nur für lokale Tests tun – deaktiviere die Zertifikatsprüfung niemals für etwas, das mit einem echten Server kommuniziert.)

mcp-remote (zum Testen gegen Claude Desktop)

npx mcp-remote https://localhost:8090/mcp

und richte die Konfiguration von Claude Desktop auf die resultierende lokale Stdio-Brücke aus, gemäß der eigenen Dokumentation von mcp-remote.

Worauf du achten solltest

  • Kein resource= angefordert (ein einfacher OIDC-Client oder ein MCP-Client, der kein resource sendet): Authgear stellt einem Drittanbieter-/DCR-Client standardmäßig einen opaken Token aus. Dieser Server kann einen opaken Token überhaupt nicht verifizieren (er ist kein JWT), daher schlägt jeder Toolaufruf mit 401 fehl – das ist das beabsichtigte Verhalten (docs/specs/dcr.md, access-token-audience-binding.md): Ein ungebundener Drittanbieter-Token ist nur bei Authgears eigenem /oauth2/userinfo verwendbar, sonst nirgendwo.

  • resource=<RESOURCE_URI> angefordert: Authgear stellt ein JWT mit aud: [RESOURCE_URI] aus. whoami sollte nun unabhängig von den gewährten Scopes erfolgreich sein; list_widgets/run_widget sind nur erfolgreich, wenn der entsprechende Scope (read:tools/execute:tools) bei der Einwilligung gewährt wurde.

  • Ein an eine andere Ressource gebundener Token oder einer, dessen Ressource/Scope kein allow_dynamic_third_party_client_access besitzt: wird bereits bei Authgear selbst abgelehnt (invalid_target/invalid_scope), bevor er diesen Server überhaupt erreicht.

Fehlerbehebung

  • Failed to connect ... Protected resource <X> does not match expected <Y> (or origin) (MCP Inspector oder ein anderer RFC-9728-strenger Client) – RESOURCE_URI ist auf etwas anderes als die echte Origin dieses Servers gesetzt. Korrigiere RESOURCE_URI (und die passende Ressource in Authgear) so, dass sie https://localhost:<PORT> ist, kein beliebiger Platzhalter – siehe „Voraussetzungen“ oben.

  • invalid_target bei /oauth2/authorize oder /oauth2/token – die Ressource (und/oder der jeweilige Scope) hat kein access_policy.allow_dynamic_third_party_client_access: true, oder der vom Client gesendete resource=-Wert entspricht nicht exakt dem registrierten Wert.

  • 401 von diesem Server mit error_description: "fetch failed" – dieser Server konnte AUTHGEAR_ENDPOINT nicht erreichen, um die Discovery-Metadaten abzurufen; prüfe, ob Authgear dort tatsächlich läuft.

  • 401 mit einem JWT-Verifikationsfehler – der Token ist echt, aber entweder abgelaufen, von einem anderen Aussteller signiert oder an ein anderes aud als RESOURCE_URI gebunden.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A proof-of-concept MCP server implementing OAuth 2.1 authorization with CIMD client registration and PKCE, demonstrating protected resource access and step-up authentication.
    -
  • -
    license
    Not graded
    quality
    F
    maintenance
    A minimal remote (Streamable HTTP) MCP server that is an OAuth 2.1 resource server, demonstrating the MCP authorization spec with token validation and audience checks.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A demo MCP server protected by OAuth (DCR), enabling hands-on exploration of OAuth flow for local MCP servers.
    MIT