dsh-agent-plugins-market 0.5.2 → 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 +29 -3
- package/README.zh.md +29 -3
- package/client/client.js +1388 -204
- package/client/client.js.map +1 -1
- package/lib/application/catalog.js +537 -71
- package/lib/application/details.js +39 -12
- package/lib/catalog/archive.js +341 -0
- package/lib/catalog/git.js +85 -12
- package/lib/catalog/legacy-root-migration.js +51 -0
- package/lib/catalog/lsp-spec.js +87 -0
- package/lib/catalog/manifests.js +84 -32
- package/lib/catalog/paths.js +65 -7
- package/lib/catalog/scan-pipeline.js +50 -0
- package/lib/catalog/scan-resolvers.js +402 -0
- package/lib/catalog/source-catalog.js +22 -8
- package/lib/catalog/suite-scanner.js +5 -176
- package/lib/catalog/surfaces.js +80 -30
- package/lib/contracts/lsp-status.js +2 -0
- package/lib/contracts/market.js +8 -1
- package/lib/index.js +118 -13
- package/lib/model/state.js +14 -1
- package/lib/model/types.js +21 -2
- package/lib/routes.js +88 -6
- package/lib/runtime/commands-mounts.js +6 -2
- package/lib/runtime/hooks-mounts.js +9 -6
- package/lib/runtime/lsp-direct-config.js +44 -0
- package/lib/runtime/lsp-mounts.js +236 -0
- package/lib/runtime/lsp-status.js +96 -0
- package/lib/runtime/mcp-backend.js +60 -0
- package/lib/runtime/mcp-client/bridge.js +126 -0
- package/lib/runtime/mcp-client/config.js +42 -0
- package/lib/runtime/mcp-client/connection.js +380 -0
- package/lib/runtime/mcp-client/host-seams.js +88 -0
- package/lib/runtime/mcp-client/json-schema-subset.js +181 -0
- package/lib/runtime/mcp-client/oauth.js +379 -0
- package/lib/runtime/mcp-client/tools.js +432 -0
- package/lib/runtime/mcp-client/transport.js +103 -0
- package/lib/runtime/mcp-config.js +196 -54
- package/lib/runtime/mcp-credentials.js +20 -0
- package/lib/runtime/mcp-mounts.js +237 -27
- package/lib/runtime/mcp-overrides.js +64 -6
- package/lib/runtime/mcp-redaction.js +92 -0
- package/lib/runtime/mcp-status.js +76 -32
- package/lib/runtime/reconciler.js +35 -1
- package/lib/runtime/regions.js +56 -0
- package/lib/types/application/catalog.d.ts +191 -14
- package/lib/types/application/details.d.ts +1 -6
- package/lib/types/application/queries.d.ts +28 -1
- package/lib/types/catalog/archive.d.ts +48 -0
- package/lib/types/catalog/git.d.ts +27 -3
- package/lib/types/catalog/legacy-root-migration.d.ts +14 -0
- package/lib/types/catalog/lsp-spec.d.ts +14 -0
- package/lib/types/catalog/manifests.d.ts +16 -1
- package/lib/types/catalog/paths.d.ts +23 -2
- package/lib/types/catalog/scan-pipeline.d.ts +76 -0
- package/lib/types/catalog/scan-resolvers.d.ts +81 -0
- package/lib/types/catalog/source-catalog.d.ts +5 -0
- package/lib/types/catalog/suite-scanner.d.ts +14 -7
- package/lib/types/catalog/surfaces.d.ts +3 -3
- package/lib/types/contracts/lsp-status.d.ts +53 -0
- package/lib/types/contracts/market.d.ts +55 -1
- package/lib/types/contracts/mcp-status.d.ts +14 -2
- package/lib/types/index.d.ts +24 -8
- package/lib/types/model/types.d.ts +52 -3
- package/lib/types/runtime/lsp-direct-config.d.ts +12 -0
- package/lib/types/runtime/lsp-mounts.d.ts +89 -0
- package/lib/types/runtime/lsp-status.d.ts +30 -0
- package/lib/types/runtime/mcp-backend.d.ts +49 -0
- package/lib/types/runtime/mcp-client/bridge.d.ts +37 -0
- package/lib/types/runtime/mcp-client/config.d.ts +123 -0
- package/lib/types/runtime/mcp-client/connection.d.ts +82 -0
- package/lib/types/runtime/mcp-client/host-seams.d.ts +89 -0
- package/lib/types/runtime/mcp-client/json-schema-subset.d.ts +58 -0
- package/lib/types/runtime/mcp-client/oauth.d.ts +121 -0
- package/lib/types/runtime/mcp-client/tools.d.ts +136 -0
- package/lib/types/runtime/mcp-client/transport.d.ts +36 -0
- package/lib/types/runtime/mcp-config.d.ts +29 -12
- package/lib/types/runtime/mcp-credentials.d.ts +9 -0
- package/lib/types/runtime/mcp-mounts.d.ts +56 -6
- package/lib/types/runtime/mcp-overrides.d.ts +23 -4
- package/lib/types/runtime/mcp-redaction.d.ts +7 -0
- package/lib/types/runtime/mcp-status.d.ts +4 -2
- package/lib/types/runtime/reconciler.d.ts +22 -4
- package/lib/types/runtime/regions.d.ts +37 -0
- package/package.json +15 -11
- package/schemas/1.0.0/mcp.schema.json +22 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool bridge: discovers MCP tools, registers them on the harness ToolRuntime
|
|
3
|
+
* under deterministic server-qualified public names, and handles re-sync when
|
|
4
|
+
* the server's tool list changes.
|
|
5
|
+
*
|
|
6
|
+
* Naming contract (mirrored from the harness `dsh-mcp-client` naming
|
|
7
|
+
* invariants): every MCP tool has the stable identity `(serverName, rawName)`;
|
|
8
|
+
* the model-facing public name is `mcp__<serverName>__<rawName>`, normalized
|
|
9
|
+
* to the DeepSeek function-name constraints. The raw name is only ever sent on
|
|
10
|
+
* the wire (`tools/call`); the public name is never parsed to recover it.
|
|
11
|
+
*
|
|
12
|
+
* The host contract below (`ToolDefinition`, `ToolExecution`,
|
|
13
|
+
* `ToolExecutionResult`) is a structural mirror of `@deepseek-ai/dsh-tools`
|
|
14
|
+
* rc.2's registration surface — the runtime accepts the same object shape the
|
|
15
|
+
* upstream bridge registers, so the model-facing behavior is identical.
|
|
16
|
+
*
|
|
17
|
+
* @module runtime/mcp-client/tools
|
|
18
|
+
*/
|
|
19
|
+
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
20
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
21
|
+
import type { JsonSchemaNode } from './json-schema-subset.js';
|
|
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
|
+
}
|
|
87
|
+
/** Resolved options relevant to tool bridging. */
|
|
88
|
+
export interface ToolBridgeOptions {
|
|
89
|
+
/** Whether a registry conflict is contained or rejects this synchronization. */
|
|
90
|
+
registrationFailure: 'contain' | 'throw';
|
|
91
|
+
serverName: string;
|
|
92
|
+
toolCallTimeoutMs: number;
|
|
93
|
+
}
|
|
94
|
+
/** State for one sync generation: the current set of disposers keyed by public name. */
|
|
95
|
+
export type ToolDisposers = Map<string, () => void>;
|
|
96
|
+
/**
|
|
97
|
+
* Derive the model-facing public name for one MCP tool.
|
|
98
|
+
*
|
|
99
|
+
* Deterministic pure function of `(serverName, rawName)`: the clean case is
|
|
100
|
+
* `mcp__<serverName>__<rawName>` verbatim. When character replacement or
|
|
101
|
+
* truncation to the DeepSeek function-name contract (64 chars,
|
|
102
|
+
* `[A-Za-z0-9_-]`) changes the name, a 12-hex-char SHA-256 hash of the
|
|
103
|
+
* identity is appended so distinct MCP identities never collapse into the
|
|
104
|
+
* same public name.
|
|
105
|
+
*
|
|
106
|
+
* @param serverName - Stable local namespace from bridge config.
|
|
107
|
+
* @param rawName - The MCP server's own tool name.
|
|
108
|
+
* @returns The globally unique, model-facing ToolRuntime name.
|
|
109
|
+
*/
|
|
110
|
+
export declare function publicToolName(serverName: string, rawName: string): string;
|
|
111
|
+
/**
|
|
112
|
+
* Sync the MCP server's tool list into the harness ToolRuntime.
|
|
113
|
+
*
|
|
114
|
+
* Two phases keep the swap safe:
|
|
115
|
+
*
|
|
116
|
+
* 1. Fetch: drain uncached `tools/list` pagination and build the full next
|
|
117
|
+
* generation of `ToolDefinition`s under public names. Any failure here
|
|
118
|
+
* (network error, duplicate raw name in the server's list) rejects and
|
|
119
|
+
* leaves the previous generation registered untouched.
|
|
120
|
+
* 2. Swap: dispose the previous generation, register the new one. A registry
|
|
121
|
+
* conflict here can only mean a foreign registration squats on this
|
|
122
|
+
* server's `mcp__<serverName>__` namespace — the partial generation is
|
|
123
|
+
* rolled back (zero tools from this server) and logged. Initial strict
|
|
124
|
+
* synchronization may propagate the conflict so its parent transaction
|
|
125
|
+
* rejects; ordinary clients and later re-syncs return an empty map.
|
|
126
|
+
*
|
|
127
|
+
* @param client - Connected MCP Client instance used to list and call tools.
|
|
128
|
+
* @param host - The bridge host providing the tool registry and logger.
|
|
129
|
+
* @param opts - Bridge options: server namespace and per-call timeout.
|
|
130
|
+
* @param previous - Disposer map from the prior sync generation; disposed
|
|
131
|
+
* during the swap phase (only after the fetch phase succeeded).
|
|
132
|
+
* @returns A map of registered public tool names to their unregister
|
|
133
|
+
* disposers — the exact set of live registrations owned by this server.
|
|
134
|
+
*/
|
|
135
|
+
export declare function syncTools(client: Client, host: ToolHost, opts: ToolBridgeOptions, previous: ToolDisposers): Promise<ToolDisposers>;
|
|
136
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport factory: creates the appropriate MCP transport based on the
|
|
3
|
+
* bridge's resolved config. Stdio spawns a child process (with credential
|
|
4
|
+
* scrubbing); Streamable HTTP connects to a URL; legacy SSE connects to a
|
|
5
|
+
* URL with the pre-Streamable-HTTP protocol pairing. Both HTTP transports
|
|
6
|
+
* carry OAuth 2.1 authorization by default, activated by the server's first
|
|
7
|
+
* `401` challenge.
|
|
8
|
+
*
|
|
9
|
+
* @module runtime/mcp-client/transport
|
|
10
|
+
*/
|
|
11
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
12
|
+
import { LoopbackOAuthClientProvider } from './oauth.js';
|
|
13
|
+
import type { Config } from './config.js';
|
|
14
|
+
/**
|
|
15
|
+
* Create an MCP transport from the resolved bridge config.
|
|
16
|
+
*
|
|
17
|
+
* With OAuth active (the default), the Streamable HTTP and SSE transports
|
|
18
|
+
* carry an {@link OAuthClientProvider}: the first `401` from the server
|
|
19
|
+
* starts discovery, dynamic registration, and a loopback browser
|
|
20
|
+
* authorization, and granted tokens persist through the credentials service
|
|
21
|
+
* for later processes. Transport generation identity stays with the caller —
|
|
22
|
+
* the provider binds whichever transport it hands out, so a reconnect builds
|
|
23
|
+
* a fresh generation on the same durable state.
|
|
24
|
+
*
|
|
25
|
+
* @param config - Resolved bridge config discriminated on `transport`.
|
|
26
|
+
* @param credentials - The composition's credential store, when mounted; OAuth
|
|
27
|
+
* state persists only when it is.
|
|
28
|
+
* @param log - Diagnostic sink for the human leg of the flow.
|
|
29
|
+
* @returns The transport plus its OAuth provider, when active — the
|
|
30
|
+
* supervisor queries the provider to hold the generation open while a
|
|
31
|
+
* browser leg is pending.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createTransport(config: Config, credentials?: unknown, log?: (message: string) => void): {
|
|
34
|
+
transport: Transport;
|
|
35
|
+
oauthProvider: LoopbackOAuthClientProvider | undefined;
|
|
36
|
+
};
|
|
@@ -1,30 +1,47 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Config } from './mcp-client/config.js';
|
|
2
2
|
import { type McpSuiteOverrides } from './mcp-overrides.js';
|
|
3
|
-
import type { Suite } from '../model/types.js';
|
|
3
|
+
import type { McpServer, Suite } from '../model/types.js';
|
|
4
4
|
export interface McpMountRequest {
|
|
5
5
|
suiteId: string;
|
|
6
6
|
serverKey: string;
|
|
7
|
-
config:
|
|
7
|
+
config: Config;
|
|
8
8
|
}
|
|
9
|
+
export type McpMountFailureCode = 'unsupported-transport' | 'missing-credential' | 'credential-error' | 'unmount-failed' | 'mount-failed'
|
|
10
|
+
/** The derived serverName's namespace is already mounted by another MCP client — informational, not a failure. */
|
|
11
|
+
| 'foreign-mount';
|
|
9
12
|
export interface McpMountFailure {
|
|
10
13
|
serverKey: string;
|
|
11
14
|
reason: string;
|
|
15
|
+
code?: McpMountFailureCode;
|
|
16
|
+
credentialRefs?: string[];
|
|
17
|
+
}
|
|
18
|
+
/** Optional DSH credential resolver used while building an in-memory mount. */
|
|
19
|
+
export interface McpCredentialResolver {
|
|
20
|
+
resolve(ref: string): Promise<{
|
|
21
|
+
value: string;
|
|
22
|
+
source?: string;
|
|
23
|
+
} | undefined>;
|
|
12
24
|
}
|
|
13
25
|
/**
|
|
14
|
-
* Build one mount request per supported mcp.json server
|
|
26
|
+
* Build one mount request per supported mcp.json server, resolving every
|
|
27
|
+
* `${NAME}` through the credential resolver before a child process or HTTP
|
|
28
|
+
* request is created. Missing references fail closed per server instead of
|
|
29
|
+
* becoming empty strings.
|
|
30
|
+
*
|
|
15
31
|
* @param overrides user-owned per-server overrides (url/headers/env/args
|
|
16
|
-
* replacement plus enable/disable); applied
|
|
17
|
-
*
|
|
18
|
-
* @returns mount requests plus per-server failures
|
|
19
|
-
*
|
|
20
|
-
* checked by the mount registry, not here).
|
|
32
|
+
* replacement plus enable/disable); applied before mount. Disabled servers
|
|
33
|
+
* are omitted entirely.
|
|
34
|
+
* @returns mount requests plus per-server failures. serverName collisions are
|
|
35
|
+
* reported by the mount registry, not here.
|
|
21
36
|
*/
|
|
22
|
-
export declare function toMcpMounts(suite: Suite, pluginDataRoot: string, overrides?: McpSuiteOverrides): {
|
|
37
|
+
export declare function toMcpMounts(suite: Suite, pluginDataRoot: string, overrides?: McpSuiteOverrides, resolver?: McpCredentialResolver): Promise<{
|
|
23
38
|
mounts: McpMountRequest[];
|
|
24
39
|
failures: McpMountFailure[];
|
|
25
|
-
}
|
|
40
|
+
}>;
|
|
41
|
+
/** Find external credential references used by one MCP server definition. */
|
|
42
|
+
export declare function credentialRefsInServer(server: McpServer): string[];
|
|
26
43
|
/**
|
|
27
|
-
* Derive a stable, unique-ish
|
|
44
|
+
* Derive a stable, unique-ish bridge serverName from the suite and
|
|
28
45
|
* server ids: `${suiteId}__${serverKey}` sanitized, truncated to 32 chars
|
|
29
46
|
* with a deterministic 12-hex suffix when the join exceeds the budget (the
|
|
30
47
|
* same deterministic-hash policy the MCP client uses for long tool names).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Resolve MCP `${NAME}` placeholders through the optional DSH credentials seam. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { McpCredentialResolver } from './mcp-config.js';
|
|
4
|
+
/**
|
|
5
|
+
* Build the resolver used at mount time. Standard DSH profiles provide
|
|
6
|
+
* `ctx.credentials`; minimal profiles retain the historical process-env
|
|
7
|
+
* fallback so MCP mounting remains backwards compatible.
|
|
8
|
+
*/
|
|
9
|
+
export declare function mcpCredentialResolver(ctx: Context): McpCredentialResolver;
|
|
@@ -1,37 +1,87 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Runtime MCP mounts: one live
|
|
2
|
+
* Runtime MCP mounts: one live self-built bridge child plugin per enabled
|
|
3
3
|
* suite's mcp.json server, mounted through `ctx.plugin`.
|
|
4
4
|
*
|
|
5
5
|
* Mounts reconcile against the enabled-suite set: reconcile() unmounts rows
|
|
6
6
|
* whose suite was disabled or removed and mounts rows that appeared. A
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* diagnostic list.
|
|
7
|
+
* duplicate derived serverName or a load failure is contained per server — a
|
|
8
|
+
* broken third-party suite must not take the host down — and reported through
|
|
9
|
+
* the manager's diagnostic list.
|
|
11
10
|
*/
|
|
12
11
|
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
-
import type {
|
|
12
|
+
import type { McpBackend } from './mcp-backend.js';
|
|
13
|
+
import { type McpSuiteOverrides } from './mcp-overrides.js';
|
|
14
|
+
import { type McpMountFailureCode } from './mcp-config.js';
|
|
14
15
|
import type { Suite } from '../model/types.js';
|
|
15
16
|
export interface McpMountDiagnostic {
|
|
16
17
|
suiteId: string;
|
|
17
18
|
serverKey: string;
|
|
18
19
|
reason: string;
|
|
20
|
+
code?: McpMountFailureCode;
|
|
21
|
+
credentialRefs?: string[];
|
|
19
22
|
}
|
|
20
23
|
export declare class McpMountRegistry {
|
|
21
24
|
private readonly ctx;
|
|
22
25
|
private readonly pluginDataRoot;
|
|
23
26
|
private readonly live;
|
|
24
27
|
private readonly names;
|
|
28
|
+
private readonly credentialRefs;
|
|
25
29
|
private overridesProvider;
|
|
30
|
+
/** Live model-facing tool names, for foreign-namespace detection before a mount. */
|
|
31
|
+
private toolNamesProvider;
|
|
32
|
+
/** The active MCP mount backend ('builtin' bridge or host client compat mode). */
|
|
33
|
+
private backendProvider;
|
|
26
34
|
/** Serialize mount and unmount passes so a disable cannot race an in-flight spawn. */
|
|
27
35
|
private reconcileQueue;
|
|
36
|
+
/** Pending retry timers keyed by mount key, so a teardown can cancel them. */
|
|
37
|
+
private readonly retries;
|
|
38
|
+
/** Attempt count per mount key; reset whenever a retry succeeds. */
|
|
39
|
+
private readonly attempts;
|
|
40
|
+
/**
|
|
41
|
+
* Mount keys flagged for an explicit rebuild (re-authorize): the next
|
|
42
|
+
* reconcile must tear down and remount them even when the resolved config
|
|
43
|
+
* fingerprint is unchanged — dropping a grant changes no config field.
|
|
44
|
+
*/
|
|
45
|
+
private readonly forcedRemounts;
|
|
46
|
+
/** Snapshot of the last reconciled suite set, replayed by retry passes. */
|
|
47
|
+
private lastEnabled;
|
|
28
48
|
constructor(ctx: Context, pluginDataRoot: string);
|
|
29
49
|
/** Install the per-suite overrides provider (suiteId -> overrides). */
|
|
30
50
|
setOverridesProvider(provider: () => Promise<Map<string, McpSuiteOverrides>>): void;
|
|
51
|
+
/**
|
|
52
|
+
* Install the live model-facing tool-name provider (the host registry
|
|
53
|
+
* snapshot) used to detect foreign `mcp__<serverName>__` namespaces before
|
|
54
|
+
* a mount is attempted.
|
|
55
|
+
*/
|
|
56
|
+
setToolNamesProvider(provider: () => string[]): void;
|
|
57
|
+
/** Install the backend provider deciding which client mounts each server. */
|
|
58
|
+
setBackendProvider(provider: () => Promise<McpBackend>): void;
|
|
59
|
+
/** Whether the last catalog snapshot uses one credential reference. */
|
|
60
|
+
usesCredential(ref: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Flag one mount for an explicit rebuild on the next reconcile: used by
|
|
63
|
+
* re-authorize, which deletes a grant without changing any resolved config
|
|
64
|
+
* field, so a fingerprint-only reconcile would keep the live bridge (and
|
|
65
|
+
* its in-memory tokens) untouched.
|
|
66
|
+
*/
|
|
67
|
+
forceRemount(suiteId: string, serverKey: string): void;
|
|
68
|
+
/** The mount key owning one derived serverName; undefined when not mounted here. */
|
|
69
|
+
serverOwner(serverName: string): {
|
|
70
|
+
suiteId: string;
|
|
71
|
+
serverKey: string;
|
|
72
|
+
} | undefined;
|
|
31
73
|
/** Queue one reconciliation behind any in-flight mount/unmount pass. */
|
|
32
74
|
reconcile(enabledSuites: Suite[]): Promise<McpMountDiagnostic[]>;
|
|
33
75
|
/** Mount/unmount MCP servers to match the enabled suites exactly. */
|
|
34
76
|
private reconcileNow;
|
|
77
|
+
/**
|
|
78
|
+
* Schedule a delayed re-attempt for a mount that still is not live.
|
|
79
|
+
*
|
|
80
|
+
* Mounts are retried because a remote endpoint or a credential may become
|
|
81
|
+
* available slightly after the suite is enabled. The schedule is bounded, and
|
|
82
|
+
* a server that keeps failing simply stops retrying until the next reconcile.
|
|
83
|
+
*/
|
|
84
|
+
private scheduleRetry;
|
|
35
85
|
/** Dispose every live mount after queued reconciliation passes settle. */
|
|
36
86
|
disposeAll(): Promise<void>;
|
|
37
87
|
/** Mount one precomputed request (source config merged with overrides). */
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { McpServerStreamableHttp, McpServerStdio } from '../model/types.js';
|
|
1
|
+
import type { McpServerSse, McpServerStreamableHttp, McpServerStdio } from '../model/types.js';
|
|
2
|
+
/** HTTP-shaped server sources (url + headers carriers) overrides can edit. */
|
|
3
|
+
type McpServerHttp = McpServerStreamableHttp | McpServerSse;
|
|
2
4
|
/** Per-server override record; absent fields pass through from the source. */
|
|
3
5
|
export type McpServerOverride = {
|
|
4
6
|
/** Disabled servers are not mounted at all (default enabled). */
|
|
@@ -11,6 +13,11 @@ export type McpServerOverride = {
|
|
|
11
13
|
env?: Record<string, string>;
|
|
12
14
|
/** Replaces the whole args list (stdio). */
|
|
13
15
|
args?: string[];
|
|
16
|
+
/** Replaces the OAuth block (streamable-http only); `enabled: false` disables a source-declared flow. */
|
|
17
|
+
auth?: {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
scope?: string;
|
|
20
|
+
};
|
|
14
21
|
};
|
|
15
22
|
/** Overrides for one suite, keyed by mcp.json server key. */
|
|
16
23
|
export type McpSuiteOverrides = Record<string, McpServerOverride>;
|
|
@@ -26,12 +33,24 @@ export declare function saveSuiteOverrides(dataRoot: string, suiteId: string, ov
|
|
|
26
33
|
export declare function sanitizeOverrides(raw: unknown): McpSuiteOverrides;
|
|
27
34
|
/** Validate a client-supplied patch for one server into an override value. */
|
|
28
35
|
export declare function sanitizeOverridePatch(patch: unknown): McpServerOverride | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Merge one client patch into the override already on disk.
|
|
38
|
+
*
|
|
39
|
+
* The UI redacts secret-shaped values before rendering them, so a patch built
|
|
40
|
+
* from that redacted view must not be written verbatim — that would silently
|
|
41
|
+
* drop keys the user did not intend to change. Existing stored values are
|
|
42
|
+
* therefore preserved and only the fields the patch actually carries replace
|
|
43
|
+
* them.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mergeOverridePatch(existing: McpServerOverride, patch: McpServerOverride): McpServerOverride;
|
|
29
46
|
/**
|
|
30
47
|
* Apply one server's overrides onto its source shape. Field replacement is
|
|
31
48
|
* whole-map (headers/env/args), never per-key merge, so removing a key in the
|
|
32
49
|
* UI actually removes it at mount time. Values pass through the ordinary
|
|
33
50
|
* mount-time placeholder pipeline afterwards, so override authors may
|
|
34
|
-
* reference secrets as `${NAME}` or `${NAME:-fallback}` (resolved
|
|
35
|
-
*
|
|
51
|
+
* reference secrets as `${NAME}` or `${NAME:-fallback}` (resolved through the
|
|
52
|
+
* Host credentials service or launch environment in memory) instead of
|
|
53
|
+
* persisting them here.
|
|
36
54
|
*/
|
|
37
|
-
export declare function applyOverride(server: McpServerStdio |
|
|
55
|
+
export declare function applyOverride(server: McpServerStdio | McpServerHttp, override: McpServerOverride | undefined): McpServerStdio | McpServerHttp;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Redact secret-shaped MCP configuration before it crosses a status/detail boundary. */
|
|
2
|
+
export declare function redactMcpConfig(value: unknown): unknown;
|
|
3
|
+
export declare function redactMcpOverrides(value: Record<string, Record<string, unknown>>): Record<string, Record<string, unknown>>;
|
|
4
|
+
/** Whether one map key is treated as secret-shaped. */
|
|
5
|
+
export declare function isSensitiveKey(key: string): boolean;
|
|
6
|
+
/** Redact credential-bearing query values from an endpoint URL. */
|
|
7
|
+
export declare function redactUrl(raw: string): string;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { type McpSuiteOverrides } from './mcp-overrides.js';
|
|
1
2
|
import type { McpStatusPayload } from '../contracts/mcp-status.js';
|
|
2
3
|
import { inspectToolRegistry, type McpToolSnapshot } from './tool-registry-observer.js';
|
|
3
|
-
import type { McpSuiteOverrides } from './mcp-overrides.js';
|
|
4
4
|
import type { Suite } from '../model/types.js';
|
|
5
5
|
export type { McpStatusEntry, McpStatusPayload, McpStatusKind, McpStatusState } from '../contracts/mcp-status.js';
|
|
6
6
|
export { inspectToolRegistry };
|
|
7
7
|
export type { McpToolSnapshot } from './tool-registry-observer.js';
|
|
8
|
-
interface McpDiagnostic {
|
|
8
|
+
export interface McpDiagnostic {
|
|
9
9
|
suiteId: string;
|
|
10
10
|
serverKey: string;
|
|
11
11
|
reason: string;
|
|
12
|
+
code?: 'unsupported-transport' | 'missing-credential' | 'credential-error' | 'unmount-failed' | 'mount-failed' | 'foreign-mount';
|
|
13
|
+
credentialRefs?: string[];
|
|
12
14
|
}
|
|
13
15
|
/** Build status rows from discovered plugin MCP definitions and observed tool names. */
|
|
14
16
|
export declare function buildMcpStatus(suites: Suite[], diagnostics: McpDiagnostic[], observed: readonly McpToolSnapshot[], overrides?: Map<string, McpSuiteOverrides>): McpStatusPayload;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runtime reconciliation for enabled suites.
|
|
3
3
|
*
|
|
4
|
-
* One catalog snapshot is fanned out to the existing MCP, command, and
|
|
5
|
-
* mount adapters. The adapters remain separate because their host surfaces
|
|
6
|
-
* failure semantics differ; this module owns ordering, containment, and
|
|
4
|
+
* One catalog snapshot is fanned out to the existing MCP, command, hook, and
|
|
5
|
+
* LSP mount adapters. The adapters remain separate because their host surfaces
|
|
6
|
+
* and failure semantics differ; this module owns ordering, containment, and
|
|
7
7
|
* disposal only.
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
10
|
import { type CommandMountDiagnostic } from './commands-mounts.js';
|
|
11
11
|
import { type HooksMountDiagnostic } from './hooks-mounts.js';
|
|
12
|
+
import { LspMountRegistry, type LspMountDiagnostic } from './lsp-mounts.js';
|
|
12
13
|
import { type McpMountDiagnostic } from './mcp-mounts.js';
|
|
13
14
|
import type { Suite } from '../model/types.js';
|
|
14
15
|
import { type HostTranslate } from './host-locale.js';
|
|
@@ -17,8 +18,9 @@ export interface RuntimeDiagnostics {
|
|
|
17
18
|
mcp: McpMountDiagnostic[];
|
|
18
19
|
commands: CommandMountDiagnostic[];
|
|
19
20
|
hooks: HooksMountDiagnostic[];
|
|
21
|
+
lsp: LspMountDiagnostic[];
|
|
20
22
|
errors: Array<{
|
|
21
|
-
surface: 'mcp' | 'commands' | 'hooks';
|
|
23
|
+
surface: 'mcp' | 'commands' | 'hooks' | 'lsp';
|
|
22
24
|
reason: string;
|
|
23
25
|
}>;
|
|
24
26
|
}
|
|
@@ -26,9 +28,25 @@ export declare class RuntimeReconciler {
|
|
|
26
28
|
private readonly mcp;
|
|
27
29
|
private readonly commands;
|
|
28
30
|
private readonly hooks;
|
|
31
|
+
private readonly lspRegistry;
|
|
29
32
|
constructor(ctx: Context, dataRoot: string, t?: HostTranslate);
|
|
33
|
+
/** The LSP mount registry, consumed by the LSP status surface. */
|
|
34
|
+
get lsp(): LspMountRegistry;
|
|
30
35
|
/** Install the per-suite MCP overrides provider used at mount time. */
|
|
31
36
|
setMcpOverridesProvider(provider: () => Promise<Map<string, import('./mcp-overrides.js').McpSuiteOverrides>>): void;
|
|
37
|
+
/** Install the live tool-name provider used for foreign-namespace mount guards. */
|
|
38
|
+
setMcpToolNamesProvider(provider: () => string[]): void;
|
|
39
|
+
/** Install the backend provider deciding which MCP client mounts each server. */
|
|
40
|
+
setMcpBackendProvider(provider: () => Promise<import('./mcp-backend.js').McpBackend>): void;
|
|
41
|
+
/** Whether the current MCP snapshot uses one credential reference. */
|
|
42
|
+
usesCredential(ref: string): boolean;
|
|
43
|
+
/** Flag one MCP mount for an explicit rebuild on the next reconcile. */
|
|
44
|
+
forceMcpRemount(suiteId: string, serverKey: string): void;
|
|
45
|
+
/** The mount key owning one derived serverName; undefined when not mounted here. */
|
|
46
|
+
mcpServerOwner(serverName: string): {
|
|
47
|
+
suiteId: string;
|
|
48
|
+
serverKey: string;
|
|
49
|
+
} | undefined;
|
|
32
50
|
/** Reconcile all runtime surfaces against one enabled-suite snapshot. */
|
|
33
51
|
reconcile(enabledSuites: readonly Suite[]): Promise<RuntimeDiagnostics>;
|
|
34
52
|
/** Dispose all live mounts and registrations. */
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Download regions for GitHub acquisition, modeled on dshmarket's regions.js:
|
|
3
|
+
* one setting answers "where are you" instead of a row of mirror toggles.
|
|
4
|
+
*
|
|
5
|
+
* The China route proxies github.com clones through a public prefix proxy —
|
|
6
|
+
* `git clone https://gh-proxy.com/https://github.com/owner/repo` — rather
|
|
7
|
+
* than rewriting the hostname, which is what lets one prefix cover clone,
|
|
8
|
+
* fetch, and release downloads without a mapping table per service. Public
|
|
9
|
+
* proxies come and go, so the route carries an environment escape hatch
|
|
10
|
+
* (`DSH_MARKET_GITHUB_PROXY`), and the region itself is a setting the user
|
|
11
|
+
* can flip at any time; sources already cloned keep their proxied origin and
|
|
12
|
+
* follow it on refresh.
|
|
13
|
+
*
|
|
14
|
+
* @module runtime/regions
|
|
15
|
+
*/
|
|
16
|
+
/** The persisted setting: `auto` follows the interface language. */
|
|
17
|
+
export type DownloadRegionSetting = 'auto' | 'global' | 'china';
|
|
18
|
+
/** The route downloads actually take. */
|
|
19
|
+
export type EffectiveRegion = 'global' | 'china';
|
|
20
|
+
/** Narrow an untrusted stored value to a setting; anything else reads as auto. */
|
|
21
|
+
export declare function narrowDownloadRegion(value: unknown): DownloadRegionSetting;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the effective region: an explicit choice wins; `auto` follows the
|
|
24
|
+
* interface language — a Chinese-language user defaults to the China route.
|
|
25
|
+
* @param setting - the persisted `downloadRegion` value.
|
|
26
|
+
* @param localePreference - the Host `locale.preference` value, when known.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveRegion(setting: DownloadRegionSetting, localePreference: string | undefined): EffectiveRegion;
|
|
29
|
+
/**
|
|
30
|
+
* Route a clone URL for the region. Only github.com-family https URLs are
|
|
31
|
+
* proxied — local paths and third-party hosts go through unchanged, as does
|
|
32
|
+
* everything in the global region.
|
|
33
|
+
* @param region - the effective region for this download.
|
|
34
|
+
* @param url - the clone URL.
|
|
35
|
+
* @param env - environment, carrying the `DSH_MARKET_GITHUB_PROXY` escape hatch.
|
|
36
|
+
*/
|
|
37
|
+
export declare function githubCloneUrl(region: EffectiveRegion, url: string, env?: NodeJS.ProcessEnv): string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agent-plugins-market",
|
|
3
3
|
"description": "The standard way to install Claude Code / Codex / Cursor / Kimi plugin marketplace suites in DeepSeek Harness (DSH) — zero conversion, zero file copying. Skills, MCP servers, hooks and slash commands are injected into your dsh sessions at runtime.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -76,9 +76,9 @@
|
|
|
76
76
|
],
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
79
|
-
"@deepseek-ai/dsh-hooks-claude-code": "^0.1.
|
|
80
|
-
"@deepseek-ai/dsh-mcp-client": "^0.1.
|
|
81
|
-
"@deepseek-ai/dsh-skill": "^0.1.
|
|
79
|
+
"@deepseek-ai/dsh-hooks-claude-code": "^0.1.2-rc.1",
|
|
80
|
+
"@deepseek-ai/dsh-mcp-client": "^0.1.2-rc.1",
|
|
81
|
+
"@deepseek-ai/dsh-skill": "^0.1.2-rc.1"
|
|
82
82
|
},
|
|
83
83
|
"peerDependenciesMeta": {
|
|
84
84
|
"@deepseek-ai/dsh-mcp-client": {
|
|
@@ -89,16 +89,20 @@
|
|
|
89
89
|
}
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
93
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
92
94
|
"ajv": "^8.17.1",
|
|
93
|
-
"
|
|
95
|
+
"fflate": "^0.8.3",
|
|
96
|
+
"yaml": "^2.7.0",
|
|
97
|
+
"zod": "^4.4.3"
|
|
94
98
|
},
|
|
95
99
|
"devDependencies": {
|
|
96
100
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
97
|
-
"@deepseek-ai/dsh-client-ui-primitives": "
|
|
98
|
-
"@deepseek-ai/dsh-hooks-claude-code": "0.1.
|
|
99
|
-
"@deepseek-ai/dsh-llm": "
|
|
100
|
-
"@deepseek-ai/dsh-mcp-client": "
|
|
101
|
-
"@deepseek-ai/dsh-skill": "
|
|
101
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
|
|
102
|
+
"@deepseek-ai/dsh-hooks-claude-code": "0.1.2-rc.1",
|
|
103
|
+
"@deepseek-ai/dsh-llm": "0.1.2-rc.1",
|
|
104
|
+
"@deepseek-ai/dsh-mcp-client": "0.1.2-rc.1",
|
|
105
|
+
"@deepseek-ai/dsh-skill": "0.1.2-rc.1",
|
|
102
106
|
"@eslint/js": "^9.39.5",
|
|
103
107
|
"@tsdown/css": "^0.22.14",
|
|
104
108
|
"@types/node": "^22.15.0",
|
|
@@ -106,7 +110,7 @@
|
|
|
106
110
|
"@types/react-dom": "^19.2.4",
|
|
107
111
|
"@vitest/coverage-v8": "^4.1.11",
|
|
108
112
|
"dependency-cruiser": "^18.2.0",
|
|
109
|
-
"eslint": "^9.
|
|
113
|
+
"eslint": "^10.9.1",
|
|
110
114
|
"jsdom": "^30.0.1",
|
|
111
115
|
"prettier": "^3.9.6",
|
|
112
116
|
"react": "^18.3.1",
|
|
@@ -85,6 +85,9 @@
|
|
|
85
85
|
},
|
|
86
86
|
"headers": {
|
|
87
87
|
"$ref": "#/$defs/headers"
|
|
88
|
+
},
|
|
89
|
+
"auth": {
|
|
90
|
+
"$ref": "#/$defs/oauth"
|
|
88
91
|
}
|
|
89
92
|
},
|
|
90
93
|
"required": ["type", "url"],
|
|
@@ -104,11 +107,30 @@
|
|
|
104
107
|
},
|
|
105
108
|
"headers": {
|
|
106
109
|
"$ref": "#/$defs/headers"
|
|
110
|
+
},
|
|
111
|
+
"auth": {
|
|
112
|
+
"$ref": "#/$defs/oauth"
|
|
107
113
|
}
|
|
108
114
|
},
|
|
109
115
|
"required": ["type", "url"],
|
|
110
116
|
"additionalProperties": false
|
|
111
117
|
},
|
|
118
|
+
"oauth": {
|
|
119
|
+
"title": "OAuth 2.1 authorization",
|
|
120
|
+
"type": "object",
|
|
121
|
+
"description": "OAuth 2.1 authorization for servers that answer 401 with a challenge. The flow activates by default on the server's first 401; set enabled to false to opt out.",
|
|
122
|
+
"properties": {
|
|
123
|
+
"enabled": {
|
|
124
|
+
"type": "boolean",
|
|
125
|
+
"description": "Enable the OAuth flow (default true; set false to opt out explicitly)."
|
|
126
|
+
},
|
|
127
|
+
"scope": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"description": "Space-separated scope string requested during authorization."
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"additionalProperties": false
|
|
133
|
+
},
|
|
112
134
|
"headers": {
|
|
113
135
|
"title": "HTTP headers",
|
|
114
136
|
"type": "object",
|