{"slug": "same-kitchen-two-doors-one-maitre-d", "title": "Same Kitchen, Two Doors, One Maître d'", "summary": "A developer demonstrated that Azure API Management can serve as a unified gateway in front of a single backend, closing the authentication-parity gap between REST and MCP endpoints. The approach lets APIM validate OAuth tokens against Entra ID, inject backend function keys so agents never hold credentials, and apply rate limits, tracing, and MCP-aware content safety policies to tool calls. APIM can also generate an MCP server directly from existing REST APIs, offering a path for enterprises whose backends cannot be modified.", "body_md": "In [MCP vs API Is the Wrong Question](https://dev.to/steefjan_wiggers_34a415b/mcp-vs-api-is-the-wrong-question-392f) I deployed one Azure Functions app that exposes the same backend twice: a REST door for callers who know the contract, and an MCP door for agents that discover it. The caveats section admitted, out loud, what the demo left open: the REST endpoints were anonymous, and the MCP endpoint was guarded by a system key that every client had to carry.\n\nA reader summarized the gap better than I did: the useful test is whether both doors preserve the same auth, error, and idempotency behavior, because otherwise the integration surface splits even when the backend does not. Auth parity is exactly what the demo lacked. This post closes that gap with Azure API Management, and it turns out APIM can play three different roles in front of this architecture. Sticking with the restaurant analogy: the kitchen cooks, the doors let people in, and APIM is the maître d'. It checks reservations, limits party size, and keeps the guest book, without touching a single pan.\n\nBoth doors in the sample share one property: they authenticate the caller mechanically, not organizationally. A function key or an mcp_extension system key says \"this caller has the string\" and nothing else. There is no identity, no revocation short of rotating the key for everyone, no per-caller limits, and no answer to the question every platform team eventually asks: who called what, how often, and should they be allowed to?\n\nThat question is gateway work. And the more actions we let agents perform, the more urgent it becomes, because an agent retries, chains calls, and occasionally misreads a tool description. You want the blast radius of that behavior governed somewhere the agent cannot negotiate with.\n\nThe familiar pattern, and I will keep it short because it is the subject of my API Management for AI Workloads series. APIM fronts the /api endpoints: validate-jwt against Entra ID or subscription keys for consumers, rate limits and quotas per product, IP filtering where it applies, and the function app locked down so only the gateway reaches it. The REST door's contract does not change; it just stops being naked.\n\nAPIM can also expose an existing MCP server as a passthrough, and a Functions-hosted MCP endpoint is an explicitly supported backend. The MCP traffic (streamable HTTP) flows through the gateway, and policies apply to it like any other API.\n\nThis changes the security posture in one important way: the agent no longer holds the backend credential. The client authenticates to APIM properly, with OAuth against Entra ID or a subscription key, and a policy injects the x-functions-key toward the backend. Rotating the system key becomes an operation nobody outside the platform team notices. On top of that you get the gateway's usual gifts, now applied to tool calls: throttling per agent identity, request tracing that shows which tools are called at which rate, and since Build 2026 even MCP-aware content safety policies.\n\nIn the first post I noted that developer tools happily send a key in a header while end-user MCP clients follow the authorization spec and demand a real OAuth flow. Pattern two is one of the two clean answers to that boundary; the Functions extension's built-in Entra auth is the other. With a gateway in front, the OAuth conversation happens at the maître d', where it belongs.\n\nThe third option surprised me when I first tried it: APIM can expose a REST API as an MCP server by itself. You pick operations from an API already managed in the gateway, and APIM manufactures MCP tools from them. In that design the function app implements only the REST door, the MCP extension disappears from the codebase, and the second door is generated at the gateway.\n\nThis is genuinely attractive for one class of backend: the one you cannot change. An enterprise has hundreds of APIs already registered in APIM, owned by teams with no bandwidth for an agent project. Pattern three gives every one of them an MCP door without a single code change, and the same gateway policies govern it from day one.\n\nBut the first post argued that tool descriptions are load-bearing: the agent chooses tools based on that text, and vague descriptions produce vague agents. Pattern three moves those descriptions out of the code and into gateway configuration. The team that owns the business logic no longer owns the words the agent reads. In pattern two, the descriptions live in McpToolTrigger attributes, versioned in the same pull request as the logic they describe. That difference sounds small and is not; it decides who fixes the contract when the agent starts picking the wrong tool.\n\nPattern one is never wrong so much as incomplete; it governs only the door that agents do not use.\n\nPattern two is the wrong answer when there is no code to own. If the backend team cannot or will not adopt the MCP extension, insisting on in-code tools blocks the whole effort. It also costs you nothing to skip if no agent ever calls the system; an MCP door with no visitors is just surface area.\n\nPattern three has sharper edges. APIM's MCP support is tools-only today: no MCP resources, no prompts. The Functions extension gained resource and prompt triggers at Build 2026, so if your server needs more than tools, the gateway cannot generate it. Auto-generated tools also inherit the granularity of the REST operations, and a fifty-operation API becomes a fifty-tool server, which is more choices than an agent should have to reason about. And every tool call pays the extra hop, which is fine for governance but worth knowing.\n\nThe honest default for new systems: pattern two, with descriptions in code and the gateway doing identity, limits, and observability. Pattern three for the estate you already have. Pattern one always, underneath both.\n\nWhat deploying it taught me\n\nThree things the documentation did not. The mcpProperties.endpoints contract wants an object keyed by endpoint name, while both the published examples and the Bicep type definitions say array; the deployment error is the only place the truth lives, which is what a preview API version means in practice. The client-facing URL is the API path plus an /mcp suffix, a small trap everyone will hit exactly once. And when a spec-compliant MCP client receives a 401 from the gateway, it does not ask for a header; it starts OAuth dynamic client registration, which APIM answers with a 404. Two different clients have now walked into that same wall from different directions in this series, and it is the strongest argument yet for doing the OAuth setup properly.\n\nThe first post ended by replacing \"MCP or API?\" with a better question: where do I want discovery, and where do I want a hard contract? APIM adds the operational half: wherever you put discovery, put a gateway in front of it that knows who is discovering. The kitchen stays the kitchen. The doors stay honest. The maître d' remembers every guest.\n\nThe companion repo will gain an infra/apim variant with the Bicep and policies for patterns two and three. Until then, the [original sample](https://github.com/steefjan1/mcp-vs-api-azure-functions) is the substrate all of this builds on.", "url": "https://wpnews.pro/news/same-kitchen-two-doors-one-maitre-d", "canonical_source": "https://dev.to/steefjan_wiggers_34a415b/same-kitchen-two-doors-one-maitre-d-510", "published_at": "2026-09-13 14:10:21+00:00", "updated_at": "2026-09-13 14:40:43.031466+00:00", "lang": "en", "topics": ["ai-agents", "ai-infrastructure", "ai-tools", "developer-tools", "mlops"], "entities": ["Azure API Management", "Azure Functions", "Microsoft Entra ID", "MCP", "REST", "Build 2026"], "alternates": {"html": "https://wpnews.pro/news/same-kitchen-two-doors-one-maitre-d", "markdown": "https://wpnews.pro/news/same-kitchen-two-doors-one-maitre-d.md", "text": "https://wpnews.pro/news/same-kitchen-two-doors-one-maitre-d.txt", "jsonld": "https://wpnews.pro/news/same-kitchen-two-doors-one-maitre-d.jsonld"}}