MCP für LangGraph Agents, Schritt für Schritt: Server, Transporte und die Adapterbrücke
MCP für LangGraph-Agenten, Schritt für Schritt: Host-, Client- und Serverrollen, ein FastMCP-Server, stdio gegenüber HTTP-Transporten, langchain-mcp-Adapter sowie häufige Fehlerquellen.
Quellen: MCP for LangGraph Developers: From Basics to Production
Die Voraussetzungen sind gering: Man sollte mit der Projektstruktur von LangGraph sowie mit dem Konzept einer mit @tool dekorierten Funktion vertraut sein. Das Model Context Protocol ist eine neue Infrastrukturkomponente und keine neue Denkweise bezüglich Graphen; daher beginnt die Erklärung bei Null mit dem MCP selbst und kehrt erst dann zu LangGraph zurück, wenn das Konzept verstanden ist.
Das Skalierungsproblem mit herkömmlichen Tools
Ein Tool in LangGraph beginnt in der Regel so:
@tool
def search_web(query: str) -> str:
"""Search the web for current information."""
return f"Search results for: {query}"
Das ist in Ordnung, solange keine Skalierung erforderlich ist. Angenommen, fünf Agenten in fünf Projekten benötigen alle ein Tool zum „Suchen in der Unternehmensdatenbank“. Mit dem oben beschriebenen Ansatz wird dasselbe Tool fünfmal in fünf Codebasen, möglicherweise sogar in fünf verschiedenen Sprachen, geschrieben. Jede Änderung am Schema bedeutet fünf Bearbeitungen. Der Versuch, es mit einem Kollegen zu teilen, der eine andere Art von KI-App entwickelt – vielleicht sogar nicht mit LangGraph –, scheitert völlig, denn die @tool-Funktion gehört zur Python-Welt von LangChain.
Der Name für diese Schwierigkeit ist das N×M-Integrationssproblem. N Tools (Datenbanken, APIs, Dateisysteme) müssen mit M KI-Anwendungen (einem LangGraph-Agenten, einem benutzerdefinierten Agenten eines Kollegen, Claude Desktop, einem IDE-Assistenten) verbunden werden. Ohne einen gemeinsamen Standard entstehen N×M individuell angepasste Integrationen.
Das Model Context Protocol (MCP) ist ein offenes, universelles Standard zur Verbindung von KI-Anwendungen mit Tools und Daten. Ein einmal als MCP-Server erstelltes Tool kann von jeder MCP-kompatiblen Anwendung verwendet werden – sei es ein LangGraph-Agent, Claude Desktop, Cursor oder ein benutzerdefinierter Agent – ohne zusätzlichen Integrationsscode. Die übliche Analogie ist ein USB-C-Anschluss: ein einheitlicher Steckersstandard, beliebige kompatible Geräte an beiden Enden.
Der Weg von „Was ist MCP?“ bis zu einem produktionsreifen Server, der mit einem echten LangGraph-Agent verbunden ist, umfasst fünf Ebenen, wobei jede Ebene eine weitere Idee hinzufügt.
Ebene 1: Was MCP ist
Beginnen Sie mit einem Bild, bevor es zu Code kommt. Ein Hotel-Concierge weiß nicht persönlich, wie man ein Restaurant bucht, ein Taxi ruft oder Theaterkarten kauft. Der Concierge führt eine Liste vertrauenswürdiger lokaler Dienstleister, von denen jeder ein Spezialist ist. Wenn ein Gast eine Tischreservierung wünscht, nimmt der Concierge eine spezielle Leitung zum Buchungsdienst in Anspruch, stellt die Anfrage in einem von beiden Seiten verständlichen Format auf und übermittelt anschließend die Antwort zurück.
MCP ist diese Standardleitung sowie dieses Standardanfrageformat. Die KI-Anwendung fungiert als Concierge. Ein MCP-Server ist ein spezialisierter Dienst wie ein Restaurantbucher, ein Taxiunternehmen oder eine Ticketbüro. MCP selbst ist die gemeinsame Sprache und Verbindungsstruktur, sodass jeder Concierge mit jedem Dienst kommunizieren kann, ohne lernen zu müssen, wie jeder seine eigene Telefonzentrale betreibt.
Die drei Rollen
Jedes MCP-System umfasst genau drei Teilnehmer, und alles Weitere baut darauf auf.
- Host. Die KI-Anwendung, die der Endnutzer tatsächlich verwendet: hier ein LangGraph-Agent, an anderer Stelle Claude Desktop, Cursor oder VS Code mit einem Copilot. Er führt das Gespräch, entscheidet, wann ein Tool benötigt wird, und zeigt die Ergebnisse an.
- Client. Er befindet sich innerhalb des Hosts, und jeder Client unterhält eine eigene, eins-zu-eins-Verbindung zu genau einem Server. Ein Host, der mit drei Servern verbunden ist, führt intern drei Clients aus. Die eins-zu-eins-Verknüpfung stellt eine bewusste Sicherheitsgrenze dar, sodass ein Client für den Dateisystemserver keine Daten versehentlich an den Datenbankserver weitergeben kann.
Das Übertragungsformat: JSON-RPC 2.0
Die Nachrichten zwischen Client und Server verwenden JSON-RPC 2.0, ein einfaches und etabliertes Format mit drei Nachrichtentypen.
Eine Anfrage bittet darum, etwas auszuführen, und enthält eine eindeutige id, damit die Antwort zugeordnet werden kann:
{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "search_db", "arguments": {"query": "Q3 revenue"}}}
Eine Antwort beantwortet eine Anfrage. Sie enthält entweder ein Ergebnis bei Erfolg oder einen Fehler bei Misserfolg, und verfügt über dieselbe Id wie die Anfrage:
{"jsonrpc": "2.0", "id": 1, "result": {"content": [{"type": "text", "text": "Q3 revenue was $4.2M"}]}}
Eine Benachrichtigung ist eine einseitige Nachricht, die keine Antwort erwartet, zum Beispiel um mitzuteilen, dass sich die Liste der verfügbaren Tools geändert hat.
Raw JSON-RPC wird selten von Hand geschrieben, da die SDKs dies übernehmen. Dennoch macht das Verständnis dafür vieles einfacher, genauso wie das Wissen um HTTP einen Entwickler zu einem besseren REST-Entwickler macht, selbst wenn er niemals rohes HTTP eintippt.
Level 2: die drei Primitiven
Ein Server bietet seine Funktionen über genau drei Arten von Primitiven an, und fast jede Gestaltungsentscheidung bei der Entwicklung eines Servers ist eine Wahl unter diesen Primitiven.
Tools sind Aktionen, die das Modell ausführen kann. Ein Tool ist eine Funktion, die das LLM beim Denken aufrufen kann, genauso wie eine dekorierte Funktion in herkömmlichem LangGraph. Der Unterschied liegt darin, wo sie sich befinden: innerhalb eines MCP-Servers und nicht im LangGraph-Codebase, wobei sie über das Protokoll entdeckt und aufgerufen werden. Greifen Sie auf ein Tool zurück, wenn etwas erledigt werden muss: eine Berechnung, eine Datenbankabfrage, ein versandtes E-Mail oder eine Dateiänderung.
Resources liefern nur zum Lesen zugänglichen Kontext. Die Anwendung kann einen Resource abrufen, um dem Modell Hintergrundmaterial zur Verfügung zu stellen, ohne dass das Modell wie bei einem Tool selbst entscheiden muss, ihn aufzurufen. Resources werden über URI adressiert, ähnlich wie eine Webseite über URL. Verwenden Sie sie für nur zum Lesen zugängliches Material wie Konfigurationsdateien, Dokumente oder Listen von Datenquellen, über die das Modell vor der Auswahl des Suchbegriffs Bescheid wissen sollte.
Prompts sind wiederverwendbare Vorlagen. Ein Prompt ist eine im Voraus geschriebene Struktur, die einen bestimmten Arbeitsablauf für einen Benutzer oder den Host startet. Im Gegensatz zu Tools werden Prompts in der Regel vom Benutzer und nicht vom Modell aufgerufen, wodurch sie Slash-Befehlen ähneln. Sie eignen sich für häufige, wiederholbare Aufgaben wie „Dieses Dokument zusammenfassen“ oder „Diesen Code auf Sicherheitsprobleme überprüfen“, bei denen konsistente, gut durchdachte Anweisungen besser sind als es dem Benutzer zu überlassen.
Falls Sie unsicher sind, welches Tool zu verwenden ist, fragen Sie, ob es etwas ändert oder nur Informationen liefert. Wenn es etwas erstellt, aktualisiert, löscht, sendet oder ausführt, handelt es sich um ein Tool. Wenn es sich um nur zum Lesen bestimmte Daten handelt, die das Modell als Hintergrundinformationen benötigen könnte, ist es eine Ressource. Wenn es sich um eine Anweisungsvorlage handelt, die vom Benutzer ausdrücklich gestartet wird, ist es ein Prompt.
Level 3: Ein erster Server mit FastMCP
FastMCP ist das dominierende Python-Framework für den Aufbau von MCP-Servern. Es wandelt die umfangreiche Mechanik des Protokolls (Anfragenverarbeitung, Schema-Generierung, Lebenszyklusverwaltung) in einige Dekoratoren um, im gleichen Geist wie @tool für LangChain-Funktionen. Installation:
pip install fastmcp
Ein vollständiger Server mit einem einzigen Tool passt in eine kurze Datei:
# ── math_server.py ───────────────────────────────────────────
from fastmcp import FastMCP
# Create the server - this object is the container for everything below
mcp = FastMCP(name="Math Server")
@mcp.tool
def add(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
@mcp.tool
def multiply(a: int, b: int) -> int:
"""Multiply two numbers together."""
return a * b
if __name__ == "__main__":
mcp.run() # Defaults to stdio transport
Das ist ein gültiger, ausführbarer MCP-Server. FastMCP liest die Typhinweise der Funktionen, um das Eingabeschema zu erzeugen, und liest die Dokumentation, um die Beschreibung zu erstellen, die vom LLM angezeigt wird. Gilt dieselbe Regel wie für gewöhnliche Tools – nur mit höheren Konsequenzen: Eine vage Dokumentation lässt das Modell unklar darüber, wann es das Tool aufrufen soll, und diese Unklarheit betrifft nun jede Anwendung, die sich verbindet, nicht nur einen Agenten.
Um eine Ressource hinzuzufügen, wird ein Dekorator mit einem gewünschten URI verwendet:
# ── Adding to math_server.py ─────────────────────────────────
@mcp.resource("config://server-info")
def server_info() -> dict:
"""Provides metadata about this server's capabilities."""
return {"version": "1.0", "operations": ["add", "multiply"]}
Wenn ein Client config://server-info anfordert, ruft FastMCP die Funktion auf und gibt das Ergebnis zurück. Sie wird nur auf Anfrage ausgeführt, was eine träge Bewertung darstellt, sodass nichts berechnet wird, was das Modell niemals verwendet.
Manchmal sollte eine Ressource von einem Argument abhängen, wie beispielsweise von einem bestimmten Datensatz und nicht von einer festen Konfigurationsdatei:
@mcp.resource("history://{operation_id}")
def get_calculation(operation_id: str) -> dict:
"""Retrieve a past calculation by its ID."""
# In a real server, this would look up a database record
return {"id": operation_id, "result": "..."}
Der Platzhalter {operation_id} in der URI macht dies zu einem Ressourcentemplate. Eine Anfrage an history://abc123 veranlasst FastMCP, abc123 zu extrahieren und es automatisch an die Funktion weiterzuleiten.
Eine Anweisung wird auf dieselbe Weise definiert:
@mcp.prompt
def explain_calculation(operation: str, result: str) -> str:
"""Generate a request to explain a calculation in plain English."""
return f"Explain how the operation '{operation}' produced the result '{result}', in simple terms a beginner could follow."
Um den Server zu starten:
python math_server.py
Bevor Sie eine echte KI-Anwendung verbinden, überprüfen Sie sie mit dem MCP Inspector, einem im Ökosystem enthaltenen, browserbasierten Debugging-Tool. Es listet die Tools auf, ruft sie manuell auf und inspiziert Ressourcen, ohne dass ein LLM beteiligt ist:
fastmcp dev math_server.py
Dadurch wird eine lokale Web-Oberfläche geöffnet, in der Regel unter http://127.0.0.1:6274, über die man durch die Tools navigieren, Testargumente eingeben und die hin- und herfließenden JSON-RPC-Nachrichten beobachten kann. Testen Sie hier zuerst. Ein defektes Tool ist viel schwieriger zu debuggen, wenn man sich auf das Tool-Aufrufverhalten eines LLM verlässt, als direkt im Inspector.
Level 4: Transporte
Bis jetzt lief der Server mit dem Standardtransport. Die Wahl des Transports ist wichtig, denn sie bestimmt, wie sich ein LangGraph-Agent verbindet.
Betrachten Sie den Transport als das physische Kabel und JSON-RPC als die darauf übertragenen Informationen. Englisch funktioniert sowohl über Telefon als auch über Radio – eine Sprache, zwei Kabel. MCP-Server verwenden unabhängig vom Transport immer das gleiche JSON-RPC 2.0, doch die Daten werden unterschiedlich übertragen.
stdio, für lokale Tools
Das Standardverhalten, das auch mcp.run() ausführt, wenn es ohne Argumente aufgerufen wird:
if __name__ == "__main__":
mcp.run() # stdio is the default
Durch stdio (Standard-Eingabe und -Ausgabe) startet der Host den Server als Unterprozess und kommuniziert mit ihm, indem er in dessen stdin schreibt und dessen stdout liest. Es ist keine Netzwerkverbindung nötig: Es handelt sich um eine Prozess-zu-Prozess-Kommunikation auf derselben Maschine. Dies eignet sich für einen Server, der neben dem Host läuft, wie z. B. ein Dateisystem-Tool, ein lokaler Skript-Executor oder eine persönliche Hilfsanwendung, und stellt die einfachste sowie gebräuchlichste Konfiguration für die Entwicklung dar.
Eine Einschränkung ist von großer Bedeutung. Unter stdio ist der stdout des Servers ausschließlich für Protokollnachrichten reserviert, wodurch eine versehentliche print()-Anweisung zur Fehlerbehebung den Datenstrom verunreinigt. Das Protokollieren muss daher über stderr erfolgen.
Streamable HTTP für entfernte Tools
Bei einem netzwerkfähigen Server muss der Transportkanal explizit übergeben werden:
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)
Hier läuft der Server als eigenständiger Webdienst. Clients verbinden sich über das Netzwerk mit HTTP POST-Anfragen, wobei optional Server-Sent Events zum Streamen der Antworten verwendet werden können. Dadurch ist der Server wirklich entfernt: Er kann auf einem anderen Rechner, in der Cloud oder hinter einem Load Balancer stehen und mehrere Host-Anwendungen gleichzeitig bedienen. Verwenden Sie ihn, wenn ein Server innerhalb eines Teams geteilt werden muss, zentral bereitgestellt werden soll oder von Rechnern auf anderen Maschinen erreichbar sein muss – einschließlich eines LangGraph-Agenten, der auf einem Server läuft und mit einem MCP-Server für Datenbankzugriffe an einem anderen Ort kommuniziert.
Die Faustregel lautet: stdio für lokale Tools mit einem Benutzer und einfacher Einrichtung, typisch für Entwicklung und private Nutzung; Streamable HTTP für gemeinsam genutzte Tools mit vielen Benutzern oder Anwendungen, für Produktivumgebungen sowie bei Authentifizierungsanforderungen. Genau diese Wahl muss für jeden Server in der folgenden LangGraph-Konfiguration festgelegt werden.
Level 5: Anbindung an LangGraph
Der „Brückenelement“ ist ein Paket mit dem Namen langchain-mcp-adapters, das von dem LangChain-Team gepflegt wird, um MCP-Tools innerhalb von LangGraph-Agenten nutzbar zu machen. Es erfüllt für MCP dasselbe Funktion wie der @tool-Decorator für gewöhnliche Funktionen: Es wandelt ein externes MCP-Tool in ein BaseTool-Objekt um, das direkt in einen LangGraph-ToolNode eingefügt werden kann, ohne dass im Graph spezielle Behandlung erforderlich ist. Nach der Umwandlung weiß der Agent weder, noch ist es ihm wichtig, ob ein Tool von einem MCP-Server stammt oder intern entwickelt wurde.
Installieren Sie es mit:
pip install langchain-mcp-adapters langgraph "langchain[openai]"
Das zentrale Objekt ist MultiServerMCPClient, das sich gleichzeitig mit mehreren MCP-Servern verbinden und die Transportmethoden frei miteinander kombinieren kann:
# ── MODULE 1 (extended): MCP CLIENT CONFIGURATION ───────────
from langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({
# A local server, launched as a subprocess via stdio
"math": {
"command": "python",
"args": ["/absolute/path/to/math_server.py"],
"transport": "stdio",
},
# A remote server, already running, connected over HTTP
"company_data": {
"url": "http://localhost:8000/mcp",
"transport": "http",
},
})
Jeder Wörterbucheintrag entspricht einem Server. Der Schlüssel ("math" oder "company_data") ist lediglich eine Bezeichnung, die dazu dient, Tools von verschiedenen Servern intern zu organisieren. Der Eintrag spiegelt die Übertragungsentscheidung wider: Stdio-Server erhalten eine command sowie args, HTTP-Server erhalten eine url.
Das Laden der Tools erfordert nur einen Aufruf:
# This single call discovers every tool, on every configured server,
# and converts each one into a LangChain-compatible BaseTool
tools = await client.get_tools()
print([t.name for t in tools])
# ['add', 'multiply', 'search_company_db', ...]
Das ist die gesamte Integration. client.get_tools() sendet die MCP-Anfrage tools/list an jeden konfigurierten Server, nimmt die zurückgegebenen Schemata entgegen und umhüllt jedes davon in ein Tool-Objekt, das aus der Sicht des Graphen nicht von einer manuell geschriebenen @tool-Funktion zu unterscheiden ist.
Der Rest der Verkabelung sieht genauso aus wie bei jedem LangGraph-Agenten. MCP ändert lediglich die Herkunft der Tools und nicht die Art und Weise, wie der Graph aufgebaut wird. Ein vollständiges Template:
# ============================================================
# LANGGRAPH + MCP AGENT — COMPLETE TEMPLATE
# Extends: Part 1 (core structure)
# ============================================================
# ── MODULE 1: IMPORTS & CONFIGURATION ───────────────────────
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
llm = init_chat_model("openai:gpt-4o")
client = MultiServerMCPClient({
"math": {
"command": "python",
"args": ["./math_server.py"],
"transport": "stdio",
},
"company_data": {
"url": "http://localhost:8000/mcp",
"transport": "http",
},
})
# ── MODULE 2: STATE ──────────────────────────────────────────
class State(MessagesState):
pass
# ── MODULE 4: NODES ──────────────────────────────────────────
# (Module 3, Tools, is now sourced from MCP rather than defined here)
def make_agent_node(llm_with_tools):
"""Factory pattern: tools are loaded asynchronously, so the node
closure is built after the async tool-loading step completes."""
def agent_node(state: State) -> dict:
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
return agent_node
# ── MODULE 6: GRAPH ASSEMBLY (inside an async function) ──────
async def build_graph():
tools = await client.get_tools()
llm_with_tools = llm.bind_tools(tools)
graph_builder = StateGraph(State)
graph_builder.add_node("agent", make_agent_node(llm_with_tools))
graph_builder.add_node("tools", ToolNode(tools))
graph_builder.add_edge(START, "agent")
graph_builder.add_conditional_edges("agent", tools_condition)
graph_builder.add_edge("tools", "agent")
return graph_builder.compile(checkpointer=MemorySaver())
# ── MODULE 7: ENTRYPOINT ──────────────────────────────────────
async def main():
graph = await build_graph()
config = {"configurable": {"thread_id": "session-001"}}
response = await graph.ainvoke(
{"messages": [HumanMessage(content="What's (3 + 5) times 12?")]},
config=config
)
print(response["messages"][-1].content)
if __name__ == "__main__":
asyncio.run(main())
Alles wird asynchron
MCP bringt eine echte Anpassung mit sich. Die Kommunikation ist von Natur aus asynchron, da jeder Tool-Aufruf eine Netzwerk- oder Unterprozess-Rundreise darstellt – selbst über stdio. Daher muss client.get_tools() abgewartet werden, die Graphenkonstruktion findet in der Regel in einem async def-Funktion statt, und der kompilierte Graph wird mit ainvoke und nicht mit invoke aufgerufen. Alles andere, StateGraph, MessagesState, tools_condition sowie MemorySaver, bleibt unverändert.
Wieder der vorgefertigte Router
Das Routing verschwindet in diesem Template praktisch vollständig. Anstelle eines benutzerdefinierten Routers wird tools_condition, das vorgefertigte Tool, verwendet: Wenn die letzte Nachricht Toolaufrufe enthält, wird auf "tools" weitergeleitet, ansonsten wird die Verarbeitung abgeschlossen. Der Router kann MCP-Tools nicht von manuell geschriebenen Tools unterscheiden, da beide zum Zeitpunkt, an dem der Graph sie wahrnimmt, BaseTool-Objekte sind.
Level 6: Produktionsaspekte
Mehrere Faktoren unterscheiden eine funktionierende Demo von einem einsatzbereiten System, und diese treten am häufigsten in echten MCP- und LangGraph-Einrichtungen auf.
Halten Sie die Clientverbindung aktiv. MultiServerMCPClient ist standardmäßig zustandslos: Jeder Toolaufruf eröffnet eine neue Verbindung, führt sie aus und schließt sie wieder. Das ist für eine einzige Anfrage in Ordnung. Bei einer Anwendung, die viele Anfragen bearbeitet, führt das Eröffnen eines Unterprozesses oder einer HTTP-Verbindung bei jedem Aufruf zu erheblicher Verzögerung. In der Produktion sollte der Client beim Start des Prozesses einmal geöffnet werden und für die gesamte Laufzeit der Anwendung wiederverwendet werden:
# Hold the connection open across the application's lifetime,
# rather than opening/closing per request
async with MultiServerMCPClient({...}) as client:
tools = await client.get_tools()
graph = await build_graph_with(tools)
# ... serve many requests using this same graph ...
Vermeiden Sie Stdio in einem Webserver. Stdio wurde für lokale, einzelbenutzerbasierte Szenarien auf derselben Maschine entwickelt, wie zum Beispiel eine Desktop-App, die einen Hilfsprozess startet. Wenn ein LangGraph-Agent als Webdienst für viele Benutzer eingesetzt wird, stellen Stdio-Server ein Risiko dar: Für jede Anfrage werden neue Unterprozesse gestartet, wobei es keine natürliche Möglichkeit gibt, den Zustand zu teilen oder die Leistung auszubauen. Für jede serverseitige Implementierung sollte Streamable HTTP bevorzugt werden, und man sollte ehrlich prüfen, ob überhaupt ein separater MCP-Prozess notwendig ist. Es gibt Fälle, in denen eine einfache @tool-Funktion, die direkt eine interne Bibliothek aufruft, sowohl in Bezug auf Einfachheit als auch Geschwindigkeit dem zusätzlichen Protokollweg überlegen ist.
Fügen Sie eine menschliche Freigabe für sensible Tools hinzu. MCP-Tools sind einmal sie im Graphen angekommen gewöhnliche Tools, weshalb alle geltenden Muster Anwendung finden. Wenn ein Server etwas Sensibles freigibt, wie zum Beispiel E-Mails versenden, eine Datenbank ändern oder Geld ausgeben, umhüllen Sie dies mit dem gleichen interrupt()-Prüfschritt, der für jeden anderen Toolaufruf verwendet wird. MCP bietet Erkennung und Standardisierung, stellt aber standardmäßig keine Sicherheit bereit. Das bleibt die Aufgabe des Entwicklers.
Server können den Zustand des Graphen nicht sehen. MCP-Server laufen als separate Prozesse. Sie können weder auf den state von LangGraph, noch auf den Checkpointer oder auf irgendwelche in-memory-Python-Objekte zugreifen. Wenn eine Anfrage personalisiert werden muss – beispielsweise mit einer gespeicherten Benutzerpräferenz – muss der Wert explizit als Argument der Tool-Funktion übergeben werden oder über Interceptor-Elemente, ein fortgeschrittenes Hook-System von langchain-mcp-adapters, das eine Anfrage vor ihrem Versand unter Verwendung des Laufzeitkontexts des Graphen überprüfen und ändern kann. Das Übergeben von Werten als Argumente reicht für die meisten Agenten aus und ist weitaus einfacher.
Level 7: MCP in einem Mehr-Agenten-System
MCP wird im Zusammenhang mit dem Supervisor-Muster eingesetzt. Mehrere spezialisierte Agenten beziehen jeweils ihre Tools von ihrem eigenen dedizierten Server:
# ── Specialist agents, each pulling tools from a DIFFERENT MCP server ──
async def build_multi_agent_system():
client = MultiServerMCPClient({
"research": {"url": "http://localhost:8001/mcp", "transport": "http"},
"code": {"url": "http://localhost:8002/mcp", "transport": "http"},
})
research_tools = await client.get_tools(server_name="research")
code_tools = await client.get_tools(server_name="code")
researcher_llm = llm.bind_tools(research_tools)
coder_llm = llm.bind_tools(code_tools)
# From here, wire researcher_node, coder_node, and a supervisor_node
# exactly as shown in Part 4 - the rest of the supervisor pattern
# is completely unchanged.
...
MCP führt hier keine konkurrierende Architektur ein. Ein Spezialist, der von einem MCP-Server unterstützt wird, ist strukturell identisch mit einem, der von manuell erstellten Tools unterstützt wird: Der Supervisor leitet die Kommunikation auf dieselbe Weise weiter und die Spezialisten melden ihre Ergebnisse auf dieselbe Weise zurück. MCP ändert sich nur darin, wo die Tool-Definitionen gespeichert sind und wie viele Anwendungen sie wiederverwenden können.
Kurzfassung
- Architektur. Host ist die KI-Anwendung, mit der der Benutzer interagiert (hier der LangGraph-Agent). Client befindet sich im Host, jeweils ein Exemplar pro verbundenem Server, und verwaltet eine eins-zu-eins-Sitzung. Server ist das eigenständige Programm, das Tools, Ressourcen und Anfragen bereitstellt, ohne zu wissen, welcher Host sich anschließt. JSON-RPC 2.0 ist das Nachrichtenformat für alle Austausche: Anfragen, Antworten und Benachrichtigungen.
- Primitiven. Tool ist eine ausführbare Aktion, die das Modell aufrufen kann und mit
@mcp.toolerstellt wird. Resource sind nur zum Lesen zugängliche Daten, die über einen URI adressiert werden, von der Anwendung oder dem Benutzer gesteuert werden und mit@mcp.resource("uri://...")erstellt werden. Prompt ist ein wiederverwendbares Anweisungsschema, das vom Benutzer aufgerufen wird und mit@mcp.prompterstellt wird. - FastMCP.
FastMCP(name="...")erstellt den Server, den Container für alles.@mcp.toolstellt eine Funktion zur Verfügung, die Typhinweise für das Schema sowie eine Dokumentation für die Beschreibung liest.@mcp.resource("scheme://{param}")deklariert nur zum Lesen zugängliche Daten, wobei{param}ein Template erstellt.@mcp.promptdeklariert ein Template.mcp.run()startet den Server – standardmäßig über stdio oder mittransport="streamable-http"für den Netzwerkgebrauch.fastmcp dev server.pyöffnet den Inspector zum Testen ohne LLM. - Transport.
stdiobasiert auf Unterprozessen und ist lokal ausgelegt, wobei der Host den Server startet, und die Debug-Ausgabe darf auf keinen Fall an stdout gesendet werden.streamable-httparbeitet über das Netzwerk, unterstützt mehrere gleichzeitige Hosts und ist die Standardlösung für Produktivumgebungen auf gemeinsam genutzten Servern. - Integration mit LangGraph.
MultiServerMCPClient({...})auslangchain_mcp_adapters.clientverbindet sich mit einem oder mehreren Servern unter Verwendung verschiedener Transportmethoden in einem einzigen Dictionary.await client.get_tools()entdeckt alle Tools und wandelt sie inBaseTool-Objekte um.tools_conditionfungiert als vorgefertigter Router und funktioniert identisch für selbst geschriebene Tools sowie Tools, die über MCP bereitgestellt werden. Alles ist asynchron:get_tools(), das Erstellen des Graphen sowieainvokeerfordern alleawait.
Ist MCP hier notwendig?
- Ein Tool, ein Agent, eine Codebasis. Kein MCP erforderlich. Eine einfache
@tool-Funktion ist einfacher und verursacht keinen Protokoll-Overhead. - Ein Tool, das in verschiedenen Agenten oder Projekten wiederverwendet wird oder mit Kollegen in anderen Frameworks geteilt wird. Bauen Sie es als MCP-Server. Das ist der Kernwert.
- Man möchte Hunderte vorhandener Community-Server (GitHub, Slack, Postgres, Google Drive, Stripe und mehr) anstelle von manuell geschriebenem Integrationsscode nutzen. Verbinden Sie sich über
MultiServerMCPClientund vermeiden Sie so das Schreiben der Integration – oft die größte Zeiteinsparung, die MCP bietet. - Ein Tool auf einem anderen Rechner als dem Agent oder gleichzeitig von mehreren Hosts genutzt. Verwenden Sie Streamable HTTP als eigenständigen Dienst.
Ein Standard, kein Ersatz
Das Erlernen eines neuen Protokolls weckt die Sorge, dass es das bereits Bekannte verdrängt. MCP tut das nicht. Es lässt LangGraphs Tools, Knoten und Graphen unberührt. Was sich ändert, ist der Ursprung der Tools sowie die Anzahl der Anwendungen, die sie teilen können. Nachdem ein MCP-Tool den ToolNode erreicht hat, verhält es sich wie jedes manuell geschriebene Tool, wird durch dieselben interrupt()-Muster genehmigt und passt zu denselben Überwachungsarchitekturen.
Der Fortschritt verlief schrittweise: Konzepte und Rollen, die drei Primitiven, ein funktionsfähiger Server, die Wahl des Transports, die LangGraph-Brücke, die Verhärtung für die Produktion sowie die Komposition mit Mehr-Agenten-Systemen. Jede Stufe fügte einer stabilen Grundlage eine weitere Idee hinzu. In Kombination mit einer kanonischen Graphenstruktur, Speichermanagement, Sicherheit durch menschliche Einmischung, Orchestrierung und Abruf von Mehr-Agenten-Systemen deckt dies den größten Teil dessen ab, was eine ernstzunehmende Produktionsanwendung mit LangGraph benötigt, und MCP ermöglicht es den Werkzeugen, sich über eine einzelne Codebasis hinaus teilweise zu skalieren.
Verwandte Literatur
- Praktische Hinweise: Warum niemand über LangChain, LangGraph oder AutoAgent spricht — Detaillierte Anleitung zu den Praktischen Hinweisen: Warum niemand über LangChain, LangGraph oder AutoAgent spricht: Verträge, Überprüfungen sowie Code-Blöcke für Teams, die dieses Muster einsetzen.
- ReAct in LangGraph: Vom manuell erstellten Schleifenmodell bis zu ToolNode und StateGraph — Erstellen Sie einen ReAct-Agenten zweimal: zunächst als manuelle asynchrone Schleife und anschließend mithilfe von LangGraphs ToolNode, tools_condition sowie StateGraph, die die gekoppelten Tool-Aufrufe für Sie übernehmen.