Recently, I saw a LinkedIn post discussing "MCP vs API" and felt like we needed to choose one. But I don't think we do, as each option solves a different problem, and the fastest way to see that is to run the same backend behind both. So I built one Azure Functions app that exposes identical business logic twice: once as a classic REST API and once as a remote MCP server. The code is in the companion repo, and this post walks through it.
Two different problems
The simplest way I think about it is this. An API tells software how to talk to another piece of software. MCP, the Model Context Protocol, helps an AI system understand what tools and resources are available, and how to use them.
Consider a normal application that needs a file from Google Drive. The developer already knows what is needed: the app, the Google Drive API, and the file. They read the API documentation, call the right endpoint, and process the response. The knowledge lives in the developer and gets compiled into the application.
Now ask an AI assistant: "Find our latest sales presentation, compare the numbers with our customer database, check whether the related GitHub project has changed, and summarise everything." The assistant may need to work across Google Drive, a database, GitHub, and internal documents. Nobody hardcoded that sequence. The AI has to discover what tools exist, what each one does, which parameters are required, and which tool to use next. That discovery problem is what MCP solves.
A good analogy: an API is calling a restaurant directly. You already know the restaurant, its number, and what you want. MCP is giving your assistant a standardized directory of restaurants, menus, and available actions. The assistant discovers what is available and chooses the right capability. And underneath, the restaurant still uses the same kitchen. That kitchen is often the API.
Same kitchen, two doors
I took that analogy literally. The sample is a small restaurant directory with exactly one implementation of its business logic, an IRestaurantDirectory service that can search restaurants, return menus, and place orders. Two doors sit in front of it.
Door one is the REST API, three HTTP-triggered functions:
[Function(nameof(GetRestaurants))]
public IActionResult GetRestaurants(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "restaurants")] HttpRequest req)
{
string? cuisine = req.Query["cuisine"];
string? city = req.Query["city"];
return new OkObjectResult(directory.Search(cuisine, city));
}
Fixed endpoints, documented parameters, request, and response. If you know the contract, this is the most direct path.
Door two is the MCP server, built with the Azure Functions MCP extension. I compared the hosting options for remote MCP servers on Functions in an earlier post; this sample uses the binding extension, which has since reached a stable 1.x release with typed tool property attributes. The same operations become tools, and the trigger attributes carry something the REST door never needed: descriptions written for a machine that has to figure out what to call.
[Function(nameof(SearchRestaurantsTool))]
public string SearchRestaurantsTool(
[McpToolTrigger("search_restaurants",
"Searches the restaurant directory. Both filters are optional; call it without arguments to list every restaurant.")]
ToolInvocationContext context,
[McpToolProperty("cuisine", "Cuisine to filter by, for example 'Italian' or 'Japanese'.", isRequired: false)]
string? cuisine,
[McpToolProperty("city", "City to filter by, for example 'Nijmegen' or 'Utrecht'.", isRequired: false)]
string? city)
=> ...
Neither door contains business logic. Both call the same service. The
Functions runtime hosts the MCP endpoint at /runtime/webhooks/mcp, protected in Azure by a system key, and the whole thing deploys to a Flex Consumption plan with azd up.
Here is the deployed architecture, exactly as the Bicep in the repo provisions it:
One deployment lesson worth passing on: function names must be unique across both doors. My first deployment had a PlaceOrder HTTP trigger and a PlaceOrder MCP tool trigger, and the host refused to start with a cryptic "Sequence contains more than one matching element". The MCP-facing tool names stay whatever you declare in the attribute; the .NET function names behind them need a suffix.
What changes at door two
Run the app locally, point an MCP client at it (the repo ships a .vscode/mcp.json for VS Code, and Claude works too), and ask: "Find me an Italian restaurant in Nijmegen, show me the menu, and order two Margheritas."
The agent lists the available tools, reads their descriptions, and chains search_restaurants, get_menu, and place_order on its own. It passes the restaurant ID from the first call into the second, and the exact item name from the second into the third. Nobody wrote that orchestration. Against the REST door, that same flow is three documented calls a developer wires together at design time.
That is the whole difference in one demo. The REST door serves callers who know. The MCP door serves callers who discover. Instead of teaching an AI system separately how to interact with twenty different tools, MCP gives those tools a consistent way to expose capabilities, which makes AI systems easier to extend, orchestrate, and maintain.
One consequence surprised me in a useful way: tool descriptions become load-bearing. The agent chooses tools based on the text in those attributes. Vague descriptions produce vague agents. Treat tool descriptions like API contracts, because for an agent, they are.
Where MCP is the wrong answer
MCP is not the right door for everything, and reaching for it by default is how we get the next round of architecture astronautics.
Skip MCP when the caller is deterministic software. A backend service that needs a file from Drive should call the Drive API. Adding an MCP layer between two pieces of conventional software adds latency and a dependency, and discovers nothing, because no model is doing the discovering.
Skip it when there is exactly one integration, and leave it that way. The discovery machinery pays off across many tools; for a single well-known endpoint it is overhead.
And be honest about what MCP does not do. It doesn't make an agent intelligent, and it doesn't do authorization for you. We still need everything we already need for APIs, authentication, permissions, observability, and governance. In fact, the more actions we allow AI to perform, the more important these become.
In the sample, the demo endpoints are deliberately anonymous, and the README's caveats section says so out loud; in production, the REST door belongs behind API Management and the MCP door behind Entra ID, which the Functions extension now supports as built-in MCP auth. I ran into that boundary while testing: developer tools like Claude Code, VS Code, and MCP Inspector happily send a system key in a header, but end-user clients that follow the MCP authorization spec expect a real OAuth flow and refuse anything less.
The real opportunity
So when someone asks me which is better, MCP or API, my answer is: wrong comparison. The architecture that keeps showing up in practice is AI assistant, MCP, existing API, business system. APIs connect software. MCP helps AI understand how to interact with that software. And AI agents turn those connections into actions and workflows.
The sample repo has the full code, local run instructions, and Bicep to deploy both doors to Azure Functions. Clone it, open both doors, and the debate settles itself.

 in Sweden Central, with managed identity access to storage, Application Insights and Log Analytics, provisioned by azd and Bicep](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0zkj2zikpajjpvn082wg.png)

Top comments (4)
The discovery-problem framing is the cleanest case I've read for why MCP isn't just a REST rename. We expose tools to agents behind a plain HTTP layer, and the moment the model has to choose the next call, the assumption that the developer already knows the sequence falls apart.
Did you measure the token cost of the live MCP discovery round-trip against handing the model a curated tool manifest up front? Wondering where that break-even sits.
Good question, and no, I didn't measure it. The discovery round-trip runs once per session, not per turn, and the tools/list it returns is essentially the manifest you would curate by hand, so both end up as tool definitions in context on every model call. At three tools, break-even is immediate. At dozens, it shifts with tool count and description length rather than the discovery mechanism. Measuring that curve is on my list.
“Same kitchen, two doors” is a useful way to frame it. MCP can handle discovery and tool use without replacing the API underneath.
Thanks! That's exactly the conclusion the sample forced on me. The doors stay thin: all the logic lives in the one service behind them, and the API remains the contract of record.