@sunshaobei/dsh-mcp-manager 0.4.0 → 0.6.1

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.
@@ -20,8 +20,8 @@ import type { ServerResponse } from "node:http";
20
20
  import { queryParam } from "./routes-helpers.js";
21
21
  export { ROUTES };
22
22
  export { queryParam };
23
- /** 组装 /api/dsh-mcp/* 路由。cwd 参数仅用于兼容旧调用(不再被路由使用)。 */
24
- export declare function makeRoutes(manager: RoutesManager, _cwd?: string): WebRoute[];
23
+ /** Assemble routes in contract order; cwd selects a request-owned workspace without rebinding other requests. */
24
+ export declare function makeRoutes(manager: RoutesManager, _cwd?: string, scoped?: boolean): WebRoute[];
25
25
  /** 配置变更 SSE 帧:客户端收到后重新 GET /api/dsh-mcp/config 就地更新浮窗位置。 */
26
26
  export declare function uiConfigChangedFrame(): string;
27
27
  /**
@@ -31,7 +31,7 @@ export declare function scoreTerms(terms: string[], haystack: string): {
31
31
  * 单 root 实现(searchCatalogMulti 循环调用;兼容既有测试)。
32
32
  * truncated:任一服务器存在被 limit 裁掉的结果即 true(B10 截断事实,
33
33
  * 供调用方精确标注「是否因 limit 裁掉」——恰好命中 limit 不误报)。 */
34
- export declare function searchCatalog(units: Map<string, ProjectUnit>, root: string, query: string, limit: number): {
34
+ export declare function searchCatalog(units: Map<string, ProjectUnit>, root: string, query: string, limit: number, serverEnabled?: (root: string, name: string) => boolean): {
35
35
  results: SearchHit[];
36
36
  unavailable: Array<{
37
37
  server: string;
@@ -43,7 +43,7 @@ export declare function searchCatalog(units: Map<string, ProjectUnit>, root: str
43
43
  * 与 searchCatalog 同语义,仅数据源扩展为多个 root。
44
44
  * 单 root 直接委托 searchCatalog(保持原顺序,不引入跨单元排序变化);
45
45
  * 多 root 合并后统一排序并按全局 limit 截断(truncated=合并后曾有超限结果)。 */
46
- export declare function searchCatalogMulti(units: Map<string, ProjectUnit>, roots: readonly string[], query: string, limit: number): {
46
+ export declare function searchCatalogMulti(units: Map<string, ProjectUnit>, roots: readonly string[], query: string, limit: number, serverEnabled?: (root: string, name: string) => boolean): {
47
47
  results: SearchHit[];
48
48
  unavailable: Array<{
49
49
  server: string;
@@ -65,7 +65,7 @@ export declare function isToolDisabledForEntry(disabledTools: DisabledToolsMap |
65
65
  * 命中条目在工具行标注禁用(供模型感知)。
66
66
  * @throws 全名 root 不属于当前 roots → 路由一致性错误(与 ws_mcp_call 口径一致)。
67
67
  */
68
- export declare function listCatalog(units: Map<string, ProjectUnit>, roots: readonly string[], serverFilter: string | undefined, toolLimit: number, emptyHint: string, disabledTools?: DisabledToolsMap): ListCatalogResult;
68
+ export declare function listCatalog(units: Map<string, ProjectUnit>, roots: readonly string[], serverFilter: string | undefined, toolLimit: number, emptyHint: string, disabledTools?: DisabledToolsMap, serverEnabled?: (root: string, name: string) => boolean): ListCatalogResult;
69
69
  /** 每服务器工具条数上限:非有限数或非正回落缺省;正数向下取整(历史口径)。 */
70
70
  export declare function safeToolLimit(toolLimit: number): number;
71
71
  /** 该 root 是否参与盘点:显式过滤集命中即参与,无过滤集则全参与。 */
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * 纯解析在 sources.ts(本模块消费它);这里回答「生效表如何无损落进运行面」:
5
5
  * - 每个生效名按其来源 scope 走 add/update(同名已存在即 update,等效覆盖)
6
- * - 被遮蔽(同名落选)的名字:若出现在对应 scope store 里则移除并断开
7
- * (互斥 = 全局与项目两个运行面都只保留生效来源那份)
6
+ * - 全局来源只更新共享全局配置,项目来源只更新指定项目的配置;
7
+ * 同名互斥按工作区投影,不删除其他工作区仍需使用的全局配置。
8
8
  * - 项目 store 需要 session(projectStoreOrThrow);无 session 时项目侧
9
9
  * 跳过并在摘要里说明,不抛错(面板会在有会话后重新同步)
10
10
  *
@@ -17,6 +17,7 @@
17
17
  import type { SseHub } from "../../vendor/shared/sse-hub.js";
18
18
  import type { ClientUiConfig } from "../../shared/interface.js";
19
19
  import type { ServerConfig } from "../config/interface.js";
20
+ import type { ParsedSource, SourceId } from "../config/sources.js";
20
21
  import type { McpStore } from "../store/interface.js";
21
22
  export type { McpManager } from "./orchestrator/interface.js";
22
23
  export type { ProjectUnit, ConnectionEntry } from "./runtime/interface.js";
@@ -28,6 +29,12 @@ export interface SupervisorLite {
28
29
  }
29
30
  /** routes 使用的 McpManager 最小面(避免 index↔routes 循环 import)。 */
30
31
  export interface RoutesManager {
32
+ sourceSnapshot?(): {
33
+ sources: ParsedSource[];
34
+ prefs: ReadonlyMap<string, SourceId>;
35
+ } | undefined;
36
+ sourcesChanged?(): Promise<void>;
37
+ forWorkspace?(cwd?: string): Promise<RoutesManager>;
31
38
  setSession(cwd: string | undefined): Promise<void>;
32
39
  refreshFromDisk(): Promise<void>;
33
40
  uiConfig(): ClientUiConfig;
@@ -57,6 +64,8 @@ export interface RoutesManager {
57
64
  store: McpStore;
58
65
  /** 当前会话项目根(tool-disable 路由一致性校验用)。 */
59
66
  projectRoot?: string;
67
+ /** 孤儿禁用痕清扫:生效表之外的 userDisabled 记录移除(互斥同步收口用)。 */
68
+ sweepOrphanUserDisabled?(validGlobal: ReadonlySet<string>, validProject: ReadonlySet<string>): Promise<void>;
60
69
  /** 设置命名空间写入 sink(apply 注入;config 写路由落盘用)。 */
61
70
  uiUpdate?: (patch: Record<string, unknown>) => Promise<unknown>;
62
71
  /**
@@ -16,6 +16,7 @@ import type { Context, LoggerService } from "@deepseek-ai/cordis";
16
16
  import type { ServerConfig } from "../../config/interface.js";
17
17
  import type { ClientUiConfig, ServerState } from "../../../shared/interface.js";
18
18
  import type { CatalogCache, CatalogServer } from "../../catalog/interface.js";
19
+ import type { RoutesManager } from "../interface.js";
19
20
  import type { McpStore } from "../../store/interface.js";
20
21
  import type { McpStatsCollector } from "../../stats/interface.js";
21
22
  import type { McpMiddleware } from "../runtime/interface.js";
@@ -88,7 +89,7 @@ export declare function reloadStoreQuietly(store: McpStore, onFail: (error: unkn
88
89
  */
89
90
  export declare function touchUnitInBackground(middleware: McpMiddleware, target: string, onFail: (error: unknown) => void): void;
90
91
  /** 池内不再需要的连接(配置里已无 / 已禁用)释放;返回是否释放过。 */
91
- export declare function releaseUndesiredConnections(middleware: McpMiddleware | undefined, desired: DesiredServers): boolean;
92
+ export declare function releaseUndesiredConnections(middleware: McpMiddleware | undefined, desired: DesiredServers, projectRoot?: string): boolean;
92
93
  /**
93
94
  * 该 (root, 裸名) 是否还不在池里(= 连接集合变化)。中间层缺失时**不算**变化:
94
95
  * 实例缺失时什么都触达不了,记变化只会让状态广播空转。
@@ -279,6 +280,9 @@ export declare class McpManager {
279
280
  initMiddleware(): Promise<McpMiddleware>;
280
281
  /** userDisabled 映射(root → Set<server>),中间层单元创建时合并。 */
281
282
  disabledByRoot: Map<string, Set<string>>;
283
+ /** Workspace-local switches for shared global transports (stored with the existing root state). */
284
+ globalEnabledFor(root: string, name: string): boolean;
285
+ private setGlobalEnabled;
282
286
  /** 工具级禁用(root → server → Set<tool>);root=@global 跨工作空间共享。 */
283
287
  disabledTools: DisabledToolsMap;
284
288
  /**
@@ -294,6 +298,7 @@ export declare class McpManager {
294
298
  setToolDisabled(root: string, server: string, tool: string, disabled: boolean): Promise<void>;
295
299
  /** 读取/复用某项目根的 store(工作区缓存命中直接返回,不重复读盘)。 */
296
300
  projectStoreFor(root: string | undefined): Promise<McpStore | undefined>;
301
+ private loadProjectStore;
297
302
  /**
298
303
  * 会话目录数据源:全局服务器 + 该 cwd 所属项目的项目级服务器(**配置层面**,
299
304
  * 与 host 当前 setSession 状态解耦)。此前直接用 manager.supervisors(实时
@@ -327,12 +332,18 @@ export declare class McpManager {
327
332
  server: ServerConfig;
328
333
  scope: string;
329
334
  }>): Promise<CatalogCache>;
330
- /**
331
- * 会话切换:只切 currentRoot + fire-and-forget 惰性启动(不 await 任何连接)。
332
- * 变更点驱动(#111/#228):POST /api/dsh-mcp/session 永不挂起——连接/发现
333
- * 由中间层惰性驱动(per-root in-flight 去重),此处零连接副作用。
334
- * 单池后项目级连接恒由中间层池按 root 常驻,切走不断开。
335
- */
335
+ /** Cached workspace resolution and preparation never wait for a remote MCP server. */
336
+ private workspaceRoots;
337
+ private selectionVersion;
338
+ private touchedRoots;
339
+ private projectStoreLoads;
340
+ sourceSnapshot?: RoutesManager["sourceSnapshot"];
341
+ sourcesChanged?: RoutesManager["sourcesChanged"];
342
+ prepareWorkspace?: (cwd?: string) => Promise<void>;
343
+ private workspaceRoot;
344
+ /** A request owns its workspace view; asynchronous requests cannot change another request's root. */
345
+ forWorkspace(cwd?: string): Promise<McpManager>;
346
+ /** Selection changes pointers only. Source sync/connection checks belong to background preparation. */
336
347
  setSession(cwd: string | undefined): Promise<void>;
337
348
  /**
338
349
  * 切回前台可用性恢复:对本次请求工作空间连接的非强制恢复(对齐 SSE forceReconnect 的
@@ -378,6 +389,7 @@ export declare class McpManager {
378
389
  * @returns {boolean} 连接集合是否变化(释放了在册连接,或触达了尚未在册的服务器)
379
390
  */
380
391
  reconcileServers(): boolean;
392
+ private transportStore;
381
393
  startAll(): Promise<void>;
382
394
  /**
383
395
  * 启动一个服务器的连接(单池后唯一的连接路径)。
@@ -415,9 +427,8 @@ export declare class McpManager {
415
427
  */
416
428
  private ensureMiddlewareServer;
417
429
  /**
418
- * 拆除中间层池中该 server 的连接(update/remove 配置变更后强制重建;
419
- * 不写 userDisabled——与 disconnect 的禁用语义区分)。此前 update/remove 仅
420
- * 处理项目单元,@global 池连接与目录残留导致「已删服务器仍可调用」。
430
+ * 只拆除目标 (root, server) 的连接与目录,不影响其他工作区的同名服务器。
431
+ * 不写 userDisabled——配置变更与用户主动断开的语义不同。
421
432
  */
422
433
  private dropMiddlewareConnection;
423
434
  /**
@@ -470,6 +481,13 @@ export declare class McpManager {
470
481
  * 拆除只发起不等结算(裁定 V):官方 dispose 会等在途首连,挂死的服务器能把它拖到 SDK 的 60s 超时。
471
482
  */
472
483
  disconnect(name: string, scope?: string): Promise<void>;
484
+ /**
485
+ * 孤儿禁用痕清扫:userDisabled 里挂着但对应 scope 的生效表已无此名的记录
486
+ * 一律移除。早期互斥清理经 disconnect 落下的污染痕(同名切走后残留)会让
487
+ * 之后切回/重启时的 start 静默短路(userDisabled 命中不连),表现为「经常
488
+ * 连接不全」;生效表已是唯一事实,孤儿痕无语义。
489
+ */
490
+ sweepOrphanUserDisabled(validGlobal: ReadonlySet<string>, validProject: ReadonlySet<string>): Promise<void>;
473
491
  reconnect(name: string, scope?: string): Promise<void>;
474
492
  /** 面板数据:配置 + 实时状态 + 工具列表 + 项目信息。 */
475
493
  summary(): Record<string, unknown>;
@@ -104,6 +104,7 @@ export interface ManagerLite {
104
104
  }
105
105
  /** 中间层宿主最小面(McpManager 实现;McpMiddleware 构造注入)。 */
106
106
  export interface MiddlewareHost {
107
+ serverEnabledFor?(root: string, target: string, name: string): boolean;
107
108
  ctx: Pick<Context, "tools">;
108
109
  logger: LoggerService;
109
110
  /** 按 root 读取项目级服务器配置(惰性;root 无标记 → undefined)。 */
@@ -92,6 +92,8 @@ export declare class McpMiddleware {
92
92
  */
93
93
  forwarding: Set<ToolExecutionToken>;
94
94
  constructor(host: MiddlewareHost);
95
+ /** Authorization belongs to the caller workspace, independently of the shared connection root. */
96
+ serverEnabledFor(root: string, target: string, name: string): boolean;
95
97
  /** 读取/创建 root 的单元(root 无项目标记 → undefined)。 */
96
98
  projectUnitFor(root: string | undefined): Promise<ProjectUnit | undefined>;
97
99
  /** 连接一个服务器(in-flight 去重;失败后台重连)。
@@ -109,7 +111,7 @@ export declare class McpMiddleware {
109
111
  * 全部吞掉,且旧 attempt 收敛时命中让位/disposed 守卫静默返回、无人补连 → 这段时间内
110
112
  * 该服务器无法重连。拆除时必须同步废弃标记;旧 attempt 稍后收敛由 connectInternal
111
113
  * 的让位/disposed 守卫兜底,无副作用。 */
112
- abandonInFlight(serverName: string): void;
114
+ abandonInFlight(serverName: string, root?: string): void;
113
115
  private connectInternal;
114
116
  /**
115
117
  * 装载面:注册面视图 / 工具元信息 / 脱敏都是本类方法的转发,形状与调用点无关——
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunshaobei/dsh-mcp-manager",
3
- "version": "0.4.0",
3
+ "version": "0.6.1",
4
4
  "description": "Manage MCP servers (stdio / streamable-http) for dsh web; tools are reached only through four middleware tools. DSH MCP 管理器:Web 侧边栏「MCP」入口,面板按状态分级展示 MCP 服务器(运行中/连接中/已配置/失败),支持快速接入(stdio / streamable-http 表单)与粘贴 mcpServers JSON 导入。已连接服务器的工具一律经中间层工具(ws_mcp_search / ws_mcp_detail / ws_mcp_call / ws_mcp_list)访问:宿主注册名 mcp__<id>__<tool> 是内部标识(id 按 (工作空间, 服务器名) 分配、不可由服务器名推导),不对模型可见,禁止直呼。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -152,11 +152,10 @@
152
152
  "license": "MIT",
153
153
  "repository": {
154
154
  "type": "git",
155
- "url": "ssh://git@git.lbk.world:8022/eren.sun/dsh-mcp-manager.git",
156
- "directory": "packages/dsh-mcp-manager"
155
+ "url": "git+https://github.com/sunshaobei/dsh-mcp-manager.git"
157
156
  },
158
157
  "bugs": {
159
- "url": "ssh://git@git.lbk.world:8022/eren.sun/dsh-mcp-manager.git"
158
+ "url": "https://github.com/sunshaobei/dsh-mcp-manager/issues"
160
159
  },
161
160
  "keywords": [
162
161
  "dsh",