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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions