dsh-mcp-connector 0.2.4 → 0.2.6
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 +31 -1
- package/README.en.md +6 -4
- package/README.md +6 -4
- package/lib/index.js +107 -49
- package/package.json +21 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.2.6] - 2026-08-21
|
|
8
|
+
|
|
9
|
+
### Documentation
|
|
10
|
+
|
|
11
|
+
- 记录 `0.2.5` GitHub/npm 发布、DSH Desktop 精确版本安装、孤立 grant 自动清理与 19/19 Server 健康回归结果。
|
|
12
|
+
- 扩充 GitHub Topic、仓库简介、README 和 npm 关键词,覆盖 MCP连接器、连接管理、插件/扩展、集成、Qichacha/QCC 和企查查等真实检索入口。
|
|
13
|
+
- 明确“技能扩展”指 MCP 工具与 Prompt 对智能体能力的扩展,不将本包标记为独立 DSH Skill。
|
|
14
|
+
|
|
15
|
+
## [0.2.5] - 2026-08-21
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- 同一连接器已有 OAuth 授权流程进行中时,后续重复点击复用同一 Promise,不再重复打开授权页或创建重复 grant。
|
|
20
|
+
|
|
21
|
+
### Security
|
|
22
|
+
|
|
23
|
+
- OAuth 重新授权成功后,撤销并删除已不再被连接引用的旧 refresh token/grant;插件启动时同步清理历史孤立 grant,仅保留当前连接仍引用的本机凭据。
|
|
24
|
+
- 授权流程中途失败时清理未被连接引用的临时 grant,避免失败重试累积敏感记录。
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- 修复连续或并发 OAuth 重新授权后,本机可能残留重复且未被连接引用的授权记录。
|
|
29
|
+
|
|
30
|
+
### Documentation
|
|
31
|
+
|
|
32
|
+
- 记录 `0.2.4` GitHub/npm 发布、DSH Desktop 精确版本安装和连接健康状态实机回归结果。
|
|
33
|
+
- 记录旧企查查 OAuth 插件凭据迁移的用户确认、幂等执行和源数据保留验证结果。
|
|
34
|
+
|
|
7
35
|
## [0.2.4] - 2026-08-21
|
|
8
36
|
|
|
9
37
|
### Added
|
|
@@ -119,7 +147,9 @@
|
|
|
119
147
|
- 外部 URL 与导入 Header 执行安全校验。
|
|
120
148
|
- iframe 消息校验同源和消息来源。
|
|
121
149
|
|
|
122
|
-
[Unreleased]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.
|
|
150
|
+
[Unreleased]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.6...HEAD
|
|
151
|
+
[0.2.6]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.5...v0.2.6
|
|
152
|
+
[0.2.5]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.4...v0.2.5
|
|
123
153
|
[0.2.4]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.3...v0.2.4
|
|
124
154
|
[0.2.3]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.2...v0.2.3
|
|
125
155
|
[0.2.2]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.1...v0.2.2
|
package/README.en.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
# MCP Connector Marketplace for DeepSeek Harness
|
|
1
|
+
# MCP Connector and Connection Manager Marketplace for DeepSeek Harness
|
|
2
2
|
|
|
3
|
-
> A general-purpose MCP connector marketplace initiated and maintained by the Qichacha team
|
|
3
|
+
> A general-purpose MCP connector, plugin extension, and integration marketplace initiated and maintained by the Qichacha (QCC) team
|
|
4
4
|
|
|
5
|
-
Browse and install MCP connectors from different providers in DeepSeek Harness Desktop. Connect through OAuth, API key/URL configuration, JSON import, or a connector descriptor URL; discover tools, start a new conversation
|
|
5
|
+
Browse and install MCP connectors from different providers in DeepSeek Harness Desktop. Connect MCP servers through OAuth, API key/URL configuration, JSON import, or a connector descriptor URL; discover tools and prompts, extend agent skills, start a new conversation, and manage installed connections.
|
|
6
|
+
|
|
7
|
+
> Here, “skill extension” means extending an agent through MCP tools and prompts; this package does not present itself as a standalone DSH Skill.
|
|
6
8
|
|
|
7
9
|
[简体中文](README.md)
|
|
8
10
|
|
|
@@ -97,7 +99,7 @@ npm run dev:ui
|
|
|
97
99
|
|
|
98
100
|
`npm run check` performs syntax checks, automated tests, and an npm package allowlist/sensitive-content audit. Tags matching `v*` trigger GitHub Actions; the tag must match `package.json`. npm releases use Trusted Publishing through GitHub OIDC and do not require a long-lived `NPM_TOKEN`.
|
|
99
101
|
|
|
100
|
-
The current public version is [`dsh-mcp-connector@0.2.
|
|
102
|
+
The current public version is [`dsh-mcp-connector@0.2.6`](https://www.npmjs.com/package/dsh-mcp-connector), with [GitHub Release v0.2.6](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.6).
|
|
101
103
|
|
|
102
104
|
See [CHANGELOG.md](CHANGELOG.md) for version history and [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md) for the Desktop release checklist.
|
|
103
105
|
|
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
# MCP
|
|
1
|
+
# MCP连接器与连接管理市场(DeepSeek Harness 插件 / 扩展)
|
|
2
2
|
|
|
3
|
-
> 通用 MCP Connector Marketplace
|
|
3
|
+
> 通用 MCP Connector / Connection Manager / Integration Marketplace,由企查查(Qichacha/QCC)团队发起并维护
|
|
4
4
|
|
|
5
|
-
在 DeepSeek Harness Desktop 中浏览和安装不同厂商的 MCP
|
|
5
|
+
在 DeepSeek Harness Desktop 中浏览和安装不同厂商的 MCP连接器,连接 MCP Server,通过 OAuth、API Key/URL 或 JSON 接入服务,发现工具与 Prompt,扩展 AI 技能,并管理已安装连接。
|
|
6
|
+
|
|
7
|
+
> 注:“技能扩展”指通过 MCP 工具和 Prompt 扩展智能体能力,本包不会伪装成独立 DSH Skill。
|
|
6
8
|
|
|
7
9
|
[English](README.en.md)
|
|
8
10
|
|
|
@@ -93,7 +95,7 @@ npm run dev:ui
|
|
|
93
95
|
|
|
94
96
|
`check` 执行语法检查、自动测试和 npm 发布包白名单校验;`dev:ui` 启动不含真实凭据的本地 mock 市场。CI 使用 `--legacy-peer-deps` 安装显式测试依赖,DSH 运行期 peer 仍由 Host 提供。`v*` Tag 会触发 GitHub Actions;Tag 必须与 `package.json` 版本一致。Release 通过 npm Trusted Publishing (GitHub OIDC) 发布,不依赖长期 `NPM_TOKEN`。
|
|
95
97
|
|
|
96
|
-
当前公开版本为 [`dsh-mcp-connector@0.2.
|
|
98
|
+
当前公开版本为 [`dsh-mcp-connector@0.2.6`](https://www.npmjs.com/package/dsh-mcp-connector),对应 [GitHub Release v0.2.6](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.6)。
|
|
97
99
|
|
|
98
100
|
版本能力与变更记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
99
101
|
Desktop 发版回归见 [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md)。
|
package/lib/index.js
CHANGED
|
@@ -78,6 +78,7 @@ export async function apply(ctx, config) {
|
|
|
78
78
|
connections: new Map(), // key -> ConnectionRecord
|
|
79
79
|
grants: new Map(), // grantKey -> { grant, timer, refreshPromise, needsReauth }
|
|
80
80
|
health: new Map(), // connectorId -> 最近一次主动连通性检查摘要(不持久化)
|
|
81
|
+
oauthConnects: new Map(), // connectorId -> 正在进行的 OAuth 授权 Promise(防重复弹窗/重复 grant)
|
|
81
82
|
};
|
|
82
83
|
|
|
83
84
|
/* ───────────────────────── 目录加载 ───────────────────────── */
|
|
@@ -192,6 +193,36 @@ export async function apply(ctx, config) {
|
|
|
192
193
|
entry.timer.unref?.();
|
|
193
194
|
}
|
|
194
195
|
|
|
196
|
+
async function retireGrantIfUnused(grantKey, { revoke = false } = {}) {
|
|
197
|
+
if ([...state.connections.values()].some((record) => record.auth?.grantKey === grantKey)) return false;
|
|
198
|
+
const entry = state.grants.get(grantKey);
|
|
199
|
+
if (!entry) return false;
|
|
200
|
+
if (revoke && entry.grant.refreshToken) {
|
|
201
|
+
try {
|
|
202
|
+
const metadata = await discoverServerMetadata(entry.grant.issuer, config.requestTimeoutMs);
|
|
203
|
+
await revokeRefreshToken(metadata.revocationEndpoint, {
|
|
204
|
+
clientId: entry.grant.clientId,
|
|
205
|
+
refreshToken: entry.grant.refreshToken,
|
|
206
|
+
timeoutMs: config.requestTimeoutMs,
|
|
207
|
+
});
|
|
208
|
+
} catch (error) {
|
|
209
|
+
logger.warn(`retire unused grant ${grantKey}: revoke failed, removing local copy: ${error.message}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
213
|
+
state.grants.delete(grantKey);
|
|
214
|
+
if (config.persistSecrets) await grantStore.delete(grantKey).catch(() => {});
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function pruneOrphanGrants({ revoke = false } = {}) {
|
|
219
|
+
let removed = 0;
|
|
220
|
+
for (const grantKey of [...state.grants.keys()]) {
|
|
221
|
+
if (await retireGrantIfUnused(grantKey, { revoke })) removed += 1;
|
|
222
|
+
}
|
|
223
|
+
return removed;
|
|
224
|
+
}
|
|
225
|
+
|
|
195
226
|
/* ───────────────────────── 连接记录落库 / 挂载 ───────────────────────── */
|
|
196
227
|
|
|
197
228
|
async function persistRecord(record) {
|
|
@@ -323,58 +354,82 @@ export async function apply(ctx, config) {
|
|
|
323
354
|
if (!connector) return { ok: false, message: `连接器 "${connectorId}" 不存在(可先 mcp_connector_refresh_catalog 或 mcp_connector_catalog 查看)` };
|
|
324
355
|
|
|
325
356
|
if (connector.auth.mode === 'oauth2-pkce') {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (!server) continue;
|
|
355
|
-
const record = {
|
|
356
|
-
key: `${connector.id}-${sk}`,
|
|
357
|
-
connectorId: connector.id,
|
|
358
|
-
kind: 'oauth',
|
|
359
|
-
name: connector.servers.length > 1 ? `${connector.name}·${sk}` : connector.name,
|
|
360
|
-
serverKey: sk,
|
|
361
|
-
transport: server.transport,
|
|
362
|
-
url: server.url,
|
|
363
|
-
serverName: server.serverName,
|
|
364
|
-
headers: server.headers,
|
|
365
|
-
auth: { mode: 'oauth', grantKey },
|
|
366
|
-
enabled: true,
|
|
367
|
-
createdAt: Date.now(),
|
|
357
|
+
const inFlight = state.oauthConnects.get(connector.id);
|
|
358
|
+
if (inFlight) return inFlight;
|
|
359
|
+
const promise = (async () => {
|
|
360
|
+
try {
|
|
361
|
+
const previousGrantKeys = new Set(
|
|
362
|
+
[...state.grants]
|
|
363
|
+
.filter(([, entry]) => entry.grant.connectorIds?.includes(connector.id))
|
|
364
|
+
.map(([key]) => key),
|
|
365
|
+
);
|
|
366
|
+
const authz = await oauthAuthorize({ connector, config, logger, signal });
|
|
367
|
+
const grantKey = grantStore.keyFor(config.account, authz.issuer, authz.clientId, authz.scope);
|
|
368
|
+
const entryResource = authz.entryResource;
|
|
369
|
+
const grant = {
|
|
370
|
+
key: grantKey,
|
|
371
|
+
issuer: authz.issuer,
|
|
372
|
+
clientId: authz.clientId,
|
|
373
|
+
clientName: authz.clientName,
|
|
374
|
+
scope: authz.scope,
|
|
375
|
+
account: config.account,
|
|
376
|
+
accessToken: authz.token.accessToken,
|
|
377
|
+
accessTokenExpiresAt: Date.now() + authz.token.expiresIn * 1000 - config.refreshSkewMs,
|
|
378
|
+
refreshToken: authz.token.refreshToken ?? '',
|
|
379
|
+
authorizedResources: connector.servers
|
|
380
|
+
.filter((s) => authz.grantedKeys.includes(s.serverKey))
|
|
381
|
+
.map((s) => s.url).length
|
|
382
|
+
? connector.servers.filter((s) => authz.grantedKeys.includes(s.serverKey)).map((s) => s.url)
|
|
383
|
+
: [entryResource],
|
|
384
|
+
connectorIds: [connector.id],
|
|
368
385
|
updatedAt: Date.now(),
|
|
369
386
|
};
|
|
370
|
-
|
|
371
|
-
|
|
387
|
+
state.grants.set(grantKey, { grant, timer: undefined, refreshPromise: null, needsReauth: false });
|
|
388
|
+
if (config.persistSecrets) await grantStore.put(grant);
|
|
389
|
+
|
|
390
|
+
const created = [];
|
|
391
|
+
for (const sk of authz.grantedKeys) {
|
|
392
|
+
const server = connector.servers.find((s) => s.serverKey === sk);
|
|
393
|
+
if (!server) continue;
|
|
394
|
+
const record = {
|
|
395
|
+
key: `${connector.id}-${sk}`,
|
|
396
|
+
connectorId: connector.id,
|
|
397
|
+
kind: 'oauth',
|
|
398
|
+
name: connector.servers.length > 1 ? `${connector.name}·${sk}` : connector.name,
|
|
399
|
+
serverKey: sk,
|
|
400
|
+
transport: server.transport,
|
|
401
|
+
url: server.url,
|
|
402
|
+
serverName: server.serverName,
|
|
403
|
+
headers: server.headers,
|
|
404
|
+
auth: { mode: 'oauth', grantKey },
|
|
405
|
+
enabled: true,
|
|
406
|
+
createdAt: Date.now(),
|
|
407
|
+
updatedAt: Date.now(),
|
|
408
|
+
};
|
|
409
|
+
await upsertRecord(record);
|
|
410
|
+
created.push(record.key);
|
|
411
|
+
}
|
|
412
|
+
scheduleRefresh(grantKey);
|
|
413
|
+
let retiredGrantCount = 0;
|
|
414
|
+
for (const previousKey of previousGrantKeys) {
|
|
415
|
+
if (previousKey !== grantKey && await retireGrantIfUnused(previousKey, { revoke: true })) retiredGrantCount += 1;
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
ok: true,
|
|
419
|
+
message: `已连接 "${connector.name}"(${created.length} 个 MCP server),共 ${created.length} 条`,
|
|
420
|
+
detail: { keys: created, grantKey, retiredGrantCount },
|
|
421
|
+
};
|
|
422
|
+
} catch (error) {
|
|
423
|
+
logger.error(`oauth connect failed: ${error.message}`);
|
|
424
|
+
await pruneOrphanGrants();
|
|
425
|
+
return { ok: false, message: `连接失败: ${error.message}` };
|
|
372
426
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
427
|
+
})();
|
|
428
|
+
state.oauthConnects.set(connector.id, promise);
|
|
429
|
+
try {
|
|
430
|
+
return await promise;
|
|
431
|
+
} finally {
|
|
432
|
+
if (state.oauthConnects.get(connector.id) === promise) state.oauthConnects.delete(connector.id);
|
|
378
433
|
}
|
|
379
434
|
}
|
|
380
435
|
|
|
@@ -845,6 +900,9 @@ export async function apply(ctx, config) {
|
|
|
845
900
|
}
|
|
846
901
|
}
|
|
847
902
|
|
|
903
|
+
const prunedGrantCount = await pruneOrphanGrants();
|
|
904
|
+
if (prunedGrantCount > 0) logger.info(`removed ${prunedGrantCount} unreferenced OAuth grant(s) from local storage`);
|
|
905
|
+
|
|
848
906
|
for (const [grantKey, g] of state.grants) {
|
|
849
907
|
if (g.grant.accessTokenExpiresAt <= Date.now()) {
|
|
850
908
|
try {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mcp-connector",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "General-purpose MCP
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"description": "General-purpose MCP connector, connection manager, plugin extension, and integration marketplace for DeepSeek Harness, initiated and maintained by Qichacha/QCC.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -46,18 +46,36 @@
|
|
|
46
46
|
"cordis",
|
|
47
47
|
"mcp",
|
|
48
48
|
"mcp-client",
|
|
49
|
+
"mcp-server",
|
|
49
50
|
"mcp-connector",
|
|
51
|
+
"mcp-marketplace",
|
|
50
52
|
"connector",
|
|
53
|
+
"connection",
|
|
54
|
+
"connection-manager",
|
|
51
55
|
"marketplace",
|
|
56
|
+
"plugin",
|
|
57
|
+
"extension",
|
|
58
|
+
"integration",
|
|
59
|
+
"agent-skills",
|
|
60
|
+
"skill-extension",
|
|
61
|
+
"ai-tools",
|
|
52
62
|
"oauth",
|
|
53
63
|
"oauth2",
|
|
54
64
|
"pkce",
|
|
65
|
+
"api-key",
|
|
55
66
|
"json-import",
|
|
56
67
|
"enterprise-data",
|
|
57
68
|
"qichacha",
|
|
58
69
|
"qcc",
|
|
59
70
|
"tools",
|
|
60
|
-
"prompts"
|
|
71
|
+
"prompts",
|
|
72
|
+
"MCP连接器",
|
|
73
|
+
"连接器",
|
|
74
|
+
"连接管理",
|
|
75
|
+
"技能扩展",
|
|
76
|
+
"插件",
|
|
77
|
+
"扩展",
|
|
78
|
+
"企查查"
|
|
61
79
|
],
|
|
62
80
|
"license": "MIT",
|
|
63
81
|
"publishConfig": {
|