Skip to main content
Glama
curtismu7

mcp-resource-server

by curtismu7

mcp-resource-server

Ein MCP-Server, der überwiegend lesende Tools über zehn Mock-Vertikalen (Bankwesen, Gesundheitswesen, Regierung, Fertigung, Einzelhandel, Sportartikel, Universität, Arbeitskräfte, Abercrombie & Fitch, Fluggesellschaften) sowie eine Reihe von Investment-Tools bereitstellt. Jede Vertikale liest ihre eigene gebündelte SQLite-Datenbank – kein anderer Dienst ist erforderlich, um sie auszuführen. Investment-Tools können optional an eine von Ihnen bereitgestellte Banking-API weiterleiten (siehe unten).

Voraussetzungen

  • Docker + Docker Compose

  • Eine PingOne-Umgebung zum Erstellen und Verifizieren von Bearer-Tokens (oder ein beliebiger OAuth-AS, der ein JWT mit den richtigen aud/scope-Claims ausstellen und einen JWKS-Endpunkt veröffentlichen kann – dieser Server verlässt sich nur auf standardmäßige OIDC-Erkennung, nicht auf etwas PingOne-Spezifisches).

Related MCP server: mock-mcp

Schnellstart

cp .env.example .env

Bearbeiten Sie .env: Setzen Sie MCP_RESOURCE_SERVER_RESOURCE_URI, PINGONE_ENVIRONMENT_ID und PINGONE_REGION für Ihre eigene PingOne-Umgebung. Lassen Sie PINGONE_ISSUER auskommentiert, bis Sie echte Tokens haben (siehe „Auth-Modi“).

docker compose up --build

Der Server lauscht auf http://localhost:8081. SQLite-Datenbanken werden in ./data gespeichert (beim ersten Gebrauch aus seed/ befüllt; ein Neustart befüllt eine nicht leere Datenbank nie erneut).

Überprüfen, ob er läuft

curl http://localhost:8081/health
curl http://localhost:8081/.well-known/oauth-protected-resource

Der zweite Aufruf gibt die beworbenen Scopes der Ressource und, wenn PINGONE_ENVIRONMENT_ID/PINGONE_REGION gesetzt sind, deren Autorisierungsserver zurück – dies sind die RFC-9728-Metadaten, die ein MCP-Client für die OAuth-Erkennung verwendet.

Auth-Modi

Ob die Signatur eines Tokens verifiziert wird, entscheidet sich danach, ob eine JWKS-Quelle konfiguriert ist – nicht durch STRICT_AUTH:

  • JWKS-Quelle gesetzt (PINGONE_ISSUER, PINGONE_JWKS_URI oder PINGONE_BASE_URL) – jedes Token wird gegen die Schlüssel Ihrer PingOne-Umgebung verifiziert und bei Fehlschlag abgelehnt. STRICT_AUTH hat keine Wirkung. Führen Sie es so aus, sobald echte Tokens fließen.

  • Keine JWKS-Quelle – STRICT_AUTH=false (die mitgelieferte Standardeinstellung) akzeptiert ein wohlgeformtes Token mit einer Konsolenwarnung, sodass Sie jedes Tool mit einem handgemachten Token testen können, bevor PingOne angeschlossen ist; STRICT_AUTH=true lehnt stattdessen jedes Token ab. Lassen Sie die Standardeinstellung nicht für andere als Sie erreichbar.

.env.example wird mit allen drei JWKS-Variablen auskommentiert geliefert – kommentieren Sie eine aus, wenn Sie echte Tokens haben.

Ein Bearer-Token erhalten

Jeder Tool-Aufruf benötigt ein Bearer-Token, dessen aud-Claim mit MCP_RESOURCE_SERVER_RESOURCE_URI übereinstimmt und dessen scope-Claim das aufgerufene Tool abdeckt (siehe tools/list für die maßgebliche, aktuelle Liste – sie wird aus der eigenen Registrierung dieses Servers generiert).

Lokales Testen (STRICT_AUTH=false) – jedes wohlgeformte JWT mit den richtigen Claims funktioniert; die Signatur wird nicht geprüft.

node -e "
const b64 = s => Buffer.from(JSON.stringify(s)).toString('base64url');
const header = b64({alg:'none',typ:'JWT'});
const payload = b64({sub:'test-user',scope:'airlines:read',aud:'your-resource-uri',exp:Math.floor(Date.now()/1000)+3600});
console.log(header+'.'+payload+'.');
"

(tauschen Sie aud gegen Ihren eigenen MCP_RESOURCE_SERVER_RESOURCE_URI-Wert und scope gegen das/die Tool(s), das/die Sie testen)

Echte Tokens (STRICT_AUTH=true) – erstellen Sie eines aus Ihrer PingOne-Umgebung. Ein Client-Credentials-Grant gegen Ihren PingOne-Token-Endpunkt, der die Audience dieses Servers als Ressource anfordert:

curl -s -X POST "https://auth.pingone.<region>/<env-id>/as/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=<your PingOne worker app client id>" \
  -d "client_secret=<your PingOne worker app client secret>" \
  -d "scope=<space-separated scopes, e.g. banking:read airlines:read>" \
  -d "resource=<MCP_RESOURCE_SERVER_RESOURCE_URI value>"

Dies erfordert, dass die App dieses Clients für diese Ressource und diese Scopes in PingOne autorisiert ist (Applications → Ihre App → Resources) – ein PingOne-seitiger Einrichtungsschritt, den dieser Server nicht für Sie übernimmt.

Ein Tool direkt aufrufen (Sanity-Check)

TOKEN="<paste a token from above>"
curl -s -X POST http://localhost:8081/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_airline_bookings","arguments":{}}}'

Einen MCP-Client verbinden

Der Server spricht MCP sowohl über WebSocket als auch über HTTP (streamable, POST /mcp) auf demselben Port – ws://localhost:8081 oder http://localhost:8081/mcp.

MCP Inspector (das offizielle Entwickler-Tool – funktioniert mit jedem Server und ermöglicht das Setzen eines manuellen Headers, daher ist es der zuverlässigste Weg, diesen zu testen):

npx @modelcontextprotocol/inspector

Setzen Sie Transport auf „Streamable HTTP“, URL auf http://localhost:8081/mcp und fügen Sie vor dem Verbinden einen Authorization: Bearer <token>-Header in den Verbindungseinstellungen des Inspectors hinzu.

Claude Desktop / Cursor / Windsurf (statische Konfiguration, HTTP-Transport):

{
  "mcpServers": {
    "mcp-resource-server": {
      "url": "http://localhost:8081/mcp",
      "transport": "http"
    }
  }
}
  • Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Cursor: .cursor/mcp.json in Ihrem Projektstamm

  • Windsurf: ~/.codeium/windsurf/mcp_config.json

Diese Konfigurationen haben kein Feld für ein statisches Bearer-Token – wenn der Client ein geschütztes Tool aufruft, liest er /.well-known/oauth-protected-resource, findet Ihre PingOne-Umgebung als Autorisierungsserver und fordert Sie zur Anmeldung auf. Das funktioniert nur, wenn Ihre PingOne-Umgebung einen OAuth-Client für diese spezifische MCP-Client-App registriert hat, unter Verwendung der Redirect-URI, die in der Dokumentation dieses Clients angegeben ist – ein PingOne-seitiger Einrichtungsschritt außerhalb dieses Servers. Starten Sie den Client nach dem Bearbeiten seiner Konfiguration neu.

Investment-Tools

get_investment_accounts, get_investment_balance, get_portfolio_summary und get_investment_transactions funktionieren sofort aus einer gebündelten SQLite-Datenbank (data/invest.db, beim ersten Gebrauch aus seed/invest.seed.json befüllt), genau wie jede andere Vertikale.

Setzen Sie BANKING_API_BASE_URL nur, wenn Sie eine eigene Banking-API betreiben und möchten, dass diese vier Tools das Bearer-Token des Aufrufers an sie weiterleiten und zurückgeben, was auch immer sie zurückgibt. Wenn es gesetzt ist, wird die gebündelte Invest-Datenbank nicht verwendet.

Ein Tool hinzufügen

Tools sind Code, keine Konfiguration. Der Katalog (tools/list), das Pro-Tool-Scope-Gate und die in /.well-known/oauth-protected-resource beworbenen scopes_supported werden alle aus ALL_TOOLS in src/tools/registry.ts abgeleitet. Ein Tool, das Sie zur Liste einer Vertikale hinzufügen, ist also überall live, sobald Sie das Image neu erstellen (der --build-Befehl im Schnellstart).

1. Ein Tool zu einer bestehenden Vertikale hinzufügen

Zwei Änderungen, keine Registrierungsänderung:

  1. Fügen Sie eine Tool-Definition zum Array src/tools/<vertical>Tools.ts dieser Vertikale hinzu, z. B. in sportingGoodsTools.ts:

    {
      name: 'gear_return_status',
      description: 'Show the status of a sporting-goods return.',
      inputSchema: {
        type: 'object',
        properties: { orderId: { type: 'string', description: 'Order ID' } },
        required: ['orderId'],
      },
      requiredScopes: ['read'],   // the bearer token must carry every scope listed
      readOnly: true,
      intentHints: ['check my gear return'],   // required — tests/registry.test.ts asserts it
    },
  2. Fügen Sie einen passenden case 'gear_return_status': zum switch in src/tools/<vertical>ToolHandler.ts hinzu. Der Handler gibt nur JSON zurück – lesen Sie aus src/db/<vertical>Db.ts dieser Vertikale oder etwas anderem.

2. Eine neue Vertikale hinzufügen

Gleiche zwei Dateien wie oben (src/tools/<vertical>Tools.ts, die ein <VERTICAL>_TOOLS: McpToolDef[] exportiert, und src/tools/<vertical>ToolHandler.ts, die dispatch<Vertical>Tool exportiert), plus:

  • Daten (optional): src/db/<vertical>Db.ts + seed/<vertical>.seed.json. Kopieren Sie sportingGoodsDb.ts – es öffnet data/<vertical>.db, erstellt das Schema und wendet den Seed nur an, wenn die Tabellen leer sind. Dockerfile kopiert bereits seed/, und die Compose-Datei mountet bereits data/.

  • Registrieren in src/tools/registry.ts: Importieren Sie die beiden Exporte, verteilen Sie <VERTICAL>_TOOLS in ALL_TOOLS, fügen Sie ein const <VERTICAL>_TOOL_NAMES = new Set(<VERTICAL>_TOOLS.map((t) => t.name)) hinzu und eine Zeile in dispatch(): if (<VERTICAL>_TOOL_NAMES.has(toolName)) return dispatch<Vertical>Tool(toolName, args, subject); (subject ist das sub des Tokens – akzeptieren Sie es in Ihrem Handler, wenn Lesezugriffe auf den Aufrufer beschränkt sein müssen, wie bei Banking und Fluggesellschaften.)

  • Ressourcen (optional): tools/list ist automatisch, aber MCP-Ressourcen (resources/list, resources/read) stammen aus dem manuell gepflegten RESOURCE_CATALOG in src/index.ts – fügen Sie dort einen Eintrag hinzu, wenn die Vertikale ihr List-Tool auch als Ressource bereitstellen soll.

Dinge, die beißen

  • requiredScopes müssen Scopes sein, die Ihre PingOne-Ressource tatsächlich gewährt. Ein Token, dem eines davon fehlt, erhält 403 bei tools/call, und tools/list verbirgt das Tool für dieses Token vollständig.

  • Tool-Namen sind global. dispatch() leitet anhand des ersten Namenssatzes weiter, der übereinstimmt. Ein Name, der in zwei Vertikalen wiederverwendet wird, geht also stillschweigend an diejenige, die zuerst geprüft wird.

  • Verwenden Sie eine datengestützte Vertikale (z. B. Sportartikel) als Vorlage, nicht invest – invest ist der dispatch()-Fallthrough und trägt einen Proxy-vs-SQLite-Schalter, den Sie nicht benötigen.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A lightweight MCP server that simulates financial data interactions with dummy authentication and static JSON datasets for testing financial applications.
    2
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    A minimal local HTTP MCP mock server for development and testing, providing predictable tool responses with OAuth token support and zero dependencies.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    A production-grade MCP server for a fictional digital bank, exposing tools for an AI copilot to service customers across the full risk spectrum from read-only lookups to money movement and destructive admin actions, with OAuth 2.1 security and a realistic dataset.
    21 npm
    1
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    A Test/Sandbox MCP server that integrates with the SnapTrade API Sandbox environment to provide portfolio oversight, market data, and trading capabilities.
    -