Zum Hauptinhalt springen
Version: 0.2.0-beta.1

Tutorial: Baue einen Todo-Manager

In diesem Tutorial bauen wir einen Todo-Manager-MCP-Server mit Benutzer-Authentifizierung und -Autorisierung. Nach der neuesten MCP-Spezifikation agiert unser MCP-Server als OAuth 2.0 Ressourcenserver (Resource Server), der Zugangstokens validiert und berechtigungsbasierte Berechtigungen durchsetzt.

Nach Abschluss dieses Tutorials hast du:

  • ✅ Ein grundlegendes Verständnis, wie du rollenbasierte Zugangskontrolle (RBAC) in deinem MCP-Server einrichtest.
  • ✅ Einen MCP-Server, der als Ressourcenserver agiert und Zugangstokens verwendet, die von einem Autorisierungsserver ausgegeben werden.
  • ✅ Eine funktionierende Implementierung der durch Berechtigungen gesteuerten Zugriffskontrolle für Todo-Operationen.

Überblick

Das Tutorial umfasst folgende Komponenten:

  • MCP-Client (MCP Inspector): Ein visuelles Test-Tool für MCP-Server, das als OAuth 2.0/OIDC-Client agiert. Es startet den Autorisierungs-Flow mit dem Autorisierungsserver und erhält Zugangstokens, um Anfragen an den MCP-Server zu authentifizieren.
  • Autorisierungsserver: Ein OAuth 2.1- oder OpenID Connect-Anbieter, der Benutzeridentitäten verwaltet, Benutzer authentifiziert und Zugangstokens mit passenden Berechtigungen an autorisierte Clients ausgibt.
  • MCP-Server (Ressourcenserver): Nach der neuesten MCP-Spezifikation agiert der MCP-Server als Ressourcenserver im OAuth 2.0-Framework. Er validiert Zugangstokens, die vom Autorisierungsserver ausgegeben wurden, und erzwingt berechtigungsbasierte Berechtigungen für Todo-Operationen.

Diese Architektur folgt dem Standard-OAuth 2.0-Flow, bei dem:

  • Der MCP Inspector geschützte Ressourcen im Namen des Benutzers anfordert
  • Der Autorisierungsserver den Benutzer authentifiziert und Zugangstokens ausgibt
  • Der MCP-Server Tokens validiert und geschützte Ressourcen basierend auf gewährten Berechtigungen bereitstellt

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 unterstützen. Berechtigungen (Scopes) repräsentieren die Rechte, die einem Benutzer gewährt wurden.

Logto bietet RBAC-Unterstützung durch seine API-Ressourcen (gemäß RFC 8707: Resource Indicators for OAuth 2.0) und Rollenfunktionen. So richtest du es ein:

  1. Melde dich bei der Logto Console (oder deiner selbst gehosteten Logto Console) an.

  2. 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"
  3. 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
  4. 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.

Tokens validieren und Berechtigungen prüfen

Nach der neuesten MCP-Spezifikation agiert der MCP-Server als Ressourcenserver (Resource Server) im OAuth 2.0-Framework. Als Ressourcenserver hat der MCP-Server folgende Aufgaben:

  1. Token-Validierung: Überprüfe die Echtheit und Integrität der von MCP-Clients erhaltenen Zugangstokens
  2. Berechtigungsdurchsetzung: Extrahiere und prüfe die Berechtigungen aus dem Zugangstoken, um festzustellen, welche Operationen der Client ausführen darf
  3. Ressourcenschutz: Gebe geschützte Ressourcen (Tools ausführen) nur frei, wenn der Client gültige Tokens mit ausreichenden Berechtigungen vorlegt

Wenn dein MCP-Server eine Anfrage erhält, läuft der Validierungsprozess wie folgt ab:

  1. Extrahiere das Zugangstoken aus dem Authorization-Header (Bearer-Token-Format)
  2. Überprüfe die Signatur und das Ablaufdatum des Zugangstokens
  3. Extrahiere die Berechtigungen und Benutzerinformationen aus dem validierten Token
  4. Prüfe, ob das Token die erforderlichen Berechtigungen für die angeforderte Operation enthält

Beispiel: Wenn ein Benutzer einen neuen Todo-Eintrag erstellen möchte, muss sein Zugangstoken die Berechtigung create:todos enthalten. So funktioniert der Validierungs-Flow des Ressourcenservers:

Dynamische Client-Registrierung

Die dynamische Client-Registrierung ist für dieses Tutorial nicht erforderlich, kann aber nützlich sein, wenn du den MCP-Client-Registrierungsprozess 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.

hinweis

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 erstellen
  • get-todos: Alle Todos auflisten
  • delete-todo: Ein Todo anhand der ID löschen

Zur Zugriffskontrolle auf diese Tools definieren wir folgende Berechtigungen:

  • create:todos: Erlaubt das Erstellen neuer Todo-Einträge
  • delete:todos: Erlaubt das Löschen bestehender Todo-Einträge
  • read:todos: Erlaubt das Abfragen und Abrufen aller Todo-Einträge

Rollen und Berechtigungen

Wir definieren zwei Rollen mit unterschiedlichen Zugriffsrechten:

Rollecreate:todosread:todosdelete: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:

  • Benutzer haben nicht die Berechtigungen read:todos oder delete:todos, können aber trotzdem:
    • Ihre eigenen Todo-Einträge lesen
    • Ihre eigenen Todo-Einträge löschen
  • Admins haben volle Berechtigungen (read:todos und delete:todos) und können:
    • Alle Todo-Einträge im System sehen
    • 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 Rechte für eigene Ressourcen gewährt, während administrative Rollen explizite Rechte für alle Ressourcen erhalten.

Mehr erfahren

Um tiefer in RBAC-Konzepte und Best Practices einzutauchen, siehe Mastering RBAC: A Comprehensive Real-World Example.

Autorisierung im 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 mit verschiedenen Anbietern:

Logto bietet RBAC-Unterstützung durch seine API-Ressourcen und Rollenfunktionen. So richtest du es ein:

  1. Melde dich bei der Logto Console (oder deiner selbst gehosteten Logto Console) an.

  2. Erstelle API-Ressource und Berechtigungen:

    • Gehe zu "API-Ressourcen"
    • Erstelle eine neue API-Ressource namens "Todo Manager" und verwende http://localhost:3001 als Ressourcenindikator.
      • Wichtig: Der Ressourcenindikator muss mit der URL deines MCP-Servers übereinstimmen. Für dieses Tutorial verwenden wir http://localhost:3001, da unser MCP-Server auf Port 3001 läuft. In Produktion verwende deine tatsächliche MCP-Server-URL (z. B. https://your-mcp-server.example.com).
    • Erstelle folgende Berechtigungen:
      • create:todos: "Neue Todo-Einträge erstellen"
      • read:todos: "Alle Todo-Einträge lesen"
      • delete:todos: "Beliebigen Todo-Eintrag löschen"
  3. 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".
  4. 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
Programmatische Rollenverwaltung

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 Bau von Admin-Panels.

Beim Anfordern eines Zugangstokens wird Logto die Berechtigungen im scope-Anspruch des Tokens basierend auf den Rollenzuweisungen des Benutzers eintragen.

Nach der Konfiguration deines Autorisierungsservers erhalten Benutzer Zugangstokens mit ihren gewährten Berechtigungen. Der MCP-Server verwendet 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

Neues Python-Projekt einrichten:

mkdir mcp-todo-server
cd mcp-todo-server

# Neues Python-Projekt initialisieren
uv init

# Neue virtuelle Umgebung mit uv erstellen
uv venv

# Virtuelle Umgebung aktivieren (optional bei 'uv run')
source .venv/bin/activate
hinweis

Dieses Projekt verwendet uv für das Paketmanagement, aber du kannst auch andere Paketmanager wie pip, poetry oder conda verwenden.

MCP-SDK und Abhängigkeiten installieren

Installiere die benötigten Abhängigkeiten:

uv add "mcp[cli]" uvicorn starlette

MCP-Server erstellen

Erstelle zunächst einen Basis-MCP-Server mit den Tool-Definitionen:

Erstelle eine Datei namens server.py und füge folgenden Code hinzu:

# server.py

import contextlib
from typing import Any
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount

# Initialisiere den FastMCP-Server
mcp = FastMCP(name="Todo Manager", stateless_http=True, streamable_http_path='/')

@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
    """Neues Todo erstellen. Erfordert 'create:todos'-Berechtigung."""
    return {"error": "Not implemented"}

@mcp.tool()
def get_todos() -> dict[str, Any]:
    """Todos auflisten. Benutzer mit 'read:todos'-Berechtigung sehen alle Todos."""
    return {"error": "Not implemented"}

@mcp.tool()
def delete_todo(id: str) -> dict[str, Any]:
    """Todo anhand der ID löschen. Benutzer können ihre eigenen Todos löschen."""
    return {"error": "Not implemented"}

@contextlib.asynccontextmanager
async def lifespan(app: Starlette):
    async with contextlib.AsyncExitStack() as stack:
        await stack.enter_async_context(mcp.session_manager.run())
        yield

# App erstellen
app = Starlette(
    routes=[
        Mount("/", app=mcp.streamable_http_app()),
    ],
    lifespan=lifespan,
)

Starte den Server mit:

# Todo Manager Server mit uvicorn starten
uvicorn server:app --host 127.0.0.1 --port 3001

# Oder mit uv:
# uv run uvicorn server:app --host 127.0.0.1 --port 3001

MCP-Server inspizieren

MCP Inspector klonen und starten

Nachdem der MCP-Server läuft, können wir den MCP Inspector verwenden, um zu prüfen, ob Tools verfügbar sind.

Die offizielle MCP Inspector v0.16.2 hat einige Bugs, die die Authentifizierungsfunktionalität beeinträchtigen. Um diese Probleme zu beheben, haben wir eine gepatchte Version des MCP Inspectors erstellt, die notwendige Fixes für OAuth/OIDC-Authentifizierungsflows enthält. Wir haben auch Pull Requests an das offizielle Repository eingereicht, um diese Fixes beizutragen.

Um den MCP Inspector zu starten, verwende folgenden Befehl (Node.js erforderlich):

git clone https://github.com/mcp-auth/inspector.git -b patch/0.16.2-fixes
cd inspector
npm install
npm run dev

Der MCP Inspector öffnet sich automatisch im Standardbrowser, oder du kannst ihn manuell über den Link aus der Terminalausgabe aufrufen (achte darauf, den Link mit dem Parameter MCP_PROXY_AUTH_TOKEN zu verwenden, z. B. http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=458ae4a4...acab1907).

MCP Inspector mit dem MCP-Server verbinden

Vor dem Fortfahren prüfe folgende Konfiguration im MCP Inspector:

  • Transport-Typ: Setze auf Streamable HTTP.
  • URL: Setze auf die URL deines MCP-Servers. In unserem Fall: http://localhost:3001.

Jetzt kannst du auf den "Connect"-Button klicken, um zu sehen, ob der MCP Inspector eine Verbindung zum MCP-Server herstellen kann. Wenn alles funktioniert, siehst du den Status "Connected" im MCP Inspector.

Checkpoint: Todo-Manager-Tools ausführen

  1. Klicke im oberen Menü des MCP Inspectors auf den Tab "Tools".
  2. Klicke auf den Button "List Tools".
  3. Du solltest die Tools create-todo, get-todos und delete-todo aufgelistet sehen. Klicke darauf, um die Tool-Details zu öffnen.
  4. Rechts solltest du den Button "Run Tool" sehen. Klicke darauf und gib die erforderlichen Parameter ein, um das Tool auszuführen.
  5. Du solltest das Tool-Ergebnis mit der JSON-Antwort {"error": "Not implemented"} sehen.

MCP Inspector erster Lauf

Integration mit deinem Autorisierungsserver

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 Utilities von MCP Auth verwenden, um die Metadaten automatisch abzurufen.
  • Wenn dein Autorisierungsserver diese Standards nicht unterstützt, musst du die Metadaten-URL oder Endpunkte manuell in der MCP-Server-Konfiguration angeben. Siehe die Dokumentation deines Anbieters für die spezifischen Endpunkte.
Wie du den MCP Inspector als Client im 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 im Autorisierungsserver registrieren.
Verstehe Token-Request-Parameter

Beim Anfordern von Zugangstokens von verschiedenen Autorisierungsservern gibt es verschiedene Ansätze, um die Zielressource und Berechtigungen anzugeben. Hier 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": "http://localhost:3001",
        "scope": "create:todos read:todos"
      }
    • Der Server gibt Tokens aus, die speziell an die angeforderte Ressource gebunden sind
  • Audience-basiert:

    • Verwendet den Parameter audience, um den beabsichtigten Token-Empfänger anzugeben
    • Ähnlich wie Ressourcenindikatoren, aber mit anderen Semantiken
    • Beispielanfrage:
      {
        "audience": "todo-api",
        "scope": "create:todos read:todos"
      }
  • Nur Scope-basiert:

    • Verwendet ausschließlich Berechtigungen ohne resource/audience-Parameter
    • Traditioneller OAuth 2.0-Ansatz
    • Beispielanfrage:
      {
        "scope": "todo-api:create todo-api:read openid profile"
      }
    • Oft werden Präfixe verwendet, um Berechtigungen zu namespacen
    • Häufig in einfacheren OAuth 2.0-Implementierungen
Best Practices
  • Prüfe die Dokumentation deines Anbieters auf unterstützte Parameter
  • Manche Anbieter unterstützen mehrere Ansätze gleichzeitig
  • Ressourcenindikatoren bieten bessere Sicherheit durch Audience-Restriktion
  • Verwende 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

Die Integration des Todo-Managers mit Logto ist einfach, da es ein OpenID Connect-Anbieter ist, der Ressourcenindikatoren und Berechtigungen unterstützt. So kannst du deine Todo-API mit http://localhost:3001 als Ressourcenindikator absichern.

Da Logto noch keine Dynamic Client Registration unterstützt, musst du den MCP Inspector manuell als Client in deinem Logto-Tenant registrieren:

  1. Öffne deinen MCP Inspector, gehe zur Authentifizierungskonfiguration und klicke auf die "OAuth2.0 Flow"-Konfiguration. Kopiere den Redirect URI-Wert, z. B. http://localhost:6274/oauth/callback.
  2. Melde dich bei der Logto Console (oder deiner selbst gehosteten Logto Console) an.
  3. Navigiere zum Tab "Anwendungen", klicke auf "Anwendung erstellen". Unten auf der Seite klicke auf "App ohne Framework erstellen".
  4. Fülle die Anwendungsdetails aus und klicke auf "Anwendung erstellen":
    • Anwendungstyp auswählen: "Single-page application" wählen.
    • Anwendungsname: Z. B. "MCP Inspector".
  5. Im Bereich "Einstellungen / Redirect URIs" füge den kopierten Redirect URI ein. Klicke dann unten auf "Änderungen speichern".
  6. Im oberen Bereich siehst du den Wert "App ID". Kopiere ihn.
  7. Gehe zurück zum MCP Inspector und füge die "App ID" in der Authentifizierungskonfiguration unter "OAuth2.0 Flow" im Feld "Client ID" ein.
  8. Im Feld "Scope" gib ein: create:todos read:todos delete:todos. So enthält das Zugangstoken von Logto die nötigen Berechtigungen für den Todo-Manager.

MCP Auth einrichten

Installiere zunächst das MCP Auth SDK in deinem MCP-Server-Projekt.

uv add mcpauth==0.2.0b1

Jetzt müssen wir MCP Auth im MCP-Server initialisieren. Das umfasst zwei Hauptschritte:

  1. Metadaten des Autorisierungsservers abrufen: Wird für die spätere MCP Auth-Überprüfung der vom Autorisierungsserver ausgegebenen Zugangstokens verwendet und um die Issuer-ID des Auth-Servers in den Ressourcenmetadaten einzutragen
  2. Geschützte Ressourcenmetadaten konfigurieren: Definiere die Ressourcenkennung deines MCP-Servers und unterstützte Berechtigungen

Schritt 1: Metadaten des Autorisierungsservers abrufen

Laut OAuth / OIDC-Spezifikation können wir die Metadaten des Autorisierungsservers anhand der Issuer-URL abrufen.

In Logto findest du die Issuer-URL auf der Anwendungsdetailseite in der Logto Console unter "Endpoints & Credentials / Issuer endpoint". Sie sieht aus wie https://my-project.logto.app/oidc.

Jetzt rufe die Metadaten des Autorisierungsservers mit der MCP Auth Utility-Funktion ab:

from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config

issuer_url = "<issuer-url>"  # Ersetze durch die Issuer-URL deines Autorisierungsservers

# Autorisierungsserver-Konfiguration abrufen
auth_server_config = fetch_server_config(issuer_url, AuthServerType.OIDC) # oder AuthServerType.OAUTH

Wenn du alternative Wege benötigst, um die Metadaten des Autorisierungsservers abzurufen oder die Konfiguration anpassen möchtest, siehe Andere Wege zur Konfiguration der Autorisierungsserver-Metadaten.

Schritt 2: Geschützte Ressourcenmetadaten konfigurieren

Als nächstes konfigurieren wir die Protected Resource Metadata beim Erstellen der MCP Auth-Instanz. Anschließend stellt der MCP-Server die in MCP Auth konfigurierten Ressourcenmetadaten bereit.

# server.py

# weitere Importe...
from mcpauth.types import ResourceServerConfig, ResourceServerMetadata

# Ressourcenkennung für diesen MCP-Server definieren
resource_id = "http://localhost:3001"

mcp_auth = MCPAuth(
    protected_resources=ResourceServerConfig(
        metadata=ResourceServerMetadata(
            resource=resource_id,
            # Autorisierungsserver-Metadaten aus dem vorherigen Schritt
            authorization_servers=[auth_server_config],
            # Unterstützte Berechtigungen dieses MCP-Servers
            scopes_supported=[
                "create:todos",
                "read:todos",
                "delete:todos"
            ]
        )
    )
)

MCP-Server aktualisieren

Fast geschafft! Jetzt aktualisieren wir den MCP-Server, um die MCP Auth-Route und Middleware-Funktion anzuwenden und die berechtigungsbasierte Zugriffskontrolle für die Todo-Manager-Tools basierend auf den Benutzerberechtigungen zu implementieren.

Jetzt Protected Resource Metadata-Routen anwenden, damit MCP-Clients die erwarteten Ressourcenmetadaten vom MCP-Server abrufen können.

# server.py

# ..weitere Codes

app = Starlette(
    routes=[
        # Protected Resource Metadata-Routen einrichten
        # Stellt Metadaten über diesen Ressourcenserver für OAuth-Clients bereit
        *mcp_auth.resource_metadata_router().routes,
        Mount("/", app=mcp.streamable_http_app()),
    ],
    lifespan=lifespan,
)

Als nächstes wenden wir die MCP Auth-Middleware auf den MCP-Server an. Diese Middleware übernimmt Authentifizierung und Autorisierung für eingehende Anfragen und stellt sicher, dass nur autorisierte Benutzer Zugriff auf die Todo-Manager-Tools haben.

# server.py

# weitere Importe...
from starlette.middleware import Middleware

# weitere Codes...

# Middleware erstellen
bearer_auth = Middleware(mcp_auth.bearer_auth_middleware('jwt', resource=resource_id, audience=resource_id))

app = Starlette(
    routes=[
        *mcp_auth.resource_metadata_router().routes,
        # MCP Auth-Middleware anwenden
        Mount("/", app=mcp.streamable_http_app(), middleware=[bearer_auth]),
    ],
    lifespan=lifespan,
)

Jetzt können wir die Todo-Manager-Tools so aktualisieren, dass sie die MCP Auth-Middleware für Authentifizierung und Autorisierung nutzen.

Aktualisiere die Implementierung der Tools wie folgt.

# server.py

# weitere Importe...

from typing import Any, List, Optional
from mcpauth.exceptions import MCPAuthBearerAuthException, BearerAuthExceptionCode
from mcpauth.types import AuthInfo, ResourceServerConfig, ResourceServerMetadata

# Wird im nächsten Abschnitt erwähnt
from service import TodoService

def assert_user_id(auth_info: Optional[AuthInfo]) -> str:
    """Stellt sicher, dass auth_info eine gültige Benutzer-ID enthält und gibt sie zurück."""
    if not auth_info or not auth_info.subject:
        raise Exception("Invalid auth info")
    return auth_info.subject

def has_required_scopes(user_scopes: List[str], required_scopes: List[str]) -> bool:
    """Prüft, ob der Benutzer alle erforderlichen Berechtigungen hat."""
    return all(scope in user_scopes for scope in required_scopes)

# TodoService-Instanz erstellen
todo_service = TodoService()

@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
    """Neues Todo erstellen. Erfordert 'create:todos'-Berechtigung."""
    auth_info = mcp_auth.auth_info
    user_id = assert_user_id(auth_info)
    
    # Nur Benutzer mit 'create:todos'-Berechtigung dürfen Todos erstellen
    user_scopes = auth_info.scopes if auth_info else []
    if not has_required_scopes(user_scopes, ["create:todos"]):
        raise MCPAuthBearerAuthException(BearerAuthExceptionCode.MISSING_REQUIRED_SCOPES)
    
    created_todo = todo_service.create_todo(content=content, owner_id=user_id)
    return created_todo

@mcp.tool()
def get_todos() -> dict[str, Any]:
    """
    Todos auflisten. Benutzer mit 'read:todos'-Berechtigung sehen alle Todos,
    sonst nur ihre eigenen Todos.
    """
    auth_info = mcp_auth.auth_info
    user_id = assert_user_id(auth_info)
    
    # Mit 'read:todos'-Berechtigung Zugriff auf alle Todos,
    # sonst nur eigene Todos
    user_scopes = auth_info.scopes if auth_info else []
    todo_owner_id = None if has_required_scopes(user_scopes, ["read:todos"]) else user_id
    
    todos = todo_service.get_all_todos(todo_owner_id)
    return {"todos": todos}

@mcp.tool()
def delete_todo(id: str) -> dict[str, Any]:
    """
    Todo anhand der ID löschen. Benutzer können ihre eigenen Todos löschen.
    Benutzer mit 'delete:todos'-Berechtigung können beliebige Todos löschen.
    """
    auth_info = mcp_auth.auth_info
    user_id = assert_user_id(auth_info)
    
    todo = todo_service.get_todo_by_id(id)
    
    if not todo:
        return {"error": "Failed to delete todo"}
    
    # Benutzer können nur ihre eigenen Todos löschen
    # Benutzer mit 'delete:todos'-Berechtigung können beliebige Todos löschen
    user_scopes = auth_info.scopes if auth_info else []
    if todo.owner_id != user_id and not has_required_scopes(user_scopes, ["delete:todos"]):
        return {"error": "Failed to delete todo"}
    
    deleted_todo = todo_service.delete_todo(id)
    
    if deleted_todo:
        return {
            "message": f"Todo {id} deleted",
            "details": deleted_todo
        }
    else:
        return {"error": "Failed to delete todo"}

Jetzt erstelle den "Todo-Service", der in obigem Code verwendet wird, um die zugehörige Funktionalität zu implementieren:

Erstelle die Datei service.py für den Todo-Service:

"""
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 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]]:
        """
        Gibt alle Todos zurück, 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]:
        """
        Gibt ein Todo anhand seiner ID zurück.
        
        Args:
            todo_id: Die ID des 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]:
        """
        Erstellt ein neues Todo.
        
        Args:
            content: Inhalt des Todos
            owner_id: ID des Benutzers, dem das 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]]:
        """
        Löscht ein Todo anhand seiner ID.
        
        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:
        """Generiert eine zufällige ID für ein Todo."""
        return ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))

🎉 Glückwunsch! Wir haben erfolgreich einen vollständigen MCP-Server mit Authentifizierung und Autorisierung implementiert!

Du kannst auch unseren Beispielcode als Referenz ansehen:

info

Siehe das MCP Auth Python SDK Repository für den vollständigen Code des MCP-Servers (OIDC-Version).

Checkpoint: Die todo-manager-Tools ausführen

Starte deinen MCP-Server neu und öffne den MCP Inspector im Browser. Wenn du auf den "Connect"-Button klickst, solltest du zur Anmeldeseite deines Autorisierungsservers weitergeleitet werden.

Nach der Anmeldung und Rückkehr zum MCP Inspector 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
  • Wenn du als Admin (mit allen Berechtigungen: create:todos, read:todos, delete:todos) angemeldet bist:

    • Du kannst neue Todos erstellen
    • Du kannst alle Todos im System mit dem Tool get-todos ansehen
    • Du kannst beliebige Todos mit dem Tool delete-todo löschen, unabhängig davon, wer sie erstellt hat

Du kannst diese unterschiedlichen Berechtigungsstufen testen, indem du:

  1. Die aktuelle Sitzung abmeldest (klicke auf "Disconnect" im MCP Inspector)
  2. Dich mit einem anderen Benutzerkonto anmeldest, das andere Rollen/Berechtigungen hat
  3. Die gleichen Tools erneut ausprobierst, um zu sehen, wie sich das Verhalten je nach Benutzerberechtigungen ändert

Das demonstriert, wie rollenbasierte Zugangskontrolle (RBAC) in der Praxis funktioniert, wobei verschiedene Benutzer unterschiedliche Zugriffsebenen auf die Systemfunktionen haben.

MCP Inspector Todo-Manager-Tool-Ergebnis

info

Siehe das MCP Auth Python SDK Repository für den vollständigen Code des MCP-Servers (OIDC-Version).

Abschließende Hinweise

🎊 Glückwunsch! Du hast das Tutorial erfolgreich abgeschlossen. Lass uns zusammenfassen, was wir gemacht haben:

  • Einen Basis-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 Aufruf der Tools verwendet werden

Sieh dir auch andere Tutorials und die Dokumentation an, um das Beste aus MCP Auth herauszuholen.