dsh-mcp 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.en.md CHANGED
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [1.9.0] - 2026-08-28
13
+
14
+ ### Added
15
+
16
+ - **Tool-list stability (better prompt-cache hits)**: tool schemas are canonicalized (recursive key sorting) before registration, so servers reordering schema keys no longer triggers dispose/re-register churn; MCP tools in the system prompt are rendered in stable name order, so the same tool set renders byte-identically no matter the hot-set or `tools/list` order
17
+
12
18
  ## [1.8.0] - 2026-08-27
13
19
 
14
20
  ### Added
package/CHANGELOG.md CHANGED
@@ -9,6 +9,12 @@
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [1.9.0] - 2026-08-28
13
+
14
+ ### 新增
15
+
16
+ - **工具列表稳定化增强(提升 prompt cache 命中)**:注册前对工具 schema 做规范化(递归排序键),服务器返回的键顺序变化不再导致工具被误判为变化而注销重注册;系统提示词中 MCP 工具按名称稳定排序,同一工具集合的渲染文本恒定——热注入集(search 模式每次调用都会变动)与服务器 `tools/list` 顺序不再影响提示词稳定性
17
+
12
18
  ## [1.8.0] - 2026-08-27
13
19
 
14
20
  ### 新增
package/lib/index.js CHANGED
@@ -1182,6 +1182,16 @@ let McpManagerService = (() => {
1182
1182
  });
1183
1183
  }
1184
1184
  assembly.tools = kept;
1185
+ // Canonical tool order: render MCP tools sorted by name so the
1186
+ // system prompt stays byte-stable for the same tool set no
1187
+ // matter the registration or hot-set order (hot tools move on
1188
+ // every call, and servers may reorder tools/list) — stable
1189
+ // output is what preserves prompt-cache hits.
1190
+ const mcpKept = [];
1191
+ const otherKept = [];
1192
+ for (const tool of assembly.tools) (tool.name.startsWith("mcp__") ? mcpKept : otherKept).push(tool);
1193
+ mcpKept.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
1194
+ assembly.tools = [...otherKept, ...mcpKept];
1185
1195
  return next();
1186
1196
  });
1187
1197
  this.ctx.on("tools/result", (exec) => {
package/lib/mcp-client.js CHANGED
@@ -205,9 +205,22 @@ function toolPublicEqual(a, b) {
205
205
  if (!jsonEqual(a.parameters, b.parameters)) return false;
206
206
  return jsonEqual(a.output && a.output.schema, b.output && b.output.schema);
207
207
  }
208
- /** Deep equality of JSON-serializable values (schema objects). */
208
+ /**
209
+ * Recursively sort object keys so semantically identical JSON compares equal
210
+ * regardless of key order (servers often reorder schema keys between lists).
211
+ */
212
+ function canonicalize(value) {
213
+ if (Array.isArray(value)) return value.map(canonicalize);
214
+ if (value !== null && typeof value === "object") {
215
+ const out = {};
216
+ for (const key of Object.keys(value).sort()) out[key] = canonicalize(value[key]);
217
+ return out;
218
+ }
219
+ return value;
220
+ }
221
+ /** Deep equality of JSON-serializable values, ignoring object key order. */
209
222
  function jsonEqual(a, b) {
210
- return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
223
+ return JSON.stringify(canonicalize(a ?? null)) === JSON.stringify(canonicalize(b ?? null));
211
224
  }
212
225
  /** Keep a supported advertised schema; unsupported MCP vocabulary falls back to JsonValue. */
213
226
  function supportedOutputSchema(candidate) {
@@ -225,7 +238,11 @@ function createDefinition(client, ctx, publicName, rawName, description, paramet
225
238
  return {
226
239
  name: publicName,
227
240
  description,
228
- parameters,
241
+ // Canonicalize before registration: servers often return JSON Schema
242
+ // with unstable key ordering; sorting keys keeps the model-visible tool
243
+ // list byte-stable across re-syncs and reconnects, which is what
244
+ // preserves prompt-cache hits.
245
+ parameters: canonicalize(parameters),
229
246
  output: createOutput(rawName, structuredSchema),
230
247
  execute: createExecutor(client, ctx, rawName, taskRequired, opts, projections),
231
248
  finalizeContent(exec, result) {
@@ -249,7 +266,7 @@ function createOutput(rawName, structuredSchema) {
249
266
  type: "array",
250
267
  items: {}
251
268
  },
252
- structuredContent: structuredSchema ?? {}
269
+ structuredContent: canonicalize(structuredSchema ?? {})
253
270
  },
254
271
  required: structuredSchema === void 0 ? ["content"] : ["content", "structuredContent"],
255
272
  additionalProperties: false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mcp",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "MCP 管理界面 + tool search:可视化配置/启停/刷新 MCP 服务器与工具勾选,按需检索(tool search)热注入;工具列表稳定命中缓存、不撑爆上下文。DeepSeek Harness plugin: MCP management UI + tool search — stable tool list, cache-friendly, no context bloat.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",