dsh-mcp-connector 0.1.0 → 0.2.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/CHANGELOG.md +49 -0
- package/README.md +16 -3
- package/catalog/catalog.json +132 -34
- package/docs/DESKTOP-E2E.md +28 -0
- package/docs/MARKET-REGISTRATION.md +107 -0
- package/lib/catalog.js +35 -4
- package/lib/client.js +41 -7
- package/lib/index.js +168 -21
- package/lib/mcp-validation.js +168 -0
- package/lib/migration.js +131 -0
- package/lib/oauth.js +8 -1
- package/lib/probe.js +163 -0
- package/lib/prompts.js +33 -0
- package/lib/schema.js +40 -0
- package/lib/stores.js +10 -0
- package/lib/tools.js +31 -2
- package/lib/web.js +15 -1
- package/package.json +15 -3
- package/registry/README.md +13 -0
- package/registry/catalog.json +229 -0
- package/registry/connectors/example.sample.json +44 -0
- package/registry/connectors/pkulaw-legal.json +44 -0
- package/registry/connectors/wind-stock-data.json +40 -0
- package/registry/schema/connector.schema.json +115 -0
- package/scripts/build-registry.mjs +13 -0
- package/scripts/probe-connector.mjs +69 -0
- package/scripts/ui-harness.mjs +80 -0
- package/ui/assets/pkulaw-logo.png +0 -0
- package/ui/assets/wind-logo.png +0 -0
- package/ui/index.html +527 -77
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/);首个公开版本从 `0.1.0` 开始。
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.2.0] - 2026-08-21
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- 公共市场新增第 5 张“北大法宝·法律检索”第三方卡片,以一个 Bearer Token 批量配置官网公开的 9 个 MCP Server。
|
|
12
|
+
- 公共市场新增第 6 张“Wind·股票数据”第三方卡片,按万得 AIFin Market 官方配置接入股票数据 MCP Server。
|
|
13
|
+
- 图形化“添加连接”:手动配置、`mcpServers` JSON、连接器描述 URL,并提供错误修复提示。
|
|
14
|
+
- JSON 导入提供多行缩进示例、粘贴/手动格式化,并明确区分本机连接与市场卡片。
|
|
15
|
+
- Bearer/API Key 市场连接器支持一次填写凭据后批量连接卡片下所有 Server。
|
|
16
|
+
- 参数化 Prompt:发送前收集企业、人员或主题等变量,不再硬编码示例主体。
|
|
17
|
+
- Tier 1 registry 种子:JSON Schema、确定性构建、无凭据 MCP/OAuth 探针和每周健康巡检。
|
|
18
|
+
- 两个旧企查查 OAuth 插件的授权预览和幂等复制迁移;源插件与源凭据始终保留。
|
|
19
|
+
- 本地 UI mock 测试壳和 Desktop 发版回归清单。
|
|
20
|
+
- 市场注册指南,覆盖 ConnectorDescriptor、公共 registry 与 OAuth 一键授权服务端要求。
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- 市场 Bearer/API Key 连接器改为先验证所有 MCP Server 的连通性与凭据,通过后才进入“已安装”。
|
|
25
|
+
- 精简“导入 JSON”页面,只保留格式、本机凭据说明与导入操作;市场同级卡片按目录声明顺序陈列。
|
|
26
|
+
- “添加连接”默认打开并优先展示“导入 JSON”,同时缩短编辑区并固定底部操作栏,常规桌面窗口首屏即可看到导入按钮。
|
|
27
|
+
- 内置精选 Prompt 直接展示可读示例值并一键带入新会话;仅缺少必填默认值时才打开参数补全弹框。
|
|
28
|
+
- 未连接卡片也可查看详情;registry 提供 `toolsSnapshot` 时可在授权前预览工具。
|
|
29
|
+
- 工具每批渲染 50 条、连接器每批 60 张,并保留搜索能力。
|
|
30
|
+
- 增加基础中英文、系统深浅主题、焦点样式、Escape 关闭和弹框焦点循环。
|
|
31
|
+
- URL 安装描述持久化,重启后仍在市场显示。
|
|
32
|
+
|
|
33
|
+
### Security
|
|
34
|
+
|
|
35
|
+
- 远程 JSON 响应限制 2 MiB,Web API 请求体限制 1 MiB。
|
|
36
|
+
- 在 Schema 丢弃未知字段前扫描 token、API Key、secret 等夹带凭据。
|
|
37
|
+
- OAuth 元数据和重定向终点执行 HTTPS/loopback 白名单;Web UI 增加 CSP、`nosniff`、`no-referrer`。
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- 错误 Key、超时、DNS 或 TLS/网络失败不再被当作“已连接”,失败时保留弹框且不持久化凭据。
|
|
42
|
+
- 历史凭据失效且工具全部加载失败时,详情页提供“重新配置凭据”入口并暂停发送 Prompt。
|
|
43
|
+
- 未配置远程 registry 时,“刷新”不再暴露 `catalogUrl 为空`等内部实现提示。
|
|
44
|
+
- Bearer/API Key 市场卡片配置成功后,市场操作按钮正确切换为“已连接”。
|
|
45
|
+
- Bearer/API Key 连接器的详情页按钮直接打开凭据录入弹框,不再显示面向开发者的 `mcp_connector_configure` 错误。
|
|
46
|
+
- 工具详情加载时正确携带已保存的 Bearer Token/API Key,避免连接成功后仍返回 401。
|
|
47
|
+
- 北大法宝卡片改用用户确认的北大法宝印章 Logo,不再显示通用天平 Emoji。
|
|
48
|
+
- 参数补全弹框和发送状态提示现在始终位于连接器详情弹框之上。
|
|
49
|
+
- 正确解析 `WWW-Authenticate: Bearer resource_metadata="…"` 首个参数。
|
|
50
|
+
- 修复鉴权表单 `hidden` 被网格布局覆盖、无鉴权模式误显示 API Key 字段。
|
|
51
|
+
|
|
5
52
|
## [0.1.0] - 2026-08-20
|
|
6
53
|
|
|
7
54
|
### Added
|
|
@@ -22,4 +69,6 @@
|
|
|
22
69
|
- 外部 URL 与导入 Header 执行安全校验。
|
|
23
70
|
- iframe 消息校验同源和消息来源。
|
|
24
71
|
|
|
72
|
+
[Unreleased]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.0...HEAD
|
|
73
|
+
[0.2.0]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.1.0...v0.2.0
|
|
25
74
|
[0.1.0]: https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -10,13 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
- 左侧主导航入口:目标位置为“新会话”下方、“工作区/会话列表”上方;若 DSH DOM 结构不兼容,自动回退到底部公开插槽。
|
|
12
12
|
- 图形化市场:市场/已安装、搜索、连接状态与刷新。
|
|
13
|
+
- 图形化添加:手动 URL/鉴权、`mcpServers` JSON、连接器描述 URL 三种入口,失败时保留表单并给出修复建议。
|
|
13
14
|
- 连接器详情:精选 Prompt 优先展示,点击可带入 DSH 新会话;工具按 Server 分组,支持描述、搜索和独立滚动。
|
|
15
|
+
- Prompt 模板:使用 `{{company}}` 等变量,发送前填写真实查询主体。
|
|
14
16
|
- 三种接入:OAuth 2.0 PKCE、自定义 URL/鉴权、导入 `mcpServers` JSON;也支持从连接器描述 URL 安装。
|
|
17
|
+
- 市场 Bearer/API Key 连接器先执行 MCP initialize 连通性与凭据校验,全部 Server 通过后才持久化凭据并进入“已安装”。
|
|
15
18
|
- 生命周期管理:连接持久化、重启恢复、启停、断开、OAuth 刷新与撤销。
|
|
16
19
|
- 目录运营:内置目录、远程 registry、本地覆盖,支持 `published` 上下架与 `featured` 精选。
|
|
20
|
+
- Registry 工具链:Schema/唯一性/密钥审计、MCP/OAuth 无凭据探针、每周健康巡检。
|
|
21
|
+
- 平滑迁移:显式扫描并复制两个旧企查查 OAuth 插件授权;不删除原插件或原凭据。
|
|
17
22
|
- 对话工具:`mcp_connector_catalog`、`connect`、`configure`、`import_json`、`install_from_url`、`status`、`set_enabled`、`disconnect`、`refresh_catalog`、`publish`、`tools_list`。
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
当前市场前 4 张为企查查连接器,第 5 张为“北大法宝·法律检索”,第 6 张为“Wind·股票数据”;插件架构不限定厂商。
|
|
20
25
|
|
|
21
26
|
## 安装
|
|
22
27
|
|
|
@@ -62,20 +67,28 @@ Bundle 默认配置位于 `cordis.patch.yml`:
|
|
|
62
67
|
|
|
63
68
|
```bash
|
|
64
69
|
npm run check
|
|
70
|
+
npm run registry:build
|
|
71
|
+
npm run registry:validate
|
|
72
|
+
npm run dev:ui
|
|
65
73
|
```
|
|
66
74
|
|
|
67
|
-
|
|
75
|
+
`check` 执行语法检查、自动测试和 npm 发布包白名单校验;`dev:ui` 启动不含真实凭据的本地 mock 市场。CI 使用 `--legacy-peer-deps` 安装显式测试依赖,DSH 运行期 peer 仍由 Host 提供。`v*` Tag 会触发 GitHub Actions;Tag 必须与 `package.json` 版本一致。仓库配置 `NPM_TOKEN` 后自动发布 npm,否则只创建 GitHub Release。
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
当前公开版本为 [`dsh-mcp-connector@0.2.0`](https://www.npmjs.com/package/dsh-mcp-connector),对应 [GitHub Release v0.2.0](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.0)。
|
|
70
78
|
|
|
71
79
|
版本能力与变更记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
80
|
+
Desktop 发版回归见 [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md)。
|
|
81
|
+
市场卡片、公共 registry 与 OAuth 一键授权要求见 [docs/MARKET-REGISTRATION.md](docs/MARKET-REGISTRATION.md)。
|
|
72
82
|
|
|
73
83
|
## 安全与限制
|
|
74
84
|
|
|
75
85
|
- 凭证只持久化在 DSH storage domain,不进入目录、Git 仓库或对话历史。
|
|
86
|
+
- 市场 Key/Token 校验失败时不写入 storage domain;鉴权、超时、DNS、TLS/网络错误会分类提示。
|
|
76
87
|
- 外部 URL 仅允许 HTTPS,HTTP 仅允许回环地址;导入配置会校验 URL 与 Header。
|
|
88
|
+
- 远程目录/描述响应限制 2 MiB,Web API 请求限制 1 MiB;原始 JSON 在归一化前扫描凭据字段。
|
|
77
89
|
- 当前以 streamable-http 为主并兼容 SSE;stdio 配置会被明确跳过。
|
|
78
90
|
- 顶部入口通过 DSH 稳定 `data-slot` 定位并使用 React Portal;DSH 若移除该标记,入口会回退到底部,不影响连接器功能。
|
|
91
|
+
- 旧授权迁移必须显式确认,只复制不删除;确认新连接可用后再手动停用旧插件。
|
|
79
92
|
|
|
80
93
|
## License
|
|
81
94
|
|
package/catalog/catalog.json
CHANGED
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"published": true,
|
|
14
14
|
"featured": true,
|
|
15
15
|
"homepage": "https://open.qcc.com",
|
|
16
|
+
"promptVariables": [
|
|
17
|
+
{ "name": "company", "label": "企业名称", "default": "小米科技有限责任公司", "placeholder": "例如:小米科技有限责任公司", "required": true },
|
|
18
|
+
{ "name": "person", "label": "人员姓名", "default": "雷军", "placeholder": "例如:雷军", "required": true }
|
|
19
|
+
],
|
|
16
20
|
"auth": {
|
|
17
21
|
"mode": "oauth2-pkce",
|
|
18
22
|
"issuer": "https://agent.qcc.com",
|
|
@@ -30,32 +34,32 @@
|
|
|
30
34
|
{ "serverKey": "executive", "url": "https://agent.qcc.com/mcp/executive/stream", "serverName": "qcc-executive", "transport": "streamable-http", "headers": {} }
|
|
31
35
|
],
|
|
32
36
|
"prompts": [
|
|
33
|
-
{ "title": "企业最新工商变更", "text": "查询
|
|
34
|
-
{ "title": "股东结构", "text": "查询
|
|
35
|
-
{ "title": "实际控制人", "text": "查询
|
|
36
|
-
{ "title": "主要人员(高管团队)", "text": "查询
|
|
37
|
-
{ "title": "对外投资", "text": "查询
|
|
38
|
-
{ "title": "受益所有人识别", "text": "查询
|
|
39
|
-
{ "title": "企业风险全景扫描", "text": "对
|
|
40
|
-
{ "title": "失信信息", "text": "查询
|
|
41
|
-
{ "title": "被执行人信息", "text": "查询
|
|
42
|
-
{ "title": "经营异常", "text": "查询
|
|
43
|
-
{ "title": "商标信息", "text": "查询
|
|
44
|
-
{ "title": "专利信息", "text": "查询
|
|
45
|
-
{ "title": "软件著作权", "text": "查询
|
|
46
|
-
{ "title": "作品著作权", "text": "查询
|
|
47
|
-
{ "title": "招投标信息", "text": "查询
|
|
48
|
-
{ "title": "资质证书", "text": "查询
|
|
49
|
-
{ "title": "行政许可", "text": "查询
|
|
50
|
-
{ "title": "新闻舆情", "text": "查询
|
|
51
|
-
{ "title": "历史股东", "text": "查询
|
|
52
|
-
{ "title": "历史法定代表人", "text": "查询
|
|
53
|
-
{ "title": "历史失信", "text": "查询
|
|
54
|
-
{ "title": "历史行政处罚", "text": "查询
|
|
55
|
-
{ "title": "董监高个人风险扫描", "text": "对
|
|
56
|
-
{ "title": "担任法定代表人", "text": "查询
|
|
57
|
-
{ "title": "在外任职", "text": "查询
|
|
58
|
-
{ "title": "全部关联企业", "text": "查询
|
|
37
|
+
{ "title": "企业最新工商变更", "text": "查询 {{company}} 的最新工商变更记录", "server": "company" },
|
|
38
|
+
{ "title": "股东结构", "text": "查询 {{company}} 的股东构成与出资信息", "server": "company" },
|
|
39
|
+
{ "title": "实际控制人", "text": "查询 {{company}} 的实际控制人信息", "server": "company" },
|
|
40
|
+
{ "title": "主要人员(高管团队)", "text": "查询 {{company}} 的主要人员信息", "server": "company" },
|
|
41
|
+
{ "title": "对外投资", "text": "查询 {{company}} 的对外投资信息", "server": "company" },
|
|
42
|
+
{ "title": "受益所有人识别", "text": "查询 {{company}} 的受益所有人识别信息", "server": "company" },
|
|
43
|
+
{ "title": "企业风险全景扫描", "text": "对 {{company}} 做一次企业风险全景扫描", "server": "risk" },
|
|
44
|
+
{ "title": "失信信息", "text": "查询 {{company}} 的失信信息", "server": "risk" },
|
|
45
|
+
{ "title": "被执行人信息", "text": "查询 {{company}} 的被执行人信息", "server": "risk" },
|
|
46
|
+
{ "title": "经营异常", "text": "查询 {{company}} 的经营异常信息", "server": "risk" },
|
|
47
|
+
{ "title": "商标信息", "text": "查询 {{company}} 的商标信息", "server": "ipr" },
|
|
48
|
+
{ "title": "专利信息", "text": "查询 {{company}} 的专利信息", "server": "ipr" },
|
|
49
|
+
{ "title": "软件著作权", "text": "查询 {{company}} 的软件著作权信息", "server": "ipr" },
|
|
50
|
+
{ "title": "作品著作权", "text": "查询 {{company}} 的作品著作权信息", "server": "ipr" },
|
|
51
|
+
{ "title": "招投标信息", "text": "查询 {{company}} 的招投标信息", "server": "operation" },
|
|
52
|
+
{ "title": "资质证书", "text": "查询 {{company}} 的资质证书信息", "server": "operation" },
|
|
53
|
+
{ "title": "行政许可", "text": "查询 {{company}} 的行政许可信息", "server": "operation" },
|
|
54
|
+
{ "title": "新闻舆情", "text": "查询 {{company}} 的新闻舆情信息", "server": "operation" },
|
|
55
|
+
{ "title": "历史股东", "text": "查询 {{company}} 的历史股东变动记录", "server": "history" },
|
|
56
|
+
{ "title": "历史法定代表人", "text": "查询 {{company}} 的历史法定代表人记录", "server": "history" },
|
|
57
|
+
{ "title": "历史失信", "text": "查询 {{company}} 的历史失信记录(已移出名单)", "server": "history" },
|
|
58
|
+
{ "title": "历史行政处罚", "text": "查询 {{company}} 的历史行政处罚记录", "server": "history" },
|
|
59
|
+
{ "title": "董监高个人风险扫描", "text": "对 {{company}} 的董监高 {{person}} 做一次个人风险全景扫描", "server": "executive" },
|
|
60
|
+
{ "title": "担任法定代表人", "text": "查询 {{company}} 的董监高 {{person}} 担任法定代表人的情况", "server": "executive" },
|
|
61
|
+
{ "title": "在外任职", "text": "查询 {{company}} 的董监高 {{person}} 的在外任职信息", "server": "executive" },
|
|
62
|
+
{ "title": "全部关联企业", "text": "查询 {{company}} 的董监高 {{person}} 的全部关联企业", "server": "executive" }
|
|
59
63
|
]
|
|
60
64
|
},
|
|
61
65
|
{
|
|
@@ -70,6 +74,9 @@
|
|
|
70
74
|
"published": true,
|
|
71
75
|
"featured": false,
|
|
72
76
|
"homepage": "https://open.qcc.com",
|
|
77
|
+
"promptVariables": [
|
|
78
|
+
{ "name": "topic", "label": "法律主题", "default": "劳动合同解除", "placeholder": "例如:劳动合同解除", "required": true }
|
|
79
|
+
],
|
|
73
80
|
"auth": {
|
|
74
81
|
"mode": "oauth2-pkce",
|
|
75
82
|
"issuer": "https://agent.qcc.com",
|
|
@@ -83,10 +90,10 @@
|
|
|
83
90
|
{ "serverKey": "case", "url": "https://agent.qcc.com/mcp/case/stream", "serverName": "qcc-case", "transport": "streamable-http", "headers": {} }
|
|
84
91
|
],
|
|
85
92
|
"prompts": [
|
|
86
|
-
{ "title": "法规检索", "text": "
|
|
87
|
-
{ "title": "法条检索", "text": "
|
|
88
|
-
{ "title": "司法案例检索", "text": "
|
|
89
|
-
{ "title": "权威案例检索", "text": "
|
|
93
|
+
{ "title": "法规检索", "text": "检索与「{{topic}}」相关的法律法规", "server": "regulation" },
|
|
94
|
+
{ "title": "法条检索", "text": "检索与「{{topic}}」相关的法条原文", "server": "regulation" },
|
|
95
|
+
{ "title": "司法案例检索", "text": "检索与「{{topic}}」相关的司法案例", "server": "case" },
|
|
96
|
+
{ "title": "权威案例检索", "text": "检索与「{{topic}}」相关的权威司法案例", "server": "case" }
|
|
90
97
|
]
|
|
91
98
|
},
|
|
92
99
|
{
|
|
@@ -101,6 +108,10 @@
|
|
|
101
108
|
"published": true,
|
|
102
109
|
"featured": false,
|
|
103
110
|
"homepage": "https://open.qcc.com",
|
|
111
|
+
"promptVariables": [
|
|
112
|
+
{ "name": "keyword", "label": "项目关键词", "default": "智慧城市", "placeholder": "例如:智慧城市", "required": true },
|
|
113
|
+
{ "name": "company", "label": "企业名称", "default": "华为技术有限公司", "placeholder": "例如:华为技术有限公司", "required": true }
|
|
114
|
+
],
|
|
104
115
|
"auth": {
|
|
105
116
|
"mode": "oauth2-pkce",
|
|
106
117
|
"issuer": "https://agent.qcc.com",
|
|
@@ -113,9 +124,9 @@
|
|
|
113
124
|
{ "serverKey": "tender", "url": "https://agent.qcc.com/mcp/tender/stream", "serverName": "qcc-tender", "transport": "streamable-http", "headers": {} }
|
|
114
125
|
],
|
|
115
126
|
"prompts": [
|
|
116
|
-
{ "title": "招投标检索", "text": "
|
|
117
|
-
{ "title": "拟建项目检索", "text": "
|
|
118
|
-
{ "title": "企业招投标检索", "text": "查询
|
|
127
|
+
{ "title": "招投标检索", "text": "检索与「{{keyword}}」相关的招投标、采购和中标标讯", "server": "tender" },
|
|
128
|
+
{ "title": "拟建项目检索", "text": "检索与「{{keyword}}」相关的拟建项目", "server": "tender" },
|
|
129
|
+
{ "title": "企业招投标检索", "text": "查询 {{company}} 相关的招标、投标、中标和采购标讯", "server": "tender" }
|
|
119
130
|
]
|
|
120
131
|
},
|
|
121
132
|
{
|
|
@@ -130,6 +141,9 @@
|
|
|
130
141
|
"published": true,
|
|
131
142
|
"featured": false,
|
|
132
143
|
"homepage": "https://open.qcc.com",
|
|
144
|
+
"promptVariables": [
|
|
145
|
+
{ "name": "company", "label": "企业名称", "default": "小米科技有限责任公司", "placeholder": "例如:小米科技有限责任公司", "required": true }
|
|
146
|
+
],
|
|
133
147
|
"auth": {
|
|
134
148
|
"mode": "oauth2-pkce",
|
|
135
149
|
"issuer": "https://agent.qcc.com",
|
|
@@ -141,7 +155,91 @@
|
|
|
141
155
|
"servers": [
|
|
142
156
|
{ "serverKey": "document", "url": "https://agent.qcc.com/mcp/document/stream", "serverName": "qcc-document", "transport": "streamable-http", "headers": {} }
|
|
143
157
|
],
|
|
144
|
-
"prompts": [
|
|
158
|
+
"prompts": [
|
|
159
|
+
{ "title": "企业尽调报告", "text": "为 {{company}} 生成一份企业尽职调查报告", "server": "document" },
|
|
160
|
+
{ "title": "企业风险报告", "text": "为 {{company}} 生成一份企业风险分析报告", "server": "document" },
|
|
161
|
+
{ "title": "工商信息摘要", "text": "整理 {{company}} 的工商与股权信息摘要", "server": "document" }
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"id": "pkulaw-legal",
|
|
166
|
+
"name": "北大法宝·法律检索",
|
|
167
|
+
"vendor": "北大法宝 PKULaw",
|
|
168
|
+
"icon": "/mcp-connector/ui/assets/pkulaw-logo.png",
|
|
169
|
+
"category": "法律数据",
|
|
170
|
+
"summary": "法规 / 法条 / 司法案例检索与引用核验",
|
|
171
|
+
"description": "接入北大法宝法律法规、司法案例、法条识别与引用核验能力。需使用北大法宝 Access Token,相关服务可能需要单独开通或购买。",
|
|
172
|
+
"tags": ["法律", "法规", "案例", "法条", "第三方收录"],
|
|
173
|
+
"published": true,
|
|
174
|
+
"featured": false,
|
|
175
|
+
"homepage": "https://mcp.pkulaw.com/",
|
|
176
|
+
"promptVariables": [
|
|
177
|
+
{ "name": "topic", "label": "法律主题", "default": "劳动合同解除", "placeholder": "例如:劳动合同解除", "required": true },
|
|
178
|
+
{ "name": "caseTopic", "label": "案例主题", "default": "竞业限制违约", "placeholder": "例如:竞业限制违约", "required": true },
|
|
179
|
+
{ "name": "citation", "label": "法律引用", "default": "《民法典》第五百七十七条", "placeholder": "例如:《民法典》第五百七十七条", "required": true }
|
|
180
|
+
],
|
|
181
|
+
"auth": {
|
|
182
|
+
"mode": "bearer",
|
|
183
|
+
"credentialName": "北大法宝 Access Token",
|
|
184
|
+
"credentialPlaceholder": "请输入您的北大法宝 Access Token",
|
|
185
|
+
"credentialDescription": "用于访问北大法宝 MCP;仅保存在 DSH 本机。",
|
|
186
|
+
"credentialHelpLabel": "如何获取北大法宝 Token?"
|
|
187
|
+
},
|
|
188
|
+
"servers": [
|
|
189
|
+
{ "serverKey": "law-search", "url": "https://apim-gateway.pkulaw.com/mcp-law-search-service", "serverName": "pkulaw-law-search", "transport": "streamable-http", "headers": {} },
|
|
190
|
+
{ "serverKey": "law-keyword", "url": "https://apim-gateway.pkulaw.com/mcp-law", "serverName": "pkulaw-law-keyword", "transport": "streamable-http", "headers": {} },
|
|
191
|
+
{ "serverKey": "case-semantic-search", "url": "https://apim-gateway.pkulaw.com/mcp-case-search-service", "serverName": "pkulaw-case-semantic-search", "transport": "streamable-http", "headers": {} },
|
|
192
|
+
{ "serverKey": "case-keyword", "url": "https://apim-gateway.pkulaw.com/mcp-case", "serverName": "pkulaw-case-keyword", "transport": "streamable-http", "headers": {} },
|
|
193
|
+
{ "serverKey": "law-item-keyword", "url": "https://apim-gateway.pkulaw.com/mcp-fatiao", "serverName": "pkulaw-law-item-keyword", "transport": "streamable-http", "headers": {} },
|
|
194
|
+
{ "serverKey": "law-recognition", "url": "https://apim-gateway.pkulaw.com/law_recognition", "serverName": "pkulaw-law-recognition", "transport": "streamable-http", "headers": {} },
|
|
195
|
+
{ "serverKey": "case-number-recognition", "url": "https://apim-gateway.pkulaw.com/case_number_recognition", "serverName": "pkulaw-case-number-recognition", "transport": "streamable-http", "headers": {} },
|
|
196
|
+
{ "serverKey": "citation-validator", "url": "https://apim-gateway.pkulaw.com/pku_citation_validator", "serverName": "pkulaw-citation-validator", "transport": "streamable-http", "headers": {} },
|
|
197
|
+
{ "serverKey": "doc-link", "url": "https://apim-gateway.pkulaw.com/add-doc-link", "serverName": "pkulaw-doc-link", "transport": "streamable-http", "headers": {} }
|
|
198
|
+
],
|
|
199
|
+
"prompts": [
|
|
200
|
+
{ "title": "法规与法条检索", "text": "用北大法宝 MCP 检索与「{{topic}}」相关的现行法律规定,并给出法条原文和效力信息", "server": "law-search" },
|
|
201
|
+
{ "title": "典型司法案例", "text": "用北大法宝 MCP 检索与「{{caseTopic}}」相关的典型司法案例,并列出案号和裁判要点", "server": "case-semantic-search" },
|
|
202
|
+
{ "title": "法律引用识别", "text": "用北大法宝 MCP 识别并核验 {{citation}} 的标准出处和现行法条原文", "server": "law-recognition" },
|
|
203
|
+
{ "title": "法律引用核验", "text": "用北大法宝 MCP 核验 {{citation}} 是否准确,并说明当前效力状态", "server": "citation-validator" }
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"id": "wind-stock-data",
|
|
208
|
+
"name": "Wind·股票数据",
|
|
209
|
+
"vendor": "Wind 万得",
|
|
210
|
+
"icon": "/mcp-connector/ui/assets/wind-logo.png",
|
|
211
|
+
"category": "金融数据",
|
|
212
|
+
"summary": "全球股票档案 / 行情 / 技术指标 / 财务与公司事件",
|
|
213
|
+
"description": "接入万得股票数据服务,覆盖公司档案、股本与股东、行情报价、技术指标、基本面财务、公司事件及风险波动性数据。需使用 Wind Key,服务权限以万得账户实际开通范围为准。",
|
|
214
|
+
"tags": ["股票", "行情", "财务", "技术指标", "第三方收录"],
|
|
215
|
+
"published": true,
|
|
216
|
+
"featured": false,
|
|
217
|
+
"homepage": "https://aifinmarket.wind.com.cn/#/market?tab=mcps&detailType=mcp&detailId=wind_stock_data-0",
|
|
218
|
+
"promptVariables": [
|
|
219
|
+
{ "name": "stock", "label": "股票名称或代码", "default": "贵州茅台(600519.SH)", "placeholder": "例如:贵州茅台(600519.SH)", "required": true }
|
|
220
|
+
],
|
|
221
|
+
"auth": {
|
|
222
|
+
"mode": "bearer",
|
|
223
|
+
"credentialName": "Wind API Key(个人密钥)",
|
|
224
|
+
"credentialPlaceholder": "请输入您的个人 Wind API Key",
|
|
225
|
+
"credentialDescription": "用于访问 Wind MCP 的个人访问令牌;仅保存在 DSH 本机。",
|
|
226
|
+
"credentialHelpLabel": "如何获取 Wind API Key?"
|
|
227
|
+
},
|
|
228
|
+
"servers": [
|
|
229
|
+
{
|
|
230
|
+
"serverKey": "stock-data",
|
|
231
|
+
"url": "https://mcp.wind.com.cn/vserver_stock_data/mcp/",
|
|
232
|
+
"serverName": "wind_stock_data",
|
|
233
|
+
"transport": "streamable-http",
|
|
234
|
+
"headers": { "Accept": "application/json, text/event-stream" }
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
"prompts": [
|
|
238
|
+
{ "title": "公司档案与行业", "text": "用 Wind 股票数据 MCP 查询 {{stock}} 的公司档案、主营业务和所属行业", "server": "stock-data" },
|
|
239
|
+
{ "title": "最新行情", "text": "用 Wind 股票数据 MCP 查询 {{stock}} 的最新价格、成交量、换手率和买卖报价", "server": "stock-data" },
|
|
240
|
+
{ "title": "技术与波动指标", "text": "用 Wind 股票数据 MCP 查询 {{stock}} 最近 20 个交易日的 MACD、RSI、波动率和涨跌幅", "server": "stock-data" },
|
|
241
|
+
{ "title": "核心财务指标", "text": "用 Wind 股票数据 MCP 查询 {{stock}} 最新报告期的营业收入、净利润、ROE 和现金流", "server": "stock-data" }
|
|
242
|
+
]
|
|
145
243
|
},
|
|
146
244
|
{
|
|
147
245
|
"id": "legacy-internal",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Desktop / Web UI 回归清单
|
|
2
|
+
|
|
3
|
+
## 自动门禁
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm run check
|
|
7
|
+
npm run dev:ui
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
访问 `http://127.0.0.1:4173/mcp-connector/ui/`。本地测试壳不读凭据、不访问真实 MCP,固定提供 125 个 mock 工具,用于验证大目录分批渲染。
|
|
11
|
+
|
|
12
|
+
## 每次发版前 Desktop 实机
|
|
13
|
+
|
|
14
|
+
1. 完全退出并重启 DSH Desktop;展开/收起侧栏,入口始终在“新会话”下、“工作区”上且不重复。
|
|
15
|
+
2. 切换工作区、新建会话、重建侧栏后入口仍存在;目标 slot 缺失时 footer 降级可点击。
|
|
16
|
+
3. 打开市场,验证中英文切换、系统深浅主题、Tab 顺序、Escape 关闭与焦点回到触发按钮。
|
|
17
|
+
4. 点击未连接卡片可查看详情;点击已连接卡片可读实时工具、搜索并每次再加载 50 条。
|
|
18
|
+
5. “添加连接”依次验证手动、JSON、市场卡片;JSON 示例/粘贴可缩进格式化,错误 URL/JSON/凭据得到可行动提示。
|
|
19
|
+
6. Bearer/API Key 型市场卡片先填错误凭据:弹框内应显示鉴权/网络分类错误,不得进入「已安装」;再填正确凭据,只有卡片下所有 Server 均通过 initialize 校验后才批量安装。
|
|
20
|
+
7. 参数化 Prompt 填写企业/人员/主题后,发送到当前工作区新会话草稿;重复点击和超时有提示。
|
|
21
|
+
8. OAuth:授权回调、重启恢复、停用/启用、断开撤销;二级弹框蒙层与关闭按钮清晰。
|
|
22
|
+
9. 旧插件迁移先预览,明确确认后复制;旧凭据仍保留,新连接验证后再手工停用旧插件。
|
|
23
|
+
|
|
24
|
+
## 不自动执行的项目
|
|
25
|
+
|
|
26
|
+
- 不在 CI 中完成真实 OAuth 登录。
|
|
27
|
+
- 不自动删除旧插件、旧 storage domain 或凭据。
|
|
28
|
+
- 不因探针一次失败直接删除社区条目;定时报告供维护者复核。
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# MCP 连接器市场注册指南
|
|
2
|
+
|
|
3
|
+
## 1. 先区分两种 JSON
|
|
4
|
+
|
|
5
|
+
| 类型 | 用途 | 是否含凭据 | 显示位置 |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| `mcpServers` 配置 | 为当前用户直接创建 MCP 连接 | 可以包含 Bearer/API Key | 「已安装」 |
|
|
8
|
+
| `ConnectorDescriptor` | 定义可浏览、可授权的市场卡片 | **不得包含任何凭据** | 「市场」 |
|
|
9
|
+
|
|
10
|
+
`mcpServers` 只有运行连接所需的 URL/Header,不包含厂商、Logo、产品说明、鉴权流程和示例 Prompt,因此导入后不会自动上架市场。
|
|
11
|
+
|
|
12
|
+
## 2. 先安装到本机市场
|
|
13
|
+
|
|
14
|
+
1. 按 `registry/schema/connector.schema.json` 编写无密钥的 ConnectorDescriptor。
|
|
15
|
+
2. 将 JSON 托管在 HTTPS URL(本机调试可使用 loopback HTTP)。
|
|
16
|
+
3. 在插件中选择「添加连接 → 市场卡片」,粘贴 URL。
|
|
17
|
+
4. Schema/密钥审计通过后,卡片会持久化到本机市场。
|
|
18
|
+
|
|
19
|
+
Bearer/API Key 型连接器可在卡片上点「配置」,一次填写凭据后批量连接该卡片的所有 Server。
|
|
20
|
+
|
|
21
|
+
## 3. 提交公共市场
|
|
22
|
+
|
|
23
|
+
1. 在 `registry/connectors/<id>.json` 新增一个描述文件,一个连接器一个文件。
|
|
24
|
+
2. 运行 `npm run registry:build` 和 `npm run registry:validate`。
|
|
25
|
+
3. 提交 PR。CI 会检查 Schema、重复 id/serverName、密钥、URL、MCP initialize、OAuth 元数据和图标。
|
|
26
|
+
4. 合并后生成远程 `catalog.json`;客户端刷新目录后可见。
|
|
27
|
+
|
|
28
|
+
公开 registry 独立仓库未拆分前,先向 `duhu2000/dsh-mcp-connector` 开发分支提交描述;拆分后流程不变。
|
|
29
|
+
|
|
30
|
+
## 4. OAuth 一键授权要求
|
|
31
|
+
|
|
32
|
+
`auth.mode: "oauth2-pkce"` 不是将现有 Bearer Token 改个名称。厂商服务端必须支持:
|
|
33
|
+
|
|
34
|
+
- OAuth Authorization Code + PKCE S256;
|
|
35
|
+
- MCP Protected Resource Metadata(RFC 9728);
|
|
36
|
+
- Authorization Server Metadata(RFC 8414);
|
|
37
|
+
- `authorization_endpoint`、`token_endpoint`、`registration_endpoint`、`revocation_endpoint`;
|
|
38
|
+
- Dynamic Client Registration,且支持 loopback callback URI;
|
|
39
|
+
- Refresh Token 和撤销。
|
|
40
|
+
|
|
41
|
+
符合上述条件的最小描述:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"schemaVersion": 1,
|
|
46
|
+
"id": "vendor-legal",
|
|
47
|
+
"name": "厂商·法律数据",
|
|
48
|
+
"vendor": "厂商名称",
|
|
49
|
+
"category": "法律数据",
|
|
50
|
+
"summary": "法规与案例检索",
|
|
51
|
+
"published": true,
|
|
52
|
+
"auth": {
|
|
53
|
+
"mode": "oauth2-pkce",
|
|
54
|
+
"issuer": "https://auth.vendor.example",
|
|
55
|
+
"scope": "mcp:tools",
|
|
56
|
+
"clientName": "DeepSeek Harness - MCP 连接器",
|
|
57
|
+
"tokenEndpointAuthMethod": "none"
|
|
58
|
+
},
|
|
59
|
+
"servers": [
|
|
60
|
+
{
|
|
61
|
+
"serverKey": "law",
|
|
62
|
+
"url": "https://mcp.vendor.example/law",
|
|
63
|
+
"serverName": "vendor-law",
|
|
64
|
+
"transport": "streamable-http"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 5. 北大法宝当前适配结论
|
|
71
|
+
|
|
72
|
+
北大法宝当前公开文档的主路径是在控制台生成 Access Token,然后通过 `Authorization: Bearer ...` 访问多个 MCP Server。因此:
|
|
73
|
+
|
|
74
|
+
- JSON 导入:可直接使用,显示在「已安装」;
|
|
75
|
+
- 公共市场:**技术评估为“有条件通过(Tier 1 / 第三方)”**。可用 `auth.mode: "bearer"` 注册,用户一次填写 Token 后批量连接所有 Server;
|
|
76
|
+
- 真正 OAuth 一键授权:需北大法宝提供第 4 节所列的标准端点,或与 DSH 进行预注册客户端集成;仅凭当前 `mcpServers` JSON 无法自动获得此能力。
|
|
77
|
+
|
|
78
|
+
正式发布公共卡片前还需完成以下门槛:
|
|
79
|
+
|
|
80
|
+
- 由北大法宝确认可公开使用的产品名称、Logo、描述和支持地址;未确认前必须标识为“第三方/非官方收录”;
|
|
81
|
+
- 描述文件不得内置 Token,并明确提示用户需自行注册、购买或开通相应服务;
|
|
82
|
+
- 对官网列出的全部 Server 执行 Schema、HTTPS、MCP initialize 和共享 Token 批量连接验证;
|
|
83
|
+
- 如要宣称“OAuth 一键授权”,必须补齐并验证标准发现元数据、Authorization Code + PKCE、动态客户端注册、刷新与撤销流程。
|
|
84
|
+
|
|
85
|
+
因此当前建议是:**可以准备并提交 Bearer 型公共市场卡片;暂不作为“官方精选”或“OAuth 一键授权”卡片发布。**
|
|
86
|
+
|
|
87
|
+
参考:
|
|
88
|
+
|
|
89
|
+
- https://mcp.pkulaw.com/docs?doc=authentication
|
|
90
|
+
- https://mcp.pkulaw.com/docs?doc=mcp-integration
|
|
91
|
+
|
|
92
|
+
## 6. Wind 股票数据当前适配结论
|
|
93
|
+
|
|
94
|
+
万得 AIFin Market 的“万得股票数据服务”详情页公开了标准 MCP 手工配置:
|
|
95
|
+
|
|
96
|
+
- MCP URL:`https://mcp.wind.com.cn/vserver_stock_data/mcp/`;
|
|
97
|
+
- 鉴权:`Authorization: Bearer YOUR_WIND_KEY`;
|
|
98
|
+
- 响应协商:`Accept: application/json, text/event-stream`;
|
|
99
|
+
- 当前公开能力范围:公司档案、股本与股东、行情报价、技术指标、基本面财务、公司事件、风险与波动性,共 10 个工具。
|
|
100
|
+
|
|
101
|
+
因此该服务可按 **Tier 1 / 第三方 Bearer 连接器**上架市场:用户填写一次 Wind Key 后建立一条股票数据连接。卡片只描述官网当前公开的股票 MCP 能力,不扩展宣称基金、债券、新闻或宏观数据;实际数据权限以用户的万得账户开通范围为准。
|
|
102
|
+
|
|
103
|
+
当前公开页面没有展示可由通用客户端自动发现的 OAuth Authorization Code + PKCE 流程,因此本卡片不标注“OAuth 一键授权”。如万得后续开放标准 OAuth 元数据,可再升级为 OAuth 连接器。
|
|
104
|
+
|
|
105
|
+
参考:
|
|
106
|
+
|
|
107
|
+
- https://aifinmarket.wind.com.cn/#/market?tab=mcps&detailType=mcp&detailId=wind_stock_data-0
|
package/lib/catalog.js
CHANGED
|
@@ -6,6 +6,23 @@ import { readFileSync } from 'node:fs';
|
|
|
6
6
|
import { normalizeConnectorDescriptor } from './schema.js';
|
|
7
7
|
import { assertSafeUrl, assertSafeHeaderName } from './util.js';
|
|
8
8
|
|
|
9
|
+
const MAX_CATALOG_BYTES = 2 * 1024 * 1024;
|
|
10
|
+
|
|
11
|
+
const SECRET_KEY_RE = /^(?:access[-_]?token|refresh[-_]?token|token|api[-_]?key|secret|client[-_]?secret|password|authorization)$/i;
|
|
12
|
+
|
|
13
|
+
/** 在 zod 丢弃未知字段前扫描原始 JSON,避免把夹带的 token 静默忽略。 */
|
|
14
|
+
export function auditRawDescriptor(raw, path = 'connector') {
|
|
15
|
+
if (!raw || typeof raw !== 'object') return raw;
|
|
16
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
17
|
+
const fieldPath = `${path}.${key}`;
|
|
18
|
+
if (SECRET_KEY_RE.test(key) && value !== undefined && value !== null && String(value).trim() !== '') {
|
|
19
|
+
throw new Error(`${fieldPath} 禁止携带凭证或密钥`);
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === 'object') auditRawDescriptor(value, fieldPath);
|
|
22
|
+
}
|
|
23
|
+
return raw;
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
/** 读取包内内置目录 */
|
|
10
27
|
export function loadBundledCatalog() {
|
|
11
28
|
try {
|
|
@@ -32,19 +49,33 @@ export function auditDescriptor(descriptor) {
|
|
|
32
49
|
return descriptor;
|
|
33
50
|
}
|
|
34
51
|
|
|
52
|
+
export async function readLimitedJson(response, maxBytes = MAX_CATALOG_BYTES) {
|
|
53
|
+
const declared = Number(response.headers.get('content-length'));
|
|
54
|
+
if (Number.isFinite(declared) && declared > maxBytes) throw new Error(`JSON 响应超过 ${maxBytes} bytes`);
|
|
55
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
56
|
+
if (bytes.byteLength > maxBytes) throw new Error(`JSON 响应超过 ${maxBytes} bytes`);
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
59
|
+
} catch (error) {
|
|
60
|
+
throw new Error(`JSON 解析失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
35
64
|
/** 拉取远程 registry(HTTPS + TTL 缓存 + 可选 ETag) */
|
|
36
65
|
export async function fetchRemoteCatalog(catalogUrl, { requestTimeoutMs, etag } = {}) {
|
|
66
|
+
assertSafeUrl(catalogUrl);
|
|
37
67
|
const controller = new AbortController();
|
|
38
68
|
const timer = setTimeout(() => controller.abort(new Error('timeout')), requestTimeoutMs ?? 15_000);
|
|
39
69
|
try {
|
|
40
70
|
const headers = {};
|
|
41
71
|
if (etag) headers['If-None-Match'] = etag;
|
|
42
72
|
const response = await fetch(catalogUrl, { headers, signal: controller.signal });
|
|
73
|
+
assertSafeUrl(response.url || catalogUrl);
|
|
43
74
|
if (response.status === 304) return { notModified: true };
|
|
44
75
|
if (!response.ok) throw new Error(`catalog fetch HTTP ${response.status}`);
|
|
45
|
-
const data = await response
|
|
76
|
+
const data = await readLimitedJson(response);
|
|
46
77
|
const list = Array.isArray(data) ? data : Array.isArray(data?.connectors) ? data.connectors : [];
|
|
47
|
-
const connectors = list.map((raw) => auditDescriptor(normalizeConnectorDescriptor(raw)));
|
|
78
|
+
const connectors = list.map((raw) => auditDescriptor(normalizeConnectorDescriptor(auditRawDescriptor(raw))));
|
|
48
79
|
return { notModified: false, etag: response.headers.get('etag') ?? undefined, connectors };
|
|
49
80
|
} finally {
|
|
50
81
|
clearTimeout(timer);
|
|
@@ -83,6 +114,6 @@ export function listCatalog(merged, { category, keyword, publishedOnly = true }
|
|
|
83
114
|
.some((v) => String(v).toLowerCase().includes(kw)),
|
|
84
115
|
);
|
|
85
116
|
}
|
|
86
|
-
//
|
|
87
|
-
return [...list].sort((a, b) => (b.featured === true) - (a.featured === true)
|
|
117
|
+
// 精选位优先;同级保留目录声明顺序,便于维护明确的市场陈列顺序。
|
|
118
|
+
return [...list].sort((a, b) => (b.featured === true) - (a.featured === true));
|
|
88
119
|
}
|