Skip to content

[RUST]: Rewrite wrapper module in Rust #1618

@crivetimihai

Description

@crivetimihai

Summary

Rewrite the wrapper module in Rust - the HTTP client bridge that forwards stdio JSON-RPC requests to remote HTTP endpoints, parsing streaming responses (SSE, NDJSON) with incremental UTF-8 decoding.

Current Implementation

File: mcpgateway/wrapper.py

The wrapper module handles:

  • HTTP client requests to remote MCP endpoints
  • Streaming response parsing (SSE, NDJSON)
  • Incremental UTF-8 decoding
  • JSON-RPC message forwarding

Rust Benefits

Aspect Python Rust
Stream parsing Per-chunk overhead Native zero-copy
UTF-8 decoding Python string alloc In-place with bytes
Message roundtrip ~5ms overhead ~0.5ms overhead
Buffer management O(n²) string concat O(1) with BytesMut

Key Improvements

  1. Native stream parsing with nom/bytes eliminates per-chunk Python overhead
  2. Zero-copy buffer management using BytesMut instead of Python string concatenation
  3. Significant savings per message roundtrip - reduced latency for every MCP call
  4. Efficient incremental UTF-8 handling without Python string allocations

Proposed Architecture

┌─────────────────────────────────────────────────────────┐
│                    Rust Core (PyO3)                      │
├─────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
│  │ HTTP Client │  │ SSE Parser  │  │  NDJSON Parser  │  │
│  │  (reqwest)  │  │ (nom/bytes) │  │  (serde_json)   │  │
│  └─────────────┘  └─────────────┘  └─────────────────┘  │
├─────────────────────────────────────────────────────────┤
│                    Python Interface                      │
│      (async def forward_request(...) -> Response)       │
└─────────────────────────────────────────────────────────┘

Current Performance Issues

From todo/newperf/optimize-stream-parser-buffers.md:

# Current O(n²) pattern in wrapper.py
buffer = ""
async for chunk in resp.aiter_bytes():
    buffer += text  # O(n) - creates new string!
    buffer = buffer[nl_idx + 1:]  # O(n) - creates new string!

Rust eliminates this entirely with BytesMut::split_to().

Implementation Approach

Phase 1: Stream Parsers

  • Implement SSE parser with nom combinators
  • Implement NDJSON parser with streaming serde_json
  • Create BytesMut-based buffer management

Phase 2: HTTP Client

  • Build async HTTP client with reqwest
  • Add connection pooling and keep-alive
  • Implement streaming response handling

Phase 3: PyO3 Integration

  • Expose as async Python module
  • Return Python async iterators for streaming
  • Maintain httpx-compatible interface

Crates to Use

  • reqwest - HTTP client
  • tokio - async runtime
  • bytes - buffer management
  • nom or winnow - stream parsing
  • serde_json - JSON handling
  • pyo3 + pyo3-asyncio - Python bindings

Acceptance Criteria

  • Stream parsing is O(n) not O(n²)
  • Memory usage bounded regardless of response size
  • Message roundtrip latency < 1ms overhead
  • Compatible with existing wrapper API
  • Passes existing wrapper tests
  • Supports SSE, NDJSON, and chunked responses

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    performancePerformance related itemsrustRust programming

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions