Modell-Kontext-Protokoll für Anfänger mit FastMCP und Ollama
Erlernen Sie die MCP-Rollen – Host, Client, Server, Transport – und verbinden Sie anschließend einen Wetter-Tool-Server mit einem lokalen qwen3:8b-Modell über FastMCP und STDIO.
Das Model Context Protocol, üblicherweise als MCP abgekürzt, ist eine gemeinsame Sprache zur Verbindung großer Sprachmodelle mit Tools und Datenquellen, die sie nicht selbst erreichen können. Die Bezeichnung als Protokoll betont, dass es standardisiert, wie der Austausch aussehen sollte; konkrete Bibliotheken implementieren anschließend diesen Standard, sodass Teams nicht selbst Sockets und Nachrichtenschemata entwickeln müssen. FastMCP ist eine solche Implementierung, die im untenstehenden Beispiel verwendet wird.
Companion-Repository: https://github.com/harshagangari747/MCPTutorial/tree/main
Voraussetzungen
Die Demo setzt drei Pakete voraus: fastmcp, ollama und langchain-community. Die Inferenz erfolgt mit dem lokalen qwen3:8b-Modell. Starten Sie es mit:
ollama run qwen3:8b
Erstellen Sie ein Projektverzeichnis, das bereits leere Vorlagen mit den Namen weather_server_mcp.py und app.py enthält, damit Server und Anwendung klare Orte haben.
MCP verstehen
Eine LLM ist im Alleingang lediglich ein Token-Umwandler. Tokens kommen herein; Tokens gehen hinaus. Sie ruft weder Wetter-APIs auf, öffnet Datenbanken noch liest die Systemuhr – es sei denn, etwas außerhalb des Modells führt diese Aktionen aus. Cloud-Anbieter fügen manchmal proprietäre Tool-Runner zu ihren APIs hinzu, was in der Produktion praktisch ist, aber unpraktisch, wenn das Ziel darin besteht, das Protokoll selbst zu betrachten. Das Ausführen eines lokalen Modells über Ollama hält das Experiment selbstständig.
Probieren Sie eine Frage wie „Wie ist das Wetter heute in Italien?“ aus. Eine typische lokale Antwort beginnt damit, dass zugegeben wird, es gäbe keinen Echtzeit-Wetterdatenstrom. Dennoch enthält der Satz drei Hinweise, die das System entschlüsseln muss: Wetter als Thema, „heute“ als Datum und Italien als Ort. Das Modell benötigt einen Weg, um das Wetter zu berechnen oder abzurufen, eine Methode, um „heute“ zu bestimmen, und einen Weg, dieses Wetter mit Italien in Verbindung zu bringen.
Die Angabe des Kalenders stellt die offensichtliche Lücke dar. Die Gewichte kennen das aktuelle Datum nicht zuverlässig. MCP wird nützlich, wenn das Modell Tools und Argumente vorschlagen kann und eine umgebende Laufzeit tatsächlich diese Tools ausführt und frische Beobachtungen zurückgibt, die das Modell in eine Antwort einbinden kann.
Komponenten in MCP
Eine praktische Implementierung von MCP benennt in der Regel mehrere zusammenarbeitende Komponenten:
- Eine funktionierende API – ein Service, der bereits Fragen zum jeweiligen Bereich beantwortet, wie z. B. ein Wetter-Endpunkt im Internet.
- MCP-Server – ein Prozess, der versteckt, wie auf die API oder Datenbank zugegriffen wird, und aufrufbare Tools bereitstellt.
- MCP-Host – die Benutzeroberfläche des Produkts, beispielsweise ein intelligenter Reiseplaner, der LLM-Logik mit Echtzeitdaten kombiniert.
- MCP-Client – eine Brücke, die innerhalb des Hosts vorhanden ist. Sie teilt dem Modell mit, welche Tools verfügbar sind, wandelt Modelleinsätze in MCP-Anfragen um und konvertiert MCP-Antworten in für das Modell verständlichen Kontext.
- Transportebene – JSON-RPC 2.0, das entweder über HTTP/SSE übertragen wird, wenn die Komponenten entfernt sind, oder über STDIO, wenn Modell und Tools auf demselben Rechner liegen.
- LLM – hier
qwen3:8b, das von Ollama bereitgestellt wird.
Sobald diese Rollen benannt sind, wird die Frage zum Wetter in Italien zu einer Art Choreografie anstelle eines einzigen Modellaufrufs.
Analogie
Eine überzeugende Metapher hält die Rollen fest im Gedächtnis. Die Absicht zu fahren ist die Host-Anwendung. Das Gehirn entspricht dem LLM: Es analysiert den Kontext der „Autobahn“ und entscheidet, ob es beschleunigen, bremsen oder Gang wechseln soll, kann aber die Pedale nicht betätigen. Die Gliedmaßen entsprechen dem MCP-Server; die Muskeln und Knochen in einer Gliedmaße sind einzelne „Werkzeuge“ – eine Gliedmaße steuert oder schaltet, die andere bremst oder beschleunigt. Die Nervenverbindung zwischen Gehirn und Muskeln ist der MCP-Client. Die Nerven, die elektrische Impulse übertragen, stellen den Transport dar. Das Auto ist die externe API. Der zusammengesetzte Körper ist das „Gehäuse“, das dafür sorgt, dass alle Teile zusammenarbeiten.
Kompakte Zuordnung:
- LLM → Gehirn
- MCP-Server → Gliedmaße
- Werkzeug → Muskelaktivität
- MCP-Host → Fahrensabsicht
- MCP-Client → Nervenverbindung
- Transport → Nerven
- Arbeitende API → Auto
- Gehäuse → Körpereinheit
Bild allein reicht aus, um Server, Client und Transportmechanismus davon abzuhalten, zu einem verschwommenen „Plugin“ zusammenzuschmelzen.
Funktionsweise von MCP
Die Implementierung folgt dabei den jeweiligen Rollen: Ein Server, ein Host, ein LLM, ein Transportmechanismus – optional ein Harness sowie eine echte API oder Dienstleistung. Der Server abstrahiert die API und stellt Werkzeuge zur Verfügung. Jedes Werkzeug repräsentiert eine einzelne Aktion, die das Modell anfordern kann; das Modell führt selbst niemals den HTTP-Aufruf aus. Der Client bewirbt das Angebot und übersetzt in beide Richtungen, sodass Modell und Server locker voneinander getrennt bleiben.
Für einen Server, der get_todays_date() und get_weather_data(city, date) anbietet, kann eine Anfrage wie „Wie ist das Wetter heute in Paris?“ wie folgt ablaufen:
- Das Modell erkennt, dass es das heutige Datum benötigt.
- Es bittet den MCP-Client,
get_todays_datezu verwenden. - Der Client leitet die Anfrage an den Server weiter.
get_weather_data mit Stadt und Datum aufzurufen.Historische Fragen, die innerhalb des Trainingszeitraums liegen, können allein aus dem Gedächtnis beantwortet werden, doch der Sinn von MCP liegt im aktuellen Kontext: Datumsangaben und Wetterbedingungen, die nach dem Training ändern.
Das Projekt
Das Beispiel sorgt dafür, dass die Wetterbeschreibung konkret bleibt. Ein MCP-Server enthält die Logik, die mit einer externen Wetter-API kommuniziert. Eine Host-Anwendung erstellt den MCP-Client, registriert den Server und stellt Anfragen an Ollama. Die Isolierung des Zugriffs auf das LLM in einem eigenen Hilfsprogramm macht die Verbindungslogik übersichtlich.
MCP-Server
# MCP Server
# weather_server_mcp.py
from fastmcp import FastMCP
import requests
# This is a server instance that we register in our host
server = FastMCP("weather-mcp-server")
# Third party api data
WEATHER_API_KEY = "api_key_here"
WEATHER_BASE_URL = "https://api.weatherapi.com/v1/"
# Tool 1
@server.tool()
def get_weather_data(city: str) -> float:
"""Get current temperature in Celsius"""
response = requests.get(
WEATHER_BASE_URL + "current.json",
params={"key": WEATHER_API_KEY, "q": city},
)
response.raise_for_status()
return response.json()["current"]["temp_c"]
# Tool 2
@server.tool()
def get_historical_weather_data(city: str, date: str) -> float:
"""Get max temperature for a historical date"""
response = requests.get(
WEATHER_BASE_URL + "history.json",
params={"key": WEATHER_API_KEY, "q": city, "dt": date},
)
response.raise_for_status()
return response.json()["forecast"]["forecastday"][0]["day"]["maxtemp_c"]
if __name__ == "__main__":
server.run()
Funktionen, die auf die API zugreifen, sind mit @server.tool() annotiert, wodurch sie als Tools veröffentlicht werden. Die Docstrings am Anfang jeder Funktion dienen nicht nur der Dokumentation; sie weisen das Modell darauf hin, wann dieses Tool verwendet werden soll. Das Beispiel bietet zwei Tools an: Eines zur Abfrage des aktuellen Wetters einer Stadt und ein weiteres zur Abfrage des historischen Wetters einer Stadt an einem bestimmten Tag in der Vergangenheit.
MCP-Host, Client, LLM, Übertragungsmethode
import asyncio
import sys
import json
from pathlib import Path
from langchain_community.llms import Ollama
from fastmcp import Client
from fastmcp.client.transports import StdioTransport
async def main():
# We mention the mcp server path.
server_path = Path(__file__).parent / "weather_server_mcp.py"
# The transport method here is STDIO
transport = StdioTransport(
command=sys.executable,
args=[str(server_path)],
)
# Register the MCP Client
mcp_client = Client(transport)
# LLM via Ollama
llm = Ollama(model="qwen3:8b", temperature=0.5)
async with mcp_client:
print("✓ Connected to MCP server!")
# We can now access that tools are present in the weather server mcp now.
mcp_tools = await mcp_client.list_tools()
tools_info = "\n".join([f"- {t.name}: {t.description or t.name}" for t in mcp_tools])
print(f"✓ Available tools:\n{tools_info}\n")
# Interactive loop
while True:
question = input("🌤️ Ask: ").strip()
if question.lower() == 'exit':
break
try:
# Step 1: Ask LLM to decide which tool to use
decision_prompt = f"""Given the question: "{question}"
Available tools:
{tools_info}
Respond with ONLY a JSON object (no other text):
{{"tool": "tool_name", "params": {{"city": "city_name"}}}}
For get_historical_weather_data, use: {{"tool": "get_historical_weather_data", "params": {{"city": "city_name", "date": "YYYY-MM-DD"}}}}"""
print(f"\n📍 Processing: {question}")
llm_response = llm.invoke(decision_prompt)
# Step 2: Parse JSON from LLM response
json_start = llm_response.find('{')
json_end = llm_response.rfind('}') + 1
if json_start == -1 or json_end == 0:
print("❌ LLM didn't return valid tool call")
continue
json_str = llm_response[json_start:json_end]
tool_call = json.loads(json_str)
print("Tool call: ", tool_call)
# Handle array responses
if isinstance(tool_call, list):
tool_call = tool_call[0]
tool_name = tool_call.get("tool")
params = tool_call.get("params", {})
print(f"🔧 Calling: {tool_name} with {params}")
# Step 3: Call MCP tool. This is where we actually call the tool.
result = await mcp_client.call_tool(tool_name, params)
answer = result.content[0].text
print(f"✓ Answer: {answer}°C\n")
except json.JSONDecodeError as e:
print(f"❌ JSON parsing error: {e}")
except Exception as e:
print(f"❌ Error: {e}\n")
if __name__ == "__main__":
asyncio.run(main())
Was geschieht hier?
Lösen Sie den Pfad zum Server-Modul neben dem Host auf:
server_path = Path(__file__).parent / "weather_server_mcp.py"
Erstellen Sie einen STDIO-Transport, der dieses Modul mit dem aktuellen Python-Interpreter startet:
# The transport method here is STDIO
transport = StdioTransport(
command=sys.executable,
args=[str(server_path)],
)
Instanziieren Sie den MCP-Client über diesen Transport:
mcp_client = Client(transport)
Der Host verfügt nun über einen registrierten Serverpfad, einen ausgewählten Transport sowie einen Client. Fügen Sie das Modell über Ollama hinzu:
llm = Ollama(model="qwen3:8b", temperature=0.5)
Fragen Sie den Client nach dem von weather_server_mcp.py veröffentlichten Toolkatalog:
mcp_tools = await mcp_client.list_tools()
Geben Sie diesen Katalog in die Anweisung ein und weisen Sie das Modell an, nur den Toolnamen sowie die Parameter zurückzugeben. Nach der Parsing-Phase führen Sie das ausgewählte Tool aus:
result = await mcp_client.call_tool(tool_name, params)
Der Kern des Tutorials besteht daher darin: Den Server zu erstellen, ihn zu registrieren, den Client zu registrieren, ein LLM hinzuzufügen und einen Transport auszuwählen. Agent-Harnesses können einen Großteil dieser Verbindungen verbergen; eine einfache Schleife sorgt dafür, dass während des Lernens jeder Protokollschritt sichtbar bleibt.
Insgesamt ist MCP weniger ein einzelner Bibliotheksaufruf und eher eine Aufteilung der Arbeit. Das Modell schlägt vor; der Client übersetzt; der Server handelt; der Transport überträgt JSON-RPC-Nachrichten; der Host ist für den Benutzerkontakt verantwortlich. Sobald diese Grenzen klar sind, besteht der Wechsel von Wetterdaten zu Kalendern, CRM-Systemen oder internen Suchfunktionen hauptsächlich darin, neue Tools zu entwickeln und sie gut genug zu dokumentieren, damit das Modell die richtige Wahl treffen kann. Während der Ausführung des Zyklus sollte man beobachten, was das Modell vor jedem Toolaufruf ausgibt. Ein gesunder Ablauf zeigt, dass das Modell den Namen eines tatsächlich existierenden Tools angibt, die im Dokumentationstext beschriebenen Argumente übermittelt und auf die Rückgabe von Daten durch den Client wartet, bevor es einen Satz für den Benutzer formuliert. Wenn das Modell einen fiktiven Toolnamen erfindet, sollte der Prompt präzisiert oder die Toolbeschreibungen verbessert werden. Wenn der Server Fehler auslöst, muss dieser über den Client sichtbar gemacht werden, damit das Modell es erneut versuchen oder sich entschuldigen kann, anstatt Halluzinationen zu erzeugen.
Die Verarbeitung der Wetterwerte. Diese Disziplin bei der Rückkopplung ist genauso wichtig wie die anfängliche Konfiguration.