Skip to content

IKRC Insights

MCP tool permissions in .NET applications

Built-in authentication decides who reaches an MCP server. Which of those callers may see or call the tool that reads every invoice is decided by the application's own authorization.

Put an MCP server on an internal scheduling application, place Microsoft Entra sign-in in front of it, and a security review can ask what stops a warehouse supervisor from calling the tool that returns every customer invoice. The protocol, the identity provider and the hosting platform each answer part of that question. The rest sits in the application.

Behind staff sign-in, every caller of an internal line-of-business server holds a token for the organization's tenant. That set can include service identities and guest accounts as well as employees, and the question is which of those identities may do what. Customer-facing MCP servers have a different threat model.

What App Service authentication covers

Microsoft states the boundary in an Important note on the App Service page for built-in MCP server authorization: it defines access to the server and does not provide granular control over individual MCP tools. A valid Entra token shows the caller may reach the server. Whether that caller may read every invoice is a separate check.

The MCP authorization specification works at the transport level and is optional. Servers on HTTP transports should follow it; servers on the STDIO transport should not, and retrieve credentials from the environment instead. That changes how the server process obtains credentials. The application still has to decide what the person behind a request may do, and a STDIO server running under a service account can reach whatever that account can reach.

If the server leaves per-tool checks out, any caller with a valid token for the server can call every tool, including the invoice export.

Which tools does each caller see?

The current MCP tools specification lets the answer differ by caller. A server returns the tools currently available to the requesting client, and that set may vary by the authorization presented on the request, for example only the tools the caller's granted scopes permit. It must not vary per connection or as a side effect of earlier requests on the connection. A warehouse supervisor's client can receive a tool list with no invoice tool in it.

Filtering discovery is half of the control. The official C# SDK documents AddAuthorizationFilters() with [Authorize] and [AllowAnonymous] on tools, prompts and resources (SDK filters documentation). The permission still has to be enforced when the tool is invoked, because a tools/call request names the tool directly. Where the invoice page's permission check lives in a service method, a tool that calls that method with the same caller identity is refused for the supervisor in the same way. When the HTTP context is available, App Service Authentication exposes the user's claims to the app, which is what makes that possible.

A useful exercise while the tool list is still short: write down each tool, the screen in the existing application that does the same thing, and the permission check that screen performs. A blank third column does not show that a tool needs no check: some tools have no screen equivalent, and some apply a policy the application has never enforced. Each blank row is a review gap until a named service-layer policy covers that call.

Tool methods and controllers drift apart

Hosting the server inside the ASP.NET Core application that already owns the logic takes little code: add the ModelContextProtocol.AspNetCore package, register AddMcpServer() with WithHttpTransport() and WithToolsFromAssembly(), and map the endpoint with MapMcp(). The tools then share the application's dependency injection container, database context, authentication middleware and connection strings.

The logic can still diverge. Microsoft's App Service MCP tutorial has its sample tool class duplicate the existing TodosController and notes that the duplication is unnecessary; its recommended fix is a service class called by both the controller and the tool. If a credit-hold rule is later added only to the controller, the tool keeps answering the old way and nothing fails at build time.

The aim is that authorization and business rules live in the shared service that both the controller and the tool call. A tool method can still validate and map its arguments, or compose several service calls into one answer. Code review can then check whether a rule, such as the credit hold or a permission check, has been implemented in the tool instead of in that service.

Tokens, client registration and preview status

The token the MCP client presents is meant for the MCP server. The specification requires servers to validate that tokens were issued for them as the intended audience and not to accept or transit any other tokens, and the App Service page warns against forwarding the incoming token to downstream resources. When a tool needs to call another API as the user, it should obtain a new token through the on-behalf-of flow or another explicit delegation mechanism.

Client registration depends on the identity provider. The 2026-07-28 specification lists three ways for a client to get a client ID: Client ID Metadata Documents, pre-registration, and Dynamic Client Registration, which it marks as deprecated and retains for backwards compatibility with authorization servers that do not support Client ID Metadata Documents. Microsoft Entra ID does not support Dynamic Client Registration, so an Entra-protected server needs the client preconfigured with a client ID. If App Service created the Entra registration, its default policy only allows tokens obtained by the app itself, and the MCP client has to be added to the allowed applications list before any call succeeds.

Release status needs two separate checks. The official C# SDK has stable releases, with v2.2.0 the latest on its releases page, although Microsoft's tutorial still installs the package with --prerelease; pin the stable version you tested. App Service's built-in protected resource metadata support, switched on with the WEBSITE_AUTH_PRM_DEFAULT_WITH_SCOPES setting, is documented as preview. That preview label belongs to the hosting feature, not to the SDK release.

Related Reading

For why a narrow tool list serves users better than a general assistant, read Why a Task-Specific Agent Beats a Chatbot Bolted Onto Your App.

Contact IKRC

Your next software project.

Connection Lost

Attempting to reconnect to the server...