python-sdk icon indicating copy to clipboard operation
python-sdk copied to clipboard

Introduce a shared Transport abstraction to reduce stdio/SSE/WebSocket/HTTP duplication

Open dgenio opened this issue 1 month ago • 0 comments

Summary

The SDK currently implements multiple server and client transports (stdio, SSE, WebSocket, Streamable HTTP). Each one has its own message loop, error handling, and resource management logic with significant duplication.

This makes it harder to:

  • fix bugs consistently across transports,
  • add new transports, and
  • ensure feature parity between them.

A small Transport abstraction could centralize most of the shared behavior.

Problems

  • Duplication: Similar read/write loops and error handling are repeated in:
    • src/mcp/server/stdio.py
    • src/mcp/server/sse.py
    • src/mcp/server/websocket.py
    • src/mcp/server/streamable_http.py
  • Inconsistent behavior: Features like cancellation, error mapping, and shutdown can diverge between transports.
  • Maintenance cost: Adding a new transport or changing session semantics requires updating each implementation.

Proposal

  1. Define a Transport protocol/interface

    For example:

    class Transport(Protocol):
        async def connect(self) -> None: ...
        async def send_message(self, message: JSONType) -> None: ...
        async def receive_message(self) -> JSONType | None: ...
        async def close(self) -> None: ...
    
  • Implementations handle encoding details (JSON lines, SSE, WebSocket frames, HTTP streaming).
  1. Refactor sessions to depend on Transport

    • BaseSession (and server/client sessions) should operate on a Transport instance rather than transport-specific primitives.
    • Transport-agnostic session logic (request routing, error mapping) lives in one place.
  2. Align behavior across transports

    • Standardize behavior for:

      • connection setup/teardown,
      • error handling,
      • cancellation and shutdown semantics.
  3. Incremental migration

    • Start with one or two transports (e.g. stdio + Streamable HTTP) to validate the abstraction.
    • Gradually migrate remaining transports.

Why this matters

  • Consistency: Users can expect the same semantics regardless of transport choice.
  • Easier maintenance: Bug fixes and feature additions happen once, in a shared layer.
  • Future-proofing: Adding new transports becomes easier and less error-prone.

Acceptance criteria

  • [ ] A Transport interface (or equivalent) is defined for server and client usage.
  • [ ] At least two transports are refactored to use the shared abstraction.
  • [ ] Session logic no longer duplicates transport-specific read/write loops.
  • [ ] Tests verify identical semantics (errors, cancellation, shutdown) across transports.

dgenio avatar Nov 28 '25 12:11 dgenio