IKRC Content

Your MCP Server Authenticates the User, Not the Tool Call

Authentication in front of an MCP server decides who gets through the door. Which of them may call the tool that reads every invoice is a question only your application code answers.

The request arrives sounding small. Somebody in operations has been using Copilot all quarter and wants it to answer questions about the scheduling system directly, so a developer spends a fortnight wiring an MCP server onto the application and puts Microsoft Entra sign-in in front of it. It demonstrates beautifully. Then somebody in the security review asks what stops a warehouse supervisor from calling the tool that returns every customer invoice, and the room discovers that the answer is not in the protocol, not in the identity provider, and not in the hosting platform.

It is in the application code, and nobody wrote it. Exposing MCP to the outside world is a different exercise with a different threat model. This one is about the internal line-of-business server, where every caller is already an employee and the interesting question is which of them may do what.

Server Authorization Stops at the Server

Microsoft puts it in an Important callout on the App Service page for MCP server authorization, and it is the sentence that should be read out loud in the design meeting: MCP server authorization defines access to the server, and it does not provide granular control to individual MCP tools or other constructs. Authentication in front of the server is a door. Behind that door the tool list is one room, and everybody who gets through the door is standing in it.

The protocol makes this sharper than most teams expect. The MCP authorization specification says authorization is optional for MCP implementations, and for servers on the STDIO transport it says implementations should not follow the authorization specification at all and should instead retrieve credentials from the environment. So there is no layer underneath you holding a per-user rule. There is a transport, and there is whatever you wrote.

What a connected caller can do first is list. ListToolsRequest is a standard message, described as sent by the client to request a list of tools the server has, and the response carries every tool with the description attached to it. Those descriptions are written to be maximally explicit, because their whole job is to let a model pick correctly: reads all customer invoices, filtered by account and date range. That is a specification of your internal capability surface, written in plain English, handed to anyone who can complete a handshake. A tool nobody is authorized to call is still a tool everybody can read about.

So per-tool authorization is code, written by you, inside the tool. The version that survives contact with the business is not a second set of rules living in the MCP layer but the tool calling the same service method the screen calls, with the same caller identity, and that service refusing the same way it already refuses. When the HTTP context is available, App Service Authentication exposes the user claims to it, which is the hook that makes this possible. Then a rule change lands in one place and both callers inherit it.

The practical test to apply while the tool list is still short: take it, and for each entry name the screen in the existing application that does the same thing and the check that screen performs. Any tool with no answer in that second column is a tool that has no authorization, and writing the list out is usually the moment somebody says the invoice one was only meant for finance.

Your Tool and Your Controller Will Drift

Hosting the server inside the ASP.NET Core application that already owns the logic is a small amount of code. Add the ModelContextProtocol.AspNetCore package, which the documentation installs with the prerelease flag, register with AddMcpServer followed by WithHttpTransport and WithToolsFromAssembly, and map the endpoint with MapMcp. The C# SDK is split into several packages, with ModelContextProtocol.Core for minimal dependencies and the AspNetCore one for HTTP-hosted servers.

The reason to host it in the same process is not convenience. It is the same dependency injection container, the same database context, the same authentication middleware and the same connection strings, which means the tool and the screen cannot disagree about configuration because there is only one.

They can still disagree about logic, and Microsoft flags exactly this in its own tutorial. The sample tool class reimplements what the existing controller already does, and the tutorial says so plainly while it does it, noting that the duplication is unnecessary and that the fix is to move the app logic to a service class and call it from both the controller and the tool. That is an unusually honest note to leave in a walkthrough, and it is the failure worth designing against: nothing breaks on the day you duplicate the logic. It breaks eight months later, when a credit-hold rule is added to the controller because that is where the team looks, and the tool keeps answering the way it always did.

Keep the tool classes thin enough that this cannot happen. A tool method that is more than argument mapping, a service call, and a return is a tool method that has started to own business logic, and it should be caught in review as a defect.

What Does the Token Actually Let Through?

The token that authorizes the caller to your MCP server is scoped to your MCP server, and the temptation is to forward it to the systems behind. The specification forbids it in a sentence with no wriggle room: the MCP server must not pass through the token it received from the MCP client. The same document requires servers to validate that tokens were issued specifically for them as the intended audience, and the App Service guidance repeats the warning and names the alternative, which is to obtain a new token through the on-behalf-of flow or another mechanism for explicit delegation.

Three configuration details cost teams a day each and are all written down in advance. First, if App Service created the Entra registration for you, a default policy only allows tokens obtained by the app itself, so the MCP client has to be added to the allowed applications list before anything works. Second, MCP assumes Dynamic Client Registration is available and many identity providers do not offer it, including Microsoft Entra ID, so the client must be preconfigured with a client ID. Third, the protocol requires the server to publish protected resource metadata and to return a WWW-Authenticate header on a 401 so the client can find it; App Service support for that metadata is currently in preview and is switched on by setting WEBSITE_AUTH_PRM_DEFAULT_WITH_SCOPES to your scope list.

Preview is worth reading as preview here. The packages install with a prerelease flag and the metadata support is not finished, which is fine for an internal server behind a corporate identity provider and is a real conversation for anything with a compliance obligation attached to it.

Where IKRC Fits

The protocol part of this is a week. The part that takes longer is the one nobody scopes: going through the existing application to find where each rule is actually enforced, deciding which capabilities become tools at all, and getting the tool methods down to a shape where they cannot drift from the screens. That is ordinary integration work on an application somebody already depends on, and IKRC does it through API and system integration on .NET systems that are already in production.

Bring three things to a first conversation and it goes quickly: the list of capabilities somebody wants an agent to reach, the place in the code where authorization currently lives, and an honest answer about whether the business rules sit in a service layer or in the controllers. Call IKRC at 646-783-1441 or email info@ikrc.co.

Related Reading

For the outward-facing version of this question, read Customer-Facing MCP Servers. For why a narrow tool list beats a general assistant, read Why a Task-Specific Agent Beats a Chatbot Bolted Onto Your App.

Need this solved in your software?

IKRC builds the custom systems, integrations, and modernization work discussed in this article.

Ready to Build?

Let's engineer your solution.

Every project starts with a conversation. Tell us what you're trying to solve.