- syncTools: on paginated listTools failure, unregister any tools already
registered in the current sync before rethrowing (prevents orphans)
- Effect disposer: call client.close() directly without awaiting startup
completion — aborts a hanging connect promptly on HMR/dispose
Prove the full MCP protocol flow works end-to-end against real servers:
- Self-written fixture server: tool discovery, execution, error handling,
image placeholder, toolPrefix, and clean disposal
- @modelcontextprotocol/server-everything: echo, get-sum, get-tiny-image
- @modelcontextprotocol/server-filesystem: write_file + read_file round-trip,
list_directory with world-verification
All 15 tests keyless and deterministic (no API key needed).
Addresses CI coverage gap: exercises the latest-wins resync coalescing
(pendingResync branch), the client.onclose callback, and ensures index.ts
is loaded without module mocks for stable v8 coverage across environments.
- Add client.onclose handler to unregister tools when the MCP server
disconnects (crash or intentional close)
- Replace bare `let disposers` with a shared mutable state object so the
effect disposer and notification handler always reference the current
generation
- Serialize tools/list_changed resyncs with latest-wins coalescing
(syncing + pendingResync flags) to prevent concurrent races
Connects to an external MCP server and registers its tools on
ctx.tools. Supports stdio (child process) and Streamable HTTP
transports. Credential-shaped env vars are scrubbed before forwarding
to child processes.
- Plugin lifecycle: connect, sync tools, re-sync on ToolListChanged,
dispose unregisters and closes
- Full JSDoc on all exports (@param/@returns on functions)
- 100% per-file coverage (apply lifecycle, args coercion, env scrubbing)
- Config catalog regenerated