dsh-mcp-panel 0.2.0 → 0.3.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.md +95 -0
- package/README.es.md +17 -5
- package/README.hi.md +17 -5
- package/README.md +17 -5
- package/README.pt.md +17 -5
- package/README.zh.md +17 -5
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/docs/optimization-plan-v2.zh.md +325 -0
- package/docs/optimization-plan.zh.md +157 -0
- package/docs/research-notes.zh.md +114 -0
- package/docs/upstream-proposal.md +150 -0
- package/lib/client.js +283 -130
- package/lib/client.js.map +1 -1
- package/lib/index.js +87 -19
- package/lib/typert.host.js +2 -1
- package/lib/types/client/McpPanelTab.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +12 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/present.d.ts +29 -1
- package/lib/types/client/present.d.ts.map +1 -1
- package/lib/types/client/remote.d.ts +9 -9
- package/lib/types/command.d.ts +6 -0
- package/lib/types/command.d.ts.map +1 -1
- package/lib/types/probe.d.ts.map +1 -1
- package/lib/types/service.d.ts +11 -2
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/typert.host.d.ts +9 -9
- package/lib/types/wire.d.ts +21 -21
- package/lib/types/wire.d.ts.map +1 -1
- package/package.json +18 -8
- package/src/client/McpPanelTab.tsx +68 -14
- package/src/client/locales.ts +12 -0
- package/src/client/present.ts +49 -1
- package/src/client/styles.ts +32 -0
- package/src/command.ts +39 -4
- package/src/probe.ts +1 -1
- package/src/service.ts +62 -10
- package/src/wire.ts +3 -3
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Upstream proposal: minimal connection-status observability for `@deepseek-ai/dsh-mcp-client`
|
|
2
|
+
|
|
3
|
+
> **副本说明**:本文档随本插件仓库分发(权威副本)。提案的最终落点目标是
|
|
4
|
+
> `deepseek-ai/deepseek-harness` 仓库的 `docs/upstream-proposal.md`;若二者不一致,
|
|
5
|
+
> 以本插件仓库版本为插件实现所依据的契约,PR 内容以提交到上游仓库的版本为准。
|
|
6
|
+
|
|
7
|
+
**Status:** proposal (not yet merged upstream). Implementation exists in the fork branch
|
|
8
|
+
`PerryLink/deepseek-harness:feat/mcp-client-status-observability-seam` (commit `e1611e9`,
|
|
9
|
+
rebase of `deepseek-ai/deepseek-harness:master`) with tests, bilingual docs, and an Agent
|
|
10
|
+
Note; a ready-to-open PR is described in the handoff at
|
|
11
|
+
[deepseek-harness Discussions #1300](https://github.com/deepseek-ai/deepseek-harness/discussions/1300)
|
|
12
|
+
(compare link: `deepseek-ai/deepseek-harness/compare/master...PerryLink:feat/mcp-client-status-observability-seam`).
|
|
13
|
+
The upstream repository currently does not accept external pull requests; whoever holds merge
|
|
14
|
+
access can open the PR from that branch. Target repository: `deepseek-ai/deepseek-harness`,
|
|
15
|
+
package `packages/mcp/mcp-client`.
|
|
16
|
+
**Author:** `dsh-mcp-panel` (runtime management panel for the official MCP client).
|
|
17
|
+
**Scope:** status events + a status query service only. No transport, OAuth, protocol, or reconnect-policy changes.
|
|
18
|
+
|
|
19
|
+
## Motivation
|
|
20
|
+
|
|
21
|
+
The official MCP client keeps every connection fact in supervisor closure state
|
|
22
|
+
(`packages/mcp/mcp-client/src/connection.ts`): the live client generation,
|
|
23
|
+
`failedAttempts`, `connectedAt`, `firstAttemptError`, and the registered tool
|
|
24
|
+
disposers are private locals of `startConnection()`. The only observability is
|
|
25
|
+
the logger: "reconnecting (warn, with attempt count and delay)", "recovered
|
|
26
|
+
(info)", "final failure and disabled-loss (error)" (README "Behavior"). The
|
|
27
|
+
package's own invariant companion states the gap explicitly:
|
|
28
|
+
|
|
29
|
+
> "the bridge exposes no independent server-to-tool snapshot after an
|
|
30
|
+
> asynchronous resync" — `src/invariant.ts`
|
|
31
|
+
|
|
32
|
+
A read-only runtime management surface (a `/mcp` command, a web settings card)
|
|
33
|
+
therefore cannot show connection status, recent errors, or reconnect counts
|
|
34
|
+
without either reimplementing the client or guessing from the tool registry — and guessing from `ctx.tools` misreports a failed-but-tools-still-registered
|
|
35
|
+
server as healthy. This proposal adds the minimal seam that lets any consumer
|
|
36
|
+
observe the supervisor without touching its transport or reconnect logic.
|
|
37
|
+
|
|
38
|
+
## Proposed surface
|
|
39
|
+
|
|
40
|
+
### 1. Typed Cordis event `mcp/status` (emit)
|
|
41
|
+
|
|
42
|
+
One emission per supervisor state transition, payload:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
/** App-level connection-status payload emitted on every mcp-client state transition. */
|
|
46
|
+
export interface McpStatusPayload {
|
|
47
|
+
/** Stable local namespace from plugin config. */
|
|
48
|
+
serverName: string
|
|
49
|
+
/** Supervisor phase after the transition. */
|
|
50
|
+
phase: 'connecting' | 'connected' | 'waiting' | 'exhausted' | 'disposed'
|
|
51
|
+
/** Consecutive failed attempts in the current outage (0 while connected). */
|
|
52
|
+
attempt: number
|
|
53
|
+
/** Resolved reconnect budget (`reconnect.maxAttempts`). */
|
|
54
|
+
maxAttempts: number
|
|
55
|
+
/** Scheduled backoff delay while `waiting`. */
|
|
56
|
+
delayMs?: number
|
|
57
|
+
/** Raw error text of the failed attempt or re-sync; consumers sanitize before display. */
|
|
58
|
+
error?: string
|
|
59
|
+
/** Tools registered after the last successful sync (`disposers.size`). */
|
|
60
|
+
toolCount: number
|
|
61
|
+
/** Epoch ms of the last successful connect; absent while down. */
|
|
62
|
+
connectedAt?: number
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Emission sites (all inside `startConnection()`, current line numbers):
|
|
67
|
+
|
|
68
|
+
| Phase | Where | Notes |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `connecting` | top of `connectGeneration()` (line ~237) | `attempt` = `failedAttempts` at entry; first attempt reports 0 |
|
|
71
|
+
| `connected` | after initial sync, `connectedAt` set (line ~303) | `attempt: 0`, `toolCount` = `disposers.size` |
|
|
72
|
+
| `connected` (re-sync failure) | notification re-sync catch (line ~267) | phase stays `connected` (last good list keeps serving), `error` set |
|
|
73
|
+
| `waiting` | `scheduleReconnect()` after the timer is armed (line ~218) | `attempt` after increment, `delayMs` included |
|
|
74
|
+
| `exhausted` | give-up branch (line ~213) | after `maxAttempts` consecutive failures |
|
|
75
|
+
| `disposed` | `dispose()` entry (line ~327) | terminal per plugin instance |
|
|
76
|
+
|
|
77
|
+
The event is process-app-level (no agent/session): connection state belongs to
|
|
78
|
+
the app, not to a conversation, so a session event would be the wrong carrier
|
|
79
|
+
and would pollute every session log with duplicated app state.
|
|
80
|
+
|
|
81
|
+
### 2. Query service `mcpStatus`
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
/** Current per-server status snapshot; the query face of `mcp/status`. */
|
|
85
|
+
export interface McpServerStatus extends McpStatusPayload {}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Per-app status registry. `report()` is the single writer: it stores the
|
|
89
|
+
* payload and emits the typed `mcp/status` event, so push consumers and
|
|
90
|
+
* late-joining query consumers observe the same truth.
|
|
91
|
+
*/
|
|
92
|
+
export class McpStatusService extends Service {
|
|
93
|
+
constructor(ctx: Context) // super(ctx, 'mcpStatus')
|
|
94
|
+
report(payload: McpStatusPayload): void
|
|
95
|
+
list(): McpServerStatus[]
|
|
96
|
+
get(serverName: string): McpServerStatus | undefined
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
One service per app root, created by the first live mcp-client instance and
|
|
101
|
+
shared by the rest — the same `WeakMap<Context, …>` singleton pattern the file
|
|
102
|
+
already uses for `activeServerNames` (`src/index.ts` lines ~45, 148). Each
|
|
103
|
+
instance's supervisor calls `report()` at the six sites above. `dispose()`
|
|
104
|
+
reports `disposed` before unregistering tools so `toolCount` is still accurate
|
|
105
|
+
in the terminal payload.
|
|
106
|
+
|
|
107
|
+
### 3. Typing
|
|
108
|
+
|
|
109
|
+
- Event: `declare module '@deepseek-ai/cordis' { interface Events { 'mcp/status'(payload: McpStatusPayload): void } }` with `@mode emit` JSDoc.
|
|
110
|
+
- Service: `declare module '@deepseek-ai/cordis' { interface Context { mcpStatus: McpStatusService } }`.
|
|
111
|
+
- Both live in a new `src/status.ts`, re-exported from the package root; a new
|
|
112
|
+
`mcp-client-invariant` companion can assert `report` — tool-registry
|
|
113
|
+
generation if desired (optional, not required for this proposal).
|
|
114
|
+
|
|
115
|
+
## Deliberate non-goals
|
|
116
|
+
|
|
117
|
+
- **No transport / OAuth / protocol changes** — the supervisor's reconnect
|
|
118
|
+
loop, transport factory, and tool bridge stay byte-for-byte; this only adds
|
|
119
|
+
notifications around them.
|
|
120
|
+
- **No sanitization in the event** — the payload is trusted same-process data;
|
|
121
|
+
`error` carries the real text. Display consumers redact before rendering
|
|
122
|
+
(reference implementation: `dsh-mcp-panel` `src/sanitize.ts`).
|
|
123
|
+
- **No Typert remote export from mcp-client itself** — which host services
|
|
124
|
+
reach the browser is an app-composition choice (gateway selection), not a
|
|
125
|
+
client-package concern. Panels compose their own remote service over this
|
|
126
|
+
seam, as `dsh-mcp-panel` does.
|
|
127
|
+
- **No per-session projection** — app-level runtime-varying state does not
|
|
128
|
+
belong in session logs.
|
|
129
|
+
|
|
130
|
+
## PR contents (when implemented)
|
|
131
|
+
|
|
132
|
+
1. `src/status.ts` — payload type, service, event declaration.
|
|
133
|
+
2. `src/connection.ts` — six `report()` call sites (no behavior change).
|
|
134
|
+
3. `src/index.ts` — mount the shared `McpStatusService` singleton; export the types.
|
|
135
|
+
4. `README.md` — "Observability" section documenting the event and the service.
|
|
136
|
+
5. Tests — `status.spec.ts` (report/list/get, singleton across two instances),
|
|
137
|
+
`reconnect.spec.ts` additions asserting the emitted phase sequence for a
|
|
138
|
+
crash loop (connecting → connected → waiting → … → exhausted) and for
|
|
139
|
+
`reconnect.enabled: false`.
|
|
140
|
+
6. Agent Note per repository convention (non-trivial change).
|
|
141
|
+
|
|
142
|
+
## Consumer behavior (dsh-mcp-panel, implemented against this proposal)
|
|
143
|
+
|
|
144
|
+
The panel subscribes `ctx.on('mcp/status', …)` and optionally queries
|
|
145
|
+
`ctx.get('mcpStatus')` on start (feature detection — the service is absent
|
|
146
|
+
until this PR lands). When neither produces data, the panel reports status as
|
|
147
|
+
`unknown` with `statusSource: 'derived'` (from loader entries + tool registry
|
|
148
|
+
only) instead of fabricating a connection state. That keeps the panel honest
|
|
149
|
+
both before and after this proposal lands.
|
|
150
|
+
|