dsh-mcp-panel 0.2.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/LICENSE +201 -0
- package/README.es.md +129 -0
- package/README.hi.md +129 -0
- package/README.md +129 -0
- package/README.pt.md +129 -0
- package/README.zh.md +129 -0
- package/cordis.patch.yml +23 -0
- package/lib/client.js +5045 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +1079 -0
- package/lib/typert.host.js +4261 -0
- package/lib/types/aggregate.d.ts +92 -0
- package/lib/types/aggregate.d.ts.map +1 -0
- package/lib/types/client/McpPanelTab.d.ts +16 -0
- package/lib/types/client/McpPanelTab.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +35 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/locales.d.ts +90 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/present.d.ts +79 -0
- package/lib/types/client/present.d.ts.map +1 -0
- package/lib/types/client/remote.d.ts +119 -0
- package/lib/types/client/remote.d.ts.map +1 -0
- package/lib/types/client/styles.d.ts +12 -0
- package/lib/types/client/styles.d.ts.map +1 -0
- package/lib/types/command.d.ts +122 -0
- package/lib/types/command.d.ts.map +1 -0
- package/lib/types/config.d.ts +63 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/grouping.d.ts +49 -0
- package/lib/types/grouping.d.ts.map +1 -0
- package/lib/types/index.d.ts +41 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/probe.d.ts +67 -0
- package/lib/types/probe.d.ts.map +1 -0
- package/lib/types/sanitize.d.ts +42 -0
- package/lib/types/sanitize.d.ts.map +1 -0
- package/lib/types/service.d.ts +107 -0
- package/lib/types/service.d.ts.map +1 -0
- package/lib/types/typert.host.d.ts +110 -0
- package/lib/types/typert.host.d.ts.map +1 -0
- package/lib/types/upstream.d.ts +67 -0
- package/lib/types/upstream.d.ts.map +1 -0
- package/lib/types/wire.d.ts +342 -0
- package/lib/types/wire.d.ts.map +1 -0
- package/package.json +111 -0
- package/src/aggregate.ts +248 -0
- package/src/client/McpPanelTab.tsx +257 -0
- package/src/client/index.ts +87 -0
- package/src/client/locales.ts +92 -0
- package/src/client/present.ts +127 -0
- package/src/client/remote.ts +35 -0
- package/src/client/styles.ts +235 -0
- package/src/command.ts +387 -0
- package/src/config.ts +110 -0
- package/src/grouping.ts +109 -0
- package/src/index.ts +86 -0
- package/src/probe.ts +198 -0
- package/src/sanitize.ts +115 -0
- package/src/service.ts +279 -0
- package/src/typert.host.ts +25 -0
- package/src/upstream.ts +72 -0
- package/src/wire.ts +221 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hand-written Typert HOST manifest for `dsh-mcp-panel`, exported as
|
|
3
|
+
* `./typert` so the harness's typert-loader registers the `mcpPanel` status
|
|
4
|
+
* and probe invocations automatically when this plugin mounts. Same shape as
|
|
5
|
+
* a generator output (validated by the loader): package face, empty model and
|
|
6
|
+
* schemas, and the canonical invocation list shared with the client Remote
|
|
7
|
+
* contribution (`src/wire.ts`).
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-mcp-panel/typert
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { MCP_PANEL_INVOCATIONS } from './wire.ts'
|
|
13
|
+
|
|
14
|
+
/** Host Typert manifest (validated by `@deepseek-ai/dsh-typert-loader`). */
|
|
15
|
+
export const TYPERT = Object.freeze({
|
|
16
|
+
package: 'dsh-mcp-panel',
|
|
17
|
+
face: 'host',
|
|
18
|
+
schemas: Object.freeze([]),
|
|
19
|
+
invocations: MCP_PANEL_INVOCATIONS,
|
|
20
|
+
model: Object.freeze({
|
|
21
|
+
services: Object.freeze([]),
|
|
22
|
+
events: Object.freeze([]),
|
|
23
|
+
objects: Object.freeze([]),
|
|
24
|
+
}),
|
|
25
|
+
})
|
package/src/upstream.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The proposed upstream observability seam of `@deepseek-ai/dsh-mcp-client`
|
|
3
|
+
* (see `docs/upstream-proposal.md` in the deepseek-harness repository),
|
|
4
|
+
* consumed here before it ships: the typed `mcp/status` Cordis event and the
|
|
5
|
+
* `mcpStatus` query service face.
|
|
6
|
+
*
|
|
7
|
+
* Declarations merge into `@deepseek-ai/cordis`. If upstream later ships the
|
|
8
|
+
* same seam, its identical declarations merge cleanly; a conflicting
|
|
9
|
+
* signature fails this package's compile, which is the intended tripwire.
|
|
10
|
+
* At runtime the seam is feature-detected: with no upstream implementation
|
|
11
|
+
* mounted, no events arrive and `ctx.mcpStatus` is absent, so the panel falls
|
|
12
|
+
* back to derived facts and reports `statusSource: 'derived'`.
|
|
13
|
+
*
|
|
14
|
+
* @module dsh-mcp-panel/upstream
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Supervisor phase after one `mcp/status` transition (mirrors the proposal). */
|
|
18
|
+
export type McpStatusPhase = 'connecting' | 'connected' | 'waiting' | 'exhausted' | 'disposed'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* App-level connection-status payload emitted on every mcp-client state
|
|
22
|
+
* transition. `error` carries raw same-process text; DISPLAY consumers must
|
|
23
|
+
* sanitize (this package's `sanitize.ts`) before rendering.
|
|
24
|
+
*/
|
|
25
|
+
export interface McpStatusPayload {
|
|
26
|
+
/** Stable local namespace from plugin config. */
|
|
27
|
+
serverName: string
|
|
28
|
+
/** Supervisor phase after the transition. */
|
|
29
|
+
phase: McpStatusPhase
|
|
30
|
+
/** Consecutive failed attempts in the current outage (0 while connected). */
|
|
31
|
+
attempt: number
|
|
32
|
+
/** Resolved reconnect budget (`reconnect.maxAttempts`). */
|
|
33
|
+
maxAttempts: number
|
|
34
|
+
/** Scheduled backoff delay while `waiting`. */
|
|
35
|
+
delayMs?: number
|
|
36
|
+
/** Raw error text of the failed attempt or re-sync. */
|
|
37
|
+
error?: string
|
|
38
|
+
/** Tools registered after the last successful sync. */
|
|
39
|
+
toolCount: number
|
|
40
|
+
/** Epoch ms of the last successful connect; absent while down. */
|
|
41
|
+
connectedAt?: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Current per-server status snapshot; the query face of `mcp/status`. */
|
|
45
|
+
export type McpServerStatus = McpStatusPayload
|
|
46
|
+
|
|
47
|
+
/** Structural query face of the proposed `mcpStatus` service (feature-detected). */
|
|
48
|
+
export interface McpStatusQuery {
|
|
49
|
+
/** Current status of every server this process knows. */
|
|
50
|
+
list(): readonly McpServerStatus[]
|
|
51
|
+
/** Current status of one server namespace, or `undefined`. */
|
|
52
|
+
get(serverName: string): McpServerStatus | undefined
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare module '@deepseek-ai/cordis' {
|
|
56
|
+
interface Events {
|
|
57
|
+
/**
|
|
58
|
+
* One MCP client supervisor state transition (the proposed upstream
|
|
59
|
+
* observability seam). App-level: no agent or session scope.
|
|
60
|
+
* @param payload - post-transition status facts.
|
|
61
|
+
* @mode emit
|
|
62
|
+
*/
|
|
63
|
+
'mcp/status'(payload: McpStatusPayload): void
|
|
64
|
+
}
|
|
65
|
+
interface Context {
|
|
66
|
+
/** The proposed upstream status query service; absent until upstream ships it. */
|
|
67
|
+
mcpStatus?: McpStatusQuery
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Exact event name, exported so consumers never hardcode the literal twice. */
|
|
72
|
+
export const MCP_STATUS_EVENT = 'mcp/status'
|
package/src/wire.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The panel's wire vocabulary: the snapshot types served over the `mcpPanel`
|
|
3
|
+
* Remote namespace, their zod v4 validation schema (the strict codec both
|
|
4
|
+
* Typert faces carry), and the single invocation descriptor shared by the
|
|
5
|
+
* host `./typert` manifest and the client Remote contribution. One canonical
|
|
6
|
+
* source for both faces keeps the host and client codecs from ever drifting
|
|
7
|
+
* apart.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-mcp-panel/wire
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { z } from 'zod'
|
|
13
|
+
import type { InvocationDescriptor } from '@deepseek-ai/dsh-typert-protocol'
|
|
14
|
+
|
|
15
|
+
/** Transport recorded for one configured mcp-client row. */
|
|
16
|
+
export type McpTransport = 'stdio' | 'streamable-http' | 'unknown'
|
|
17
|
+
|
|
18
|
+
/** Cordis Fiber phases projected for display; `null` = no fiber observed. */
|
|
19
|
+
export type McpFiberPhase = 'pending' | 'loading' | 'active' | 'failed' | 'unloading' | null
|
|
20
|
+
|
|
21
|
+
/** Connection phase from the proposed upstream `mcp/status` seam, plus `unknown`. */
|
|
22
|
+
export type McpConnectionPhase = 'connecting' | 'connected' | 'waiting' | 'exhausted' | 'disposed' | 'unknown'
|
|
23
|
+
|
|
24
|
+
/** Provenance of the connection fields: upstream events or derived facts only. */
|
|
25
|
+
export type McpStatusSource = 'upstream-event' | 'derived'
|
|
26
|
+
|
|
27
|
+
/** One MCP tool's model-facing view (public name + one-line description). */
|
|
28
|
+
export interface McpToolView {
|
|
29
|
+
/** Public tool name (`mcp__<server>__<raw>` or its deterministic normalized form). */
|
|
30
|
+
name: string
|
|
31
|
+
/** Server-provided description; empty when absent. */
|
|
32
|
+
description: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** One MCP server's read-only status view. */
|
|
36
|
+
export interface McpServerView {
|
|
37
|
+
/** Stable namespace from plugin config. */
|
|
38
|
+
serverName: string
|
|
39
|
+
/** Loader entry id carrying the mcp-client row. */
|
|
40
|
+
entryId: string
|
|
41
|
+
/** Declared transport; `unknown` when the row is not an mcp-client row or is malformed. */
|
|
42
|
+
transport: McpTransport
|
|
43
|
+
/** Display target: the command line (stdio) or the sanitized URL (streamable-http). */
|
|
44
|
+
target: string
|
|
45
|
+
/** Effective loader disabled state (includes parent groups and `!!js` evaluation). */
|
|
46
|
+
enabled: boolean
|
|
47
|
+
/** Cordis fiber phase of the row; `null` when no fiber exists. */
|
|
48
|
+
fiberPhase: McpFiberPhase
|
|
49
|
+
/** Config-declared policy facts (reconnect budget, fail-fast, tool timeout); `null` = defaults. */
|
|
50
|
+
configuredNote: string | null
|
|
51
|
+
/** Registered tools from `ctx.tools.schemas()` under `mcp__<server>__`. */
|
|
52
|
+
toolCount: number
|
|
53
|
+
/** The model-visible tools; empty for a server with none registered. */
|
|
54
|
+
tools: readonly McpToolView[]
|
|
55
|
+
/** Connection phase; `unknown` when no upstream status was observed. */
|
|
56
|
+
phase: McpConnectionPhase
|
|
57
|
+
/** Failed attempts in the current outage (upstream); `-1` when unknown. */
|
|
58
|
+
attempt: number
|
|
59
|
+
/** Resolved reconnect budget (upstream); `-1` when unknown. */
|
|
60
|
+
maxAttempts: number
|
|
61
|
+
/** Scheduled backoff delay while `waiting`; `null` otherwise or unknown. */
|
|
62
|
+
delayMs: number | null
|
|
63
|
+
/** Reconnect attempts observed this process; `-1` when unknown. */
|
|
64
|
+
reconnectCount: number
|
|
65
|
+
/** Most recent error, sanitized for display; `null` when none or unknown. */
|
|
66
|
+
lastError: string | null
|
|
67
|
+
/** Epoch ms of the last successful connect (upstream); `null` otherwise or unknown. */
|
|
68
|
+
connectedAt: number | null
|
|
69
|
+
/** Epoch ms when this process last received an upstream status event; `null` without one. */
|
|
70
|
+
observedAt: number | null
|
|
71
|
+
/** Passive-probe reachability (`null` = probing disabled or never run). */
|
|
72
|
+
probeState: 'reachable' | 'unreachable' | null
|
|
73
|
+
/** Epoch ms of the latest passive-probe settlement; `null` without one. */
|
|
74
|
+
probeCheckedAt: number | null
|
|
75
|
+
/** Where the connection fields came from. */
|
|
76
|
+
statusSource: McpStatusSource
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** One background connectivity probe (panel-only; never model context). */
|
|
80
|
+
export interface McpProbeView {
|
|
81
|
+
/** Background-job id (`mcp-probe-N`). */
|
|
82
|
+
id: string
|
|
83
|
+
/** Server the probe targeted. */
|
|
84
|
+
serverName: string
|
|
85
|
+
/** Job lifecycle state. */
|
|
86
|
+
status: 'running' | 'stopping' | 'completed' | 'killed' | 'failed'
|
|
87
|
+
/** Epoch ms when the probe started. */
|
|
88
|
+
startedAt: number
|
|
89
|
+
/** Epoch ms when the probe settled; `null` while running. */
|
|
90
|
+
finishedAt: number | null
|
|
91
|
+
/** Sanitized one-line detail (HTTP status, latency, server info, or error). */
|
|
92
|
+
detail: string | null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The complete panel snapshot served by `mcpPanel/status`. */
|
|
96
|
+
export interface McpPanelSnapshot {
|
|
97
|
+
/** True when the proposed upstream `mcp/status` seam produced data this process. */
|
|
98
|
+
observed: boolean
|
|
99
|
+
/** Absolute path of the profile patch layer that disable/enable suggestions name. */
|
|
100
|
+
patchFile: string | null
|
|
101
|
+
/** Suggested panel refresh interval in ms; `0` = the tab refreshes on demand only. */
|
|
102
|
+
refreshIntervalMs: number
|
|
103
|
+
/** One row per server namespace (configured rows first, leftover namespaces last). */
|
|
104
|
+
servers: readonly McpServerView[]
|
|
105
|
+
/** Connectivity probes this process, newest first. */
|
|
106
|
+
probes: readonly McpProbeView[]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Strict wire schema for {@link McpPanelSnapshot} (zod v4, both Typert faces). */
|
|
110
|
+
export const MCP_PANEL_SNAPSHOT_SCHEMA = z.object({
|
|
111
|
+
observed: z.boolean(),
|
|
112
|
+
patchFile: z.string().nullable(),
|
|
113
|
+
refreshIntervalMs: z.number().int(),
|
|
114
|
+
servers: z.array(z.object({
|
|
115
|
+
serverName: z.string(),
|
|
116
|
+
entryId: z.string(),
|
|
117
|
+
transport: z.union([z.literal('stdio'), z.literal('streamable-http'), z.literal('unknown')]),
|
|
118
|
+
target: z.string(),
|
|
119
|
+
enabled: z.boolean(),
|
|
120
|
+
fiberPhase: z.union([z.literal('pending'), z.literal('loading'), z.literal('active'), z.literal('failed'), z.literal('unloading'), z.null()]),
|
|
121
|
+
configuredNote: z.string().nullable(),
|
|
122
|
+
toolCount: z.number().int(),
|
|
123
|
+
tools: z.array(z.object({
|
|
124
|
+
name: z.string(),
|
|
125
|
+
description: z.string(),
|
|
126
|
+
})),
|
|
127
|
+
phase: z.union([z.literal('connecting'), z.literal('connected'), z.literal('waiting'), z.literal('exhausted'), z.literal('disposed'), z.literal('unknown')]),
|
|
128
|
+
attempt: z.number().int(),
|
|
129
|
+
maxAttempts: z.number().int(),
|
|
130
|
+
delayMs: z.number().int().nullable(),
|
|
131
|
+
reconnectCount: z.number().int(),
|
|
132
|
+
lastError: z.string().nullable(),
|
|
133
|
+
connectedAt: z.number().int().nullable(),
|
|
134
|
+
observedAt: z.number().int().nullable(),
|
|
135
|
+
probeState: z.union([z.literal('reachable'), z.literal('unreachable'), z.null()]),
|
|
136
|
+
probeCheckedAt: z.number().int().nullable(),
|
|
137
|
+
statusSource: z.union([z.literal('upstream-event'), z.literal('derived')]),
|
|
138
|
+
})),
|
|
139
|
+
probes: z.array(z.object({
|
|
140
|
+
id: z.string(),
|
|
141
|
+
serverName: z.string(),
|
|
142
|
+
status: z.union([z.literal('running'), z.literal('stopping'), z.literal('completed'), z.literal('killed'), z.literal('failed')]),
|
|
143
|
+
startedAt: z.number().int(),
|
|
144
|
+
finishedAt: z.number().int().nullable(),
|
|
145
|
+
detail: z.string().nullable(),
|
|
146
|
+
})),
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The `mcpPanel/status` invocation descriptor, shared verbatim by the host
|
|
151
|
+
* `TYPERT` manifest (`src/typert.host.ts`) and the client
|
|
152
|
+
* `TypertRemoteContribution` (`src/client/remote.ts`). Hand-written in the
|
|
153
|
+
* exact shape the Typert generator emits; validated by the typert loader and
|
|
154
|
+
* the client registry at mount time.
|
|
155
|
+
*/
|
|
156
|
+
export const MCP_PANEL_STATUS_DESCRIPTOR = Object.freeze({
|
|
157
|
+
id: 'dsh-mcp-panel#mcpPanel/status',
|
|
158
|
+
service: 'mcpPanel',
|
|
159
|
+
namespace: 'mcpPanel',
|
|
160
|
+
method: 'status',
|
|
161
|
+
invocation: Object.freeze({ kind: 'direct' }),
|
|
162
|
+
parameters: Object.freeze([]),
|
|
163
|
+
result: Object.freeze({
|
|
164
|
+
mode: 'strict',
|
|
165
|
+
typeSymbol: 'dsh-mcp-panel/types#McpPanelSnapshot',
|
|
166
|
+
schema: MCP_PANEL_SNAPSHOT_SCHEMA,
|
|
167
|
+
}),
|
|
168
|
+
sourceLocation: Object.freeze({ file: 'src/wire.ts', line: 1, column: 1 }),
|
|
169
|
+
} as const) satisfies InvocationDescriptor
|
|
170
|
+
|
|
171
|
+
/** Result of the `mcpPanel/probe` invocation: a started panel-only probe. */
|
|
172
|
+
export interface ProbeStarted {
|
|
173
|
+
/** Background-job id (`mcp-probe-N`). */
|
|
174
|
+
jobId: string
|
|
175
|
+
/** Where the result lands: the settings tab, never model context. */
|
|
176
|
+
note: string
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Strict wire schema for {@link ProbeStarted}. */
|
|
180
|
+
export const PROBE_STARTED_SCHEMA = z.object({
|
|
181
|
+
jobId: z.string(),
|
|
182
|
+
note: z.string(),
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The `mcpPanel/probe` invocation descriptor: start a one-shot probe from the
|
|
187
|
+
* settings panel (same background-job mechanics as the `mcp_probe` tool).
|
|
188
|
+
*/
|
|
189
|
+
export const MCP_PANEL_PROBE_DESCRIPTOR = Object.freeze({
|
|
190
|
+
id: 'dsh-mcp-panel#mcpPanel/probe',
|
|
191
|
+
service: 'mcpPanel',
|
|
192
|
+
namespace: 'mcpPanel',
|
|
193
|
+
method: 'probe',
|
|
194
|
+
invocation: Object.freeze({ kind: 'direct' }),
|
|
195
|
+
parameters: Object.freeze([Object.freeze({
|
|
196
|
+
name: 'serverName',
|
|
197
|
+
wire: 'serverName',
|
|
198
|
+
source: 'json',
|
|
199
|
+
codec: Object.freeze({
|
|
200
|
+
mode: 'strict',
|
|
201
|
+
typeSymbol: 'dsh-mcp-panel/types#ProbeRequestServerName',
|
|
202
|
+
schema: z.string(),
|
|
203
|
+
}),
|
|
204
|
+
} satisfies InvocationDescriptor['parameters'][number])]),
|
|
205
|
+
result: Object.freeze({
|
|
206
|
+
mode: 'strict',
|
|
207
|
+
typeSymbol: 'dsh-mcp-panel/types#ProbeStarted',
|
|
208
|
+
schema: PROBE_STARTED_SCHEMA,
|
|
209
|
+
}),
|
|
210
|
+
sourceLocation: Object.freeze({ file: 'src/wire.ts', line: 1, column: 1 }),
|
|
211
|
+
} as const) satisfies InvocationDescriptor
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The canonical invocation list both Typert faces register — the host
|
|
215
|
+
* manifest and the client contribution share these exact descriptor objects,
|
|
216
|
+
* so the two wire codecs can never drift apart.
|
|
217
|
+
*/
|
|
218
|
+
export const MCP_PANEL_INVOCATIONS = Object.freeze([
|
|
219
|
+
MCP_PANEL_STATUS_DESCRIPTOR,
|
|
220
|
+
MCP_PANEL_PROBE_DESCRIPTOR,
|
|
221
|
+
])
|