Initial implementation of TypeScript SDK#13
Conversation
|
Here's a video with an overview and some demos: https://youtu.be/1El_XjABrbA |
jeongukjae
left a comment
There was a problem hiding this comment.
this looks really nice! thanks a lot!
Return processed list responses from InterceptingMcpClient; use MCP -32602 for unknown interceptors; add gateway name→client routing cache with invalidation on invalid-params; parallelize interceptors/list across hosts; fix defineInterceptor destructuring to receive full invoke params.
jeongukjae
left a comment
There was a problem hiding this comment.
Looks solid for me. Thanks a lot!
|
@BobDickinson this has been approved-but-conflicting for a few weeks, and I would like to help get it in. Happy to take a pass at the conflict resolution and send it to your branch (PR-to-your-fork or a plain diff, whichever you prefer). Motive is simple: I have been building conformance fixtures over on #20 and want a merged mainline TypeScript SDK as the canonical target to certify against. |
# Conflicts: # typescript/sdk/src/index.ts # typescript/sdk/src/interceptors.ts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 'enforce' legacy-read-only) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Aligns with sep.md (PR modelcontextprotocol#25) and the C# SDK: capability now lives at capabilities.extensions["io.modelcontextprotocol/interceptors"] instead of a top-level 'interceptor' key. The extensions record is typed in the v1 SDK's ServerCapabilitiesSchema, so the capability now also survives stock v1 Client initialize parsing (the old key was silently stripped). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ion semantics - ChainInterceptorEntry with per-entry InterceptorOverrides (failOpen, priorityHint, mode, timeoutMs, hook narrowing); widening overrides are rejected with InterceptorOverrideHookError per the SEP MUST. - resolvePriority takes override precedence (overrides.priorityHint ?? interceptor.priorityHint). - Chain timeout is now actually enforced against real interceptor hosts: the orchestrator's signal is threaded through invokeInterceptor into client.request, with already-aborted signals short-circuited. - Per-interceptor timeoutMs aborts just that invoke and routes through resolved failOpen; the chain-aggregate timeout maps to status 'timeout'. - Caller cancellation now rejects with the abort reason instead of being misreported as chain status 'timeout'. - All validations complete before rejecting; blocked chains aggregate every validation result and summary count (SEP aggregation rule). - Per-interceptor config from ExecuteChainRequestParams.config is forwarded to interceptor/invoke (was silently dropped). - AbortSignal.any fallback for Node 20.0-20.2. - Empty host list is a no-op success chain (enables gateway pass-through). - Overrides plumbed through executeInterceptorChainOnClients options and InterceptorChainRunner options, keyed by interceptor name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… error
The invoke handler now races the interceptor handler against the timeout
signal, so a handler that ignores the signal can no longer hold the request
past timeoutMs. Timeouts surface as JSON-RPC -32000 with
{ interceptor, timeoutMs, phase } data per SEP-2624. The client-side wire
timeout becomes a backstop with headroom so the host's richer error wins.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A mutation that removed fields (e.g. argument redaction) previously had the original arguments silently restored via '?? args' fallbacks, forwarding the exact data the interceptor stripped. Malformed mutated payloads (no string name) now fail loudly instead of silently discarding the mutation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y routing listAllInterceptors drains every page (nextCursor) from a host; chain discovery, the gateway routing table, and the bridge's list aggregation all use it, so interceptors beyond page one can no longer be silently skipped. The bridge also rejects foreign cursors and no longer forwards one host's cursor to every other host. A failed routing-table build is dropped instead of poisoning every subsequent interceptor/invoke with the cached rejection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Resolver-only gateways pass requests through instead of erroring when the resolver returns no connections for a request. - Partial connect failures in the client pool close already-connected owned clients instead of leaking live transports per request. - Resolved-client dispose settles every owned client even if one close throws, and a failing per-request dispose no longer masks the request outcome. - Backend notifications/resources/updated and notifications/message are now relayed to proxied clients (subscribe was a silent end-to-end no-op), and tools/call progress is relayed under the caller's progressToken. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…overrides
The package is ESM-only ('type': 'module'); the 'require' condition pointed
at the ESM build and produced ERR_REQUIRE_ESM at runtime for CJS consumers.
CI now uses npm ci against the committed lockfile.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Pushed a batch of commits on top of @BobDickinson's work to unblock this and merge. Bob's original commits are untouched; everything lands as additive commits. Suite is green: 91 tests (up from 74), typecheck, lint, and build. Merge + SEP alignment (main moved under this PR — #17, #21, #24, #25):
Correctness fixes found in deep review:
Not addressed (follow-ups): sink semantics should be re-checked against #28 once that merges; reflection param-name binding under minification; pool eviction for dead cached clients; descriptor caching for the transparent path. 🤖 Generated with Claude Code |
Initial implementation of MCP Interceptor TypeScript SDK
Checklist
Additional context
See typescript/sdk/docs/design-and-implementation.md for design rationale and implementation details.