Skip to content

feat: Add distributed EventStore implementations (Redis, PostgreSQL) for production deployments #2570

@Kevin990001

Description

@Kevin990001

Problem

The EventStore abstract class in streamable_http.py enables SSE stream resumability, but the only available implementation is the in-memory SimpleEventStore in the test suite. This works for single-process development but breaks in any realistic production deployment:

  • Kubernetes / multi-replica: Each pod has its own in-memory state. A client reconnecting after a pod restart or load-balancer reroute to a different replica will find no events to replay — resumability silently fails.
  • Process restarts: Any server restart drops all stored events, making Last-Event-ID reconnection useless.
  • Horizontal scaling: As agent workloads scale out, there's no way to share event history across instances.

Proposed Solution

Add two concrete EventStore implementations as optional extras:

RedisEventStore (mcp[redis])

  • Store events as Redis Lists per stream_id with configurable TTL
  • store_event: RPUSH + EXPIRE
  • replay_events_after: LRANGE + filter by event ID
  • Uses redis-py async client with connection pooling
  • Suitable for high-throughput, low-latency workloads

PostgresEventStore (mcp[postgres])

  • Store events in a simple mcp_events(stream_id, event_id SERIAL, message JSONB, created_at) table
  • replay_events_after: indexed query WHERE stream_id = ? AND event_id > ?
  • Uses asyncpg
  • Suitable for workloads needing durability or auditability

Both implementations would:

  • Be tested with testcontainers (real Redis/Postgres containers, no mocks)
  • Include TTL/cleanup configuration to prevent unbounded growth
  • Live under src/mcp/server/contrib/ or a similar location — open to maintainer preference on placement

Why Now

The resumability feature was added relatively recently and the EventStore interface is clean and well-defined. Adding distributed backends is the natural next step before production adoption grows and this becomes a pain point.

Before I Start Coding

  • Is there a preferred location for optional contrib implementations?
  • Any preference on redis-py vs coredis for the Redis client?
  • Should these ship as optional extras in pyproject.toml (mcp[redis]) or as a separate package?

Happy to start with Redis only and follow up with Postgres in a second PR if that's easier to review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions