Startseite / Artikel

Erstellen Sie Ihren ersten MCP-Server in Python: Tools, Ressourcen, Anfragen und Inspektor

Erstellen Sie einen MCP-Server in Python mit FastMCP: ein praktischer Helfer mit Tools, Ressourcen und Prompts, Vergleich von stdio und HTTP, Debugger mit Inspector sowie eine Konfiguration für 3 Clients.

Verlag: REACTAPP.TOP9 Min. Lesezeit2056 Wörter

Quellen: What Is MCP? Build a Custom MCP Server in Python

Stellen Sie sich drei nahezu identische Python-Dateien vor, die Claude, Cursor und ChatGPT genannt sind und jeweils die Slack-API aufrufen, die Antwort parsen und sie für einen bestimmten KI-Client umformen. Stellen Sie sich nun vor, alle drei durch eine einzige Datei zu ersetzen, aus der jeder dieser Clients direkt lesen kann. Wenn Slack ein Feld ändert, müssen Sie es nur einmal korrigieren. Diese einzige Datei ist ein MCP-Server, und seine Erstellung dauert etwa einen Nachmittag.

MCP in wenigen Sätzen

Das Model Context Protocol ist so kompakt, dass es in drei Nachrichten beschrieben werden kann. Ein Client verbindet sich mit einem Server und fragt nach dessen Funktionalitäten. Der Server antwortet mit den Tools, die ein Modell aufrufen kann, den Daten, die ein Host abrufen kann, sowie den Prompts, die ein Benutzer auswählen kann. Anschließend ruft der Client ein Tool mit Argumenten auf, der Server führt es aus und antwortet. Über das Netzwerk geschieht genau das.

Was MCP nutzbar macht, ist nicht das Nachrichtenformat, sondern die gemeinsame Vereinbarung. Claude Code, Cursor, VS Code’s Copilot und weitere Tools verwenden alle denselben Ablauf, sodass eine jetzt erstellte Python-Datei später an jedes davon angehängt werden kann.

Ein Server kann eine von drei Arten von Funktionalitäten anbieten:

  • Tools sind aufrufbare Funktionen, die das Modell nach Namen auswählt – beispielsweise eine Funktion zur Suche in einer Datenbank, zum Versenden einer Branch oder zur Zusammenfassung eines Logs.
  • Resources sind nur zum Lesen zugängliche Materialien wie Konfigurationsdateien, Schemata oder aktuelle Commit-Informationen, die der Host bei Bedarf herunterlädt.
  • Prompts sind wiederverwendbare Vorlagen, die der Benutzer auswählt; sie erscheinen als Schrägstrich-Befehl in Claude Code oder als Menüeintrag in Claude Desktop.

In der Praxis liefern die meisten Server Tools, während Ressourcen und Anleitungen an Bedeutung verlieren. Wenn Sie nur eines von drei lernen sollen, dann lernen Sie Tools. Wie Nachrichten übertragen werden und wie die Authentifizierung funktioniert, ist später wichtig. Merken Sie sich vorerst Folgendes: Ein Server, viele Clients – drei Dinge, die bereitgestellt werden müssen.

Warum Integrationen früher zahlreicher wurden

Vor MCP bedeutete die Verbindung eines KI-Tools mit einem Backend, dass man einen Adapter zwischen der Struktur des Backends und dem von dem Client gewünschten Tool-Aufruf-Format schreiben musste. Ein Client erwartete das Schema von Anthropics Tools, ein anderer nutzte sein eigenes Format, und ein dritter verlangte die Funktionsaufruf-Struktur von OpenAI. Die Suchfunktion in Slack blieb unverändert, während sich um sie herum drei Wrapper-Schichten entwickelten.

Die Rechenlogik wird schnell unübersichtlich. Drei Clients und zwei Backends bedeuten sechs Adapter, die gewartet werden müssen. Ein vierter Client erhöht die Zahl auf acht, und ein drittes Backend auf zwölf – jeweils mit eigener Authentifizierung, Wiederholungsmechanismen und Besonderheiten. MCP zerlegt dieses Konstrukt auf. Clients und Backends kommunizieren nicht mehr direkt miteinander; beide sprechen stattdessen mit dem Protokoll. Dadurch kostet die Einführung eines neuen AI-Clients lediglich einen Konfigurationseintrag statt einer neuen Integration.

Das Design ist absichtlich unausgeglichen. Der Client entscheidet über alles: Welche Server geladen werden, wann Tools aufgerufen werden und wie die Ergebnisse angezeigt werden. Die Aufgabe des Servers ist deutlich geringer – er deklariert lediglich seine Tools und antwortet, wenn darauf zugegriffen wird. Diese Asymmetrie sorgt dafür, dass das Protokoll kompakt genug bleibt, damit jeder große Client es schnell umsetzen kann.

Ein Hilfsmittel für Standups in einer Datei

Ein Standup-Hilfsmittel ist ein klassisches internes Tool, das Ingenieure ständig neu schreiben. Es liest den lokalen git Log und fasst zusammen, was ein Entwickler gestern getan hat. Hier wird es als Tool, Ressource und Prompt in einer einzigen Python-Datei bereitgestellt.

Man benötigt das FastMCP-Paket, die hochlevelige Python-Schicht, auf der viele MCP-Server basieren.

pip install fastmcp

Erstellen Sie standup_server.py. Zunächst wird die Bibliothek importiert und das Tool definiert.

# standup_server.py
import subprocess
from typing import TypedDict

from fastmcp import FastMCP

mcp = FastMCP("standup-helper")


class StandupSummary(TypedDict):
    branch: str
    since: str
    commit_count: int
    commits: list[str]


@mcp.tool()
def summarize_standup(
    branch: str = "main",
    since: str = "yesterday",
) -> StandupSummary:
    """Summarize recent git activity for a standup.

    Reads the local git log on the given branch since the
    given time window. Returns commit count and one-line
    subjects for each commit. Used by AI clients via MCP.
    """
    try:
        result = subprocess.run(
            [
                "git", "log",
                f"--since={since}",
                "--pretty=format:%h %s",
                branch,
            ],
            capture_output=True,
            text=True,
            timeout=5,
            check=True,
        )
    except (subprocess.CalledProcessError,
            subprocess.TimeoutExpired) as exc:
        return {
            "branch": branch,
            "since": since,
            "commit_count": 0,
            "commits": [f"git error: {exc}"],
        }

    lines = [
        line for line in result.stdout.splitlines() if line
    ]
    return {
        "branch": branch,
        "since": since,
        "commit_count": len(lines),
        "commits": lines,
    }


# resources and prompts come next

Hinter den Kulissen übernimmt der Dekorator vier Aufgaben. Er liest die Typhinweise, um ein JSON-Schema zu erzeugen, verwendet die Dokumentation als Beschreibung, die dem Modell angezeigt wird, listet das Tool während des Handshakes unter den Fähigkeiten des Servers auf und leitet eingehende JSON-RPC-Aufrufe an die entsprechende Funktion weiter.

Beachten Sie den Aufruf eines Unterprozesses mit einer strengen Fünf-Sekunden-Timeout-Frist. Wenn ein git-Befehl hängen bleibt, hängt auch die Host-Anwendung, die auf die Antwort des Tools wartet, mit ihm fest – daher benötigt jeder externe Aufruf einen Timeout. Auch der Fehlerfall gibt dieselbe Dictionary-Struktur wie der Erfolgsfall zurück, sodass das Modell stets eine vorhersehbare Struktur erhält. Ein umfangreicheres Tool könnte außerdem die GitHub CLI zum Öffnen von Pull Requests aufrufen, indem es einen weiteren Unterprozessaufruf vornimmt und die Ausgabe dem Dictionary hinzufügt.

Danach folgen Ressource und Prompt, die die Datei vervollständigen.

# standup_server.py (continued)

@mcp.resource("recent_commits://main")
def recent_commits_main() -> str:
    """Last 10 commits on the main branch, plain text.

    Resources are pulled by the host opportunistically.
    They are not invoked by the model the way tools are.
    """
    result = subprocess.run(
        [
            "git", "log",
            "-n", "10",
            "--pretty=format:%h %ad %s",
            "--date=short",
            "main",
        ],
        capture_output=True,
        text=True,
        timeout=5,
    )
    return result.stdout or "(no commits found)"


@mcp.prompt("standup_template")
def standup_template(focus: str = "shipping work") -> str:
    """Reusable standup question exposed as a prompt
    template. Surfaces as a slash command in clients that
    expose prompts (e.g. /standup_template in Claude Code).
    """
    return (
        f"Summarize what I worked on yesterday, focusing on "
        f"{focus}. Use the summarize_standup tool to get the "
        f"git log, then write a one-paragraph standup note."
    )


if __name__ == "__main__":
    mcp.run()

Ein Resource-URI ist einfach nur eine Zeichenkette. Es gibt kein strenges Schema, das seine Struktur vorgibt, obwohl üblicherweise ein Schema auf einen Pfad folgt. FastMCP unterstützt URI-Vorlagen, wenn Parameter benötigt werden. Die Prompt-Funktion ist lediglich eine Funktion, die formatierten Text zurückgibt. Die Clients stellen sie als Vorlage bereit, die der Benutzer aufruft; Claude Code stellt dies als Schrägstrich-Befehl zur Verfügung. Prompts werden niemals automatisch eingefügt, sodass der Benutzer sie stets selbst auswählt.

Die letzte Zeile startet den Server. Es gibt weder eine Portnummer noch ein Web-Framework sowie keinen Event-Loop, der verwaltet werden müsste, da FastMCP die stdio-Verarbeitung übernimmt.

Transport und Authentifizierung

Der Transport bestimmt, wie JSON-RPC-Nachrichten zwischen Client und Server übertragen werden.

Verwenden Sie stdio für lokale Server. Der Host startet Ihr Skript als Kindprozess. Stdio umgeht den gesamten Netzwerkstapel: es gibt weder einen Port noch TLS noch Authentifizierung, da der Kindprozess die Vertrauensgrenzen des Hosts erbt.

Verwenden Sie Streamable HTTP, wenn der Server den Zustand über mehrere Anfragen hinweg beibehalten muss oder von mehreren Maschinen aus erreichbar sein soll. Ein entfernter Server ist dem Netzwerk ausgesetzt, weshalb eine strenge Authentifizierung erforderlich ist.

# bottom of standup_server.py

if __name__ == "__main__":
    # Default transport is stdio. The host (Claude Code,
    # Cursor, Claude Desktop, etc.) launches this script
    # as a subprocess and talks to it over stdin/stdout.
    # No port, no TLS, no auth. The trust boundary is
    # whoever launched the host.
    mcp.run()

    # To expose the same server over the network instead,
    # use Streamable HTTP. SSE was deprecated in the
    # March 2025 spec update. Do not use it for new code.
    #
    # Production HTTP also needs an auth layer in front.
    # OAuth 2.1 with Dynamic Client Registration is the
    # current pattern. See Week 22 for the full flow.
    #
    # mcp.run(
    #     transport="streamable-http",
    #     host="0.0.0.0",
    #     port=8000,
    # )

Die Aufrufung von run ohne Argumente bedeutet stdio, und der kommentierte Block zeigt, wie ein HTTP-Upgrade aussieht. Diese Version bleibt bei stdio, was die richtige Wahl für die meisten internen Entwicklerwerkzeuge ist. Ein Werkzeug, das nur lokale Dateien oder ein lokales Repository liest, sollte überhaupt nicht hinter einer Netzwerkschnittstelle liegen.

Der lokale Entwicklungszyklus

Testen Sie einen neuen Server nicht, indem Sie ihn direkt in einen AI-Client anschließen. Clients sind darauf ausgelegt, widerstandsfähig zu sein. Sie ignorieren stumm Abweichungen im Schema sowie fehlende Funktionen, damit das Chatten weiterhin funktioniert, und wenn ein Werkzeug versagt, entschuldigt sich das Modell einfach.

Verwenden Sie stattdessen den offiziellen MCP Inspector – eine browserbasierte Entwicklungs-Oberfläche, die über stdio mit Ihrem Server kommuniziert.

npx @modelcontextprotocol/inspector python standup_server.py

Der Befehl startet Ihr Skript als Unterprozess und öffnet ein lokales Dashboard. Sie können die Rohdaten des Handshakes einsehen, genau erkennen, welche Funktionen Ihr Server angekündigt hat, sowie Tools und Ressourcen in Echtzeit ausführen.

In diesem Zyklus treten in der Regel drei Probleme auf:

  1. Schema-Unterschied. Der Rückgabewert der Funktion stimmt nicht mit ihrer Typangabe überein, und der Inspector weist dies umgehend darauf hin.
  2. Fehlende Funktion. Sie rufen etwas auf, das der Server nie angekündigt hat, wodurch das Dashboard nur die tatsächlich während der Verhandlung registrierten Funktionen anzeigt.
  3. Ausgabeverunreinigung. Jeder ungeplante print() schädigt den JSON-RPC-Stream, da stdio die Nachrichten überträgt. Leiten Sie Logs mit Python’s logging-Modul an den Standardfehlerkanal um, da das Protokoll diesen für lesbare Diagnosen reserviert hat.

FastMCP bietet außerdem einen Entwicklungsmodus, der den Server neu startet, wenn sich die Datei ändert – das hilft beim Arbeiten im Inspector.

Ein Server, mehrere Clients

Nun zeigt sich der Vorteil des Designs. Es gibt eine einzige Python-Datei, die im Inspector überprüft werden kann, und sie kann ohne spezifischen Code für jeden Client an verschiedene KI-Clienten angehängt werden. Jeder Client liest eine Konfigurationsdatei, in der die zu startenden lokalen Server aufgeführt sind, und die JSON-Struktur ist in Claude Code, Cursor und Claude Desktop identisch: ein mcpServers-Objekt, das den auszuführenden Befehl sowie seine Argumente enthält.

{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}
{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}
{
  "mcpServers": {
    "standup-helper": {
      "command": "python",
      "args": ["/Users/you/code/standup_server.py"]
    }
  }
}

Der Schlüssel, der Befehl und die Argumente sind in allen drei Fällen identisch. Nur die Lage der Konfigurationsdatei unterscheidet sich je nach Client.

Öffnen Sie einen beliebigen davon und fordern Sie einen Standup an. Der Client startet den Python-Prozess, verhandelt die Funktionalitäten über stdio, zeigt dem Modell das Zusammenfassungstool und leitet den Aufruf des Modells zurück an Ihr Skript. Git wird ausgeführt, das Dictionary gibt die Daten zurück und das Modell schreibt den Standup – immer auf dem gleichen Codepfad.

ChatGPT ist nicht in dieser Liste, weil seine Anbindungen über Streamable HTTP mit entfernten Servern arbeiten. Die Einbindung dieses Hilfsprogramms erfordern würde eine öffentliche URL sowie eine Authentifizierungsschicht, was ein völlig anderes Betriebsprofil im Vergleich zu dieser rein lokalen Implementierung darstellt.

Wann MCP nicht geeignet ist

Das Protokoll ist kein universelles Werkzeug. In einigen Situationen eignet es sich nicht gut:

  • Sehr latenzempfindliche Pfade. Jeder Aufruf eines Tools beinhaltet eine JSON-RPC-Rundreise, weshalb Tools innerhalb einer engen Schleife eine direkte SDK-Integration verwenden sollten.
  • Individuelle interaktive Ausgaben. Wenn Sie ein bestimmtes Diagramm oder ein maßgeschneidertes Formular benötigen, entwickeln Sie eine Host-Integration, da das Protokoll Daten von der Darstellung trennt.
  • Einstellungen, die Sie vollständig kontrollieren. Wenn ein einziges Team sowohl den AI-Client als auch den Backend entwickelt, kann deren direkte Verbindung im Vergleich zum Einsatz von MCP als Zwischenschritt einfacher zu warten sein.

Kleines Protokoll, großer Wandel

Drei Primitive, zwei Transportmechanismen und ein JSON-RPC-Handshake bilden das gesamte Protokoll. Genau diese Kompaktheit ist der Vorteil: Sie trennt diejenigen, die die Intelligenz entwickeln, von denen, die den Kontext liefern. Der Aufbau eines ersten Servers dauert nur einen Nachmittag und lohnt sich jedes Mal, wenn ein weiteres KI-Tool erscheint.