agentfootprint 9.80.0 → 9.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +20 -0
- package/CHANGELOG.md +109 -0
- package/CLAUDE.md +2 -0
- package/dist/esm/lib/mcp/connectionRefusals.d.ts +30 -0
- package/dist/esm/lib/mcp/connectionRefusals.js +110 -0
- package/dist/esm/lib/mcp/connectionRefusals.js.map +1 -0
- package/dist/esm/lib/mcp/index.d.ts +3 -2
- package/dist/esm/lib/mcp/index.js +4 -0
- package/dist/esm/lib/mcp/index.js.map +1 -1
- package/dist/esm/lib/mcp/mcpClient.d.ts +18 -5
- package/dist/esm/lib/mcp/mcpClient.js +102 -38
- package/dist/esm/lib/mcp/mcpClient.js.map +1 -1
- package/dist/esm/lib/mcp/mcpServe.js +45 -13
- package/dist/esm/lib/mcp/mcpServe.js.map +1 -1
- package/dist/esm/lib/mcp/sdkLoadFailure.d.ts +60 -0
- package/dist/esm/lib/mcp/sdkLoadFailure.js +74 -0
- package/dist/esm/lib/mcp/sdkLoadFailure.js.map +1 -0
- package/dist/esm/lib/mcp/throttleRetry.d.ts +20 -1
- package/dist/esm/lib/mcp/throttleRetry.js +20 -1
- package/dist/esm/lib/mcp/throttleRetry.js.map +1 -1
- package/dist/esm/lib/mcp/transportUrl.d.ts +30 -0
- package/dist/esm/lib/mcp/transportUrl.js +71 -0
- package/dist/esm/lib/mcp/transportUrl.js.map +1 -0
- package/dist/esm/lib/mcp/types.d.ts +143 -13
- package/dist/esm/tool-providers/index.d.ts +2 -2
- package/dist/esm/tool-providers/index.js +6 -1
- package/dist/esm/tool-providers/index.js.map +1 -1
- package/dist/lib/mcp/connectionRefusals.js +114 -0
- package/dist/lib/mcp/connectionRefusals.js.map +1 -0
- package/dist/lib/mcp/index.js +6 -1
- package/dist/lib/mcp/index.js.map +1 -1
- package/dist/lib/mcp/mcpClient.js +102 -38
- package/dist/lib/mcp/mcpClient.js.map +1 -1
- package/dist/lib/mcp/mcpServe.js +45 -13
- package/dist/lib/mcp/mcpServe.js.map +1 -1
- package/dist/lib/mcp/sdkLoadFailure.js +78 -0
- package/dist/lib/mcp/sdkLoadFailure.js.map +1 -0
- package/dist/lib/mcp/throttleRetry.js +20 -1
- package/dist/lib/mcp/throttleRetry.js.map +1 -1
- package/dist/lib/mcp/transportUrl.js +75 -0
- package/dist/lib/mcp/transportUrl.js.map +1 -0
- package/dist/tool-providers/index.js +6 -1
- package/dist/tool-providers/index.js.map +1 -1
- package/dist/types/lib/mcp/connectionRefusals.d.ts +31 -0
- package/dist/types/lib/mcp/connectionRefusals.d.ts.map +1 -0
- package/dist/types/lib/mcp/index.d.ts +3 -2
- package/dist/types/lib/mcp/index.d.ts.map +1 -1
- package/dist/types/lib/mcp/mcpClient.d.ts +18 -5
- package/dist/types/lib/mcp/mcpClient.d.ts.map +1 -1
- package/dist/types/lib/mcp/mcpServe.d.ts.map +1 -1
- package/dist/types/lib/mcp/sdkLoadFailure.d.ts +61 -0
- package/dist/types/lib/mcp/sdkLoadFailure.d.ts.map +1 -0
- package/dist/types/lib/mcp/throttleRetry.d.ts +20 -1
- package/dist/types/lib/mcp/throttleRetry.d.ts.map +1 -1
- package/dist/types/lib/mcp/transportUrl.d.ts +31 -0
- package/dist/types/lib/mcp/transportUrl.d.ts.map +1 -0
- package/dist/types/lib/mcp/types.d.ts +143 -13
- package/dist/types/lib/mcp/types.d.ts.map +1 -1
- package/dist/types/tool-providers/index.d.ts +2 -2
- package/dist/types/tool-providers/index.d.ts.map +1 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -112,6 +112,26 @@ Transports: `stdio` (local subprocess), `http` (Streamable HTTP). The
|
|
|
112
112
|
`@modelcontextprotocol/sdk` peer-dep is lazy-required — zero runtime
|
|
113
113
|
cost when MCP isn't used. Friendly install hint if missing.
|
|
114
114
|
|
|
115
|
+
**In a BROWSER** the Node `require` loader does not exist, so supply what it
|
|
116
|
+
would have found. Give the two SDK modules as `sdk` (the library still builds
|
|
117
|
+
the transport, so headers / your own `fetch` / gateway vending / throttle retry
|
|
118
|
+
all keep working), or a client you connected yourself as `connection`:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
122
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
123
|
+
|
|
124
|
+
const sidecar = await mcpClient({
|
|
125
|
+
name: 'sidecar',
|
|
126
|
+
sdk: { Client, StreamableHTTPClientTransport },
|
|
127
|
+
transport: { transport: 'http', url: '/py/mcp' }, // relative resolves against the page
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`stdio` spawns a subprocess and is refused in words there. On the `connection`
|
|
132
|
+
arm the library builds no transport, so `retryOnThrottle` / `clientInfo` are
|
|
133
|
+
REFUSED rather than ignored — wrap your own fetch with `retryingFetch` instead.
|
|
134
|
+
|
|
115
135
|
`agent.tools(arr)` is the bulk-register companion to `agent.tool(t)`.
|
|
116
136
|
Pair with `await client.tools()` to register everything an MCP server
|
|
117
137
|
exposes in one builder call. Tool-name uniqueness is still validated
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,115 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [9.81.0] - 2026-08-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **MCP from a browser — because the barrier was never the protocol, it was one
|
|
15
|
+
line of ours.** `mcpClient` loads `@modelcontextprotocol/sdk` through a Node
|
|
16
|
+
`require` loader, and that loader does not exist in a browser bundle. The SDK
|
|
17
|
+
itself is fine: its `client/index.js` and `client/streamableHttp.js` bundle at
|
|
18
|
+
`platform: 'browser'` with **zero** `node:` edges and never pull in
|
|
19
|
+
`client/stdio.js`. So the fix is not to reimplement anything — it is to let the
|
|
20
|
+
caller supply what the library would otherwise have loaded.
|
|
21
|
+
|
|
22
|
+
**`sdk?: McpSdk`** — hand over the two SDK modules, imported statically by your
|
|
23
|
+
own bundler, and the library **still builds the transport**. Everything the
|
|
24
|
+
transport carries keeps working: `headers`, your own `fetch`, gateway
|
|
25
|
+
vending, `retryOnThrottle`, `_meta` ingestion.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
29
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
30
|
+
import { mcpClient } from 'agentfootprint/providers';
|
|
31
|
+
|
|
32
|
+
const sidecar = await mcpClient({
|
|
33
|
+
name: 'sidecar',
|
|
34
|
+
sdk: { Client, StreamableHTTPClientTransport },
|
|
35
|
+
transport: { transport: 'http', url: '/py/mcp' },
|
|
36
|
+
});
|
|
37
|
+
const tools = await sidecar.tools(); // the same readonly Tool[], _meta and all
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**`connection?: McpConnection`** — the full escape hatch: you connect the
|
|
41
|
+
client, the library only adapts its tools. Three methods over JSON-RPC and no
|
|
42
|
+
vendor named, so an SDK `Client`, a fake, or a future fetch-only transport all
|
|
43
|
+
satisfy it. `connect()` is deliberately **absent** from the type: you already
|
|
44
|
+
connected it, and the library never calls it. Reach for this when the library
|
|
45
|
+
must not construct anything — a strict CSP, where the SDK's own
|
|
46
|
+
`jsonSchemaValidator` (reachable only here) is what keeps ajv's `new Function`
|
|
47
|
+
off the page.
|
|
48
|
+
|
|
49
|
+
**The refusals are the design.** On the `connection` arm the library builds no
|
|
50
|
+
transport, so every option consumed INSIDE one is refused at construction,
|
|
51
|
+
naming where the behaviour went — `retryOnThrottle`, `clientInfo`, `transport`,
|
|
52
|
+
`sdk`, `_client`. Accepting a knob that names a behaviour which no longer
|
|
53
|
+
happens is the defect class this release exists to close, not a convenience.
|
|
54
|
+
`signal` IS honoured on both arms: it rides the SDK's request options.
|
|
55
|
+
|
|
56
|
+
- **`retryingFetch` is public** (`agentfootprint/providers`), with
|
|
57
|
+
`ThrottleFetch`. It was `@internal`, and on the `connection` arm — the one a
|
|
58
|
+
browser takes — a caller would otherwise have silently lost the HTTP 429
|
|
59
|
+
handling every Node consumer gets ON by default. Same implementation, applied
|
|
60
|
+
where you build the transport: `fetch: retryingFetch(yourFetch, { maxAttempts: 5 })`.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- **A relative `transport.url` resolves against the page.** `new URL('/py/mcp')`
|
|
65
|
+
throws `TypeError: Invalid URL` — correct in Node, wrong in a browser, where a
|
|
66
|
+
same-origin path is the ordinary way to reach a sidecar (and the way to avoid a
|
|
67
|
+
CORS preflight entirely). It now resolves against `globalThis.location.href`
|
|
68
|
+
when there is one, and in Node refuses **by name**, saying which world it is in
|
|
69
|
+
and to pass an absolute URL. An absolute url takes the identical first branch,
|
|
70
|
+
so Node behaviour has not moved.
|
|
71
|
+
|
|
72
|
+
- **The SDK-load errors stop lying.** Every load sat behind a bare `catch`, which
|
|
73
|
+
cannot tell "the package is absent" from "the LOADER is absent" — so a browser
|
|
74
|
+
that had the SDK installed all along was told to `npm install` it, and nothing
|
|
75
|
+
changed when it did. The failure is now classified at all seven sites (three in
|
|
76
|
+
`mcpClient`, four in `mcpServe`): a resolution failure produces the
|
|
77
|
+
**byte-identical** message every release before this one produced, and anything
|
|
78
|
+
else names the underlying error and the seam that gets past it. `mcpServe` says
|
|
79
|
+
the honest thing instead — it listens on stdio or a Node socket, so that
|
|
80
|
+
direction cannot run in a browser at all.
|
|
81
|
+
|
|
82
|
+
### Unchanged, deliberately
|
|
83
|
+
|
|
84
|
+
- **Zero packaging change.** No new subpath, no `browser` export condition (a
|
|
85
|
+
compiler is blind to it), no `typesVersions` row, no `postbuild-esm.mjs` edit.
|
|
86
|
+
`@modelcontextprotocol/sdk` stays an OPTIONAL peer, and there is no literal
|
|
87
|
+
dynamic `import()` of it anywhere — one would be statically resolved by a
|
|
88
|
+
bundler and would hard-fail the build for every consumer who does not have it.
|
|
89
|
+
|
|
90
|
+
- **Every existing Node consumer.** A call with neither `sdk` nor `connection`
|
|
91
|
+
reaches the same `lazyRequire` on the same specifiers and builds the same
|
|
92
|
+
transport; the difference is a `??` on an `undefined` parameter. `stdio` keeps
|
|
93
|
+
the loader permanently — it spawns a subprocess, so it can never be portable,
|
|
94
|
+
and keeping it there is what keeps the SDK's one Node-importing client module
|
|
95
|
+
off every browser graph.
|
|
96
|
+
|
|
97
|
+
### Proof, and its limits
|
|
98
|
+
|
|
99
|
+
`test/lib/mcp/browserGraph.test.ts` bundles the SHIPPED `dist/` the way a browser
|
|
100
|
+
build does and asserts: `agentfootprint/providers` still bundles with the MCP SDK
|
|
101
|
+
**blocked at resolve time** (the optional-peer property, stated as a build); its
|
|
102
|
+
`node:` edges are EXACTLY the two known ones; the builtins hidden behind
|
|
103
|
+
`lazyRequire` — which no module graph can see — are exactly the four known ones;
|
|
104
|
+
the path a browser walks reaches `node:module` and nothing else and never pulls
|
|
105
|
+
in `client/stdio.js`; and everything on that path except the loader bundles with
|
|
106
|
+
NO externals and zero node edges. `mcpConnection.real.test.ts` drives both new
|
|
107
|
+
arms through a real socket against the real SDK.
|
|
108
|
+
|
|
109
|
+
**What none of that proves: a browser.** This repo has no browser test
|
|
110
|
+
environment, so nobody has yet driven initialize/listTools/callTool from an
|
|
111
|
+
actual page. The honest status is *proven in Node, fenced at the graph, not gated
|
|
112
|
+
in a browser.* Three costs land on the app, not here: your server must send CORS
|
|
113
|
+
headers (every MCP request preflights, and `Mcp-Session-Id` must be in
|
|
114
|
+
`Access-Control-Expose-Headers`) — `mcpServe` sends none; SSE through a dev or
|
|
115
|
+
production proxy is unproven; and the SDK's client path adds roughly 260 KB
|
|
116
|
+
minified, about half of it ajv, whose `new Function` needs `unsafe-eval` the
|
|
117
|
+
first time a tool with an `outputSchema` is validated.
|
|
118
|
+
|
|
10
119
|
## [9.80.0] - 2026-08-30
|
|
11
120
|
|
|
12
121
|
### Added
|
package/CLAUDE.md
CHANGED
|
@@ -18,6 +18,8 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
|
|
|
18
18
|
| If you are about to build… | It is | Where | Since |
|
|
19
19
|
|---|---|---|---|
|
|
20
20
|
| turning a written operational procedure (a runbook, a triage playbook) into ONE agent tool whose every answer is EVIDENCE — coverage folded up from the inner tools it calls, rule name+version, verdict rows with GENERATED meanings, and the recorded walk as an artifact ticket, never bytes | `runbookAsTool` — dials: `procedure` (factory, invoked per call with `ctx.tools`) + `resultKind` (`'verdict/*'` arms the rowset projection; anything else ships spine + the chart's `report`) + `rules {name, version}` (default absent ⇒ `rule_version: 'undeclared'`) + `verdicts {decider, maxRows}` (default 50) + `presentation` (`'prose'` default = ship `table` + `VERDICT_RENDER_NOTE` "output it VERBATIM"; `'panel'` = the HOST renders the rowset, so NO `table` key at all + `PANEL_RENDER_NOTE` "the rows are already on the reader's screen — do not reproduce them"; the rowset half is byte-identical across modes, `table` stays RESERVED in both, an unknown value THROWS at definition) + `walk {cap, recording}` (cap default 500; over-cap ⇒ control-flow projection, declared · `recording: true` or `{label, maxBytes}`, default OFF — ALSO files the inner chart's own `{snapshot, events, structure}` under `recording/run` and puts its ref on the spine as `walk.recording_ref`, because the ROW projection cannot be drawn; snapshot read from the REDACTED mirror so one `redact` means the same for both; over `maxBytes` (`DEFAULT_RECORDING_MAX_BYTES` = 5,000,000) it is REFUSED not truncated; every absence — no store / over size / unserializable / store threw — is STATED in `walk.recording_note`, and the four `recording_*` fields are absent entirely when the dial is off) + `composedOf` (drift-checked at agent BUILD) + kept `recorders`/`keepRecord`/`keepRecordLimit`/`redact`; reserved state keys `verdicts`/`coverage`/`report` (the `report` bag lands BESIDE the spine, never over it — a report field spelling `af_coverage`/`af_provenance`/`rule_version`/`walk`/`report_note` or a live projection key is discarded and NAMED in `result.report_note`; precedence is explicit in `report.ts`, never spread order); inner `absent()` passes through VERBATIM unless the call said `allowAbsent`; walk kind `recording/chart-walk` + `walk_segment` discriminant | `src/core/runbook/` | 9.76.0, recording 9.79.0 |
|
|
21
|
+
| speaking MCP from a BROWSER — the SDK is browser-clean (its client + streamableHttp bundle at `platform:'browser'` with zero `node:` edges and never pull in `client/stdio.js`); the one barrier was that `lazyRequire` gets CALLED there, so the fix is to let the caller supply what the loader would have found | `mcpClient({ sdk })` (`McpSdk` = the two SDK modules you imported statically; the library STILL builds the transport, so headers/fetch/gateway vending/`retryOnThrottle`/`_meta` all keep working) · `mcpClient({ connection })` (`McpConnection` = listTools/callTool/close, deliberately NO `connect` — you already did; the only arm that reaches the SDK's `jsonSchemaValidator`, i.e. CSP) + `McpConnectionOptions`; `refuseConflictingOptions` REFUSES at construction every option a transport would have consumed, naming where it moved (a knob naming a behaviour that no longer happens is the defect class); `transportUrl` resolves a relative `url` against `globalThis.location.href` (absolute takes the identical branch, Node refuses by name); `sdkLoadFailure` classifies the seven load sites so a browser is never told to install a package it has (resolution failure ⇒ byte-identical historical message); `retryingFetch` + `ThrottleFetch` promoted out of `@internal` so the connection arm keeps its 429 handling. stdio keeps `lazyRequire` FOREVER — it spawns a subprocess. ZERO packaging change: no subpath, no `browser` condition (TypeScript is blind to it), peer stays optional (no literal dynamic `import()`). Fenced by `test/lib/mcp/browserGraph.test.ts` | `src/lib/mcp/` | 9.81.0 |
|
|
22
|
+
| calling ANOTHER registered tool from inside a tool's `execute` — composition over the agent's own dispatch map instead of importing the module and building a second query stack | `agentToolDispatch` + `ctx.tools` (ToolDispatch has/call) — sees static and skill-carried tools, NEVER ToolProvider-delivered ones (no build-time list, the 9.72.0 caveat); inner calls get the outer facts with hasArtifacts false and a derived toolCallId, `needs` resolved fail-closed non-interactively, `checkIn` and `wants` tools refused by name, no nested dispatch; declare ingredients via `composedOf` (and `gates` for a pausing procedure) — both travel MCP `_meta` | `src/core/agent/toolDispatch.ts` | 9.76.0 |
|
|
21
23
|
| a tool returning numbers WITH the caveats that make them honest — interval/aggregation grain, is-it-a-counter, when the world was measured, which ground was NOT covered — as typed data the model reads compactly and the record keeps whole | `semantic()` + `tools.semantics_declared` (model sees `semanticsForModel` projection; `coverage` field absorbed by the coverage()/absent() channel) | `src/lib/semantics/` | 9.53.0 |
|
|
22
24
|
| a build gate that refuses a triage/inventory tool that forgot its caveats, by tool name and field name | `checkSemantics` + `defineTool({ resultClass })` (`'triage'`/`'inventory'`, the closed set) + bin `agentfootprint-check-semantics` | `src/lib/semantics/check.ts` | 9.53.0 |
|
|
23
25
|
| an agent that runs out of `maxIterations` mid-task handing back a HALF-SENTENCE as its answer — and nothing saying the budget ran out | `wrapUpAtMaxIterations` (default ON) + `WRAP_UP_INSTRUCTION` + `agent.budget_exhausted` + `stoppedEarly.wrappedUp` | `src/core/agent/stages/wrapUp.ts` | 9.56.0 |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connectionRefusals — the two arms of `mcpClient` refuse to be mixed, at
|
|
3
|
+
* CONSTRUCTION, in words that name where the behaviour went.
|
|
4
|
+
*
|
|
5
|
+
* ── Why refuse rather than ignore ────────────────────────────────────────────
|
|
6
|
+
* `mcpClient({ connection })` hands over a client somebody else built, so this
|
|
7
|
+
* library builds no transport on that arm — and every option consumed INSIDE a
|
|
8
|
+
* transport therefore has nothing to act on. `retryOnThrottle` is the sharp
|
|
9
|
+
* one: it is ON by default, it is consumed by `retryingFetch` around the
|
|
10
|
+
* transport's `fetch`, and accepting it here would leave a caller holding an
|
|
11
|
+
* option that NAMES a behaviour which no longer happens. A knob that lies about
|
|
12
|
+
* what it does is worse than one that is absent, so these throw.
|
|
13
|
+
*
|
|
14
|
+
* The type union refuses the same combinations at compile time
|
|
15
|
+
* (`McpConnectionOptions` declares each of them `?: undefined`). This is the
|
|
16
|
+
* runtime half, and it is not redundant: excess-property checking does not
|
|
17
|
+
* survive a spread, and JavaScript callers have no compiler at all.
|
|
18
|
+
*
|
|
19
|
+
* Pattern: pure guard. Role: Layer-3 tool integration.
|
|
20
|
+
*/
|
|
21
|
+
import type { McpClientOptions, McpConnectionOptions } from './types.js';
|
|
22
|
+
/**
|
|
23
|
+
* Refuse a mixed or malformed options object before anything connects.
|
|
24
|
+
*
|
|
25
|
+
* @param opts what the caller passed, before any defaulting
|
|
26
|
+
* @param name the client's logical name, so a multi-server app knows which one
|
|
27
|
+
* @throws naming the two options that cannot travel together, or the member the
|
|
28
|
+
* connection is missing
|
|
29
|
+
*/
|
|
30
|
+
export declare function refuseConflictingOptions(opts: McpClientOptions | McpConnectionOptions, name: string): void;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connectionRefusals — the two arms of `mcpClient` refuse to be mixed, at
|
|
3
|
+
* CONSTRUCTION, in words that name where the behaviour went.
|
|
4
|
+
*
|
|
5
|
+
* ── Why refuse rather than ignore ────────────────────────────────────────────
|
|
6
|
+
* `mcpClient({ connection })` hands over a client somebody else built, so this
|
|
7
|
+
* library builds no transport on that arm — and every option consumed INSIDE a
|
|
8
|
+
* transport therefore has nothing to act on. `retryOnThrottle` is the sharp
|
|
9
|
+
* one: it is ON by default, it is consumed by `retryingFetch` around the
|
|
10
|
+
* transport's `fetch`, and accepting it here would leave a caller holding an
|
|
11
|
+
* option that NAMES a behaviour which no longer happens. A knob that lies about
|
|
12
|
+
* what it does is worse than one that is absent, so these throw.
|
|
13
|
+
*
|
|
14
|
+
* The type union refuses the same combinations at compile time
|
|
15
|
+
* (`McpConnectionOptions` declares each of them `?: undefined`). This is the
|
|
16
|
+
* runtime half, and it is not redundant: excess-property checking does not
|
|
17
|
+
* survive a spread, and JavaScript callers have no compiler at all.
|
|
18
|
+
*
|
|
19
|
+
* Pattern: pure guard. Role: Layer-3 tool integration.
|
|
20
|
+
*/
|
|
21
|
+
/** The three methods this library calls on a connection. Nothing else. */
|
|
22
|
+
const CONNECTION_METHODS = ['listTools', 'callTool', 'close'];
|
|
23
|
+
/**
|
|
24
|
+
* Refuse a mixed or malformed options object before anything connects.
|
|
25
|
+
*
|
|
26
|
+
* @param opts what the caller passed, before any defaulting
|
|
27
|
+
* @param name the client's logical name, so a multi-server app knows which one
|
|
28
|
+
* @throws naming the two options that cannot travel together, or the member the
|
|
29
|
+
* connection is missing
|
|
30
|
+
*/
|
|
31
|
+
export function refuseConflictingOptions(opts, name) {
|
|
32
|
+
const at = `mcpClient[${name}]`;
|
|
33
|
+
// Read through a permissive view on purpose. The union already narrows these
|
|
34
|
+
// combinations away at compile time, so narrowing here would leave the guard
|
|
35
|
+
// reasoning about a shape it exists precisely to disbelieve — an object built
|
|
36
|
+
// by a spread, or by JavaScript.
|
|
37
|
+
const given = opts;
|
|
38
|
+
if (given['connection'] === undefined) {
|
|
39
|
+
if (given['transport'] === undefined && given['_client'] === undefined) {
|
|
40
|
+
throw new Error(`${at}: nothing to connect to. Pass \`transport\` (the library builds the connection) ` +
|
|
41
|
+
'or `connection` (a client you connected yourself).');
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// From here on the caller chose the `connection` arm.
|
|
46
|
+
for (const [option, moved] of BUILT_BY_THE_TRANSPORT) {
|
|
47
|
+
if (given[option] !== undefined) {
|
|
48
|
+
throw new Error(`${at}: \`connection\` and \`${option}\` cannot travel together. ` +
|
|
49
|
+
`A connection you built yourself carries its own transport, and \`${option}\` is ` +
|
|
50
|
+
`consumed inside the transport this library did not build. ${moved}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
assertConnection(given['connection'], at);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The options that only exist because the library builds the transport, each
|
|
57
|
+
* paired with the sentence naming where that behaviour moved to. Ordered so the
|
|
58
|
+
* most surprising loss — throttle retry, which is ON by default — is named
|
|
59
|
+
* first when a caller passes several.
|
|
60
|
+
*/
|
|
61
|
+
const BUILT_BY_THE_TRANSPORT = [
|
|
62
|
+
[
|
|
63
|
+
'retryOnThrottle',
|
|
64
|
+
'Wrap your own `fetch` with `retryingFetch(yourFetch, options)` (exported from ' +
|
|
65
|
+
'`agentfootprint/providers`) and hand THAT to your transport — it is the same ' +
|
|
66
|
+
'implementation, applied where you build it.',
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
'clientInfo',
|
|
70
|
+
'Pass it to the SDK `Client` constructor instead: `new Client(clientInfo, { capabilities: {} })`.',
|
|
71
|
+
],
|
|
72
|
+
[
|
|
73
|
+
'transport',
|
|
74
|
+
'Drop one of the two: `transport` asks the library to connect, `connection` says it already is.',
|
|
75
|
+
],
|
|
76
|
+
[
|
|
77
|
+
'sdk',
|
|
78
|
+
'`sdk` exists so the library can build the transport without its Node loader; ' +
|
|
79
|
+
'on this arm you have already built it.',
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
'_client',
|
|
83
|
+
'`_client` is the same idea as `connection` and predates it — pass `connection` alone.',
|
|
84
|
+
],
|
|
85
|
+
];
|
|
86
|
+
/**
|
|
87
|
+
* A connection is only a connection if it can be called.
|
|
88
|
+
*
|
|
89
|
+
* This catches the near-miss people actually make: handing over the TRANSPORT
|
|
90
|
+
* rather than the client. A transport has none of these three methods, and
|
|
91
|
+
* without this check it fails on the first `tools()`, one stack frame deep
|
|
92
|
+
* inside the SDK.
|
|
93
|
+
*
|
|
94
|
+
* The OTHER near-miss — a `Client` that was constructed but never `connect()`ed
|
|
95
|
+
* — cannot be caught here, because it has all three methods. It is named in the
|
|
96
|
+
* message anyway, since it produces the same "my connection does not work" and
|
|
97
|
+
* the SDK's own "Not connected" is the thing to look for.
|
|
98
|
+
*/
|
|
99
|
+
function assertConnection(connection, at) {
|
|
100
|
+
const members = (connection ?? {});
|
|
101
|
+
for (const method of CONNECTION_METHODS) {
|
|
102
|
+
if (typeof members[method] !== 'function') {
|
|
103
|
+
throw new Error(`${at}: \`connection\` has no \`${method}()\`. It must be an MCP client that is ` +
|
|
104
|
+
'already connected, and the likely mistake is passing the TRANSPORT instead of the ' +
|
|
105
|
+
'client. (A `new Client(...)` you never awaited `connect()` on passes this check ' +
|
|
106
|
+
'and fails later with the SDK\'s own "Not connected".)');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=connectionRefusals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectionRefusals.js","sourceRoot":"","sources":["../../../../src/lib/mcp/connectionRefusals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,0EAA0E;AAC1E,MAAM,kBAAkB,GAAG,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAEvE;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAA6C,EAC7C,IAAY;IAEZ,MAAM,EAAE,GAAG,aAAa,IAAI,GAAG,CAAC;IAChC,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,iCAAiC;IACjC,MAAM,KAAK,GAAG,IAAoD,CAAC;IAEnE,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,SAAS,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;YACvE,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,kFAAkF;gBACrF,oDAAoD,CACvD,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,sDAAsD;IACtD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,sBAAsB,EAAE,CAAC;QACrD,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,0BAA0B,MAAM,6BAA6B;gBAChE,oEAAoE,MAAM,QAAQ;gBAClF,6DAA6D,KAAK,EAAE,CACvE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,gBAAgB,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,sBAAsB,GAExB;IACF;QACE,iBAAiB;QACjB,gFAAgF;YAC9E,+EAA+E;YAC/E,6CAA6C;KAChD;IACD;QACE,YAAY;QACZ,kGAAkG;KACnG;IACD;QACE,WAAW;QACX,gGAAgG;KACjG;IACD;QACE,KAAK;QACL,+EAA+E;YAC7E,wCAAwC;KAC3C;IACD;QACE,SAAS;QACT,uFAAuF;KACxF;CACF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,gBAAgB,CAAC,UAAmB,EAAE,EAAU;IACvD,MAAM,OAAO,GAAG,CAAC,UAAU,IAAI,EAAE,CAAsC,CAAC;IACxE,KAAK,MAAM,MAAM,IAAI,kBAAkB,EAAE,CAAC;QACxC,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,6BAA6B,MAAM,yCAAyC;gBAC/E,oFAAoF;gBACpF,kFAAkF;gBAClF,uDAAuD,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -9,5 +9,6 @@ export { mcpServe } from './mcpServe.js';
|
|
|
9
9
|
export { MCP_TOOL_EXTRAS_KEY, type McpToolExtras } from './toolExtras.js';
|
|
10
10
|
export { mockMcpClient, type MockMcpClientOptions, type MockMcpTool } from './mockMcpClient.js';
|
|
11
11
|
export { gatewayTransport, GatewayAuthorizationRequiredError, type GatewayTransportOptions, } from './gatewayTransport.js';
|
|
12
|
-
export
|
|
13
|
-
export type {
|
|
12
|
+
export { retryingFetch } from './throttleRetry.js';
|
|
13
|
+
export type { RetryOnThrottle, ThrottleFetch, ThrottleRetryInfo, ThrottleRetryOptions, } from './throttleRetry.js';
|
|
14
|
+
export type { McpCallToolResult, McpClient, McpClientOptions, McpConnection, McpConnectionOptions, McpSdk, McpGatewayTransport, McpHttpTransport, McpStdioTransport, McpTransport, McpSdkClient, McpServeOptions, McpServeHandle, McpServeTransport, McpHttpServeTransport, McpStdioServeTransport, McpSdkServer, } from './types.js';
|
|
@@ -11,4 +11,8 @@ export { mcpServe } from './mcpServe.js';
|
|
|
11
11
|
export { MCP_TOOL_EXTRAS_KEY } from './toolExtras.js';
|
|
12
12
|
export { mockMcpClient } from './mockMcpClient.js';
|
|
13
13
|
export { gatewayTransport, GatewayAuthorizationRequiredError, } from './gatewayTransport.js';
|
|
14
|
+
// The 429 handling `mcpClient({ transport })` applies for you — public since
|
|
15
|
+
// 9.81.0 so the `connection` arm, which builds no transport of its own, can
|
|
16
|
+
// apply the SAME implementation instead of silently going without it.
|
|
17
|
+
export { retryingFetch } from './throttleRetry.js';
|
|
14
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,mBAAmB,EAAsB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAA+C,MAAM,oBAAoB,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,iCAAiC,GAElC,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,mBAAmB,EAAsB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAA+C,MAAM,oBAAoB,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,iCAAiC,GAElC,MAAM,uBAAuB,CAAC;AAC/B,6EAA6E;AAC7E,4EAA4E;AAC5E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -39,14 +39,27 @@
|
|
|
39
39
|
* loads only when a consumer actually constructs a client. Tests
|
|
40
40
|
* inject `_client` and skip the import path entirely.
|
|
41
41
|
*/
|
|
42
|
-
import type { McpClient, McpClientOptions } from './types.js';
|
|
42
|
+
import type { McpClient, McpClientOptions, McpConnectionOptions } from './types.js';
|
|
43
43
|
/**
|
|
44
44
|
* Connect to an MCP server. Returns an `McpClient` that exposes the
|
|
45
45
|
* server's tools as agentfootprint `Tool[]` and a `close()` to tear
|
|
46
46
|
* down the transport.
|
|
47
47
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
48
|
+
* Three ways to get a connection, and they are three because a browser can
|
|
49
|
+
* only take the last two:
|
|
50
|
+
*
|
|
51
|
+
* - `{ transport }` — the library loads the SDK through its Node loader and
|
|
52
|
+
* builds everything. The default, and what every Node consumer already
|
|
53
|
+
* does.
|
|
54
|
+
* - `{ transport, sdk }` — you supply the two SDK modules with static
|
|
55
|
+
* imports; the library still builds the transport, so gateway vending,
|
|
56
|
+
* `retryOnThrottle`, `headers` and your own `fetch` all keep working.
|
|
57
|
+
* - `{ connection }` — you built and connected the client yourself; the
|
|
58
|
+
* library only adapts its tools.
|
|
59
|
+
*
|
|
60
|
+
* @throws when the two arms are mixed, when a `connection` is not one, or when
|
|
61
|
+
* `@modelcontextprotocol/sdk` cannot be loaded (the message says which of
|
|
62
|
+
* "not installed" and "no Node loader here" actually happened), or when the
|
|
63
|
+
* transport fails to connect.
|
|
51
64
|
*/
|
|
52
|
-
export declare function mcpClient(opts: McpClientOptions): Promise<McpClient>;
|
|
65
|
+
export declare function mcpClient(opts: McpClientOptions | McpConnectionOptions): Promise<McpClient>;
|
|
@@ -42,6 +42,9 @@
|
|
|
42
42
|
import { readToolExtras } from './toolExtras.js';
|
|
43
43
|
import { createVendingFetch } from './gatewayTransport.js';
|
|
44
44
|
import { retryingFetch } from './throttleRetry.js';
|
|
45
|
+
import { refuseConflictingOptions } from './connectionRefusals.js';
|
|
46
|
+
import { sdkLoadFailure } from './sdkLoadFailure.js';
|
|
47
|
+
import { transportUrl } from './transportUrl.js';
|
|
45
48
|
import { lazyRequire } from '../lazyRequire.js';
|
|
46
49
|
// Version-less identity. The MCP `clientInfo` field is informational
|
|
47
50
|
// (server logs it); a hardcoded number drifts every release. Consumers
|
|
@@ -55,14 +58,29 @@ const DEFAULT_CLIENT_INFO = {
|
|
|
55
58
|
* server's tools as agentfootprint `Tool[]` and a `close()` to tear
|
|
56
59
|
* down the transport.
|
|
57
60
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
+
* Three ways to get a connection, and they are three because a browser can
|
|
62
|
+
* only take the last two:
|
|
63
|
+
*
|
|
64
|
+
* - `{ transport }` — the library loads the SDK through its Node loader and
|
|
65
|
+
* builds everything. The default, and what every Node consumer already
|
|
66
|
+
* does.
|
|
67
|
+
* - `{ transport, sdk }` — you supply the two SDK modules with static
|
|
68
|
+
* imports; the library still builds the transport, so gateway vending,
|
|
69
|
+
* `retryOnThrottle`, `headers` and your own `fetch` all keep working.
|
|
70
|
+
* - `{ connection }` — you built and connected the client yourself; the
|
|
71
|
+
* library only adapts its tools.
|
|
72
|
+
*
|
|
73
|
+
* @throws when the two arms are mixed, when a `connection` is not one, or when
|
|
74
|
+
* `@modelcontextprotocol/sdk` cannot be loaded (the message says which of
|
|
75
|
+
* "not installed" and "no Node loader here" actually happened), or when the
|
|
76
|
+
* transport fails to connect.
|
|
61
77
|
*/
|
|
62
78
|
export async function mcpClient(opts) {
|
|
63
79
|
const name = opts.name ?? 'mcp';
|
|
64
|
-
|
|
65
|
-
|
|
80
|
+
// Before anything connects: an option that names a behaviour which cannot
|
|
81
|
+
// happen on the chosen arm is a defect, not a preference.
|
|
82
|
+
refuseConflictingOptions(opts, name);
|
|
83
|
+
const connection = await openConnection(opts);
|
|
66
84
|
// Tool cache so consumers calling `.tools()` more than once don't
|
|
67
85
|
// hammer the server. `.refresh()` invalidates it.
|
|
68
86
|
let cache = null;
|
|
@@ -76,9 +94,9 @@ export async function mcpClient(opts) {
|
|
|
76
94
|
// `signal` rides in the SDK's THIRD argument (RequestOptions), not in
|
|
77
95
|
// the request params — see `wrapMcpTool`.
|
|
78
96
|
const listed = opts.signal
|
|
79
|
-
? await
|
|
80
|
-
: await
|
|
81
|
-
return listed.tools.map((t) => wrapMcpTool(name,
|
|
97
|
+
? await connection.listTools(undefined, { signal: opts.signal })
|
|
98
|
+
: await connection.listTools();
|
|
99
|
+
return listed.tools.map((t) => wrapMcpTool(name, connection, t, opts.signal));
|
|
82
100
|
};
|
|
83
101
|
return {
|
|
84
102
|
name,
|
|
@@ -98,25 +116,35 @@ export async function mcpClient(opts) {
|
|
|
98
116
|
return;
|
|
99
117
|
closed = true;
|
|
100
118
|
cache = null;
|
|
101
|
-
await
|
|
119
|
+
await connection.close();
|
|
102
120
|
},
|
|
103
121
|
};
|
|
104
122
|
}
|
|
105
|
-
// ───
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
// ─── Getting a connection ──────────────────────────────────────────
|
|
124
|
+
/**
|
|
125
|
+
* The one funnel every arm passes through, in precedence order: a connection
|
|
126
|
+
* you handed over, the internal test seam, then the library building one.
|
|
127
|
+
*
|
|
128
|
+
* A call with neither `connection` nor `sdk` reaches `resolveClient` with two
|
|
129
|
+
* `undefined` arguments and takes the identical path it always took.
|
|
130
|
+
*/
|
|
131
|
+
async function openConnection(opts) {
|
|
132
|
+
if (opts.connection !== undefined)
|
|
133
|
+
return opts.connection;
|
|
134
|
+
if (opts._client !== undefined)
|
|
135
|
+
return opts._client;
|
|
136
|
+
// `refuseConflictingOptions` has already refused the case where this is
|
|
137
|
+
// absent, so the assertion documents that guarantee rather than assuming it.
|
|
138
|
+
const transport = opts.transport;
|
|
139
|
+
return resolveClient(transport, opts.clientInfo, opts.signal, opts.retryOnThrottle, opts.sdk);
|
|
140
|
+
}
|
|
141
|
+
// ─── SDK construction (lazy require, unless the caller supplied it) ─
|
|
142
|
+
async function resolveClient(transport, clientInfo, signal, retryOnThrottle, sdk) {
|
|
143
|
+
const mod = sdk ?? loadClientModule();
|
|
116
144
|
const client = new mod.Client(clientInfo ?? DEFAULT_CLIENT_INFO, {
|
|
117
145
|
capabilities: {},
|
|
118
146
|
});
|
|
119
|
-
const transportImpl = await buildTransport(transport, retryOnThrottle);
|
|
147
|
+
const transportImpl = await buildTransport(transport, retryOnThrottle, sdk);
|
|
120
148
|
// Same rule as callTool: options ride beside the transport, never inside it.
|
|
121
149
|
if (signal)
|
|
122
150
|
await client.connect(transportImpl, { signal });
|
|
@@ -124,15 +152,42 @@ async function resolveClient(transport, clientInfo, signal, retryOnThrottle) {
|
|
|
124
152
|
await client.connect(transportImpl);
|
|
125
153
|
return client;
|
|
126
154
|
}
|
|
127
|
-
|
|
155
|
+
/** The Node loader path, unchanged — reached only when no `sdk` was supplied. */
|
|
156
|
+
function loadClientModule() {
|
|
157
|
+
try {
|
|
158
|
+
return lazyRequire('@modelcontextprotocol/sdk/client/index.js');
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
throw new Error(sdkLoadFailure(err, {
|
|
162
|
+
notInstalled: 'mcpClient requires @modelcontextprotocol/sdk.\n' +
|
|
163
|
+
' Install: npm install @modelcontextprotocol/sdk\n' +
|
|
164
|
+
' Or pass `_client` for test injection.',
|
|
165
|
+
caller: 'mcpClient',
|
|
166
|
+
specifier: '@modelcontextprotocol/sdk/client/index.js',
|
|
167
|
+
instead: 'Pass `sdk` (the two SDK modules, imported statically) or `connection` ' +
|
|
168
|
+
'(a client you connected yourself).',
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function buildTransport(t, retryOnThrottle, sdk) {
|
|
128
173
|
if (t.transport === 'stdio') {
|
|
174
|
+
// Deliberately NOT served by `sdk`: stdio spawns a subprocess, so it can
|
|
175
|
+
// never run in a browser, and `client/stdio.js` is the SDK's one client
|
|
176
|
+
// module that imports `node:process`/`node:stream`. Keeping it behind the
|
|
177
|
+
// loader is what keeps it off every browser module graph.
|
|
129
178
|
let stdioMod;
|
|
130
179
|
try {
|
|
131
180
|
stdioMod = lazyRequire('@modelcontextprotocol/sdk/client/stdio.js');
|
|
132
181
|
}
|
|
133
|
-
catch {
|
|
134
|
-
throw new Error(
|
|
135
|
-
'
|
|
182
|
+
catch (err) {
|
|
183
|
+
throw new Error(sdkLoadFailure(err, {
|
|
184
|
+
notInstalled: 'mcpClient(stdio) requires @modelcontextprotocol/sdk/client/stdio.js — ' +
|
|
185
|
+
'check that @modelcontextprotocol/sdk is installed at the latest version.',
|
|
186
|
+
caller: 'mcpClient(stdio)',
|
|
187
|
+
specifier: '@modelcontextprotocol/sdk/client/stdio.js',
|
|
188
|
+
instead: 'stdio spawns a subprocess, so it cannot run in a browser at all — ' +
|
|
189
|
+
"reach the server over `transport: { transport: 'http', url }` instead.",
|
|
190
|
+
}));
|
|
136
191
|
}
|
|
137
192
|
return new stdioMod.StdioClientTransport({
|
|
138
193
|
command: t.command,
|
|
@@ -144,14 +199,7 @@ async function buildTransport(t, retryOnThrottle) {
|
|
|
144
199
|
// http + gateway transports both ride Streamable HTTP. They differ only in
|
|
145
200
|
// WHEN the auth headers are decided: `http` fixes them at construction,
|
|
146
201
|
// `gateway` vends them inside every request (see gatewayTransport.ts).
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
httpMod = lazyRequire('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
throw new Error(`mcpClient(${t.transport}) requires @modelcontextprotocol/sdk/client/streamableHttp.js — ` +
|
|
153
|
-
'check that @modelcontextprotocol/sdk is installed at the latest version.');
|
|
154
|
-
}
|
|
202
|
+
const httpMod = sdk ?? loadHttpModule(t.transport);
|
|
155
203
|
// Throttle retry wraps the OUTERMOST fetch, so every attempt is re-signed
|
|
156
204
|
// and re-vended: signatures expire, and a token that would have died during
|
|
157
205
|
// the wait is simply never the one reused. `retryingFetch` returns its input
|
|
@@ -166,7 +214,7 @@ async function buildTransport(t, retryOnThrottle) {
|
|
|
166
214
|
// credential is still vended per request. Passing `undefined` is the
|
|
167
215
|
// default global `fetch`, which is byte-identical to every release before
|
|
168
216
|
// the seam existed.
|
|
169
|
-
return new httpMod.StreamableHTTPClientTransport(
|
|
217
|
+
return new httpMod.StreamableHTTPClientTransport(transportUrl(t.url, 'mcpClient'), {
|
|
170
218
|
fetch: retryingFetch(createVendingFetch(t, t.fetch), retryOnThrottle),
|
|
171
219
|
});
|
|
172
220
|
}
|
|
@@ -175,13 +223,29 @@ async function buildTransport(t, retryOnThrottle) {
|
|
|
175
223
|
// hands the custom fetch, so a signer sees the static headers and has the
|
|
176
224
|
// final word over the bytes — see `McpHttpTransport.fetch`.
|
|
177
225
|
const httpFetch = retryingFetch(t.fetch, retryOnThrottle);
|
|
178
|
-
return new httpMod.StreamableHTTPClientTransport(
|
|
226
|
+
return new httpMod.StreamableHTTPClientTransport(transportUrl(t.url, 'mcpClient'), {
|
|
179
227
|
...(t.headers && { requestInit: { headers: { ...t.headers } } }),
|
|
180
228
|
...(httpFetch && { fetch: httpFetch }),
|
|
181
229
|
});
|
|
182
230
|
}
|
|
231
|
+
/** The Node loader path for Streamable HTTP — reached only with no `sdk`. */
|
|
232
|
+
function loadHttpModule(transport) {
|
|
233
|
+
try {
|
|
234
|
+
return lazyRequire('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
throw new Error(sdkLoadFailure(err, {
|
|
238
|
+
notInstalled: `mcpClient(${transport}) requires @modelcontextprotocol/sdk/client/streamableHttp.js — ` +
|
|
239
|
+
'check that @modelcontextprotocol/sdk is installed at the latest version.',
|
|
240
|
+
caller: `mcpClient(${transport})`,
|
|
241
|
+
specifier: '@modelcontextprotocol/sdk/client/streamableHttp.js',
|
|
242
|
+
instead: 'Pass `sdk` (the two SDK modules, imported statically) or `connection` ' +
|
|
243
|
+
'(a client you connected yourself).',
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
183
247
|
// ─── Tool wrapping ─────────────────────────────────────────────────
|
|
184
|
-
function wrapMcpTool(serverName,
|
|
248
|
+
function wrapMcpTool(serverName, connection, mcp, signal) {
|
|
185
249
|
const tool = {
|
|
186
250
|
schema: {
|
|
187
251
|
name: mcp.name,
|
|
@@ -213,8 +277,8 @@ function wrapMcpTool(serverName, sdk, mcp, signal) {
|
|
|
213
277
|
// `resultSchema` (2nd arg) is left to the SDK's own default.
|
|
214
278
|
const params = { name: mcp.name, arguments: argsObj };
|
|
215
279
|
const result = signal
|
|
216
|
-
? await
|
|
217
|
-
: await
|
|
280
|
+
? await connection.callTool(params, undefined, { signal })
|
|
281
|
+
: await connection.callTool(params);
|
|
218
282
|
return readToolResult(result, mcp.name, serverName);
|
|
219
283
|
},
|
|
220
284
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcpClient.js","sourceRoot":"","sources":["../../../../src/lib/mcp/mcpClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;
|
|
1
|
+
{"version":3,"file":"mcpClient.js","sourceRoot":"","sources":["../../../../src/lib/mcp/mcpClient.ts"],"names":[],"mappings":"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"}
|