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
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, die zusammenarbeiten:

- 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 übernimmt die Low-Level-Kommunikation, erfordert aber eine sorgfältige Ressourcenbereinigung, wenn dein Programm herunterfährt. Deshalb wickeln wir sie in unsere eigene Klasse ein - um diese Bereinigung automatisch zu verwalten.
Wie der Client in unsere Anwendung passt
Erinnerst du dich an unser Anwendungsablaufdiagramm? Der Client spielt in zwei entscheidenden Momenten eine wichtige Rolle:

Unser CLI-Code verwendet den Client, um:
- Eine Liste verfügbarer Tools zu erhalten, die an Claude gesendet werden
- Tools auszuführen, wenn Claude sie anfordert
Implementierung der Kernfunktionen des Clients
Lass uns die beiden wesentlichen Funktionen implementieren: list_tools und call_tool.
Für list_tools müssen wir uns mit unserer Session verbinden und die verfügbaren Tools anfordern:
async def list_tools(self) -> list[types.Tool]:
result = await self.session().list_tools()
return result.toolsFür call_tool übergeben wir den Tool-Namen und die Eingabeparameter an den Server:
async def call_tool(
self, tool_name: str, tool_input: dict
) -> types.CallToolResult | None:
return await self.session().call_tool(tool_name, tool_input)Das ist alles! Die Session übernimmt für uns alle komplexen Kommunikationsdetails.
Testen des Clients
Die Client-Datei enthält am Ende ein einfaches Test-Harness. Du kannst es direkt ausführen, um zu überprüfen, ob alles funktioniert:
uv run mcp_client.pyDies verbindet sich mit deinem MCP-Server und gibt die verfügbaren Tools aus. Du solltest eine Ausgabe sehen, die deine Tool-Definitionen zeigt, einschließlich Namen, Beschreibungen und Eingabeschemata.
Wichtige Schema-Unterschiede
Hier ist eine Falle, über die du Bescheid wissen musst: MCP-Tool-Definitionen entsprechen nicht genau dem, was Claude erwartet. Die MCP-Spezifikation hat ihr eigenes Format für Tool-Schemata, das sich leicht von dem unterscheidet, was Bedrock benötigt.
Keine Sorge - es gibt bereits Code im Projekt, der diese Konvertierung automatisch übernimmt. Die Funktion to_bedrock_tools in core/bedrock.py übersetzt MCP-Tool-Definitionen in das Format, das Claude versteht.
Testen mit Claude
Jetzt, wo sowohl der Server als auch der Client funktionieren, kannst du den vollständigen Ablauf testen. Versuche, deine Hauptanwendung auszuführen und Claude zu bitten, ein Dokument zu lesen:
uv run main.pyFrage dann: "Was ist der Inhalt des Dokuments report.pdf?"
Claude wird:
- Die Liste der verfügbaren Tools von deinem Client erhalten
- Entscheiden, das Tool read_doc_contents zu verwenden
- Dein Client wird dieses Tool auf dem MCP-Server ausführen
- Claude wird den Dokumentinhalt erhalten und antworten
Der Client fungiert als Brücke zwischen deinem Anwendungscode und dem MCP-Server und macht es einfach, Server-Funktionalität für Claude und andere Teile deines Systems verfügbar zu machen.