Mit CometAPI entwickelnEine API für mehrere Modelle erforderlich?Teste den Workflow im Playground und erstelle anschließend einen API-Schlüssel, sobald Modell und Kosten zu deinem Anwendungsfall passen.
MCP vs. Function Calling: So verbinden Sie Tools mit KI-Agenten
Vergleichen Sie MCP mit Function Calling und erstellen Sie mit der CometAPI eine vollständige Tool-Schleife. Folgen Sie dem Python-Code, um die gleiche Bestellabfrage sowohl direkt als auch über MCP auszuführen.
Function Calling gibt einem Modell eine Möglichkeit, ein Tool anzufordern. MCP gibt einer Anwendung eine standardisierte Möglichkeit, Tools auf einem Server zu entdecken und aufzurufen. Verwenden Sie eine direkte Funktionsintegration für einen kleinen, von Ihrer Anwendung verwalteten Werkzeugsatz. Fügen Sie MCP hinzu, wenn Sie einen bestehenden kompatiblen Dienst konsumieren oder Tools über eine gemeinsame Schnittstelle bereitstellen möchten. Im Folgenden verbinden Sie dieselbe Bestellabfrage auf beide Arten über CometAPI, und zwar in einer vollständigen Agenten-Schleife.
Key Takeaways
Beginnen Sie mit direktem Dispatch, wenn wenige Tools einer einzelnen Anwendung dienen; verwenden Sie MCP, wenn ein kompatibler Tool-Dienst oder eine gemeinsame Tool-Schnittstelle die Integration leichter wartbar macht.
Beide Wege benötigen einen vollständigen Loop: Tool beschreiben, Aufruf empfangen, ausführen, Ergebnis zurückgeben und das Modell antworten lassen.
Sie können dieselbe modellseitige Funktion beibehalten, während Sie ihre Ausführung von einem direkten Handler auf einen MCP-Client umstellen.
Weiterlernen
Diesen Artikel mit der nächsten Entscheidung verknüpfen.
Auf Klarheit, Quellenangabe und aktuelle API-Terminologie geprüft.
Halten Sie Tool-Verträge schlank und prüfen Sie Ergebnisse gegen die Aufgabe, die der Agent erfüllen soll.
How Function Calling and MCP Fit Together
Ein Support-Agent kann eine Versandverzögerung erklären, aber er benötigt Zugriff auf das Bestellsystem, um einem Kunden zu sagen, wo sich sein Paket befindet. Function Calling gibt dem Modell eine Möglichkeit, diese Abfrage anzufordern. Ihre Anwendung führt die Abfrage aus und gibt das Ergebnis zurück. Der Handler kann eine lokale Bibliothek, eine Datenbank oder eine Remote-API verwenden.
Um diese Anforderung über MCP zu erfüllen, verwendet die Anwendung einen Client, um Server-Tools mit tools/list zu entdecken und sie mit tools/call aufzurufen. Die Anwendung entscheidet weiterhin, welche der entdeckten Tools sie ihrem Modell zur Verfügung stellt.
Ermöglicht dem Modell, eine benannte Operation mit strukturierten Argumenten anzufordern
Standardisiert, wie kompatible Anwendungen Server-Tools entdecken und aufrufen
Tool-Definitionen
Definitionen im Format der Modell-API
Vom Server bereitgestellte Definitionen, einschließlich eines inputSchema
Ausführungsort
Der Handler, an den die Anwendung dispatcht; kann einen lokalen oder entfernten Dienst aufrufen
Die Tool-Implementierung des MCP-Servers; kann einen Backend-Dienst aufrufen
Getrennter Server nötig?
Für die Function-Calling-Schnittstelle selbst ist kein Server erforderlich
Ein MCP-Server ist erforderlich, aber ein Client kann ihn als lokalen stdio-Subprozess starten; ein separat bereitgestellter Remote-Dienst ist optional
Typischer Einsatz
Wenige, anwendungsseitig verwaltete Tools mit direktem Dispatch
Bestehende kompatible Dienste oder eine über Anwendungen hinweg wiederverwendete Tool-Schnittstelle
Können sie zusammenarbeiten?
Die Anwendung kann die Funktionsanforderung des Modells über MCP routen
Der MCP-Client erfüllt diese Anforderung mit tools/call und gibt das Ergebnis an die Anwendung zurück
Die letzte Zeile ist der Schlüssel, um mit beiden zu bauen. Ein Modell kann get_order_status anfordern, während die Anwendung entscheidet, ob sie ihren eigenen Handler oder einen MCP-Server aufruft. Die modellseitige Funktionsdefinition kann gleich bleiben.
Choose the integration your application needs
Wir empfehlen, mit der direkten Integration zu beginnen, wenn Sie nur wenige Tools besitzen, die von einer Anwendung genutzt werden. Es gibt weniger zu deployen, und der Weg von der Modellanforderung zu Ihrem Business-Code ist leicht nachvollziehbar.
MCP wird nützlich, wenn ein kompatibler Server die Fähigkeit bereits bereitstellt oder wenn mehrere KI-Anwendungen eine gemeinsame Tool-Schnittstelle benötigen. Dasselbe Bestell-Tool könnte sowohl einem Support-Assistenten als auch einer internen Operations-Anwendung dienen, wobei jeder Host steuert, wie seine Nutzer darauf zugreifen.
Ein Dispatcher kann sowohl interne Funktionen als auch MCP-Dienste unterstützen und jede Operation an ihre Implementierung routen. Bibliotheken und REST-APIs können ebenfalls gemeinsam genutzt werden; wählen Sie MCP, wenn seine standardisierte Discovery- und Aufruf-Schnittstelle für die konsumierenden Anwendungen hilfreich ist.
Build a complete tool loop through CometAPI
Wir verwenden eine schreibgeschützte Operation, get_order_status, und eine fiktive Bestellung, ORD-104. Das Beispiel ruft die Operation zunächst direkt auf und führt sie dann über einen lokalen MCP-Server aus. Beide Modi nutzen die CometAPI Responses API für die Modellinferenz.
Abbildung 2. Ein Agenten-Loop kann direkte und MCP-Ausführung unterstützen. Originale Illustration, basierend auf der MCP-Architektur und dem CometAPI-Function-Calling-Flow.
Step 1 Set up the project
Verwenden Sie Python 3.11 oder höher und einen CometAPI-API-Schlüssel. Diese Anleitung wurde mit Python 3.12.14 und mcp==2.3.0 getestet. Folgen Sie dem CometAPI Quickstart, um einen Schlüssel zu erstellen. Erstellen Sie unter macOS oder Linux im Terminal ein Projekt und aktivieren Sie eine virtuelle Umgebung:
Ersetzen Sie den Platzhalter durch Ihren Schlüssel und halten Sie ihn aus öffentlichem Quellcode heraus. Diese Anleitung pinnt das in dem Beispiel verwendete MCP-SDK; der offizielle Python-Server-Guide richtet sich an die 2.x-API. Die beiden Agenten-Befehle unten senden Anfragen an CometAPI und verbrauchen API-Guthaben.
Step 2 Define the order lookup
Speichern Sie Folgendes als orders.py. Feste Daten halten die Anleitung in sich geschlossen; später können Sie die Abfrage durch Ihren eigenen Dienst ersetzen.
"""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"}
Der Aufruf von get_order_status("ORD-104") gibt dieses Fixture zurück:
Speichern Sie das folgende vollständige Skript als agent.py. Es registriert die Funktion, sendet die Frage an CometAPI, führt angeforderte Aufrufe aus und gibt jedes Ergebnis mit der ursprünglichen call_id zurück.
Die modellseitige Definition verwendet das Feld parameters der CometAPI Responses API. MCP liefert ein inputSchema; dieses Beispiel prüft den entdeckten Vertrag gegen das Ein-String-order_id-Schema, bevor die geprüfte Funktion exponiert wird.
"""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())
Die Schleife behält die ursprüngliche Frage und jedes zurückgegebene Antwortelement bei. Entsprechend dem CometAPI Responses-Konversationsformat entfernt sie vor dem Replay die obersten id-Felder der Ausgabeelemente und hängt für jeden Aufruf ein function_call_output an. Die Beibehaltung der übrigen zurückgegebenen Elemente bewahrt auch alle Reasoning-Items, die für die nächste Runde benötigt werden.
Step 4 Run the direct path
Führen Sie aus:
python agent.py --mode direct
Die Anwendung beschreibt get_order_status gegenüber dem Modell. Wenn das Modell es anfordert, prüft dispatch() Namen und Argumente und ruft dann die Funktion in orders.py auf. Das Ergebnis fließt zurück in die Konversation, und das Modell kann daraus eine Antwort formulieren.
Verwenden Sie die in Ihrem Terminal ausgegebene Tool-Trace, um zu prüfen, dass die Abfrage ORD-104 verwendet und status: shipped zurückgegeben hat. Die finale Antwort sollte dieses Ergebnis widerspiegeln; die Formulierung kann variieren. Sie haben nun den vollständigen Function-Calling-Loop – von der Nutzerfrage über eine von der Anwendung ausgeführte Operation bis zur anschließenden Modellantwort.
Der nächste Schritt behält Modell und Konversations-Loop bei. Er verschiebt die Ausführung hinter einen MCP-Server und lässt die Anwendung das Tool über einen Client entdecken und aufrufen.
Step 5 Expose the lookup as an MCP tool
Speichern Sie dies als server.py neben den beiden anderen Dateien:
"""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")
Der mcp-Zweig in main() startet server.py, ruft list_tools() auf und prüft den Vertrag des Bestell-Tools. Wenn das Modell die Abfrage anfordert, verwendet derselbe Dispatcher nun client.call_tool(). Der Server führt die Funktion aus, und die Anwendung gibt ihr Ergebnis an das Modell zurück.
Vergleichen Sie beide Läufe anhand derselben Kriterien: das richtige Tool, ORD-104 als Argument, ein erfolgreiches Ergebnis mit shipped und eine finale Antwort, die mit diesem Ergebnis konsistent ist. Der Ausführungspfad hat sich geändert; Aufgabe und modellseitige Funktion sind unverändert geblieben.
Alle drei Quelldateien sind oben enthalten. Das Beispielpaket enthält sie ebenfalls sowie Offline-Checks zur bequemen Wiederverwendung. Diese Checks testen einen echten lokalen MCP-Server mit skriptgesteuerten Modellantworten; Live-Modellausgaben sind getrennt von dieser lokalen Verifikation.
Adapt the example to your own tools
Ersetzen Sie die festen Bestelldaten durch die kleinste Operation, die Ihre Anwendung tatsächlich benötigt. Für eine Versandfrage geben Sie eine Bestell-ID und nützliche Statusfelder zurück. Ein kompaktes Ergebnis liefert dem Modell weniger irrelevantes Material zur Interpretation und macht die Antwort leichter überprüfbar.
Halten Sie die Beschreibung und das Argumenten-Schema des Tools spezifisch. Dieses Beispiel akzeptiert genau einen String, und der Host prüft den angeforderten Tool-Namen vor dem Dispatch. Die MCP-Sicherheitsempfehlungen verlangen, dass Server Eingaben validieren und Zugriffskontrollen implementieren. Für einen realen Bestellservice leiten Sie die Kundenidentität aus vertrauenswürdigem Anwendungskontext ab und erzwingen Sie den Zugriff auf die Bestellung dieses Kunden innerhalb des Dienstes. Eine vom Modell gelieferte Bestell-ID kann keine Eigentümerschaft belegen.
Sobald das Tool funktioniert, evaluieren Sie erledigte Aufgaben, End-to-End-Zeit und tatsächliche Kosten. Fügen Sie MCP dort hinzu, wo seine Service-Schnittstelle den Anwendungen hilft, die das Tool benötigen, und behalten Sie direkte Handler dort bei, wo sie einfacher zu betreiben sind.
FAQ
Ersetzt MCP Function Calling?
Sie bedienen unterschiedliche Schnittstellen: MCP verbindet den Client der Anwendung mit einem Server. In diesem Beispiel fordert das Modell eine Funktion an, und die Anwendung erfüllt diese Anforderung über einen MCP-Client. Beide sind Teil desselben Agenten-Loops.
Kann ein MCP-Tool eine bestehende REST-API aufrufen?
Ja. Ersetzen Sie die Abfrage im MCP-Tool durch einen Aufruf Ihrer API. Der MCP-Server veröffentlicht den Tool-Vertrag und verarbeitet Tool-Aufrufe; Ihr bestehender Dienst liefert weiterhin sein fachliches Verhalten.
Wann sollte ich einen entfernten MCP-Server verwenden?
Verwenden Sie einen entfernten Dienst, wenn Clients einen separat bereitgestellten Server erreichen müssen, statt einen lokalen Prozess zu starten. MCP definiert dafür Streamable HTTP und für einen vom Client gestarteten Subprozess stdio. Konfigurieren Sie die Autorisierung des entfernten Servers für seine vorgesehenen Aufrufer.
Conclusion
Für eine einzelne Anwendung ersetzen Sie den fiktiven Datensatz in orders.py durch eine schreibgeschützte Abfrage Ihres Dienstes und beginnen Sie mit dem Direktmodus.
Wenn Sie einen bestehenden MCP-Dienst oder eine gemeinsame Tool-Schnittstelle benötigen, verwenden Sie den MCP-Pfad und behalten Sie dieselben Prüfungen bei: die richtige Operation, der richtige Datensatz und eine Antwort, die durch die zurückgegebenen Daten gestützt ist.