Start here: the basics
MCP vs function calling: what's the difference?
Function calling is how a model asks for a tool, and MCP is one way an app supplies that tool and runs it.
Function calling is a feature of a model's API: you send a list of tool definitions with your prompt, and the model can respond by asking for one to be called. MCP, the Model Context Protocol, is a separate thing: a protocol a client uses to ask a server what tools it has and to run them. An app that calls an MCP server still uses the model's function calling to let the model pick a tool. MCP supplies the tools, and function calling is how the model asks for one.
What function calling is
OpenAI's docs describe the core idea plainly: "A function or tool refers in the abstract to a piece of functionality that we tell the model it has access to. As a model generates a response to a prompt, it may decide that it needs data or functionality provided by a tool to follow the prompt's instructions." You send the model a list of tools in your API request. If it decides one fits, it doesn't run anything itself. It returns a structured call naming the tool and its arguments, and your code executes it.
A tool definition in the OpenAI Responses API looks like this, taken straight from their docs:
{
"type": "function",
"name": "get_weather",
"description": "Retrieves current weather for the given location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country e.g. Bogotá, Colombia"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Units the temperature will be returned in."
}
},
"required": ["location", "units"],
"additionalProperties": false
},
"strict": true
}Anthropic calls the same thing tool use, and draws the same line between deciding and running: "Claude determines when to call a tool based on the user's request and the tool's description. It then returns a structured call that your application executes (client tools) or that Anthropic executes (server tools)." For a tool you write yourself, Anthropic's docs list three required fields on the definition: name, description, and input_schema, "a JSON Schema object defining the expected parameters for the tool." The same get_weather tool in that shape:
{
"name": "get_weather",
"description": "Retrieves current weather for the given location.",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country e.g. Bogotá, Colombia"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location", "units"]
}
}Same idea, different field name for the schema (parameters vs input_schema), different wire format for the response (OpenAI returns a function_call output item with a call_id; Anthropic returns a tool_use content block). Every model vendor with this feature has its own shape for the request and the response. None of them tells you where the tool definitions come from, or runs the tool for you. That part is left to your application.
What MCP adds
MCP standardizes the part function calling leaves out: where tool definitions come from, and how a call actually runs. An MCP client sends a request called tools/list to a server, and gets back a list of tools, each with a name, a description, and an inputSchema. The current spec, revision 2026-07-28, defines that shape: "A tool definition includes: name: Unique identifier for the tool... description: Human-readable description of functionality... inputSchema: JSON Schema defining expected parameters." To run one, the client sends tools/call with the tool's name and arguments, and the server executes it and returns the result.
An MCP server doesn't call a model and doesn't decide anything. It answers "what tools do you have" and "run this one," over stdio or HTTP, the same way for Claude, ChatGPT, Cursor or any other client that speaks the protocol.
To see the real shape, here's a server with one tool, written with the official Python SDK:
from mcp.server import MCPServer
mcp = MCPServer("weather-demo")
@mcp.tool()
def get_weather(city: str, units: str = "celsius") -> str:
"""Get the current weather for a city.
Args:
city: The city name, for example "Paris".
units: Temperature units, either "celsius" or "fahrenheit".
"""
return f"Weather for {city} in {units}: sunny, 21 degrees"
if __name__ == "__main__":
mcp.run()Running uv run server.py and sending it a real tools/list request over stdio, with the official SDK's Client in its default mode, returns this. That client probes for the newer protocol first and negotiated revision 2026-07-28. A client that sends the older initialize handshake, as in MCP stdio vs Streamable HTTP, gets 2025-11-25 from a server built with the same SDK.
[
{
"name": "get_weather",
"description": "Get the current weather for a city.\n\nArgs:\n city: The city name, for example \"Paris\".\n units: Temperature units, either \"celsius\" or \"fahrenheit\".\n",
"inputSchema": {
"type": "object",
"properties": {
"city": {
"title": "City",
"type": "string"
},
"units": {
"default": "celsius",
"title": "Units",
"type": "string"
}
},
"required": ["city"],
"title": "get_weatherArguments"
},
"outputSchema": {
"properties": {
"result": {
"title": "Result",
"type": "string"
}
},
"required": ["result"],
"title": "get_weatherOutput",
"type": "object"
}
}
]The SDK built name, description and inputSchema straight from the function signature and docstring. Nothing in that output mentions OpenAI or Anthropic. MCP doesn't know which model API will eventually use this tool.
The same tool, three shapes
Line up the three definitions and the pattern is clear: a name, a description for the model to read, and a JSON Schema for the arguments, with the field names and envelope changed to match each vendor's wire format.
| OpenAI function | Anthropic tool | MCP tool | |
|---|---|---|---|
| Where it's defined | tools array in the API request | tools array in the API request | Returned by the server's tools/list |
| Schema field | parameters | input_schema | inputSchema |
| Model's response when it wants to call it | function_call output item with call_id | tool_use content block | Nothing. MCP has no model. The client decides based on what the model's API returned |
| Who runs the call | Your application code | Your application code (client tools), or Anthropic's infrastructure (server tools) | The MCP server, over tools/call |
Neither OpenAI's nor Anthropic's protocol runs anything. The model asks, and your application decides what to do with the request. MCP's tools/call is one way to answer: forward the request to a server instead of writing a handler yourself.
How they fit together
A client that uses MCP sits between the model API and the servers. On startup it calls tools/list on every server it's connected to, and ends up with a list of MCP tool definitions. Before it calls the model, it converts each one into whatever shape that model's API wants: inputSchema becomes parameters for an OpenAI request, or input_schema for an Anthropic one. The names and descriptions pass through unchanged, since both sides mean the same thing by them.
The model sees an ordinary function-calling request and responds the ordinary way, with a function_call or a tool_use block naming a tool and its arguments. The client reads that response, matches the name back to the MCP server that offers it, and sends tools/call. The result goes back into the conversation as a tool result, in whatever format that model's API expects, and the loop continues.
Anthropic's own docs describe exactly this role for its API: "To connect to Model Context Protocol (MCP) servers, see the MCP connector." The MCP connector runs on Anthropic's side of the API. You pass a remote server's URL in the Messages API request, and Anthropic connects to it and does the conversion above, "without a separate MCP client." It supports tool calls only, and the server has to be reachable over HTTP, so a local stdio server still needs a client of your own. Either way, Claude's tool use loop decides to call a tool, and MCP is one place that tool can come from.
This is also why a server written for MCP works with any model API that supports function calling, and why an MCP client has to translate between the two. The specs were built independently but agree on the shape of a tool definition: a name, a description, and a schema for the arguments. So the translation is mostly a field rename.
Where Statio fits
A gateway sits in the same place as a client's built-in MCP support, between the agent and the servers it's allowed to call. Statio grants access per tool rather than per server, so an agent calling get_weather can be denied delete_city on the same server, and a deny on one of those always overrides a grant. Each agent gets its own identity, credentials are injected at call time and the agent never holds them, and every call is scanned and logged. None of that changes what a tool definition looks like. It changes which calls the gateway lets through for a given agent.
Next
- What is an MCP server? covers the client, server and host roles this article assumes.
- MCP vs APIs covers how a server relates to the REST API underneath it.
- How to build an MCP server in Python builds a server like the one above, with more tools and a real client.
- MCP permissions covers deciding which tools an agent gets, once you have more than one.
Questions
parameters, input_schema and inputSchema are all a JSON Schema object describing a tool's arguments, so a client that supports more than one only has to rename the field.