MCP, Function Calling, Tool Use : le guide complet
Les grands modèles de langage (LLM) sont puissants, mais ils ont une limite fondamentale : ils ne peuvent que générer du texte. Pour qu'un agent IA puisse réellement agir — chercher sur le web, interroger une base de données, envoyer un email — il faut un mécanisme qui lui permette d'appeler des outils externes. C'est exactement ce que proposent MCP (Model Context Protocol), Function Calling (OpenAI) et Tool Use (Anthropic).
Dans ce guide, on décortique chaque approche, on compare leurs architectures, et on vous donne des exemples de code prêts à l'emploi pour intégrer des outils dans vos agents IA.
🧠 Pourquoi les LLM ont besoin d'outils
Un LLM seul ne peut pas :
- Accéder à des données en temps réel (météo, cours de bourse, actualités)
- Effectuer des calculs précis (arithmétique complexe, conversions)
- Interagir avec des systèmes externes (API, bases de données, fichiers)
- Exécuter du code ou des commandes système
Sans mécanisme d'appel d'outils, un LLM est condamné à halluciner les réponses à ces questions. Les protocoles d'appel d'outils résolvent ce problème en permettant au modèle de déclarer qu'il souhaite utiliser un outil, puis de recevoir le résultat pour formuler sa réponse finale.
Le flux général est toujours le même :
- L'utilisateur pose une question
- Le modèle identifie qu'un outil est nécessaire
- Le modèle produit un appel structuré (nom de l'outil + paramètres)
- Le système hôte exécute l'outil
- Le résultat est renvoyé au modèle
- Le modèle formule sa réponse finale
Ce qui change entre les approches, c'est comment les étapes 3 à 5 sont implémentées.
🔧 Function Calling (OpenAI)
Principe
Function Calling est l'approche introduite par OpenAI en juin 2023. L'idée est simple : vous décrivez des fonctions disponibles dans votre prompt système sous forme de schémas JSON, et le modèle peut décider d'en appeler une au lieu de répondre directement.
Architecture
Utilisateur → API OpenAI (avec définitions de fonctions)
↓
Le modèle choisit une fonction
↓
Retourne un JSON structuré
↓
Votre code exécute la fonction
↓
Résultat renvoyé au modèle
↓
Réponse finale à l'utilisateur
Définition des fonctions
Chaque fonction est décrite avec un schéma JSON Schema :
{
"name": "get_weather",
"description": "Récupère la météo actuelle pour une ville donnée",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Le nom de la ville (ex: Paris, Lyon)"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "L'unité de température"
}
},
"required": ["city"]
}
}
Exemple complet en Python
import openai
import json
client = openai.OpenAI()
# Définir les fonctions disponibles
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Récupère la météo pour une ville",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}
]
# Fonction réelle (votre logique)
def get_weather(city, unit="celsius"):
# En production : appel à une API météo
return {"city": city, "temperature": 18, "unit": unit, "condition": "nuageux"}
# Appel au modèle
messages = [{"role": "user", "content": "Quel temps fait-il à Paris ?"}]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto"
)
# Vérifier si le modèle veut appeler une fonction
message = response.choices[0].message
if message.tool_calls:
for tool_call in message.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
# Exécuter la fonction
result = get_weather(**func_args)
# Renvoyer le résultat au modèle
messages.append(message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
# Obtenir la réponse finale
final = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools
)
print(final.choices[0].message.content)
Modes d'appel
OpenAI propose trois modes via le paramètre tool_choice :
| Mode | Comportement |
|---|---|
"auto" |
Le modèle décide seul s'il doit appeler une fonction |
"none" |
Le modèle ne peut pas appeler de fonction |
"required" |
Le modèle doit appeler au moins une fonction |
{"type": "function", "function": {"name": "X"}} |
Force l'appel d'une fonction spécifique |
Appels parallèles
Depuis fin 2023, GPT-4 et GPT-3.5-turbo peuvent déclencher plusieurs appels de fonctions en parallèle dans une seule réponse. Par exemple, si l'utilisateur demande "la météo à Paris et à Lyon", le modèle produira deux tool_calls simultanés.
# Le modèle peut retourner plusieurs tool_calls
for tool_call in message.tool_calls:
# Traiter chaque appel indépendamment
...
Vous pouvez désactiver ce comportement avec parallel_tool_calls: false.
Forces et limites
Avantages :
- Syntaxe simple et bien documentée
- Support natif des appels parallèles
- Large écosystème d'exemples
- Compatible avec tous les modèles GPT récents
Limites :
- Propriétaire à OpenAI (bien que le format soit repris par d'autres)
- Les définitions de fonctions consomment des tokens
- Pas de standard ouvert officiel
🛠️ Tool Use (Anthropic)
Principe
Anthropic a introduit Tool Use pour Claude en avril 2024. Le concept est similaire au Function Calling d'OpenAI, mais avec quelques différences architecturales notables, notamment le format des messages et la gestion des résultats.
Architecture
L'architecture est proche de celle d'OpenAI, mais le format des messages diffère :
Utilisateur → API Anthropic (avec définitions d'outils)
↓
Claude choisit un outil
↓
Retourne un bloc tool_use dans le contenu
↓
Votre code exécute l'outil
↓
Résultat renvoyé via un message tool_result
↓
Réponse finale à l'utilisateur
Définition des outils
Anthropic utilise aussi JSON Schema mais avec une structure légèrement différente :
{
"name": "get_weather",
"description": "Récupère la météo actuelle pour une ville donnée. Retourne la température et les conditions.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Le nom de la ville"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "L'unité de température souhaitée"
}
},
"required": ["city"]
}
}
La différence clé : input_schema au lieu de parameters.
Exemple complet en Python
import anthropic
import json
client = anthropic.Anthropic()
# Définir les outils
tools = [
{
"name": "get_weather",
"description": "Récupère la météo pour une ville donnée",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
]
def get_weather(city, unit="celsius"):
return {"city": city, "temperature": 18, "unit": unit, "condition": "nuageux"}
# Premier appel
messages = [{"role": "user", "content": "Quel temps fait-il à Paris ?"}]
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=tools,
messages=messages
)
# Vérifier le stop_reason
if response.stop_reason == "tool_use":
# Extraire le bloc tool_use
tool_block = next(b for b in response.content if b.type == "tool_use")
# Exécuter l'outil
result = get_weather(**tool_block.input)
# Construire la conversation complète
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_block.id,
"content": json.dumps(result)
}
]
})
# Obtenir la réponse finale
final = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=tools,
messages=messages
)
print(final.content[0].text)
Contrôle de l'utilisation des outils
Anthropic propose un paramètre tool_choice similaire :
| Mode | Comportement |
|---|---|
{"type": "auto"} |
Claude décide seul |
{"type": "any"} |
Claude doit utiliser un outil (n'importe lequel) |
{"type": "tool", "name": "X"} |
Force l'utilisation d'un outil spécifique |
Particularités d'Anthropic
Le "thinking" avant l'outil : Claude peut inclure du texte (réflexion) avant l'appel d'outil dans le même message. Cela donne de la transparence sur son raisonnement.
Résultats d'erreur : Vous pouvez signaler qu'un outil a échoué :
{
"type": "tool_result",
"tool_use_id": "toolu_abc123",
"is_error": True,
"content": "Erreur : ville non trouvée"
}
Claude adaptera sa réponse en conséquence, potentiellement en reformulant sa requête ou en informant l'utilisateur.
Forces et limites
Avantages :
- Réflexion visible avant l'appel d'outil
- Gestion native des erreurs avec is_error
- Descriptions d'outils très détaillées pour un meilleur choix
- Support du streaming avec les blocs tool_use
Limites :
- Format légèrement plus verbeux
- Écosystème d'exemples moins fourni (mais en croissance rapide)
- Le résultat de l'outil doit être dans un message user (architecture particulière)
🌐 MCP (Model Context Protocol)
Principe
MCP est un protocole ouvert créé par Anthropic et publié fin 2024. Contrairement au Function Calling et au Tool Use qui sont des mécanismes d'API propres à chaque fournisseur, MCP vise à être un standard universel pour connecter les LLM à des sources de données et des outils.
L'analogie souvent utilisée : MCP est au LLM ce que USB est aux périphériques. Un protocole unique pour tout connecter.
Architecture
Pour aller plus loin sur ce sujet, consultez notre guide Créer son premier agent IA autonome.
MCP introduit une architecture client-serveur :
Pour aller plus loin sur ce sujet, consultez notre guide Les 5 patterns d'agents IA qui marchent.
┌─────────────────────────────────┐
│ Application hôte │
│ (Claude Desktop, IDE, Agent) │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Client │ │ Client │ │
│ │ MCP #1 │ │ MCP #2 │ │
│ └────┬─────┘ └────┬─────┘ │
└───────┼──────────────┼───────────┘
│ │
┌────▼─────┐ ┌────▼─────┐
│ Serveur │ │ Serveur │
│ MCP │ │ MCP │
│ (Files) │ │ (GitHub) │
└──────────┘ └──────────┘
Composants :
- Hôte MCP : L'application (Claude Desktop, un IDE, votre agent)
- Client MCP : Maintient une connexion 1:1 avec un serveur
- Serveur MCP : Expose des outils, des ressources et des prompts
Les trois primitives MCP
MCP ne se limite pas aux outils. Il expose trois types de capacités :
| Primitive | Description | Exemple |
|---|---|---|
| Tools | Fonctions appelables par le LLM | Chercher sur le web, exécuter du SQL |
| Resources | Données consultables (lecture seule) | Contenu d'un fichier, résultat d'une requête |
| Prompts | Templates de prompts réutilisables | Template de résumé, template d'analyse |
Exemple de serveur MCP en Python
Avec le SDK officiel Python :
from mcp.server.fastmcp import FastMCP
# Créer un serveur MCP
mcp = FastMCP("weather-server")
@mcp.tool()
def get_weather(city: str, unit: str = "celsius") -> dict:
"""Récupère la météo actuelle pour une ville donnée.
Args:
city: Le nom de la ville (ex: Paris, Lyon)
unit: L'unité de température (celsius ou fahrenheit)
"""
# En production : appel à une vraie API
return {
"city": city,
"temperature": 18,
"unit": unit,
"condition": "nuageux"
}
@mcp.resource("weather://current/{city}")
def current_weather(city: str) -> str:
"""Ressource : météo actuelle d'une ville."""
data = get_weather(city)
return f"{data['city']}: {data['temperature']}°C, {data['condition']}"
@mcp.prompt()
def weather_report(city: str) -> str:
"""Génère un prompt pour un rapport météo détaillé."""
return f"Génère un rapport météo complet pour {city} en incluant les prévisions sur 3 jours."
# Lancer le serveur
if __name__ == "__main__":
mcp.run()
Exemple de serveur MCP en TypeScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "weather-server",
version: "1.0.0"
});
server.tool(
"get_weather",
"Récupère la météo pour une ville",
{
city: z.string().describe("Nom de la ville"),
unit: z.enum(["celsius", "fahrenheit"]).optional()
},
async ({ city, unit = "celsius" }) => {
return {
content: [{
type: "text",
text: JSON.stringify({
city,
temperature: 18,
unit,
condition: "nuageux"
})
}]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Transports
MCP supporte deux modes de transport :
| Transport | Usage | Communication |
|---|---|---|
| stdio | Serveurs locaux | Le client lance le serveur comme un processus enfant |
| SSE (Server-Sent Events) | Serveurs distants | Communication HTTP, idéal pour les services cloud |
Configuration dans Claude Desktop
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/chemin/vers/weather_server.py"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxx"
}
}
}
}
Écosystème de serveurs MCP
L'un des grands avantages de MCP est son écosystème croissant de serveurs prêts à l'emploi :
- Fichiers : Lecture/écriture de fichiers locaux
- GitHub : Repos, issues, pull requests
- Bases de données : PostgreSQL, SQLite, MongoDB
- Web : Brave Search, Fetch
- Productivité : Google Drive, Slack, Notion
- Développement : Docker, Kubernetes
Forces et limites
Avantages :
- Standard ouvert et interopérable
- Trois primitives (tools, resources, prompts)
- Écosystème de serveurs réutilisables
- Séparation claire client/serveur
- Fonctionne avec n'importe quel LLM (pas lié à un fournisseur)
Limites :
- Plus complexe à mettre en place qu'un simple function calling
- Encore jeune (spec en évolution)
- Nécessite un runtime pour les serveurs
- Overhead pour des cas d'usage simples
📊 Tableau comparatif complet
| Critère | Function Calling (OpenAI) | Tool Use (Anthropic) | MCP |
|---|---|---|---|
| Type | API propriétaire | API propriétaire | Protocole ouvert |
| Créateur | OpenAI | Anthropic | Anthropic (open source) |
| Format de définition | JSON Schema (parameters) |
JSON Schema (input_schema) |
Décorateurs / SDK |
| Appels parallèles | ✅ Natif | ✅ Possible | ✅ Via le client |
| Gestion d'erreurs | Via le contenu du message | is_error natif |
Via le protocole |
| Streaming | ✅ | ✅ | ✅ |
| Multi-fournisseur | ❌ (format repris par d'autres) | ❌ | ✅ |
| Ressources (données) | ❌ | ❌ | ✅ |
| Prompts réutilisables | ❌ | ❌ | ✅ |
| Complexité setup | ⭐ Faible | ⭐ Faible | ⭐⭐⭐ Moyenne |
| Maturité | ⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
| Documentation | Excellente | Très bonne | Bonne (en croissance) |
🔀 Quand utiliser quoi ?
Utilisez Function Calling (OpenAI) si :
- Vous utilisez déjà les modèles GPT
- Votre cas d'usage est simple (quelques fonctions)
- Vous voulez le setup le plus rapide possible
- Vous n'avez pas besoin de partager vos outils entre projets
Utilisez Tool Use (Anthropic) si :
- Vous utilisez Claude comme modèle principal
- Vous avez besoin de la réflexion visible du modèle
- La gestion fine des erreurs est importante
- Vous voulez un modèle qui excelle dans le choix d'outils complexes
Utilisez MCP si :
- Vous construisez un agent qui doit fonctionner avec plusieurs LLM
- Vous voulez un écosystème d'outils réutilisables entre projets
- Vous avez besoin de plus que des outils (ressources, prompts)
- Vous construisez une plateforme ou un framework d'agents
- La standardisation et l'interopérabilité sont prioritaires
Combiner les approches
En pratique, beaucoup de développeurs combinent ces approches. Par exemple :
- Utiliser MCP pour définir et partager les outils
- Traduire les outils MCP en Function Calling ou Tool Use selon le LLM utilisé
- Des frameworks comme LangChain et OpenClaw font cette traduction automatiquement
# Pseudo-code : un agent qui utilise MCP en interne
# mais communique via Function Calling ou Tool Use
class UniversalAgent:
def __init__(self, mcp_servers, llm_provider):
self.mcp_client = MCPClient(mcp_servers)
self.llm = llm_provider # "openai" ou "anthropic"
def get_tools_for_llm(self):
mcp_tools = self.mcp_client.list_tools()
if self.llm == "openai":
return convert_to_function_calling(mcp_tools)
elif self.llm == "anthropic":
return convert_to_tool_use(mcp_tools)
async def run(self, user_message):
tools = self.get_tools_for_llm()
response = await self.llm.complete(user_message, tools)
# Gérer les appels d'outils via MCP
...
🚀 Bonnes pratiques communes
Quelle que soit l'approche choisie, certaines bonnes pratiques s'appliquent toujours :
1. Des descriptions claires et précises
Le modèle choisit ses outils en se basant sur les descriptions. Soyez explicite :
# ❌ Mauvais
"description": "Cherche des trucs"
# ✅ Bon
"description": "Recherche des articles dans la base de données par mot-clé. Retourne les 10 résultats les plus récents avec titre, date et résumé."
2. Validez les paramètres
Ne faites jamais confiance aux paramètres générés par le modèle sans validation :
def execute_tool(name, params):
# Toujours valider !
if name == "delete_file":
path = params.get("path", "")
if ".." in path or path.startswith("/"):
raise ValueError("Chemin non autorisé")
# ...
3. Gérez les erreurs gracieusement
Renvoyez des messages d'erreur clairs pour que le modèle puisse s'adapter :
try:
result = execute_tool(name, params)
return {"success": True, "data": result}
except Exception as e:
return {"success": False, "error": str(e), "suggestion": "Vérifiez les paramètres"}
4. Limitez le nombre d'outils
Plus vous exposez d'outils, plus le modèle risque de se tromper. Gardez un set focalisé et pertinent pour la tâche.
| Nombre d'outils | Recommandation |
|---|---|
| 1-5 | ✅ Idéal |
| 6-15 | ⚠️ Acceptable avec de bonnes descriptions |
| 15-30 | ⚠️ Regroupez en catégories |
| 30+ | ❌ Trop — utilisez un routeur ou un sélecteur d'outils |
5. Testez avec des cas limites
Les modèles peuvent halluciner des paramètres ou appeler le mauvais outil. Testez systématiquement :
- Requêtes ambiguës ("cherche ça" → quel outil ?)
- Paramètres manquants
- Résultats vides ou erreurs
- Chaînes d'appels (outil A → outil B)
🔮 L'avenir des interactions LLM-outils
L'écosystème évolue rapidement :
- MCP gagne en adoption et pourrait devenir le standard de facto
- OpenAI et Google travaillent sur leurs propres protocoles d'outils
- Les frameworks (LangChain, CrewAI, OpenClaw) abstraient ces différences
- La tendance est à l'interopérabilité : un outil défini une fois, utilisable partout
L'important n'est pas de choisir "le meilleur" protocole, mais de comprendre les principes sous-jacents. Le mécanisme fondamental — le modèle déclare vouloir utiliser un outil, votre code l'exécute, le résultat revient au modèle — reste le même partout.
Maîtrisez ce pattern, et vous pourrez construire des agents IA capables d'interagir avec n'importe quel système.
📚 Articles liés
- Qu'est-ce qu'OpenClaw ? — Découvrez comment OpenClaw implémente nativement MCP et le Tool Use
- Configurer OpenClaw : SOUL, AGENTS et Skills — Configurez les outils et skills de votre agent
- Claude (Anthropic) — Tout savoir sur Claude et son approche Tool Use
- OpenRouter — Accédez à tous les LLM via une API unique, compatible Function Calling
- Automatiser sa vie avec OpenClaw — Mettez en pratique les outils dans des automatisations concrètes