@aiwayds/dsh-mcp-adapter 0.5.1 → 0.6.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/README.md +49 -3
- package/README.zh.md +50 -4
- package/lib/index.d.ts +275 -22
- package/lib/index.js +612 -70
- package/lib/index.js.map +1 -1
- package/lib/jev/backends.d.ts +35 -0
- package/lib/jev/backends.js +48 -0
- package/lib/jev/backends.js.map +1 -0
- package/lib/jev/client.d.ts +133 -0
- package/lib/jev/client.js +288 -0
- package/lib/jev/client.js.map +1 -0
- package/lib/jev/config.d.ts +82 -0
- package/lib/jev/config.js +55 -0
- package/lib/jev/config.js.map +1 -0
- package/lib/jev/dual.d.ts +42 -0
- package/lib/jev/dual.js +42 -0
- package/lib/jev/dual.js.map +1 -0
- package/lib/jev/gate.d.ts +51 -0
- package/lib/jev/gate.js +141 -0
- package/lib/jev/gate.js.map +1 -0
- package/lib/jev/log.d.ts +93 -0
- package/lib/jev/log.js +138 -0
- package/lib/jev/log.js.map +1 -0
- package/lib/jev/seams.d.ts +118 -0
- package/lib/jev/seams.js +364 -0
- package/lib/jev/seams.js.map +1 -0
- package/lib/jev/thresholds.d.ts +37 -0
- package/lib/jev/thresholds.js +44 -0
- package/lib/jev/thresholds.js.map +1 -0
- package/lib/rank.d.ts +157 -0
- package/lib/rank.js +327 -0
- package/lib/rank.js.map +1 -0
- package/lib/usage.d.ts +232 -0
- package/lib/usage.js +362 -0
- package/lib/usage.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ This plugin keeps the official `dsh-mcp-client` as the connection layer (transpo
|
|
|
16
16
|
|
|
17
17
|
- every `mcp__*` tool schema is **folded out** of the assembled prompt (`system-prompt/assemble` waterfall);
|
|
18
18
|
- two **constant meta-tools** take their place, so standing prompt cost is O(1) in the number of servers/tools:
|
|
19
|
-
- **`mcp_list`** — compact catalog (tool names + truncated descriptions, no schemas); pass `tool` to expand one tool's full schema on demand, `server` to filter, `verbose` for everything;
|
|
19
|
+
- **`mcp_list`** — compact catalog (tool names + truncated descriptions, no schemas); pass `tool` to expand one tool's full schema on demand, `server` to filter, `query` for a relevance-ranked shortlist, `verbose` for everything;
|
|
20
20
|
- **`mcp_call`** — dispatch `{ tool, arguments }` to the still-registered definition, passing the run context through.
|
|
21
21
|
|
|
22
22
|
Tools stay registered in `ctx.tools`, so TUI rendering and `tools.restrict()` masking keep working — only the prompt payload changes. A folded, constant tool list is also friendlier to KV-prefix caching than upstream's per-resync generation swap.
|
|
@@ -79,8 +79,17 @@ Upgrading from a dsh 0.1.5 install: the old `mcp-adapter:` section of `settings.
|
|
|
79
79
|
| `servers` | `[]` | server-name whitelist: when non-empty, only these servers' tools are folded / cataloged / dispatchable (all three consult the same list) |
|
|
80
80
|
| `descriptionLimit` | `200` | max chars per tool description in the `mcp_list` catalog |
|
|
81
81
|
| `storageDir` | `""` | gate storage directory override; empty = `<dsh home>/storages/mcp-adapter` (read once at plugin start — moving it takes effect on restart) |
|
|
82
|
+
| `jevEnabled` | `false` | System One master switch — **default off**: false means zero decision calls anywhere, and the usage counters keep recording |
|
|
83
|
+
| `jevBackend` | `"zen"` | decision backend: `zen` (free opencode-zen tier) / `native` (typesafe first-party) / `openrouter`; each needs its own API key (`JEV_ZEN_API_KEY` / `TYPESAFE_API_KEY` / `OPENROUTER_API_KEY`, or the macOS keychain) |
|
|
84
|
+
| `jevModel` | `""` | pinned model id; empty = the backend's own default resolved at call time (`jev-1.13-free` / `jev-1.13.0` / `typesafe/jev-1.13`) — an upgrade is a deliberate act |
|
|
85
|
+
| `jevTimeoutMs` | `3000` | per-request timeout in ms; one attempt, no retry — the calling lane's next cadence is the retry |
|
|
86
|
+
| `jevSecretFile` | `""` | extra literal secrets for the outbound secret gate (`JEV_SECRET_FILE` semantics); swapping the path reloads the list |
|
|
87
|
+
| `jevLayaFallback` | `false` | **default off**: also call a local laya endpoint on every decision request; its answer is logged for comparison and takes over only when the primary backend fails |
|
|
88
|
+
| `jevLayaUrl` | `"http://127.0.0.1:8000/v1/systemone"` | that local laya-serve endpoint |
|
|
82
89
|
|
|
83
|
-
|
|
90
|
+
The last seven rows are the System One decision layer ([below](#self-evolution-system-one)); all of them are opt-in, and the default install makes no outbound decision call at all.
|
|
91
|
+
|
|
92
|
+
Every field is declared **volatile** (the dsh 0.1.7 settings contract): all twelve appear on the plugin's settings page under the `dsh-mcp-adapter` entry, and editing them there applies **without restarting the plugin** — the fold boundary, catalog and dispatch pick the new values up on their very next use, and the `jev*` switches are re-read the same way, so flipping `jevEnabled` in the settings page takes effect on the very next dispatch. (`storageDir` is the one exception: the storage location itself is read once at plugin start, as its row says.) The `config:` block in your profile patch keeps working exactly as before.
|
|
84
93
|
|
|
85
94
|
```yaml
|
|
86
95
|
config:
|
|
@@ -94,6 +103,42 @@ config:
|
|
|
94
103
|
|
|
95
104
|
**Trust boundary:** by default every `prefix`-matching tool is folded — the prefix is a naming convention, not a security boundary, so tools registered by third-party plugins that happen to use `mcp__*` names fold too. To trust only the official client's servers, list them explicitly in `servers`; everything else stays native (still callable directly, just outside the meta-tools).
|
|
96
105
|
|
|
106
|
+
## Self-evolution (System One)
|
|
107
|
+
|
|
108
|
+
An optional decision layer, **off by default**: with `jevEnabled: false` the plugin makes no outbound decision call anywhere and behaves exactly as it did before. With it on, three things happen — and all three are advisory.
|
|
109
|
+
|
|
110
|
+
**Usage accounting (always on, never networked).** Every finished `mcp_call` dispatch, successes and failures alike, is counted per tool (`calls`, `errors`, first/last used) into `usage.json` in the plugin's own gate directory, right beside `gate.json`. This runs regardless of `jevEnabled`, so turning the switch on later reuses everything already counted.
|
|
111
|
+
|
|
112
|
+
**Keep suggestions (the self-evolving part).** Every 20 recorded calls — crossing 20, 40, 60 … — the plugin asks the decision backend, in **one batched request fired in the background**, which of the currently *folded* tools that were actually called earn a permanent place in every prompt instead of being looked up and expanded on every use. The dispatch that crossed the threshold returns long before any of this runs; nothing awaits it. The answer is written to `suggestions.json`; a failed round leaves the previous batch exactly as it was.
|
|
113
|
+
|
|
114
|
+
**Viewing them.** `/mcp suggest` renders the current batch — probability, band, the usage evidence behind it, and the `keep` fragment the row would translate to — plus the recorded totals. The view says so itself: `display only — nothing was written to keep, prefix, or the server gate.` You paste the fragment into your own config; **this plugin never edits your configuration**, and no decision path ever writes `keep`, `prefix` or the server gate on its own.
|
|
115
|
+
|
|
116
|
+
The same backend, asked by the model rather than by the counter, powers the two model-facing searches:
|
|
117
|
+
|
|
118
|
+
- `mcp_list { "query": "<what you want to do>" }` — a **subset** view: a lexical prefilter over tool names and descriptions first (deterministic, no network), then an optional rerank of that shortlist. The answer says how many tools matched in total and points back at the unfiltered catalog; a no-argument `mcp_list` call is still the full catalog, and a no-query call makes no decision call at all.
|
|
119
|
+
- `mcp_call` with an unregistered tool name — the error keeps its original sentence and gains a `Did you mean: "…"?` tail, ordered by a lexical near-miss pass (edit distance first, then shared name tokens) and optionally reranked. The tail always labels where its order came from (`lexical` or `jev reranked`), so an unranked guess never reads like a ranked one.
|
|
120
|
+
|
|
121
|
+
**Fail-open:** an unreachable, slow or unhelpful backend is indistinguishable from a disabled one — every decision path falls back to the lexical behavior it had before, and a failed suggestion round costs you that round's rows and nothing else.
|
|
122
|
+
|
|
123
|
+
**Privacy:** the counters, the suggestions and the decision log (`decisions.jsonl`) never leave the machine — they are three files in the gate directory. With `jevEnabled` on, tool names, the leading 200 chars of their descriptions, the usage counts and the model's own query text **do** go out to the decision backend as the question context; every outbound body is scanned by a built-in secret gate (JWT, provider keys, GitHub/Slack/AWS tokens, PEM blocks, plus whatever literals you list in `jevSecretFile`) and a hit aborts the call before anything leaves the process. `jevBackend` decides where: `zen` (the free opencode-zen tier, default), `native` (typesafe first-party) or `openrouter`. The local laya pace-maker (`jevLayaFallback`) is off by default.
|
|
124
|
+
|
|
125
|
+
### Not the local laya backend — not yet (measured against laya 0.3.20)
|
|
126
|
+
|
|
127
|
+
**Leave `jevLayaFallback` off.** Measured head-to-head against the free `zen` tier on this plugin's own three seams, with real MCP tool catalogs driven through `jevAskDual` in parallel:
|
|
128
|
+
|
|
129
|
+
| Seam | zen (free) | laya 0.3.20 | pure lexical |
|
|
130
|
+
| --- | --- | --- | --- |
|
|
131
|
+
| keep suggestions (12 tools, batched noul) | AUC **1.000** — hot 0.71 / borderline 0.48 / fold 0.11 | AUC **0.333**, worse than chance: every probability lands in 51–55% | — |
|
|
132
|
+
| `mcp_list {query}` ranking (10 queries) | **8/10** | 4/10 — ranks `read_file` below a SQL `query` | 6/10 |
|
|
133
|
+
| did-you-mean rerank (6 misspellings) | 5/6 | 4/6 | 5/6 |
|
|
134
|
+
| Chinese query, semantic fallback (5 queries) | **5/5** | 1/5 — anchors on one tool regardless of the query | 0 (no lexical match) |
|
|
135
|
+
|
|
136
|
+
laya answers roughly 8–10× faster (70 ms–1 s vs 0.6–6 s), but the discrimination is not close. It cannot read the call-frequency signal the keep suggestion is *about* — a tool called 412 times and one called once come back at the same confidence — and cross-language query matching, which the catalog seam leans on, is where it collapses outright. laya's own runtime warns that the bundled checkpoint's confidence values are uncalibrated.
|
|
137
|
+
|
|
138
|
+
So the pace-maker stays a degraded-mode safety net only: when the primary backend fails, laya's answer takes over **degraded** — relative order only, never absolute scores against the calibrated bands, and keep suggestions are skipped that round rather than shown with an uncalibrated number. Both sources' verdicts still land in `decisions.jsonl`, which makes the comparison free to run continuously.
|
|
139
|
+
|
|
140
|
+
**Revisit when** laya ships a typed-decisions checkpoint (or a version whose calibration is published instead of warned about) and this table inverts — then re-measure against your own catalogs before believing it.
|
|
141
|
+
|
|
97
142
|
## Notes
|
|
98
143
|
|
|
99
144
|
- `mcp_call` only accepts `prefix`-matching tools (and, when `servers` is set, whitelisted servers) — it can never be used to bypass another tool's own pre-execute pipeline.
|
|
@@ -110,7 +155,8 @@ The plugin registers one slash command on the platform `commands` service — co
|
|
|
110
155
|
|---|---|
|
|
111
156
|
| `/mcp` or `/mcp list` | Tree overview — every server line carries its stable `[<id>]`; disabled ones show `⏸ disabled` and hide their tools; closed by a folding-health footer |
|
|
112
157
|
| `/mcp list <name>` | `<name>` matching a server → that server's full tool list (a disabled server adds a `⏸` note); matching a full tool name → full description + complete input schema |
|
|
113
|
-
| `/mcp config` | The effective `prefix` / `keep` / `servers` / `descriptionLimit`, each with its hitting tools, plus the persistent enable/disable inventory |
|
|
158
|
+
| `/mcp config` | The effective `prefix` / `keep` / `servers` / `descriptionLimit`, each with its hitting tools, plus the persistent enable/disable inventory, the seven `jev*` settings and the recorded usage counters |
|
|
159
|
+
| `/mcp suggest` | The current keep-suggestion batch: per tool a probability, a band, the usage evidence and the exact `keep` fragment, plus the recorded totals. Read-only: it writes nothing to `keep`, `prefix` or the gate |
|
|
114
160
|
| `/mcp disable <id>` | Latch a whole server off: its tools force-fold out of every prompt — keep and `servers` exemptions included — it disappears from the `mcp_list` catalog, and `mcp_call` refuses it with an `/mcp enable <id>` hint |
|
|
115
161
|
| `/mcp enable <id>` | Restore it under the same stable id |
|
|
116
162
|
|
package/README.zh.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
- 每个匹配的 `mcp__*` 工具 schema 被**折叠出**装配后的 prompt(`system-prompt/assemble` waterfall);
|
|
18
18
|
- 原位换上两个**恒定 meta-tool**,常驻 prompt 成本对 server/工具数量而言是 O(1):
|
|
19
|
-
- **`mcp_list`** — 紧凑目录(工具名 + 截断描述,不含 schema);传 `tool` 按需展开单个工具的完整 schema,传 `server` 过滤,传 `verbose` 全量内联;
|
|
19
|
+
- **`mcp_list`** — 紧凑目录(工具名 + 截断描述,不含 schema);传 `tool` 按需展开单个工具的完整 schema,传 `server` 过滤,传 `query` 拿相关性精排的子集,传 `verbose` 全量内联;
|
|
20
20
|
- **`mcp_call`** — 按 `{ tool, arguments }` 把调用分发到仍然注册着的定义上,运行上下文原样透传。
|
|
21
21
|
|
|
22
22
|
工具本身仍注册在 `ctx.tools` 里,所以 TUI 渲染、`tools.restrict()` 掩蔽照常工作——变的只是 prompt 载荷。折叠后恒定的工具列表也比上游"每次重同步就换代"的模式更利于 KV 前缀缓存。
|
|
@@ -71,6 +71,17 @@ dsh plugin --profile <name> remove @aiwayds/dsh-mcp-adapter
|
|
|
71
71
|
| `servers` | `[]` | server 白名单:非空时只有这些 server 的工具会被折叠 / 进目录 / 可分发(三处共用同一份名单) |
|
|
72
72
|
| `descriptionLimit` | `200` | `mcp_list` 目录里每条工具描述的最大字符数 |
|
|
73
73
|
| `storageDir` | `""` | 门闩存储目录覆盖;空 = `<dsh home>/storages/mcp-adapter`(插件启动时读一次,改后需重启插件生效) |
|
|
74
|
+
| `jevEnabled` | `false` | System One 总开关——**默认关闭**:false 表示任何环节都不发决策请求,而调用统计照常累积 |
|
|
75
|
+
| `jevBackend` | `"zen"` | 决策后端:`zen`(opencode-zen 免费档)/ `native`(typesafe 直连)/ `openrouter`;各自需要自己的 API key(`JEV_ZEN_API_KEY` / `TYPESAFE_API_KEY` / `OPENROUTER_API_KEY`,或 macOS 钥匙串) |
|
|
76
|
+
| `jevModel` | `""` | 钉死的模型 id;空 = 调用时解析后端自带默认(`jev-1.13-free` / `jev-1.13.0` / `typesafe/jev-1.13`)——升级是一次有意为之的动作 |
|
|
77
|
+
| `jevTimeoutMs` | `3000` | 单次请求超时(毫秒);只试一次、不重试——下一次节奏就是重试 |
|
|
78
|
+
| `jevSecretFile` | `""` | 出站密钥闸的额外字面量密钥清单(`JEV_SECRET_FILE` 语义);换路径即热更清单 |
|
|
79
|
+
| `jevLayaFallback` | `false` | **默认关闭**:每次决策请求同时并行打一发本地 laya;其答案只作对比记录,仅在主后端失败时接管 |
|
|
80
|
+
| `jevLayaUrl` | `"http://127.0.0.1:8000/v1/systemone"` | 该本地 laya-serve 端点 |
|
|
81
|
+
|
|
82
|
+
后七行即 System One 决策层(见下节),全部是可选开关——默认安装下插件不会发出任何一次对外决策请求。
|
|
83
|
+
|
|
84
|
+
以上字段全部声明为 **volatile**(dsh 0.1.7 settings 契约):十二个键都会出现在插件设置页(entry `dsh-mcp-adapter`)中,在设置页修改**无需重启插件即可生效**——折叠边界、目录与分发在下一次使用时立即采用新值;`jev*` 开关同样每次使用时现读,在设置页打开 `jevEnabled` 后,下一次分发立即生效。(唯一例外是 `storageDir`:存储位置本身在插件启动时读一次,如其表行所述。)profile patch 里的 `config:` 写法照旧可用。
|
|
74
85
|
|
|
75
86
|
```yaml
|
|
76
87
|
config:
|
|
@@ -82,10 +93,44 @@ config:
|
|
|
82
93
|
- github
|
|
83
94
|
```
|
|
84
95
|
|
|
85
|
-
以上字段全部声明为 **volatile**(dsh 0.1.7 settings 契约):五个键都会出现在插件设置页(entry `dsh-mcp-adapter`)中,在设置页修改**无需重启插件即可生效**——折叠边界、目录与分发在下一次使用时立即采用新值。profile patch 里的 `config:` 写法照旧可用。
|
|
86
|
-
|
|
87
96
|
**信任边界:** 默认所有匹配 `prefix` 的工具都会被折叠——前缀只是命名约定而非安全边界,第三方插件恰好用 `mcp__*` 注册的工具同样会折叠。若只信任官方 client 的 server,请在 `servers` 里显式列出;其余保持原生(仍可直调,只是不走 meta-tool)。
|
|
88
97
|
|
|
98
|
+
## 自我进化(System One 决策层)
|
|
99
|
+
|
|
100
|
+
一个**默认关闭**的可选决策层:`jevEnabled: false` 时插件任何地方都不发对外决策请求,行为与接入前完全一致。打开后只发生三件事,且三件都是建议性的。
|
|
101
|
+
|
|
102
|
+
**调用统计(常开,永不走网络)。** 每一次走完的 `mcp_call` 分发(成功与失败都算)按工具计数(`calls`、`errors`、首次/最近使用时间),落进插件自有门闩目录里与 `gate.json` 并排的 `usage.json`。统计与 `jevEnabled` 无关——事后打开开关,此前攒下的计数直接复用。
|
|
103
|
+
|
|
104
|
+
**keep 建议(自我进化的部分)。** 每累计 20 次调用(越过 20、40、60……)触发一次后台批量提问:问决策后端,当前**处于折叠态**、且确实被调用过的工具里,哪些值得常驻每一条 prompt(省掉每次都要查目录、展开 schema 的成本)。触发它的那次分发早就返回了,这些活没人 await。答案写入 `suggestions.json`;这一轮失败则原样保留上一批。
|
|
105
|
+
|
|
106
|
+
**查看。** `/mcp suggest` 渲染当前这批建议——每行给出概率、档位、支撑它的调用证据,以及该行对应的 `keep` 片段——外加已记录的总量。视图自己会写明:`display only — nothing was written to keep, prefix, or the server gate.` 片段由你亲手粘进配置;**本插件永不修改你的配置**,任何一条决策路径都不会自行写入 `keep`、`prefix` 或门闩。
|
|
107
|
+
|
|
108
|
+
同一个后端,除了被计数器调用,也服务模型侧的两处检索:
|
|
109
|
+
|
|
110
|
+
- `mcp_list { "query": "<你想做什么>" }` — 是一个**子集**视图:先做一遍词法预筛(对工具名与描述,确定性、无网络),再对该批候选做可选重排。答案会说明总共命中多少个工具,并指回未过滤的完整目录;无参 `mcp_list` 依然是完整目录,而不带 query 的调用根本不会发起决策请求。
|
|
111
|
+
- `mcp_call` 打到未注册的工具名 — 报错保留原句,末尾追加 `Did you mean: "…"?` 建议:先按词法近似排序(编辑距离优先,其次共享的名字 token),可选再重排。末尾始终标注排序来源(`lexical` 或 `jev reranked`),没排过序的猜测不会被当成排过序的。
|
|
112
|
+
|
|
113
|
+
**故障放行(fail-open):** 后端不可达、超时或给不出可用答案,与开关关闭不可区分——每条决策路径都退回它原本的词法行为;建议那一轮失败,代价仅是这一批没有更新。
|
|
114
|
+
|
|
115
|
+
**隐私:** 计数、建议与决策日志(`decisions.jsonl`)都不出本机——它们是门闩目录里的三个文件。打开 `jevEnabled` 后,工具名、其描述的前 200 字、调用计数以及模型自己的 query 文本**会**作为问题上下文发给决策后端;每个出站请求体都会先过内置密钥闸(JWT、各家 provider key、GitHub/Slack/AWS 令牌、PEM 块,外加你在 `jevSecretFile` 里列的字面量),命中即在出进程前中止这次调用。发往哪里由 `jevBackend` 决定:`zen`(opencode-zen 免费档,默认)、`native`(typesafe 直连)或 `openrouter`。本地 laya 陪跑(`jevLayaFallback`)默认关闭。
|
|
116
|
+
|
|
117
|
+
### 别把本地 laya 当后端——暂时不建议(实测针对 laya 0.3.20)
|
|
118
|
+
|
|
119
|
+
**`jevLayaFallback` 保持关闭。** 用真实 MCP 工具目录、经 `jevAskDual` 并行与免费档 `zen` 头对头,在本插件自己的三条缝上实测:
|
|
120
|
+
|
|
121
|
+
| 缝 | zen(免费档) | laya 0.3.20 | 纯词法 |
|
|
122
|
+
| --- | --- | --- | --- |
|
|
123
|
+
| keep 建议(12 工具批量 noul) | AUC **1.000**——高频 0.71 / 边界 0.48 / 该折叠 0.11 | AUC **0.333**,比随机还差:概率全挤在 51–55% | — |
|
|
124
|
+
| `mcp_list {query}` 排序(10 问) | **8/10** | 4/10——把 `read_file` 排到 SQL `query` 之下 | 6/10 |
|
|
125
|
+
| did-you-mean 重排(6 错拼) | 5/6 | 4/6 | 5/6 |
|
|
126
|
+
| 中文 query 语义兜底(5 问) | **5/5** | 1/5——不管问什么都锚在同一个工具上 | 0(词法零命中) |
|
|
127
|
+
|
|
128
|
+
laya 快约 8–10 倍(70 毫秒–1 秒 vs 0.6–6 秒),但判别力差得不是一点:它读不出 keep 建议**赖以成立**的调用频次信号——调用 412 次和调用 1 次的工具给回来的置信度一样高;而目录排序缝依赖的跨语言 query 匹配,是它彻底塌掉的地方。laya 自己的运行时也在警告内置 checkpoint 的置信度未校准。
|
|
129
|
+
|
|
130
|
+
所以陪跑只当降级模式的安全网:主路失败时 laya 的答案**降级**接管——只借相对排序,绝不拿它的绝对分数去撞校准过的档位,那一轮的 keep 建议直接跳过,而不是拿未校准的数字糊弄你。两边的判定行照旧都进 `decisions.jsonl`,等于这份对比免费持续跑着。
|
|
131
|
+
|
|
132
|
+
**何时再看:** 等 laya 出 typed-decisions checkpoint(或某个版本不再只"警告未校准"而是公开校准结果),并且上表被翻转时——届时先拿你自己的目录重测一遍再信。
|
|
133
|
+
|
|
89
134
|
## 备注
|
|
90
135
|
|
|
91
136
|
- `mcp_call` 只接受匹配 `prefix` 的工具(配置了 `servers` 时还须在白名单内)——它不可能被用来绕过其它工具自己的 pre-execute 管线。
|
|
@@ -102,7 +147,8 @@ config:
|
|
|
102
147
|
|---|---|
|
|
103
148
|
| `/mcp` 或 `/mcp list` | 树形总览——每行 server 带 stable `[<id>]` 前缀;disabled 的标 `⏸ disabled` 且不列工具;尾部附折叠健康行 |
|
|
104
149
|
| `/mcp list <name>` | `<name>` 匹配某 server → 该 server 全部工具(disabled 的附加 `⏸` 说明行);匹配完整工具名 → 完整描述 + 完整 input schema |
|
|
105
|
-
| `/mcp config` | 当前生效的 `prefix` / `keep` / `servers` / `descriptionLimit` 及各自命中清单,外加持久 enable/disable
|
|
150
|
+
| `/mcp config` | 当前生效的 `prefix` / `keep` / `servers` / `descriptionLimit` 及各自命中清单,外加持久 enable/disable 台账、七个 `jev*` 设置与已记录的调用统计 |
|
|
151
|
+
| `/mcp suggest` | 当前这批 keep 建议:每行给出概率、档位、调用证据与对应的 `keep` 片段,外加已记录总量。只读:不向 `keep`、`prefix` 或门闩写任何东西 |
|
|
106
152
|
| `/mcp disable <id>` | 把一个 server 整体闩上:工具强制折叠出 prompt(keep 与 `servers` 豁免一并覆盖)、从 `mcp_list` 目录消失、`mcp_call` 拒绝并给 `/mcp enable <id>` 指引 |
|
|
107
153
|
| `/mcp enable <id>` | 用同一个 stable id 复原 |
|
|
108
154
|
|
package/lib/index.d.ts
CHANGED
|
@@ -57,6 +57,9 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
57
57
|
import z from '@deepseek-ai/schemastery';
|
|
58
58
|
import type { ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm';
|
|
59
59
|
import type { ToolDefinition, ToolExecution, ToolExecutionResult } from '@deepseek-ai/dsh-tools';
|
|
60
|
+
import type { JevConfigValue } from './jev/config.ts';
|
|
61
|
+
import type { RankCandidate } from './rank.ts';
|
|
62
|
+
import type { SuggestionRecord } from './usage.ts';
|
|
60
63
|
import type { ScopeKey } from '@deepseek-ai/dsh-scope';
|
|
61
64
|
/** Cordis plugin name used by loader diagnostics. */
|
|
62
65
|
export declare const name = "mcp-adapter";
|
|
@@ -274,8 +277,15 @@ export interface VolatileRef<T> {
|
|
|
274
277
|
* Resolved adapter configuration as `apply` receives it. Every knob is
|
|
275
278
|
* user-tunable from the settings page (entry `dsh-mcp-adapter`) and hot-reloads
|
|
276
279
|
* without a plugin restart.
|
|
280
|
+
*
|
|
281
|
+
* The seven `jev*` keys are the System One decision settings (see
|
|
282
|
+
* ./jev/config.ts): the master switch, the backend/model to ask, the request
|
|
283
|
+
* timeout, the outbound secret list, and the local laya pace-maker. They are
|
|
284
|
+
* spread into {@link Config} from `JEV_CONFIG_FIELDS` so the settings schema
|
|
285
|
+
* and this interface cannot drift, and they are read through their volatile
|
|
286
|
+
* refs per use like every other knob.
|
|
277
287
|
*/
|
|
278
|
-
export interface AdapterConfig {
|
|
288
|
+
export interface AdapterConfig extends JevConfigValue {
|
|
279
289
|
/** Tool-name prefix to fold out of assembled prompts. */
|
|
280
290
|
prefix: VolatileRef<string>;
|
|
281
291
|
/** Name patterns ("*" wildcard) kept native in the prompt. */
|
|
@@ -292,7 +302,7 @@ export interface AdapterConfig {
|
|
|
292
302
|
/**
|
|
293
303
|
* Gate storage directory override; empty (default) =
|
|
294
304
|
* `<dsh home>/storages/mcp-adapter`. Read once at apply time (the store is
|
|
295
|
-
* built there
|
|
305
|
+
* built there; changing it takes effect on the next plugin restart).
|
|
296
306
|
*/
|
|
297
307
|
storageDir: VolatileRef<string>;
|
|
298
308
|
}
|
|
@@ -386,6 +396,19 @@ export interface McpListOptions {
|
|
|
386
396
|
* once at apply time and must observe later /mcp disable/enable writes.
|
|
387
397
|
*/
|
|
388
398
|
getGate?: () => ServerGateState | undefined;
|
|
399
|
+
/**
|
|
400
|
+
* Optional async relevance reranker for `{ query }` calls ONLY (production:
|
|
401
|
+
* the System One seam). Called with the candidate pool — the lexical
|
|
402
|
+
* shortlist, or the whole visible catalog when the prefilter matched
|
|
403
|
+
* nothing — and resolves to the preferred order, or to `undefined` to keep
|
|
404
|
+
* the order it was given.
|
|
405
|
+
*
|
|
406
|
+
* Deliberately a promise-returning callback on a SYNCHRONOUS builder: the
|
|
407
|
+
* remote call happens in the async execute layer, and this builder stays a
|
|
408
|
+
* pure function of (args, schemas, options, ranking). Disabled, failed, or
|
|
409
|
+
* absent rerank ⇒ `undefined` ⇒ the given order, i.e. pre-seam behavior.
|
|
410
|
+
*/
|
|
411
|
+
rankQuery?: (query: string, candidates: readonly RankCandidate[]) => Promise<readonly string[] | undefined>;
|
|
389
412
|
}
|
|
390
413
|
/** Normalized `mcp_list` arguments. */
|
|
391
414
|
export interface McpListArgs {
|
|
@@ -395,6 +418,13 @@ export interface McpListArgs {
|
|
|
395
418
|
server?: string;
|
|
396
419
|
/** Inline every tool's schema in the catalog. */
|
|
397
420
|
verbose?: boolean;
|
|
421
|
+
/**
|
|
422
|
+
* Free-text relevance query. Absent (or blank) = the unfiltered catalog,
|
|
423
|
+
* byte-identical to the pre-query behavior; present = a ranked SHORTLIST
|
|
424
|
+
* plus the total match count, never a silent narrowing of the only
|
|
425
|
+
* discovery path the model has.
|
|
426
|
+
*/
|
|
427
|
+
query?: string;
|
|
398
428
|
}
|
|
399
429
|
/** One catalog entry; `parameters` is present only when expanded. */
|
|
400
430
|
export interface CatalogToolEntry {
|
|
@@ -407,9 +437,24 @@ export interface CatalogServerEntry {
|
|
|
407
437
|
server: string;
|
|
408
438
|
tools: CatalogToolEntry[];
|
|
409
439
|
}
|
|
410
|
-
/**
|
|
440
|
+
/**
|
|
441
|
+
* `mcp_list`'s canonical result: catalog, single expansion, or error.
|
|
442
|
+
*
|
|
443
|
+
* The `{ servers }` variant carries four OPTIONAL keys that are present only
|
|
444
|
+
* on a query answer: `query`, `ranked`, `total` and `note`. They are what
|
|
445
|
+
* makes the subset self-describing — the model can see that this is a ranked
|
|
446
|
+
* shortlist of N of M matches and that the full catalog is one no-argument
|
|
447
|
+
* call away, and `ranked: false` says the prefilter matched nothing at all
|
|
448
|
+
* and this is the semantic fallback rather than a ranking. An unqueried
|
|
449
|
+
* catalog omits them entirely, so the wire value stays byte-identical to the
|
|
450
|
+
* pre-seam shape.
|
|
451
|
+
*/
|
|
411
452
|
export type McpListResult = {
|
|
412
453
|
servers: CatalogServerEntry[];
|
|
454
|
+
query?: string;
|
|
455
|
+
ranked?: boolean;
|
|
456
|
+
total?: number;
|
|
457
|
+
note?: string;
|
|
413
458
|
} | {
|
|
414
459
|
tool: {
|
|
415
460
|
name: string;
|
|
@@ -419,9 +464,43 @@ export type McpListResult = {
|
|
|
419
464
|
} | {
|
|
420
465
|
error: string;
|
|
421
466
|
};
|
|
467
|
+
/**
|
|
468
|
+
* A precomputed ranking for one `{ query }` catalog call — the seam between
|
|
469
|
+
* the async jev layer and the pure builder. `pool`/`total` are the LEXICAL
|
|
470
|
+
* result (so the builder does not have to redo the work and cannot disagree
|
|
471
|
+
* with the shortlist jev was asked about); `order` is jev's permutation of
|
|
472
|
+
* that pool, absent when there is no opinion (fail-open ⇒ `pool` order).
|
|
473
|
+
*/
|
|
474
|
+
export interface ListRanking {
|
|
475
|
+
/** The query this ranking was computed for. */
|
|
476
|
+
query: string;
|
|
477
|
+
/**
|
|
478
|
+
* The candidate set, at most {@link RANK_CANDIDATE_LIMIT} names: the lexical
|
|
479
|
+
* shortlist, best first — or, under {@link ListRanking.semantic}, the whole
|
|
480
|
+
* visible catalog in registry order.
|
|
481
|
+
*/
|
|
482
|
+
pool: readonly string[];
|
|
483
|
+
/** Every lexical match, uncapped — the denominator of the `total` field. */
|
|
484
|
+
total: number;
|
|
485
|
+
/** Reranked names, best first (production: the jev answer). */
|
|
486
|
+
order?: readonly string[];
|
|
487
|
+
/**
|
|
488
|
+
* True when the lexical prefilter matched NOTHING and `pool` is instead the
|
|
489
|
+
* SEMANTIC-FALLBACK set: the whole visible catalog, capped, handed to a
|
|
490
|
+
* reranker that can still tell a Chinese query from an English description
|
|
491
|
+
* and an ASCII one cannot. Absent (the ordinary case) = a lexical ranking,
|
|
492
|
+
* and the answer says `ranked: true` with no mention of a fallback.
|
|
493
|
+
*/
|
|
494
|
+
semantic?: boolean;
|
|
495
|
+
}
|
|
422
496
|
/**
|
|
423
497
|
* Narrow arbitrary model arguments to the `mcp_list` shape, dropping
|
|
424
498
|
* unknown or mistyped keys instead of throwing (the model misbehaves).
|
|
499
|
+
*
|
|
500
|
+
* A blank `query` is dropped rather than kept: an empty search is not a
|
|
501
|
+
* search, and honoring it would put the catalog on a different code path (the
|
|
502
|
+
* ranked one) for a request that means "show me everything".
|
|
503
|
+
*
|
|
425
504
|
* @param args - raw model arguments.
|
|
426
505
|
* @returns the normalized catalog query.
|
|
427
506
|
*/
|
|
@@ -449,21 +528,55 @@ export declare function serverOfToolName(name: string, prefix: string): string;
|
|
|
449
528
|
* @returns the single-line truncated description.
|
|
450
529
|
*/
|
|
451
530
|
export declare function truncateDescription(text: string, limit: number): string;
|
|
531
|
+
/**
|
|
532
|
+
* The lexical relevance shortlist for one `query` catalog call — the pure,
|
|
533
|
+
* synchronous half of seam B. This is both what the jev reranker is asked
|
|
534
|
+
* about and the order that stands when jev is disabled or fails.
|
|
535
|
+
*
|
|
536
|
+
* @param args - the normalized catalog query (must carry a `query`).
|
|
537
|
+
* @param schemas - schemas visible to the calling agent.
|
|
538
|
+
* @param options - catalog knobs.
|
|
539
|
+
* @param gate - live disabled-server snapshot (default: none disabled).
|
|
540
|
+
* @returns the shortlist (name + full description per entry, so a reranker
|
|
541
|
+
* can ask about them without re-reading the schemas) plus the uncapped
|
|
542
|
+
* match count. A query the prefilter cannot lexically match comes back
|
|
543
|
+
* `semantic` with the whole visible catalog as its pool. A structured error
|
|
544
|
+
* means the catalog itself is empty or fully gated.
|
|
545
|
+
*/
|
|
546
|
+
export declare function listQueryRanking(args: McpListArgs, schemas: readonly ToolSchema[], options: McpListOptions, gate?: ServerGateState): {
|
|
547
|
+
pool: readonly RankCandidate[];
|
|
548
|
+
total: number;
|
|
549
|
+
semantic?: boolean;
|
|
550
|
+
error?: string;
|
|
551
|
+
};
|
|
452
552
|
/**
|
|
453
553
|
* Build the `mcp_list` result over the currently visible schemas.
|
|
454
554
|
*
|
|
455
555
|
* Priority: `tool` (full single-tool expansion) > catalog (optionally
|
|
456
|
-
* filtered by `server`, optionally `verbose
|
|
457
|
-
* and any query that selects nothing
|
|
458
|
-
*
|
|
556
|
+
* filtered by `server`, optionally `verbose`, optionally ranked by `query`).
|
|
557
|
+
* Unknown or non-MCP `tool`, and any query that selects nothing out of an
|
|
558
|
+
* empty or fully gated catalog, return a structured error — never a throw —
|
|
559
|
+
* so the model can correct itself.
|
|
560
|
+
*
|
|
561
|
+
* A `query` that the LEXICAL prefilter cannot match is NOT an error: it
|
|
562
|
+
* degrades to the semantic fallback (the whole visible catalog, capped, and
|
|
563
|
+
* `ranked: false`), because "no word of the query appeared anywhere" is not
|
|
564
|
+
* "no tool matches" — see {@link semanticFallbackPool}.
|
|
565
|
+
*
|
|
566
|
+
* `query` is a MODEL-EXPLICIT narrowing: it is the only catalog path that
|
|
567
|
+
* consults `ranking` (production: the System One seam), and a missing/failed
|
|
568
|
+
* rerank degrades to the lexical order. With no `query`, this function is the
|
|
569
|
+
* pre-seim builder unchanged.
|
|
459
570
|
*
|
|
460
571
|
* @param args - the normalized catalog query.
|
|
461
572
|
* @param schemas - schemas visible to the calling agent.
|
|
462
573
|
* @param options - catalog knobs.
|
|
463
574
|
* @param gate - live disabled-server snapshot (default: none disabled).
|
|
575
|
+
* @param ranking - precomputed relevance ranking for `args.query`
|
|
576
|
+
* (production: the async layer's jev answer).
|
|
464
577
|
* @returns the canonical result value.
|
|
465
578
|
*/
|
|
466
|
-
export declare function buildMcpListResult(args: McpListArgs, schemas: readonly ToolSchema[], options: McpListOptions, gate?: ServerGateState): McpListResult;
|
|
579
|
+
export declare function buildMcpListResult(args: McpListArgs, schemas: readonly ToolSchema[], options: McpListOptions, gate?: ServerGateState, ranking?: ListRanking): McpListResult;
|
|
467
580
|
/** Structural slice of a resolvable tool: what dispatch actually consumes. */
|
|
468
581
|
export interface DispatchableTool<E> {
|
|
469
582
|
/** Cooperative timeout budget declared by the tool, if any. */
|
|
@@ -471,6 +584,36 @@ export interface DispatchableTool<E> {
|
|
|
471
584
|
/** The tool body dispatch invokes. */
|
|
472
585
|
execute(args: unknown, exec: E): Promise<unknown>;
|
|
473
586
|
}
|
|
587
|
+
/**
|
|
588
|
+
* The near-miss seam of the unknown-tool branch (seam C).
|
|
589
|
+
*
|
|
590
|
+
* `candidates` is deliberately cheap and SYNCHRONOUS — it is called on a path
|
|
591
|
+
* the model is already reading an error on, and it must answer even when jev
|
|
592
|
+
* is off, timing out, or broken. `rerank` is the optional async refinement
|
|
593
|
+
* (production: the System One seam); `undefined`, a rejection, or an
|
|
594
|
+
* unusable answer all mean "keep the lexical order", which is the behavior
|
|
595
|
+
* the branch had before the seam existed.
|
|
596
|
+
*/
|
|
597
|
+
export interface DidYouMeanHook {
|
|
598
|
+
/** Visible tool names a near-miss may point at, in the calling scope. */
|
|
599
|
+
candidates(): readonly RankCandidate[];
|
|
600
|
+
/** Optional async reorder; absent/undefined/throw ⇒ lexical order stands. */
|
|
601
|
+
rerank?(tool: string, candidates: readonly RankCandidate[]): Promise<readonly string[] | undefined>;
|
|
602
|
+
}
|
|
603
|
+
/** The near-miss pool cap — the same batch ceiling the jev seams use. */
|
|
604
|
+
export declare const DID_YOU_MEAN_POOL_LIMIT = 40;
|
|
605
|
+
/**
|
|
606
|
+
* The visible tools a near-miss may point at: this scope's prefix-matching
|
|
607
|
+
* MCP tools under the servers whitelist, minus the meta-tools and minus
|
|
608
|
+
* disabled servers (dispatch would refuse those anyway, so naming one would
|
|
609
|
+
* be advice the model cannot take).
|
|
610
|
+
*
|
|
611
|
+
* @param schemas - schemas visible to the calling agent.
|
|
612
|
+
* @param options - adapter knobs (prefix/servers).
|
|
613
|
+
* @param gate - live disabled-server snapshot (default: none disabled).
|
|
614
|
+
* @returns the pool, in registry order.
|
|
615
|
+
*/
|
|
616
|
+
export declare function didYouMeanPool(schemas: readonly ToolSchema[], options: Pick<McpCommandOptions, 'prefix' | 'servers'>, gate?: ServerGateState): RankCandidate[];
|
|
474
617
|
/**
|
|
475
618
|
* Dispatch one `mcp_call` to the resolved child tool.
|
|
476
619
|
*
|
|
@@ -497,6 +640,11 @@ export interface DispatchableTool<E> {
|
|
|
497
640
|
* Errors never throw out of dispatch: a child rejection (or raced timeout)
|
|
498
641
|
* is wrapped into a structured `{ error }` the model can correct and retry.
|
|
499
642
|
*
|
|
643
|
+
* The unknown-tool branch additionally offers near-misses (seam C): a lexical
|
|
644
|
+
* prefilter over the scope's visible tools always answers, and an optional
|
|
645
|
+
* async rerank (production: System One) can reorder it. The base error text
|
|
646
|
+
* is unchanged, so this stays a pure addition to the message.
|
|
647
|
+
*
|
|
500
648
|
* @param args - raw model arguments (`{ tool, arguments? }`, validated here).
|
|
501
649
|
* @param prefix - the configured MCP prefix.
|
|
502
650
|
* @param resolve - scope-aware tool resolver (production: `ctx.tools.get`).
|
|
@@ -506,9 +654,11 @@ export interface DispatchableTool<E> {
|
|
|
506
654
|
* disabled branch sits after the prefix/whitelist validation: a disabled
|
|
507
655
|
* server's tools are refused with an explicit `/mcp enable <id>` hint
|
|
508
656
|
* before the child is ever resolved.
|
|
657
|
+
* @param didYouMean - the near-miss seam for the unknown-tool branch
|
|
658
|
+
* (default: none — the pre-seam message, unchanged).
|
|
509
659
|
* @returns the child's resolved value verbatim, or a structured error.
|
|
510
660
|
*/
|
|
511
|
-
export declare function dispatchMcpCall<E>(args: unknown, prefix: string, resolve: (name: string) => DispatchableTool<E> | undefined, exec: E, servers?: readonly string[], gate?: ServerGateState): Promise<unknown>;
|
|
661
|
+
export declare function dispatchMcpCall<E>(args: unknown, prefix: string, resolve: (name: string) => DispatchableTool<E> | undefined, exec: E, servers?: readonly string[], gate?: ServerGateState, didYouMean?: DidYouMeanHook): Promise<unknown>;
|
|
512
662
|
/** Knobs shared by every /mcp renderer — plain data, no Cordis. */
|
|
513
663
|
export interface McpCommandOptions {
|
|
514
664
|
/** Tool-name prefix identifying MCP tools. */
|
|
@@ -534,6 +684,36 @@ export interface McpCommandOptions {
|
|
|
534
684
|
* one in the first place.
|
|
535
685
|
*/
|
|
536
686
|
unassignedServers?: readonly string[];
|
|
687
|
+
/**
|
|
688
|
+
* The seven System One settings as plain data (production: the live
|
|
689
|
+
* volatile config refs, resolved per invocation). Absent ⇒ the views stay
|
|
690
|
+
* byte-identical to the pre-jev plugin — a caller that does not wire the
|
|
691
|
+
* decision support never sees a jev line.
|
|
692
|
+
*/
|
|
693
|
+
jev?: McpJevOptions;
|
|
694
|
+
/**
|
|
695
|
+
* Recorded usage counters for the views that talk about the suggestion
|
|
696
|
+
* cadence (production: the usage mirror). Absent ⇒ those views report "no
|
|
697
|
+
* usage recorded yet" instead of a number.
|
|
698
|
+
*/
|
|
699
|
+
usage?: McpUsageOptions;
|
|
700
|
+
}
|
|
701
|
+
/** The seven jev settings, flattened for display. */
|
|
702
|
+
export interface McpJevOptions {
|
|
703
|
+
readonly jevEnabled: boolean;
|
|
704
|
+
readonly jevBackend: string;
|
|
705
|
+
readonly jevModel: string;
|
|
706
|
+
readonly jevTimeoutMs: number;
|
|
707
|
+
readonly jevSecretFile: string;
|
|
708
|
+
readonly jevLayaFallback: boolean;
|
|
709
|
+
readonly jevLayaUrl: string;
|
|
710
|
+
}
|
|
711
|
+
/** Recorded-usage counters, flattened for display. */
|
|
712
|
+
export interface McpUsageOptions {
|
|
713
|
+
/** Dispatches recorded across every tool. */
|
|
714
|
+
readonly totalCalls: number;
|
|
715
|
+
/** Tools carrying at least one recorded call. */
|
|
716
|
+
readonly toolCount: number;
|
|
537
717
|
}
|
|
538
718
|
/** Usage text returned for unparseable /mcp invocations. */
|
|
539
719
|
export declare const MCP_COMMAND_USAGE: string;
|
|
@@ -545,6 +725,8 @@ export type ParsedMcpCommandInput = {
|
|
|
545
725
|
name: string;
|
|
546
726
|
} | {
|
|
547
727
|
form: 'config';
|
|
728
|
+
} | {
|
|
729
|
+
form: 'suggest';
|
|
548
730
|
} | {
|
|
549
731
|
form: 'disable';
|
|
550
732
|
id: number;
|
|
@@ -557,9 +739,10 @@ export type ParsedMcpCommandInput = {
|
|
|
557
739
|
/**
|
|
558
740
|
* Parse one /mcp raw input into its form. `''` and `'list'` are the tree
|
|
559
741
|
* overview; `'list <name>'` is a server/tool detail; `'config'` shows the
|
|
560
|
-
* effective configuration; `'
|
|
561
|
-
*
|
|
562
|
-
*
|
|
742
|
+
* effective configuration; `'suggest'` shows the current keep suggestions;
|
|
743
|
+
* `'disable <n>'` / `'enable <n>'` are whole-server toggles (n is the stable id
|
|
744
|
+
* shown by `/mcp`, so a bare `0` or non-numeric token is already usage).
|
|
745
|
+
* Everything else parses to the usage error.
|
|
563
746
|
* @param rawInput - exact text following the command name.
|
|
564
747
|
* @returns the normalized command form.
|
|
565
748
|
*/
|
|
@@ -707,6 +890,24 @@ export interface McpDetailView {
|
|
|
707
890
|
* @returns the rendered view.
|
|
708
891
|
*/
|
|
709
892
|
export declare function renderMcpDetail(name: string, schemas: readonly ToolSchema[], options: McpCommandOptions): McpDetailView;
|
|
893
|
+
/**
|
|
894
|
+
* Render the keep-suggestion view (`/mcp suggest`): the current batch of
|
|
895
|
+
* System One verdicts about which FOLDED tools earned a permanent place in
|
|
896
|
+
* every prompt, each with its probability, the usage evidence behind it, and
|
|
897
|
+
* the exact `keep` pattern the operator would write.
|
|
898
|
+
*
|
|
899
|
+
* Strictly a DISPLAY (red line ③): nothing here writes keep, prefix or the
|
|
900
|
+
* gate — the operator decides, one settings-page edit, with the same manual
|
|
901
|
+
* authority they have over every other knob.
|
|
902
|
+
*
|
|
903
|
+
* Every state is explained rather than blank: jev off, jev on with no batch
|
|
904
|
+
* yet, and jev on with rows. Output is line-capped defensively.
|
|
905
|
+
*
|
|
906
|
+
* @param suggestions - the current batch (production: suggestions.json).
|
|
907
|
+
* @param options - adapter knobs; `jev.jevEnabled` and `usage` are read.
|
|
908
|
+
* @returns the rendered view.
|
|
909
|
+
*/
|
|
910
|
+
export declare function renderMcpSuggest(suggestions: readonly SuggestionRecord[], options: Pick<McpCommandOptions, 'jev' | 'usage'>): string;
|
|
710
911
|
/**
|
|
711
912
|
* Render the effective configuration with each knob's hitting tools:
|
|
712
913
|
* which names match the prefix, what each keep pattern holds back, and what
|
|
@@ -750,6 +951,13 @@ export interface McpCommandView {
|
|
|
750
951
|
* finishes in the background — same-process code cannot hard-kill it.
|
|
751
952
|
*/
|
|
752
953
|
signal?: AbortSignal;
|
|
954
|
+
/**
|
|
955
|
+
* The current keep-suggestion batch for `/mcp suggest` (production: read
|
|
956
|
+
* from `<gate dir>/suggestions.json` at invocation time). Absent ⇒ the view
|
|
957
|
+
* reports "no suggestions yet", which is also what a missing or unreadable
|
|
958
|
+
* document means.
|
|
959
|
+
*/
|
|
960
|
+
suggestions?: readonly SuggestionRecord[];
|
|
753
961
|
}
|
|
754
962
|
/** CommandResult-compatible outcome (platform shape: success may omit text). */
|
|
755
963
|
export interface McpCommandOutcome {
|
|
@@ -823,18 +1031,54 @@ export declare function renderDispatched(metaArgs: unknown, value: unknown, reso
|
|
|
823
1031
|
export declare function delegateFinalizeContent(metaArgs: unknown, result: Readonly<ToolExecutionResult>, exec: Readonly<ToolExecution>, resolveChild: (name: string) => ToolDefinition | undefined): ContentBlock[] | undefined;
|
|
824
1032
|
/**
|
|
825
1033
|
* Build the `mcp_list` meta-tool definition.
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
1034
|
+
*
|
|
1035
|
+
* The execute layer is where the async seam lives: a no-argument call is the
|
|
1036
|
+
* pre-seim builder verbatim (no promise, no remote), and only a `query` call
|
|
1037
|
+
* consults `options.rankQuery` — and even then only AFTER a candidate pool
|
|
1038
|
+
* exists, so a disabled/failed rerank changes the ORDER and nothing else.
|
|
1039
|
+
*
|
|
1040
|
+
* @param options - catalog knobs (prefix, description limit, optional
|
|
1041
|
+
* rankQuery). MAY be a live options object (plain getters): the factory
|
|
1042
|
+
* reads `prefix`, `servers`, `descriptionLimit`, `getGate` and
|
|
1043
|
+
* `rankQuery` through the object on EVERY execution, so volatile config can
|
|
1044
|
+
* be wired by reference — only the description text freezes at factory
|
|
1045
|
+
* time.
|
|
831
1046
|
* @param listSchemas - visible-schemas source (production: `ctx.tools.schemas`,
|
|
832
1047
|
* called with the calling agent so restrictions are respected).
|
|
833
1048
|
* @returns the complete tool definition.
|
|
834
1049
|
*/
|
|
835
1050
|
export declare function createMcpListTool(options: McpListOptions, listSchemas: (scope?: ScopeKey) => readonly ToolSchema[]): ToolDefinition;
|
|
1051
|
+
/**
|
|
1052
|
+
* Optional seams {@link createMcpCallTool} wires into mcp_call. All three are
|
|
1053
|
+
* contained: a throwing hook degrades to the pre-seam behavior and never
|
|
1054
|
+
* breaks a dispatch.
|
|
1055
|
+
*/
|
|
1056
|
+
export interface McpCallHooks {
|
|
1057
|
+
/**
|
|
1058
|
+
* Near-miss pool for the unknown-tool branch, resolved in the CALLING
|
|
1059
|
+
* agent's scope (production: the scope's visible prefix tools). Absent ⇒
|
|
1060
|
+
* the pre-seam error text, unchanged.
|
|
1061
|
+
*/
|
|
1062
|
+
didYouMean?: (scope: ScopeKey | undefined) => readonly RankCandidate[];
|
|
1063
|
+
/**
|
|
1064
|
+
* Async rerank of that pool (production: System One). Resolving to
|
|
1065
|
+
* `undefined` or rejecting keeps the lexical order.
|
|
1066
|
+
*/
|
|
1067
|
+
didYouMeanRank?: (scope: ScopeKey | undefined, tool: string, candidates: readonly RankCandidate[]) => Promise<readonly string[] | undefined>;
|
|
1068
|
+
/**
|
|
1069
|
+
* Told about every finished dispatch, success and failure alike (production:
|
|
1070
|
+
* the usage store). Receives `tool: undefined` for a call that never named
|
|
1071
|
+
* one. Synchronous and best-effort: this is on the hot path, so it must not
|
|
1072
|
+
* return a promise and must never be awaited.
|
|
1073
|
+
*/
|
|
1074
|
+
onDispatched?: (event: {
|
|
1075
|
+
tool: string | undefined;
|
|
1076
|
+
error: boolean;
|
|
1077
|
+
}) => void;
|
|
1078
|
+
}
|
|
836
1079
|
/**
|
|
837
1080
|
* Build the `mcp_call` meta-tool definition.
|
|
1081
|
+
*
|
|
838
1082
|
* @param prefix - the configured MCP prefix (dispatch boundary). A thunk is
|
|
839
1083
|
* accepted so volatile config is re-read on every dispatch; a plain string
|
|
840
1084
|
* freezes the boundary at factory time.
|
|
@@ -846,9 +1090,10 @@ export declare function createMcpListTool(options: McpListOptions, listSchemas:
|
|
|
846
1090
|
* gate store's current mirror; default/omitted: nothing is disabled).
|
|
847
1091
|
* Deliberately a callback — the definition object outlives any one gate
|
|
848
1092
|
* snapshot and must observe later /mcp enable/disable writes.
|
|
1093
|
+
* @param hooks - the usage/did-you-mean seams (default: none wired).
|
|
849
1094
|
* @returns the complete tool definition.
|
|
850
1095
|
*/
|
|
851
|
-
export declare function createMcpCallTool(prefix: string | (() => string), resolve: (name: string, scope?: ScopeKey) => ToolDefinition | undefined, servers?: readonly string[] | (() => readonly string[]), getGate?: () => ServerGateState | undefined): ToolDefinition;
|
|
1096
|
+
export declare function createMcpCallTool(prefix: string | (() => string), resolve: (name: string, scope?: ScopeKey) => ToolDefinition | undefined, servers?: readonly string[] | (() => readonly string[]), getGate?: () => ServerGateState | undefined, hooks?: McpCallHooks): ToolDefinition;
|
|
852
1097
|
/**
|
|
853
1098
|
* Register the two meta-tools and install the assembly folding listener.
|
|
854
1099
|
*
|
|
@@ -863,11 +1108,19 @@ export declare function createMcpCallTool(prefix: string | (() => string), resol
|
|
|
863
1108
|
* unreadable document degrades to the empty gate (everything enabled) at
|
|
864
1109
|
* load time, keeping the assemble waterfall alive.
|
|
865
1110
|
*
|
|
866
|
-
* Config volatility: every knob (prefix/keep/servers/descriptionLimit
|
|
867
|
-
* volatile reference — 0.1.7 settings-page writes swap
|
|
868
|
-
* WITHOUT remounting the plugin, so nothing captured at apply
|
|
869
|
-
* stale: the waterfall, both meta-tools and the /mcp command all
|
|
870
|
-
* through {@link cfgNow} per use.
|
|
1111
|
+
* Config volatility: every knob (prefix/keep/servers/descriptionLimit AND the
|
|
1112
|
+
* seven jev* keys) is a volatile reference — 0.1.7 settings-page writes swap
|
|
1113
|
+
* values in place WITHOUT remounting the plugin, so nothing captured at apply
|
|
1114
|
+
* time may go stale: the waterfall, both meta-tools and the /mcp command all
|
|
1115
|
+
* re-read through {@link cfgNow} / {@link jevEnv} per use.
|
|
1116
|
+
*
|
|
1117
|
+
* System One wiring (the only remote calls in this plugin): the decision log,
|
|
1118
|
+
* the usage mirror and the suggestion document all live in the SAME gate
|
|
1119
|
+
* directory as gate.json. The seams are wired to exactly three places — the
|
|
1120
|
+
* background keep-suggestion batch ({@link maybeSuggestBatch}, fired by the
|
|
1121
|
+
* usage counter and awaited by nobody), `mcp_list`'s model-explicit `query`,
|
|
1122
|
+
* and `mcp_call`'s unknown-tool branch. Folding, an unqueried catalog, and a
|
|
1123
|
+
* successful dispatch touch none of them.
|
|
871
1124
|
*
|
|
872
1125
|
* Commands: `/mcp` mounts through a runtime `ctx.inject(['commands'], ...)`,
|
|
873
1126
|
* so hosts without the commands service keep this plugin fully functional —
|