Jetzt, wo unser MCP-Server funktioniert, ist es Zeit, die Client-Seite zu bauen. Der Client ist das, was unserer Anwendung ermöglicht, mit dem MCP-Server zu kommunizieren und auf dessen Funktionalität zuzugreifen.
Die Client-Architektur verstehen
In den meisten realen Projekten wirst du entweder einen MCP-Client ODER einen MCP-Server implementieren - nicht beides. Wir bauen in diesem Projekt beides, nur damit du sehen kannst, wie sie zusammenarbeiten.

Der MCP-Client besteht aus zwei Hauptkomponenten:
- MCP Client - Eine benutzerdefinierte Klasse, die wir erstellen, um die Nutzung der Session zu vereinfachen
- Client Session - Die eigentliche Verbindung zum Server (Teil des MCP Python SDK)

Die Client Session erfordert eine ordnungsgemäße Ressourcenbereinigung, wenn wir mit ihr fertig sind. Deshalb umschließen wir sie mit unserer benutzerdefinierten MCP Client-Klasse - um diese ganze Bereinigung automatisch zu erledigen.
Wie der Client in unsere Anwendung passt
Erinnerst du dich an unseren Anwendungsablauf? Unser CLI-Code muss zwei Hauptaufgaben mit dem MCP-Server erledigen:

- Eine Liste der verfügbaren Tools abrufen, um sie an Claude zu senden
- Tools ausführen, wenn Claude sie anfordert
Der MCP-Client stellt diese Fähigkeiten durch einfache Methodenaufrufe bereit, die unser Anwendungscode nutzen kann.
Implementierung der Kernmethoden
Wir müssen zwei Schlüsselmethoden in unserem Client implementieren: list_tools() und call_tool().
List Tools-Methode
Diese Methode ruft alle verfügbaren Tools vom Server ab:
async def list_tools(self) -> list[types.Tool]:
result = await self.session().list_tools()
return result.toolsEs ist unkompliziert - wir greifen auf unsere Session zu (die Verbindung zum Server), rufen die integrierte list_tools()-Funktion auf und geben die Tools aus dem Ergebnis zurück.
Call Tool-Methode
Diese Methode führt ein bestimmtes Tool auf dem Server aus:
async def call_tool(
self, tool_name: str, tool_input: dict
) -> types.CallToolResult | None:
return await self.session().call_tool(tool_name, tool_input)Wir übergeben den Tool-Namen und die Eingabeparameter (bereitgestellt von Claude) an den Server und geben das Ergebnis zurück.
Testen des Clients
Um unsere Implementierung zu testen, können wir den Client direkt ausführen. Die Datei enthält ein Test-Framework, das sich mit unserem MCP-Server verbindet und unsere Methoden aufruft:
async with MCPClient(
command="uv", args=["run", "mcp_server.py"]
) as client:
result = await client.list_tools()
print(result)Wenn wir diesen Test ausführen, sollten wir unsere Tool-Definitionen ausgegeben sehen, einschließlich der Tools read_doc_contents und edit_document, die wir zuvor erstellt haben.
Alles zusammenfügen
Jetzt, wo unser Client Tools auflisten und aufrufen kann, können wir den vollständigen Ablauf testen. Wenn wir unsere Hauptanwendung ausführen und Claude nach einem Dokument fragen:
- Unser Code verwendet den Client, um verfügbare Tools abzurufen
- Diese Tools werden zusammen mit der Frage des Nutzers an Claude gesendet
- Claude entscheidet sich, das Tool
read_doc_contentszu verwenden - Unser Code verwendet den Client, um dieses Tool auszuführen
- Das Ergebnis wird zurück an Claude gesendet, der dann dem Nutzer antwortet
Zum Beispiel wird die Frage „Was ist der Inhalt des Dokuments report.pdf?" dazu führen, dass Claude unser Tool zum Lesen von Dokumenten verwendet, und wir erhalten Informationen über das 20m-Kondensatorturm-Dokument zurück, das wir in unserem Server eingerichtet haben.
Der Client fungiert als Brücke zwischen unserer Anwendungslogik und dem MCP-Server und macht es einfach, auf die Server-Funktionalität zuzugreifen, ohne sich um die zugrunde liegenden Verbindungsdetails sorgen zu müssen.