Companion to my LinkedIn post on MCP vs API: https://www.linkedin.com/posts/activity-7478506787869097984-2_Y7
Three small scripts that reach the same function — a company-policy lookup — three ways:
the_api_way.py— your code knows the function and calls it directly.client.py— a client connects to a server, lists the tools it offers, reads each tool's schema, and calls one. No language model; just the plumbing.agent.py— a language model is given the discovered tools and decides which one to call, and with what arguments, from a plain-English question.
One tool, no agent framework, so the only thing that changes between them is how the call is found and who decides to make it.
- Python 3.10+
mcp>=1.2,openai>=1.0(pip install -r requirements.txt)- An OpenAI key for
agent.pyonly:export OPENAI_API_KEY=sk-...(the_api_way.pyandclient.pyneed no key.)
pip install -r requirements.txt
# 1) The API way — the call is written into the code
python the_api_way.py
# 2) The MCP way — the client lists tools, then calls the one it found
python client.py
# 3) The MCP way with a model deciding — needs OPENAI_API_KEY
python agent.py "how much notice do I need to cancel?"the_api_way.py — a call you can read in the source:
The API way (hardcoded call):
lookup_policy('cancellation') -> Cancellation requires at least 14 days notice before the next billing date.
client.py — the tools it discovered, then a call it made:
Discovered tools (the client hardcoded none of these):
• lookup_policy — Look up the company policy for a topic. Valid topics: cancellation, refunds, downgrades.
input schema: {'topic': {'title': 'Topic', 'type': 'string'}}
Calling discovered tool 'lookup_policy' with topic='cancellation'...
Result: Cancellation requires at least 14 days notice before the next billing date.
agent.py — the model reads the question, picks the tool and the argument itself, then answers:
User asks: how much notice do I need to cancel?
1) Tools discovered from the MCP server (none hardcoded here):
- lookup_policy: Look up the company policy for a topic. Valid topics: cancellation, refunds, downgrades.
2) The model chose to call: lookup_policy({'topic': 'cancellation'})
3) Ran it over MCP -> Cancellation requires at least 14 days notice before the next billing date.
4) Final answer:
You need to provide at least 14 days' notice before your next billing date to cancel.
Note step 2: nothing in agent.py says to call lookup_policy, and nothing sets topic='cancellation'. The model worked both out from the tool's description and the question.
The whole flow, trimmed to the parts that matter:
# 1) Ask the MCP server what tools exist — nothing about them is hardcoded here.
discovered = await session.list_tools()
# 2) Hand those tool descriptions to the model. We do not say which to use.
tools = [
{
"type": "function",
"function": {
"name": t.name,
"description": t.description, # what the tool does
"parameters": t.inputSchema, # how to call it (JSON schema)
},
}
for t in discovered.tools
]
reply = llm.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "how much notice do I need to cancel?"}],
tools=tools,
tool_choice="auto", # the model decides whether, and what, to call
)
# 3) The model returned a tool name and arguments. Run that call over MCP.
call = reply.choices[0].message.tool_calls[0]
result = await session.call_tool(
call.function.name,
json.loads(call.function.arguments),
)
# 4) Feed `result` back to the model for a final, plain-English answer.Using Claude instead of OpenAI? Only this "ask the model" call changes — the MCP half (discover, then call) is identical. Anthropic's tool-use API takes the same tool list in a slightly different shape.
server.py— an MCP server that exposes one tool,lookup_policy, using the official MCP SDK.the_api_way.py— the same lookup as a direct, hardcoded call, for contrast.client.py— connects, lists tools, reads the schema, and calls the tool it found.agent.py— gives the discovered tools to a model and lets it choose the call.requirements.txt—mcpandopenai.
All of these reach the same data. What changes is who decides what to call.
With the API way, you write the call. Add another function tomorrow and you write more code to use it — in every app that needs it.
With MCP, the client asks the server what tools exist and how to call them, and a model can pick the right one on its own. Add a tool on the server and it can be used with no change on this side.