MP-301f · Module 3

Enterprise Audit Trails

4 min read

Enterprise audit trails go beyond simple access logging. They must satisfy regulatory requirements (SOC 2 Type II, HIPAA, GDPR), support security incident investigation, and provide evidence for compliance audits. An enterprise-grade MCP audit trail captures every data access event with enough context to reconstruct the full chain: which user, through which AI session, reading which resource, from which source system, with which access controls applied, and what data was returned (by classification level, not by content).

The audit trail spans three layers that must be correlated. The AI platform layer logs the conversation — what the user asked, what the model responded. The MCP server layer logs resource access — which URIs were read, which servers were queried. The enterprise platform layer logs API calls — which records were fetched, under which user identity. Correlating across layers requires a shared session ID that flows from the AI platform through the MCP server to the enterprise platform. Without this correlation ID, investigating an incident requires manual timeline reconstruction across three separate log systems.

Audit retention and immutability are non-negotiable for regulated industries. SOC 2 requires audit logs to be retained for at least one year and protected from modification. HIPAA requires six years. PCI DSS requires one year immediately available and archived for an additional year. The MCP server should write audit logs to a separate, immutable storage system — not the same database it reads from. Cloud-native options include AWS CloudTrail with S3 Object Lock, Azure Immutable Blob Storage, or GCP Cloud Logging with retention policies.

interface EnterpriseAuditEntry {
  // Correlation
  sessionId: string;          // From the AI platform
  mcpRequestId: string;       // Generated by MCP server
  platformRequestId?: string; // From the enterprise platform

  // Who
  userId: string;
  userEmail: string;
  userRoles: string[];
  propagatedIdentity?: string; // Platform-level identity if different

  // What
  action: "read" | "subscribe" | "unsubscribe";
  resourceUri: string;
  sourceSystem: string;
  classification: Classification;

  // Controls
  accessControlResult: "allowed" | "denied";
  maskingApplied: boolean;
  fieldsReturned: number;
  fieldsMasked: number;
  responseBytes: number;

  // When
  timestamp: string;
  durationMs: number;

  // Context
  serverVersion: string;
  deploymentId: string;
}

// Middleware that wraps every resource handler with audit logging
function withAudit(
  handler: ResourceHandler,
  classification: Classification
): ResourceHandler {
  return async (uri, params, request) => {
    const start = Date.now();
    const user = getUserContext(request);
    const requestId = crypto.randomUUID();

    try {
      const result = await handler(uri, params, request);
      const content = result.contents[0]?.text ?? "";

      await auditLogger.log({
        sessionId: request.headers?.["x-session-id"] ?? "unknown",
        mcpRequestId: requestId,
        userId: user.userId,
        userEmail: user.email,
        userRoles: user.roles,
        action: "read",
        resourceUri: uri.href,
        sourceSystem: extractSource(uri),
        classification,
        accessControlResult: "allowed",
        maskingApplied: classification !== "public",
        fieldsReturned: countFields(content),
        fieldsMasked: countMaskedFields(content),
        responseBytes: new TextEncoder().encode(content).length,
        timestamp: new Date().toISOString(),
        durationMs: Date.now() - start,
        serverVersion: process.env.SERVER_VERSION ?? "unknown",
        deploymentId: process.env.DEPLOYMENT_ID ?? "local",
      });

      return result;
    } catch (err) {
      await auditLogger.log({
        sessionId: request.headers?.["x-session-id"] ?? "unknown",
        mcpRequestId: requestId,
        userId: user.userId,
        userEmail: user.email,
        userRoles: user.roles,
        action: "read",
        resourceUri: uri.href,
        sourceSystem: extractSource(uri),
        classification,
        accessControlResult: "denied",
        maskingApplied: false,
        fieldsReturned: 0,
        fieldsMasked: 0,
        responseBytes: 0,
        timestamp: new Date().toISOString(),
        durationMs: Date.now() - start,
        serverVersion: process.env.SERVER_VERSION ?? "unknown",
        deploymentId: process.env.DEPLOYMENT_ID ?? "local",
      });
      throw err;
    }
  };
}
  1. Implement end-to-end correlation Generate a session ID at the AI platform layer. Pass it through the MCP client to the server via request headers. Include it in every audit entry and forward it to enterprise platform API calls.
  2. Choose immutable storage Write audit logs to a storage system that enforces immutability — S3 with Object Lock, Azure Immutable Blob, or a write-only database role. Never use the same storage as your application data.
  3. Set regulatory retention periods Configure retention based on your compliance requirements — 1 year for SOC 2, 6 years for HIPAA, 7 years for financial regulations. Automate archival and deletion at the storage layer.