Large Language ModelsGenerate imagesGenerate videos

MCP vs Function Calling: Differences With OpenAI and Claude Examples

MCP and function calling solve different problems. Function calling lets a model request an action, while MCP gives apps a shared protocol for finding tools. This article compares both with OpenAI and Claude code samples, a security checklist, and a simple rule for choosing.

MCP vs Function Calling: Differences With OpenAI and Claude Examples
Cristian Da Conceicao
Founder of Picasso IA

Pick the wrong layer and you will rebuild the same integration twice. MCP vs function calling looks like a fork in the road, but the two sit at different levels of the same stack. Function calling is how a model asks for an action. The Model Context Protocol (MCP) is how an application finds and reaches the tools that action runs on.

The short answer, before any code: function calling is a model feature, MCP is a wire protocol, and most MCP setups use function calling under the hood. With two or three tools inside one app, plain function calling is simpler. When several apps need the same tools, MCP pays for itself fast. The sections below show OpenAI and Claude examples for both approaches, side by side.

Hand about to plug a USB-C cable into a laptop port beside an aluminum hub

What Function Calling Actually Does

A language model only produces text. Function calling gives that text a shape your code can trust. With every request you send a list of tool definitions: a name, a plain-language description, and a JSON Schema for the arguments. When the model decides a tool is needed, it does not run anything. It returns a structured call with the tool name and arguments, and your code does the work.

The Request and Response Loop

  1. Your app sends the user message plus the tool definitions.
  2. The model answers with a tool call instead of final text.
  3. Your code runs the function and captures the result.
  4. You send the result back as a new message.
  5. The model writes the final answer, or asks for another tool.

💡 Tip: The model never executes code. It proposes a call, and your application decides whether to run it. That gap is exactly where permission checks and logging belong.

Carpenter's hand lifting a hammer from its painted outline on a pegboard wall

Where the Schema Lives

Definitions live in your own codebase, usually next to the function they describe. OpenAI calls the schema field parameters. Claude calls it input_schema. The envelope differs a little between providers, so a tool written for one needs a thin adapter for the other.

That is the real cost of plain function calling: every app keeps its own copy of every definition, and every provider wants its own flavor. Build the same order lookup for a chat app, an IDE plugin, and a support bot, and you maintain three copies.

What MCP Adds on Top

Anthropic released MCP in November 2024 as an open protocol, and OpenAI adopted it in March 2025. MCP standardizes how tools are described, listed, and called, so a tool written once as an MCP server works in every client that speaks the protocol. It is the standard port from the photo above: one connector shape, many devices. Instead of N apps times M tools worth of custom glue, you build N clients and M servers.

Hosts, Clients, and Servers

MCP has three roles:

  • Host: the application the user sees, such as a chat app, an IDE, or your own agent.
  • Client: a connector inside the host that holds one session with one server.
  • Server: a small program that exposes tools, data, and prompts.

Messages use JSON-RPC 2.0. Local servers talk over stdio, and remote servers talk over Streamable HTTP. The client opens a session, asks the server what it offers, and calls things by name.

Operator inserting a braided cord into a vintage telephone switchboard

Tools, Resources, and Prompts

A server can expose three kinds of capability:

PrimitiveWhat it isWho controls it
ToolsActions or computations the model can callThe model
ResourcesReadable data identified by a URI, like a file or a recordThe application
PromptsReusable message templatesThe user

Only tools overlap with function calling. Resources and prompts have no standard answer in plain function calling, which is part of why people reach for MCP.

Here is the wire traffic for a single tool. The client asks the server for its tools:

{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}

The server answers with definitions that look like the ones you would write by hand, except the schema field is inputSchema in camelCase:

{"jsonrpc": "2.0", "id": 1, "result": {"tools": [{
  "name": "get_order_status",
  "description": "Look up the shipping status of an order by its ID.",
  "inputSchema": {
    "type": "object",
    "properties": {"order_id": {"type": "string"}},
    "required": ["order_id"]
  }
}]}}

Later, when the model asks for that tool, the client sends:

{"jsonrpc": "2.0", "id": 2, "method": "tools/call",
 "params": {"name": "get_order_status", "arguments": {"order_id": "8841"}}}

Hand sliding open a wooden card catalog drawer full of index cards

MCP vs Function Calling Side by Side

QuestionFunction callingMCP
What is it?A feature of the model APIAn open protocol between apps and tool servers
Where do definitions live?In your app code, sent with each requestOn the server, listed on demand with tools/list
Who runs the tool?Your application processThe MCP server, local or remote
Reuse across appsCopy and adapt per appWrite once, connect from any client
Needs the other?NoYes, the model still calls tools through function calling
Setup effortMinutesA few hours for a first server, minutes for an existing one
Best forFew tools, private app logicShared tools and third-party integrations

Overhead view of a single screwdriver pouch beside a modular bit set in a hard case

How MCP Calls Become Function Calls

The two are not rivals, because MCP feeds function calling. Here is the full path of one request in an MCP setup:

  1. The MCP client lists the tools from each connected server.
  2. It converts them into the provider's own tool format and sends them with the request.
  3. The model returns an ordinary function call.
  4. The MCP client forwards it to the right server as tools/call.
  5. The server's result goes back to the model as a normal tool result.

From the model's point of view, nothing changed. It saw tool definitions and emitted a call. What changed is who wrote the definitions and who ran the function.

💡 Rule of thumb: If you can point to the line of your own code that runs the function, you are doing plain function calling. If a separate server runs it, you are doing MCP, with function calling still happening between the model and the client.

OpenAI and Claude Examples in Code

Same tool, four ways: an order status lookup. The examples use Python, and the MCP server URL is a placeholder you swap for your own.

Developer typing at a desk in a quiet loft office at dusk

OpenAI Function Calling

This version uses the Responses API with GPT 5. You define the tool, run the function yourself, and send back a function_call_output item.

import json
from openai import OpenAI

client = OpenAI()

def get_order_status(order_id: str) -> dict:
    return {"order_id": order_id, "status": "shipped", "eta": "2026-10-09"}

tools = [{
    "type": "function",
    "name": "get_order_status",
    "description": "Look up the shipping status of an order by its ID.",
    "parameters": {
        "type": "object",
        "properties": {"order_id": {"type": "string"}},
        "required": ["order_id"],
        "additionalProperties": False,
    },
    "strict": True,
}]

input_items = [{"role": "user", "content": "Where is order 8841?"}]
response = client.responses.create(model="gpt-5", tools=tools, input=input_items)

input_items += response.output
for item in response.output:
    if item.type == "function_call":
        result = get_order_status(**json.loads(item.arguments))
        input_items.append({
            "type": "function_call_output",
            "call_id": item.call_id,
            "output": json.dumps(result),
        })

final = client.responses.create(model="gpt-5", tools=tools, input=input_items)
print(final.output_text)

OpenAI Remote MCP

Same question, no schema and no loop in your code. The API connects to the server, lists its tools, and calls them for you.

response = client.responses.create(
    model="gpt-5",
    tools=[{
        "type": "mcp",
        "server_label": "orders",
        "server_url": "https://mcp.example.com/mcp",
        "require_approval": "always",
    }],
    input="Where is order 8841?",
)
print(response.output_text)

With require_approval set to "always", the response contains an approval request for each call, and your code decides whether to allow it. Use "never" only for servers you fully trust.

Claude Tool Use

The Messages API follows the same loop with different field names. The schema goes in input_schema, the model signals a call with stop_reason == "tool_use", and you answer with a tool_result block.

import json
import anthropic

client = anthropic.Anthropic()

tools = [{
    "name": "get_order_status",
    "description": "Look up the shipping status of an order by its ID.",
    "input_schema": {
        "type": "object",
        "properties": {"order_id": {"type": "string"}},
        "required": ["order_id"],
    },
}]

messages = [{"role": "user", "content": "Where is order 8841?"}]
response = client.messages.create(
    model="claude-sonnet-5-5", max_tokens=1024, tools=tools, messages=messages
)

if response.stop_reason == "tool_use":
    messages.append({"role": "assistant", "content": response.content})
    results = []
    for block in response.content:
        if block.type == "tool_use":
            output = get_order_status(**block.input)
            results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": json.dumps(output),
            })
    messages.append({"role": "user", "content": results})
    response = client.messages.create(
        model="claude-sonnet-5-5", max_tokens=1024, tools=tools, messages=messages
    )

print(response.content[-1].text)

Send every tool_result from one turn in a single user message. Splitting them across messages teaches the model to stop calling tools in parallel.

Two engineers reviewing a printed architecture diagram around an oak table

Claude MCP Connector

The MCP connector needs two halves: the server in mcp_servers, and a matching mcp_toolset entry in tools. It also sits behind a beta header, and it reaches remote servers over HTTP, not local stdio ones.

response = client.beta.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    betas=["mcp-client-2025-11-20"],
    mcp_servers=[{
        "type": "url",
        "url": "https://mcp.example.com/mcp",
        "name": "orders",
    }],
    tools=[{"type": "mcp_toolset", "mcp_server_name": "orders"}],
    messages=[{"role": "user", "content": "Where is order 8841?"}],
)

Compare the four. The MCP versions are shorter because the schema and the loop moved out of your code. The trade is control: you hand the loop to the API and you trust whatever the server says about itself.

Security and Cost Traps to Watch

Untrusted Tool Descriptions

Tool names, descriptions, and results all flow into the model's context. A careless or hostile server can hide instructions in them, which is a prompt injection route. Plain function calling has the same risk with tool results, but MCP widens it because third parties write the descriptions.

  • Connect only servers you trust, and pin versions where you can.
  • Keep human approval on any action that writes, sends, or deletes.
  • Give each server the narrowest credentials that still work.
  • Log every call with its arguments.

Heavy padlock and chain on a locked equipment cabinet in a network room

Token Cost of Long Tool Lists

Every definition is sent as input tokens on every request. Forty tools with long descriptions can add thousands of tokens before the user says a word. MCP makes that easy to do by accident, because attaching a server pulls in all of its tools at once.

Three fixes work well:

  1. Attach only the servers a given task needs.
  2. Shorten descriptions to what the model must know to choose the tool.
  3. Load definitions on demand, for example with Claude's tool search tool, and keep the stable part of the list cacheable.

💡 Common mistakes: attaching every server to every request, auto-approving write actions, and skipping call logs. Each one is cheap to avoid on day one and expensive to fix after an incident.

When to Pick Each One

Pick Function Calling When

  • You have a handful of tools used by one app.
  • The tools need in-process access to your own state, such as a database session or the logged-in user.
  • You want full control of latency, retries, and approval screens.
  • You are prototyping and want the fewest moving parts.

Pick MCP When

  • Several clients need the same tools: a chat app, an IDE, and an agent.
  • A third party already ships an MCP server for the service you need.
  • Different teams own the tools and the apps, and they should not block each other.
  • You want to switch model providers without rewriting tool glue.

Hiker at a fork in a forest trail at sunrise

Most production stacks end up mixed. Private logic stays as in-process function tools, and shared or third-party capabilities arrive through MCP servers. Both reach the model through the same function-calling interface, so mixing them costs almost nothing.

A Real MCP Server: PicassoIA

PicassoIA exposes its image and video generation through an MCP connector, which makes it a handy example of the design choices above. Generation takes time, so the tools are split into starters and a status check. This is the same shape you would build by hand with two function definitions.

What the Connector Exposes

ToolWhat it does
generate_imageStarts an image job and returns a prediction ID
edit_imageStarts an edit of an existing image
generate_video_picassoiaStarts a video job with the PicassoIA video model
generate_video_seedanceStarts a video job with Seedance
get_generationPolls a prediction until it succeeds or fails
list_generationsLists past generations
cancel_generationCancels a job that is still queued or running
list_modelsShows which models your account can use
get_accountShows your plan and parallel limits

The pattern is worth copying in your own servers: return an ID fast, poll with a separate tool, and give the model a cancel tool so it can back out of a bad run. The developer docs list 5 concurrent predictions per account, shared across API credentials and MCP connections, so a well-behaved client polls instead of firing everything at once. The same jobs are also available through a Replicate-style REST API at https://api.picassoia.com/v1. Check the pricing page for which plans include API and MCP access.

How to Use Claude Sonnet 5 on PicassoIA

Before wiring up code, you can draft tool schemas and test prompts on the Claude Sonnet 5 page. The model page exposes these inputs:

  1. Prompt: type the request, for example: "Write a JSON Schema for a tool named get_order_status that takes an order ID and returns shipping status."
  2. System prompt (optional): set a role once, such as "You are an API designer. Reply with JSON only."
  3. Effort: the default is low, which turns thinking off for the fastest, cheapest replies. Move to high or max for a tricky multi-file bug or a design with many tools. The levels are low, medium, high, xhigh, and max.
  4. Max tokens: the default is 8192, enough for a full schema plus explanation.
  5. Image (optional): attach a screenshot of an error or a wireframe as extra context.
  6. Run and compare: send the same prompt to GPT 5 and see which schema is cleaner.

💡 Tip: Ask for the schema first, then ask the model to critique its own description. Short, specific descriptions make the tool easier to choose correctly.

Create Your Own Images With Picasso IA

Every photo in this article started as a text prompt: a pegboard, a switchboard, a card catalog, a trail fork. Each one turns an abstract idea into something you can see, and the same recipe works for your own posts, docs, and slide decks.

Use this structure: subject and action, environment, lighting, camera and lens, texture details. For example: "Close-up of a hand plugging a cable into a laptop on an oak desk, soft window light from the right, 100mm macro lens, shallow depth of field, brushed aluminum texture."

Open Picasso IA, pick a text-to-image model from the full model list, paste a prompt in that shape, and run it. Change one detail at a time, such as the lens or the light direction, and watch how the result shifts. If you already work in Claude, connect the PicassoIA MCP connector from your account and ask for images straight from the chat. Try three variations of one idea today and keep the best.

Share this article