Jetzt, da unser MCP-Server funktioniert, ist es Zeit, die Client-Seite zu bauen. Der Client ist das, was es unserer Anwendung ermöglicht, mit dem MCP-Server zu kommunizieren und auf seine Funktionalität zuzugreifen.
Die Client-Architektur verstehen
Bevor wir in den Code eintauchen, lass uns einen wichtigen Punkt zu MCP-Projekten klären. Normalerweise würdest 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 einfacher zu machen
- Client Session - Die eigentliche Verbindung zum Server (Teil des MCP Python SDK)

Die Client Session erfordert eine Ressourcenbereinigung, wenn wir mit ihr fertig sind, weshalb wir sie in unsere benutzerdefinierte Klasse einwickeln. Dies übernimmt das Verbindungsmanagement und die Bereinigung automatisch.
Wie der Client in unsere Anwendung passt
Erinnerst du dich an unseren Anwendungsablauf? Unser CLI-Code muss auf zwei wesentliche Arten mit Claude interagieren:

Der Client ermöglicht beide dieser Interaktionen, indem er die Funktionalität des Servers für unsere Codebasis verfügbar macht.
Implementierung der Kernfunktionen des Clients
Wir müssen zwei wesentliche Funktionen implementieren: list_tools und call_tool.
List Tools Funktion
Diese Funktion holt alle verfügbaren Tools vom Server:
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 MCP-Server), rufen die eingebaute Funktion list_tools auf und geben die Tools aus dem Ergebnis zurück.
Call Tool Funktion
Diese Funktion 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.
Den Client testen
Um zu überprüfen, dass unsere Implementierung funktioniert, können wir sie direkt testen. Die Client-Datei enthält ein Test-Harness, das sich mit dem MCP-Server verbindet und Befehle gegen ihn ausführt. Lass es list_tools() aufrufen und das Ergebnis ausgeben:
async with MCPClient(
command="uv", args=["run", "mcp_server.py"]
) as client:
result = await client.list_tools()
print(result)Das Ausführen von uv run mcp_client.py sollte eine Liste der verfügbaren Tools mit ihren Beschreibungen und Eingabeschemas zurückgeben. Du solltest Tools wie read_doc_contents und edit_document sehen, die wir in unserem Server definiert haben.
End-to-End-Testing
Jetzt, da sowohl der Client als auch der Server funktionieren, können wir den kompletten Ablauf testen. Wenn wir unsere Hauptanwendung ausführen und Claude fragen "Was ist der Inhalt des Dokuments report.pdf?", sollte Folgendes passieren:
- Die Liste der verfügbaren Tools wird an Claude gesendet
- Claude entscheidet sich, das Tool
read_doc_contentszu verwenden - Unser Client ruft das Tool auf dem Server auf
- Der Server gibt den Dokumentinhalt zurück
- Claude antwortet mit den Informationen
Der Client fungiert als Brücke zwischen deinem Anwendungscode und dem MCP-Server und macht es einfach, auf die Funktionalität des Servers zuzugreifen, ohne direkt mit den Low-Level-Verbindungsdetails umgehen zu müssen.