@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.
Files changed (49) hide show
  1. package/LICENSE +201 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +149 -0
  4. package/README.zh.md +150 -0
  5. package/lib/auth.d.ts +66 -0
  6. package/lib/auth.d.ts.map +1 -0
  7. package/lib/auth.js +121 -0
  8. package/lib/auth.js.map +1 -0
  9. package/lib/connection.d.ts +98 -0
  10. package/lib/connection.d.ts.map +1 -0
  11. package/lib/connection.js +409 -0
  12. package/lib/connection.js.map +1 -0
  13. package/lib/defer.d.ts +39 -0
  14. package/lib/defer.d.ts.map +1 -0
  15. package/lib/defer.js +40 -0
  16. package/lib/defer.js.map +1 -0
  17. package/lib/index.d.ts +129 -0
  18. package/lib/index.d.ts.map +1 -0
  19. package/lib/index.js +198 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/invariant.d.ts +16 -0
  22. package/lib/invariant.d.ts.map +1 -0
  23. package/lib/invariant.js +22 -0
  24. package/lib/invariant.js.map +1 -0
  25. package/lib/prompts.d.ts +44 -0
  26. package/lib/prompts.d.ts.map +1 -0
  27. package/lib/prompts.js +166 -0
  28. package/lib/prompts.js.map +1 -0
  29. package/lib/registry.d.ts +94 -0
  30. package/lib/registry.d.ts.map +1 -0
  31. package/lib/registry.js +101 -0
  32. package/lib/registry.js.map +1 -0
  33. package/lib/resources.d.ts +42 -0
  34. package/lib/resources.d.ts.map +1 -0
  35. package/lib/resources.js +136 -0
  36. package/lib/resources.js.map +1 -0
  37. package/lib/stdio-stderr.d.ts +66 -0
  38. package/lib/stdio-stderr.d.ts.map +1 -0
  39. package/lib/stdio-stderr.js +187 -0
  40. package/lib/stdio-stderr.js.map +1 -0
  41. package/lib/tools.d.ts +161 -0
  42. package/lib/tools.d.ts.map +1 -0
  43. package/lib/tools.js +373 -0
  44. package/lib/tools.js.map +1 -0
  45. package/lib/transport.d.ts +50 -0
  46. package/lib/transport.d.ts.map +1 -0
  47. package/lib/transport.js +79 -0
  48. package/lib/transport.js.map +1 -0
  49. 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
@@ -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"}
@@ -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
+ }