Protocole de contexte pour débutants avec FastMCP et Ollama
Apprenez les rôles MCP — hôte, client, serveur, transport — puis connectez un serveur d’outil météorologique à un modèle local qwen3:8b via FastMCP et STDIO.
Le Model Context Protocol, généralement abrégé en MCP, est un langage commun permettant de relier les grands modèles de langage à des outils et des sources de données qu’ils ne peuvent pas atteindre seuls. L’appellation « protocole » souligne qu’il standardise la manière dont la conversation doit s’organiser ; des bibliothèques concrètes mettent ensuite en œuvre cette norme afin que les équipes n’aient pas à inventer seules des sockets et des schémas de messages. FastMCP est l’une de ces implementations utilisées dans la démonstration ci-dessous.
Repositorio associé : https://github.com/harshagangari747/MCPTutorial/tree/main
Prérequis
La démonstration dépend de trois paquets : fastmcp, ollama et langchain-community. L’inférence s’exécute sur le modèle local qwen3:8b. Lancez-le avec :
ollama run qwen3:8b
Préparez un dossier de projet qui contient déjà des fichiers vides nommés weather_server_mcp.py et app.py afin que le serveur et l’application aient des emplacements bien définis.
Comprendre MCP
Seul, un LLM n’est qu’un convertisseur de tokens : les tokens entrent et les tokens sortent. Il ne communique pas avec des API météorologiques, n’ouvre pas de bases de données et ne lit pas l’horloge système à moins qu’une action externe au modèle ne le fasse. Les fournisseurs de services cloud ajoutent parfois des outils propriétaires à leurs API, ce qui est pratique en environnement de production mais gênant lorsque l’objectif est d’examiner le protocole lui-même. Lancer un modèle local via Ollama permet de garder l’expérience autonome.
Essayez une question telle que « quel temps fait-il aujourd’hui en Italie ? » Une réponse typique locale commence par admettre qu’il n’y a pas de flux météorologique en temps réel. Néanmoins, cette phrase contient trois indications que le système doit interpréter : le temps en tant que sujet, « aujourd’hui » en tant que date, et l’Italie en tant que lieu. Le modèle a besoin d’un moyen de calculer ou de récupérer les informations météorologiques, d’une façon de déterminer la date actuelle, et d’un moyen de relier ces données météorologiques à l’Italie.
Le problème évident réside dans la détermination de la date. Les poids du modèle ne permettent pas de connaître avec fiabilité la date actuelle. MCP devient utile lorsque le modèle peut proposer des outils et des arguments, et que l’environnement d’exécution correspondant met réellement en œuvre ces outils, fournissant ainsi des données fraîches que le modèle peut intégrer dans sa réponse.
Composants de MCP
Une implémentation pratique de MCP désigne généralement plusieurs éléments qui coopèrent entre eux :
- Une API fonctionnelle — tout service qui répond déjà à la question du domaine, comme une interface météo trouvée en ligne.
- Serveur MCP — un processus qui masque la manière d’accéder à l’API ou à la base de données et publie des outils pouvant être appelés.
- Hôte MCP — l’interface utilisateur, par exemple un planificateur de voyages intelligent qui combine le raisonnement des LLM avec des données en temps réel.
- Client MCP — un pont intégré à l’hôte. Il indique au modèle quels outils sont disponibles, transforme les intentions du modèle en requêtes MCP et convertit les réponses MCP en un contexte adapté au modèle.
- Couche de transport — JSON-RPC 2.0, transmis soit via HTTP/SSE lorsque les composants sont distants, soit via STDIO lorsque le modèle et les outils se trouvent sur la même machine.
- LLM — ici
qwen3:8bfourni par Ollama.
Avec ces rôles définis, la question concernant le temps en Italie devient une chorégraphie d’opérations plutôt qu’un simple appel à un modèle.
Analogie
Une métaphore de conduite permet de maintenir les rôles bien définis. L’intention de conduire correspond à l’application hôte. Le cerveau représente le LLM : il analyse le contexte routier et décide d’accélérer, de freiner ou de changer de vitesse, mais il ne peut pas appuyer sur les pédales. Les membres correspondent au serveur MCP ; les muscles et les os à l’intérieur d’un membre sont des outils individuels — un membre sert à diriger ou à changer de vitesse, un autre à freiner ou à accélérer. L’interface nerveuse entre le cerveau et les muscles est le client MCP. Les nerfs qui transmettent des impulsions électriques représentent le système de transport. La voiture correspond à l’API externe. Le corps assemblé représente le système de fixation qui permet aux différentes parties de coopérer.
Mappage compressé :
- LLM → cerveau
- Serveur MCP → membre
- Outil → action musculaire
- Hôte MCP → intention de conduite
- Client MCP → interface nerveuse
- Transport → nerfs
- API fonctionnelle → voiture
- Système de fixation → assemblage du corps
Cette image suffit à empêcher que le serveur, le client et le mécanisme de transport ne se fusionnent en un simple « plugin » vague.
Fonctionnement de MCP
L’implémentation suit alors les rôles suivants : mettre en place un serveur, un hôte, un LLM, un mécanisme de transport, éventuellement un outil d’intégration, ainsi qu’une véritable API ou service. Le serveur abstracte l’API et expose des outils. Chaque outil représente une action spécifique que le modèle peut demander ; le modèle ne lance jamais lui-même l’appel HTTP. Le client à la fois publie le catalogue des outils et assure la traduction dans les deux sens, ce qui permet au modèle et au serveur de rester lâchement couplés.
Avec un serveur offrant les fonctions get_todays_date() et get_weather_data(city, date), une requête telle que « Quel temps fait-il aujourd’hui à Paris ? » peut se dérouler comme suit :
- Le modèle constate qu’il a besoin de la date d’aujourd’hui.
- Il demande au client MCP d’utiliser la fonction
get_todays_date. - Le client envoie la requête au serveur.
get_weather_data en indiquant la ville et la date.Les questions historiques relevant de la période d’entraînement peuvent être répondues uniquement à partir de la mémoire, mais l’avantage du MCP réside dans le contexte actuel : dates et conditions météorologiques qui changent après l’entraînement.
Le projet
Cet exemple rend le récit météorologique concret. Un serveur MCP gère la logique de communication avec une API météorologique externe. Une application hôte crée le client MCP, enregistre le serveur et interroge Ollama. En isolant l’accès au LLM dans son propre outil d’aide, on assure une lisibilité claire des connexions de transport.
Serveur MCP
# 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()
Les fonctions qui interagissent avec l’API sont annotées avec @server.tool(), ce qui les publie en tant qu’outils. Les docstrings en haut de chaque fonction ne sont pas des décorations ; elles indiquent au modèle quand choisir cet outil. L’exemple fournit deux outils : l’un permet de récupérer les conditions météorologiques actuelles d’une ville, et l’autre permet de récupérer les conditions météorologiques historiques d’une ville pour une date passée.
Hôte MCP, client, LLM, méthode de transport
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())
Que se passe-t-il ?
Résoudre le chemin du module serveur à côté de l’hôte :
server_path = Path(__file__).parent / "weather_server_mcp.py"
Créez un transport STDIO qui lance ce module avec l’interpréteur Python actuel :
# The transport method here is STDIO
transport = StdioTransport(
command=sys.executable,
args=[str(server_path)],
)
Instanciez le client MCP à partir de ce transport :
mcp_client = Client(transport)
L’hôte dispose désormais d’un chemin de serveur enregistré, d’un transport choisi et d’un client. Associez le modèle via Ollama :
llm = Ollama(model="qwen3:8b", temperature=0.5)
Demandez au client le catalogue d’outils publié par weather_server_mcp.py :
mcp_tools = await mcp_client.list_tools()
Passez ce catalogue dans la requête et indiquez au modèle de répondre uniquement par le nom de l’outil et ses paramètres. Après analyse, exécutez l’outil choisi :
result = await mcp_client.call_tool(tool_name, params)
L’essentiel de ce tutoriel consiste donc à créer le serveur, à l’enregistrer, à enregistrer le client, à associer un LLM et à sélectionner un transport. Les outils d’agentisation peuvent cacher une partie de ces connexions ; une boucle simple permet de voir chaque étape du protocole pendant l’apprentissage.
En somme, MCP relève moins d’une simple appel de bibliothèque que d’une répartition des tâches. Le modèle propose ; le client traduit ; le serveur agit ; le transport transmet des messages JSON-RPC ; l’hôte gère la boucle destinée à l’utilisateur. Une fois ces limites claires, remplacer les informations météorologiques par des calendriers, des CRM ou des outils de recherche internes consiste principalement à créer de nouveaux outils et à les documenter suffisamment bien afin que le modèle puisse choisir correctement. Lorsque la boucle est en cours d’exécution, observez ce que le modèle émet avant chaque appel à un outil. Un trace sain montre que le modèle mentionne un outil qui existe réellement, fournit les clés d’argument décrites dans la documentation, et attend que le client renvoie des données avant de rédiger la phrase destinée à l’utilisateur. Si le modèle invente un nom d’outil, affinez la demande ou améliorez les descriptions des outils. Si le serveur génère une erreur, affichez-la via le client afin que le modèle puisse réessayer ou s’excuser au lieu de produire des hallucinations.
Les valeurs météorologiques en temps réel. Cette discipline de feedback est tout aussi importante que le câblage initial.