dsh-mcp-connector 0.2.49 → 0.2.50
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 +14 -0
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/lib/cli-providers.js +5 -2
- package/lib/mcp-validation.js +9 -1
- package/lib/oauth.js +34 -4
- package/package.json +1 -1
- package/ui/index.html +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.2.50] - 2026-09-16
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- 修复 #79:含路径的 OAuth issuer 按 RFC 8414 在主机与路径之间插入发现地址;标准地址返回 404/405 时兼容旧后缀地址,无路径行为不变。
|
|
12
|
+
- OIDC 补充发现兼容插入式和历史后缀式地址;仅合并 issuer 精确一致的元数据,拒绝缺失/不匹配的 OAuth issuer 及带 query/fragment 的 issuer,不因 403/429/5xx 或网络故障转试旧地址。
|
|
13
|
+
- 区分 CLI 未授权与状态响应异常,保留可识别的诊断标记;Host 未保留具体原因时明确标记原因未知,不猜测管理员限制。
|
|
14
|
+
- 不再因错误包含“连接”二字自动追加 MCP URL 建议;CLI/OAuth 错误保留原有指引。
|
|
15
|
+
|
|
16
|
+
### Verification
|
|
17
|
+
|
|
18
|
+
- 新增 14 项 OAuth 发现回归;完整 279 项测试通过,7 张商店截图和 78 个发布文件检查通过。
|
|
19
|
+
- 修复后只读发现 Espressif docs 的公开 OAuth 元数据成功;未创建客户端、未进行用户授权或调用工具。本版不包含仍在独立 PR 中的钉钉浏览器授权新流程。
|
|
20
|
+
|
|
7
21
|
## [0.2.49] - 2026-09-15
|
|
8
22
|
|
|
9
23
|
### Fixed
|
package/README.en.md
CHANGED
|
@@ -190,7 +190,7 @@ npm run dev:ui
|
|
|
190
190
|
|
|
191
191
|
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.
|
|
192
192
|
|
|
193
|
-
The current public version is [`dsh-mcp-connector@0.2.
|
|
193
|
+
The current public version is [`dsh-mcp-connector@0.2.50`](https://www.npmjs.com/package/dsh-mcp-connector), with [GitHub Release v0.2.50](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.50).
|
|
194
194
|
|
|
195
195
|
See [CHANGELOG.md](CHANGELOG.md) for version history and [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md) for the Desktop release checklist.
|
|
196
196
|
|
package/README.md
CHANGED
|
@@ -185,7 +185,7 @@ npm run dev:ui
|
|
|
185
185
|
|
|
186
186
|
公共 Registry 每次合并后会生成 `catalog-stats.json`;本仓库的定时工作流每小时同步中英文介绍和统计快照。npm 页面中的静态正文随版本发布更新,上方动态统计徽标则直接读取 Registry,可在不发布新 npm 版本时保持实时数量一致。
|
|
187
187
|
|
|
188
|
-
当前公开版本为 [`dsh-mcp-connector@0.2.
|
|
188
|
+
当前公开版本为 [`dsh-mcp-connector@0.2.50`](https://www.npmjs.com/package/dsh-mcp-connector),对应 [GitHub Release v0.2.50](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.50)。
|
|
189
189
|
|
|
190
190
|
版本能力与变更记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
191
191
|
Desktop 发版回归见 [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md)。
|
package/lib/cli-providers.js
CHANGED
|
@@ -385,11 +385,14 @@ export async function ensureCliProviderReady(providerId, options = {}) {
|
|
|
385
385
|
try {
|
|
386
386
|
status = JSON.parse(output);
|
|
387
387
|
} catch {
|
|
388
|
-
throw new Error('无法验证钉钉 OAuth 状态:官方 CLI 未返回有效 JSON;请检查 CLI 版本后重试');
|
|
388
|
+
throw new Error('[CLI_AUTH_STATUS_INVALID] 无法验证钉钉 OAuth 状态:官方 CLI 未返回有效 JSON;请检查 CLI 版本后重试');
|
|
389
|
+
}
|
|
390
|
+
if (status?.success === true && status?.authenticated === false) {
|
|
391
|
+
throw new Error('[CLI_AUTH_REQUIRED] 钉钉待授权:官方 CLI 当前未登录。具备企业授权条件后,在运行 DSH 的同一系统用户终端执行 npx -y dingtalk-workspace-cli@1.0.61 auth login。若已确认企业未开放 CLI,请等待管理员处理,无需反复登录、重启或填写 API Key;仅凭未登录状态不能判断管理员是否限制');
|
|
389
392
|
}
|
|
390
393
|
if (!status || typeof status !== 'object' || Array.isArray(status)
|
|
391
394
|
|| status.success !== true || status.authenticated !== true) {
|
|
392
|
-
throw new Error('
|
|
395
|
+
throw new Error('[CLI_AUTH_STATUS_INVALID] 无法确认钉钉授权状态:官方 CLI 返回失败或不完整的状态响应;请核对 CLI 诊断。不能据此判断为未登录或管理员限制');
|
|
393
396
|
}
|
|
394
397
|
}
|
|
395
398
|
|
package/lib/mcp-validation.js
CHANGED
|
@@ -57,6 +57,12 @@ function errorChain(error) {
|
|
|
57
57
|
|
|
58
58
|
export function classifyConnectionError(error, { transport } = {}) {
|
|
59
59
|
const text = errorChain(error);
|
|
60
|
+
if (text.includes('[CLI_AUTH_REQUIRED]')) {
|
|
61
|
+
return { kind: 'authorization-required', message: '钉钉待授权:官方 CLI 当前未登录。若已确认企业尚未开放 CLI,请等待管理员处理,无需反复登录、重启或填写 API Key;具备授权条件后再完成官方登录并连接' };
|
|
62
|
+
}
|
|
63
|
+
if (text.includes('[CLI_AUTH_STATUS_INVALID]')) {
|
|
64
|
+
return { kind: 'authorization-unknown', message: '钉钉授权状态未知:官方 CLI 返回异常或不完整响应,请检查 CLI 诊断;不能据此判断为未登录或管理员限制' };
|
|
65
|
+
}
|
|
60
66
|
if (/AbortError|timeout|timed out|ETIMEDOUT/i.test(text)) {
|
|
61
67
|
return {
|
|
62
68
|
kind: 'timeout',
|
|
@@ -86,7 +92,9 @@ export function classifyConnectionError(error, { transport } = {}) {
|
|
|
86
92
|
};
|
|
87
93
|
}
|
|
88
94
|
if (/initial (?:connection|tool synchronization)|tool synchronization failed|initialize(?: handshake)? failed/i.test(text)) {
|
|
89
|
-
return { kind: 'startup', message:
|
|
95
|
+
return { kind: 'startup', message: transport === 'stdio'
|
|
96
|
+
? '本地 MCP 初始化或首次工具同步失败,Host 未提供可识别的具体原因;请检查官方 CLI 状态、启动命令和 Host 日志,不能仅据此判断为授权失败'
|
|
97
|
+
: 'MCP 初始化或首次工具同步失败,请检查服务配置和 Host 日志' };
|
|
90
98
|
}
|
|
91
99
|
if (transport === 'stdio') {
|
|
92
100
|
return { kind: 'startup', message: '本地 MCP Server 启动或初始化失败,请检查命令、参数、运行环境和 Host 日志' };
|
package/lib/oauth.js
CHANGED
|
@@ -140,18 +140,48 @@ export async function discoverProtectedResource(streamUrl, timeoutMs) {
|
|
|
140
140
|
|
|
141
141
|
/* ─────────────────────────── 阶段二:OAuth Server Metadata ─────────────────────────── */
|
|
142
142
|
|
|
143
|
+
function serverMetadataUrls(issuer, suffix) {
|
|
144
|
+
const url = new URL(issuer);
|
|
145
|
+
if (url.search || url.hash || url.username || url.password) {
|
|
146
|
+
throw new OAuthError('invalid_metadata', 'OAuth issuer must not contain query, fragment or credentials');
|
|
147
|
+
}
|
|
148
|
+
const path = url.pathname.replace(/\/+$/, '');
|
|
149
|
+
// RFC 8414 inserts before the issuer path; retain suffix-form compatibility.
|
|
150
|
+
return [...new Set([
|
|
151
|
+
`${url.origin}/.well-known/${suffix}${path}`,
|
|
152
|
+
`${issuer.replace(/\/+$/, '')}/.well-known/${suffix}`,
|
|
153
|
+
])];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function validateServerMetadata(body, issuer) {
|
|
157
|
+
if (!body || typeof body !== 'object' || Array.isArray(body) || body.issuer !== issuer) {
|
|
158
|
+
throw new OAuthError('invalid_metadata', 'OAuth metadata issuer must exactly match the requested issuer');
|
|
159
|
+
}
|
|
160
|
+
return body;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function fetchServerMetadata(issuer, suffix, timeoutMs) {
|
|
164
|
+
const urls = serverMetadataUrls(issuer, suffix);
|
|
165
|
+
for (let index = 0; index < urls.length; index++) {
|
|
166
|
+
try {
|
|
167
|
+
return validateServerMetadata(await fetchJson(urls[index], { method: 'GET' }, timeoutMs), issuer);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
// Do not mask issuer mismatches, access denial, rate limits or outages.
|
|
170
|
+
if (index === urls.length - 1 || ![404, 405].includes(error.httpStatus)) throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
143
175
|
export async function discoverServerMetadata(issuer, timeoutMs) {
|
|
144
176
|
assertSafeUrl(issuer);
|
|
145
|
-
const
|
|
146
|
-
const oauthUrl = `${base}/.well-known/oauth-authorization-server`;
|
|
147
|
-
const oauthBody = await fetchJson(oauthUrl, { method: 'GET' }, timeoutMs);
|
|
177
|
+
const oauthBody = await fetchServerMetadata(issuer, 'oauth-authorization-server', timeoutMs);
|
|
148
178
|
|
|
149
179
|
// 一些兼容 OIDC 的服务会把 DCR / token revoke 端点只发布在
|
|
150
180
|
// openid-configuration 中。优先使用 RFC 8414 元数据,仅在扩展端点缺失时合并 OIDC 元数据。
|
|
151
181
|
let oidcBody = {};
|
|
152
182
|
if (!oauthBody.registration_endpoint || !oauthBody.revocation_endpoint) {
|
|
153
183
|
try {
|
|
154
|
-
oidcBody = await
|
|
184
|
+
oidcBody = await fetchServerMetadata(issuer, 'openid-configuration', timeoutMs);
|
|
155
185
|
} catch {
|
|
156
186
|
// OIDC 发现是可选兼容路径;OAuth-only 服务不应因此失败。
|
|
157
187
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mcp-connector",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.50",
|
|
4
4
|
"description": "DeepSeek Harness MCP Connector: connect servers, search tools across active connections, and troubleshoot discovery. Includes a continuously updated catalog; supports OAuth 2.0 PKCE, API keys, Streamable HTTP/stdio, and mcpServers JSON import.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
package/ui/index.html
CHANGED
|
@@ -1331,11 +1331,12 @@
|
|
|
1331
1331
|
function guidanceFor(message) {
|
|
1332
1332
|
const text = String(message || '未知错误');
|
|
1333
1333
|
const withAdvice = (advice) => `${text.replace(/[。!?.!?\s]+$/, '')}。${advice}`;
|
|
1334
|
+
if (/CLI|OAuth|待授权|管理员|本地 MCP/i.test(text)) return text;
|
|
1334
1335
|
if (/private-network HTTP requires explicit confirmation/i.test(text)) return withAdvice('如目标是可信的局域网私有 IP,请勾选明文 HTTP 风险确认;请勿对公网或不可信服务开启。');
|
|
1335
1336
|
if (/protocol not allowed|only https/i.test(text)) return withAdvice('公网地址必须使用 HTTPS;HTTP 只允许回环地址,或经显式确认的私有 IP 字面量。');
|
|
1336
1337
|
if (/JSON/i.test(text)) return withAdvice('请检查引号、逗号,并确认顶层是 mcpServers 或 connections。');
|
|
1337
1338
|
if (/401|403|token|凭据|api.?key/i.test(text)) return withAdvice('请核对鉴权方式、Token/API Key 是否有效以及 Header 名。');
|
|
1338
|
-
if (/timeout|timed out|fetch
|
|
1339
|
+
if (/timeout|timed out|fetch|network|ECONN|ENOTFOUND/i.test(text)) return withAdvice('请检查网络、端点是否可达以及 MCP URL 是否完整。');
|
|
1339
1340
|
return text;
|
|
1340
1341
|
}
|
|
1341
1342
|
|
|
@@ -1418,7 +1419,7 @@
|
|
|
1418
1419
|
if (!connector) return;
|
|
1419
1420
|
openModal(`连接 ${connector.name}`, `
|
|
1420
1421
|
<input id="scope-connector-id" type="hidden" value="${esc(connectorId)}" />
|
|
1421
|
-
<div class="form-note">${connectionLabels(connector).map(esc).join(' · ')}${connector.credentialDescription ? `<p>${esc(connector.credentialDescription)}</p>` : ''}${connectorId === 'dingtalk' ? '<p
|
|
1422
|
+
<div class="form-note">${connectionLabels(connector).map(esc).join(' · ')}${connector.credentialDescription ? `<p>${esc(connector.credentialDescription)}</p>` : ''}${connectorId === 'dingtalk' ? '<p>企业尚未开放 CLI 或您无法取得授权?可以取消,等待管理员处理,无需反复登录或重启。一键授权也不能绕过企业政策。本版本不会自动发起登录;工具注册不代表已获得全部业务权限。</p>' : ''}</div>
|
|
1422
1423
|
<div class="form-note"><strong>选择连接影响范围</strong>连接配置与凭据仍只保存一份;项目范围会由 Host 在工具可见性和执行层强制隔离。</div>
|
|
1423
1424
|
${scopeFieldHtml()}
|
|
1424
1425
|
<div class="inline-error" id="modal-error" tabindex="-1"></div>
|