Crea con CometAPITi serve una sola API per più modelli?Testa il flusso di lavoro in Playground, poi crea una chiave API quando modello e costo sono adatti al tuo caso d'uso.
MCP vs Function Calling: Come connettere gli strumenti agli agenti IA - CometAPI
MCP vs Function Calling: Come connettere gli strumenti agli agenti IA
Confronta MCP con le chiamate di funzione e costruisci un ciclo completo di strumenti con CometAPI. Segui il codice Python per eseguire la stessa ricerca dell’ordine sia direttamente sia tramite MCP.
Richiamo di funzioni offre al modello un modo per richiedere uno strumento. MCP fornisce all’applicazione un modo standard per scoprire e chiamare strumenti su un server. Usa un’integrazione diretta delle funzioni per un piccolo set di strumenti di proprietà della tua applicazione. Aggiungi MCP quando vuoi consumare un servizio compatibile esistente o esporre strumenti tramite un’interfaccia condivisa. Di seguito collegherai la stessa consultazione dell’ordine in entrambi i modi tramite CometAPI, utilizzando un unico ciclo completo dell’agente.
Punti chiave
Inizia con l’instradamento diretto quando pochi strumenti servono un’unica applicazione; usa MCP quando un servizio di strumenti compatibile o un’interfaccia di strumenti condivisa rende l’integrazione più facile da mantenere.
Entrambi i percorsi richiedono un ciclo completo: descrivere lo strumento, ricevere una chiamata, eseguirla, restituire il risultato e lasciare che il modello risponda.
Revisionato per chiarezza, attribuzione delle fonti e terminologia API aggiornata.
Puoi mantenere la stessa funzione esposta al modello cambiando la sua esecuzione da un handler diretto a un client MCP.
Mantieni contratti di strumento stretti e verifica i risultati rispetto al compito che vuoi che l’agente completi.
Come si integrano il richiamo di funzioni e MCP
Un agente di supporto può spiegare un ritardo di spedizione, ma ha bisogno di accedere al sistema ordini per dire a un cliente dove si trova il suo pacco. Il richiamo di funzioni offre al modello un modo per richiedere quella consultazione. La tua applicazione esegue la consultazione e restituisce il risultato. L’handler può usare una libreria locale, un database o un’API remota.
Per soddisfare quella richiesta tramite MCP, l’applicazione usa un client per scoprire gli strumenti del server con tools/list e invocarli con tools/call. L’applicazione decide comunque quali strumenti scoperti esporre al proprio modello.
Azione richiesta dal modello al codice dell’applicazione
Client MCP dell’applicazione verso un server di strumenti
Ruolo principale
Permettere al modello di richiedere un’operazione nominata con argomenti strutturati
Standardizzare come le applicazioni compatibili scoprono e invocano strumenti sul server
Definizioni degli strumenti
Definizioni fornite nel formato dell’API del modello
Definizioni fornite dal server, inclusa un’inputSchema
Posizione di esecuzione
L’handler a cui l’applicazione effettua il dispatch, che può chiamare un servizio locale o remoto
L’implementazione dello strumento sul server MCP, che può chiamare un servizio di backend
Server separato richiesto?
Nessun server è richiesto dall’interfaccia di richiamo di funzioni
È richiesto un server MCP, ma un client può avviarlo come subprocess stdio; un servizio remoto distribuito separatamente è opzionale
Uso tipico
Pochi strumenti di proprietà dell’applicazione con dispatch diretto
Servizi compatibili esistenti o un’interfaccia di strumenti riutilizzata tra applicazioni
Possono funzionare insieme?
L’applicazione può instradare una richiesta di funzione del modello tramite MCP
Il client MCP soddisfa quella richiesta con tools/call e restituisce il risultato all’applicazione
L’ultima riga è la chiave per costruire con entrambi. Un modello può richiedere get_order_status, mentre l’applicazione sceglie se chiamare il proprio handler o un server MCP. La definizione della funzione esposta al modello può rimanere la stessa.
Scegli l’integrazione di cui la tua applicazione ha bisogno
Consigliamo di partire con un’integrazione diretta quando possiedi pochi strumenti usati da una sola applicazione. C’è meno da distribuire, e il percorso dalla richiesta del modello al tuo codice di business è facile da seguire.
MCP diventa utile quando un server compatibile fornisce già la capacità, o quando più applicazioni di IA hanno bisogno di un’interfaccia di strumenti comune. Lo stesso strumento per gli ordini può servire un assistente di supporto e un’applicazione interna di operazioni, con ciascun host che controlla come i suoi utenti vi accedono.
Un dispatcher può supportare sia funzioni interne sia servizi MCP, instradando ogni operazione alla sua implementazione. Librerie e API REST possono anche essere condivise; scegli MCP quando la sua interfaccia standard di discovery e invocazione è utile alle applicazioni che consumano gli strumenti.
Costruisci un ciclo completo di strumenti tramite CometAPI
Useremo un’operazione di sola lettura, get_order_status, e un ordine fittizio, ORD-104. L’esempio prima chiama l’operazione direttamente, poi la esegue tramite un server MCP locale. Entrambe le modalità usano la Responses API di CometAPI per l’inferenza del modello.
Figura 2. Un unico ciclo agente può supportare esecuzione diretta e MCP. Illustrazione originale, basata sull’architettura MCP e sul flusso di richiamo di funzioni di CometAPI.
Passaggio 1 Configura il progetto
Usa Python 3.11 o successivo e una chiave API CometAPI. Questo walkthrough è stato testato con Python 3.12.14 e mcp==2.3.0. Segui il quickstart di CometAPI per creare una chiave. In una shell macOS o Linux, crea un progetto e attiva un ambiente virtuale:
Sostituisci il segnaposto con la tua chiave e tienila fuori dal codice sorgente pubblico. Questo walkthrough blocca l’SDK MCP usato dall’esempio; la guida ufficiale al server Python si riferisce all’API 2.x. I due comandi dell’agente sotto inviano richieste a CometAPI e consumano credito API.
Passaggio 2 Definisci la ricerca dell’ordine
Salva questo come orders.py. Dati fissi mantengono il walkthrough auto-contenuto; in seguito puoi sostituire la consultazione con il tuo servizio.
"""Read-only, invented order data; never connects to a real order system."""
import re
def get_order_status(order_id: str) -> dict[str, str]:
"""Look up an invented order; ORD-104 is the only available record."""
if not isinstance(order_id, str) or not re.fullmatch(r"ORD-[0-9]{3}", order_id):
raise ValueError("order_id must have the form ORD-104")
if order_id != "ORD-104":
raise ValueError("Order not found in this demo")
return {"order_id": order_id, "status": "shipped", "source": "fictional_demo"}
Chiamare get_order_status("ORD-104") restituisce questo fixture:
Salva il seguente script completo come agent.py. Registra la funzione, invia la domanda a CometAPI, esegue le chiamate richieste e restituisce ciascun risultato usando il call_id originale.
La definizione esposta al modello usa il campo parameters della Responses API di CometAPI. MCP fornisce un inputSchema; questo esempio verifica il contratto scoperto rispetto allo schema a una sola stringa order_id prima di esporre la funzione revisionata.
"""A deliberately narrow direct/MCP bridge, not a general MCP adapter."""
import argparse
import asyncio
import json
import os
from pathlib import Path
import sys
from urllib.request import Request, urlopen
from mcp import Client, StdioServerParameters
from mcp.client.stdio import stdio_client
from orders import get_order_status
NAME = "get_order_status"
SCHEMA = {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"], "additionalProperties": False}
TOOL = {"type": "function", "name": NAME, "description": "Look up fictional order ORD-104.",
"parameters": SCHEMA, "strict": True}
def validate_arguments(arguments):
# Exact validation for SCHEMA above; deliberately not a JSON Schema engine.
if (not isinstance(arguments, dict) or set(arguments) != {"order_id"}
or not isinstance(arguments["order_id"], str)):
raise ValueError("Expected exactly one string argument: order_id")
def validate_catalog(tools):
if len(tools) != 1 or tools[0].name != NAME:
raise RuntimeError("Unexpected MCP tool catalog")
schema = tools[0].input_schema
if (schema.get("type") != "object" or schema.get("required") != ["order_id"]
or set(schema.get("properties", {})) != {"order_id"}
or schema["properties"]["order_id"].get("type") != "string"):
raise RuntimeError("MCP schema does not match this reviewed demo tool")
async def dispatch(name, arguments, client=None):
if name != NAME:
raise ValueError("Tool is not allowlisted")
try:
validate_arguments(arguments)
if client is None:
return {"is_error": False, "data": get_order_status(**arguments)}
except ValueError as error:
return {"is_error": True, "error": str(error)}
result = await client.call_tool(name, arguments)
if getattr(result, "result_type", "complete") != "complete":
raise RuntimeError("Input-required MCP results are outside this demo")
if any(block.type != "text" for block in result.content):
raise RuntimeError("Non-text MCP content is outside this demo")
return {"is_error": bool(result.is_error),
"content": [block.text for block in result.content],
"structured_content": result.structured_content}
async def run_agent(send, execute, question, max_rounds=4):
history = [{"role": "user", "content": question}]
seen_call_ids = set()
for _ in range(max_rounds):
response = send({"model": "gpt-6-astra", "input": list(history),
"tools": [TOOL], "reasoning": {"effort": "low"}})
if response.get("status") != "completed" or response.get("error"):
raise RuntimeError("Model response failed or did not complete")
output = response["output"]
# CometAPI's documented manual replay: keep ALL items, omit top-level ids.
history.extend({key: value for key, value in item.items() if key != "id"}
for item in output)
blocks = [block for item in output if item["type"] == "message"
for block in item.get("content", [])]
if any(block["type"] == "refusal" for block in blocks):
raise RuntimeError("Model refused the request")
calls = [item for item in output if item["type"] == "function_call"]
if not calls:
text = "\n".join(block["text"] for block in blocks
if block["type"] == "output_text")
if not text:
raise RuntimeError("No final text returned")
return text
for call in calls:
call_id = call["call_id"]
if not isinstance(call_id, str) or not call_id or call_id in seen_call_ids:
raise ValueError("Missing or duplicate function call_id")
seen_call_ids.add(call_id)
try:
arguments = json.loads(call["arguments"])
except (ValueError, TypeError):
result = {"is_error": True, "error": "Arguments must be JSON text"}
else:
result = await execute(call["name"], arguments)
print(json.dumps({"tool": call["name"], "arguments": call["arguments"],
"result": result}), file=sys.stderr)
history.append({"type": "function_call_output", "call_id": call_id,
"output": json.dumps(result)})
raise RuntimeError("Tool loop reached its round limit")
def send_live(payload):
key = os.environ["COMETAPI_KEY"] # Only live mode reads the caller's key.
request = Request("https://api.cometapi.com/v1/responses",
data=json.dumps(payload).encode(),
headers={"Authorization": f"Bearer {key}",
"Content-Type": "application/json"})
with urlopen(request, timeout=45) as response:
return json.load(response)
def server_parameters():
return StdioServerParameters(command=sys.executable,
args=[str(Path(__file__).with_name("server.py"))])
async def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--mode", choices=["direct", "mcp"], required=True)
parser.add_argument("--question", default="Use the tool to check order ORD-104.")
args = parser.parse_args()
if args.mode == "direct":
answer = await run_agent(send_live, dispatch, args.question)
else:
async with Client(stdio_client(server_parameters())) as client:
discovered = await client.list_tools()
validate_catalog(discovered.tools)
async def execute(name, arguments):
return await dispatch(name, arguments, client)
answer = await run_agent(send_live, execute, args.question)
print(answer)
if __name__ == "__main__":
asyncio.run(main())
Il loop mantiene la domanda originale e ogni voce di risposta restituita. Seguendo il formato di conversazione della Responses di CometAPI, rimuove i campi id di primo livello degli elementi di output prima del replay e aggiunge un function_call_output per ogni chiamata. Conservare il resto degli elementi restituiti preserva anche eventuali elementi di ragionamento necessari per il turno successivo.
Passaggio 4 Esegui il percorso diretto
Esegui:
python agent.py --mode direct
L’applicazione descrive get_order_status al modello. Quando il modello lo richiede, dispatch() verifica il nome e gli argomenti, quindi chiama la funzione in orders.py. Il risultato rientra nella conversazione, e il modello può trasformarlo in una risposta.
Usa il trace dello strumento stampato sul tuo terminale per verificare che la consultazione abbia usato ORD-104 e restituito status: shipped. La risposta finale dovrebbe riflettere quel risultato; il suo wording può variare. Ora hai il ciclo completo di richiamo di funzioni, dalla domanda dell’utente a un’operazione eseguita dall’applicazione e a una risposta successiva del modello.
Il prossimo passaggio mantiene in posizione il modello e il ciclo di conversazione. Sposta l’esecuzione dietro a un server MCP e consente all’applicazione di scoprire e invocare lo strumento tramite un client.
Passaggio 5 Esporre la ricerca come strumento MCP
Salva questo come server.py, accanto agli altri due file:
"""One read-only MCP tool, served over stdio. No stdout logging."""
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
from orders import get_order_status as lookup_order
server = MCPServer("fictional-orders")
@server.tool(structured_output=True)
def get_order_status(order_id: str) -> dict[str, str]:
"""Look up fictional order ORD-104; this demo has no real customer data."""
try:
return lookup_order(order_id)
except ValueError as error:
raise ToolError(str(error)) from error
if __name__ == "__main__":
server.run(transport="stdio")
Il ramo mcp in main() avvia server.py, chiama list_tools() e verifica il contratto dello strumento per gli ordini. Quando il modello richiede la consultazione, lo stesso dispatcher ora usa client.call_tool(). Il server esegue la funzione, e l’applicazione restituisce il suo risultato al modello.
Confronta le due esecuzioni rispetto agli stessi criteri: lo strumento corretto, ORD-104 come argomento, un risultato positivo contenente shipped, e una risposta finale coerente con quel risultato. Il percorso di esecuzione è cambiato; il compito e la funzione esposta al modello sono rimasti gli stessi.
Tutti e tre i file sorgente sono inclusi sopra. Il pacchetto di esempio li contiene anche e include i controlli offline per un riuso conveniente. Tali controlli esercitano un vero server MCP locale con risposte del modello scriptate; l’output del modello live è separato da quella verifica locale.
Adatta l’esempio ai tuoi strumenti
Sostituisci i dati di ordine fissi con la minima operazione di cui la tua applicazione ha realmente bisogno. Per una domanda sulla spedizione, restituisci un ID ordine e campi di stato utili. Un risultato compatto offre al modello meno materiale irrilevante da interpretare e rende la risposta più facile da verificare.
Mantieni la descrizione dello strumento e il contratto degli argomenti specifici. Questo esempio accetta esattamente una stringa, e l’host verifica il nome dello strumento richiesto prima del dispatch. La guida alla sicurezza MCP richiede che i server convalidino gli input e implementino controlli di accesso. Per un vero servizio ordini, deriva l’identità del cliente dal contesto dell’applicazione affidabile e applica l’accesso all’ordine di quel cliente all’interno del servizio. Un ID ordine fornito dal modello non può stabilire la proprietà.
Una volta che lo strumento funziona, valuta i compiti completati, il tempo end-to-end e il costo effettivo. Aggiungi MCP dove la sua interfaccia di servizio aiuta le applicazioni che hanno bisogno dello strumento, e mantieni handler diretti dove sono più semplici da gestire.
FAQ
MCP sostituisce il richiamo di funzioni?
Gestiscono interfacce diverse: MCP collega il client dell’applicazione a un server. In questo esempio, il modello richiede una funzione e l’applicazione soddisfa quella richiesta tramite un client MCP. Entrambi partecipano allo stesso ciclo dell’agente.
Uno strumento MCP può chiamare una REST API esistente?
Sì. Sostituisci la consultazione all’interno dello strumento MCP con una chiamata alla tua API. Il server MCP pubblica il contratto dello strumento e gestisce le chiamate di strumento; il tuo servizio esistente continua a fornire il suo comportamento di business.
Quando dovrei usare un server MCP remoto?
Usa un servizio remoto quando i client hanno bisogno di raggiungere un server distribuito separatamente piuttosto che avviare un processo locale. MCP definisce Streamable HTTP per quella connessione e stdio per un subprocess avviato dal client. Configura l’autorizzazione del server remoto per i suoi chiamanti previsti.
Conclusione
Per una singola applicazione, sostituisci il record fittizio in orders.py con una sola query di sola lettura dal tuo servizio e inizia con la modalità diretta.
Se ti serve un servizio MCP esistente o un’interfaccia di strumenti condivisa, usa il percorso MCP e mantieni gli stessi controlli: l’operazione giusta, il record giusto e una risposta supportata dai dati restituiti.