dsh-mcp-bar 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-mcp-bar contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,140 @@
1
- # Temporary Holding Version
1
+ # MCP Bar
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ DeepSeek Harness 的 MCP 管理插件。它在侧边栏提供一个完整的管理面板:新建、启停、改配置、删条目、测连接、看工具,都不需要再改 `cordis.patch.yml` 或翻日志。对话页不注入任何常驻 UI。
4
+
5
+ MCP Bar 是独立插件,不依赖 `dsh-mcp-connector`。两者同时管理同名 Server 会重复注册工具,装了这个就可以卸掉那个。
6
+
7
+ ## 能力
8
+
9
+ - **服务器列表**:合并展示 MCP Bar 自己管理的 Server 和 profile 中已有的原生 `mcp-client` 条目,带状态圆点、传输方式、地址 / 命令、工具数。支持搜索、启停、编辑、删除。
10
+ - **新建**:表单和 JSON 两种方式。表单覆盖名称、类型(stdio / Streamable HTTP)、超时、协议、命令与参数、URL 与请求头、环境变量、工作目录。JSON 兼容 `{"mcpServers": {...}}`、裸 map、单条对象和数组,以及 Claude / Cursor 风格的 `command + args`、`url`、`disabled`、`toolCallTimeoutMs` 字段。
11
+ - **连接测试**:走 `initialize` → `notifications/initialized` → `tools/list`,支持 stdio(换行帧 / Content-Length 帧)与 Streamable HTTP(含 SSE 响应、分页、会话清理)。失败时区分协议、鉴权、HTTP、超时、网络与进程启动错误。
12
+ - **工具查看**:列出一个 Server 暴露的工具、描述和输入 schema。
13
+ - **持久化**:记录保存在 DSH 的 storage domain `mcp_bar`。启动时根据记录重建动态 `mcp-client` 条目,重启后自动恢复。具体文件见[配置落在哪些文件](#配置落在哪些文件)。
14
+ - **原生条目接管**:profile 里已有的 `mcp-client` 条目可以直接接管,之后能在面板里编辑和启停;删除接管记录会恢复原条目。配置中含非字面量表达式时拒绝接管,避免把宿主代码写进存储。
15
+
16
+ ## 安装
17
+
18
+ 要求 DeepSeek Harness 0.2.x 与 Node.js 20+。
19
+
20
+ ```bash
21
+ # 方式一:从 npm 安装(发布后)
22
+ dsh plugin --profile <profile> add dsh-mcp-bar
23
+
24
+ # 方式二:从本地目录安装(clone 后无需执行 npm install:
25
+ # 运行期用到的 zod 会回退到宿主 @deepseek-ai/dsh-storage-domain 自带的版本)
26
+ dsh plugin --profile <profile> add /path/to/dsh-mcp-bar
27
+
28
+ # 或者直接编辑 profile 的 package.json,把 dsh-mcp-bar 加进 bundles
29
+ ```
30
+
31
+ 插件声明了 `dsh.bundle.patch`,安装后插件自带的 `cordis.patch.yml` 会作为 bundle patch 参与 profile 组合。装完重启 DSH,侧边栏会多一个「MCP 服务器」入口。
32
+
33
+ ## 使用
34
+
35
+ 从侧边栏「MCP 服务器」打开面板:
36
+
37
+ 1. **新建** → 选「表单」或「JSON」。stdio 填命令与参数,HTTP 填 URL 与请求头;保存前可以先点「测试连接」确认能拿到工具。
38
+ 2. 列表里可以「停用 / 启用」。「停用」会把对应的动态条目标记为 disabled,工具从当前会话消失,但配置保留。
39
+ 3. 编辑保存后会自动重建条目并重新连接;如果目标暂时连不上,配置仍然保存,状态显示为异常,稍后可以重试。
40
+ 4. profile 中的原生 Server 会以「profile 配置」分组出现,可以一键接管到 MCP Bar 管理。接管前它们只提供「接管 / 测试 / 详情」,因为插件不改写 `cordis.patch.yml`;接管成为 MCP Bar 记录后,启停、编辑、删除才可用。
41
+
42
+ ## 配置落在哪些文件
43
+
44
+ Server 配置本身只落一个文件;运行状态和动态 loader 条目不写盘。
45
+
46
+ | 内容 | 位置 |
47
+ |---|---|
48
+ | MCP Server 记录 | `~/.dsh/storages/mcp_bar.json`(设置了 `DSH_HOME` 时为 `$DSH_HOME/storages/mcp_bar.json`) |
49
+ | 动态 `mcp-bar-<key>` 条目 | 不落盘。插件启动时按存储记录重建 |
50
+ | profile 中的原生 `mcp-client` 条目 | profile 的 `cordis.patch.yml`,插件不修改;接管与停用只影响运行时 |
51
+ | 插件安装状态 | profile 的 `package.json`、`pnpm-lock.yaml` 与 `node_modules/` |
52
+
53
+ `mcp_bar.json` 是 DSH storage-json 的 `single` 单元文件,结构如下:
54
+
55
+ ```json
56
+ {
57
+ "unit": { "name": "mcp_bar", "version": 1 },
58
+ "global": null,
59
+ "tables": {
60
+ "servers": {
61
+ "s60d4b11350b5": {
62
+ "key": "s60d4b11350b5",
63
+ "serverName": "e2e_echo",
64
+ "transport": "stdio",
65
+ "command": "node",
66
+ "args": ["server.mjs"],
67
+ "env": {},
68
+ "headers": {},
69
+ "timeoutMs": 60000,
70
+ "enabled": true,
71
+ "nativeEntryId": null,
72
+ "createdAt": 1792939997451,
73
+ "updatedAt": 1792939997451
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ 几点约定:
81
+
82
+ - 文件在第一次保存 Server 时创建;每次新增、编辑、启停或删除都会原子重写整份单元文件。
83
+ - `env` 与 `headers` 会以明文保存。运行期错误和连接状态只存在内存里,不进入这个文件。
84
+ - 动态条目只存在于运行时。插件启动时会先停用被接管的原生条目,再从记录重建;因此不要把这些条目手写进 `cordis.patch.yml`。
85
+ - 接管原生条目时,`nativeEntryId` 记录来源;原来的 YAML 不会被改写,插件只在每次启动时于运行时停用它。删除接管记录会恢复原条目。
86
+ - 安装插件会修改 `~/.dsh/profiles/<profile>/package.json`(增加依赖并加入 `dsh.profile.bundles`),并更新该 profile 的 `pnpm-lock.yaml` 与 `node_modules/`。插件自己的 `cordis.patch.yml` 作为 bundle patch 参与组合;要改 `entryPrefix` 或 `probeTimeoutMs`,在 profile 的 `cordis.patch.yml` 中覆盖。
87
+
88
+ ## 数据与安全
89
+
90
+ - 配置只包含用户填写的内容(含 env / headers 凭据),不包含运行状态;运行期错误只存在内存里。
91
+ - 浏览器半区与宿主半区通过同源 `POST /mcp-bar/api` 通信。方法名走白名单,请求需要同时满足:回环 Host(`localhost` / `127.0.0.1` / `::1` 等)、非 `Sec-Fetch-Site: cross-site`、`Origin` 与 `Host` 同源。请求体上限 1 MiB。
92
+ - stdio 探测会以当前用户权限启动配置中的命令。只添加你信任的 Server。
93
+
94
+ ## 开发
95
+
96
+ ```bash
97
+ npm install
98
+ npm run check # 语法检查 + 单元测试
99
+ npm run dev:ui # 静态 UI harness(React + mock API),不需要启动 DSH
100
+ ```
101
+
102
+ 目录结构:
103
+
104
+ ```
105
+ lib/index.js 宿主半区:插件入口、状态聚合、API 实现
106
+ lib/record.js 表单 / JSON / 原生配置 → 存储记录的规范化
107
+ lib/args.js 命令参数解析与格式化
108
+ lib/json-import.js JSON 导入
109
+ lib/provision.js 动态 mcp-client 条目的增删改与原生条目接管
110
+ lib/probe.js stdio / Streamable HTTP 连接探测
111
+ lib/store.js storage domain(mcp_bar / servers)
112
+ lib/web.js 同源 HTTP API 与信任 fence
113
+ lib/client.js 浏览器半区:面板、表单
114
+ scripts/ UI harness 与验收脚本
115
+ test/ node:test 单元测试
116
+ ```
117
+
118
+ 浏览器半区没有构建步骤:`lib/client.js` 是包在 `window.__ModuleLoader__.load(...)` 里的 CommonJS 工厂,直接用 `require("react")` 和 `React.createElement`。改完刷新页面即可。
119
+
120
+ ## 发布
121
+
122
+ 维护者发布到 npm(包名 `dsh-mcp-bar`):
123
+
124
+ 1. 按 semver 更新 `package.json` 的 `version` 并提交。
125
+ 2. `npm login` 登录维护者账号;账号开了 2FA 时准备一次性验证码。
126
+ 3. `npm publish`。`prepublishOnly` 会先自动跑 `npm run check`;`files` 白名单保证只发布 `lib/`、`cordis.patch.yml`、README 与 LICENSE。
127
+ 4. `npm view dsh-mcp-bar version` 确认已生效。
128
+
129
+ 发错的版本 72 小时内可 `npm unpublish dsh-mcp-bar@<version>` 撤回,之后只能 `npm deprecate`。
130
+
131
+ ## 已知限制
132
+
133
+ - 只支持 `stdio` 与 Streamable HTTP。旧版的 `sse` 配置在导入时会提示并按 Streamable HTTP 保存。
134
+ - 原生条目接管只处理字面量配置;含 Cordis 表达式的条目会提示手动新建。
135
+ - 动态 loader 条目不会写进 profile,而是插件启动时按存储记录重建。因此不要把 MCP Bar 创建的条目再手写进 `cordis.patch.yml`,否则会造成重复。
136
+ - 协议版本展示的是探测结果;实际工具调用协议由 `dsh-mcp-client` 决定。
137
+
138
+ ## License
139
+
140
+ MIT
@@ -0,0 +1,10 @@
1
+ # MCP Bar 插件(DSH Bundle patch)
2
+ # 作为 bundle 被合入 profile 时,自动插入本插件行。
3
+ - insert:
4
+ - id: mcp-bar
5
+ name: 'dsh-mcp-bar'
6
+ config:
7
+ # 本插件托管的 mcp-client 条目 id 前缀,避免与 profile 中原生条目冲突。
8
+ entryPrefix: 'mcp-bar'
9
+ # 连接探测(initialize + tools/list)超时。
10
+ probeTimeoutMs: 20000
package/lib/args.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * 参数字符串 ↔ argv 数组。
3
+ *
4
+ * 表单里的「参数(空格分隔)」看起来像 shell,但这里**不执行 shell**:
5
+ * 只做引号与反斜杠转义解析,避免用户输入被解释成命令。支持:
6
+ * --flag "two words" 'single' a\ b
7
+ */
8
+ export function parseArgs(text) {
9
+ if (text === undefined || text === null) return [];
10
+ const source = String(text);
11
+ const args = [];
12
+ let current = '';
13
+ let quote = null;
14
+ let escaped = false;
15
+ let started = false;
16
+
17
+ for (const ch of source) {
18
+ if (escaped) {
19
+ current += ch;
20
+ escaped = false;
21
+ started = true;
22
+ continue;
23
+ }
24
+ if (ch === '\\' && quote !== "'") {
25
+ escaped = true;
26
+ started = true;
27
+ continue;
28
+ }
29
+ if (quote !== null) {
30
+ if (ch === quote) quote = null;
31
+ else current += ch;
32
+ started = true;
33
+ continue;
34
+ }
35
+ if (ch === '"' || ch === "'") {
36
+ quote = ch;
37
+ started = true;
38
+ continue;
39
+ }
40
+ if (/\s/.test(ch)) {
41
+ if (started) {
42
+ args.push(current);
43
+ current = '';
44
+ started = false;
45
+ }
46
+ continue;
47
+ }
48
+ current += ch;
49
+ started = true;
50
+ }
51
+
52
+ if (escaped) current += '\\';
53
+ if (quote !== null) throw new Error('参数里的引号没有闭合');
54
+ if (started) args.push(current);
55
+ return args;
56
+ }
57
+
58
+ /** argv → 可读的单行参数字符串(需要时加双引号)。 */
59
+ export function formatArgs(args) {
60
+ if (!Array.isArray(args) || args.length === 0) return '';
61
+ return args.map((arg) => {
62
+ const value = String(arg);
63
+ if (value === '') return '""';
64
+ if (!/[\s"'\\]/.test(value)) return value;
65
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
66
+ }).join(' ');
67
+ }