dsh-mcp-connector 0.2.39 → 0.2.40
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 +17 -1
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/USER-GUIDE.md +4 -1
- package/lib/index.js +53 -2
- package/lib/tools.js +19 -0
- package/lib/util.js +9 -0
- package/lib/web.js +1 -0
- package/package.json +1 -1
- package/ui/index.html +29 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.2.40] - 2026-09-09
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- “已安装”页为用户自定义和 JSON 导入的 MCP 连接新增“重命名”,启用状态下也可无中断修改显示名。
|
|
12
|
+
- 新增 `mcp_connector_rename` 对话工具,便于按连接 key 修改显示名。
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- 重命名只更新本机连接元数据并创建可恢复快照;connection key、`serverName`、工具前缀、连接范围、凭据和 Host 挂载均保持不变。
|
|
17
|
+
|
|
18
|
+
### Verification
|
|
19
|
+
|
|
20
|
+
- 209 项自动测试、lint、版本/营销元数据/商店截图门禁和 npm 发布包白名单/敏感内容扫描全部通过;新增覆盖显示名校验、Web API、对话工具、界面入口、启用状态下无重挂载、脱敏导出、快照与重启持久化。
|
|
21
|
+
|
|
7
22
|
## [0.2.39] - 2026-09-08
|
|
8
23
|
|
|
9
24
|
### Fixed
|
|
@@ -607,7 +622,8 @@
|
|
|
607
622
|
- 外部 URL 与导入 Header 执行安全校验。
|
|
608
623
|
- iframe 消息校验同源和消息来源。
|
|
609
624
|
|
|
610
|
-
[Unreleased]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.
|
|
625
|
+
[Unreleased]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.40...HEAD
|
|
626
|
+
[0.2.40]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.39...v0.2.40
|
|
611
627
|
[0.2.39]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.38...v0.2.39
|
|
612
628
|
[0.2.38]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.37...v0.2.38
|
|
613
629
|
[0.2.37]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.36...v0.2.37
|
package/README.en.md
CHANGED
|
@@ -175,7 +175,7 @@ npm run dev:ui
|
|
|
175
175
|
|
|
176
176
|
Every Registry merge regenerates `catalog-stats.json`; an hourly workflow in this repository synchronizes the Chinese and English product copy plus a local stats snapshot. The static npm README updates with package releases, while the live badges above read the Registry directly and therefore stay current without another npm release.
|
|
177
177
|
|
|
178
|
-
The current public version is [`dsh-mcp-connector@0.2.
|
|
178
|
+
The current public version is [`dsh-mcp-connector@0.2.40`](https://www.npmjs.com/package/dsh-mcp-connector), with [GitHub Release v0.2.40](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.40).
|
|
179
179
|
|
|
180
180
|
See [CHANGELOG.md](CHANGELOG.md) for version history and [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md) for the Desktop release checklist.
|
|
181
181
|
|
package/README.md
CHANGED
|
@@ -171,7 +171,7 @@ npm run dev:ui
|
|
|
171
171
|
|
|
172
172
|
公共 Registry 每次合并后会生成 `catalog-stats.json`;本仓库的定时工作流每小时同步中英文介绍和统计快照。npm 页面中的静态正文随版本发布更新,上方动态统计徽标则直接读取 Registry,可在不发布新 npm 版本时保持实时数量一致。
|
|
173
173
|
|
|
174
|
-
当前公开版本为 [`dsh-mcp-connector@0.2.
|
|
174
|
+
当前公开版本为 [`dsh-mcp-connector@0.2.40`](https://www.npmjs.com/package/dsh-mcp-connector),对应 [GitHub Release v0.2.40](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.40)。
|
|
175
175
|
|
|
176
176
|
版本能力与变更记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
177
177
|
Desktop 发版回归见 [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md)。
|
package/docs/USER-GUIDE.md
CHANGED
|
@@ -152,6 +152,7 @@ stdio 进程由 `@deepseek-ai/dsh-mcp-client` 管理,插件透传 `command`、
|
|
|
152
152
|
在“已安装”中可以:
|
|
153
153
|
|
|
154
154
|
- 查看连接状态和 Server 数量;
|
|
155
|
+
- 修改自定义或 JSON 导入连接的显示名,无需先停用连接;
|
|
155
156
|
- 启用或停用连接;
|
|
156
157
|
- 重新授权 OAuth,或重新录入 Token/API Key;
|
|
157
158
|
- 执行健康检查;
|
|
@@ -159,12 +160,14 @@ stdio 进程由 `@deepseek-ai/dsh-mcp-client` 管理,插件透传 `command`、
|
|
|
159
160
|
|
|
160
161
|
OAuth 断开时,插件会尽力调用服务商的撤销端点;无撤销端点时仍会删除 DSH 本机授权记录。连接状态发生变化后,可点击“刷新连接器目录”重新检查。
|
|
161
162
|
|
|
163
|
+
“重命名”只修改用户看到的连接显示名,不会改变 connection key、`serverName`、`mcp__<serverName>__*` 工具前缀、连接范围、凭据或启停状态,也不会重新挂载 MCP Server。市场连接器的名称继续由公共目录维护。
|
|
164
|
+
|
|
162
165
|
### 7.1 配置备份与恢复
|
|
163
166
|
|
|
164
167
|
“已安装”页的“配置备份”提供两种互补能力:
|
|
165
168
|
|
|
166
169
|
- **脱敏导出**:复制或下载可携带 JSON。Token/API Key、静态 Header/env 值、stdio 参数、本地目录以及可能含凭据的 URL 会变成明确占位符;目标设备导入前必须重填。OAuth 只导出连接引用,必须从市场重新授权。
|
|
167
|
-
-
|
|
170
|
+
- **本机快照**:连接、配置、导入、重命名、启停或断开前自动保存变更范围,也可手动创建;最多保留 20 个。恢复前可查看将恢复和移除的 key,恢复过程中任一 Server 或持久化步骤失败会整体回到恢复前状态。
|
|
168
171
|
|
|
169
172
|
快照可能包含用于原样恢复的本机凭据,因此只保存在当前 profile 的 storage domain,不会通过 Web API、对话工具或日志原样返回。断开并撤销 OAuth 后,快照不能重建服务端 Grant,会明确要求重新授权。完整边界见[配置备份说明](CONFIG-BACKUP.md)。
|
|
170
173
|
|
package/lib/index.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - 连接记录与 OAuth grant 持久化,重启恢复与跨 Host Token 轮换
|
|
8
8
|
* - 通过 ctx.loader 动态配置 @deepseek-ai/dsh-mcp-client 条目
|
|
9
9
|
* - 对话工具:mcp_connector_catalog / connect / configure / import_json /
|
|
10
|
-
* export_config / snapshots / install_from_url / status / set_enabled /
|
|
10
|
+
* export_config / snapshots / install_from_url / status / rename / set_enabled /
|
|
11
11
|
* disconnect / refresh_catalog / publish
|
|
12
12
|
*/
|
|
13
13
|
import { randomUUID } from 'node:crypto';
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
DEFAULT_REFRESH_RETRY_BASE_MS,
|
|
23
23
|
DEFAULT_REFRESH_RETRY_MAX_MS,
|
|
24
24
|
DEFAULT_CATALOG_TTL_MS,
|
|
25
|
+
CUSTOM_CONNECTOR_ID,
|
|
26
|
+
JSON_CONNECTOR_ID,
|
|
25
27
|
} from './constants.js';
|
|
26
28
|
import { defineConnectorDomain, ConnectionStore, GrantStore, CatalogStore, SnapshotStore, GovernanceStore, ConnectionScopeStore } from './stores.js';
|
|
27
29
|
import { normalizeConnectorDescriptor, normalizeConnectionRecord } from './schema.js';
|
|
@@ -36,7 +38,7 @@ import { listMcpTools, McpHttpError } from './mcp-http.js';
|
|
|
36
38
|
import { registerTools } from './tools.js';
|
|
37
39
|
import { mountWebRoutes } from './web.js';
|
|
38
40
|
import { createVersionStatusService } from './version-status.js';
|
|
39
|
-
import { assertSafeUrl } from './util.js';
|
|
41
|
+
import { assertSafeUrl, normalizeConnectionDisplayName } from './util.js';
|
|
40
42
|
import { readLegacyGrantCandidates, planLegacyMigration, toConnectorGrant, toConnectionRecords } from './migration.js';
|
|
41
43
|
import { classifyRefreshFailure, refreshRetryDelay } from './grant-lifecycle.js';
|
|
42
44
|
import { buildHealthSummary, diagnosticForRecord } from './diagnostics.js';
|
|
@@ -575,6 +577,10 @@ export async function apply(ctx, config) {
|
|
|
575
577
|
if (record?.connectorId) state.health.delete(record.connectorId);
|
|
576
578
|
}
|
|
577
579
|
|
|
580
|
+
function connectionDisplayNameEditable(record) {
|
|
581
|
+
return [CUSTOM_CONNECTOR_ID, JSON_CONNECTOR_ID].includes(record?.connectorId);
|
|
582
|
+
}
|
|
583
|
+
|
|
578
584
|
async function persistRecord(record) {
|
|
579
585
|
// 存储成功后才更新内存,避免落盘失败却在当前进程显示为“已安装”。
|
|
580
586
|
if (config.persistSecrets) await connectionStore.put(record);
|
|
@@ -2200,6 +2206,7 @@ export async function apply(ctx, config) {
|
|
|
2200
2206
|
name: record.name,
|
|
2201
2207
|
connectorId: record.connectorId,
|
|
2202
2208
|
kind: record.kind,
|
|
2209
|
+
canRename: connectionDisplayNameEditable(record),
|
|
2203
2210
|
serverName: record.serverName,
|
|
2204
2211
|
url: record.url,
|
|
2205
2212
|
transport: record.transport,
|
|
@@ -2362,6 +2369,50 @@ export async function apply(ctx, config) {
|
|
|
2362
2369
|
};
|
|
2363
2370
|
},
|
|
2364
2371
|
|
|
2372
|
+
async renameConnection(key, name) {
|
|
2373
|
+
const record = state.connections.get(key);
|
|
2374
|
+
if (!record) return { ok: false, message: `连接 "${key}" 不存在` };
|
|
2375
|
+
if (!connectionDisplayNameEditable(record)) {
|
|
2376
|
+
return { ok: false, message: '仅用户自定义或 JSON 导入的连接可修改显示名;市场连接器名称由目录维护' };
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
let nextName;
|
|
2380
|
+
try {
|
|
2381
|
+
nextName = normalizeConnectionDisplayName(name);
|
|
2382
|
+
} catch (error) {
|
|
2383
|
+
return { ok: false, message: `重命名失败: ${error.message}` };
|
|
2384
|
+
}
|
|
2385
|
+
if (nextName === record.name) {
|
|
2386
|
+
return {
|
|
2387
|
+
ok: true,
|
|
2388
|
+
message: `连接显示名未变更:"${record.name}"`,
|
|
2389
|
+
detail: { key: record.key, name: record.name, serverName: record.serverName, unchanged: true, snapshotId: null },
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
let snapshot;
|
|
2394
|
+
try {
|
|
2395
|
+
snapshot = await captureConnectionSnapshot('rename', [key]);
|
|
2396
|
+
const next = { ...record, name: nextName, updatedAt: Date.now() };
|
|
2397
|
+
const persisted = config.persistSecrets ? await connectionStore.put(next) : next;
|
|
2398
|
+
state.connections.set(key, persisted);
|
|
2399
|
+
return {
|
|
2400
|
+
ok: true,
|
|
2401
|
+
message: `已将连接显示名修改为 "${persisted.name}"`,
|
|
2402
|
+
detail: {
|
|
2403
|
+
key: persisted.key,
|
|
2404
|
+
name: persisted.name,
|
|
2405
|
+
serverName: persisted.serverName,
|
|
2406
|
+
enabled: persisted.enabled !== false,
|
|
2407
|
+
snapshotId: snapshot?.key ?? null,
|
|
2408
|
+
},
|
|
2409
|
+
};
|
|
2410
|
+
} catch (error) {
|
|
2411
|
+
await discardConnectionSnapshot(snapshot);
|
|
2412
|
+
return { ok: false, message: `重命名失败,已保留原显示名: ${error.message}` };
|
|
2413
|
+
}
|
|
2414
|
+
},
|
|
2415
|
+
|
|
2365
2416
|
async setEnabled(key, enabled) {
|
|
2366
2417
|
const record = state.connections.get(key);
|
|
2367
2418
|
if (!record) return { ok: false, message: `连接 "${key}" 不存在` };
|
package/lib/tools.js
CHANGED
|
@@ -469,6 +469,25 @@ export function registerTools(ctx, api) {
|
|
|
469
469
|
},
|
|
470
470
|
});
|
|
471
471
|
|
|
472
|
+
reg({
|
|
473
|
+
name: 'mcp_connector_rename',
|
|
474
|
+
description:
|
|
475
|
+
'修改用户自定义或 JSON 导入 MCP 连接的显示名。不改变 connection key、serverName、工具前缀、运行状态或凭据。',
|
|
476
|
+
parameters: {
|
|
477
|
+
type: 'object',
|
|
478
|
+
properties: {
|
|
479
|
+
key: { type: 'string', description: '连接 key(来自 mcp_connector_status)' },
|
|
480
|
+
name: { type: 'string', description: '新的连接显示名,最长 80 个字符' },
|
|
481
|
+
},
|
|
482
|
+
required: ['key', 'name'],
|
|
483
|
+
additionalProperties: false,
|
|
484
|
+
},
|
|
485
|
+
output: resultOutput,
|
|
486
|
+
async execute(args) {
|
|
487
|
+
return api.renameConnection(args.key, args.name);
|
|
488
|
+
},
|
|
489
|
+
});
|
|
490
|
+
|
|
472
491
|
reg({
|
|
473
492
|
name: 'mcp_connector_disconnect',
|
|
474
493
|
description: '断开并移除一条 MCP 连接;OAuth 连接会按引用计数撤销 refresh_token(无其他连接引用该授权时)。',
|
package/lib/util.js
CHANGED
|
@@ -52,6 +52,15 @@ export function slugServerName(input) {
|
|
|
52
52
|
return slug;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/** 归一化用户可编辑的连接显示名;不用于 serverName / 工具前缀。 */
|
|
56
|
+
export function normalizeConnectionDisplayName(input) {
|
|
57
|
+
const name = String(input ?? '').trim();
|
|
58
|
+
if (!name) throw new Error('连接显示名不能为空');
|
|
59
|
+
if (/[\u0000-\u001f\u007f]/.test(name)) throw new Error('连接显示名不能包含控制字符');
|
|
60
|
+
if ([...name].length > 80) throw new Error('连接显示名不能超过 80 个字符');
|
|
61
|
+
return name;
|
|
62
|
+
}
|
|
63
|
+
|
|
55
64
|
function parseUrl(rawUrl) {
|
|
56
65
|
let url;
|
|
57
66
|
try {
|
package/lib/web.js
CHANGED
|
@@ -41,6 +41,7 @@ const API_WHITELIST = new Map([
|
|
|
41
41
|
['previewSnapshot', (api, p) => api.previewSnapshot(p?.snapshotId)],
|
|
42
42
|
['restoreSnapshot', (api, p) => api.restoreSnapshot(p?.snapshotId)],
|
|
43
43
|
['installFromUrl', (api, p) => api.installFromUrl(p?.url, p ?? {})],
|
|
44
|
+
['renameConnection', (api, p) => api.renameConnection(p?.key, p?.name)],
|
|
44
45
|
['disconnect', (api, p) => api.disconnect(p?.key)],
|
|
45
46
|
['setEnabled', (api, p) => api.setEnabled(p?.key, p?.enabled !== false)],
|
|
46
47
|
['refreshCatalog', (api) => api.refreshCatalog()],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mcp-connector",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.40",
|
|
4
4
|
"description": "DeepSeek Harness MCP Connector and MCP Server marketplace with over one hundred MCP connectors, continuously updated. Discover, authorize, and manage connections in one place; supports OAuth 2.0 PKCE, API keys, stdio/HTTP, mcpServers JSON import, and tool and prompt discovery. Maintained by Qichacha/QCC.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
package/ui/index.html
CHANGED
|
@@ -1111,6 +1111,7 @@
|
|
|
1111
1111
|
|
|
1112
1112
|
function rowHtml(r) {
|
|
1113
1113
|
const key = esc(r.key);
|
|
1114
|
+
const canRename = r.canRename === true;
|
|
1114
1115
|
const authMode = r.authMode === 'oauth' ? '<span class="badge auth-oauth">OAuth</span>' : `<span class="badge">${esc(r.authMode || 'manual')}</span>`;
|
|
1115
1116
|
const healthLabels = { healthy: '已连接', unknown: '状态未知', configured: '已配置', reauth: '需重新授权', recovering: '自动重试中', degraded: '部分异常', unavailable: '连接异常', disabled: '已停用' };
|
|
1116
1117
|
const healthClass = r.connectionState === 'healthy' ? 'pass' : ['reauth', 'recovering', 'degraded'].includes(r.connectionState) ? 'partial' : r.connectionState === 'unavailable' ? 'fail' : '';
|
|
@@ -1131,6 +1132,7 @@
|
|
|
1131
1132
|
<input type="checkbox" ${enabled ? 'checked' : ''} onchange="window.__mcp.toggle('${key}', this.checked)">
|
|
1132
1133
|
<span class="slider"></span>
|
|
1133
1134
|
</label>
|
|
1135
|
+
${canRename ? `<button class="btn ghost" type="button" onclick="window.__mcp.openRename('${key}')">重命名</button>` : ''}
|
|
1134
1136
|
<button class="btn ghost" type="button" onclick="window.__mcp.openScope('${key}')">范围</button>
|
|
1135
1137
|
<button class="btn danger" onclick="window.__mcp.disconnect('${key}')">断开</button>
|
|
1136
1138
|
</div>`;
|
|
@@ -1244,6 +1246,21 @@
|
|
|
1244
1246
|
<div class="m-actions"><button class="btn ghost" type="button" onclick="window.__mcp.closeModal()">取消</button><button class="btn" type="button" onclick="window.__mcp.submitScopeChange()">预览并应用</button></div>`);
|
|
1245
1247
|
}
|
|
1246
1248
|
|
|
1249
|
+
function openRenameConnection(key) {
|
|
1250
|
+
const item = installedItems.find((candidate) => candidate.key === key);
|
|
1251
|
+
if (!item) { showToast('连接不存在,请刷新后重试', false); return; }
|
|
1252
|
+
if (item.canRename !== true) {
|
|
1253
|
+
showToast('市场连接器名称由目录维护', false);
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
openModal('修改连接显示名', `
|
|
1257
|
+
<div class="form-note"><strong>只修改页面显示名</strong>当前连接不会中断;<code>serverName</code>、工具前缀、作用范围和凭据均保持不变。</div>
|
|
1258
|
+
<label for="rename-name">连接显示名(必填)</label><input id="rename-name" value="${esc(item.name)}" maxlength="80" autocomplete="off" />
|
|
1259
|
+
<label for="rename-server-name">serverName(工具前缀,不可修改)</label><input id="rename-server-name" value="${esc(item.serverName || '')}" disabled />
|
|
1260
|
+
<div class="inline-error" id="modal-error" tabindex="-1"></div>
|
|
1261
|
+
<div class="m-actions"><button class="btn ghost" type="button" onclick="window.__mcp.closeModal()">取消</button><button class="btn" type="button" onclick="window.__mcp.submitRename('${esc(key)}')">保存显示名</button></div>`);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1247
1264
|
function openAddConnection(mode = 'json', preset = {}) {
|
|
1248
1265
|
const server = (preset.servers || [{}])[0] || {};
|
|
1249
1266
|
if (mode === 'manual' && preset.id && ['bearer', 'api-key'].includes(preset.authMode)) {
|
|
@@ -1371,6 +1388,18 @@
|
|
|
1371
1388
|
if (connectorId) return window.__mcp.connect(connectorId);
|
|
1372
1389
|
},
|
|
1373
1390
|
openScope(key) { openScopeManager(key); },
|
|
1391
|
+
openRename(key) { openRenameConnection(key); },
|
|
1392
|
+
async submitRename(key) {
|
|
1393
|
+
const name = $('#rename-name')?.value.trim() || '';
|
|
1394
|
+
if (!name) { showModalError('连接显示名不能为空'); return; }
|
|
1395
|
+
setModalBusy(true);
|
|
1396
|
+
const result = await call('renameConnection', { key, name });
|
|
1397
|
+
setModalBusy(false);
|
|
1398
|
+
if (!result.ok) { showModalError(result.message); return; }
|
|
1399
|
+
closeModal();
|
|
1400
|
+
showToast(result.message, true);
|
|
1401
|
+
await loadInstalled();
|
|
1402
|
+
},
|
|
1374
1403
|
async submitScopeChange() {
|
|
1375
1404
|
if (!pendingScopeKey) return;
|
|
1376
1405
|
let target;
|