dsh-agent-plugins-market 0.6.2 → 0.7.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 +117 -111
- package/README.zh.md +112 -98
- package/client/client.js +547 -540
- package/client/client.js.map +1 -1
- package/lib/application/catalog-context.js +181 -0
- package/lib/application/catalog.js +217 -1110
- package/lib/application/details.js +6 -6
- package/lib/application/install-store.js +95 -0
- package/lib/application/lsp-service.js +97 -0
- package/lib/application/mcp-service.js +246 -0
- package/lib/application/panel-resources.js +29 -12
- package/lib/application/ports.js +21 -0
- package/lib/application/project-agent-roles.js +1 -1
- package/lib/application/snapshot-cache.js +160 -0
- package/lib/application/source-store.js +521 -0
- package/lib/catalog/archive.js +34 -29
- package/lib/catalog/component-files.js +14 -6
- package/lib/catalog/fs-probes.js +43 -0
- package/lib/catalog/manifests.js +53 -92
- package/lib/catalog/native-project.js +2 -2
- package/lib/catalog/paths.js +66 -55
- package/lib/catalog/project-config.js +2 -1
- package/lib/catalog/project-hooks.js +9 -1
- package/lib/catalog/scan-resolvers.js +29 -45
- package/lib/catalog/skills-parse.js +14 -10
- package/lib/catalog/source-catalog.js +3 -2
- package/lib/catalog/suite-components.js +5 -5
- package/lib/catalog/suite-scanner.js +3 -1
- package/lib/catalog/surfaces.js +5 -26
- package/lib/catalog/validate.js +7 -11
- package/lib/contracts/market.js +1 -0
- package/lib/index.js +91 -208
- package/lib/model/types.js +26 -3
- package/lib/routes.js +45 -15
- package/lib/runtime/agent-role-names.js +70 -0
- package/lib/runtime/agent-role-router.js +116 -102
- package/lib/runtime/commands-mounts.js +9 -6
- package/lib/runtime/deadline.js +28 -0
- package/lib/runtime/feedback-tool.js +94 -43
- package/lib/runtime/hooks-mounts.js +9 -11
- package/lib/runtime/host-locale.js +10 -21
- package/lib/runtime/lsp-direct-config.js +19 -19
- package/lib/runtime/lsp-mounts.js +159 -68
- package/lib/runtime/lsp-server-state.js +4 -4
- package/lib/runtime/lsp-status.js +1 -1
- package/lib/runtime/mcp-auth-record.js +42 -0
- package/lib/runtime/mcp-backend.js +6 -5
- package/lib/runtime/mcp-client/config.js +0 -1
- package/lib/runtime/mcp-client/connection.js +32 -37
- package/lib/runtime/mcp-client/host-contract.js +10 -0
- package/lib/runtime/mcp-client/oauth.js +5 -7
- package/lib/runtime/mcp-client/plugin-identity.js +28 -0
- package/lib/runtime/mcp-client/projection.js +202 -0
- package/lib/runtime/mcp-client/tools.js +4 -189
- package/lib/runtime/mcp-config.js +29 -10
- package/lib/runtime/mcp-direct-config.js +33 -16
- package/lib/runtime/mcp-mounts.js +35 -75
- package/lib/runtime/mcp-overrides.js +5 -5
- package/lib/runtime/mcp-redaction.js +3 -2
- package/lib/runtime/mcp-status.js +7 -8
- package/lib/runtime/mount-lifecycle.js +73 -0
- package/lib/runtime/profile-seam.js +321 -0
- package/lib/runtime/project-runtime.js +11 -8
- package/lib/runtime/reconcile-scheduler.js +99 -0
- package/lib/runtime/reconciler.js +4 -0
- package/lib/runtime/server-config.js +6 -4
- package/lib/runtime/settings-namespace.js +136 -0
- package/lib/runtime/skills-provider.js +5 -3
- package/lib/runtime/source-auto-update.js +70 -0
- package/lib/{model/state.js → runtime/state-store.js} +9 -11
- package/lib/runtime/storage-migration.js +35 -18
- package/lib/runtime/subagent-catalog.js +5 -4
- package/lib/runtime/user-commands.js +2 -2
- package/lib/runtime/user-panels.js +31 -21
- package/lib/runtime/user-store.js +12 -13
- package/lib/types/application/catalog-context.d.ts +118 -0
- package/lib/types/application/catalog.d.ts +99 -304
- package/lib/types/application/install-store.d.ts +21 -0
- package/lib/types/application/lsp-service.d.ts +36 -0
- package/lib/types/application/mcp-service.d.ts +71 -0
- package/lib/types/application/panel-resources.d.ts +8 -0
- package/lib/types/application/ports.d.ts +72 -0
- package/lib/types/application/queries.d.ts +13 -26
- package/lib/types/application/snapshot-cache.d.ts +51 -0
- package/lib/types/application/source-store.d.ts +100 -0
- package/lib/types/catalog/archive.d.ts +3 -11
- package/lib/types/catalog/component-files.d.ts +4 -0
- package/lib/types/catalog/fs-probes.d.ts +10 -0
- package/lib/types/catalog/manifests.d.ts +17 -16
- package/lib/types/catalog/native-project.d.ts +2 -2
- package/lib/types/catalog/paths.d.ts +47 -15
- package/lib/types/catalog/scan-pipeline.d.ts +3 -3
- package/lib/types/catalog/scan-resolvers.d.ts +3 -9
- package/lib/types/catalog/skills-parse.d.ts +0 -7
- package/lib/types/catalog/source-catalog.d.ts +3 -3
- package/lib/types/catalog/suite-scanner.d.ts +8 -6
- package/lib/types/catalog/validate.d.ts +0 -2
- package/lib/types/contracts/lsp-status.d.ts +35 -0
- package/lib/types/contracts/market.d.ts +3 -2
- package/lib/types/contracts/mcp-status.d.ts +4 -0
- package/lib/types/model/types.d.ts +29 -5
- package/lib/types/runtime/agent-role-names.d.ts +6 -0
- package/lib/types/runtime/agent-role-router.d.ts +54 -51
- package/lib/types/runtime/deadline.d.ts +18 -0
- package/lib/types/runtime/feedback-tool.d.ts +33 -11
- package/lib/types/runtime/hooks-mounts.d.ts +1 -1
- package/lib/types/runtime/host-locale.d.ts +1 -1
- package/lib/types/runtime/lsp-direct-config.d.ts +3 -3
- package/lib/types/runtime/lsp-mounts.d.ts +62 -19
- package/lib/types/runtime/lsp-status.d.ts +2 -1
- package/lib/types/runtime/mcp-auth-record.d.ts +29 -0
- package/lib/types/runtime/mcp-backend.d.ts +7 -4
- package/lib/types/runtime/mcp-client/config.d.ts +0 -2
- package/lib/types/runtime/mcp-client/host-contract.d.ts +78 -0
- package/lib/types/runtime/mcp-client/oauth.d.ts +1 -1
- package/lib/types/runtime/mcp-client/plugin-identity.d.ts +16 -0
- package/lib/types/runtime/mcp-client/projection.d.ts +45 -0
- package/lib/types/runtime/mcp-client/tools.d.ts +5 -72
- package/lib/types/runtime/mcp-config.d.ts +25 -4
- package/lib/types/runtime/mcp-direct-config.d.ts +18 -4
- package/lib/types/runtime/mcp-mounts.d.ts +10 -15
- package/lib/types/runtime/mcp-overrides.d.ts +1 -1
- package/lib/types/runtime/mcp-status.d.ts +1 -1
- package/lib/types/runtime/mount-lifecycle.d.ts +53 -0
- package/lib/types/runtime/profile-seam.d.ts +64 -0
- package/lib/types/runtime/reconcile-scheduler.d.ts +45 -0
- package/lib/types/runtime/reconciler.d.ts +2 -0
- package/lib/types/runtime/settings-namespace.d.ts +76 -0
- package/lib/types/runtime/source-auto-update.d.ts +33 -0
- package/lib/types/{model/state.d.ts → runtime/state-store.d.ts} +2 -2
- package/lib/types/runtime/storage-migration.d.ts +5 -3
- package/lib/types/runtime/subagent-catalog.d.ts +2 -0
- package/lib/types/runtime/user-panels.d.ts +8 -11
- package/lib/types/runtime/user-store.d.ts +2 -4
- package/package.json +48 -21
- package/schemas/README.md +1 -1
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { ReconnectConfig } from './connection.js';
|
|
12
12
|
import type { McpServerPolicy } from '../../model/types.js';
|
|
13
|
-
export type { ReconnectConfig, ResolvedReconnectPolicy } from './connection.js';
|
|
14
|
-
export { RECONNECT_DEFAULTS } from './connection.js';
|
|
15
13
|
/** Default timeout for individual MCP tool calls (ms). */
|
|
16
14
|
export declare const DEFAULT_TOOL_CALL_TIMEOUT_MS = 60000;
|
|
17
15
|
/** Valid `serverName`, kept below the public tool-name budget. */
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural mirrors of the harness tool-registration surface the bridge
|
|
3
|
+
* registers against: the runtime accepts the same object shape the upstream
|
|
4
|
+
* `@deepseek-ai/dsh-tools` bridge registers, so the model-facing behavior is
|
|
5
|
+
* identical. Mirrored rather than imported so the market never loads a second
|
|
6
|
+
* copy of the host runtime.
|
|
7
|
+
*
|
|
8
|
+
* @module runtime/mcp-client/host-contract
|
|
9
|
+
*/
|
|
10
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
11
|
+
import type { JsonSchemaNode } from './json-schema-subset.js';
|
|
12
|
+
/** Lossless JSON value vocabulary the bridge exchanges with the registry. */
|
|
13
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
14
|
+
[key: string]: JsonValue;
|
|
15
|
+
};
|
|
16
|
+
/** Canonical MCP result exposed to Code Mode without discarding protocol blocks. */
|
|
17
|
+
export type McpResult<Structured extends JsonValue = JsonValue> = {
|
|
18
|
+
content: JsonValue[];
|
|
19
|
+
structuredContent?: Structured;
|
|
20
|
+
};
|
|
21
|
+
/** The agent on whose behalf a tool call runs (structural mirror). */
|
|
22
|
+
interface ToolExecutionAgent {
|
|
23
|
+
session?: {
|
|
24
|
+
requestHeader?: () => {
|
|
25
|
+
config?: {
|
|
26
|
+
provider?: string;
|
|
27
|
+
model?: string;
|
|
28
|
+
};
|
|
29
|
+
} | undefined;
|
|
30
|
+
};
|
|
31
|
+
options?: {
|
|
32
|
+
provider?: string;
|
|
33
|
+
model?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Execution identity handed to a tool body (structural mirror). */
|
|
37
|
+
export interface ToolExecution {
|
|
38
|
+
/** Caller-owned cancellation for this invocation. */
|
|
39
|
+
readonly signal: AbortSignal;
|
|
40
|
+
/** The calling agent, when the call came from one. */
|
|
41
|
+
readonly agent?: ToolExecutionAgent;
|
|
42
|
+
}
|
|
43
|
+
/** A settled tool outcome (structural mirror). */
|
|
44
|
+
export interface ToolExecutionResult {
|
|
45
|
+
readonly value: JsonValue;
|
|
46
|
+
readonly content: ContentBlock[];
|
|
47
|
+
readonly isError: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** One registered tool (structural mirror of the host registration shape). */
|
|
50
|
+
export interface ToolDefinition {
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly description: string;
|
|
53
|
+
readonly parameters: Record<string, unknown>;
|
|
54
|
+
readonly output: {
|
|
55
|
+
readonly schema: JsonSchemaNode;
|
|
56
|
+
render(args: unknown, value: JsonValue): ContentBlock[];
|
|
57
|
+
};
|
|
58
|
+
execute(args: unknown, exec: ToolExecution): Promise<unknown>;
|
|
59
|
+
finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The host surface the bridge needs: a tool registry, a logger, and the two
|
|
63
|
+
* optional services image projection consults. Structural — the cordis
|
|
64
|
+
* context is adapted onto this shape by the bridge shell.
|
|
65
|
+
*/
|
|
66
|
+
export interface ToolHost {
|
|
67
|
+
readonly logger: {
|
|
68
|
+
error(message: string): void;
|
|
69
|
+
warn(message: string): void;
|
|
70
|
+
info(message: string): void;
|
|
71
|
+
};
|
|
72
|
+
readonly tools: {
|
|
73
|
+
register(definition: ToolDefinition): () => void;
|
|
74
|
+
};
|
|
75
|
+
/** Optional service lookup; image projection degrades when absent. */
|
|
76
|
+
getService?(name: 'attachments' | 'llm'): unknown;
|
|
77
|
+
}
|
|
78
|
+
export {};
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* then last exactly as long as the process.
|
|
23
23
|
*
|
|
24
24
|
* Ported from the harness `dsh-mcp-client` OAuth implementation (upstream
|
|
25
|
-
* commits archived at `docs/upstream-proposal/patches/`); the record format is
|
|
25
|
+
* commits archived at `docs/developer/upstream-proposal/patches/`); the record format is
|
|
26
26
|
* byte-compatible, so grants written by either implementation are interchangeable.
|
|
27
27
|
*
|
|
28
28
|
* @module runtime/mcp-client/oauth
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This plugin's own identity as advertised to the servers it talks to: the MCP
|
|
3
|
+
* client implementation name and version, and the RFC 7591 `software_id` /
|
|
4
|
+
* `software_version` carried in OAuth dynamic-registration metadata.
|
|
5
|
+
*
|
|
6
|
+
* The version is read from the installed manifest through Node's package
|
|
7
|
+
* self-reference, so a release bumps exactly one number — `package.json`. A
|
|
8
|
+
* copy that can no longer resolve its own manifest (a bundle that inlined this
|
|
9
|
+
* module) reports {@link UNKNOWN_VERSION} rather than failing the connection.
|
|
10
|
+
*
|
|
11
|
+
* @module runtime/mcp-client/plugin-identity
|
|
12
|
+
*/
|
|
13
|
+
/** Name npm and the harness know this plugin by. */
|
|
14
|
+
export declare const PLUGIN_NAME = "dsh-agent-plugins-market";
|
|
15
|
+
/** This plugin's release version. */
|
|
16
|
+
export declare const PLUGIN_VERSION: string;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP result projection: turn the content blocks an MCP server returns into
|
|
3
|
+
* the host content vocabulary the model sees.
|
|
4
|
+
*
|
|
5
|
+
* Text-like blocks collapse into newline-joined text runs; declared images are
|
|
6
|
+
* decoded, admitted against the live model route, and saved to the durable
|
|
7
|
+
* attachment store. Every refusal — an unsupported media type, a model that
|
|
8
|
+
* does not declare image input, a storage fault — projects the block as text
|
|
9
|
+
* explaining why, and the canonical raw value stays available to programmatic
|
|
10
|
+
* callers in the tool result.
|
|
11
|
+
*
|
|
12
|
+
* Everything here reads an untrusted network payload: the MCP server is an
|
|
13
|
+
* external process and the SDK's declared-required fields may be absent at
|
|
14
|
+
* runtime, so each block is narrowed field by field.
|
|
15
|
+
*
|
|
16
|
+
* @module runtime/mcp-client/projection
|
|
17
|
+
*/
|
|
18
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
19
|
+
import type { JsonValue, McpResult, ToolExecution, ToolHost } from './host-contract.js';
|
|
20
|
+
/** Async rich projection staged for one exact execution. */
|
|
21
|
+
export interface PreparedProjection {
|
|
22
|
+
/** Canonical MCP value returned by execute before registry materialization. */
|
|
23
|
+
value: McpResult;
|
|
24
|
+
/** Synchronous output.render projection expected before finalization. */
|
|
25
|
+
fallback: ContentBlock[];
|
|
26
|
+
/** Image-enriched or explicit-refusal projection prepared during execute. */
|
|
27
|
+
content: ContentBlock[];
|
|
28
|
+
}
|
|
29
|
+
/** Whether an untrusted MCP content array contains a declared image block. */
|
|
30
|
+
export declare function containsImage(content: JsonValue[]): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Decode, preflight, and durably save one MCP result's ordered image batch.
|
|
33
|
+
* Any refusal projects every image as text while retaining the canonical raw
|
|
34
|
+
* value for programmatic callers.
|
|
35
|
+
*/
|
|
36
|
+
export declare function prepareImageProjection(host: ToolHost, exec: ToolExecution, content: JsonValue[], toolName: string): Promise<ContentBlock[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Extract text from an MCP content array into a single string.
|
|
39
|
+
* - text blocks: join with '\n'
|
|
40
|
+
* - image/audio/resource blocks: replaced with a placeholder
|
|
41
|
+
*
|
|
42
|
+
* Defensive: fields that the MCP spec declares required (mimeType, text) are
|
|
43
|
+
* guarded with fallbacks because this is a network trust boundary.
|
|
44
|
+
*/
|
|
45
|
+
export declare function extractText(mcpContent: JsonValue[], toolName: string): string;
|
|
@@ -9,81 +9,15 @@
|
|
|
9
9
|
* to the DeepSeek function-name constraints. The raw name is only ever sent on
|
|
10
10
|
* the wire (`tools/call`); the public name is never parsed to recover it.
|
|
11
11
|
*
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* upstream bridge registers, so the model-facing behavior is identical.
|
|
12
|
+
* The registration surface this module targets is mirrored by
|
|
13
|
+
* `./host-contract.js`; MCP results are mapped into host content blocks by
|
|
14
|
+
* `./projection.js`.
|
|
16
15
|
*
|
|
17
16
|
* @module runtime/mcp-client/tools
|
|
18
17
|
*/
|
|
19
18
|
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
20
|
-
import type {
|
|
21
|
-
|
|
22
|
-
/** Lossless JSON value vocabulary the bridge exchanges with the registry. */
|
|
23
|
-
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
24
|
-
[key: string]: JsonValue;
|
|
25
|
-
};
|
|
26
|
-
/** Canonical MCP result exposed to Code Mode without discarding protocol blocks. */
|
|
27
|
-
export type McpResult<Structured extends JsonValue = JsonValue> = {
|
|
28
|
-
content: JsonValue[];
|
|
29
|
-
structuredContent?: Structured;
|
|
30
|
-
};
|
|
31
|
-
/** The agent on whose behalf a tool call runs (structural mirror). */
|
|
32
|
-
interface ToolExecutionAgent {
|
|
33
|
-
session?: {
|
|
34
|
-
requestHeader?: () => {
|
|
35
|
-
config?: {
|
|
36
|
-
provider?: string;
|
|
37
|
-
model?: string;
|
|
38
|
-
};
|
|
39
|
-
} | undefined;
|
|
40
|
-
};
|
|
41
|
-
options?: {
|
|
42
|
-
provider?: string;
|
|
43
|
-
model?: string;
|
|
44
|
-
};
|
|
45
|
-
} /** Execution identity handed to a tool body (structural mirror). */
|
|
46
|
-
export interface ToolExecution {
|
|
47
|
-
/** Caller-owned cancellation for this invocation. */
|
|
48
|
-
readonly signal: AbortSignal;
|
|
49
|
-
/** The calling agent, when the call came from one. */
|
|
50
|
-
readonly agent?: ToolExecutionAgent;
|
|
51
|
-
}
|
|
52
|
-
/** A settled tool outcome (structural mirror). */
|
|
53
|
-
export interface ToolExecutionResult {
|
|
54
|
-
readonly value: JsonValue;
|
|
55
|
-
readonly content: ContentBlock[];
|
|
56
|
-
readonly isError: boolean;
|
|
57
|
-
}
|
|
58
|
-
/** One registered tool (structural mirror of the host registration shape). */
|
|
59
|
-
export interface ToolDefinition {
|
|
60
|
-
readonly name: string;
|
|
61
|
-
readonly description: string;
|
|
62
|
-
readonly parameters: Record<string, unknown>;
|
|
63
|
-
readonly output: {
|
|
64
|
-
readonly schema: JsonSchemaNode;
|
|
65
|
-
render(args: unknown, value: JsonValue): ContentBlock[];
|
|
66
|
-
};
|
|
67
|
-
execute(args: unknown, exec: ToolExecution): Promise<unknown>;
|
|
68
|
-
finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* The host surface the bridge needs: a tool registry, a logger, and the two
|
|
72
|
-
* optional services image projection consults. Structural — the cordis
|
|
73
|
-
* context is adapted onto this shape by the bridge shell.
|
|
74
|
-
*/
|
|
75
|
-
export interface ToolHost {
|
|
76
|
-
readonly logger: {
|
|
77
|
-
error(message: string): void;
|
|
78
|
-
warn(message: string): void;
|
|
79
|
-
info(message: string): void;
|
|
80
|
-
};
|
|
81
|
-
readonly tools: {
|
|
82
|
-
register(definition: ToolDefinition): () => void;
|
|
83
|
-
};
|
|
84
|
-
/** Optional service lookup; image projection degrades when absent. */
|
|
85
|
-
getService?(name: 'attachments' | 'llm'): unknown;
|
|
86
|
-
}
|
|
19
|
+
import type { ToolHost } from './host-contract.js';
|
|
20
|
+
export type { JsonValue, McpResult, ToolDefinition, ToolExecution, ToolExecutionResult, ToolHost } from './host-contract.js';
|
|
87
21
|
/** Resolved options relevant to tool bridging. */
|
|
88
22
|
export interface ToolBridgeOptions {
|
|
89
23
|
enabledTools?: string[];
|
|
@@ -136,4 +70,3 @@ export declare function publicToolName(serverName: string, rawName: string): str
|
|
|
136
70
|
* disposers — the exact set of live registrations owned by this server.
|
|
137
71
|
*/
|
|
138
72
|
export declare function syncTools(client: Client, host: ToolHost, opts: ToolBridgeOptions, previous: ToolDisposers): Promise<ToolDisposers>;
|
|
139
|
-
export {};
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import type { Config } from './mcp-client/config.js';
|
|
2
|
-
import { type McpSuiteOverrides } from './mcp-overrides.js';
|
|
3
|
-
import type { McpServer, Suite } from '../model/types.js';
|
|
2
|
+
import { type McpServerOverride, type McpSuiteOverrides } from './mcp-overrides.js';
|
|
3
|
+
import type { McpServer, McpServerSse, McpServerStdio, McpServerStreamableHttp, Suite } from '../model/types.js';
|
|
4
4
|
export interface McpMountRequest {
|
|
5
5
|
suiteId: string;
|
|
6
6
|
serverKey: string;
|
|
7
7
|
config: Config;
|
|
8
8
|
}
|
|
9
|
+
/** One `mcp.json` server as it will actually be used, with the user's override applied. */
|
|
10
|
+
export interface EffectiveMcpServer {
|
|
11
|
+
serverKey: string;
|
|
12
|
+
server: McpServerStdio | McpServerStreamableHttp | McpServerSse;
|
|
13
|
+
/** The override that produced this view; consumers render its state from it. */
|
|
14
|
+
override: McpServerOverride | undefined;
|
|
15
|
+
/** Whether the server is used at all — an override can disable a declaration without removing it. */
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
/** External credential references the effective definition needs. */
|
|
18
|
+
credentialRefs: string[];
|
|
19
|
+
}
|
|
9
20
|
export type McpMountFailureCode = 'unsupported-transport' | 'missing-credential' | 'credential-error' | 'unmount-failed' | 'mount-failed'
|
|
10
21
|
/** The derived serverName's namespace is already mounted by another MCP client — informational, not a failure. */
|
|
11
22
|
| 'foreign-mount'
|
|
@@ -24,6 +35,14 @@ export interface McpCredentialResolver {
|
|
|
24
35
|
source?: string;
|
|
25
36
|
} | undefined>;
|
|
26
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The effective view of one suite's `mcp.json` servers: the user's per-server
|
|
40
|
+
* overrides applied, every row flagged with whether it is used at all, and each
|
|
41
|
+
* row's credential references read off the effective shape. Mounting, status
|
|
42
|
+
* and detail rendering read this one projection, so a server cannot be listed
|
|
43
|
+
* as enabled while mounting something else.
|
|
44
|
+
*/
|
|
45
|
+
export declare function effectiveMcpServers(suite: Suite, overrides?: McpSuiteOverrides): EffectiveMcpServer[];
|
|
27
46
|
/**
|
|
28
47
|
* Build one mount request per supported mcp.json server, resolving every
|
|
29
48
|
* `${NAME}` through the credential resolver before a child process or HTTP
|
|
@@ -33,12 +52,14 @@ export interface McpCredentialResolver {
|
|
|
33
52
|
* @param overrides user-owned per-server overrides (url/headers/env/args
|
|
34
53
|
* replacement plus enable/disable); applied before mount. Disabled servers
|
|
35
54
|
* are omitted entirely.
|
|
36
|
-
* @returns mount requests
|
|
37
|
-
*
|
|
55
|
+
* @returns mount requests, per-server failures, and the credential references
|
|
56
|
+
* the effective servers need. serverName collisions are reported by the
|
|
57
|
+
* mount registry, not here.
|
|
38
58
|
*/
|
|
39
59
|
export declare function toMcpMounts(suite: Suite, pluginDataRoot: string, overrides?: McpSuiteOverrides, resolver?: McpCredentialResolver): Promise<{
|
|
40
60
|
mounts: McpMountRequest[];
|
|
41
61
|
failures: McpMountFailure[];
|
|
62
|
+
credentialRefs: string[];
|
|
42
63
|
}>;
|
|
43
64
|
/** Find external credential references used by one MCP server definition. */
|
|
44
65
|
export declare function credentialRefsInServer(server: McpServer): string[];
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type McpSuiteConfig, type Suite } from '../model/types.js';
|
|
2
2
|
export declare const USER_MCP_SOURCE = "@user-mcp";
|
|
3
3
|
export declare const USER_MCP_SUITE = "user-mcp";
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* User-created services reuse the owned bridge lifecycle without changing host configuration.
|
|
6
|
+
*
|
|
7
|
+
* The declarations live in the shared Agent layout root
|
|
8
|
+
* (`~/.agents/mcp.json`), so a service the user adds by hand is ordinary
|
|
9
|
+
* `mcpServers` JSON in the same place other Agent tools look.
|
|
10
|
+
*
|
|
11
|
+
* The suite always carries an `mcp` document — an absent or malformed
|
|
12
|
+
* `mcp.json` leaves an empty one — so callers read `.mcp.servers` directly
|
|
13
|
+
* instead of re-checking the optional field.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadUserMcpSuite(agentsRoot: string): Promise<Suite & {
|
|
16
|
+
mcp: McpSuiteConfig;
|
|
17
|
+
}>;
|
|
6
18
|
/** Add one service atomically; reject collisions and malformed config before writing. */
|
|
7
|
-
export declare function addUserMcpServer(
|
|
19
|
+
export declare function addUserMcpServer(agentsRoot: string, name: string, server: unknown): Promise<void>;
|
|
20
|
+
/** The user's hand-written MCP declaration file: `<agentsRoot>/mcp.json`. */
|
|
21
|
+
export declare function userMcpPath(agentsRoot: string): string;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { Context } from '@deepseek-ai/cordis';
|
|
12
12
|
import type { McpBackend } from './mcp-backend.js';
|
|
13
|
-
import {
|
|
13
|
+
import type { McpSuiteOverrides } from './mcp-overrides.js';
|
|
14
14
|
import { type McpMountFailureCode } from './mcp-config.js';
|
|
15
15
|
import type { Suite } from '../model/types.js';
|
|
16
16
|
export interface McpMountDiagnostic {
|
|
@@ -23,7 +23,6 @@ export interface McpMountDiagnostic {
|
|
|
23
23
|
export declare class McpMountRegistry {
|
|
24
24
|
private readonly ctx;
|
|
25
25
|
private readonly pluginDataRoot;
|
|
26
|
-
private readonly namespace?;
|
|
27
26
|
private readonly live;
|
|
28
27
|
private readonly names;
|
|
29
28
|
private readonly credentialRefs;
|
|
@@ -33,11 +32,9 @@ export declare class McpMountRegistry {
|
|
|
33
32
|
/** The active MCP mount backend ('builtin' bridge or host client compat mode). */
|
|
34
33
|
private backendProvider;
|
|
35
34
|
/** Serialize mount and unmount passes so a disable cannot race an in-flight spawn. */
|
|
36
|
-
private
|
|
37
|
-
/**
|
|
35
|
+
private readonly passes;
|
|
36
|
+
/** Delayed re-attempts for mounts that are not live yet. */
|
|
38
37
|
private readonly retries;
|
|
39
|
-
/** Attempt count per mount key; reset whenever a retry succeeds. */
|
|
40
|
-
private readonly attempts;
|
|
41
38
|
/**
|
|
42
39
|
* Mount keys flagged for an explicit rebuild (re-authorize): the next
|
|
43
40
|
* reconcile must tear down and remount them even when the resolved config
|
|
@@ -46,7 +43,7 @@ export declare class McpMountRegistry {
|
|
|
46
43
|
private readonly forcedRemounts;
|
|
47
44
|
/** Snapshot of the last reconciled suite set, replayed by retry passes. */
|
|
48
45
|
private lastEnabled;
|
|
49
|
-
constructor(ctx: Context, pluginDataRoot: string
|
|
46
|
+
constructor(ctx: Context, pluginDataRoot: string);
|
|
50
47
|
/** Install the per-suite overrides provider (suiteId -> overrides). */
|
|
51
48
|
setOverridesProvider(provider: () => Promise<Map<string, McpSuiteOverrides>>): void;
|
|
52
49
|
/**
|
|
@@ -66,6 +63,12 @@ export declare class McpMountRegistry {
|
|
|
66
63
|
* its in-memory tokens) untouched.
|
|
67
64
|
*/
|
|
68
65
|
forceRemount(suiteId: string, serverKey: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Flag every live mount for a rebuild on the next reconcile: the manual
|
|
68
|
+
* retry path. A bridge whose server died underneath it still matches its own
|
|
69
|
+
* config fingerprint, so nothing else would ever re-verify that mount.
|
|
70
|
+
*/
|
|
71
|
+
forceRemountAll(): void;
|
|
69
72
|
/** The mount key owning one derived serverName; undefined when not mounted here. */
|
|
70
73
|
serverOwner(serverName: string): {
|
|
71
74
|
suiteId: string;
|
|
@@ -75,14 +78,6 @@ export declare class McpMountRegistry {
|
|
|
75
78
|
reconcile(enabledSuites: Suite[]): Promise<McpMountDiagnostic[]>;
|
|
76
79
|
/** Mount/unmount MCP servers to match the enabled suites exactly. */
|
|
77
80
|
private reconcileNow;
|
|
78
|
-
/**
|
|
79
|
-
* Schedule a delayed re-attempt for a mount that still is not live.
|
|
80
|
-
*
|
|
81
|
-
* Mounts are retried because a remote endpoint or a credential may become
|
|
82
|
-
* available slightly after the suite is enabled. The schedule is bounded, and
|
|
83
|
-
* a server that keeps failing simply stops retrying until the next reconcile.
|
|
84
|
-
*/
|
|
85
|
-
private scheduleRetry;
|
|
86
81
|
/** Dispose every live mount after queued reconciliation passes settle. */
|
|
87
82
|
disposeAll(): Promise<void>;
|
|
88
83
|
/** Mount one precomputed request (source config merged with overrides). */
|
|
@@ -29,7 +29,7 @@ export declare function overridesDir(dataRoot: string): string;
|
|
|
29
29
|
export declare function suiteOverridePath(dataRoot: string, suiteId: string): string;
|
|
30
30
|
/** Load one suite's persisted overrides; unreadable files yield none. */
|
|
31
31
|
export declare function loadSuiteOverrides(dataRoot: string, suiteId: string): Promise<McpSuiteOverrides>;
|
|
32
|
-
/** Persist one suite's overrides
|
|
32
|
+
/** Persist one suite's overrides through the harness atomic write. */
|
|
33
33
|
export declare function saveSuiteOverrides(dataRoot: string, suiteId: string, overrides: McpSuiteOverrides): Promise<void>;
|
|
34
34
|
/** Keep only recognized fields with correct shapes; drop everything else. */
|
|
35
35
|
export declare function sanitizeOverrides(raw: unknown): McpSuiteOverrides;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { McpSuiteOverrides } from './mcp-overrides.js';
|
|
2
2
|
import type { McpStatusPayload } from '../contracts/mcp-status.js';
|
|
3
3
|
import { inspectToolRegistry, type McpToolSnapshot } from './tool-registry-observer.js';
|
|
4
4
|
import type { Suite } from '../model/types.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared mount lifecycle: the pass queue and the bounded retry schedule that
|
|
3
|
+
* every surface registry runs on top of its own mount/unmount semantics. Which
|
|
4
|
+
* failures are worth retrying stays the surface's own decision; only the timing
|
|
5
|
+
* and the bookkeeping live here.
|
|
6
|
+
*/
|
|
7
|
+
/** Structural `ctx.plugin` handle for one dynamically mounted plugin. */
|
|
8
|
+
export interface MountPluginHandle {
|
|
9
|
+
await(): Promise<unknown>;
|
|
10
|
+
dispose(): void | Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
/** Structural `ctx.plugin` surface for mounting one plugin instance. */
|
|
13
|
+
export interface PluginMountContext {
|
|
14
|
+
plugin(plugin: unknown, config: unknown): MountPluginHandle;
|
|
15
|
+
}
|
|
16
|
+
/** One failed mount offered to the retry schedule. */
|
|
17
|
+
export interface RetryTarget {
|
|
18
|
+
/** Mount key; also the retry identity, so a key holds at most one pending timer. */
|
|
19
|
+
key: string;
|
|
20
|
+
/** How the mount is named in log lines, e.g. `suite/server`. */
|
|
21
|
+
label: string;
|
|
22
|
+
/** Why the mount failed; omitted when the failure must not be retried. */
|
|
23
|
+
reason?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Serializes reconcile passes: each pass starts after the previous one settles. */
|
|
26
|
+
export declare class SerialPassQueue {
|
|
27
|
+
private tail;
|
|
28
|
+
/** Queue one pass behind the in-flight one, so a disable cannot race an in-flight spawn. */
|
|
29
|
+
run<T>(pass: () => Promise<T>): Promise<T>;
|
|
30
|
+
}
|
|
31
|
+
export interface RetrySchedulerOptions {
|
|
32
|
+
/** Re-run the surface's reconcile against its last known input. */
|
|
33
|
+
replay(): Promise<unknown>;
|
|
34
|
+
/** Write one lifecycle log line through the surface's own logger. */
|
|
35
|
+
log(message: string): void;
|
|
36
|
+
}
|
|
37
|
+
/** Delayed re-attempts for a mount that is not live yet, keyed by mount key. */
|
|
38
|
+
export declare class RetryScheduler {
|
|
39
|
+
private readonly options;
|
|
40
|
+
/** Pending retry timers keyed by mount key, so a teardown can cancel them. */
|
|
41
|
+
private readonly timers;
|
|
42
|
+
/** Attempt count per mount key, dropped when its target stops being retryable. */
|
|
43
|
+
private readonly attempts;
|
|
44
|
+
constructor(options: RetrySchedulerOptions);
|
|
45
|
+
/**
|
|
46
|
+
* Arm the retry for one failed mount, replacing any timer already pending for
|
|
47
|
+
* its key. A target carrying no reason is a deterministic skip rather than a
|
|
48
|
+
* transient failure: it drops that key's budget and cancels its timer.
|
|
49
|
+
*/
|
|
50
|
+
schedule(target: RetryTarget): void;
|
|
51
|
+
/** Cancel every pending retry and drop all attempt budgets. */
|
|
52
|
+
clear(): void;
|
|
53
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Every package the plugin provisions for LSP, in declaration order. */
|
|
2
|
+
export declare const LSP_PACKAGE_SPECIFIERS: readonly ["@deepseek-ai/dsh-lsp", "@deepseek-ai/dsh-lsp-stdio", "@deepseek-ai/dsh-tool-lsp"];
|
|
3
|
+
/** Whether the launcher watches the profile patch file after boot. */
|
|
4
|
+
export type PatchReload = 'live' | 'startup';
|
|
5
|
+
/** One `insert` row that claims a seam the plugin owns. */
|
|
6
|
+
export interface LegacySeamRow {
|
|
7
|
+
/** The row's `id`, or an empty string when the user wrote none. */
|
|
8
|
+
id: string;
|
|
9
|
+
/** The package name the row inserts. */
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
/** A profile carrying a legacy LSP layer that must be removed. */
|
|
13
|
+
export interface LegacyLspSeam {
|
|
14
|
+
/** Profile name, i.e. its directory basename. */
|
|
15
|
+
profile: string;
|
|
16
|
+
/** Absolute profile directory. */
|
|
17
|
+
dir: string;
|
|
18
|
+
/** Absolute path of the profile's patch file. */
|
|
19
|
+
patchPath: string;
|
|
20
|
+
/** The conflicting `insert` rows, in file order. */
|
|
21
|
+
rows: LegacySeamRow[];
|
|
22
|
+
/** `dsh.profile.bundles` entries naming one of {@link LSP_PACKAGE_SPECIFIERS}. */
|
|
23
|
+
bundles: string[];
|
|
24
|
+
/** Declared dependency names drawn from {@link LSP_PACKAGE_SPECIFIERS}. */
|
|
25
|
+
dependencies: string[];
|
|
26
|
+
/** Whether the host hot-reloads the patch file, i.e. whether a restart is needed. */
|
|
27
|
+
patchReload: PatchReload;
|
|
28
|
+
}
|
|
29
|
+
/** Outcome of removing one profile's legacy LSP layer. */
|
|
30
|
+
export interface SeamMigrationResult {
|
|
31
|
+
profile: string;
|
|
32
|
+
patchPath: string;
|
|
33
|
+
/** Where the pre-edit content was kept. */
|
|
34
|
+
backupPath: string;
|
|
35
|
+
/** The rows that were removed. */
|
|
36
|
+
removed: LegacySeamRow[];
|
|
37
|
+
/** True when the host must restart before the removal takes effect. */
|
|
38
|
+
restartRequired: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find every profile that still registers a legacy LSP seam.
|
|
42
|
+
*
|
|
43
|
+
* Best effort by design: profile files are the user's, so a directory that
|
|
44
|
+
* cannot be read or a patch file that does not parse is skipped rather than
|
|
45
|
+
* failing the status surface that calls this.
|
|
46
|
+
* @param home - the Harness home; defaults to `$DSH_HOME` or `~/.dsh`.
|
|
47
|
+
* @returns each matching profile, ordered by profile name.
|
|
48
|
+
*/
|
|
49
|
+
export declare function findLegacyLspSeams(home?: string): Promise<LegacyLspSeam[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Remove one profile's legacy LSP rows.
|
|
52
|
+
*
|
|
53
|
+
* The file is re-read here rather than trusting the caller's snapshot, so a
|
|
54
|
+
* migration always edits the current on-disk content. The rewrite is verified
|
|
55
|
+
* before it lands and the pre-edit content is written beside it first, so a
|
|
56
|
+
* failure leaves the profile exactly as it was.
|
|
57
|
+
* @param seam - a profile previously returned by {@link findLegacyLspSeams}.
|
|
58
|
+
* @param now - timestamp used in the backup filename; injectable for tests.
|
|
59
|
+
* @returns what was removed and where the backup went.
|
|
60
|
+
* @throws Error when the profile no longer carries a removable row, or the rewrite does not verify.
|
|
61
|
+
*/
|
|
62
|
+
export declare function migrateLegacyLspSeam(seam: LegacyLspSeam, now?: Date): Promise<SeamMigrationResult>;
|
|
63
|
+
/** One-line remediation shared by the mount diagnostic and the status payload. */
|
|
64
|
+
export declare function describeLegacySeam(seam: LegacyLspSeam): string;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coalesced runtime reconciliation for the enabled suite set.
|
|
3
|
+
*
|
|
4
|
+
* Settings, credential and catalog events arrive in bursts and can land while
|
|
5
|
+
* a slow MCP connection is still opening. One pass is kept in flight and at
|
|
6
|
+
* most one fresh follow-up is remembered, so a burst collapses into a single
|
|
7
|
+
* reconcile instead of a queue of stale snapshots that each reconnect the
|
|
8
|
+
* same servers.
|
|
9
|
+
*
|
|
10
|
+
* @module runtime/reconcile-scheduler
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import type { Suite } from '../model/types.js';
|
|
14
|
+
import type { McpMountDiagnostic } from './mcp-mounts.js';
|
|
15
|
+
import type { RuntimeReconciler } from './reconciler.js';
|
|
16
|
+
/** The catalog surface one reconcile pass reads from and publishes to. */
|
|
17
|
+
export interface ReconcileHost {
|
|
18
|
+
/** Enabled user-dimension suites to reconcile. */
|
|
19
|
+
enabledSuites(): Promise<Suite[]>;
|
|
20
|
+
/** Publish the pass's MCP mount diagnostics for the status surface. */
|
|
21
|
+
publishMcpDiagnostics(diagnostics: McpMountDiagnostic[]): void;
|
|
22
|
+
}
|
|
23
|
+
export declare class ReconcileScheduler {
|
|
24
|
+
private readonly ctx;
|
|
25
|
+
private readonly runtime;
|
|
26
|
+
private readonly host;
|
|
27
|
+
private disposed;
|
|
28
|
+
private requested;
|
|
29
|
+
private pass;
|
|
30
|
+
private readonly pendingCredentialRefs;
|
|
31
|
+
private credentialFlush;
|
|
32
|
+
private readonly releaseCredentialUpdates;
|
|
33
|
+
constructor(ctx: Context, runtime: RuntimeReconciler, host: ReconcileHost);
|
|
34
|
+
/** Reconcile now, joining the pass in flight and remembering one follow-up for it. */
|
|
35
|
+
request(): Promise<void>;
|
|
36
|
+
/** Stop scheduling; later events are dropped. */
|
|
37
|
+
dispose(): void;
|
|
38
|
+
private reconcileOnce;
|
|
39
|
+
/**
|
|
40
|
+
* Coalesce credential updates: the debounce collapses a burst into one pass,
|
|
41
|
+
* and the pending refs are kept until that pass runs, so an update arriving
|
|
42
|
+
* mid-reconcile is never dropped.
|
|
43
|
+
*/
|
|
44
|
+
private flushCredentialUpdates;
|
|
45
|
+
}
|
|
@@ -44,6 +44,8 @@ export declare class RuntimeReconciler {
|
|
|
44
44
|
usesCredential(ref: string): boolean;
|
|
45
45
|
/** Flag one MCP mount for an explicit rebuild on the next reconcile. */
|
|
46
46
|
forceMcpRemount(suiteId: string, serverKey: string): void;
|
|
47
|
+
/** Flag every live MCP mount for a rebuild on the next reconcile. */
|
|
48
|
+
forceMcpRemountAll(): void;
|
|
47
49
|
/** The mount key owning one derived serverName; undefined when not mounted here. */
|
|
48
50
|
mcpServerOwner(serverName: string): {
|
|
49
51
|
suiteId: string;
|