Architecture·Audit Logging·CQRS

MCP server event sourcing security: append-only audit logs, event replay attacks, and CQRS authorization

Event sourcing — storing system state as an immutable sequence of domain events rather than current-state snapshots — is a natural fit for MCP server audit requirements. Every tool invocation, every authorization decision, every state change is recorded as an event. But event sourcing introduces security considerations of its own: the event store becomes a high-value target, replay attacks become a concern, and CQRS read/write separation creates authorization split points that must be secured consistently.

The append-only audit log: why write-delete must be impossible from application code

An event store's security property is its immutability: events that are appended cannot be modified or deleted. This property only holds if the application code that writes events cannot also delete them. Two implementation patterns preserve immutability: (1) separate the write path (append events) from the delete path (which should not exist in application code at all) using database-level permissions — the application's database user has INSERT but not DELETE or UPDATE on the events table; (2) use a dedicated event store service (EventStoreDB, Kafka with log compaction disabled, AWS Kinesis with data retention) that provides an append-only API with no delete endpoint exposed to application code.

Log retention policy must also be managed separately from application code. Retention is a compliance decision (90 days, 1 year, 7 years depending on regulatory framework) enforced at the infrastructure level, not via application logic that could be triggered by a compromised process.

Event replay attacks

Event replay is a legitimate feature in event-sourced systems — replaying events to rebuild state is how read models are projected. But if tool invocations are themselves events that trigger side effects (send email, charge payment, delete record), replaying those events without guards will re-execute the side effects. An attacker who can inject or replay events can cause actions to execute multiple times.

Defense: separate events into two categories — state events (what happened, safe to replay) and command events (what to do, must be idempotent or replay-blocked). For command events, store an idempotency key in the event and check it before executing side effects. Use a deduplicated execution log keyed by event ID — if an event ID has already produced a side effect, skip execution. Event IDs must be globally unique (UUIDs v7 or similar) and generated by the server, never supplied by the caller.

CQRS command/query authorization split

Command Query Responsibility Segregation (CQRS) separates the write model (commands that change state) from the read model (queries that return current state). This split is useful for MCP servers because it mirrors the authorization distinction between tools that write and tools that read — different callers may be authorized for reads but not writes. The security risk: if the CQRS split is implemented as two separate codepaths with separate authorization logic, there's a risk of the two getting out of sync. A permission change that updates the command path authorization may not update the query path.

Best practice: derive both command and query authorization from the same policy definition. Use a centralized authorization service or policy library (OPA, Cedar, Casbin) and call it from both command handlers and query handlers, rather than maintaining parallel authorization logic in two places.

Event store access control

The event store contains a complete history of everything that happened in your MCP server — every tool call, every credential use, every piece of data returned. This makes it an extremely high-value target: an attacker who reads the event store gets not just current state but historical state and patterns of use. Access control requirements: the event store database should be accessible only from within the application's private network segment. Application-level read access to the event store should be granted only to the projection service, not to tool handlers. Admin access to the event store should require a separate service account with multi-factor authentication.

Snapshot security

Event-sourced systems often create periodic snapshots to avoid replaying the entire event log on startup. A snapshot is a serialized representation of aggregate state — if it contains sensitive data (user PII, credentials, financial data), it must be encrypted at rest with the same controls applied to the event store. Snapshot storage must also be append-only or versioned: a corrupted or tampered snapshot that bypasses the event log's integrity guarantees could cause an aggregate to rebuild incorrect state.

What SkillAudit checks for event sourcing security

SkillAudit's Security and Documentation axes flag event sourcing security issues:

Analyze your MCP server's event sourcing security → SkillAudit detects append-only violations, replay attack risks, and CQRS authorization drift