Skip to content

feat: Streamable HTTP transport with SSE notifications and E2E tests - #61

Merged
its-mash merged 13 commits into
mainfrom
feat-streamable-http
Feb 14, 2026
Merged

feat: Streamable HTTP transport with SSE notifications and E2E tests#61
its-mash merged 13 commits into
mainfrom
feat-streamable-http

Conversation

@its-mash

@its-mash its-mash commented Feb 10, 2026

Copy link
Copy Markdown
Member

Summary

  • Streamable HTTP transport support: Full MCP Streamable HTTP transport implementation for the gateway, replacing legacy SSE-only transport
  • SSE notification pipeline: Server-to-client notifications via SSE streams with content-based deduplication (hash-based), throttling, and batch suppression in McpNotifier
  • Shadow channel fix (rmcp SDK): Patched upstream rmcp SDK to prevent idle SSE reconnect streams from stealing notifications — shadow channels now only receive keep-alive pings, not real messages. Dead primary channels are auto-replaced on next GET. rust-sdk PR #660
  • Process stderr capture: Server stderr streams are captured and forwarded to the log manager for visibility in the desktop UI
  • Logging middleware: Enhanced header logging with last-event-id tracked for debugging SSE resume behavior
  • Credential store fix: Added missing granted_scopes field in test initializers for StoredCredentials

Notification test coverage

Rust integration tests (tests/rust/)

  • streamable_http/notifications.rs (8 tests): Session management, tools/list_changed / prompts/list_changed / resources/list_changed delivery, multi-client isolation, protocol negotiation
  • streamable_http/gateway_notifications.rs (9 tests): Gateway-level notification pipeline — capabilities, forwarding, content-based dedup, throttle window, space isolation, server features refresh
  • gateway/stdio_transport.rs: stdio transport integration tests

rmcp SDK tests (rust-sdk/)

  • test_sse_channel_replacement_bug.rs (15 tests): Shadow creation, dead primary replacement, notification routing, Last-Event-ID resume paths, mixed reconnect scenarios, edge cases

Desktop E2E tests (tests/e2e/)

  • streamable-http.wdio.ts (11 test cases TC-SH-001 through TC-SH-015): Full end-to-end notification pipeline through the Tauri desktop app — server lifecycle, tool/prompt/resource list_changed, gateway restart resilience

Test plan

  • pnpm validate (cargo fmt + clippy + check + eslint + typecheck)
  • TypeScript tests (133 passed)
  • Rust unit tests — mcpmux-core (113 passed), mcpmux-gateway (62 passed)
  • rmcp SDK tests (138 lib tests + 15 shadow channel tests passed)
  • CI: Full workspace cargo clippy + nextest
  • CI: Desktop E2E tests (Windows/macOS/Linux)

🤖 Generated with Claude Code

Mohammod Al Amin Ashik and others added 3 commits February 8, 2026 19:17
…otifications

Add two layers of test coverage for the Streamable HTTP transport:

Rust integration tests (gateway_notifications.rs):
- Gateway capabilities advertisement (listChanged: true)
- Tools/prompts/resources list_changed forwarding to clients
- Server disconnect notification propagation
- Grant change notification delivery
- Content-based deduplication preventing spurious notifications
- Time-based throttling coalescing rapid notifications
- Server features refresh triggering notifications
- Full re-fetch cycle after notification

Tauri E2E tests (streamable-http.wdio.ts):
- Gateway Streamable HTTP endpoint serving
- Backend HTTP transport connection
- Stub server control endpoint verification
- All three notification types from backend
- Dynamic tool add/remove with notifications
- Rapid successive notification handling
- Server disable/re-enable reconnection cycle
- OAuth DCR registration and approval
- PKCE token exchange flow
- Authenticated MCP initialize with capabilities check
- Session management via Mcp-Session-Id header

Supporting infrastructure:
- Stub MCP server control endpoints for triggering notifications
- E2E helpers for OAuth flow and stub server control
- Enhanced transport-level notification tests (prompts, resources)

Signed-off-by: Myko <myko@mcpmux.com>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
Comment thread tests/e2e/specs/streamable-http.wdio.ts Fixed
Comment thread tests/e2e/specs/streamable-http.wdio.ts Fixed
its-mash and others added 8 commits February 10, 2026 17:48
Remove unused imports flagged by code review:
- createClient, listClients, listFeatureSetsBySpace,
  grantFeatureSetToClient from tauri-api
- waitForGateway from mcp-client

Signed-off-by: Myko <myko@mcpmux.com>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
Log response status and body before assertions to debug failures.
Read response as text first, then parse JSON, so CI output shows
the actual error returned by the gateway.

Signed-off-by: Myko <myko@mcpmux.com>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
The gateway returns 406 Not Acceptable when the Accept header doesn't
include both application/json and text/event-stream, as required by
the MCP Streamable HTTP transport spec (2025-03-26).

Add 'Accept: application/json, text/event-stream' to all fetch calls
in TC-SH-014 and TC-SH-015 E2E tests.

Signed-off-by: Myko <myko@mcpmux.com>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
Per the MCP Streamable HTTP spec (2025-03-26), when a POST contains
JSON-RPC requests, the server may respond with either Content-Type:
application/json or text/event-stream. The client MUST support both.

Add parseMcpResponse() helper that checks the Content-Type header and
extracts JSON from SSE data: lines when the gateway returns an SSE
stream instead of plain JSON.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
Three fixes for the SSE notification pipeline:

1. logging_middleware: Skip body.collect() for text/event-stream responses.
   The middleware was buffering infinite SSE streams, blocking forever and
   preventing VS Code from receiving any SSE events (notifications, pings).

2. mcp_notifier: Add `force` parameter to notify_all_list_changed().
   Grant/feature-set events now bypass content-based hash dedup since the
   hash is computed from all features in the space and can't detect
   per-client grant changes that alter effective visibility.

3. Upgrade rmcp to 0.15.0 from fork with SSE channel replacement fix
   (409 Conflict on duplicate GET streams) and adapt to new struct fields
   (granted_scopes, Default trait changes).

Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
- Update rmcp to latest commit with comprehensive SSE shadow channel
  tests (15 tests covering reconnect, dead primary replacement,
  notification routing, resume paths, and edge cases)
- Add last-event-id to non-redacted headers in logging middleware
- Fix missing granted_scopes field in credential_store test initializers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
@its-mash its-mash changed the title feat: streamable http feat: Streamable HTTP transport with SSE notifications and E2E tests Feb 14, 2026
its-mash and others added 2 commits February 14, 2026 15:50
The integration test crate pinned rmcp to 0.14.0 while the workspace
patch targets 0.15.0, causing CI to use the unpatched crates.io version
which lacks the StoredCredentials::granted_scopes field and has
incompatible ServerHandler trait bounds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
Updates rmcp to use 404 Not Found (instead of 401 Unauthorized) for
missing/terminated MCP sessions, per MCP spec. This prevents VS Code
from triggering full OAuth re-authentication on McpMux restart.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
@its-mash
its-mash merged commit ca5b0ff into main Feb 14, 2026
33 of 37 checks passed
@its-mash
its-mash deleted the feat-streamable-http branch February 14, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant