Beyond Basic HTTP MCP: Production Security Enhancements


What the Default HTTP MCP Doesn’t Give You

The MCP HTTP specification defines the wire protocol: JSON-RPC 2.0 over HTTP POST. Follow the spec, and you’ll have a working server. But production AI systems accessing corporate knowledge bases need security controls the specification deliberately leaves to implementers.

What’s Missing from Basic HTTP MCP:

 Security Requirement   Default HTTP MCP   Production Enhancement 
 Authentication   ”Use HTTPS”   MISE OAuth 2.1 with Azure AD token validation 
 Multi-Tenancy   Not addressed   Tenant extraction from JWT, query-level isolation 
 Audit Compliance   Not addressed   User+tool+data access logging, GDPR-ready 
 Rate Limiting   Not addressed   Per-user quotas, abuse prevention 
 Input Validation   Basic JSON parsing   Parameter sanitization, injection prevention 
 Error Handling   Generic JSON-RPC errors   Redacted errors, no internal detail exposure 

The knowledge-mcp server implements these enhancements. This article focuses on the critical additions that make HTTP MCP production-ready.

Enhancement Architecture Overview

The knowledge-mcp server layers security controls on top of the standard MCP HTTP transport:

Standard MCP HTTP (from specification)
   ↓
+ MISE Authentication Layer (Azure AD OAuth 2.1)
   ↓
+ Tenant Isolation Middleware (multi-tenant context)
   ↓
+ Enhanced JSON-RPC Validation (beyond spec requirements)
   ↓
+ Audit Logging Middleware (compliance trail)
   ↓
+ Secure Tool Design (parameter validation + output redaction)

Each layer adds specific security value. The following sections detail why each enhancement exists and how it’s implemented.

Enhancement 1: MISE Authentication (Beyond Basic OAuth)

Problem: The MCP specification mentions OAuth but doesn’t mandate implementation details. Basic JWT validation misses critical enterprise features: conditional access policies, device compliance, automatic token rotation.

Why MISE: Microsoft Identity Service Essentials provides 20+ years of security hardening. Instead of 400+ lines of custom OAuth code, this single configuration delivers production-grade authentication:

// Standard MCP HTTP setup (from specification)
builder.Services
   .AddMcpServer()
   .WithHttpTransport();

// ENHANCEMENT: Add MISE authentication
builder.Services
   .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
   .AddMicrosoftIdentityWebApi(configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization(options =>
{
   options.AddPolicy("RequireKnowledgeScope", policy =>
   {
       policy.RequireAuthenticatedUser();
       policy.RequireClaim("scp", "knowledge_read");
   });
});

Security Value Delivered:

  • ✅ JWT signature validation (prevents token tampering)
  • ✅ Automatic token expiration enforcement (15-minute windows)
  • ✅ Conditional access integration (MFA, device compliance, IP restrictions)
  • ✅ Scope-based authorization (prevents privilege escalation)
  • ✅ Multi-tenant tenant ID validation (prevents cross-tenant access)

ReferenceMCP Authorization Specification

Enhancement 2: Multi-Tenant Isolation (Preventing Data Leaks)

Problem: Default HTTP MCP has no tenant concept. Without tenant isolation, AI assistant queries from Organization A could theoretically access Organization B’s data if both use the same server.

Critical Pattern: Extract tenant ID from validated JWT claims (never trust client-provided tenant IDs), inject into request context, enforce at database query level.

public class TenantResolverMiddleware
{
   public async Task InvokeAsync(HttpContext context)
   {
       // SECURITY: Extract tenant from JWT (already validated by MISE)
       var tenantId = context.User.FindFirst("tid")?.Value;
       
       if (string.IsNullOrEmpty(tenantId))
       {
           context.Response.StatusCode = 401;
           return;
       }
       
       // SECURITY: Validate tenant is active (prevents suspended tenant access)
       var tenant = await tenantService.GetTenantAsync(tenantId);
       if (tenant == null || !tenant.IsActive)
       {
           context.Response.StatusCode = 403;
           return;
       }
       
       // CRITICAL: Inject tenant context (all downstream queries inherit)
       context.Items["TenantId"] = tenantId;
       
       await next(context);
   }
}

Enforcement at Query Level:

// MCP tool implementation
public async Task<object> SearchKnowledgeAsync(string query)
{
   // Tenant context automatically available (never parameter-based)
   var tenantId = httpContext.Items["TenantId"] as string;
   
   // ALL queries must include tenant filter
   var results = await searchService.SearchAsync(
       query, 
       tenantId,  // Enforces row-level security
       maxResults: 10
   );
   
   return results;
}

Security Value:

  • ✅ Cross-tenant query injection prevented (tenant from validated JWT, not request)
  • ✅ Cache isolation (keys prefixed with tenant ID)
  • ✅ Log segregation (every audit entry tagged with tenant)
  • ✅ Suspended tenant access blocked (active status check)

Enhancement 3: Enhanced JSON-RPC Validation (Attack Prevention)

Problem: MCP specifies JSON-RPC 2.0 format but doesn’t mandate security validation. Malicious payloads can attempt injection attacks, method enumeration, or DoS amplification.

Critical Addition: Request buffering for security validation.

public async Task InvokeAsync(HttpContext context)
{
   if (context.Request.Path.StartsWithSegments("/mcp"))
   {
       // CRITICAL: Enable buffering (allows multiple reads)
       context.Request.EnableBuffering();
       
       // Validate before processing
       var validationResult = await ValidateJsonRpcAsync(context);
       
       if (!validationResult.IsValid)
       {
           // SECURITY: Return generic error (no internal details)
           context.Response.StatusCode = 400;
           await context.Response.WriteAsync(validationResult.Error);
           return;
       }
       
       // CRITICAL: Reset stream position for MCP handler
       context.Request.Body.Position = 0;
   }
   
   await next(context);
}

Why This Matters: Without EnableBuffering(), reading the body consumes the stream. The validation middleware reads it, then the MCP handler gets an empty body. This pattern allows security validation while preserving the request for actual processing.

Validation Checks Added:

  • ✅ Payload size limits (1MB max, prevents memory exhaustion)
  • ✅ Batch request limits (max 10, prevents amplification attacks)
  • ✅ Method allowlist (only registered tools, prevents enumeration)
  • ✅ Parameter type validation (prevents type confusion attacks)

ReferenceJSON-RPC 2.0 Specification

Enhancement 4: Comprehensive Audit Logging (Compliance Ready)

Problem: MCP specification requires logging to STDERR but doesn’t define what to log. Compliance (SOC 2, GDPR, ISO 27001) demands user-level audit trails.

Critical Pattern: Log user identity, not just ”AI assistant accessed data.”

public class AuditLoggingMiddleware
{
   public async Task InvokeAsync(HttpContext context)
   {
       var startTime = DateTime.UtcNow;
       
       // COMPLIANCE: Extract user identity from validated JWT
       var userId = context.User.FindFirst("oid")?.Value;
       var userEmail = context.User.FindFirst("upn")?.Value;
       var tenantId = context.Items["TenantId"] as string;
       
       try
       {
           await next(context);
           
           // AUDIT TRAIL: Log successful access
           logger.LogInformation(
               "MCP Access | User: {Email} | Tenant: {TenantId} | " +
               "Path: {Path} | Status: {Status} | Duration: {Duration}ms",
               userEmail, tenantId, context.Request.Path,
               context.Response.StatusCode,
               (DateTime.UtcNow - startTime).TotalMilliseconds
           );
       }
       catch (Exception ex)
       {
           // SECURITY MONITORING: Log failures for anomaly detection
           logger.LogError(ex,
               "MCP Access Failed | User: {Email} | Tenant: {TenantId}",
               userEmail, tenantId
           );
           throw;
       }
   }
}

Structured JSON Output (STDERR per MCP spec):

{
 "timestamp": "2026-02-04T15:30:00Z",
 "level": "Information",
 "message": "MCP Tool Invoked",
 "userId": "uuid",
 "userEmail": "alice@example.com",
 "tenantId": "tenant-uuid",
 "toolName": "search_knowledge",
 "query": "customer data retention policy",
 "resultCount": 5,
 "duration": 234
}

Compliance Value:

  • ✅ SOC 2 Evidence: User-level access trails for auditors
  • ✅ GDPR Requests: ”Show all data accessed by user X”
  • ✅ Incident Response: Timeline reconstruction during security events
  • ✅ Anomaly Detection: Query for excessive access patterns

Enhancement 5: Secure Tool Design (Input Validation + Output Redaction)

Problem: MCP tools using [McpServerTool] attributes automatically expose methods to AI assistants. Without security controls, tools become attack vectors for injection, data exfiltration, or privilege escalation.

Critical Patterns:

1. Parameter Validation: Never trust AI-generated parameters 2. Tenant Context Injection: Never accept tenant ID as parameter (prevents spoofing) 3. Output Redaction: Strip sensitive fields before returning results 4. Generic Error Messages: Never expose internal exceptions to AI assistants

[McpServerToolType]
public class SearchKnowledgeTool(
   IKnowledgeSearch knowledgeSearch,
   IHttpContextAccessor httpContextAccessor)
{
   [McpServerTool(Idempotent = true, ReadOnly = true)]
   [DisplayName("search_knowledge")]
   public async Task<object> SearchAsync(
       [Description("Search query (max 500 characters)")] string query,
       [Description("Max results (1-20)")] int maxResults = 5)
   {
       // SECURITY: Validate parameters (prevents injection)
       if (string.IsNullOrWhiteSpace(query) || query.Length > 500)
           return new { success = false, error = "Invalid query" };
       
       if (maxResults < 1 || maxResults > 20)
           return new { success = false, error = "Invalid range" };
       
       // SECURITY: Tenant from context (never from parameters)
       var tenantId = httpContextAccessor.HttpContext
           .Items["TenantId"] as string;
       
       try
       {
           var results = await knowledgeSearch.SearchAsync(
               query, tenantId, maxResults
           );
           
           // SECURITY: Redact sensitive fields
           var sanitized = results.Select(r => new
           {
               Title = r.Title,
               Summary = r.Summary,
               Url = r.Url
               // OMIT: InternalId, AuthorEmail, SystemPaths
           });
           
           return new { success = true, results = sanitized };
       }
       catch (Exception ex)
       {
           // SECURITY: Log full exception, return generic error
           logger.LogError(ex, "Search failed for user {User}",
               httpContextAccessor.HttpContext.User.Identity?.Name);
           
           return new { 
               success = false, 
               error = "Search failed"  // Generic message only
           };
       }
   }
}

Security Value:

  • ✅ SQL injection prevented (query length limits, type validation)
  • ✅ Tenant spoofing prevented (context-based, not parameter-based)
  • ✅ Data exfiltration reduced (sensitive fields never returned)
  • ✅ Error detail leakage prevented (generic messages to AI)

ReferenceMCP Tools Specification

Azure Container Apps Deployment

The complete deployment follows standard .NET container patterns. Key enhancements for MCP servers:

1. Dynamic URL Resolution: Azure Container Apps generate environment-specific URLs. OAuth redirect URIs break with hardcoded localhost values. Production pattern:

// Check environment-provided URL first (Azure Container Apps set this)
var containerAppUrl = Environment.GetEnvironmentVariable("CONTAINER_APP_URL");
if (!string.IsNullOrEmpty(containerAppUrl))
   return containerAppUrl;

// Fallback for local development
return environment.IsDevelopment() ? "http://localhost:8080" : throw new InvalidOperationException();

2. Forwarded Headers Configuration: Container Apps sit behind Azure ingress. Preserve client IP for audit logs and rate limiting:

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
   options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                             ForwardedHeaders.XForwardedProto |
                             ForwardedHeaders.XForwardedHost;
   options.KnownNetworks.Clear();  // Trust Azure ingress
   options.KnownProxies.Clear();
});

3. Health Checks for Container Orchestration:

Container Apps require separate liveness (restart unhealthy containers) and readiness (prevent traffic to warming containers) probes:

app.MapHealthChecks("/health/live");   // Basic process health
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
   Predicate = check => check.Tags.Contains("ready")  // Dependency health
});

Deployment ReferenceAzure Container Apps Documentation

Production Patterns Summary

What We Added Beyond Basic HTTP MCP:

 Enhancement   Security Value   Compliance Impact 
 MISE Authentication   20+ years OAuth hardening vs custom code   SOC 2 Type II ready 
 Multi-Tenant Isolation   Prevents cross-tenant data access   GDPR tenant segregation 
 Enhanced JSON-RPC Validation   Blocks injection/enumeration attacks   Defense-in-depth requirement 
 Comprehensive Audit Logging   User-level access trail   SOC 2, ISO 27001, GDPR audits 
 Secure Tool Design   Prevents parameter injection, data exfiltration   Least privilege enforcement 

When These Enhancements Matter:

  • Enterprise AI knowledge systems serving corporate data
  • Multi-tenant SaaS with shared infrastructure
  • Compliance-regulated environments (healthcare, finance, government)
  • Zero-trust architectures requiring per-request validation

When Basic HTTP MCP Suffices:

  • Personal knowledge bases for local use
  • Single-tenant deployments with no shared infrastructure
  • Non-sensitive data without compliance requirements
  • Prototype/POC environments before production hardening

Implementation Priorities

If building incrementally, implement in this order for maximum security ROI:

1. MISE Authentication (OAuth 2.1, scope validation) 2. Audit Logging (user context, tool invocation, data access) 3. Multi-Tenant Isolation (if supporting multiple organizations) 4. Enhanced JSON-RPC Validation (request buffering, attack prevention) 5. Secure Tool Design (parameter validation, output redaction)

Each layer builds on the previous, creating defense-in-depth.


Resources

Related Articles:

  • Securing AI Knowledge Access: HTTP MCP Server Architecture
  • Building Enterprise STDIO MCP Servers