Wird geladen

Implementierung eines Clients

Lektion 5310 Min.

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:

python
async def list_tools(self) -> list[types.Tool]:
    result = await self.session().list_tools()
    return result.tools

Für call_tool übergeben wir den Tool-Namen und die Eingabeparameter an den Server:

python
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:

bash
uv run mcp_client.py

Dies 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:

bash
uv run main.py

Frage dann: "Was ist der Inhalt des Dokuments report.pdf?"

Claude wird:

  1. Die Liste der verfügbaren Tools von deinem Client erhalten
  2. Entscheiden, das Tool read_doc_contents zu verwenden
  3. Dein Client wird dieses Tool auf dem MCP-Server ausführen
  4. 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.