FastMCP Advanced: Composition, Resources, Prompts, OAuth2

FastMCP Advanced: Composition, Resources, Prompts, OAuth2, Multi-Tenant. Code-Beispiele aus Pexon-Produktionssetups jenseits des Hello-World-Tutorials.

27 Juli. 2026 | AI

Inhaltsverzeichnis

Pexon Consulting | 11 Min. Lesezeit | 10.06.2026


TL;DR

FastMCP Advanced Patterns sind die Komponenten, die in jedem FastMCP-Setup ab dem zweiten oder dritten produktiven Server gebraucht werden: Server-Composition für modulare Architektur, Resources statt Tools für Read-Only-Daten, Prompts als wiederverwendbare Templates, OAuth2-Auth für produktive Sicherheit, Multi-Tenant-Setup für Mandanten-getrennte Daten. Pexon Consulting baut diese Patterns als Standard-Komponenten in jedem MCP-Sprint — hier die konkreten Code-Beispiele und Production-Hinweise.

Pattern Wofür Wann produktiv nötig
Composition Modulare Server aus Sub-Servern ab 2-3 Servern mit Überlapp
Resources Read-Only-Daten (Schemas, Configs) bei großem Doku-Bestand
Prompts Wiederverwendbare Prompt-Templates bei mehreren Apps am gleichen Server
OAuth2 User-Identity statt Service-Account ab Production-Stufe
Multi-Tenant Mandanten-Trennung bei SaaS-Setups oder Multi-Werk

Warum FastMCP Advanced jenseits des Tutorials

Wer mit dem FastMCP-Einstiegs-Blog gestartet ist, hat einen produktiven Single-Server mit ein paar Tools. Das funktioniert für die ersten Use-Cases. Bei zwei oder drei produktiven Servern wird klar: Mit Tools allein kommst du nicht weit, wenn du saubere Architektur willst.

Die häufigsten Skalierungs-Pains: Erstens überlappende Tools zwischen Servern (Customer-Lookup im CRM-Server und nochmal im Service-Server). Zweitens unstrukturierte Daten, die als Tool-Output ständig durchgereicht werden müssen (Datenbank-Schemas, Konfigurations-Dateien). Drittens Prompt-Duplikation zwischen Apps, die alle den gleichen Server nutzen. Viertens Auth, das initial mit Service-Accounts funktioniert, aber bei produktiven Usern Identity-Pass-Through braucht.

FastMCP hat für jeden dieser Pains ein eingebautes Pattern. Die meisten werden in Tutorials nicht behandelt, weil sie über das Hello-World hinausgehen. Hier behandelt.


Pattern 1: Server-Composition

Statt eines monolithischen Servers mit 30 Tools baust du mehrere kleinere Server und mountest sie zusammen. Jeder Sub-Server hat seine eigene Verantwortung, der Top-Level-Server bündelt sie.

from fastmcp import FastMCP

customer_server = FastMCP("customer-server")

@customer_server.tool
def search_customer(query: str) -> list[dict]:
    """Suche Kunden im CRM."""
    return crm.search(query)

@customer_server.tool
def get_customer_history(customer_id: str) -> list[dict]:
    """Holt Bestell-Historie des Kunden."""
    return crm.get_history(customer_id)


order_server = FastMCP("order-server")

@order_server.tool
def create_order(customer_id: str, items: list[dict]) -> dict:
    """Legt eine neue Bestellung im ERP an."""
    return erp.create_order(customer_id, items)


main = FastMCP("crm-orders")
main.mount(customer_server, prefix="customer")
main.mount(order_server, prefix="order")

main.run()

Was hier passiert: Der Top-Level-Server crm-orders exponiert customer_search_customer, customer_get_customer_history, order_create_order etc. Die Sub-Server bleiben separat testbar, der Code pro Domain ist sauber getrennt.

Bei einem Pexon-Kunden haben wir damit einen 47-Tool-Monolithen in fünf Sub-Server mit je 7-12 Tools refactored. Resultat: Team kann unabhängig an den Sub-Servern arbeiten, Test-Setup pro Server ist einfacher, Deployment-Risiko sinkt.


Pattern 2: Resources statt Tools

Tools sind für Aktionen — etwas tun, etwas suchen, etwas anlegen. Für statische oder semi-statische Daten ist die richtige FastMCP-Primitive @mcp.resource. Resources werden vom Agent gelesen, ohne dass sie als Tool-Call im Agent-Loop erscheinen.

from fastmcp import FastMCP

mcp = FastMCP("data-server")

@mcp.resource("crm://schema")
def crm_schema() -> str:
    """JSON-Schema des CRM-Datenmodells."""
    return open("schemas/crm.json").read()

@mcp.resource("crm://field-glossary")
def field_glossary() -> str:
    """Glossar der CRM-Felder mit Geschäfts-Bedeutung."""
    return open("docs/crm-glossary.md").read()

@mcp.resource("crm://standard-segments")
def standard_segments() -> dict:
    """Standard-Kundensegmente und ihre Definition."""
    return {
        "premium": {"min_revenue": 100000, "tier": "A"},
        "standard": {"min_revenue": 25000, "tier": "B"},
        "small": {"min_revenue": 0, "tier": "C"},
    }

Warum das ein Game-Changer ist: Der Agent kann Resources lesen, ohne den Tool-Use-Loop zu strapazieren. Bei einem typischen Customer-Service-Setup haben wir vor der Resource-Migration 32 Tools mit Schema-Erklärungen in den Beschreibungen gehabt — Tokens waren teuer und das Modell verwirrt. Nach Migration auf Resources hat der Agent 12 Tools und greift bei Bedarf auf 8 Resources zu. Tool-Treffer-Quote stieg um 23 Prozentpunkte, Token-Verbrauch pro Konversation sank um 41%.


Pattern 3: Prompts als wiederverwendbare Templates

Wenn mehrere Apps den gleichen Server nutzen, brauchst du standardisierte Prompts. FastMCP hat @mcp.prompt als eingebaute Primitive.

from fastmcp import FastMCP

mcp = FastMCP("support-server")

@mcp.prompt
def customer_inquiry_template(
    customer_id: str,
    inquiry_type: str = "general",
) -> list[dict]:
    """
    Standardisierter Prompt für Customer-Inquiry-Bearbeitung.
    """
    return [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": f"""
            Beantworte eine Anfrage zum Kunden {customer_id}.
            Inquiry-Typ: {inquiry_type}
            
            Vorgehen:
            1. Hole Kundendaten via customer_search_customer.
            2. Hole Bestell-Historie via customer_get_customer_history.
            3. Wenn schreibende Aktion nötig: customer-Bestätigung einholen.
        """},
    ]

Die App-Seite ruft den Prompt ab und nutzt ihn — Prompt-Logik liegt nicht in der App, sondern beim Server. Damit kann die Prompt-Engineering-Verantwortung auf den Server-Owner liegen, die App-Entwickler konsumieren standardisierte Best-Practice-Prompts. Bei einem Pilotkunden mit drei Apps am gleichen MCP-Server haben wir damit Inkonsistenzen in der Prompt-Qualität beseitigt — alle drei Apps nutzten plötzlich die gleichen, qualitätsgesicherten Prompts.


Pattern 4: OAuth2-Auth mit User-Identity

Tutorial-Code arbeitet mit Service-Accounts: „MCP-Server hat einen Token, alle Tool-Calls laufen über diesen Token“. Für Production reicht das nicht — du brauchst die echte User-Identity im Tool-Handler, sonst hast du keinen sauberen Audit-Trail.

FastMCP unterstützt seit Version 2.3 native OAuth2-Auth mit User-Identity-Pass-Through.

from fastmcp import FastMCP, Context
from fastmcp.auth import BearerTokenAuth
from fastmcp.exceptions import ToolError

auth = BearerTokenAuth(
    jwks_url="https://login.microsoftonline.com/TENANT_ID/discovery/v2.0/keys",
    issuer="https://login.microsoftonline.com/TENANT_ID/v2.0",
    audience="api://mcp-customer-server",
)

mcp = FastMCP("customer-server", auth=auth)

@mcp.tool
def get_customer_details(customer_id: str, ctx: Context) -> dict:
    """Holt Kundendetails mit User-Identity-Check."""
    user_email = ctx.auth.claims.get("email")
    user_roles = ctx.auth.claims.get("roles", [])
    
    if "customer.read" not in user_roles:
        raise ToolError(f"User {user_email} hat keine Lese-Berechtigung auf Kundendaten.")
    
    audit_log.write(user=user_email, action="customer.read", target=customer_id)
    return crm.get(customer_id)

Was hier passiert: Der MCP-Server validiert den Bearer-Token gegen das JWKS-Endpoint von Entra ID. User-Claims (Email, Rollen) landen im Context und sind im Tool-Handler verfügbar. Permissions werden pro Tool gecheckt, Audit-Trail wird mit User-Identity geschrieben.

Mehr zur Tiefe von Agent-Permissions und OAuth-Patterns bauen wir in unserem Agent-Permissions-OAuth-Blog aus.


Pattern 5: Multi-Tenant-Setup

Bei SaaS-Produkten oder Multi-Werk-Setups muss der MCP-Server Mandanten-getrennte Daten liefern. Du baust das nicht über separate Server pro Mandant (das skaliert nicht), sondern über Mandanten-Kontext im Tool-Handler.

@mcp.tool
def search_orders(query: str, ctx: Context) -> list[dict]:
    """Sucht Bestellungen im Mandanten-Bereich."""
    tenant_id = ctx.auth.claims.get("tenant_id")
    if not tenant_id:
        raise ToolError("Kein Mandanten-Kontext im Token.")
    
    return order_db.search(
        query=query,
        tenant_id=tenant_id,
        limit=20,
    )

Wichtig: Der Tenant-Filter muss auf Datenbank-Ebene durchgesetzt werden, nicht erst nach dem Fetch. Wir nutzen typisch Postgres Row-Level Security (RLS) plus dem Tenant-ID aus dem Token-Claim. Damit fällt das „Tenant-Leakage“-Risiko durch Programmierfehler weg — die Datenbank verweigert den Zugriff auf andere Tenants strukturell.


Häufig gestellte Fragen

Wie viele Sub-Server sind in einer Composition sinnvoll?

Aus unseren Projekten: 3-8 Sub-Server pro Top-Level-Server. Bei mehr wird die Mount-Logik unübersichtlich. Wir bauen dann hierarchische Compositions — mehrere Top-Level-Server, die gemeinsam einem App-spezifischen Aggregator zugeführt werden.

Können Resources auch dynamisch sein?

Ja, der @mcp.resource-Decorator unterstützt Funktionen, die bei jedem Lese-Aufruf neu evaluiert werden. Caching ist möglich, machen wir typisch mit einem 5-Minuten-TTL für semi-statische Daten und einem 1-Stunden-TTL für Schemas.

Wie integriere ich OAuth2 mit Claude Code oder Claude Agent SDK?

Claude Code spricht die MCP-Server transparent durch — wenn der Server Bearer-Token verlangt, muss der User-Token in der Claude-Code-Konfiguration hinterlegt sein. Für interaktive User-Sessions empfehlen wir, einen OAuth2-Callback-Flow in der App zu bauen, der den User-Token holt und an Claude Code übergibt.

Was, wenn der OAuth2-Provider down ist?

Wir bauen typisch einen Token-Cache mit 30-60 Minuten TTL. Bei kurzen Outage des IdP läuft der Server weiter mit gecachten Tokens. Bei längeren Outages wird der Server entsprechend Read-Only oder geht in Fail-Safe-Modus.

Wie testet man Multi-Tenant-Logik?

Mit klaren Integration-Tests pro Tenant-Szenario. Wir bauen typisch ein Test-Setup mit 3-5 Tenant-Mocks und prüfen automatisiert, dass Tool-Calls aus Tenant A keine Daten aus Tenant B sehen. Plus RLS-Test auf Datenbank-Ebene als zweite Verteidigungslinie.


Nächster Schritt

Wenn dein Team mit FastMCP über das Tutorial hinaus produktiv werden will: Wir machen einen 60-Minuten-Architektur-Call mit dir und idealerweise einem Senior-Engineer. Du beschreibst die geplanten Server-Strukturen, wir skizzieren die Advanced-Patterns mit Festpreis-Sprint.


Verwandte Themen


Microsoft Fabric. Bilder Blogtemplate Whitepaper

Claude Code im Unternehmen

  • Praxis-Guide für IT-Leiter – mit konkreten Einblicken zu Kosten, Sicherheit und Rollout von Claude Code im Unternehmen.

Beratungsgespräch

Sichern Sie sich Ihre kostenfreie Erstberatung

Analyse Ihres individuellen Cloud- oder KI-Bedarfs
Erste Empfehlungen zu Umsetzungsstrategien
Transparente Einblicke in unsere Methoden, Technologien & Referenzen