fix(network-intercept): evict session cache on HTTP delete#764
Conversation
Register the HTTP session-delete lifecycle hook with the network interceptor cache so deleted streamable HTTP sessions drop their per-session rule state instead of retaining stale NetworkInterceptor instances. Constraint: HTTP transport already emits session deletion but the MCP transport abstraction did not expose that lifecycle hook. Rejected: Static import from mcp-server to network-intercept | it would introduce a stronger circular dependency because network-intercept already imports mcp-server for client resolution. Confidence: high Scope-risk: narrow Directive: Keep transport-owned lifecycle cleanup optional so stdio remains stateless and unaffected. Tested: npx jest --runTestsByPath tests/unit/network-intercept-tool.test.ts tests/unit/mcp-server-session-cleanup.test.ts --runInBand; npx eslint --max-warnings=0 src/tools/network-intercept.ts src/transports/index.ts src/mcp-server.ts tests/unit/network-intercept-tool.test.ts tests/unit/mcp-server-session-cleanup.test.ts; npx tsc --noEmit --pretty false Not-tested: live HTTP client DELETE against a running MCP server
f75b4a8 to
6a3bf23
Compare
PR Review SummaryVerdictApprove Scope Reviewed
Blocking IssuesNone. WarningsNone. Mutation-Test Thinking
Complexity / CRAP-style Risk
Test Quality Assessment (6/7)
Security / Operational RiskNone. The change reduces long-lived state retention; no auth or external input surface is widened. Looks Good
Final RecommendationApprove. Merge once the pending GitHub build check completes successfully. |
Ready to merge — approve summaryAll required CI checks are green on the latest commit What this PR isThe follow-up to #763 that was split out during the v0.6.1 release (PR #762, Codex P2 #3) because the cleanest fix needed new MCP-server-side session-lifecycle wiring. Issue #763 stayed open with full repro and a proposed fix outline; this PR implements that outline:
The HTTP transport already exposed an Scope / direction review
Why merge is OK
Verification on
|
| Test | What it locks |
|---|---|
evicts only the selected MCP session cache on session deletion |
removeNetworkInterceptorForSession('session-a') returns 1, drops the session-a interceptor (subsequent getNetworkInterceptorForSession('session-a') returns a new instance with no rules), and leaves session-b rules intact. Kills the obvious "delete wrong session" / "no-op delete" mutants. |
reports no eviction for unknown or empty session ids |
Returns 0 for 'missing' and '', doesn't touch existing entries. Kills the "delete everything on empty string" mutant. |
registers network interceptor eviction for transport session deletion (MCPServer) |
Confirms MCPServer.start({ transport: 'http' }) wires onSessionDelete(handler) and that calling handler('session-a') triggers removeNetworkInterceptorForSession('session-a'). Kills "forgot to register" and "called with wrong sessionId" mutants. |
Out-of-scope follow-ups (not blocking this PR)
- Live HTTP DELETE smoke test. Documented gap; unit coverage on the wiring point is appropriate for a narrow lifecycle change.
- Other per-session caches (if any are added later). They can register their own subscribers via the same optional hook.
Approving on the strength of green CI, zero bot findings, the scope-discipline above, and the wiring + cache-isolation tests that lock both the registration point and the eviction semantics.
PR #764: fix(network-intercept): evict session cache on HTTP delete
P0 — Blockers (must fix before merge)None. P1 — Must Fix (should fix in this PR)None. P2 — Improve (can be follow-up)None at the >= 60/100 confidence threshold. Per-axis findings
Wiring proof
End-to-end: a Test coverage delta (+3 unit tests, +1 wiring test)
VerificationGitHub CI on Summary
VerdictAPPROVE. Rationale, in order of weight:
Merge Notes
— |
Summary
MCPTransport.onSessionDeletelifecycle hook through the transport abstraction.removeNetworkInterceptorForSession(sessionId)sonetwork_interceptper-session rule state is evicted when streamable HTTP sessions are deleted.Issue analysis / scope
Open issue #763 is still valid: HTTP transport already has a session delete callback, but
MCPServerdid not register cleanup, so the module-level network interceptor map retained per-sessionNetworkInterceptorinstances afterDELETE /mcp.This PR intentionally keeps the scope narrow:
mcp-server -> network-interceptimport cycle by moving the cache into a side-effect-light module that both layers can share synchronously.Validation
npx jest --runTestsByPath tests/unit/network-intercept-tool.test.ts tests/unit/mcp-server-session-cleanup.test.ts --runInBandnpx eslint --max-warnings=0 src/tools/network-intercept.ts src/tools/network-intercept-cache.ts src/transports/index.ts src/mcp-server.ts tests/unit/network-intercept-tool.test.ts tests/unit/mcp-server-session-cleanup.test.tsnpx tsc --noEmit --pretty falseCloses #763