Skip to content

SwiftMCP 1.10.4

Latest

Choose a tag to compare

@odrobnik odrobnik released this 14 Aug 11:33
ad39954

Resource subscriptions now survive a session reset: MCPServerProxy remembers what it subscribed to and replays it after any re-initialize, so a reconnected client keeps receiving resources/updated without every application writing its own resubscribe dance. (#183, fixes #179)

🔁 Subscriptions are replayed after a re-initialize (#183, fixes #179)

MCP resource subscriptions are per-session server state — they live in the session and evaporate with it, on server restart, session expiry, or any re-initialize. That is correct per spec, and it makes replay inherently a client duty. MCPServerProxy kept no record of what it had subscribed to, so every consumer had to implement its own reconnect-and-resubscribe dance, and forgetting produced a silent failure mode: a client that looks perfectly healthy — POSTs succeed, pings succeed — but never receives another notifications/resources/updated. That is the same symptom 1.10.3 fixed from the server side, reached from the other end.

  • subscribeResource(uri:) records the URI, unsubscribeResource(uri:) drops it, and every successful initialize replays the recorded set before the connection reports ready.
  • The URI is recorded after the server accepts the request, not before. Inserting on the way in would leave a phantom entry behind a failed subscribeResource — a disconnected proxy, say — to be silently replayed on every later reconnect, so the recorded set only ever reflects confirmed server-side subscriptions.
  • A replay failure is logged per URI and does not abort the handshake. One resource that will not resubscribe should not cost you the connection.
  • disconnect() clears the set: an intentional teardown is a new logical client, not a session to restore.

Still the application's job: the proxy replays subscriptions, not reads. Only the app knows which resources it must also re-fetch to resync state across the gap, so a re-initialize should still be followed by whatever seeding it does on first connect.

🧪 Tests

SubscriptionReplayTests covers the set management (subscribe adds, unsubscribe removes, disconnect() clears) and the end-to-end path over HTTP: connect, subscribe, reconnect without disconnecting to simulate session recovery, wait for the new session's general stream, then broadcast — and assert the notification arrives on the strength of the replayed subscription alone.