MCP, API, and function calling are constantly compared, as if one must be chosen. In reality, these are three different layers that work together. I will demonstrate the difference not with abstract diagrams, but with a single real-world task – searching my blog articles, which exist in several forms: a search page for people, a tool for an internal AI agent, and an OpenCode MCP server. This example clearly shows what each layer does, how to correctly and incorrectly transform an endpoint into a tool for a model, and when to choose which.
⚡ In Brief
✅ These are not three alternatives, but three layers: API – how a service provides data, function calling – how a model requests a function call, MCP – how tools are delivered to an AI application.
✅ The model does nothing on its own. It only suggests a call; the application executes it – both with function calling and with MCP.
✅ The main mistake is wrapping an endpoint 1:1. Models need a task (“find an article”), not all the technical parameters of an API.
✅ The choice is simple: no LLM among consumers – API; agent only within your application – function calling; your tools must be accessible to external AI clients – MCP.
🎯 You will get: a clear distinction between the three concepts, an example of correct wrapping, and a table on "what to choose in your situation."
👇 Below is a breakdown with real code, comparison tables, and five common myths.
Short Answer: API, function calling, and MCP answer three different questions.
API – how a service provides data and accepts commands. A contract between programs: endpoint, parameters, response format. It knows nothing about AI.
Function calling – how a model requests a function call. The model's own mechanism: it is given a list of functions with descriptions, and instead of text, it can return "call function X with these arguments."
MCP – how tools get into an AI application. A delivery protocol: a server describes its tools, and any compatible client (Claude, ChatGPT, Cursor, your agent) connects them without separate integration.
In a real call, all three layers work together, one above the other:
Model ── function calling: "call search_articles("ollama 8GB")"
↓
AI Application ── receives the suggestion, decides whether to execute
(Claude, agent)
↓
MCP ── transmits the call to the MCP server (tools/call)
↓
MCP Server ── executes the search
↓
API / Database ── where the server gets data from
💡 Why are they compared at all: all three relate to how something is "called." But comparing them as alternatives is like comparing HTTP, REST, and a browser: they are different layers of the same system. A more useful question is not "which is better," but "which layer do I need to build."
⚙️ Section 2. Function Calling in a Minute
I discussed the mechanics of function calling, JSON schema, and its connection to RAG in detail in the article Tool Use vs Function Calling. Here, only what's necessary for comparison.
The application passes a list of functions to the model along with the question: name, description, and JSON schema of parameters. The model either responds with text or returns a structured suggestion: "call search_articles with the argument query = "ollama 8GB"." The application executes the function, returns the result to the model – and it forms the answer.
⚠️ Key point that is often missed: the model executes nothing. It only suggests a call. The application always executes it – and it is the application that decides whether it's worth doing: it checks permissions, asks for user confirmation. This is the security boundary, and it's the same for both function calling and MCP.
How the model decides when to call a function and when to answer on its own is explained in the article How a Model Decides When to Search.
📊 Section 3. Comparison Table
API
Function Calling
MCP
What it is
contract between programs
model's mechanism
tool delivery protocol
Who is the consumer
another service, frontend, mobile app
model within your application
any MCP client: Claude, ChatGPT, Cursor, agents
How the consumer knows what's available
documentation, OpenAPI
you pass the list of functions in the request to the model
the server itself provides the list (tools/list)
Who the description is for
for the developer
for the model
for the model
Who executes the call
consumer's code
your application
MCP server on client command
Transport
HTTP, gRPC, etc.
none – it's part of the model request
stdio (locally) or Streamable HTTP
Where the code lives
in the service
in your application, next to the model
in a separate server accessible externally
Who can use it
anyone with access
only your application
any AI client you connect
Section Conclusion: Function calling and MCP are similar in that the description is written for the model. The difference is in who can use the tool: only your application or any AI client.
🔍 Chapter 4. One Search — Three Ways
On webscraft.org, article search exists in several forms — and this is the best illustration of the difference.
Way 1. API: Search for Programs and People
The site has a search page for people. If another service wanted to search my articles from its backend, it would need a JSON endpoint like this (I don't have a public JSON API for search — this is an illustration of what it would look like):
GET /api/articles/search?q=ollama+8gb&limit=3
[
{"title": "Which Ollama model to choose in 2026...", "url": "https://webscraft.org/blog/...", "score": 0.73},
...
]
A developer reads the documentation, writes the call code, and processes the response. AI has nothing to do with it.
Way 2. Function Calling: A Tool for My Own Agent
I have an SEO agent that analyzes Search Console data. To prevent it from suggesting writing an article that already exists, it has a tool to search for similar articles. In Spring AI, this is a regular method with an annotation (simplified):
@Tool(description = "Find existing webscraft.org articles similar to a phrase. "
+ "Use before suggesting a new article to check if the topic is already covered.")
public List<SimilarArticle> findSimilarArticles(String phrase, String lang) {
return similarityService.findSimilar(phrase, lang, 5);
}
// in the agent's service:
chatClient.prompt(question)
.tools(gscTools, gaTools, demandTools) // methods' descriptions are passed to the models
.call();
The model sees the description and can suggest a call, Spring AI executes the method and returns the result to the model. But this tool exists only within my application. Neither Claude nor Cursor know about it and cannot use it.
Way 3. MCP: The Same Search for Any AI Client
To allow Claude, ChatGPT, or Cursor to use the search, I created an MCP server with the search_articles tool:
@McpTool(
name = "search_articles",
description = "Semantic search over webscraft.org — a Ukrainian-language tech blog ... "
+ "Always give the user the article URL.",
annotations = @McpTool.McpAnnotations(readOnlyHint = true))
public SearchResult searchArticles(String query, Integer limit) {
// search, error handling, status ok / not_found / unavailable
}
The code is almost the same as in way 2. The difference is in delivery: now any MCP client connects at https://webscraft.org/mcp, gets the list of tools itself, and calls them. How I built this server and what decisions I made — in the article MCP Server for a Website: How to Let Claude and ChatGPT Search Your Articles.
API
@Tool (function calling)
MCP
Who uses it
other programs
only my SEO agent
Claude, ChatGPT, Cursor, any agent
Where is the code
site controller
inside the agent's application
separate package with its own endpoint
What to maintain
API versions, documentation
description for the model
description for the model, limits, public access security
👤 From my experience: the most interesting thing is that all three ways use the exact same vector search on the database. Only the "wrapper" changes, and who can use it. That's why the question "MCP or API" is almost always wrong: you're not choosing between them, you're choosing who to grant access to.
✂️ Chapter 5. Naive 1:1 Wrapper vs. Task-Oriented Wrapper
The most common mistake when creating tools for a model is to take an internal method or endpoint and expose it "as is."
How it's Usually Done
My internal search has many technical parameters. A naive wrapper would expose them all:
search_articles(
query, // query text
minScore, // similarity threshold, 0..1
candidates, // how many nearest chunks to take for grouping
perPost, // how many fragments from one article
locale, // uk, en, de, es
page, size, // pagination
sort, // score | date
includeDrafts // whether to include drafts
)
Why it's bad:
The model doesn't know the correct values. What minScore to set — 0.3 or 0.7? It depends on the embedding model and is calibrated on real data. The model will guess — and guess differently with each call.
More parameters mean more errors. Each parameter is another decision the model can make incorrectly, and a longer description that eats up context.
Dangerous parameters are exposed.includeDrafts in a public tool is a direct path to leaking unpublished articles.
Internal implementation becomes a contract. If I want to change the grouping algorithm, I'll have to break the interface for all clients.
The same applies to automatic generation of MCP tools from an OpenAPI specification: it's fast, but you get exactly a naive wrapper — one tool for each endpoint, with all technical parameters.
How I Did It
search_articles(
query, // what to search for,
limit // how many articles, 1–5, default 3
)
Everything else is inside the server: the similarity threshold is calibrated and set by configuration, no more than two fragments per article, only Ukrainian published articles, only the first page of results. The model only answers the questions "what are we searching for" and "how many."
💡 Why it's like this: models need tasks, not endpoints. A tool should answer the question "what does the user want to do," not "what parameters are in the database." A good rule: if a parameter has a "correct" value that you know better than the model, it shouldn't be a parameter.
📦 Chapter 6. What to Expose via MCP and What Not To
Aggregated tools are better than granular ones. Instead of three tools "find article," "get text," "get metadata" — one search that immediately returns the title, link, date, and relevant fragments. Every extra step is another call, another chance for error, and a bit more context.
Reading is safe, writing is cautious. Tools that only read are safe even in public access. Tools that modify data (create, delete, send, pay) require authorization, user confirmation, and a separate decision for each.
What's better to keep only in the API:
Deterministic service-to-service integrations — synchronization, webhooks, import. They don't need a model to "decide," they need predictability.
Bulk operations — updating a thousand records with one model call is not advisable.
Payments and irreversible actions — either leave them in the API, or expose them via MCP only with explicit human confirmation.
Don't expose everything at once. The more tools a model sees, the more often it chooses the wrong one. For the blog, three were enough for me. What to do when there are dozens of tools — in the article Tool RAG: What to Do When an Agent Has Too Many Tools.
🧭 Chapter 7. When API, when Function Calling, when MCP
Situation
What to choose
Why
Another service or frontend receives your data
API
the consumer is a program, predictability and a documented contract are needed
AI agent inside your application, one model
Function calling (@Tool)
tools are not needed by anyone except your agent; no network, authorization, separate deployment
Your data should be available in Claude, ChatGPT, Cursor
MCP
external clients do not see your @Tool; MCP is the only standard way to connect them
Several internal agents in the company should use the same tools
MCP
one server instead of a copy of tools in each agent
Your agent needs to use external services (GitHub, database)
MCP client in your application
ready-made MCP servers already exist - no need to write integrations yourself
Mass synchronization, webhooks, payments
API
a model is not needed here and even gets in the way
In practice, these options often coexist. I have article search both in the SEO agent as @Tool and in the MCP server for external clients - over the same search service. A good development path: start with @Tool, and when external consumers appear - move these same tools to MCP.
🧩 Chapter 8. Five myths about the difference
Myth 1. "MCP will replace API."
No. MCP works on top of API or database - my MCP server itself accesses the database. Integrations without a model, webhooks, mobile applications will continue to work through the API as they did.
Myth 2. "Function calling and MCP are the same thing."
No, they are different levels. Function calling is a model mechanism: how it requests a call. MCP is a way to deliver tools to the application. An MCP client receives tools from the server and passes them to the model through regular function calling. The model doesn't even know that the tool came through MCP.
Myth 3. "With MCP, the model calls your API itself."
No. The model suggests a call, and the client application executes it - and it can refuse, ask the user, or limit permissions. This is important for security: the responsibility for the call lies with the application, not the model.
Myth 4. "MCP server is OpenAPI converted into tools."
Technically, it can be done this way, and such generators exist. But the result is a naive wrapper from Chapter 5: all technical parameters are external, one tool per endpoint. A good MCP server is designed for tasks, not copied from the API.
Myth 5. "If function calling exists, MCP is not needed."
Half true. If the agent lives only in your application - yes, function calling is enough (Chapter 7). But as soon as your tools are needed by Claude, Cursor, or other agents, function calling won't help: it doesn't know how to "deliver" a tool to an external application.
🚫 Chapter 9. When all this is NOT needed
There are no LLMs among consumers. If your data is used only by programs and people through an interface - a regular API is needed. Neither function calling nor MCP will add anything but complexity.
One application and one model. If the AI agent lives only in your service and no one from the outside will use its tools - @Tool is enough. An MCP server will add networking, limits, public access security, and another endpoint to monitor.
The model does not require live data. If the answer can be given from general knowledge or static text in the prompt - tools are generally unnecessary.
❓ Frequently Asked Questions (FAQ)
Is MCP an API?
In a broad sense - yes, it's a programmatic interface. But it's designed for AI applications: the server itself describes its tools in a language understandable to the model, and the client connects them without separate integration. It does not replace a regular REST API for other services.
Will MCP replace REST?
No. MCP works on top of REST, databases, and other services. Integrations without a model - synchronization, webhooks, mobile applications - will continue to work through the API.
Can an MCP server be generated from OpenAPI?
Yes, and for quick prototyping, it's fine. But generated tools copy the API structure: all technical parameters and one tool per endpoint. For production, it's better to design several tools for user tasks.
Does function calling work without MCP?
Yes. Function calling is a mechanism of the model itself, it appeared before MCP. If the tools are only needed by your application, you pass them to the model directly, without any MCP server.
✅ Conclusions
API, function calling, and MCP are not competitors, but three levels of the same system: how a service provides data, how a model requests a call, and how tools get into an AI application. Therefore, the right question is not "what is better," but "who should use my tools." Only programs - API. Only your agent - function calling. Any AI client - MCP.
And regardless of the level - design tools for tasks, not for endpoints: fewer parameters, sensible default values inside, only what is safe to expose externally. How this looks on a real MCP server is in the article MCP server for a website, and the mistakes to easily stumble upon are in the article "Typical mistakes when creating an MCP server".