@dsh-cc/mcp-client 0.5.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/LICENSE +201 -0
- package/README.i18n.yaml +6 -0
- package/README.md +149 -0
- package/README.zh.md +150 -0
- package/lib/auth.d.ts +66 -0
- package/lib/auth.d.ts.map +1 -0
- package/lib/auth.js +121 -0
- package/lib/auth.js.map +1 -0
- package/lib/connection.d.ts +98 -0
- package/lib/connection.d.ts.map +1 -0
- package/lib/connection.js +409 -0
- package/lib/connection.js.map +1 -0
- package/lib/defer.d.ts +39 -0
- package/lib/defer.d.ts.map +1 -0
- package/lib/defer.js +40 -0
- package/lib/defer.js.map +1 -0
- package/lib/index.d.ts +129 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +198 -0
- package/lib/index.js.map +1 -0
- package/lib/invariant.d.ts +16 -0
- package/lib/invariant.d.ts.map +1 -0
- package/lib/invariant.js +22 -0
- package/lib/invariant.js.map +1 -0
- package/lib/prompts.d.ts +44 -0
- package/lib/prompts.d.ts.map +1 -0
- package/lib/prompts.js +166 -0
- package/lib/prompts.js.map +1 -0
- package/lib/registry.d.ts +94 -0
- package/lib/registry.d.ts.map +1 -0
- package/lib/registry.js +101 -0
- package/lib/registry.js.map +1 -0
- package/lib/resources.d.ts +42 -0
- package/lib/resources.d.ts.map +1 -0
- package/lib/resources.js +136 -0
- package/lib/resources.js.map +1 -0
- package/lib/stdio-stderr.d.ts +66 -0
- package/lib/stdio-stderr.d.ts.map +1 -0
- package/lib/stdio-stderr.js +187 -0
- package/lib/stdio-stderr.js.map +1 -0
- package/lib/tools.d.ts +161 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +373 -0
- package/lib/tools.js.map +1 -0
- package/lib/transport.d.ts +50 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +79 -0
- package/lib/transport.js.map +1 -0
- package/package.json +62 -0
package/lib/tools.js
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool bridge: discovers MCP tools, registers them on the harness ToolRuntime
|
|
3
|
+
* under deterministic server-qualified public names, and handles re-sync when
|
|
4
|
+
* the server's tool list changes.
|
|
5
|
+
*
|
|
6
|
+
* Naming contract (see the mcp-client Agent Note "Naming invariants"): every MCP tool
|
|
7
|
+
* has the stable identity `(serverName, rawName)`; the model-facing public name
|
|
8
|
+
* is `mcp__<serverName>__<rawName>`, normalized to the DeepSeek function-name
|
|
9
|
+
* constraints. The raw name is only ever sent on the wire (`tools/call`); the
|
|
10
|
+
* public name is never parsed to recover it.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from 'node:crypto';
|
|
15
|
+
import { ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
16
|
+
import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
import { assertSupportedJsonSchema } from '@dsh-cc/tools';
|
|
19
|
+
import { DEFAULT_DEFER_TOOL_THRESHOLD, publishListedTool, toolSearchSeam } from "./defer.js";
|
|
20
|
+
export { DEFAULT_DEFER_TOOL_THRESHOLD } from "./defer.js";
|
|
21
|
+
/** The generation representing "nothing registered yet" (or a rolled-back swap). */
|
|
22
|
+
export function emptyToolGeneration() {
|
|
23
|
+
return { disposers: new Map(), fingerprint: undefined, client: undefined, eagerCount: 0, deferredCount: 0 };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Deterministic JSON serialization: object keys are sorted recursively while
|
|
27
|
+
* array order is preserved, so semantically identical JSON payloads with
|
|
28
|
+
* unstable key order serialize to identical bytes. Primitives go through
|
|
29
|
+
* `JSON.stringify`; `undefined` (absent optional fields) serializes as `null`.
|
|
30
|
+
*/
|
|
31
|
+
function stableStringify(value) {
|
|
32
|
+
if (value === undefined)
|
|
33
|
+
return 'null';
|
|
34
|
+
if (value === null || typeof value !== 'object')
|
|
35
|
+
return JSON.stringify(value);
|
|
36
|
+
if (Array.isArray(value))
|
|
37
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
38
|
+
const entries = Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
39
|
+
return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(',')}}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Fingerprint the raw tools/list payload for swap short-circuiting.
|
|
43
|
+
*
|
|
44
|
+
* Entries are ordered by public name, then stable-stringified (recursive key
|
|
45
|
+
* sort, array order preserved) and hashed. The fingerprint covers every field
|
|
46
|
+
* of the raw entries — including `execution.taskSupport`, which
|
|
47
|
+
* `createExecutor` bakes into the executor's semantics, and `outputSchema`,
|
|
48
|
+
* which shapes the registered output schema — so any server-side semantic
|
|
49
|
+
* change forces a swap. Executor closures are not compared (functions are not
|
|
50
|
+
* serializable); identical fingerprints on one client generation imply the
|
|
51
|
+
* rebuilt executors would be behaviorally identical.
|
|
52
|
+
*
|
|
53
|
+
* @param serverName - Namespace used to derive each entry's public name for ordering.
|
|
54
|
+
* @param tools - Raw entries exactly as returned by the server's `tools/list`.
|
|
55
|
+
* @returns A hex digest that is equal precisely when the payload is semantically unchanged.
|
|
56
|
+
*/
|
|
57
|
+
export function fingerprintTools(serverName, tools) {
|
|
58
|
+
const rawName = (entry) => {
|
|
59
|
+
const name = entry?.name;
|
|
60
|
+
return typeof name === 'string' ? name : '';
|
|
61
|
+
};
|
|
62
|
+
const ordered = [...tools].sort((a, b) => {
|
|
63
|
+
const nameA = publicToolName(serverName, rawName(a));
|
|
64
|
+
const nameB = publicToolName(serverName, rawName(b));
|
|
65
|
+
return nameA < nameB ? -1 : nameA > nameB ? 1 : 0;
|
|
66
|
+
});
|
|
67
|
+
return createHash('sha256').update(stableStringify(ordered)).digest('hex');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* DeepSeek function-name contract: at most 64 characters. Wire-protocol
|
|
71
|
+
* constant, not configuration.
|
|
72
|
+
*/
|
|
73
|
+
const MAX_PUBLIC_NAME_LENGTH = 64;
|
|
74
|
+
/** DeepSeek function-name contract: only `[A-Za-z0-9_-]` is allowed. */
|
|
75
|
+
const INVALID_NAME_CHARS = /[^A-Za-z0-9_-]/g;
|
|
76
|
+
/** Hex chars of the SHA-256 identity hash appended on lossy normalization. */
|
|
77
|
+
const HASH_LENGTH = 12;
|
|
78
|
+
/** Raw result record: the bridge owns JSON-value validation after transport. */
|
|
79
|
+
const RawCallToolResultSchema = z.record(z.string(), z.unknown());
|
|
80
|
+
/** List without mutating the SDK's per-page output-validator cache. */
|
|
81
|
+
function listToolsUncached(client, cursor) {
|
|
82
|
+
return client.request({ method: 'tools/list', ...cursor === undefined ? {} : { params: { cursor } } }, ListToolsResultSchema);
|
|
83
|
+
}
|
|
84
|
+
/** Call without the SDK pre-validating an output schema the bridge may not support. */
|
|
85
|
+
function callToolUncached(client, rawName, args, exec, opts) {
|
|
86
|
+
return client.request({ method: 'tools/call', params: { name: rawName, arguments: args } }, RawCallToolResultSchema, {
|
|
87
|
+
signal: exec.signal,
|
|
88
|
+
timeout: opts.toolCallTimeoutMs,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Derive the model-facing public name for one MCP tool.
|
|
93
|
+
*
|
|
94
|
+
* Deterministic pure function of `(serverName, rawName)`: the clean case is
|
|
95
|
+
* `mcp__<serverName>__<rawName>` verbatim. When character replacement or
|
|
96
|
+
* truncation to the DeepSeek function-name contract (64 chars,
|
|
97
|
+
* `[A-Za-z0-9_-]`) changes the name, a 12-hex-char SHA-256 hash of the
|
|
98
|
+
* identity is appended so distinct MCP identities never collapse into the
|
|
99
|
+
* same public name.
|
|
100
|
+
*
|
|
101
|
+
* @param serverName - Stable local namespace from plugin config.
|
|
102
|
+
* @param rawName - The MCP server's own tool name.
|
|
103
|
+
* @returns The globally unique, model-facing ToolRuntime name.
|
|
104
|
+
*/
|
|
105
|
+
export function publicToolName(serverName, rawName) {
|
|
106
|
+
const joined = `mcp__${serverName}__${rawName}`;
|
|
107
|
+
const normalized = joined.replace(INVALID_NAME_CHARS, '_');
|
|
108
|
+
if (normalized === joined && normalized.length <= MAX_PUBLIC_NAME_LENGTH)
|
|
109
|
+
return normalized;
|
|
110
|
+
const hash = createHash('sha256').update(`${serverName}\0${rawName}`).digest('hex').slice(0, HASH_LENGTH);
|
|
111
|
+
return `${normalized.slice(0, MAX_PUBLIC_NAME_LENGTH - HASH_LENGTH - 1)}_${hash}`;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Sync the MCP server's tool list into the harness ToolRuntime.
|
|
115
|
+
*
|
|
116
|
+
* Two phases keep the swap safe:
|
|
117
|
+
*
|
|
118
|
+
* 1. Fetch: drain uncached `tools/list` pagination and build the full next
|
|
119
|
+
* generation of `ToolDefinition`s under public names. Any failure here
|
|
120
|
+
* (network error, duplicate raw name in the server's list) rejects and
|
|
121
|
+
* leaves the previous generation registered untouched.
|
|
122
|
+
* 2. Swap: dispose the previous generation, publish the new one. A registry
|
|
123
|
+
* conflict here can only mean a foreign registration squats on this
|
|
124
|
+
* server's `mcp__<serverName>__` namespace — the partial generation is
|
|
125
|
+
* rolled back (zero tools from this server) and logged. Initial strict
|
|
126
|
+
* synchronization may propagate the conflict so its parent transaction
|
|
127
|
+
* rejects; ordinary clients and later re-syncs return an empty generation.
|
|
128
|
+
* Deferred publishing detects the same squat up front (`ctx.tools.get`)
|
|
129
|
+
* because `registerDeferred` only reserves the name.
|
|
130
|
+
*
|
|
131
|
+
* When the `ctx.toolSearch` seam is mounted and the server lists at least
|
|
132
|
+
* `deferToolThreshold` tools (default {@link DEFAULT_DEFER_TOOL_THRESHOLD}),
|
|
133
|
+
* each deferrable tool registers through `registerDeferred` instead: its
|
|
134
|
+
* definition stays out of the model-visible schema until a ToolSearch hit
|
|
135
|
+
* activates it. Tools flagged `_meta['anthropic/alwaysLoad']` register eagerly
|
|
136
|
+
* even on a deferred server. Without the seam the swap is eager at any
|
|
137
|
+
* threshold.
|
|
138
|
+
*
|
|
139
|
+
* Between the phases, a fingerprint of the raw payload decides whether the
|
|
140
|
+
* swap is needed at all: when the payload is semantically unchanged (key
|
|
141
|
+
* order may drift; content may not) AND `previous` was produced by the same
|
|
142
|
+
* client generation, the live registrations are kept and `previous` is
|
|
143
|
+
* returned unchanged — dispose+register churn (and the request-prefix churn
|
|
144
|
+
* it risks) is skipped. A new client generation always forces a real swap,
|
|
145
|
+
* so reconnects never reuse the previous generation's registrations.
|
|
146
|
+
*
|
|
147
|
+
* @param client - Connected MCP Client instance used to list and call tools.
|
|
148
|
+
* @param ctx - Cordis context providing the `tools` service for registration.
|
|
149
|
+
* @param opts - Bridge options: server namespace and per-call timeout.
|
|
150
|
+
* @param previous - The prior sync generation; its registrations are disposed
|
|
151
|
+
* during the swap phase (only after the fetch phase succeeded and the
|
|
152
|
+
* fingerprint check found a real change).
|
|
153
|
+
* @returns The live generation — `previous` itself on a fingerprint hit,
|
|
154
|
+
* otherwise the newly registered one.
|
|
155
|
+
*/
|
|
156
|
+
export async function syncTools(client, ctx, opts, previous) {
|
|
157
|
+
// Phase 1: fetch and build the next generation without touching the registry.
|
|
158
|
+
const definitions = new Map();
|
|
159
|
+
const rawTools = [];
|
|
160
|
+
let cursor;
|
|
161
|
+
do {
|
|
162
|
+
const response = await listToolsUncached(client, cursor);
|
|
163
|
+
for (const tool of response.tools) {
|
|
164
|
+
rawTools.push(tool);
|
|
165
|
+
const publicName = publicToolName(opts.serverName, tool.name);
|
|
166
|
+
if (definitions.has(publicName)) {
|
|
167
|
+
throw new Error(`mcp-client(${opts.serverName}): server listed tool "${tool.name}" more than once — invalid tool list`);
|
|
168
|
+
}
|
|
169
|
+
definitions.set(publicName, {
|
|
170
|
+
rawName: tool.name,
|
|
171
|
+
alwaysLoad: tool?._meta?.['anthropic/alwaysLoad'] === true,
|
|
172
|
+
definition: {
|
|
173
|
+
name: publicName,
|
|
174
|
+
description: tool.description ?? '',
|
|
175
|
+
parameters: tool.inputSchema,
|
|
176
|
+
output: createOutput(tool.name, supportedOutputSchema(tool.outputSchema)),
|
|
177
|
+
execute: createExecutor(client, tool.name, tool.execution?.taskSupport === 'required', opts),
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
cursor = response.nextCursor;
|
|
182
|
+
} while (cursor);
|
|
183
|
+
// Semantically unchanged payload on the same client generation: keep the
|
|
184
|
+
// live generation so the registered definitions (and any request prefix
|
|
185
|
+
// built on them) stay byte-stable. A different client generation never
|
|
186
|
+
// short-circuits — reconnects must rebuild against their own client.
|
|
187
|
+
const fingerprint = fingerprintTools(opts.serverName, rawTools);
|
|
188
|
+
if (previous.client === client && previous.fingerprint === fingerprint) {
|
|
189
|
+
ctx.logger.debug(`mcp-client(${opts.serverName}): tool list unchanged (fingerprint ${fingerprint.slice(0, 12)}) — keeping ${previous.disposers.size} registered tools`);
|
|
190
|
+
return previous;
|
|
191
|
+
}
|
|
192
|
+
const seam = toolSearchSeam(ctx);
|
|
193
|
+
const deferServer = seam !== undefined
|
|
194
|
+
&& rawTools.length >= (opts.deferToolThreshold ?? DEFAULT_DEFER_TOOL_THRESHOLD);
|
|
195
|
+
// Phase 2: swap generations.
|
|
196
|
+
for (const dispose of previous.disposers.values())
|
|
197
|
+
dispose();
|
|
198
|
+
const disposers = new Map();
|
|
199
|
+
let eagerCount = 0;
|
|
200
|
+
let deferredCount = 0;
|
|
201
|
+
try {
|
|
202
|
+
for (const [publicName, entry] of definitions) {
|
|
203
|
+
disposers.set(publicName, publishListedTool(ctx, opts, publicName, entry, seam, deferServer));
|
|
204
|
+
if (deferServer && !entry.alwaysLoad)
|
|
205
|
+
deferredCount += 1;
|
|
206
|
+
else
|
|
207
|
+
eagerCount += 1;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
// A conflict on an `mcp__<serverName>__`-qualified name means a foreign
|
|
212
|
+
// registration occupies this server's namespace. Roll back so the model
|
|
213
|
+
// sees either the full generation or none of it — never a partial set.
|
|
214
|
+
// Deferred disposers unwind both the reservation and any activated
|
|
215
|
+
// registration, so one loop reclaims everything this sync published.
|
|
216
|
+
for (const dispose of disposers.values())
|
|
217
|
+
dispose();
|
|
218
|
+
ctx.logger.error(`mcp-client(${opts.serverName}): tool registration failed, no tools registered: ${String(error)}`);
|
|
219
|
+
if (opts.registrationFailure === 'throw')
|
|
220
|
+
throw error;
|
|
221
|
+
// No fingerprint: nothing is registered, so the next sync must attempt a
|
|
222
|
+
// real swap even if the payload is unchanged.
|
|
223
|
+
return emptyToolGeneration();
|
|
224
|
+
}
|
|
225
|
+
return { disposers, fingerprint, client, eagerCount, deferredCount };
|
|
226
|
+
}
|
|
227
|
+
/** Keep a supported advertised schema; unsupported MCP vocabulary falls back to JsonValue. */
|
|
228
|
+
function supportedOutputSchema(candidate) {
|
|
229
|
+
if (candidate === undefined)
|
|
230
|
+
return undefined;
|
|
231
|
+
try {
|
|
232
|
+
assertSupportedJsonSchema(candidate);
|
|
233
|
+
return candidate;
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** Build the canonical result schema and existing Native text projection. */
|
|
240
|
+
function createOutput(rawName, structuredSchema) {
|
|
241
|
+
return {
|
|
242
|
+
schema: {
|
|
243
|
+
type: 'object',
|
|
244
|
+
properties: {
|
|
245
|
+
content: { type: 'array', items: {} },
|
|
246
|
+
structuredContent: structuredSchema ?? {},
|
|
247
|
+
},
|
|
248
|
+
required: structuredSchema === undefined ? ['content'] : ['content', 'structuredContent'],
|
|
249
|
+
additionalProperties: false,
|
|
250
|
+
},
|
|
251
|
+
render(_args, value) {
|
|
252
|
+
const result = value;
|
|
253
|
+
return [{ type: 'text', text: extractText(result.content, rawName) }];
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Run an MCP request, retrying once on a mid-session `UnauthorizedError`.
|
|
259
|
+
* Between the original attempt and the retry, `onUnauthorized` runs to drop
|
|
260
|
+
* stale OAuth state and re-establish a token. Only a single retry is attempted
|
|
261
|
+
* (the spec's "401 自动重试一次"); a second failure propagates to the caller.
|
|
262
|
+
*
|
|
263
|
+
* @param request - the MCP request to attempt.
|
|
264
|
+
* @param onUnauthorized - re-auth hook run before the single retry.
|
|
265
|
+
* @returns the request result.
|
|
266
|
+
*/
|
|
267
|
+
export async function retryUnauthorizedOnce(request, onUnauthorized) {
|
|
268
|
+
try {
|
|
269
|
+
return await request();
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (!isUnauthorized(error) || onUnauthorized === undefined)
|
|
273
|
+
throw error;
|
|
274
|
+
await onUnauthorized();
|
|
275
|
+
return request();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/** Whether a thrown error signals an expired/revoked OAuth session. */
|
|
279
|
+
export function isUnauthorized(error) {
|
|
280
|
+
return error instanceof UnauthorizedError
|
|
281
|
+
|| (error instanceof Error && /unauthorized/i.test(error.message));
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Create an execute function for one MCP tool. The executor closes over the
|
|
285
|
+
* raw MCP tool name and sends an uncached `tools/call` request with it (never
|
|
286
|
+
* the public name), with abort signal and timeout, then maps the result to
|
|
287
|
+
* harness ContentBlocks. Owning the raw request prevents the SDK's internal
|
|
288
|
+
* per-page schema cache from pre-validating a different contract.
|
|
289
|
+
*
|
|
290
|
+
* When the MCP server returns `isError: true`, the executor throws so that
|
|
291
|
+
* the ToolRuntime's catch path produces an `isError` result for the model.
|
|
292
|
+
*/
|
|
293
|
+
function createExecutor(client, rawName, taskRequired, opts) {
|
|
294
|
+
return async (args, exec) => {
|
|
295
|
+
if (taskRequired) {
|
|
296
|
+
throw new Error(`Tool "${rawName}" requires task-based execution, which this bridge does not support`);
|
|
297
|
+
}
|
|
298
|
+
// The agent loop passes `JSON.parse(model_arguments)` which is usually an
|
|
299
|
+
// object, but can be any JSON value if the model misbehaves (outputs a bare
|
|
300
|
+
// string/number/null). Fallback to {} lets the MCP server produce a
|
|
301
|
+
// specific "missing required param" error the model can learn from.
|
|
302
|
+
const argsObj = (typeof args === 'object' && args !== null ? args : {});
|
|
303
|
+
const result = await retryUnauthorizedOnce(() => callToolUncached(client, rawName, argsObj, exec, opts), opts.onUnauthorized);
|
|
304
|
+
// The SDK may return a legacy `toolResult` shape; normalize to content array.
|
|
305
|
+
if (!Array.isArray(result.content)) {
|
|
306
|
+
const rendered = 'toolResult' in result
|
|
307
|
+
? JSON.stringify(result.toolResult)
|
|
308
|
+
: '(no output)';
|
|
309
|
+
const text = typeof rendered === 'string' ? rendered : '(no output)';
|
|
310
|
+
if (result.isError === true)
|
|
311
|
+
throw new Error(text);
|
|
312
|
+
return {
|
|
313
|
+
content: [{ type: 'text', text }],
|
|
314
|
+
...result.structuredContent !== undefined
|
|
315
|
+
? { structuredContent: result.structuredContent }
|
|
316
|
+
: {},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
// Trust boundary: the SDK's return type erases to `any[]` due to the
|
|
320
|
+
// union of CallToolResult | CompatibilityCallToolResult; extractText
|
|
321
|
+
// validates each element.
|
|
322
|
+
const content = result.content;
|
|
323
|
+
const text = extractText(content, rawName);
|
|
324
|
+
// MCP isError → throw so ToolRuntime produces an isError result for the model.
|
|
325
|
+
if (result.isError === true) {
|
|
326
|
+
throw new Error(text);
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
content,
|
|
330
|
+
...result.structuredContent !== undefined
|
|
331
|
+
? { structuredContent: result.structuredContent }
|
|
332
|
+
: {},
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Extract text from an MCP content array into a single string.
|
|
338
|
+
* - text blocks: join with '\n'
|
|
339
|
+
* - image/audio/resource blocks: replaced with a placeholder
|
|
340
|
+
*
|
|
341
|
+
* Defensive: fields that the MCP spec declares required (mimeType, text) are
|
|
342
|
+
* guarded with fallbacks because this is a network trust boundary.
|
|
343
|
+
*/
|
|
344
|
+
function extractText(mcpContent, toolName) {
|
|
345
|
+
const parts = [];
|
|
346
|
+
for (const value of mcpContent) {
|
|
347
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
348
|
+
parts.push('[unsupported content type: unknown]');
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
const block = value;
|
|
352
|
+
switch (block.type) {
|
|
353
|
+
case 'text':
|
|
354
|
+
if (block.text !== undefined)
|
|
355
|
+
parts.push(block.text);
|
|
356
|
+
break;
|
|
357
|
+
case 'image':
|
|
358
|
+
parts.push(`[image: ${block.mimeType ?? 'unknown'}, content discarded]`);
|
|
359
|
+
break;
|
|
360
|
+
case 'audio':
|
|
361
|
+
parts.push(`[audio: ${block.mimeType ?? 'unknown'}, content discarded]`);
|
|
362
|
+
break;
|
|
363
|
+
case 'resource':
|
|
364
|
+
case 'resource_link':
|
|
365
|
+
parts.push('[resource: content discarded]');
|
|
366
|
+
break;
|
|
367
|
+
default:
|
|
368
|
+
parts.push(`[unsupported content type: ${block.type}]`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return parts.join('\n') || `(${toolName} returned no text content)`;
|
|
372
|
+
}
|
|
373
|
+
//# sourceMappingURL=tools.js.map
|
package/lib/tools.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport factory: creates the appropriate MCP transport based on the
|
|
3
|
+
* plugin's resolved config. Stdio spawns a child process (with credential
|
|
4
|
+
* scrubbing); Streamable HTTP and SSE connect to a URL, optionally through the
|
|
5
|
+
* credentials-backed OAuth provider.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { Config } from './index.ts';
|
|
12
|
+
import { CredentialsOAuthClientProvider } from './auth.ts';
|
|
13
|
+
import type { OAuthConfig } from './auth.ts';
|
|
14
|
+
/**
|
|
15
|
+
* Build the credentials-backed OAuth provider for a network transport.
|
|
16
|
+
* @param ctx - Cordis context carrying the `credentials` service.
|
|
17
|
+
* @param serverName - server namespace used to derive credential references.
|
|
18
|
+
* @param oauth - resolved OAuth configuration.
|
|
19
|
+
* @returns the MCP SDK `OAuthClientProvider` implementation.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildAuthProvider(ctx: Context, serverName: string, oauth: OAuthConfig): CredentialsOAuthClientProvider;
|
|
22
|
+
/** Optional transport construction inputs. */
|
|
23
|
+
export interface TransportContext {
|
|
24
|
+
/** Cordis context used to build the credentials-backed OAuth provider. */
|
|
25
|
+
ctx?: Context;
|
|
26
|
+
/** A pre-built OAuth provider; takes precedence over `ctx` for network configs. */
|
|
27
|
+
authProvider?: CredentialsOAuthClientProvider;
|
|
28
|
+
/**
|
|
29
|
+
* Directory for captured stdio stderr (`<serverName>.log`). Tests inject a
|
|
30
|
+
* tmpdir so e2e cannot touch the developer's `$DSH_HOME`. Omission uses
|
|
31
|
+
* `$DSH_HOME/mcp-logs` (or `~/.dsh/mcp-logs`).
|
|
32
|
+
*/
|
|
33
|
+
logDir?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Size cap in bytes for stdio stderr log rotation (one `.log.1` backup).
|
|
36
|
+
* Tests inject a small cap so rotation is observable; `<= 0` disables
|
|
37
|
+
* rotation. Omission uses `STDIO_LOG_MAX_BYTES` (4 MiB).
|
|
38
|
+
*/
|
|
39
|
+
maxBytes?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create an MCP transport from the resolved plugin config.
|
|
43
|
+
*
|
|
44
|
+
* @param config - Resolved plugin config discriminated on `transport`.
|
|
45
|
+
* @param transportCtx - Optional context, pre-built OAuth provider, and
|
|
46
|
+
* stdio log directory; omit to skip OAuth and use the default log dir.
|
|
47
|
+
* @returns A connected-ready MCP Transport (stdio, Streamable HTTP, or SSE).
|
|
48
|
+
*/
|
|
49
|
+
export declare function createTransport(config: Config, transportCtx?: TransportContext): Transport;
|
|
50
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAA;AAK9E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,8BAA8B,EAAE,MAAM,WAAW,CAAA;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAa5C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,8BAA8B,CAEtH;AAED,8CAA8C;AAC9C,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,mFAAmF;IACnF,YAAY,CAAC,EAAE,8BAA8B,CAAA;IAC7C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,gBAAgB,GAAG,SAAS,CA2B1F"}
|
package/lib/transport.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport factory: creates the appropriate MCP transport based on the
|
|
3
|
+
* plugin's resolved config. Stdio spawns a child process (with credential
|
|
4
|
+
* scrubbing); Streamable HTTP and SSE connect to a URL, optionally through the
|
|
5
|
+
* credentials-backed OAuth provider.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
10
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
11
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
12
|
+
import { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess';
|
|
13
|
+
import { CredentialsOAuthClientProvider } from "./auth.js";
|
|
14
|
+
import { attachStdioStderrDrain } from "./stdio-stderr.js";
|
|
15
|
+
/**
|
|
16
|
+
* The subprocess seam's scrubbed parent env (credential-shaped and stale
|
|
17
|
+
* `DSH_*` names dropped), plus the spec's explicit env. The MCP SDK owns the
|
|
18
|
+
* actual spawn, so this transport shares the scrub definition rather than the
|
|
19
|
+
* spawn path.
|
|
20
|
+
*/
|
|
21
|
+
function buildChildEnv(extra) {
|
|
22
|
+
return { ...scrubbedParentEnv(), ...extra };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the credentials-backed OAuth provider for a network transport.
|
|
26
|
+
* @param ctx - Cordis context carrying the `credentials` service.
|
|
27
|
+
* @param serverName - server namespace used to derive credential references.
|
|
28
|
+
* @param oauth - resolved OAuth configuration.
|
|
29
|
+
* @returns the MCP SDK `OAuthClientProvider` implementation.
|
|
30
|
+
*/
|
|
31
|
+
export function buildAuthProvider(ctx, serverName, oauth) {
|
|
32
|
+
return new CredentialsOAuthClientProvider(ctx, serverName, oauth);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create an MCP transport from the resolved plugin config.
|
|
36
|
+
*
|
|
37
|
+
* @param config - Resolved plugin config discriminated on `transport`.
|
|
38
|
+
* @param transportCtx - Optional context, pre-built OAuth provider, and
|
|
39
|
+
* stdio log directory; omit to skip OAuth and use the default log dir.
|
|
40
|
+
* @returns A connected-ready MCP Transport (stdio, Streamable HTTP, or SSE).
|
|
41
|
+
*/
|
|
42
|
+
export function createTransport(config, transportCtx) {
|
|
43
|
+
switch (config.transport) {
|
|
44
|
+
case 'stdio': {
|
|
45
|
+
const transport = new StdioClientTransport({
|
|
46
|
+
command: config.command,
|
|
47
|
+
args: config.args,
|
|
48
|
+
env: buildChildEnv(config.env),
|
|
49
|
+
cwd: config.cwd,
|
|
50
|
+
stderr: 'pipe',
|
|
51
|
+
});
|
|
52
|
+
attachStdioStderrDrain(transport, config.serverName, transportCtx?.logDir, transportCtx?.maxBytes);
|
|
53
|
+
return transport;
|
|
54
|
+
}
|
|
55
|
+
case 'streamable-http':
|
|
56
|
+
return new StreamableHTTPClientTransport(new URL(config.url), transportOptions(config, transportCtx));
|
|
57
|
+
case 'sse':
|
|
58
|
+
/* oxlint-disable-next-line no-deprecated -- SSE support is a required
|
|
59
|
+
Claude Code parity surface; the SDK deprecates it only because
|
|
60
|
+
Streamable HTTP is preferred for new servers. */
|
|
61
|
+
return new SSEClientTransport(new URL(config.url), transportOptions(config, transportCtx));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Build the SDK transport options, wiring OAuth when configured. */
|
|
65
|
+
function transportOptions(config, transportCtx) {
|
|
66
|
+
const opts = {
|
|
67
|
+
requestInit: { headers: config.headers },
|
|
68
|
+
};
|
|
69
|
+
if (config.oauth !== undefined) {
|
|
70
|
+
const provider = transportCtx?.authProvider
|
|
71
|
+
?? (transportCtx?.ctx !== undefined ? buildAuthProvider(transportCtx.ctx, config.serverName, config.oauth) : undefined);
|
|
72
|
+
if (provider === undefined) {
|
|
73
|
+
throw new Error(`mcp-client(${config.serverName}): oauth requires the credentials service to be available`);
|
|
74
|
+
}
|
|
75
|
+
opts.authProvider = provider;
|
|
76
|
+
}
|
|
77
|
+
return opts;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAA;AAClG,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AAE5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAE/D,OAAO,EAAE,8BAA8B,EAAE,MAAM,WAAW,CAAA;AAE1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE1D;;;;;GAKG;AACH,SAAS,aAAa,CAAC,KAA6B;IAClD,OAAO,EAAE,GAAG,iBAAiB,EAAE,EAAE,GAAG,KAAK,EAAE,CAAA;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAY,EAAE,UAAkB,EAAE,KAAkB;IACpF,OAAO,IAAI,8BAA8B,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,CAAA;AACnE,CAAC;AAsBD;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,YAA+B;IAC7E,QAAQ,MAAM,CAAC,SAAS,EAAE,CAAC;QACzB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC;gBACzC,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC;gBAC9B,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,MAAM,EAAE,MAAM;aACf,CAAC,CAAA;YACF,sBAAsB,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAA;YAClG,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,KAAK,iBAAiB;YACpB,OAAO,IAAI,6BAA6B,CACtC,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EACnB,gBAAgB,CAAC,MAAM,EAAE,YAAY,CAAC,CAC1B,CAAA;QAChB,KAAK,KAAK;YACR;;+DAEmD;YACnD,OAAO,IAAI,kBAAkB,CAC3B,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EACnB,gBAAgB,CAAC,MAAM,EAAE,YAAY,CAAC,CACvC,CAAA;IACL,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,SAAS,gBAAgB,CAAC,MAAiE,EAAE,YAA0C;IACrI,MAAM,IAAI,GAAqE;QAC7E,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE;KACzC,CAAA;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,YAAY,EAAE,YAAY;eACtC,CAAC,YAAY,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACzH,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,cAAc,MAAM,CAAC,UAAU,2DAA2D,CAAC,CAAA;QAC7G,CAAC;QACD,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAA;IAC9B,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dsh-cc/mcp-client",
|
|
3
|
+
"description": "MCP client bridge: connects to MCP servers and registers their tools on ctx.tools",
|
|
4
|
+
"version": "0.5.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/dsh-cc/dsh-cc.git",
|
|
8
|
+
"directory": "packages/mcp/mcp-client"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./lib/index.d.ts",
|
|
14
|
+
"default": "./lib/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./invariant": {
|
|
17
|
+
"types": "./lib/invariant.d.ts",
|
|
18
|
+
"default": "./lib/invariant.js"
|
|
19
|
+
},
|
|
20
|
+
"./src/*": "./src/*",
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"lib"
|
|
25
|
+
],
|
|
26
|
+
"license": "Apache-2.0",
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@deepseek-ai/dsh-credentials": ">=0.1.1-rc.2",
|
|
29
|
+
"@deepseek-ai/dsh-invariants": ">=0.1.1-rc.2",
|
|
30
|
+
"@deepseek-ai/dsh-llm": ">=0.1.1-rc.2",
|
|
31
|
+
"@deepseek-ai/dsh-skill": ">=0.1.1-rc.2",
|
|
32
|
+
"@deepseek-ai/dsh-subprocess": ">=0.1.1-rc.2",
|
|
33
|
+
"@deepseek-ai/dsh-timeout": ">=0.1.1-rc.2",
|
|
34
|
+
"@deepseek-ai/cordis": ">=0.1.1-rc.2",
|
|
35
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
36
|
+
"@dsh-cc/tools": "^0.5.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
40
|
+
"zod": "^4.4.3"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@deepseek-ai/dsh-credentials": "link:../../../../deepseek-harness/packages/credentials/credentials",
|
|
44
|
+
"@deepseek-ai/dsh-credentials-local": "link:../../../../deepseek-harness/packages/credentials/credentials-local",
|
|
45
|
+
"@deepseek-ai/dsh-invariants": "link:../../../../deepseek-harness/packages/runtime-diagnostics/invariants",
|
|
46
|
+
"@deepseek-ai/dsh-llm": "link:../../../../deepseek-harness/packages/llm/llm",
|
|
47
|
+
"@deepseek-ai/dsh-skill": "link:../../../../deepseek-harness/packages/skill/skill",
|
|
48
|
+
"@deepseek-ai/dsh-subprocess": "link:../../../../deepseek-harness/packages/subprocess/subprocess",
|
|
49
|
+
"@deepseek-ai/dsh-timeout": "link:../../../../deepseek-harness/packages/util/timeout",
|
|
50
|
+
"@modelcontextprotocol/server-everything": "^2026.7.4",
|
|
51
|
+
"@modelcontextprotocol/server-filesystem": "^2026.7.4",
|
|
52
|
+
"@deepseek-ai/cordis": "link:../../../../deepseek-harness/vendor/cordis",
|
|
53
|
+
"@deepseek-ai/schemastery": "link:../../../../deepseek-harness/vendor/schemastery",
|
|
54
|
+
"@deepseek-ai/cordis-plugin-loader": "link:../../../../deepseek-harness/vendor/loader",
|
|
55
|
+
"@deepseek-ai/dsh-system-prompt": "link:../../../../deepseek-harness/packages/core/system-prompt",
|
|
56
|
+
"@dsh-cc/tools": "^0.5.0",
|
|
57
|
+
"@dsh-cc/tool-search": "^0.5.0"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
}
|
|
62
|
+
}
|