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.
Files changed (86) hide show
  1. package/README.md +29 -3
  2. package/README.zh.md +29 -3
  3. package/client/client.js +1388 -204
  4. package/client/client.js.map +1 -1
  5. package/lib/application/catalog.js +537 -71
  6. package/lib/application/details.js +39 -12
  7. package/lib/catalog/archive.js +341 -0
  8. package/lib/catalog/git.js +85 -12
  9. package/lib/catalog/legacy-root-migration.js +51 -0
  10. package/lib/catalog/lsp-spec.js +87 -0
  11. package/lib/catalog/manifests.js +84 -32
  12. package/lib/catalog/paths.js +65 -7
  13. package/lib/catalog/scan-pipeline.js +50 -0
  14. package/lib/catalog/scan-resolvers.js +402 -0
  15. package/lib/catalog/source-catalog.js +22 -8
  16. package/lib/catalog/suite-scanner.js +5 -176
  17. package/lib/catalog/surfaces.js +80 -30
  18. package/lib/contracts/lsp-status.js +2 -0
  19. package/lib/contracts/market.js +8 -1
  20. package/lib/index.js +118 -13
  21. package/lib/model/state.js +14 -1
  22. package/lib/model/types.js +21 -2
  23. package/lib/routes.js +88 -6
  24. package/lib/runtime/commands-mounts.js +6 -2
  25. package/lib/runtime/hooks-mounts.js +9 -6
  26. package/lib/runtime/lsp-direct-config.js +44 -0
  27. package/lib/runtime/lsp-mounts.js +236 -0
  28. package/lib/runtime/lsp-status.js +96 -0
  29. package/lib/runtime/mcp-backend.js +60 -0
  30. package/lib/runtime/mcp-client/bridge.js +126 -0
  31. package/lib/runtime/mcp-client/config.js +42 -0
  32. package/lib/runtime/mcp-client/connection.js +380 -0
  33. package/lib/runtime/mcp-client/host-seams.js +88 -0
  34. package/lib/runtime/mcp-client/json-schema-subset.js +181 -0
  35. package/lib/runtime/mcp-client/oauth.js +379 -0
  36. package/lib/runtime/mcp-client/tools.js +432 -0
  37. package/lib/runtime/mcp-client/transport.js +103 -0
  38. package/lib/runtime/mcp-config.js +196 -54
  39. package/lib/runtime/mcp-credentials.js +20 -0
  40. package/lib/runtime/mcp-mounts.js +237 -27
  41. package/lib/runtime/mcp-overrides.js +64 -6
  42. package/lib/runtime/mcp-redaction.js +92 -0
  43. package/lib/runtime/mcp-status.js +76 -32
  44. package/lib/runtime/reconciler.js +35 -1
  45. package/lib/runtime/regions.js +56 -0
  46. package/lib/types/application/catalog.d.ts +191 -14
  47. package/lib/types/application/details.d.ts +1 -6
  48. package/lib/types/application/queries.d.ts +28 -1
  49. package/lib/types/catalog/archive.d.ts +48 -0
  50. package/lib/types/catalog/git.d.ts +27 -3
  51. package/lib/types/catalog/legacy-root-migration.d.ts +14 -0
  52. package/lib/types/catalog/lsp-spec.d.ts +14 -0
  53. package/lib/types/catalog/manifests.d.ts +16 -1
  54. package/lib/types/catalog/paths.d.ts +23 -2
  55. package/lib/types/catalog/scan-pipeline.d.ts +76 -0
  56. package/lib/types/catalog/scan-resolvers.d.ts +81 -0
  57. package/lib/types/catalog/source-catalog.d.ts +5 -0
  58. package/lib/types/catalog/suite-scanner.d.ts +14 -7
  59. package/lib/types/catalog/surfaces.d.ts +3 -3
  60. package/lib/types/contracts/lsp-status.d.ts +53 -0
  61. package/lib/types/contracts/market.d.ts +55 -1
  62. package/lib/types/contracts/mcp-status.d.ts +14 -2
  63. package/lib/types/index.d.ts +24 -8
  64. package/lib/types/model/types.d.ts +52 -3
  65. package/lib/types/runtime/lsp-direct-config.d.ts +12 -0
  66. package/lib/types/runtime/lsp-mounts.d.ts +89 -0
  67. package/lib/types/runtime/lsp-status.d.ts +30 -0
  68. package/lib/types/runtime/mcp-backend.d.ts +49 -0
  69. package/lib/types/runtime/mcp-client/bridge.d.ts +37 -0
  70. package/lib/types/runtime/mcp-client/config.d.ts +123 -0
  71. package/lib/types/runtime/mcp-client/connection.d.ts +82 -0
  72. package/lib/types/runtime/mcp-client/host-seams.d.ts +89 -0
  73. package/lib/types/runtime/mcp-client/json-schema-subset.d.ts +58 -0
  74. package/lib/types/runtime/mcp-client/oauth.d.ts +121 -0
  75. package/lib/types/runtime/mcp-client/tools.d.ts +136 -0
  76. package/lib/types/runtime/mcp-client/transport.d.ts +36 -0
  77. package/lib/types/runtime/mcp-config.d.ts +29 -12
  78. package/lib/types/runtime/mcp-credentials.d.ts +9 -0
  79. package/lib/types/runtime/mcp-mounts.d.ts +56 -6
  80. package/lib/types/runtime/mcp-overrides.d.ts +23 -4
  81. package/lib/types/runtime/mcp-redaction.d.ts +7 -0
  82. package/lib/types/runtime/mcp-status.d.ts +4 -2
  83. package/lib/types/runtime/reconciler.d.ts +22 -4
  84. package/lib/types/runtime/regions.d.ts +37 -0
  85. package/package.json +15 -11
  86. 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 { StdioConfig, StreamableHttpConfig } from '@deepseek-ai/dsh-mcp-client';
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: StdioConfig | StreamableHttpConfig;
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 after source expansion, before
17
- * mount. Disabled servers are omitted from the result entirely.
18
- * @returns mount requests plus per-server failures (unsupported transport,
19
- * invalid server key, or derived serverName collision candidates are
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 `dsh-mcp-client` serverName from the suite and
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 `dsh-mcp-client` child plugin per enabled
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
- * missing `@deepseek-ai/dsh-mcp-client` install, a duplicate derived
8
- * serverName, or a load failure is contained per server — a broken third-party
9
- * suite must not take the host down — and reported through the manager's
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 { McpSuiteOverrides } from './mcp-overrides.js';
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 from the
35
- * process environment in memory) instead of persisting them here.
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 | McpServerStreamableHttp, override: McpServerOverride | undefined): McpServerStdio | McpServerStreamableHttp;
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 hook
5
- * mount adapters. The adapters remain separate because their host surfaces and
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.5.2",
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.0-rc.8",
80
- "@deepseek-ai/dsh-mcp-client": "^0.1.0-rc.8",
81
- "@deepseek-ai/dsh-skill": "^0.1.0-rc.8"
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
- "yaml": "^2.7.0"
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": "^0.1.0-rc.8",
98
- "@deepseek-ai/dsh-hooks-claude-code": "0.1.0-rc.8",
99
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
100
- "@deepseek-ai/dsh-mcp-client": "^0.1.0-rc.8",
101
- "@deepseek-ai/dsh-skill": "^0.1.0-rc.8",
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.39.5",
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",