Tutorial: Baue einen Todo-Manager
In diesem Tutorial bauen wir einen Todo-Manager-MCP-Server mit Benutzer-Authentifizierung (Authentifizierung) und Autorisierung (Autorisierung).
Nach Abschluss dieses Tutorials hast du:
- ✅ Ein grundlegendes Verständnis, wie du rollenbasierte Zugangskontrolle (RBAC) in deinem MCP-Server einrichtest.
- ✅ Einen MCP-Server, der persönliche Todo-Listen verwalten kann.
Bevor du beginnst, empfehlen wir dir dringend, zuerst das Who am I Tutorial durchzugehen, falls du mit dem MCP-Server und OAuth 2 nicht vertraut bist.
Überblick
Das Tutorial umfasst folgende Komponenten:
- MCP-Server: Ein einfacher MCP-Server, der die offiziellen MCP-SDKs verwendet, um Anfragen zu bearbeiten, mit einem integrierten Todo-Service zur Verwaltung der Todo-Einträge der Benutzer.
- MCP Inspector: Ein visuelles Test-Tool für MCP-Server. Es agiert auch als OAuth / OIDC-Client, um den Autorisierungsfluss zu starten und Zugangstokens abzurufen.
- Autorisierungsserver: Ein OAuth 2.1- oder OpenID Connect-Anbieter, der Benutzeridentitäten verwaltet und Zugangstokens ausstellt.
Hier ist ein Überblicksdiagramm der Interaktion zwischen diesen Komponenten:
Verstehe deinen Autorisierungsserver
Zugangstokens mit Berechtigungen (Scopes)
Um rollenbasierte Zugangskontrolle (RBAC) in deinem MCP-Server zu implementieren, muss dein Autorisierungsserver das Ausstellen von Zugangstokens mit Berechtigungen (Scopes) unterstützen. Berechtigungen repräsentieren die Rechte, die einem Benutzer gewährt wurden.
- Logto
- OAuth 2.0 / OIDC
Logto bietet RBAC-Unterstützung über seine API-Ressourcen (gemäß RFC 8707: Resource Indicators for OAuth 2.0) und Rollenfunktionen. So richtest du es ein:
-
Melde dich bei der Logto Console (oder deiner selbst gehosteten Logto Console) an.
-
Erstelle API-Ressource und Berechtigungen:
- Gehe zu "API-Ressourcen"
- Erstelle eine neue API-Ressource namens "Todo Manager"
- Füge folgende Berechtigungen hinzu:
create:todos
: "Neue Todo-Einträge erstellen"read:todos
: "Alle Todo-Einträge lesen"delete:todos
: "Beliebigen Todo-Eintrag löschen"
-
Erstelle Rollen (empfohlen für einfachere Verwaltung):
- Gehe zu "Rollen"
- Erstelle eine "Admin"-Rolle und weise alle Berechtigungen zu (
create:todos
,read:todos
,delete:todos
) - Erstelle eine "User"-Rolle und weise nur die Berechtigung
create:todos
zu
-
Berechtigungen zuweisen:
- Gehe zu "Benutzer"
- Wähle einen Benutzer aus
- Du kannst entweder:
- Rollen im Tab "Rollen" zuweisen (empfohlen)
- Oder direkt Berechtigungen im Tab "Berechtigungen" zuweisen
Die Berechtigungen werden im scope
-Anspruch des JWT-Zugangstokens als durch Leerzeichen getrennte Zeichenkette enthalten sein.
OAuth 2.0 / OIDC-Anbieter unterstützen in der Regel berechtigungsbasierte Zugangskontrolle. Bei der Implementierung von RBAC:
- Definiere die benötigten Berechtigungen in deinem Autorisierungsserver
- Konfiguriere deinen Client so, dass diese Berechtigungen während des Autorisierungsflusses angefordert werden
- Stelle sicher, dass dein Autorisierungsserver die gewährten Berechtigungen im Zugangstoken einschließt
- Die Berechtigungen sind normalerweise im
scope
-Anspruch des JWT-Zugangstokens enthalten
Sieh in der Dokumentation deines Anbieters nach, wie:
- Berechtigungen definiert und verwaltet werden
- Berechtigungen im Zugangstoken enthalten sind
- Zusätzliche RBAC-Funktionen wie Rollenverwaltung funktionieren
Tokens validieren und Berechtigungen prüfen
Wenn dein MCP-Server eine Anfrage erhält, muss er:
- Die Signatur und das Ablaufdatum des Zugangstokens validieren
- Die Berechtigungen aus dem validierten Token extrahieren
- Prüfen, ob das Token die erforderlichen Berechtigungen für die angeforderte Operation enthält
Wenn ein Benutzer z. B. einen neuen Todo-Eintrag erstellen möchte, muss sein Zugangstoken die Berechtigung create:todos
enthalten. So funktioniert der Ablauf:
Dynamische Client-Registrierung
Die dynamische Client-Registrierung ist für dieses Tutorial nicht erforderlich, kann aber nützlich sein, wenn du die MCP-Client-Registrierung mit deinem Autorisierungsserver automatisieren möchtest. Siehe Ist Dynamic Client Registration erforderlich? für weitere Details.
Verstehe RBAC im Todo-Manager
Zu Demonstrationszwecken implementieren wir ein einfaches rollenbasiertes Zugangskontrollsystem (RBAC) in unserem Todo-Manager-MCP-Server. Das zeigt dir die Grundprinzipien von RBAC bei überschaubarer Implementierung.
Auch wenn dieses Tutorial RBAC-basierte Berechtigungsverwaltung demonstriert, ist es wichtig zu wissen, dass nicht alle Authentifizierungsanbieter die Berechtigungsverwaltung über Rollen implementieren. Manche Anbieter haben eigene Mechanismen zur Verwaltung von Zugangskontrolle und Berechtigungen.
Tools und Berechtigungen
Unser Todo-Manager-MCP-Server stellt drei Haupttools bereit:
create-todo
: Einen neuen Todo-Eintrag erstellenget-todos
: Alle Todos auflistendelete-todo
: Ein Todo anhand der ID löschen
Um den Zugriff auf diese Tools zu steuern, definieren wir folgende Berechtigungen:
create:todos
: Erlaubt das Erstellen neuer Todo-Einträgedelete:todos
: Erlaubt das Löschen bestehender Todo-Einträgeread:todos
: Erlaubt das Abfragen und Abrufen aller Todo-Einträge
Rollen und Berechtigungen
Wir definieren zwei Rollen mit unterschiedlichen Zugriffsrechten:
Rolle | create:todos | read:todos | delete:todos |
---|---|---|---|
Admin | ✅ | ✅ | ✅ |
User | ✅ |
- User: Ein normaler Benutzer, der Todo-Einträge erstellen und nur seine eigenen Todos ansehen oder löschen kann
- Admin: Ein Administrator, der alle Todo-Einträge erstellen, ansehen und löschen kann, unabhängig vom Eigentümer
Ressourcenbesitz
Obwohl die obige Berechtigungstabelle die explizit zugewiesenen Berechtigungen pro Rolle zeigt, gibt es ein wichtiges Prinzip des Ressourcenbesitzes zu beachten:
- Benutzer haben nicht die Berechtigungen
read:todos
oderdelete:todos
, können aber trotzdem:- Ihre eigenen Todo-Einträge lesen
- Ihre eigenen Todo-Einträge löschen
- Admins haben volle Berechtigungen (
read:todos
unddelete:todos
) und können:- Alle Todo-Einträge im System ansehen
- Jeden Todo-Eintrag löschen, unabhängig vom Eigentümer
Das demonstriert ein häufiges Muster in RBAC-Systemen, bei dem der Besitz einer Ressource implizite Berechtigungen für eigene Ressourcen gewährt, während administrative Rollen explizite Berechtigungen für alle Ressourcen erhalten.
Um tiefer in RBAC-Konzepte und Best Practices einzutauchen, sieh dir Mastering RBAC: A Comprehensive Real-World Example an.
Autorisierung in deinem Anbieter konfigurieren
Um das oben beschriebene Zugangskontrollsystem zu implementieren, musst du deinen Autorisierungsserver so konfigurieren, dass er die benötigten Berechtigungen unterstützt. So geht es bei verschiedenen Anbietern:
- Logto
- Keycloak
- OAuth 2 / OIDC
Logto bietet RBAC-Unterstützung über seine API-Ressourcen und Rollenfunktionen. So richtest du es ein:
-
Melde dich bei der Logto Console (oder deiner selbst gehosteten Logto Console) an.
-
Erstelle API-Ressource und Berechtigungen:
- Gehe zu "API-Ressourcen"
- Erstelle eine neue API-Ressource namens "Todo Manager" und verwende
https://todo.mcp-server.app
(zu Demo-Zwecken) als Indikator. - Erstelle folgende Berechtigungen:
create:todos
: "Neue Todo-Einträge erstellen"read:todos
: "Alle Todo-Einträge lesen"delete:todos
: "Beliebigen Todo-Eintrag löschen"
-
Erstelle Rollen (empfohlen für einfachere Verwaltung):
- Gehe zu "Rollen"
- Erstelle eine "Admin"-Rolle und weise alle Berechtigungen zu (
create:todos
,read:todos
,delete:todos
) - Erstelle eine "User"-Rolle und weise nur die Berechtigung
create:todos
zu - Wechsle auf der Detailseite der "User"-Rolle zum Tab "Allgemein" und setze die "User"-Rolle als "Standardrolle".
-
Benutzerrollen und Berechtigungen verwalten:
- Für neue Benutzer:
- Sie erhalten automatisch die "User"-Rolle, da wir sie als Standardrolle gesetzt haben
- Für bestehende Benutzer:
- Gehe zu "Benutzerverwaltung"
- Wähle einen Benutzer aus
- Weise dem Benutzer Rollen im Tab "Rollen" zu
- Für neue Benutzer:
Du kannst auch die Management API von Logto verwenden, um Benutzerrollen programmatisch zu verwalten. Das ist besonders nützlich für automatisierte Benutzerverwaltung oder beim Erstellen von Admin-Panels.
Beim Anfordern eines Zugangstokens wird Logto die Berechtigungen im scope
-Anspruch des Tokens basierend auf den Rollenberechtigungen des Benutzers einfügen.
In Keycloak kannst du die erforderlichen Berechtigungen mit Client-Scopes einrichten:
-
Client-Scopes erstellen:
- Gehe in deinem Realm zu "Client scopes"
- Erstelle drei neue Client-Scopes:
create:todos
read:todos
delete:todos
-
Den Client konfigurieren:
- Gehe zu deinen Client-Einstellungen
- Füge im Tab "Client scopes" alle erstellten Scopes hinzu
- Stelle sicher, dass der Token-Mapper so konfiguriert ist, dass die Scopes enthalten sind
-
Optional: Rollen für einfachere Verwaltung nutzen
- Wenn du rollenbasierte Verwaltung bevorzugst:
- Erstelle Realm-Rollen für verschiedene Zugriffsebenen
- Mappe Scopes auf Rollen
- Weise Rollen Benutzern zu
- Alternativ kannst du Scopes direkt Benutzern oder über clientseitige Berechtigungen zuweisen
- Wenn du rollenbasierte Verwaltung bevorzugst:
Keycloak wird die gewährten Scopes im scope
-Anspruch des Zugangstokens enthalten.
Für OAuth 2.0- oder OpenID Connect-Anbieter musst du die Scopes konfigurieren, die verschiedene Berechtigungen repräsentieren. Die genauen Schritte hängen von deinem Anbieter ab, aber im Allgemeinen:
-
Scopes definieren:
- Konfiguriere deinen Autorisierungsserver so, dass er unterstützt:
create:todos
read:todos
delete:todos
- Konfiguriere deinen Autorisierungsserver so, dass er unterstützt:
-
Client konfigurieren:
- Registriere oder aktualisiere deinen Client, um diese Scopes anzufordern
- Stelle sicher, dass die Scopes im Zugangstoken enthalten sind
-
Berechtigungen zuweisen:
- Verwende die Oberfläche deines Anbieters, um Benutzern die passenden Scopes zuzuweisen
- Manche Anbieter unterstützen rollenbasierte Verwaltung, andere nutzen direkte Scope-Zuweisungen
- Sieh in der Dokumentation deines Anbieters nach, was empfohlen wird
Die meisten Anbieter werden die gewährten Scopes im scope
-Anspruch des Zugangstokens enthalten. Das Format ist typischerweise eine durch Leerzeichen getrennte Zeichenkette von Scope-Werten.
Nach der Konfiguration deines Autorisierungsservers erhalten Benutzer Zugangstokens mit ihren gewährten Berechtigungen. Der MCP-Server nutzt diese Berechtigungen, um zu bestimmen:
- Ob ein Benutzer neue Todos erstellen darf (
create:todos
) - Ob ein Benutzer alle Todos (
read:todos
) oder nur seine eigenen sehen darf - Ob ein Benutzer beliebige Todos (
delete:todos
) oder nur seine eigenen löschen darf
MCP-Server einrichten
Wir verwenden die offiziellen MCP-SDKs, um unseren Todo-Manager-MCP-Server zu erstellen.
Neues Projekt erstellen
- Python
- Node.js
mkdir mcp-server
cd mcp-server
uv init # Oder verwende `pipenv` oder `poetry`, um eine neue virtuelle Umgebung zu erstellen
Richte ein neues Node.js-Projekt ein:
mkdir mcp-server
cd mcp-server
npm init -y # Oder verwende `pnpm init`
npm pkg set type="module"
npm pkg set main="todo-manager.ts"
npm pkg set scripts.start="node --experimental-strip-types todo-manager.ts"
Wir verwenden TypeScript in unseren Beispielen, da Node.js v22.6.0+ TypeScript nativ mit dem Flag --experimental-strip-types
ausführen kann. Wenn du JavaScript verwendest, ist der Code ähnlich – stelle nur sicher, dass du Node.js v22.6.0 oder neuer nutzt. Siehe Node.js-Dokumentation für Details.
MCP-SDK und Abhängigkeiten installieren
- Python
- Node.js
pip install "mcp[cli]" starlette uvicorn
Oder ein anderer Paketmanager deiner Wahl, wie uv
oder poetry
.
npm install @modelcontextprotocol/sdk express zod
Oder ein anderer Paketmanager deiner Wahl, wie pnpm
oder yarn
.
MCP-Server erstellen
Erstelle zunächst einen einfachen MCP-Server mit den Tool-Definitionen:
- Python
- Node.js
Erstelle eine Datei namens todo-manager.py
und füge folgenden Code hinzu:
from typing import Any
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount
mcp = FastMCP("Todo Manager")
@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
"""Neues Todo erstellen."""
return {"error": "Nicht implementiert"}
@mcp.tool()
def get_todos() -> dict[str, Any]:
"""Alle Todos auflisten."""
return {"error": "Nicht implementiert"}
@mcp.tool()
def delete_todo(id: str) -> dict[str, Any]:
"""Todo anhand der ID löschen."""
return {"error": "Nicht implementiert"}
app = Starlette(
routes=[Mount('/', app=mcp.sse_app())]
)
Starte den Server mit:
uvicorn todo_manager:app --host 0.0.0.0 --port 3001
Da die aktuelle MCP Inspector-Implementierung keine Autorisierungsflüsse behandelt, verwenden wir den SSE-Ansatz, um den MCP-Server einzurichten. Wir aktualisieren den Code hier, sobald der MCP Inspector Autorisierungsflüsse unterstützt.
Du kannst auch pnpm
oder yarn
verwenden, wenn du möchtest.
Erstelle eine Datei namens todo-manager.ts
und füge folgenden Code hinzu:
// todo-manager.ts
import { z } from 'zod';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express';
// MCP-Server erstellen
const server = new McpServer({
name: 'Todo Manager',
version: '0.0.0',
});
server.tool('create-todo', 'Neues Todo erstellen', { content: z.string() }, async ({ content }) => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Nicht implementiert' }) }],
};
});
server.tool('get-todos', 'Alle Todos auflisten', async () => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Nicht implementiert' }) }],
};
});
server.tool('delete-todo', 'Todo anhand der ID löschen', { id: z.string() }, async ({ id }) => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Nicht implementiert' }) }],
};
});
// Boilerplate-Code aus der MCP SDK-Dokumentation
const PORT = 3001;
const app = express();
const transports = {};
app.get('/sse', async (_req, res) => {
const transport = new SSEServerTransport('/messages', res);
transports[transport.sessionId] = transport;
res.on('close', () => {
delete transports[transport.sessionId];
});
await server.connect(transport);
});
app.post('/messages', async (req, res) => {
const sessionId = String(req.query.sessionId);
const transport = transports[sessionId];
if (transport) {
await transport.handlePostMessage(req, res, req.body);
} else {
res.status(400).send('Kein Transport für sessionId gefunden');
}
});
app.listen(PORT);
Starte den Server mit:
npm start
MCP-Server inspizieren
MCP Inspector klonen und starten
Jetzt, da der MCP-Server läuft, können wir den MCP Inspector verwenden, um zu prüfen, ob das whoami
-Tool verfügbar ist.
Aufgrund der aktuellen Implementierung haben wir den MCP Inspector geforkt, um ihn flexibler und skalierbarer für Authentifizierung und Autorisierung zu machen. Wir haben auch einen Pull Request an das Original-Repository eingereicht, um unsere Änderungen einzubringen.
Um den MCP Inspector zu starten, verwende folgenden Befehl (Node.js wird benötigt):
git clone https://github.com/mcp-auth/inspector.git
cd inspector
npm install
npm run dev
Öffne dann deinen Browser und gehe zu http://localhost:6274/
(oder eine andere im Terminal angezeigte URL), um den MCP Inspector zu nutzen.
MCP Inspector mit dem MCP-Server verbinden
Prüfe vor dem Fortfahren folgende Konfiguration im MCP Inspector:
- Transporttyp: Setze auf
SSE
. - URL: Setze auf die URL deines MCP-Servers. In unserem Fall sollte das
http://localhost:3001/sse
sein.
Jetzt kannst du auf die Schaltfläche "Connect" klicken, um zu sehen, ob der MCP Inspector eine Verbindung zum MCP-Server herstellen kann. Wenn alles in Ordnung ist, solltest du im MCP Inspector den Status "Connected" sehen.
Checkpoint: Todo-Manager-Tools ausführen
- Klicke im oberen Menü des MCP Inspectors auf den Tab "Tools".
- Klicke auf die Schaltfläche "List Tools".
- Du solltest die Tools
create-todo
,get-todos
unddelete-todo
auf der Seite sehen. Klicke darauf, um die Tool-Details zu öffnen. - Du solltest rechts die Schaltfläche "Run Tool" sehen. Klicke darauf und gib die erforderlichen Parameter ein, um das Tool auszuführen.
- Du solltest das Tool-Ergebnis mit der JSON-Antwort
{"error": "Nicht implementiert"}
sehen.
Mit deinem Autorisierungsserver integrieren
Für diesen Abschnitt gibt es einige Überlegungen:
Die Issuer-URL deines Autorisierungsservers
Dies ist normalerweise die Basis-URL deines Autorisierungsservers, z. B. https://auth.example.com
. Manche Anbieter haben einen Pfad wie https://example.logto.app/oidc
, prüfe daher die Dokumentation deines Anbieters.
Wie du die Metadaten des Autorisierungsservers abrufst
- Wenn dein Autorisierungsserver dem OAuth 2.0 Authorization Server Metadata oder OpenID Connect Discovery entspricht, kannst du die eingebauten MCP Auth-Utilities verwenden, um die Metadaten automatisch abzurufen.
- Wenn dein Autorisierungsserver diesen Standards nicht entspricht, musst du die Metadaten-URL oder Endpunkte manuell in der MCP-Server-Konfiguration angeben. Prüfe die Dokumentation deines Anbieters für die spezifischen Endpunkte.
Wie du den MCP Inspector als Client in deinem Autorisierungsserver registrierst
- Wenn dein Autorisierungsserver Dynamic Client Registration unterstützt, kannst du diesen Schritt überspringen, da der MCP Inspector sich automatisch als Client registriert.
- Wenn dein Autorisierungsserver Dynamic Client Registration nicht unterstützt, musst du den MCP Inspector manuell als Client in deinem Autorisierungsserver registrieren.
Verstehe die Token-Anfrageparameter
Beim Anfordern von Zugangstokens von verschiedenen Autorisierungsservern wirst du verschiedene Ansätze zur Angabe der Zielressource und Berechtigungen sehen. Hier sind die Hauptmuster:
-
Ressourcenindikator-basiert:
- Verwendet den Parameter
resource
, um die Ziel-API anzugeben (siehe RFC 8707: Resource Indicators for OAuth 2.0) - Häufig in modernen OAuth 2.0-Implementierungen
- Beispielanfrage:
{ "resource": "https://todo.mcp-server.app", "scope": "create:todos read:todos" }
- Der Server stellt Tokens aus, die speziell an die angeforderte Ressource gebunden sind
- Verwendet den Parameter
-
Audience-basiert:
- Verwendet den Parameter
audience
, um den beabsichtigten Token-Empfänger anzugeben - Ähnlich wie Ressourcenindikatoren, aber mit anderer Semantik
- Beispielanfrage:
{ "audience": "todo-api", "scope": "create:todos read:todos" }
- Verwendet den Parameter
-
Rein scope-basiert:
- Verwendet ausschließlich Scopes ohne resource/audience-Parameter
- Traditioneller OAuth 2.0-Ansatz
- Beispielanfrage:
{ "scope": "todo-api:create todo-api:read openid profile" }
- Nutzt oft vorangestellte Scopes, um Berechtigungen zu namespacen
- Häufig in einfacheren OAuth 2.0-Implementierungen
- Prüfe die Dokumentation deines Anbieters auf unterstützte Parameter
- Manche Anbieter unterstützen mehrere Ansätze gleichzeitig
- Ressourcenindikatoren bieten bessere Sicherheit durch Audience-Beschränkung
- Nutze Ressourcenindikatoren, wenn verfügbar, für bessere Zugangskontrolle
Auch wenn jeder Anbieter eigene Anforderungen hat, führen dich die folgenden Schritte durch die Integration des MCP Inspectors und MCP Servers mit anbieter-spezifischen Konfigurationen.
MCP Inspector als Client registrieren
- Logto
- OAuth 2.0 / OIDC
Die Integration des Todo-Managers mit Logto ist unkompliziert, da es sich um einen OpenID Connect-Anbieter handelt, der Ressourcenindikatoren und Scopes unterstützt. So kannst du deine Todo-API mit https://todo.mcp-server.app
als Ressourcenindikator absichern.
Da Logto Dynamic Client Registration noch nicht unterstützt, musst du den MCP Inspector manuell als Client in deinem Logto-Tenant registrieren:
- Öffne deinen MCP Inspector, klicke auf die Schaltfläche "OAuth Configuration". Kopiere den Redirect URL (auto-populated)-Wert, z. B.
http://localhost:6274/oauth/callback
. - Melde dich bei der Logto Console (oder deiner selbst gehosteten Logto Console) an.
- Navigiere zum Tab "Anwendungen", klicke auf "Anwendung erstellen". Klicke unten auf der Seite auf "App ohne Framework erstellen".
- Fülle die Anwendungsdetails aus und klicke dann auf "Anwendung erstellen":
- Wähle einen Anwendungstyp: "Single-page application"
- Anwendungsname: z. B. "MCP Inspector"
- Im Bereich "Einstellungen / Redirect URIs" füge den kopierten Redirect URL (auto-populated)-Wert ein. Klicke dann unten auf "Änderungen speichern".
- Im oberen Bereich siehst du den Wert "App ID". Kopiere ihn.
- Gehe zurück zum MCP Inspector und füge den "App ID"-Wert im Bereich "OAuth Configuration" unter "Client ID" ein.
- Gib den Wert
{"scope": "create:todos read:todos delete:todos", "resource": "https://todo.mcp-server.app"}
im Feld "Auth Params" ein. Dadurch enthält das von Logto zurückgegebene Zugangstoken die erforderlichen Berechtigungen für den Zugriff auf den Todo-Manager.
Dies ist eine generische OAuth 2.0 / OpenID Connect-Anbieter-Integrationsanleitung. Beide folgen ähnlichen Schritten, da OIDC auf OAuth 2.0 aufbaut. Prüfe die Dokumentation deines Anbieters für Details.
Wenn dein Anbieter Dynamic Client Registration unterstützt, kannst du direkt zu Schritt 8 unten gehen, um den MCP Inspector zu konfigurieren; andernfalls musst du den MCP Inspector manuell als Client registrieren:
-
Öffne deinen MCP Inspector, klicke auf die Schaltfläche "OAuth Configuration". Kopiere den Redirect URL (auto-populated)-Wert, z. B.
http://localhost:6274/oauth/callback
. -
Melde dich bei der Konsole deines Anbieters an.
-
Navigiere zum Bereich "Anwendungen" oder "Clients" und erstelle eine neue Anwendung oder einen neuen Client.
-
Falls dein Anbieter einen Client-Typ verlangt, wähle "Single-page application" oder "Public client".
-
Nach dem Erstellen der Anwendung musst du die Redirect-URI konfigurieren. Füge den kopierten Redirect URL (auto-populated)-Wert ein.
-
Finde die "Client ID" oder "Application ID" der neu erstellten Anwendung und kopiere sie.
-
Gehe zurück zum MCP Inspector und füge den "Client ID"-Wert im Bereich "OAuth Configuration" unter "Client ID" ein.
-
Gib folgenden Wert im Feld "Auth Params" ein, um die erforderlichen Berechtigungen für Todo-Operationen anzufordern:
{ "scope": "create:todos read:todos delete:todos" }
MCP Auth einrichten
In deinem MCP-Server-Projekt musst du das MCP Auth SDK installieren und so konfigurieren, dass es die Metadaten deines Autorisierungsservers verwendet.
- Python
- Node.js
Installiere zuerst das mcpauth
-Paket:
pip install mcpauth
Oder ein anderer Paketmanager deiner Wahl, wie uv
oder poetry
.
Installiere zuerst das mcp-auth
-Paket:
npm install mcp-auth
MCP Auth benötigt die Metadaten des Autorisierungsservers zur Initialisierung. Je nach Anbieter:
- Logto
- OAuth 2.0 / OIDC
Die Issuer-URL findest du auf der Anwendungsdetailseite in der Logto Console im Bereich "Endpoints & Credentials / Issuer endpoint". Sie sieht etwa so aus: https://my-project.logto.app/oidc
.
- Python
- Node.js
Aktualisiere die todo-manager.py
, um die MCP Auth-Konfiguration einzubinden:
from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config
auth_issuer = '<issuer-endpoint>' # Ersetze durch deinen Issuer-Endpunkt
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OIDC)
mcp_auth = MCPAuth(server=auth_server_config)
Aktualisiere die todo-manager.ts
, um die MCP Auth-Konfiguration einzubinden:
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
const authIssuer = '<issuer-endpoint>'; // Ersetze durch deinen Issuer-Endpunkt
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, { type: 'oidc' }),
});
Für OAuth 2.0-Anbieter musst du:
- In der Dokumentation deines Anbieters nach der Autorisierungsserver-URL suchen (oft als Issuer-URL oder Basis-URL bezeichnet)
- Manche Anbieter stellen dies unter
https://{your-domain}/.well-known/oauth-authorization-server
bereit - Im Admin-Bereich deines Anbieters unter OAuth/API-Einstellungen nachsehen
- Python
- Node.js
Aktualisiere die todo-manager.py
, um die MCP Auth-Konfiguration einzubinden:
from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config
auth_issuer = '<issuer-endpunkt>' # Ersetze dies durch deinen Aussteller-Endpunkt
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OAUTH) # oder AuthServerType.OIDC
mcp_auth = MCPAuth(server=auth_server_config)
Aktualisiere die todo-manager.ts
, um die MCP Auth-Konfiguration einzubinden:
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
const authIssuer = '<issuer-endpunkt>'; // Ersetze dies durch deinen Aussteller-Endpunkt
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, { type: 'oauth' }), // oder { type: 'oidc' }
});
In einigen Fällen kann die Antwort des Providers fehlerhaft oder nicht dem erwarteten Metadatenformat entsprechend sein. Wenn du sicher bist, dass der Provider konform ist, kannst du die Metadaten über die Konfigurationsoption transpiliieren:
- Python
- Node.js
mcp_auth = MCPAuth(
server=fetch_server_config(
# ...andere Optionen
transpile_data=lambda data: {**data, 'response_types_supported': ['code']}
)
)
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, {
// ...andere Optionen
transpileData: (data) => ({ ...data, response_types_supported: ['code'] }),
}),
});
Wenn dein Anbieter OAuth 2.0 Authorization Server Metadata nicht unterstützt, kannst du die Metadata-URL oder Endpunkte manuell angeben. Sieh dir Weitere Möglichkeiten zur Initialisierung von MCP Auth für mehr Details an.
- Python
- Node.js
Aktualisiere die todo-manager.py
, um die MCP Auth-Konfiguration einzubinden:
from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config
auth_issuer = '<issuer-endpoint>' # Ersetze durch deinen Issuer-Endpunkt
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OIDC)
mcp_auth = MCPAuth(server=auth_server_config)
Aktualisiere die todo-manager.ts
, um die MCP Auth-Konfiguration einzubinden:
// todo-manager.ts
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
const authIssuer = '<issuer-endpoint>'; // Ersetze durch deinen Issuer-Endpunkt
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, { type: 'oidc' }),
});
MCP-Server aktualisieren
Wir sind fast fertig! Jetzt aktualisieren wir den MCP-Server, um die MCP Auth-Route und Middleware-Funktion anzuwenden und die berechtigungsbasierte Zugangskontrolle für die Todo-Manager-Tools basierend auf den Benutzerberechtigungen zu implementieren.
- Python
- Node.js
@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
"""Neues Todo erstellen."""
return (
mcp_auth.auth_info.scopes
if mcp_auth.auth_info # Wird durch die Bearer-Auth-Middleware befüllt
else {"error": "Nicht authentifiziert"}
)
# ...
bearer_auth = Middleware(mcp_auth.bearer_auth_middleware("jwt"))
app = Starlette(
routes=[
# Metadaten-Route hinzufügen (`/.well-known/oauth-authorization-server`)
mcp_auth.metadata_route(),
# MCP-Server mit Bearer-Auth-Middleware schützen
Mount('/', app=mcp.sse_app(), middleware=[bearer_auth]),
],
)
server.tool(
'create-todo',
'Neues Todo erstellen',
{ content: z.string() },
async ({ content, authInfo }) => {
return {
content: [
{ type: 'text', text: JSON.stringify(authInfo?.scopes ?? { error: 'Nicht authentifiziert' }) },
],
};
}
);
// ...
app.use(mcpAuth.delegatedRouter());
app.use(mcpAuth.bearerAuth('jwt'));
Als Nächstes implementieren wir die spezifischen Tools.
Zuerst erstellen wir einen einfachen Todo-Service, der grundlegende CRUD-Operationen für die Verwaltung von Todo-Einträgen im Speicher bereitstellt.
- Python
- Node.js
# service.py
"""
Ein einfacher Todo-Service zu Demonstrationszwecken.
Verwendet eine In-Memory-Liste zur Speicherung der Todos.
"""
from datetime import datetime
from typing import List, Optional, Dict, Any
import random
import string
class Todo:
"""Repräsentiert einen Todo-Eintrag."""
def __init__(self, id: str, content: str, owner_id: str, created_at: str):
self.id = id
self.content = content
self.owner_id = owner_id
self.created_at = created_at
def to_dict(self) -> Dict[str, Any]:
"""Todo in ein Dictionary für die JSON-Serialisierung umwandeln."""
return {
"id": self.id,
"content": self.content,
"ownerId": self.owner_id,
"createdAt": self.created_at
}
class TodoService:
"""Ein einfacher Todo-Service zu Demonstrationszwecken."""
def __init__(self):
self._todos: List[Todo] = []
def get_all_todos(self, owner_id: Optional[str] = None) -> List[Dict[str, Any]]:
"""
Alle Todos abrufen, optional gefiltert nach owner_id.
Args:
owner_id: Falls angegeben, nur Todos dieses Benutzers zurückgeben
Returns:
Liste von Todo-Dictionaries
"""
if owner_id:
filtered_todos = [todo for todo in self._todos if todo.owner_id == owner_id]
return [todo.to_dict() for todo in filtered_todos]
return [todo.to_dict() for todo in self._todos]
def get_todo_by_id(self, todo_id: str) -> Optional[Todo]:
"""
Ein Todo anhand seiner ID abrufen.
Args:
todo_id: Die ID des abzurufenden Todos
Returns:
Todo-Objekt, falls gefunden, sonst None
"""
for todo in self._todos:
if todo.id == todo_id:
return todo
return None
def create_todo(self, content: str, owner_id: str) -> Dict[str, Any]:
"""
Ein neues Todo erstellen.
Args:
content: Der Inhalt des Todos
owner_id: Die ID des Benutzers, dem dieses Todo gehört
Returns:
Dictionary-Darstellung des erstellten Todos
"""
todo = Todo(
id=self._generate_id(),
content=content,
owner_id=owner_id,
created_at=datetime.now().isoformat()
)
self._todos.append(todo)
return todo.to_dict()
def delete_todo(self, todo_id: str) -> Optional[Dict[str, Any]]:
"""
Ein Todo anhand seiner ID löschen.
Args:
todo_id: Die ID des zu löschenden Todos
Returns:
Dictionary-Darstellung des gelöschten Todos, falls gefunden, sonst None
"""
for i, todo in enumerate(self._todos):
if todo.id == todo_id:
deleted_todo = self._todos.pop(i)
return deleted_todo.to_dict()
return None
def _generate_id(self) -> str:
"""Eine zufällige ID für ein Todo generieren."""
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
// todo-service.ts
type Todo = {
id: string;
content: string;
ownerId: string;
createdAt: string;
};
/**
* Ein einfacher Todo-Service zu Demonstrationszwecken.
* Verwendet ein In-Memory-Array zur Speicherung der Todos
*/
export class TodoService {
private readonly todos: Todo[] = [];
getAllTodos(ownerId?: string): Todo[] {
if (ownerId) {
return this.todos.filter((todo) => todo.ownerId === ownerId);
}
return this.todos;
}
getTodoById(id: string): Todo | undefined {
return this.todos.find((todo) => todo.id === id);
}
createTodo({ content, ownerId }: { content: string; ownerId: string }): Todo {
const todo: Todo = {
id: this.genId(),
content,
ownerId,
createdAt: new Date().toISOString(),
};
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
this.todos.push(todo);
return todo;
}
deleteTodo(id: string): Todo | undefined {
const index = this.todos.findIndex((todo) => todo.id === id);
if (index === -1) {
return undefined;
}
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
const [deleted] = this.todos.splice(index, 1);
return deleted;
}
private genId(): string {
return Math.random().toString(36).slice(2, 10);
}
}
Dann bestimmen wir in der Tool-Schicht, ob Operationen basierend auf den Benutzerberechtigungen erlaubt sind:
- Python
- Node.js
# todo-manager.py
from typing import Any, Optional
from mcpauth.errors import MCPAuthBearerAuthError
def assert_user_id(auth_info: Optional[dict]) -> str:
"""Benutzer-ID aus den Auth-Informationen extrahieren und validieren."""
subject = auth_info.get('subject') if auth_info else None
if not subject:
raise ValueError('Ungültige Auth-Informationen')
return subject
def has_required_scopes(user_scopes: list[str], required_scopes: list[str]) -> bool:
"""Prüfen, ob der Benutzer alle erforderlichen Berechtigungen hat."""
return all(scope in user_scopes for scope in required_scopes)
# Instanz von TodoService erstellen
todo_service = TodoService()
@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
"""Neues Todo erstellen.
Nur Benutzer mit 'create:todos'-Berechtigung dürfen Todos erstellen.
"""
# Authentifizierungsinformationen abrufen
auth_info = mcp_auth.auth_info
# Benutzer-ID validieren
try:
user_id = assert_user_id(auth_info)
except ValueError as e:
return {"error": str(e)}
# Prüfen, ob der Benutzer die erforderlichen Berechtigungen hat
if not has_required_scopes(auth_info.scopes if auth_info else [], ['create:todos']):
raise MCPAuthBearerAuthError('missing_required_scopes')
# Neues Todo erstellen
created_todo = todo_service.create_todo(content=content, owner_id=user_id)
# Erstelltes Todo zurückgeben
return created_todo.__dict__
# ...
Du findest unseren Beispielcode für alle weiteren Implementierungsdetails.
// todo-manager.ts
// ... weitere Importe
import assert from 'node:assert';
import { type AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
import { TodoService } from './todo-service.js';
const todoService = new TodoService();
const assertUserId = (authInfo?: AuthInfo) => {
const { subject } = authInfo ?? {};
assert(subject, 'Ungültige Auth-Informationen');
return subject;
};
/**
* Prüfen, ob der Benutzer alle erforderlichen Berechtigungen für eine Operation hat
*/
const hasRequiredScopes = (userScopes: string[], requiredScopes: string[]): boolean => {
return requiredScopes.every((scope) => userScopes.includes(scope));
};
server.tool(
'create-todo',
'Neues Todo erstellen',
{ content: z.string() },
({ content }: { content: string }, { authInfo }) => {
const userId = assertUserId(authInfo);
/**
* Nur Benutzer mit 'create:todos'-Berechtigung dürfen Todos erstellen
*/
if (!hasRequiredScopes(authInfo?.scopes ?? [], ['create:todos'])) {
throw new MCPAuthBearerAuthError('missing_required_scopes');
}
const createdTodo = todoService.createTodo({ content, ownerId: userId });
return {
content: [{ type: 'text', text: JSON.stringify(createdTodo) }],
};
}
);
// ...
Du findest unseren Beispielcode für alle weiteren Implementierungsdetails.
Checkpoint: Die todo-manager
-Tools ausführen
Starte deinen MCP-Server neu und öffne den MCP Inspector im Browser. Wenn du auf die Schaltfläche "Connect" klickst, solltest du zur Anmeldeseite deines Autorisierungsservers weitergeleitet werden.
Nachdem du dich angemeldet hast und zum MCP Inspector zurückkehrst, wiederhole die Aktionen aus dem vorherigen Checkpoint, um die Todo-Manager-Tools auszuführen. Dieses Mal kannst du die Tools mit deiner authentifizierten Benutzeridentität nutzen. Das Verhalten der Tools hängt von den Rollen und Berechtigungen ab, die deinem Benutzer zugewiesen sind:
-
Wenn du als User (nur mit
create:todos
-Berechtigung) angemeldet bist:- Du kannst neue Todos mit dem Tool
create-todo
erstellen - Du kannst nur deine eigenen Todos ansehen und löschen
- Du kannst keine Todos anderer Benutzer sehen oder löschen
- Du kannst neue Todos mit dem Tool
-
Wenn du als Admin (mit allen Berechtigungen:
create:todos
,read:todos
,delete:todos
) angemeldet bist:- Du kannst neue Todos erstellen
- Du kannst mit dem Tool
get-todos
alle Todos im System ansehen - Du kannst mit dem Tool
delete-todo
beliebige Todos löschen, unabhängig davon, wer sie erstellt hat
Du kannst diese verschiedenen Berechtigungsstufen testen, indem du:
- Die aktuelle Sitzung abmeldest (klicke auf "Disconnect" im MCP Inspector)
- Dich mit einem anderen Benutzerkonto anmeldest, das andere Rollen/Berechtigungen hat
- Die gleichen Tools erneut ausprobierst, um zu sehen, wie sich das Verhalten je nach Benutzerberechtigungen ändert
Das zeigt, wie rollenbasierte Zugangskontrolle (RBAC) in der Praxis funktioniert, wobei verschiedene Benutzer unterschiedliche Zugriffsebenen auf die Funktionen des Systems haben.
- Python
- Node.js
Sieh dir das MCP Auth Python SDK Repository für den vollständigen Code des MCP-Servers (OIDC-Version) an.
Sieh dir das MCP Auth Node.js SDK Repository für den vollständigen Code des MCP-Servers (OIDC-Version) an.
Abschließende Hinweise
🎊 Glückwunsch! Du hast das Tutorial erfolgreich abgeschlossen. Lass uns zusammenfassen, was wir gemacht haben:
- Einen einfachen MCP-Server mit Todo-Management-Tools (
create-todo
,get-todos
,delete-todo
) eingerichtet - Rollenbasierte Zugangskontrolle (RBAC) mit unterschiedlichen Berechtigungsstufen für Benutzer und Admins implementiert
- Den MCP-Server mit einem Autorisierungsserver über MCP Auth integriert
- Den MCP Inspector so konfiguriert, dass Benutzer authentifiziert werden und Zugangstokens mit Berechtigungen zum Aufrufen von Tools verwendet werden
Sieh dir auch andere Tutorials und die Dokumentation an, um das Beste aus MCP Auth herauszuholen.