@deepseek-ai/dsh-mcp-client 0.1.5-rc.2 → 0.1.6-alpha.2
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/README.i18n.yaml +2 -2
- package/README.md +34 -16
- package/README.zh.md +39 -21
- package/lib/index.js +164 -120
- package/lib/types/connection.d.ts +7 -4
- package/lib/types/index.d.ts +8 -3
- package/lib/types/server-context.d.ts +25 -0
- package/lib/types/tools.d.ts +34 -3
- package/lib/types/transport.d.ts +1 -1
- package/package.json +33 -18
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/mcp/mcp-client/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: a241dda5d77da5cea1d7092a99d670ead564ba03
|
|
6
|
+
README.zh.md: dbebdae8675aecf9c1107f53c9aeec9469def1df
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
`dsh-mcp-client` lets the model
|
|
12
|
+
`dsh-mcp-client` lets the model use tools and resources from external Model Context Protocol (MCP) servers. Configure one server per entry; its tools use names such as `mcp__github__create_issue`. No server is enabled by default. Shipped profiles already provide [shared resource discovery and reading](../mcp-resources/README.md). An empty caller scope adds no MCP tools or prompt text. Server instructions join the logged system prompt as literal text; MCP prompt templates are unsupported. Slow or crashed servers can delay startup or fail calls until recovery.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -25,7 +25,7 @@ English | [中文](README.zh.md)
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## Use this package
|
|
27
27
|
|
|
28
|
-
Add `dsh-mcp-client` when the model should call tools from an external MCP server as if they were native.
|
|
28
|
+
Add `dsh-mcp-client` when the model should call tools from an external MCP server as if they were native. Give each server a unique name and transport. The official SDK selects the 2026-07-28 protocol when available and falls back to supported legacy revisions. Choose stdio for a local program and Streamable HTTP for a service; stdio negotiation starts a temporary probe process before the serving process.
|
|
29
29
|
|
|
30
30
|
### Minimal configuration
|
|
31
31
|
|
|
@@ -58,7 +58,8 @@ Add one entry per server; nothing else is required. After the harness starts, th
|
|
|
58
58
|
| `serverName` | required | Namespace for the server's tool names; `[A-Za-z0-9_-]{1,32}`, unique inside one registration scope |
|
|
59
59
|
| `command` / `args` / `env` / `cwd` | — | stdio: executable, arguments, extra env merged over scrubbed ambient env, working directory |
|
|
60
60
|
| `url` / `headers` | — | streamable-http: endpoint URL and extra request headers |
|
|
61
|
-
| `toolCallTimeoutMs` | `60,000` | Timeout per `tools/call`
|
|
61
|
+
| `toolCallTimeoutMs` | `60,000` | Timeout per `tools/call` or resource request |
|
|
62
|
+
| `maxInstructionBytes` | `32,768` | Maximum UTF-8 bytes of server instructions including attribution; an oversized value rejects the connection |
|
|
62
63
|
| `failOnStartupError` | `false` | Reject plugin activation when the initial connection or tool synchronization fails |
|
|
63
64
|
| `reconnect.enabled` | `true` | Reconnect automatically after a lost connection |
|
|
64
65
|
| `reconnect.initialDelayMs` | `500` | First reconnect delay; doubles per consecutive failed attempt |
|
|
@@ -67,7 +68,7 @@ Add one entry per server; nothing else is required. After the harness starts, th
|
|
|
67
68
|
|
|
68
69
|
The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-mcp-client) is the exhaustive source for every accepted field.
|
|
69
70
|
|
|
70
|
-
After startup, the server's tools appear as `mcp__<serverName>__<tool>` — try a prompt that uses one. If the initial connection fails, the harness still starts but no tools from that server appear, and an error is logged
|
|
71
|
+
After startup, the server's tools appear as `mcp__<serverName>__<tool>` — try a prompt that uses one. If the initial connection fails, the harness still starts but no tools from that server appear, and an error is logged. Setting `failOnStartupError: true` rejects plugin activation; [app-boot's startup policy](../../boot/app-boot/README.md) still permits an optional MCP entry to fail without aborting the harness.
|
|
71
72
|
|
|
72
73
|
### Tool naming and coexistence
|
|
73
74
|
|
|
@@ -76,7 +77,7 @@ The model sees each tool under a stable server-qualified name: `mcp__<serverName
|
|
|
76
77
|
- Two servers publishing the same tool name (for example `search`) coexist under their own namespaces.
|
|
77
78
|
- Two entries using the same server name: the later one fails to load with a clear error.
|
|
78
79
|
- A server that lists the same tool twice gets its tool list rejected as invalid, and the previous tool set stays active.
|
|
79
|
-
-
|
|
80
|
+
- The SDK owns discovery pagination and its page limit. A discovery failure preserves the previous tools; malformed cursor chains follow the SDK's behavior.
|
|
80
81
|
- An update that conflicts with an already-registered tool name is rejected entirely — you never get a partial tool set from that server.
|
|
81
82
|
|
|
82
83
|
### Calling tools and reading results
|
|
@@ -115,19 +116,22 @@ This section explains the design decisions behind the bridge and points at the c
|
|
|
115
116
|
|---|---|
|
|
116
117
|
| [`src/index.ts`](src/index.ts) | Plugin entry: `Config` schema, `serverName` reservation, activation await |
|
|
117
118
|
| [`src/connection.ts`](src/connection.ts) | Connection supervisor: client generations, reconnect policy, attempt budget, disposal |
|
|
119
|
+
| [`src/server-context.ts`](src/server-context.ts) | Resource-provider registration and literal server instructions |
|
|
118
120
|
| [`src/tools.ts`](src/tools.ts) | Tool bridge: discovery, naming, registration swap, execution, image projection |
|
|
119
121
|
| [`src/transport.ts`](src/transport.ts) | Transport factory: stdio spawn with scrubbed env, Streamable HTTP |
|
|
120
122
|
| — | No runtime invariant companion is published; MCP generations contribute through the tool registry, but the bridge exposes no independent server-to-tool snapshot after an asynchronous resync. |
|
|
121
123
|
|
|
124
|
+
The exported `createMcpToolDefinition(ctx, options)` adapts an upstream tool schema and raw-result callback to the same canonical values, errors, and durable image projection. Each callback receives the exact `ToolExecution`, including its Agent and cancellation signal; SDK spec-type validation checks its result before projection. Callers own registration, cancellation deadlines, and provider teardown. The native Cua Driver provider uses this adapter without opening an MCP transport.
|
|
125
|
+
|
|
122
126
|
### Lifecycle and sync
|
|
123
127
|
|
|
124
|
-
`apply` resolves the reconnect policy, reserves the `serverName` inside the current registration scope, starts the supervisor, and awaits the initial connection plus discovery. Independent Agent scopes may reuse the same namespace because their tools and transports are isolated; a duplicate inside one scope fails at load. The supervisor serializes every sync — initial, notification, and reconnect — through one queue so two syncs can never interleave their dispose-previous/register-next swap. Disposal cancels pending reconnects, closes the
|
|
128
|
+
`apply` resolves the reconnect policy, reserves the `serverName` inside the current registration scope, starts the supervisor, and awaits the initial connection plus discovery. Independent Agent scopes may reuse the same namespace because their tools and transports are isolated; a duplicate inside one scope fails at load. The supervisor serializes every sync — initial, notification, and reconnect — through one queue so two syncs can never interleave their dispose-previous/register-next swap. Disposal cancels pending reconnects, closes the negotiating transport or attached client, waits for the in-flight attempt and queued syncs to quiesce, and unregisters the current generation.
|
|
125
129
|
|
|
126
|
-
The
|
|
130
|
+
The SDK receives tool-list changes through legacy notifications or a modern subscription. The supervisor queues each re-sync; a fetch failure keeps the previous generation registered, while a registration conflict rolls back the attempted generation. Each outage shares one attempt budget: after `maxAttempts` consecutive failures the tools are unregistered and reconnection stops, and a connection that stays up past `maxDelayMs` resets the budget.
|
|
127
131
|
|
|
128
132
|
### Tool execution internals
|
|
129
133
|
|
|
130
|
-
A tool call
|
|
134
|
+
A tool call uses the SDK with the raw name, complete tool definition, JSON arguments, abort signal, and configured timeout. The SDK owns protocol validation, advertised output-schema validation, and modern request headers. Canonical success is `{ content: JsonValue[], structuredContent? }`, preserving valid MCP JSON blocks for programmatic and PTC mode callers. An MCP `isError` result throws before image persistence. The bridge validates each image batch before saving it; a refusal projects every image as diagnostic text.
|
|
131
135
|
|
|
132
136
|
### Environment scrubbing (stdio)
|
|
133
137
|
|
|
@@ -157,11 +161,11 @@ Read these pages when the package-level contract is not enough. They move from t
|
|
|
157
161
|
|
|
158
162
|
#### What the model sees
|
|
159
163
|
|
|
160
|
-
After
|
|
164
|
+
After discovery succeeds, SDK-admitted MCP tools appear as native tools named `mcp__<serverName>__<rawName>` (or their deterministic normalized form), with the server description and input schema. A re-sync replaces the generation; disposal or an exhausted reconnect budget removes it. A server without the tools capability connects with an empty tool set.
|
|
161
165
|
|
|
162
166
|
#### Token effect
|
|
163
167
|
|
|
164
|
-
The tool descriptions and input schemas enter every request while the tools are registered; re-syncs replace rather than accumulate schemas, and the server-qualified name adds tokens to every tool definition and call.
|
|
168
|
+
The tool descriptions and input schemas enter every request while the tools are registered; re-syncs replace rather than accumulate schemas, and the server-qualified name adds tokens to every tool definition and call. A configured client also enables the [shared resource tools and server-name prompt](../mcp-resources/README.md#model-experience).
|
|
165
169
|
|
|
166
170
|
#### KV Cache effect
|
|
167
171
|
|
|
@@ -181,6 +185,20 @@ Arguments, mapped text, and durable image references are retained until compacti
|
|
|
181
185
|
|
|
182
186
|
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
|
|
183
187
|
|
|
188
|
+
### Server instructions
|
|
189
|
+
|
|
190
|
+
#### What the model sees
|
|
191
|
+
|
|
192
|
+
One server-labeled section contains the nonblank instructions returned by each successful connection. Absent or blank instructions add no prompt text. Braces remain literal. A replacement connection publishes its instructions only after discovery succeeds; disposal or exhausted recovery removes the section.
|
|
193
|
+
|
|
194
|
+
#### Token effect
|
|
195
|
+
|
|
196
|
+
Server instructions contribute text to model requests while their scoped section is active. Resource documents enter history only through explicit resource reads.
|
|
197
|
+
|
|
198
|
+
#### KV Cache effect
|
|
199
|
+
|
|
200
|
+
Unchanged instructions retain identical prompt text. Updated or removed instructions change the next assembled system message and its reusable prefix.
|
|
201
|
+
|
|
184
202
|
## Known Limitations and Deferred Work
|
|
185
203
|
|
|
186
204
|
<a id="known-limitations-and-deferred-work"></a>
|
|
@@ -188,11 +206,11 @@ Append-only; newly visible content follows the reusable request prefix and does
|
|
|
188
206
|
|
|
189
207
|
These limits describe what you cannot do with this plugin and when it needs operational attention. They are current package constraints, not a comparison with other MCP clients or a task backlog.
|
|
190
208
|
|
|
191
|
-
- **
|
|
192
|
-
- **Startup and discovery timeouts are inherited from the MCP SDK** — the plugin exposes no connection or discovery timeout
|
|
193
|
-
- **Reconnect
|
|
209
|
+
- **Resources are read on demand** — shipped profiles provide the [shared resource service](../mcp-resources/README.md); resource subscriptions and MCP prompt templates are unsupported.
|
|
210
|
+
- **Startup and discovery timeouts are inherited from the MCP SDK** — the plugin exposes no separate connection or discovery timeout. Negotiation and discovery use the SDK's 60-second request default; discovery also uses its page limit. Plugin unload closes the transport to interrupt pending startup requests before awaiting teardown.
|
|
211
|
+
- **Reconnect handles failed negotiation and transport close** — a failed initial probe or crashed stdio child uses the configured reconnect budget. Once HTTP is connected, request failures use the SDK transport's recovery rather than respawning the connection.
|
|
194
212
|
- **Image is the only durable rich-result bridge** — PNG, JPEG, WebP, and GIF enter Native context after exact capability proof. Audio and embedded-resource payloads remain execution-local with explicit diagnostics, while resource links preserve only their name and URI as text.
|
|
195
|
-
- **
|
|
213
|
+
- **Invalid protocol results or output schemas fail through the SDK** — the bridge does not accept legacy `toolResult` substitutes or bypass advertised schema validation.
|
|
196
214
|
- **Task-required MCP tools are rejected at call time** — a tool that requires the task-based execution extension throws instead of bridging; the extension is not implemented.
|
|
197
215
|
|
|
198
216
|
<a id="dev-note"></a>
|
|
@@ -204,9 +222,9 @@ These limits describe what you cannot do with this plugin and when it needs oper
|
|
|
204
222
|
This Dev Note is working context for maintainers: open design questions and directions that are not decided. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes.
|
|
205
223
|
|
|
206
224
|
- The public-name algorithm is a v1 contract pinned by tests; changing it after release would break session history and permission rules.
|
|
207
|
-
- An explicit DSH-owned connection and discovery timeout is an open direction; the SDK's 60-second default bounds startup
|
|
225
|
+
- An explicit DSH-owned connection and discovery timeout is an open direction; the SDK's 60-second default bounds startup requests.
|
|
208
226
|
- Reconnect ownership for Streamable HTTP is open: per-request retry is SDK behavior, and the supervisor could also own the HTTP generation.
|
|
209
|
-
-
|
|
227
|
+
- MCP prompt templates need a separate user-selection and invocation mechanism.
|
|
210
228
|
- The pinned MCP SDK is still evolving; a breaking upstream change requires updating the bridge.
|
|
211
229
|
|
|
212
230
|
</details>
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`dsh-mcp-client`
|
|
12
|
+
`dsh-mcp-client` 让模型使用外部 MCP(Model Context Protocol)服务器的工具与资源。每台服务器配置一条记录;其工具使用 `mcp__github__create_issue` 这样的名称。默认不启用任何服务器。随附 profile 已提供[共享资源发现与读取](../mcp-resources/README.zh.md)。调用方作用域为空时,不添加 MCP 工具或提示词文本。服务器指令作为字面文本加入已记录的系统提示词;MCP 提示词模板不受支持。缓慢或崩溃的服务器可能延迟启动,或让调用失败直至恢复。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -25,7 +25,7 @@ kind: "package-reference"
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## 使用本包
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
当模型需要像调用原生工具一样调用外部 MCP 服务器时,添加 `dsh-mcp-client`。为每台服务器指定唯一名称和传输方式。官方 SDK 优先选择可用的 2026-07-28 协议,并回退到支持的旧版协议。本地程序使用 stdio,远端服务使用 Streamable HTTP;stdio 协商会先启动临时探测进程,再启动实际服务进程。
|
|
29
29
|
|
|
30
30
|
### 最小配置
|
|
31
31
|
|
|
@@ -58,16 +58,17 @@ kind: "package-reference"
|
|
|
58
58
|
| `serverName` | 必填 | 服务器工具名称的 namespace;`[A-Za-z0-9_-]{1,32}`,在一个注册作用域内唯一 |
|
|
59
59
|
| `command` / `args` / `env` / `cwd` | — | stdio:可执行文件、参数、合并到清洗过的环境之上的额外环境变量、工作目录 |
|
|
60
60
|
| `url` / `headers` | — | streamable-http:端点 URL 与额外请求标头 |
|
|
61
|
-
| `toolCallTimeoutMs` | `60,000` | 每次 `tools/call`
|
|
61
|
+
| `toolCallTimeoutMs` | `60,000` | 每次 `tools/call` 或资源请求的超时 |
|
|
62
|
+
| `maxInstructionBytes` | `32,768` | 包括服务器归属信息在内的服务器指令 UTF-8 字节上限;超出时连接失败 |
|
|
62
63
|
| `failOnStartupError` | `false` | 初始连接或工具同步失败时拒绝插件激活 |
|
|
63
64
|
| `reconnect.enabled` | `true` | 连接丢失后自动重新连接 |
|
|
64
65
|
| `reconnect.initialDelayMs` | `500` | 首次重连延迟;每次连续失败尝试翻倍 |
|
|
65
66
|
| `reconnect.maxDelayMs` | `30,000` | 退避上限;同时是重置尝试预算所需的正常运行时长 |
|
|
66
67
|
| `reconnect.maxAttempts` | `10` | 每次中断内连续失败尝试次数上限,超出后放弃 |
|
|
67
68
|
|
|
68
|
-
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-mcp-client)
|
|
69
|
+
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-mcp-client)是每个受支持字段的穷尽式真源。
|
|
69
70
|
|
|
70
|
-
启动后,服务器的工具会以 `mcp__<serverName>__<tool>` 形式出现——试着用一条提示词调用其中一个。如果初始连接失败,harness
|
|
71
|
+
启动后,服务器的工具会以 `mcp__<serverName>__<tool>` 形式出现——试着用一条提示词调用其中一个。如果初始连接失败,harness 仍会启动,但该服务器的工具不会出现,并会记录一条错误。设置 `failOnStartupError: true` 会拒绝插件激活;[app-boot 的启动策略](../../boot/app-boot/README.zh.md)仍允许可选 MCP 配置项失败,而不中止 harness。
|
|
71
72
|
|
|
72
73
|
### 工具命名与共存
|
|
73
74
|
|
|
@@ -76,7 +77,7 @@ kind: "package-reference"
|
|
|
76
77
|
- 发布相同工具名称(例如 `search`)的两个服务器会在各自的 namespace 下共存。
|
|
77
78
|
- 两条配置项使用相同的服务器名称时,后加载的一条会在加载时以明确错误失败。
|
|
78
79
|
- 服务器在工具列表中两次列出同一工具时,其工具列表会被作为无效列表拒绝,上一组工具保持可用。
|
|
79
|
-
-
|
|
80
|
+
- SDK 负责发现分页及页数上限。发现失败会保留之前的工具;格式错误的游标链遵循 SDK 的处理行为。
|
|
80
81
|
- 工具更新与已有工具名称冲突时,该更新会被整体拒绝——绝不会得到该服务器的部分工具集。
|
|
81
82
|
|
|
82
83
|
### 调用工具与读取结果
|
|
@@ -104,7 +105,7 @@ kind: "package-reference"
|
|
|
104
105
|
### 设计理念
|
|
105
106
|
|
|
106
107
|
- **服务器限定身份。** 每个 MCP 工具都有稳定的身份 `(serverName, rawName)`。namespace 是本地配置,绝不采用远程 `serverInfo.name`——远程名称不可信、在部署间不唯一、且升级时可能变化,这些都不允许静默重命名面向模型的工具。
|
|
107
|
-
- **命名是固定约定。** 公开名称是 `(serverName, rawName)` 的纯函数,并满足 DeepSeek 函数名称约定;有损规范化会追加 12 位十六进制 SHA-256 hash,使不同身份绝不会折叠。会话历史与权限规则因此能在 HMR
|
|
108
|
+
- **命名是固定约定。** 公开名称是 `(serverName, rawName)` 的纯函数,并满足 DeepSeek 函数名称约定;有损规范化会追加 12 位十六进制 SHA-256 hash,使不同身份绝不会折叠。会话历史与权限规则因此能在 HMR(热模块替换)、重新同步和其他服务器变化后保持有效。
|
|
108
109
|
- **原始名称是唯一的协议名称。** `tools/call` 始终收到原始名称;公开名称绝不会发给服务器,也绝不会被解析来还原原始名称。
|
|
109
110
|
- **要么完整世代,要么没有。** 同步会原子地交换世代:获取失败保留上一世代,注册冲突则回滚整个尝试中的世代。
|
|
110
111
|
- **一个规范值,一个投影。** 执行器返回协议完整的规范 `McpResult`;另一个有序投影准备 Native 内容,`finalizeContent` 只在注册表的执行后结果未变时安装它,因此策略块与值替换保持权威。
|
|
@@ -114,20 +115,23 @@ kind: "package-reference"
|
|
|
114
115
|
| 文件 | 职责 |
|
|
115
116
|
|---|---|
|
|
116
117
|
| [`src/index.ts`](src/index.ts) | 插件入口:`Config` schema、`serverName` 预留、激活等待 |
|
|
117
|
-
| [`src/connection.ts`](src/connection.ts) | 连接监督器:客户端世代、重连策略、尝试预算、dispose |
|
|
118
|
+
| [`src/connection.ts`](src/connection.ts) | 连接监督器:客户端世代、重连策略、尝试预算、dispose(资源释放) |
|
|
119
|
+
| [`src/server-context.ts`](src/server-context.ts) | 资源提供方注册与字面服务器指令 |
|
|
118
120
|
| [`src/tools.ts`](src/tools.ts) | 工具桥接:发现、命名、注册交换、执行、图片投影 |
|
|
119
121
|
| [`src/transport.ts`](src/transport.ts) | 传输工厂:带清洗环境的 stdio spawn、Streamable HTTP |
|
|
120
|
-
| — |
|
|
122
|
+
| — | 不发布运行时不变式伴生入口;MCP 世代会通过工具注册表发挥作用,但桥接在异步重新同步后不提供独立的服务器工具映射快照。 |
|
|
123
|
+
|
|
124
|
+
导出的 `createMcpToolDefinition(ctx, options)` 将上游工具 schema 和原始结果回调适配到相同的规范值、错误和持久化图像投影。每次回调都收到原样的 `ToolExecution`,包括其 Agent 和取消信号;SDK 的规范类型校验会在投影前检查返回结果。调用方负责注册、取消截止时间和提供方卸载。原生 Cua Driver 提供方使用此适配函数,无需打开 MCP 传输。
|
|
121
125
|
|
|
122
126
|
### 生命周期与同步
|
|
123
127
|
|
|
124
|
-
`apply` 解析重连策略、在当前注册作用域内预留 `serverName`、启动监督器,并等待初始连接加发现完成。独立
|
|
128
|
+
`apply` 解析重连策略、在当前注册作用域内预留 `serverName`、启动监督器,并等待初始连接加发现完成。独立 agent(智能体)作用域可以复用相同 namespace,因为其工具与传输彼此隔离;同一作用域内重复会在加载时失败。监督器把所有同步——初始、通知与重连——串行到同一条队列,因此两次同步绝不会交错执行各自的先 dispose 后注册交换。dispose 会取消待执行的重连、关闭协商中的传输或已绑定的客户端、等待进行中的尝试与排队同步完全停稳,然后注销当前世代。
|
|
125
129
|
|
|
126
|
-
|
|
130
|
+
SDK 通过旧版通知或现代协议订阅接收工具列表变化。监督器将每次重新同步排队;获取失败时保留之前的注册代,注册冲突则回滚本次尝试。每次故障共享一个尝试预算:连续失败达到 `maxAttempts` 后注销工具并停止重连;连接持续超过 `maxDelayMs` 则重置预算。
|
|
127
131
|
|
|
128
132
|
### 工具执行内部细节
|
|
129
133
|
|
|
130
|
-
|
|
134
|
+
工具调用向 SDK 提供原始名称、完整工具定义、JSON 参数、取消信号及配置的超时。SDK 负责协议校验、已声明输出 schema 的校验和现代协议请求 header。成功结果规范值为 `{ content: JsonValue[], structuredContent? }`,为编程调用方及 PTC 模式保留有效的 MCP JSON 块。MCP `isError` 结果会在图片持久化前抛出。桥接器在保存前校验整批图片;拒绝时将每张图片投影为诊断文本。
|
|
131
135
|
|
|
132
136
|
### 环境清洗(stdio)
|
|
133
137
|
|
|
@@ -157,11 +161,11 @@ kind: "package-reference"
|
|
|
157
161
|
|
|
158
162
|
#### 模型看到什么
|
|
159
163
|
|
|
160
|
-
|
|
164
|
+
发现成功后,SDK 接受的 MCP 工具以原生工具名称 `mcp__<serverName>__<rawName>`(或其确定性规范化形式)出现,携带服务器描述和输入 schema。重新同步会替换注册代;释放或重连预算耗尽会移除工具。未声明 tools 能力的服务器以空工具集连接。
|
|
161
165
|
|
|
162
166
|
#### Token 影响
|
|
163
167
|
|
|
164
|
-
工具注册期间,工具描述与输入 schema 会进入每次请求;重新同步会替换而非累积 schema,服务器限定名称也会为每个工具定义和调用增加 token。
|
|
168
|
+
工具注册期间,工具描述与输入 schema 会进入每次请求;重新同步会替换而非累积 schema,服务器限定名称也会为每个工具定义和调用增加 token。已配置客户端还会启用[共享资源工具与服务器名称提示词](../mcp-resources/README.zh.md#model-experience)。
|
|
165
169
|
|
|
166
170
|
#### KV Cache 影响
|
|
167
171
|
|
|
@@ -171,7 +175,7 @@ kind: "package-reference"
|
|
|
171
175
|
|
|
172
176
|
#### 模型看到什么
|
|
173
177
|
|
|
174
|
-
公开工具名称和 JSON 参数保留在 assistant 历史中。规范值始终为程序化调用方与 PTC
|
|
178
|
+
公开工具名称和 JSON 参数保留在 assistant 历史中。规范值始终为程序化调用方与 PTC 模式调用方保留完整的 MCP JSON 块与可选结构化内容;受支持的图片块在确切路由能力得到证明后,按原始顺序与文本一起投影。被拒绝的图片、音频、嵌入资源、资源链接与未知块继续以有界文本诊断可见;MCP `isError` 会在图片持久化之前拒绝调用。
|
|
175
179
|
|
|
176
180
|
#### Token 影响
|
|
177
181
|
|
|
@@ -181,6 +185,20 @@ kind: "package-reference"
|
|
|
181
185
|
|
|
182
186
|
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
|
|
183
187
|
|
|
188
|
+
### 服务器指令
|
|
189
|
+
|
|
190
|
+
#### 模型看到什么
|
|
191
|
+
|
|
192
|
+
每个成功连接返回的非空白指令保存在一个带服务器名称的段落中。未返回指令或指令仅含空白时,不向提示词添加文本。花括号保持字面值。替代连接仅在发现成功后发布其指令;释放或耗尽恢复预算时移除该段落。
|
|
193
|
+
|
|
194
|
+
#### Token 影响
|
|
195
|
+
|
|
196
|
+
作用域段落生效期间,服务器指令为模型请求贡献文本。资源文档仅通过显式资源读取进入历史。
|
|
197
|
+
|
|
198
|
+
#### KV Cache 影响
|
|
199
|
+
|
|
200
|
+
未变化的指令保留相同提示词文本。更新或移除指令会改变下一次组装的系统消息及其可复用前缀。
|
|
201
|
+
|
|
184
202
|
## 已知限制与延期工作
|
|
185
203
|
|
|
186
204
|
<a id="known-limitations-and-deferred-work"></a>
|
|
@@ -188,11 +206,11 @@ kind: "package-reference"
|
|
|
188
206
|
|
|
189
207
|
这些限制说明你无法用本插件做什么、以及何时需要运维注意。它们是当前包约束,不是与其他 MCP 客户端的对比,也不是任务积压。
|
|
190
208
|
|
|
191
|
-
-
|
|
192
|
-
- **启动与发现超时继承自 MCP SDK
|
|
193
|
-
-
|
|
209
|
+
- **资源按需读取**——随附 profile 提供[共享资源服务](../mcp-resources/README.zh.md);资源订阅与 MCP 提示词模板不受支持。
|
|
210
|
+
- **启动与发现超时继承自 MCP SDK**——插件不暴露单独的连接或发现超时。协商与发现使用 SDK 默认的 60 秒请求超时;发现也使用 SDK 的页数上限。插件卸载先关闭传输以中断待处理的启动请求,再等待清理。
|
|
211
|
+
- **重连处理协商失败与传输关闭**——初始探测失败或 stdio 子进程崩溃都会使用配置的重连预算。HTTP 建立连接后,请求失败使用 SDK 传输的恢复机制,而非重新创建连接。
|
|
194
212
|
- **图片是唯一的持久丰富结果桥接**——PNG、JPEG、WebP 与 GIF 在确切能力得到证明后进入 Native 上下文。音频与嵌入资源载荷仍只存在于执行局部并带明确诊断,资源链接只以文本保留名称与 URI。
|
|
195
|
-
-
|
|
213
|
+
- **无效的协议结果或输出 schema 由 SDK 拒绝**——桥接器不接受旧式 `toolResult` 替代结果,也不绕过已声明的 schema 校验。
|
|
196
214
|
- **要求基于任务的 MCP 工具在调用时被拒绝**——要求使用基于任务的执行(task-based execution)扩展的工具会抛出异常而非被桥接;该扩展未实现。
|
|
197
215
|
|
|
198
216
|
<a id="dev-note"></a>
|
|
@@ -204,9 +222,9 @@ kind: "package-reference"
|
|
|
204
222
|
本开发备注是维护者的工作上下文:开放设计问题与尚未决定的探索方向。它明确不具权威性——已交付行为、限制与既定理由以上文、包代码与所链接的 Agent Note 为准。
|
|
205
223
|
|
|
206
224
|
- 公开名称算法是由测试固定的 v1 约定;发布后更改会破坏会话历史与权限规则。
|
|
207
|
-
- 由 DSH 显式拥有的连接与发现超时是开放的探索方向;SDK 的 60
|
|
225
|
+
- 由 DSH 显式拥有的连接与发现超时是开放的探索方向;SDK 的 60 秒默认值约束着启动请求。
|
|
208
226
|
- Streamable HTTP 的重连归属仍未决定:按请求重试是 SDK 行为,supervisor 也可以拥有 HTTP 世代。
|
|
209
|
-
-
|
|
227
|
+
- MCP 提示词模板需要独立的用户选择和模板调用机制。
|
|
210
228
|
- 固定的 MCP SDK 仍在演化;上游破坏性变更需要更新桥接。
|
|
211
229
|
|
|
212
230
|
</details>
|
package/lib/index.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
3
3
|
import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
|
|
4
|
-
import { Client } from "@modelcontextprotocol/
|
|
5
|
-
import {
|
|
6
|
-
import { StdioClientTransport } from "@modelcontextprotocol/
|
|
7
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
|
+
import { Client, StreamableHTTPClientTransport, specTypeSchemas } from "@modelcontextprotocol/client";
|
|
5
|
+
import { assertNever } from "@deepseek-ai/dsh-util-values";
|
|
6
|
+
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
8
7
|
import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
|
|
9
8
|
import { createHash } from "node:crypto";
|
|
10
9
|
import { isDeepStrictEqual } from "node:util";
|
|
11
|
-
import { z as z$1 } from "zod";
|
|
12
10
|
import { isImageAdmissionError } from "@deepseek-ai/dsh-attachment";
|
|
13
11
|
import { assertSupportedJsonSchema } from "@deepseek-ai/dsh-tools";
|
|
14
12
|
//#region lib/types/transport.js
|
|
@@ -72,8 +70,6 @@ const MAX_PUBLIC_NAME_LENGTH = 64;
|
|
|
72
70
|
const INVALID_NAME_CHARS = /[^A-Za-z0-9_-]/g;
|
|
73
71
|
/** Hex chars of the SHA-256 identity hash appended on lossy normalization. */
|
|
74
72
|
const HASH_LENGTH = 12;
|
|
75
|
-
/** Raw result record: the bridge owns JSON-value validation after transport. */
|
|
76
|
-
const RawCallToolResultSchema = z$1.record(z$1.string(), z$1.unknown());
|
|
77
73
|
/** Raster formats supported by the durable attachment vocabulary. */
|
|
78
74
|
const IMAGE_MEDIA_TYPES = [
|
|
79
75
|
"image/png",
|
|
@@ -83,26 +79,6 @@ const IMAGE_MEDIA_TYPES = [
|
|
|
83
79
|
];
|
|
84
80
|
/** Canonical RFC 4648 base64, excluding whitespace and URL-safe aliases. */
|
|
85
81
|
const CANONICAL_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
86
|
-
/** List without mutating the SDK's per-page output-validator cache. */
|
|
87
|
-
function listToolsUncached(client, cursor) {
|
|
88
|
-
return client.request({
|
|
89
|
-
method: "tools/list",
|
|
90
|
-
...cursor === void 0 ? {} : { params: { cursor } }
|
|
91
|
-
}, ListToolsResultSchema);
|
|
92
|
-
}
|
|
93
|
-
/** Call without the SDK pre-validating an output schema the bridge may not support. */
|
|
94
|
-
function callToolUncached(client, rawName, args, exec, opts) {
|
|
95
|
-
return client.request({
|
|
96
|
-
method: "tools/call",
|
|
97
|
-
params: {
|
|
98
|
-
name: rawName,
|
|
99
|
-
arguments: args
|
|
100
|
-
}
|
|
101
|
-
}, RawCallToolResultSchema, {
|
|
102
|
-
signal: exec.signal,
|
|
103
|
-
timeout: opts.toolCallTimeoutMs
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
82
|
/**
|
|
107
83
|
* Derive the model-facing public name for one MCP tool.
|
|
108
84
|
*
|
|
@@ -129,9 +105,9 @@ function publicToolName(serverName, rawName) {
|
|
|
129
105
|
*
|
|
130
106
|
* Two phases keep the swap safe:
|
|
131
107
|
*
|
|
132
|
-
* 1. Fetch:
|
|
108
|
+
* 1. Fetch: let the SDK aggregate `tools/list` and build the full next
|
|
133
109
|
* generation of `ToolDefinition`s under public names. Any failure here
|
|
134
|
-
* (network error
|
|
110
|
+
* (network error or duplicate raw name) rejects
|
|
135
111
|
* and leaves the previous generation registered untouched.
|
|
136
112
|
* 2. Swap: dispose the previous generation, register the new one. A registry
|
|
137
113
|
* conflict here can only mean a foreign registration squats on this
|
|
@@ -150,21 +126,27 @@ function publicToolName(serverName, rawName) {
|
|
|
150
126
|
*/
|
|
151
127
|
async function syncTools(client, ctx, opts, previous) {
|
|
152
128
|
const definitions = /* @__PURE__ */ new Map();
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
129
|
+
const response = client.getServerCapabilities()?.tools === void 0 ? { tools: [] } : await client.listTools(void 0, { cacheMode: "refresh" });
|
|
130
|
+
for (const tool of response.tools) {
|
|
131
|
+
const publicName = publicToolName(opts.serverName, tool.name);
|
|
132
|
+
if (definitions.has(publicName)) throw new Error(`mcp-client(${opts.serverName}): server listed tool "${tool.name}" more than once — invalid tool list`);
|
|
133
|
+
definitions.set(publicName, createMcpToolDefinition(ctx, {
|
|
134
|
+
name: publicName,
|
|
135
|
+
rawName: tool.name,
|
|
136
|
+
description: tool.description ?? "",
|
|
137
|
+
inputSchema: tool.inputSchema,
|
|
138
|
+
outputSchema: tool.outputSchema,
|
|
139
|
+
taskRequired: tool.execution?.taskSupport === "required",
|
|
140
|
+
call: (args, execution) => client.callTool({
|
|
141
|
+
name: tool.name,
|
|
142
|
+
arguments: args
|
|
143
|
+
}, {
|
|
144
|
+
signal: execution.signal,
|
|
145
|
+
timeout: opts.toolCallTimeoutMs,
|
|
146
|
+
toolDefinition: tool
|
|
147
|
+
})
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
168
150
|
for (const dispose of previous.values()) dispose();
|
|
169
151
|
const disposers = /* @__PURE__ */ new Map();
|
|
170
152
|
try {
|
|
@@ -188,26 +170,21 @@ function supportedOutputSchema(candidate) {
|
|
|
188
170
|
}
|
|
189
171
|
}
|
|
190
172
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
173
|
+
* Adapt an upstream MCP tool to canonical values and durable image content.
|
|
174
|
+
* Registration, provider lifetime, deadlines, and transport belong to the caller.
|
|
193
175
|
* @param ctx - plugin context carrying optional attachment and model services.
|
|
194
|
-
* @param
|
|
195
|
-
* @
|
|
196
|
-
* @param description - model-facing tool description.
|
|
197
|
-
* @param parameters - MCP input schema.
|
|
198
|
-
* @param structuredSchema - supported structured-output schema, when advertised.
|
|
199
|
-
* @param taskRequired - whether this MCP tool requires unsupported task execution.
|
|
200
|
-
* @param opts - bridge timeout and namespace options.
|
|
201
|
-
* @returns a complete ToolRuntime definition.
|
|
176
|
+
* @param options - upstream tool fields and its raw-result callback.
|
|
177
|
+
* @returns the unregistered ToolRuntime definition.
|
|
202
178
|
*/
|
|
203
|
-
function
|
|
179
|
+
function createMcpToolDefinition(ctx, options) {
|
|
180
|
+
const { name, rawName, description, inputSchema } = options;
|
|
204
181
|
const projections = /* @__PURE__ */ new WeakMap();
|
|
205
182
|
return {
|
|
206
|
-
name
|
|
183
|
+
name,
|
|
207
184
|
description,
|
|
208
|
-
parameters,
|
|
209
|
-
output: createOutput(rawName,
|
|
210
|
-
execute: createExecutor(
|
|
185
|
+
parameters: inputSchema,
|
|
186
|
+
output: createOutput(rawName, supportedOutputSchema(options.outputSchema)),
|
|
187
|
+
execute: createExecutor(ctx, options, projections),
|
|
211
188
|
finalizeContent(exec, result) {
|
|
212
189
|
const projection = projections.get(exec);
|
|
213
190
|
if (projection === void 0) return void 0;
|
|
@@ -243,31 +220,17 @@ function createOutput(rawName, structuredSchema) {
|
|
|
243
220
|
};
|
|
244
221
|
}
|
|
245
222
|
/**
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* the public name), with abort signal and timeout, then maps the result to
|
|
249
|
-
* harness ContentBlocks. Owning the raw request prevents the SDK's internal
|
|
250
|
-
* per-page schema cache from pre-validating a different contract.
|
|
251
|
-
*
|
|
252
|
-
* When the MCP server returns `isError: true`, the executor throws so that
|
|
253
|
-
* the ToolRuntime's catch path produces an `isError` result for the model.
|
|
223
|
+
* Invoke the caller-owned raw-result callback and prepare canonical content.
|
|
224
|
+
* MCP isError results reject before image storage so ToolRuntime records failure.
|
|
254
225
|
*/
|
|
255
|
-
function createExecutor(
|
|
226
|
+
function createExecutor(ctx, options, projections) {
|
|
227
|
+
const { rawName, taskRequired } = options;
|
|
256
228
|
return async (args, exec) => {
|
|
257
229
|
if (taskRequired) throw new Error(`Tool "${rawName}" requires task-based execution, which this bridge does not support`);
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (result.isError === true) throw new Error(text);
|
|
263
|
-
return {
|
|
264
|
-
content: [{
|
|
265
|
-
type: "text",
|
|
266
|
-
text
|
|
267
|
-
}],
|
|
268
|
-
...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {}
|
|
269
|
-
};
|
|
270
|
-
}
|
|
230
|
+
const argsObj = typeof args === "object" && args !== null ? args : {};
|
|
231
|
+
const parsed = specTypeSchemas.CallToolResult["~standard"].validate(await options.call(argsObj, exec));
|
|
232
|
+
if (parsed.issues !== void 0) throw new Error(`Tool "${rawName}" returned an invalid MCP result: ${parsed.issues.map((issue) => issue.message).join("; ")}`);
|
|
233
|
+
const result = parsed.value;
|
|
271
234
|
const content = result.content;
|
|
272
235
|
const text = extractText(content, rawName);
|
|
273
236
|
if (result.isError === true) throw new Error(text);
|
|
@@ -302,10 +265,10 @@ function isRecord(value) {
|
|
|
302
265
|
function isImageMediaType(value) {
|
|
303
266
|
return IMAGE_MEDIA_TYPES.includes(value);
|
|
304
267
|
}
|
|
305
|
-
/** Decode one
|
|
268
|
+
/** Decode one projected image without accepting base64 aliases. */
|
|
306
269
|
function decodeImage(block) {
|
|
307
|
-
if (
|
|
308
|
-
if (
|
|
270
|
+
if (!isImageMediaType(block.mimeType)) throw new Error("the declared media type is not PNG, JPEG, WebP, or GIF");
|
|
271
|
+
if (!CANONICAL_BASE64.test(block.data)) throw new Error("the image data is not canonical base64");
|
|
309
272
|
const data = Buffer.from(block.data, "base64");
|
|
310
273
|
if (data.toString("base64") !== block.data) throw new Error("the image data is not canonical base64");
|
|
311
274
|
return {
|
|
@@ -393,8 +356,7 @@ async function prepareImageProjection(ctx, exec, content, toolName) {
|
|
|
393
356
|
* - text blocks: join with '\n'
|
|
394
357
|
* - image/audio/resource blocks: replaced with a placeholder
|
|
395
358
|
*
|
|
396
|
-
*
|
|
397
|
-
* guarded with fallbacks because this is a network trust boundary.
|
|
359
|
+
* Policy-owned canonical-value replacements may omit fields required on the MCP wire.
|
|
398
360
|
*/
|
|
399
361
|
function extractText(mcpContent, toolName) {
|
|
400
362
|
return projectContent(mcpContent, toolName).map((block) => block.text).join("\n");
|
|
@@ -475,6 +437,8 @@ const RECONNECT_DEFAULTS = Object.freeze({
|
|
|
475
437
|
maxDelayMs: 3e4,
|
|
476
438
|
maxAttempts: 10
|
|
477
439
|
});
|
|
440
|
+
/** Default UTF-8 byte limit for attributed server instructions. */
|
|
441
|
+
const DEFAULT_MAX_INSTRUCTION_BYTES = 32768;
|
|
478
442
|
const GENERATION_CLOSE_TIMEOUT_MS = 5e3;
|
|
479
443
|
/**
|
|
480
444
|
* The one explicit resolve step from raw reconnect config to the policy the
|
|
@@ -516,6 +480,7 @@ function resolveReconnectPolicy(config, path) {
|
|
|
516
480
|
*/
|
|
517
481
|
function startConnection(ctx, config, policy) {
|
|
518
482
|
const label = `mcp-client(${config.serverName})`;
|
|
483
|
+
const incompleteDisposalMessage = `${label}: transport closure could not be confirmed during disposal — server shutdown may be incomplete`;
|
|
519
484
|
const opts = {
|
|
520
485
|
registrationFailure: "contain",
|
|
521
486
|
serverName: config.serverName,
|
|
@@ -526,10 +491,12 @@ function startConnection(ctx, config, policy) {
|
|
|
526
491
|
registrationFailure: "throw"
|
|
527
492
|
} : opts;
|
|
528
493
|
let disposed = false;
|
|
494
|
+
const maxInstructionBytes = config.maxInstructionBytes ?? 32768;
|
|
495
|
+
let serverInstructions = "";
|
|
529
496
|
/** Current generation: the connecting or connected client; undefined during backoff waits and after final failure. */
|
|
530
497
|
let client;
|
|
531
|
-
/**
|
|
532
|
-
let
|
|
498
|
+
/** Transport-aware close operation paired with {@link client}. */
|
|
499
|
+
let closeClient;
|
|
533
500
|
/** Live tool registrations owned by this server; only {@link enqueueSync} and dispose swap it. */
|
|
534
501
|
let disposers = /* @__PURE__ */ new Map();
|
|
535
502
|
let reconnectTimer;
|
|
@@ -560,9 +527,20 @@ function startConnection(ctx, config, policy) {
|
|
|
560
527
|
function generationDown(generation) {
|
|
561
528
|
if (!isCurrent(generation)) return;
|
|
562
529
|
client = void 0;
|
|
563
|
-
|
|
530
|
+
closeClient = void 0;
|
|
564
531
|
scheduleReconnect();
|
|
565
532
|
}
|
|
533
|
+
/** Decide retry ownership after a failed connection's close barrier settles. */
|
|
534
|
+
function settleFailedGeneration(generation, quiesced) {
|
|
535
|
+
if (!isCurrent(generation)) return;
|
|
536
|
+
if (!quiesced) {
|
|
537
|
+
client = void 0;
|
|
538
|
+
closeClient = void 0;
|
|
539
|
+
ctx.logger.error(`${label}: failed generation could not confirm transport closure — reconnect stopped to avoid overlapping server processes; reload the plugin or restart the Host to retry`);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
generationDown(generation);
|
|
543
|
+
}
|
|
566
544
|
/** Wait for the transport-owned close signal without letting a broken transport wedge teardown forever. */
|
|
567
545
|
function waitForClose(closed) {
|
|
568
546
|
return new Promise((resolve) => {
|
|
@@ -590,6 +568,7 @@ function startConnection(ctx, config, policy) {
|
|
|
590
568
|
syncChain = syncChain.then(() => {
|
|
591
569
|
for (const dispose of disposers.values()) dispose();
|
|
592
570
|
disposers = /* @__PURE__ */ new Map();
|
|
571
|
+
serverInstructions = "";
|
|
593
572
|
});
|
|
594
573
|
ctx.logger.error(`${label}: giving up after ${policy.maxAttempts} consecutive failed reconnect attempts — tools unregistered; reload the plugin or restart the Host to reconnect`);
|
|
595
574
|
return;
|
|
@@ -617,19 +596,40 @@ function startConnection(ctx, config, policy) {
|
|
|
617
596
|
const generation = new Client({
|
|
618
597
|
name: "dsh-mcp-client",
|
|
619
598
|
version: "0.0.1"
|
|
620
|
-
}, {
|
|
599
|
+
}, {
|
|
600
|
+
capabilities: {},
|
|
601
|
+
versionNegotiation: { mode: "auto" },
|
|
602
|
+
listChanged: { tools: {
|
|
603
|
+
autoRefresh: false,
|
|
604
|
+
debounceMs: 0,
|
|
605
|
+
onChanged: () => {
|
|
606
|
+
refreshTools();
|
|
607
|
+
}
|
|
608
|
+
} }
|
|
609
|
+
});
|
|
621
610
|
const closed = Promise.withResolvers();
|
|
622
611
|
let attemptSettled = false;
|
|
623
612
|
let closeObserved = false;
|
|
613
|
+
let transport;
|
|
624
614
|
const hasClosed = () => closeObserved;
|
|
625
615
|
client = generation;
|
|
626
|
-
|
|
616
|
+
closeClient = closeGeneration;
|
|
627
617
|
generation.onclose = () => {
|
|
628
618
|
closeObserved = true;
|
|
629
619
|
closed.resolve();
|
|
630
620
|
if (attemptSettled) generationDown(generation);
|
|
631
621
|
};
|
|
632
|
-
|
|
622
|
+
/** Unattached probes close through their transport; attached clients must also report transport closure. */
|
|
623
|
+
async function closeGeneration() {
|
|
624
|
+
const attached = generation.transport !== void 0;
|
|
625
|
+
try {
|
|
626
|
+
await (attached ? generation.close() : transport?.close());
|
|
627
|
+
} catch (_error) {
|
|
628
|
+
if (!attached) return hasClosed();
|
|
629
|
+
}
|
|
630
|
+
return !attached || hasClosed() || await waitForClose(closed.promise);
|
|
631
|
+
}
|
|
632
|
+
async function refreshTools() {
|
|
633
633
|
if (!isCurrent(generation)) return;
|
|
634
634
|
ctx.logger.info(`${label}: tool list changed, re-syncing`);
|
|
635
635
|
try {
|
|
@@ -637,31 +637,30 @@ function startConnection(ctx, config, policy) {
|
|
|
637
637
|
} catch (error) {
|
|
638
638
|
if (!disposed) ctx.logger.error(`${label}: tool re-sync failed: ${String(error)}`);
|
|
639
639
|
}
|
|
640
|
-
}
|
|
640
|
+
}
|
|
641
|
+
let instructions;
|
|
641
642
|
try {
|
|
642
|
-
|
|
643
|
+
transport = createTransport(config);
|
|
644
|
+
await generation.connect(transport);
|
|
643
645
|
if (hasClosed()) {
|
|
644
646
|
attemptSettled = true;
|
|
645
647
|
generationDown(generation);
|
|
646
648
|
return;
|
|
647
649
|
}
|
|
650
|
+
if (!isCurrent(generation)) {
|
|
651
|
+
if (!await closeGeneration()) ctx.logger.error(incompleteDisposalMessage);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const serverText = generation.getInstructions()?.trimEnd() ?? "";
|
|
655
|
+
instructions = serverText ? `### MCP server: ${config.serverName}\n\n${serverText}` : "";
|
|
656
|
+
if (Buffer.byteLength(instructions) > maxInstructionBytes) throw new Error(`${label}: server instructions exceed maxInstructionBytes (${maxInstructionBytes})`);
|
|
648
657
|
await enqueueSync(generation, startup ? startupOpts : opts);
|
|
649
658
|
} catch (error) {
|
|
650
659
|
if (firstAttemptError === void 0) firstAttemptError = error;
|
|
651
660
|
if (isCurrent(generation)) ctx.logger.warn(`${label}: connection attempt failed: ${String(error)}`);
|
|
652
|
-
|
|
653
|
-
await generation.close();
|
|
654
|
-
} catch {}
|
|
655
|
-
const quiesced = hasClosed() || await waitForClose(closed.promise);
|
|
661
|
+
const quiesced = await closeGeneration();
|
|
656
662
|
attemptSettled = true;
|
|
657
|
-
|
|
658
|
-
if (!quiesced) {
|
|
659
|
-
client = void 0;
|
|
660
|
-
clientClosed = void 0;
|
|
661
|
-
ctx.logger.error(`${label}: failed generation did not close within ${GENERATION_CLOSE_TIMEOUT_MS}ms — reconnect stopped to avoid overlapping server processes; reload the plugin or restart the Host to retry`);
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
generationDown(generation);
|
|
663
|
+
settleFailedGeneration(generation, quiesced);
|
|
665
664
|
return;
|
|
666
665
|
}
|
|
667
666
|
attemptSettled = true;
|
|
@@ -670,6 +669,7 @@ function startConnection(ctx, config, policy) {
|
|
|
670
669
|
return;
|
|
671
670
|
}
|
|
672
671
|
if (!isCurrent(generation)) return;
|
|
672
|
+
serverInstructions = instructions;
|
|
673
673
|
connectedAt = Date.now();
|
|
674
674
|
if (failedAttempts > 0) ctx.logger.info(`${label}: reconnected and re-synced tools (attempt ${failedAttempts}/${policy.maxAttempts})`);
|
|
675
675
|
}
|
|
@@ -681,22 +681,33 @@ function startConnection(ctx, config, policy) {
|
|
|
681
681
|
/* v8 ignore next -- defensive: firstAttemptError is always set when connect/sync fails */
|
|
682
682
|
return { error: firstAttemptError ?? /* @__PURE__ */ new Error(`${label}: initial connection failed`) };
|
|
683
683
|
}),
|
|
684
|
+
instructions: () => serverInstructions,
|
|
685
|
+
resources: { async request(request, exec) {
|
|
686
|
+
const generation = client;
|
|
687
|
+
if (!generation || connectedAt === void 0) throw new Error(`${label}: server is disconnected`);
|
|
688
|
+
const options = {
|
|
689
|
+
signal: exec.signal,
|
|
690
|
+
timeout: config.toolCallTimeoutMs
|
|
691
|
+
};
|
|
692
|
+
switch (request.method) {
|
|
693
|
+
case "resources/list": return await generation.listResources(request.cursor === void 0 ? void 0 : { cursor: request.cursor }, options);
|
|
694
|
+
case "resources/templates/list": return await generation.listResourceTemplates(request.cursor === void 0 ? void 0 : { cursor: request.cursor }, options);
|
|
695
|
+
case "resources/read": return await generation.readResource({ uri: request.uri }, options);
|
|
696
|
+
/* v8 ignore next 2 -- resource requests are the closed, typed tool operation union */
|
|
697
|
+
default: return assertNever(request);
|
|
698
|
+
}
|
|
699
|
+
} },
|
|
684
700
|
async dispose() {
|
|
685
701
|
disposed = true;
|
|
702
|
+
serverInstructions = "";
|
|
686
703
|
if (reconnectTimer !== void 0) {
|
|
687
704
|
clearTimeout(reconnectTimer);
|
|
688
705
|
reconnectTimer = void 0;
|
|
689
706
|
}
|
|
690
|
-
const
|
|
691
|
-
const currentClosed = clientClosed;
|
|
707
|
+
const close = closeClient;
|
|
692
708
|
client = void 0;
|
|
693
|
-
|
|
694
|
-
if (
|
|
695
|
-
try {
|
|
696
|
-
await current.close();
|
|
697
|
-
} catch {}
|
|
698
|
-
if (currentClosed !== void 0 && !await waitForClose(currentClosed)) ctx.logger.error(`${label}: generation did not close within ${GENERATION_CLOSE_TIMEOUT_MS}ms during disposal — server shutdown may be incomplete`);
|
|
699
|
-
}
|
|
709
|
+
closeClient = void 0;
|
|
710
|
+
if (close !== void 0 && !await close()) ctx.logger.error(incompleteDisposalMessage);
|
|
700
711
|
await settling;
|
|
701
712
|
await syncChain;
|
|
702
713
|
for (const dispose of disposers.values()) dispose();
|
|
@@ -705,6 +716,32 @@ function startConnection(ctx, config, policy) {
|
|
|
705
716
|
};
|
|
706
717
|
}
|
|
707
718
|
//#endregion
|
|
719
|
+
//#region lib/types/server-context.js
|
|
720
|
+
/**
|
|
721
|
+
* Publish connection-owned MCP resources and literal server instructions.
|
|
722
|
+
*
|
|
723
|
+
* @module @deepseek-ai/dsh-mcp-client
|
|
724
|
+
*/
|
|
725
|
+
/**
|
|
726
|
+
* Contribute server context to the services enabled by this composition.
|
|
727
|
+
* @param ctx - server plugin's registration scope and effect owner.
|
|
728
|
+
* @param server - configured server identity.
|
|
729
|
+
* @param connection - live resource operations and successful instruction snapshot.
|
|
730
|
+
*/
|
|
731
|
+
function registerServerContext(ctx, server, connection) {
|
|
732
|
+
ctx.inject(["mcpResources"], (inner) => {
|
|
733
|
+
inner.mcpResources.register(server, connection.resources);
|
|
734
|
+
});
|
|
735
|
+
ctx.inject(["systemPrompt"], (inner) => {
|
|
736
|
+
inner.systemPrompt.section({
|
|
737
|
+
name: `mcp:${server}`,
|
|
738
|
+
order: inner.systemPrompt.getSectionOrder("MCP_SERVERS"),
|
|
739
|
+
interpolate: false,
|
|
740
|
+
text: () => connection.instructions()
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
//#endregion
|
|
708
745
|
//#region lib/types/index.js
|
|
709
746
|
/**
|
|
710
747
|
* MCP client bridge plugin: connects to an external MCP server and registers
|
|
@@ -724,7 +761,7 @@ function startConnection(ctx, config, policy) {
|
|
|
724
761
|
const name = "mcp-client";
|
|
725
762
|
/** Services required by this plugin. */
|
|
726
763
|
const inject = ["tools"];
|
|
727
|
-
/** Default timeout for individual MCP tool calls (ms). */
|
|
764
|
+
/** Default timeout for individual MCP tool calls and resource requests (ms). */
|
|
728
765
|
const DEFAULT_TOOL_CALL_TIMEOUT_MS = 6e4;
|
|
729
766
|
/** Valid `serverName`, kept below the public tool-name budget. */
|
|
730
767
|
const SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]{1,32}$/;
|
|
@@ -749,6 +786,7 @@ const Config = z.union([z.object({
|
|
|
749
786
|
cwd: z.string().default(""),
|
|
750
787
|
toolCallTimeoutMs: z.number().default(DEFAULT_TOOL_CALL_TIMEOUT_MS),
|
|
751
788
|
failOnStartupError: z.boolean().default(false),
|
|
789
|
+
maxInstructionBytes: z.number().step(1).min(1).default(DEFAULT_MAX_INSTRUCTION_BYTES),
|
|
752
790
|
reconnect: Reconnect
|
|
753
791
|
}), z.object({
|
|
754
792
|
transport: z.const("streamable-http"),
|
|
@@ -757,6 +795,7 @@ const Config = z.union([z.object({
|
|
|
757
795
|
headers: z.dict(String).default({}),
|
|
758
796
|
toolCallTimeoutMs: z.number().default(DEFAULT_TOOL_CALL_TIMEOUT_MS),
|
|
759
797
|
failOnStartupError: z.boolean().default(false),
|
|
798
|
+
maxInstructionBytes: z.number().step(1).min(1).default(DEFAULT_MAX_INSTRUCTION_BYTES),
|
|
760
799
|
reconnect: Reconnect
|
|
761
800
|
})]);
|
|
762
801
|
/**
|
|
@@ -781,11 +820,16 @@ async function apply(ctx, config) {
|
|
|
781
820
|
return () => void names.delete(config.serverName);
|
|
782
821
|
}, "mcp-client.serverName");
|
|
783
822
|
const connection = startConnection(ctx, config, reconnect);
|
|
784
|
-
ctx.
|
|
785
|
-
|
|
786
|
-
|
|
823
|
+
registerServerContext(ctx, config.serverName, connection);
|
|
824
|
+
let stopping;
|
|
825
|
+
const dispose = () => stopping ??= connection.dispose();
|
|
826
|
+
ctx.on("internal/plugin", (fiber) => {
|
|
827
|
+
if (fiber !== ctx.fiber || fiber.uid !== null) return;
|
|
828
|
+
return dispose();
|
|
829
|
+
}, { global: true });
|
|
830
|
+
ctx.effect(() => dispose, "mcp-client.connection");
|
|
787
831
|
const outcome = await connection.ready;
|
|
788
832
|
if (outcome.error !== void 0 && config.failOnStartupError) throw new Error(`mcp-client(${config.serverName}): initial connection or tool synchronization failed`, { cause: outcome.error });
|
|
789
833
|
}
|
|
790
834
|
//#endregion
|
|
791
|
-
export { Config, apply, inject, name };
|
|
835
|
+
export { Config, apply, createMcpToolDefinition, inject, name };
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* @module
|
|
16
16
|
*/
|
|
17
17
|
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
import type { ServerContext } from './server-context.ts';
|
|
18
19
|
import type { Config } from './index.ts';
|
|
19
20
|
/** Automatic reconnect policy for one MCP server connection. */
|
|
20
21
|
export interface ReconnectConfig {
|
|
@@ -29,6 +30,8 @@ export interface ReconnectConfig {
|
|
|
29
30
|
}
|
|
30
31
|
/** Defaults shared by the Config schema and {@link resolveReconnectPolicy}. */
|
|
31
32
|
export declare const RECONNECT_DEFAULTS: Required<ReconnectConfig>;
|
|
33
|
+
/** Default UTF-8 byte limit for attributed server instructions. */
|
|
34
|
+
export declare const DEFAULT_MAX_INSTRUCTION_BYTES = 32768;
|
|
32
35
|
/** Fully resolved reconnect policy captured at plugin load. */
|
|
33
36
|
export type ResolvedReconnectPolicy = Readonly<Required<ReconnectConfig>>;
|
|
34
37
|
/**
|
|
@@ -48,7 +51,7 @@ export interface ConnectionOutcome {
|
|
|
48
51
|
error?: unknown;
|
|
49
52
|
}
|
|
50
53
|
/** Handle for one plugin instance's supervised connection. */
|
|
51
|
-
export interface ConnectionHandle {
|
|
54
|
+
export interface ConnectionHandle extends ServerContext {
|
|
52
55
|
/**
|
|
53
56
|
* Settles when the first connection attempt completes (success or failure).
|
|
54
57
|
* The supervisor enters its reconnect loop regardless; the caller decides
|
|
@@ -56,9 +59,9 @@ export interface ConnectionHandle {
|
|
|
56
59
|
*/
|
|
57
60
|
ready: Promise<ConnectionOutcome>;
|
|
58
61
|
/**
|
|
59
|
-
* Stop reconnection, close the live client, wait
|
|
60
|
-
* and queued tool syncs to quiesce, then
|
|
61
|
-
* still owns.
|
|
62
|
+
* Stop reconnection, close the negotiating transport or live client, wait
|
|
63
|
+
* for the in-flight attempt and queued tool syncs to quiesce, then
|
|
64
|
+
* unregister every tool this server still owns.
|
|
62
65
|
*/
|
|
63
66
|
dispose(): Promise<void>;
|
|
64
67
|
}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
import type { Context } from '@deepseek-ai/cordis';
|
|
16
16
|
import z from '@deepseek-ai/schemastery';
|
|
17
17
|
import type { ReconnectConfig } from './connection.ts';
|
|
18
|
-
export
|
|
18
|
+
export { createMcpToolDefinition } from './tools.ts';
|
|
19
|
+
export type { McpResult, McpToolDefinitionOptions } from './tools.ts';
|
|
19
20
|
export type { ReconnectConfig, ResolvedReconnectPolicy } from './connection.ts';
|
|
20
21
|
/** Cordis plugin name used by loader diagnostics. */
|
|
21
22
|
export declare const name = "mcp-client";
|
|
@@ -39,10 +40,12 @@ export interface StdioConfig {
|
|
|
39
40
|
env: Record<string, string>;
|
|
40
41
|
/** Working directory for the child process. */
|
|
41
42
|
cwd: string;
|
|
42
|
-
/**
|
|
43
|
+
/** Timeout per tool call or resource request in milliseconds. */
|
|
43
44
|
toolCallTimeoutMs: number;
|
|
44
45
|
/** Fail plugin activation when the initial connection or tool synchronization fails. */
|
|
45
46
|
failOnStartupError: boolean;
|
|
47
|
+
/** Maximum UTF-8 bytes of attributed server instructions (default 32768). */
|
|
48
|
+
maxInstructionBytes?: number;
|
|
46
49
|
/** Automatic reconnect policy after a lost connection; omission uses the defaults. */
|
|
47
50
|
reconnect?: ReconnectConfig;
|
|
48
51
|
}
|
|
@@ -60,10 +63,12 @@ export interface StreamableHttpConfig {
|
|
|
60
63
|
url: string;
|
|
61
64
|
/** Additional headers attached to MCP requests. */
|
|
62
65
|
headers: Record<string, string>;
|
|
63
|
-
/**
|
|
66
|
+
/** Timeout per tool call or resource request in milliseconds. */
|
|
64
67
|
toolCallTimeoutMs: number;
|
|
65
68
|
/** Fail plugin activation when the initial connection or tool synchronization fails. */
|
|
66
69
|
failOnStartupError: boolean;
|
|
70
|
+
/** Maximum UTF-8 bytes of attributed server instructions (default 32768). */
|
|
71
|
+
maxInstructionBytes?: number;
|
|
67
72
|
/** Automatic reconnect policy after a lost connection; omission uses the defaults. */
|
|
68
73
|
reconnect?: ReconnectConfig;
|
|
69
74
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publish connection-owned MCP resources and literal server instructions.
|
|
3
|
+
*
|
|
4
|
+
* @module @deepseek-ai/dsh-mcp-client
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
import type { McpResourceProvider } from '@deepseek-ai/dsh-mcp-resources';
|
|
8
|
+
/** Connection-owned values used by the resource and prompt consumers. */
|
|
9
|
+
export interface ServerContext {
|
|
10
|
+
/** Resource access through the current connection generation. */
|
|
11
|
+
resources: McpResourceProvider;
|
|
12
|
+
/**
|
|
13
|
+
* Read the last successfully connected server's attributed instructions.
|
|
14
|
+
* @returns literal prompt text, or an empty string when no server instructions are active.
|
|
15
|
+
*/
|
|
16
|
+
instructions(): string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Contribute server context to the services enabled by this composition.
|
|
20
|
+
* @param ctx - server plugin's registration scope and effect owner.
|
|
21
|
+
* @param server - configured server identity.
|
|
22
|
+
* @param connection - live resource operations and successful instruction snapshot.
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerServerContext(ctx: Context, server: string, connection: ServerContext): void;
|
|
25
|
+
//# sourceMappingURL=server-context.d.ts.map
|
package/lib/types/tools.d.ts
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @module
|
|
13
13
|
*/
|
|
14
|
-
import type
|
|
14
|
+
import { type Client } from '@modelcontextprotocol/client';
|
|
15
15
|
import type { Context } from '@deepseek-ai/cordis';
|
|
16
|
+
import type { ToolDefinition, ToolExecution } from '@deepseek-ai/dsh-tools';
|
|
16
17
|
import type { JsonValue } from '@deepseek-ai/dsh-util-values';
|
|
17
18
|
/** Resolved options relevant to tool bridging. */
|
|
18
19
|
export interface ToolBridgeOptions {
|
|
@@ -48,9 +49,9 @@ export declare function publicToolName(serverName: string, rawName: string): str
|
|
|
48
49
|
*
|
|
49
50
|
* Two phases keep the swap safe:
|
|
50
51
|
*
|
|
51
|
-
* 1. Fetch:
|
|
52
|
+
* 1. Fetch: let the SDK aggregate `tools/list` and build the full next
|
|
52
53
|
* generation of `ToolDefinition`s under public names. Any failure here
|
|
53
|
-
* (network error
|
|
54
|
+
* (network error or duplicate raw name) rejects
|
|
54
55
|
* and leaves the previous generation registered untouched.
|
|
55
56
|
* 2. Swap: dispose the previous generation, register the new one. A registry
|
|
56
57
|
* conflict here can only mean a foreign registration squats on this
|
|
@@ -68,4 +69,34 @@ export declare function publicToolName(serverName: string, rawName: string): str
|
|
|
68
69
|
* disposers — the exact set of live registrations owned by this server.
|
|
69
70
|
*/
|
|
70
71
|
export declare function syncTools(client: Client, ctx: Context, opts: ToolBridgeOptions, previous: ToolDisposers): Promise<ToolDisposers>;
|
|
72
|
+
/** One upstream MCP tool and the callback that obtains its raw protocol result. */
|
|
73
|
+
export interface McpToolDefinitionOptions {
|
|
74
|
+
/** ToolRuntime name presented to the model. */
|
|
75
|
+
name: string;
|
|
76
|
+
/** Upstream name used in result diagnostics. */
|
|
77
|
+
rawName: string;
|
|
78
|
+
/** Upstream model-facing description. */
|
|
79
|
+
description: string;
|
|
80
|
+
/** Upstream JSON input schema. */
|
|
81
|
+
inputSchema: Record<string, unknown>;
|
|
82
|
+
/** Advertised structured output schema, when present. */
|
|
83
|
+
outputSchema?: unknown;
|
|
84
|
+
/** Whether the upstream tool requires the unsupported task execution extension. */
|
|
85
|
+
taskRequired?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Obtain one raw MCP result from the provider.
|
|
88
|
+
* @param args - model arguments admitted by the ToolRuntime.
|
|
89
|
+
* @param execution - exact ToolRuntime invocation, including its Agent and cancellation.
|
|
90
|
+
* @returns the external result object, validated before content projection.
|
|
91
|
+
*/
|
|
92
|
+
call(args: Record<string, unknown>, execution: ToolExecution): Promise<unknown>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Adapt an upstream MCP tool to canonical values and durable image content.
|
|
96
|
+
* Registration, provider lifetime, deadlines, and transport belong to the caller.
|
|
97
|
+
* @param ctx - plugin context carrying optional attachment and model services.
|
|
98
|
+
* @param options - upstream tool fields and its raw-result callback.
|
|
99
|
+
* @returns the unregistered ToolRuntime definition.
|
|
100
|
+
*/
|
|
101
|
+
export declare function createMcpToolDefinition(ctx: Context, options: McpToolDefinitionOptions): ToolDefinition;
|
|
71
102
|
//# sourceMappingURL=tools.d.ts.map
|
package/lib/types/transport.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
|
-
import type { Transport } from '@modelcontextprotocol/
|
|
8
|
+
import type { Transport } from '@modelcontextprotocol/client';
|
|
9
9
|
import type { Config } from './index.ts';
|
|
10
10
|
/**
|
|
11
11
|
* Create an MCP transport from the resolved plugin config.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-mcp-client",
|
|
3
3
|
"description": "MCP client bridge: connects to MCP servers and registers their tools on ctx.tools",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,30 +27,45 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@deepseek-ai/dsh-
|
|
31
|
-
"@deepseek-ai/dsh-
|
|
32
|
-
"@deepseek-ai/dsh-
|
|
33
|
-
"@deepseek-ai/dsh-
|
|
34
|
-
"@deepseek-ai/dsh-
|
|
35
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
30
|
+
"@deepseek-ai/dsh-scope": "^0.1.6-alpha.2",
|
|
31
|
+
"@deepseek-ai/dsh-attachment": "^0.1.6-alpha.2",
|
|
32
|
+
"@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
|
|
33
|
+
"@deepseek-ai/dsh-timeout": "^0.1.6-alpha.2",
|
|
34
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.6-alpha.2",
|
|
35
|
+
"@deepseek-ai/dsh-tools": "^0.1.6-alpha.2",
|
|
36
|
+
"@deepseek-ai/dsh-mcp-resources": "^0.1.6-alpha.2",
|
|
37
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.6-alpha.2",
|
|
36
38
|
"@deepseek-ai/cordis": "^4.0.2"
|
|
37
39
|
},
|
|
38
40
|
"dependencies": {
|
|
39
|
-
"@modelcontextprotocol/
|
|
40
|
-
"
|
|
41
|
-
"@deepseek-ai/
|
|
41
|
+
"@modelcontextprotocol/client": "2.0.0",
|
|
42
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
43
|
+
"@deepseek-ai/dsh-util-values": "^0.1.6-alpha.2"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"@modelcontextprotocol/server-everything": "^2026.7.4",
|
|
45
47
|
"@modelcontextprotocol/server-filesystem": "^2026.7.4",
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
48
|
-
"
|
|
49
|
-
"@deepseek-ai/dsh-
|
|
50
|
-
"@deepseek-ai/dsh-attachment-local": "^0.1.
|
|
51
|
-
"@deepseek-ai/dsh-
|
|
52
|
-
"@deepseek-ai/dsh-
|
|
48
|
+
"@modelcontextprotocol/server": "2.0.0",
|
|
49
|
+
"@modelcontextprotocol/node": "2.0.0",
|
|
50
|
+
"zod": "^4.4.3",
|
|
51
|
+
"@deepseek-ai/dsh-attachment": "^0.1.6-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-attachment-local": "^0.1.6-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-scope": "^0.1.6-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-timeout": "^0.1.6-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-tools": "^0.1.6-alpha.2",
|
|
53
57
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
54
|
-
"@deepseek-ai/dsh-http-proxy": "^0.1.
|
|
58
|
+
"@deepseek-ai/dsh-http-proxy": "^0.1.6-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-mcp-resources": "^0.1.6-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.6-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.6-alpha.2"
|
|
62
|
+
},
|
|
63
|
+
"peerDependenciesMeta": {
|
|
64
|
+
"@deepseek-ai/dsh-mcp-resources": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"@deepseek-ai/dsh-system-prompt": {
|
|
68
|
+
"optional": true
|
|
69
|
+
}
|
|
55
70
|
}
|
|
56
71
|
}
|