What is MCP Server Authentication?
MCP server authentication is the security mechanism that verifies the identity of AI clients before allowing them to discover or execute tools over remote Model Context Protocol transports. While local MCP servers communicate securely over standard input/output (stdio) within a single host machine, production enterprise architectures host MCP servers centrally in ASP.NET Core over HTTP, requiring industry-standard OAuth 2.0 bearer tokens backed by Microsoft Entra ID.
Without transport-level authentication, any client with network access to your MCP server could invoke sensitive database queries, trigger internal APIs, or exfiltrate enterprise documents.
1. Microsoft Entra ID Configuration
To protect your MCP server, register two applications in your Microsoft Entra ID tenant:
- MCP Server API App: Exposes application ID URI
api://dotnetstudio-mcp-serverand defines custom scopes:Mcp.Tools.Read: Allows discovering available tools and schemas.Mcp.Tools.Execute: Allows invoking tool functions.
- AI Client App: Represents the calling application (e.g., an automated agent background worker or a developer desktop tool). Granted permission to request
api://dotnetstudio-mcp-server/Mcp.Tools.Execute.
In your appsettings.json, configure the AzureAd section:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "yourtenant.onmicrosoft.com",
"TenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"ClientId": "33333333-3333-3333-3333-333333333333",
"Audience": "api://dotnetstudio-mcp-server"
}
}
2. Server Implementation in ASP.NET Core (.NET 10)
Install the required NuGet packages into your ASP.NET Core project:
dotnet add package ModelContextProtocol.AspNetCore --version 1.0.0
dotnet add package Microsoft.Identity.Web --version 3.2.0
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 10.0.0
Configure Program.cs to enable JWT Bearer authentication, register MCP services, and enforce authorization policies on the MCP endpoint:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using ModelContextProtocol.AspNetCore;
using ModelContextProtocol.Server;
var builder = WebApplication.CreateBuilder(args);
// 1. Add Microsoft Entra ID JWT Bearer validation
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
// 2. Define authorization policies for MCP tool execution
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("McpExecutePolicy", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireScope("Mcp.Tools.Execute");
});
});
// 3. Register MCP Server services
builder.Services.AddMcpServer(options =>
{
options.ServerInfo = new Implementation
{
Name = "DotNetStudio Enterprise Tools",
Version = "1.0.0"
};
})
.AddTool<OrderManagementTools>();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// 4. Map the MCP endpoint with required authorization
app.MapMcpServer("/mcp")
.RequireAuthorization("McpExecutePolicy");
app.Run();
3. Defining Protected MCP Tools
When registering tools in C#, you can validate caller identity directly from HttpContext or check custom claims:
using System.Security.Claims;
using ModelContextProtocol.Server;
public class OrderManagementTools
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IOrderRepository _orderRepo;
public OrderManagementTools(
IHttpContextAccessor httpContextAccessor,
IOrderRepository orderRepo)
{
_httpContextAccessor = httpContextAccessor;
_orderRepo = orderRepo;
}
[McpTool("get_customer_orders", "Retrieves recent orders for a verified customer account.")]
public async Task<string> GetCustomerOrdersAsync(
[McpParameter("Customer account ID", required: true)] string customerId,
CancellationToken ct)
{
var user = _httpContextAccessor.HttpContext?.User;
var callerId = user?.FindFirst(ClaimTypes.NameIdentifier)?.Value
?? user?.FindFirst("azp")?.Value;
// Enterprise audit logging
Console.WriteLine($"[AUDIT] Tool invoked by caller: {callerId} for customer: {customerId}");
var orders = await _orderRepo.GetOrdersByCustomerAsync(customerId, ct);
return System.Text.Json.JsonSerializer.Serialize(orders);
}
}
4. Connecting from a C# Client with DefaultAzureCredential
When building an autonomous AI agent in C# using Semantic Kernel or Microsoft Agent Framework, authenticate outbound calls using Azure.Identity:
using Azure.Core;
using Azure.Identity;
using ModelContextProtocol.Client;
using ModelContextProtocol.Http;
var tokenCredential = new DefaultAzureCredential();
var tokenContext = new TokenRequestContext(new[] { "api://dotnetstudio-mcp-server/.default" });
var tokenResult = await tokenCredential.GetTokenAsync(tokenContext, CancellationToken.None);
// Configure MCP HTTP client with Bearer Token
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokenResult.Token);
var transport = new SseClientTransport(
httpClient,
new Uri("https://mcp.internal.dotnetstudioai.com/mcp"));
var mcpClient = new McpClient(transport);
await mcpClient.ConnectAsync();
// Discover authorized tools
var tools = await mcpClient.ListToolsAsync();
foreach (var tool in tools)
{
Console.WriteLine($"Discovered Authenticated Tool: {tool.Name}");
}
5. Security & Production Checklist
- Enforce HTTPS in Production: Never transmit bearer tokens over unencrypted HTTP. Ensure TLS 1.3 is enforced at your reverse proxy (Azure Front Door or AWS ALB).
- Short-Lived Tokens: Keep access token lifetimes between 15 and 60 minutes. MCP clients should implement token refresh handlers using
DelegatingHandler. - Audit Every Tool Execution: Log the calling application ID (
appidorazpclaim), IP address, and invoked tool parameters to an immutable audit store (Azure Monitor / OpenTelemetry). - Least-Privilege Scopes: Separate read-only query tools from write/delete tools. Require elevated scopes (
Mcp.Tools.Admin) for mutating actions.