Skip to main content
Glama
izambasiron

outsystems-mcp-relay

by izambasiron

outsystems-mcp-relay

Ein leichtgewichtiges, generisches stdio → Remote-MCP-Relay mit OAuth, plus einem RFC-9207-Issuer-Override für Remote-Server, deren veröffentlichte OAuth-Metadaten nicht mit der Autorisierungsantwort übereinstimmen. Keine Laufzeitabhängigkeiten. Eine einzige Datei.

stdio (your MCP client)  ⇄  outsystems-mcp-relay  ⇄  remote MCP server (Streamable HTTP)

Warum es das gibt

Einige Remote-MCP-Bereitstellungen sind ein Reverse-Proxy vor Keycloak (das OutSystems Developer Cloud MCP-Gateway ist eines davon). Sie veröffentlichen OAuth-Metadaten, deren issuer die Proxy-URL ist (z. B. https://<tenant>/mcp), aber der Autorisierungsserver stempelt seinen echten Issuer in den iss-Parameter der Autorisierungsantwort (z. B. https://<tenant>/auth/realms/<realm>).

RFC-9207-konforme Clients müssen diese Abweichung ablehnen, daher schlägt der OAuth-Login auf jeder Plattform fehl — Claude Code, pi, Cursor, Codex, um nur einige zu nennen. Dieses Relay ermöglicht es dir, iss gegen den echten Backend-Issuer zu validieren, während alle anderen OAuth-Prüfungen streng bleiben. Für normale Server verhält es sich wie ein einfaches Relay.

Related MCP server: mcp-auth-proxy

Wann man es verwenden sollte

Probiere zuerst die offizielle direkte Verbindung aus — richte deine Plattform direkt auf die Remote-MCP-URL aus, ohne Relay dazwischen. Greife nur dann zu diesem Relay, wenn das mit dem obigen RFC-9207-Issuer-Mismatch-Fehler fehlschlägt.

Das existiert ausschließlich, um diesen einen serverseitigen Bug zu umgehen. Es macht nichts besser als der offizielle Weg, sobald der Bug nicht mehr vorhanden ist — also wenn OutSystems es mandantenweit behebt oder dein Mandant das Problem nie hatte, entferne das Relay und verbinde dich direkt. Das Relay teilt dir mit, wann das der Fall ist: Bei einem erfolgreichen Login prüft es, ob eine Issuer-Korrektur tatsächlich nötig war, und wenn nicht, gibt es einen Hinweis auf stderr aus. Warte nicht auf eine „Wird das noch gebraucht?"-Überprüfung — wenn du diesen Hinweis siehst, wechsle sofort zurück zur offiziellen direkten Verbindung.

Installation

Erfordert Node.js ≥ 20. Keine Abhängigkeiten — nur die Datei.

npm install -g outsystems-mcp-relay      # recommended
# or, without a global install:
npx outsystems-mcp-relay <remote-url> ...

Du musst dieses Repo nicht klonen, um das Relay zu nutzen. Installiere es über npm (oder verwende npx) und fertig. Klone es nur, wenn du den Quellcode (eine einzelne Datei mit ~500 Zeilen) prüfen oder dazu beitragen möchtest.

Verwendung

outsystems-mcp-relay <remote-url> [options]

  --as-metadata-url <url>   OAuth AS metadata URL (default: discover from remote-url)
  --expected-issuer <url>   Override the RFC 9207 expected issuer (the proxy fix)
  --client-id <id>          Pre-registered client id (skips dynamic registration)
  --bearer <token>          Static bearer token mode (skips OAuth entirely)
  --force                   Ignore cached tokens and re-authenticate
  --no-open                 Print the authorization URL instead of opening a browser
  --help                    Show help

Generisches Beispiel (normaler Remote-Server)

// mcp.json
{
  "mcpServers": {
    "my-remote": {
      "command": "outsystems-mcp-relay",
      "args": ["https://api.example.com/mcp"]
    }
  }
}

OutSystems-Beispiel (Issuer-Mismatch)

{
  "mcpServers": {
    "outsystems": {
      "command": "outsystems-mcp-relay",
      "args": ["https://<tenant>.outsystems.dev/mcp"]
    }
  }
}

Kein --expected-issuer nötig — das Relay erkennt automatisch den echten Autorisierungsserver-Issuer aus der Autorisierungs-Weiterleitung (Keycloak-Realm-Setups). Füge nur dann --expected-issuer https://<tenant>.outsystems.dev/auth/realms/<realm-id> hinzu, wenn die automatische Erkennung fehlschlägt (ein Nicht-Keycloak-Server): Die OAuth-Fehlermeldung gibt den Realm aus — der empfangene Wert ist der zu verwendende Wert. Die automatische Erkennung übernimmt einen Cross-Origin-Kandidaten nur, wenn dessen Issuer durch die eigenen Metadaten des Realms bestätigt wird (über die Origin-Allowlist erreichbar); für einen wirklich separaten IdP-Host übergib --expected-issuer (oder --as-metadata-url, um ihn vorab auf die Allowlist zu setzen).

Beim ersten Aufruf öffnet das Relay einen Browser zur Anmeldung (oder gibt die URL mit --no-open aus). Tokens werden zwischengespeichert und automatisch aktualisiert.

Installation über eine KI (diesen Text einfügen)

Füge dies in Claude Code, pi, Cursor oder eine beliebige KI-Plattform ein:

Install the OutSystems MCP fix (outsystems-mcp-relay). The official OutSystems
MCP fails OAuth on every AI harness with an RFC 9207 issuer mismatch — a
server-side bug in the gateway's Keycloak setup.
Step 1: run `npm install -g outsystems-mcp-relay`.
Step 2: ask me for my OutSystems tenant hostname (something like
`mycompany.outsystems.dev`).
Step 3: when I tell you, replace the broken server:
  `claude mcp remove outsystems`
  `claude mcp add -s user --transport stdio outsystems -- outsystems-mcp-relay https://<my-tenant>/mcp`
  (substitute my actual tenant. No other flags needed — the relay auto-detects
  the real authorization-server issuer; if auto-detection can't, ask me to paste
  the OAuth error and use its "received" URL as `--expected-issuer`.)
Step 4: tell me to restart Claude Code, then ask anything OutSystems-related.
The first tool call opens a browser for sign-in (or prints the URL with
`--no-open`).

Claude-Code-Schnellstart (OutSystems-Issuer-Mismatch)

Der Fehler, den du siehst, sieht so aus:

Issuer mismatch in authorization response (RFC 9207):
expected "https://<tenant>.outsystems.dev/mcp",
received "https://<tenant>.outsystems.dev/auth/realms/<realm-id>"

In einem Terminal (nicht innerhalb von Claude Code):

npm install -g outsystems-mcp-relay

# 1. remove the broken HTTP entry
claude mcp remove outsystems

# 2. add the relay as a local stdio server — no other flags needed: it
#    auto-detects the real authorization-server issuer
claude mcp add -s user --transport stdio outsystems -- \
  outsystems-mcp-relay \
  https://<tenant>.outsystems.dev/mcp

Starte dann Claude Code neu. Beim ersten OutSystems-Toolaufruf öffnet das Relay einen Browser zur Anmeldung (füge --no-open hinzu, wenn du die URL lieber selbst einfügen möchtest). Tokens werden zwischengespeichert, sodass spätere Sitzungen die Anmeldung überspringen. Überprüfe mit /mcp (der Server sollte verbunden sein) und einem einfachen „list my environments".

Du musst den Realm-Issuer nicht selbst ermitteln. Das Relay erkennt ihn automatisch aus der Autorisierungs-Weiterleitung. Falls die automatische Erkennung nicht funktioniert (ein Nicht-Keycloak-Server), gibt die Fehlermeldung den empfangenen Wert aus — dieser ist der --expected-issuer-Wert.

So funktioniert es

  • Protokollagnostische Durchreichung: liest zeilenweise JSON-RPC von stdin, sendet jeden Frame unverändert per POST an den Remote-Server und schreibt die JSON-RPC-Antwort zurück nach stdout. Keine Tool-Semantik hier — funktioniert für Tools, Ressourcen, Prompts, alles.

  • Behandelt Streamable-HTTP-Details: Mcp-Session-Id-Echo, direkte JSON-Antworten und 202/text/event-stream-Antworten (SSE-Zusammenführung).

  • OAuth: ermittelt die Autorisierungsserver-Metadaten, registriert dynamisch einen öffentlichen Client (PKCE S256), öffnet den Browser, validiert state und iss, tauscht den Code aus und aktualisiert Tokens bei 401. --expected-issuer legt den Issuer fest, gegen den iss validiert wird — die Lösung für Proxy/Keycloak-Mismatches.

  • Anfragen werden serialisiert (keine verschachtelten Antworten auf stdout).

Sicherheit

  • RFC 9207 durchgesetzt: iss wird nur validiert, wenn der Autorisierungsserver es tatsächlich sendet (fehlt es = der AS implementiert RFC 9207 nicht, keine Prüfung; vorhanden = strikter String-Vergleich gegen den erwarteten Issuer). --expected-issuer ermöglicht einen anderen erwarteten Wert — es deaktiviert niemals die Validierung.

  • Origin-Allowlist: Das Relay kontaktiert nur die konfigurierte Remote-Origin (und eine explizit angegebene --as-metadata-url). Weiterleitungen werden manuell durchlaufen und jeder Hop ist auf der Allowlist (307/308 erhalten den Request-Body; 301/302/303 wechseln gemäß HTTP-Semantik zu GET), und Authorization/Cookie werden entfernt, wenn eine Weiterleitung die Origin wechselt (wie beim nativen fetch). Kein SSRF.

  • PKCE S256 + zufälliger state (validiert) + Callback-Server nur auf localhost mit einem ephemeren Port.

  • Protokolliert niemals Geheimnisse: Tokens und Autorisierungscodes erscheinen nie in der Ausgabe (alle Diagnosen gehen nach stderr; stdout transportiert nur Protokollnachrichten).

  • Tokens werden unter ~/.mcp-auth/outsystems-mcp-relay-<sha1(url)>.json mit 0600-Berechtigungen gespeichert — die Ökosystem-Konvention (gleiche Speicherstruktur wie mcp-remote). OS-Keychain-Speicherung ist eine geplante Erweiterung; siehe Nicht-Ziele.

Testen

npm test                # mock-server protocol test (passthrough, session-id, SSE, 401)
npm run test:e2e -- <remote-url> --expected-issuer <issuer>   # real-tenant round trip

Fehlerbehebung

Symptom

Lösung

Issuer mismatch ... expected "…/mcp", received "…/auth/realms/…"

Normalerweise erledigt die automatische Erkennung das ohne zusätzliche Flags. Falls nicht, übergib die empfangene URL als --expected-issuer — der Fehler gibt sie für dich aus.

authentication failed nach langer Inaktivität

Das zwischengespeicherte Token ist abgelaufen und die Aktualisierung fehlgeschlagen. Führe es mit --force erneut aus (oder lösche die Datei in ~/.mcp-auth/), um dich neu zu authentifizieren.

Browser öffnet sich nicht

Füge --no-open hinzu — das Relay gibt die Autorisierungs-URL aus, die du in einen Browser einfügen kannst.

Dynamische Client-Registrierung schlägt fehl

Der Registrierungs-Endpunkt des Servers ist eingeschränkt (z. B. durch Keycloaks Trusted-Hosts-Richtlinie). Falls es das OutSystems-Proxy ist, sollte das nicht passieren; andernfalls registriere einen Client selbst und übergib --client-id.

Etwas anderes

Öffne ein Issue mit dem vollständigen Fehlertext (alle Diagnosen gehen nach stderr — schwärze alle Tokens).

Nicht-Ziele (v1)

  • OS-Keychain-Token-Speicherung (derzeit Datei mit 0600-Berechtigungen)

  • Multi-Server-Aggregation / -Verwaltung (verwende dafür ein Gateway)

  • Vom Server initiierte Benachrichtigungs-Streams über die Durchreichung hinaus

  • Benutzerdefinierte CA-Flags

Lizenz

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Local stdio proxy for Uno MCP Gateway that enables MCP clients without OAuth support to securely connect to authenticated remote servers.
    8
    21 PyPI
    1
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Bridges stdio-based LLM harnesses to OAuth-protected remote MCP servers via Streamable HTTP, handling PKCE browser login and token refresh automatically.
    5 npm
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    Bridge that lets stdio-only MCP clients connect to remote MCP servers with OAuth and other auth support, enabling local clients to use remote, authorized MCP servers.
    19 npm
    54
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables local MCP clients to access tools from a remote FastMCP server over stdio, handling OAuth authentication and Streamable HTTP communication.
    -