dsh-mcp-connector 0.2.19 → 0.2.20

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 CHANGED
@@ -4,6 +4,23 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.2.20] - 2026-08-25
8
+
9
+ ### Changed
10
+
11
+ - 默认公共 Registry 改用 jsDelivr CDN,并在主源请求失败时按顺序回退到 GitHub Raw,提升不同网络环境下远程市场目录的可用性。
12
+ - DSH Bundle 补丁与插件默认配置统一使用 jsDelivr 主源;用户显式配置的自定义目录地址保持单一来源,不会被公共备用源覆盖。
13
+
14
+ ### Fixed
15
+
16
+ - 主源与备用源分别处理缓存协商;备用源不会复用或写入主源 ETag,避免跨源缓存标识导致错误的 `304 Not Modified`。
17
+ - 目录主源失败时记录逐源告警,并在备用源成功后继续刷新市场,避免一次网络故障导致远程目录完全不可用。
18
+
19
+ ### Verification
20
+
21
+ - 插件 98 项自动测试、lint、npm 发布文件白名单/敏感内容扫描和 `git diff --check` 通过。
22
+ - 新增主源失败回退、主源 `304` 不访问备用源、默认配置与 Bundle 配置一致,以及自定义目录不启用公共回退的回归测试。
23
+
7
24
  ## [0.2.19] - 2026-08-24
8
25
 
9
26
  ### Fixed
@@ -331,7 +348,8 @@
331
348
  - 外部 URL 与导入 Header 执行安全校验。
332
349
  - iframe 消息校验同源和消息来源。
333
350
 
334
- [Unreleased]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.19...HEAD
351
+ [Unreleased]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.20...HEAD
352
+ [0.2.20]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.19...v0.2.20
335
353
  [0.2.19]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.18...v0.2.19
336
354
  [0.2.18]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.17...v0.2.18
337
355
  [0.2.17]: https://github.com/duhu2000/dsh-mcp-connector/compare/v0.2.16...v0.2.17
package/README.en.md CHANGED
@@ -71,7 +71,7 @@ The detailed [Chinese user guide](docs/USER-GUIDE.md) covers category browsing,
71
71
 
72
72
  ## Connector catalog
73
73
 
74
- The package contains a bundled fallback catalog. By default, it refreshes from the public [dsh-mcp-connector-registry](https://github.com/duhu2000/dsh-mcp-connector-registry); cached or bundled data remains available if the remote registry cannot be reached.
74
+ The package contains a bundled fallback catalog. By default, it refreshes the public [dsh-mcp-connector-registry](https://github.com/duhu2000/dsh-mcp-connector-registry) through jsDelivr, then tries GitHub raw if the primary source fails; cached or bundled data remains available if neither remote source can be reached. jsDelivr branch URLs can lag behind a newly merged registry commit, so new cards may not appear immediately.
75
75
 
76
76
  The public connector registration process and descriptor requirements are documented in [docs/MARKET-REGISTRATION.md](docs/MARKET-REGISTRATION.md).
77
77
  The stdio architecture, passthrough boundary, and security constraints are documented in [docs/STDIO-SUPPORT.md](docs/STDIO-SUPPORT.md).
@@ -84,14 +84,14 @@ The default bundle configuration is in `cordis.patch.yml`:
84
84
  - id: mcp-connector
85
85
  name: dsh-mcp-connector
86
86
  config:
87
- catalogUrl: 'https://raw.githubusercontent.com/duhu2000/dsh-mcp-connector-registry/main/catalog.json'
87
+ catalogUrl: 'https://cdn.jsdelivr.net/gh/duhu2000/dsh-mcp-connector-registry@main/catalog.json'
88
88
  persistSecrets: true
89
89
  entryPrefix: mcp
90
90
  refreshSkewMs: 300000
91
91
  openBrowser: true
92
92
  ```
93
93
 
94
- Set `catalogUrl` to an empty string for an explicitly offline/private setup.
94
+ Set `catalogUrl` to an empty string for an explicitly offline/private setup. A custom non-default URL is used as-is and does not fall back to the public registry.
95
95
 
96
96
  ## Development and release checks
97
97
 
@@ -105,7 +105,7 @@ npm run dev:ui
105
105
 
106
106
  `npm run check` performs syntax checks, automated tests, and an npm package allowlist/sensitive-content audit. `npm run market:check` tracks the external DSH marketplace PR and live directory. 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`.
107
107
 
108
- The current public version is [`dsh-mcp-connector@0.2.19`](https://www.npmjs.com/package/dsh-mcp-connector), with [GitHub Release v0.2.19](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.19).
108
+ The current public version is [`dsh-mcp-connector@0.2.20`](https://www.npmjs.com/package/dsh-mcp-connector), with [GitHub Release v0.2.20](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.20).
109
109
 
110
110
  See [CHANGELOG.md](CHANGELOG.md) for version history and [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md) for the Desktop release checklist.
111
111
 
package/README.md CHANGED
@@ -79,14 +79,14 @@ Bundle 默认配置位于 `cordis.patch.yml`:
79
79
  - id: mcp-connector
80
80
  name: dsh-mcp-connector
81
81
  config:
82
- catalogUrl: 'https://raw.githubusercontent.com/duhu2000/dsh-mcp-connector-registry/main/catalog.json'
82
+ catalogUrl: 'https://cdn.jsdelivr.net/gh/duhu2000/dsh-mcp-connector-registry@main/catalog.json'
83
83
  persistSecrets: true
84
84
  entryPrefix: mcp
85
85
  refreshSkewMs: 300000
86
86
  openBrowser: true
87
87
  ```
88
88
 
89
- `catalogUrl` 默认指向公共 [dsh-mcp-connector-registry](https://github.com/duhu2000/dsh-mcp-connector-registry),支持 ETag/TTL 缓存;拉取失败时继续使用上次缓存或随包内置目录。需要离线/私有模式时可将其显式设为空字符串。
89
+ `catalogUrl` 默认通过 jsDelivr CDN 读取公共 [dsh-mcp-connector-registry](https://github.com/duhu2000/dsh-mcp-connector-registry),支持 ETag/TTL 缓存;主源失败时自动尝试 GitHub raw 备用源,再回退到上次缓存或随包内置目录。jsDelivr 的分支 URL 可能存在缓存延迟,因此 Registry 合并后的新卡片不保证秒级出现。需要离线/私有模式时可将 `catalogUrl` 显式设为空字符串;显式配置其他目录 URL 时不会自动切换到公共备用源。
90
90
 
91
91
  ## 开发与发布门禁
92
92
 
@@ -100,7 +100,7 @@ npm run dev:ui
100
100
 
101
101
  `check` 执行语法检查、自动测试和 npm 发布包白名单校验;`market:check` 检查外部 DSH 市场 PR 与线上目录;`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`。
102
102
 
103
- 当前公开版本为 [`dsh-mcp-connector@0.2.19`](https://www.npmjs.com/package/dsh-mcp-connector),对应 [GitHub Release v0.2.19](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.19)。
103
+ 当前公开版本为 [`dsh-mcp-connector@0.2.20`](https://www.npmjs.com/package/dsh-mcp-connector),对应 [GitHub Release v0.2.20](https://github.com/duhu2000/dsh-mcp-connector/releases/tag/v0.2.20)。
104
104
 
105
105
  版本能力与变更记录见 [CHANGELOG.md](CHANGELOG.md)。
106
106
  Desktop 发版回归见 [docs/DESKTOP-E2E.md](docs/DESKTOP-E2E.md)。
package/cordis.patch.yml CHANGED
@@ -5,7 +5,7 @@
5
5
  - id: mcp-connector
6
6
  name: 'dsh-mcp-connector'
7
7
  config:
8
- catalogUrl: 'https://raw.githubusercontent.com/duhu2000/dsh-mcp-connector-registry/main/catalog.json'
8
+ catalogUrl: 'https://cdn.jsdelivr.net/gh/duhu2000/dsh-mcp-connector-registry@main/catalog.json'
9
9
  persistSecrets: true
10
10
  entryPrefix: 'mcp'
11
11
  refreshSkewMs: 300000
@@ -25,7 +25,7 @@ Bearer/API Key 型连接器可在卡片上点「配置」,一次填写凭据
25
25
  3. 提交 PR。CI 会检查 Schema、重复 id/serverName、密钥、URL、MCP initialize、OAuth 元数据和图标。
26
26
  4. 合并后 CI 重建根目录 `catalog.json`;客户端点击“刷新”后可见,无需重新发布 `dsh-mcp-connector` npm 包。
27
27
 
28
- 插件默认读取 `https://raw.githubusercontent.com/duhu2000/dsh-mcp-connector-registry/main/catalog.json`;远程不可用时自动回退上次缓存或随包内置目录。
28
+ 插件默认通过 `https://cdn.jsdelivr.net/gh/duhu2000/dsh-mcp-connector-registry@main/catalog.json` 读取目录;主源失败时依次尝试 GitHub raw 备用源、上次缓存和随包内置目录。jsDelivr 的分支 URL 存在缓存延迟,合并后的新卡片可能不会秒级出现。
29
29
 
30
30
  ## 4. DSH 外部插件市场验收
31
31
 
package/lib/catalog.js CHANGED
@@ -134,6 +134,28 @@ export async function fetchRemoteCatalog(catalogUrl, { requestTimeoutMs, etag }
134
134
  }
135
135
  }
136
136
 
137
+ /**
138
+ * 按顺序拉取远程 registry。仅主源使用缓存 ETag;备用源总是全量拉取,
139
+ * 且不返回备用源 ETag,避免下一次请求把它误发给主源。
140
+ */
141
+ export async function fetchRemoteCatalogWithFallback(catalogUrl, fallbackUrls = [], opts = {}) {
142
+ const urls = [catalogUrl, ...fallbackUrls].filter(Boolean);
143
+ let lastError;
144
+ for (let index = 0; index < urls.length; index += 1) {
145
+ try {
146
+ const fetched = await fetchRemoteCatalog(urls[index], {
147
+ requestTimeoutMs: opts.requestTimeoutMs,
148
+ etag: index === 0 ? opts.etag : undefined,
149
+ });
150
+ return index === 0 ? fetched : { ...fetched, etag: undefined };
151
+ } catch (error) {
152
+ lastError = error;
153
+ opts.onSourceError?.({ index, error });
154
+ }
155
+ }
156
+ throw lastError ?? new Error('catalog fetch failed for all sources');
157
+ }
158
+
137
159
  /** 合并多层来源(sources 为低→高优先级的 descriptor 数组),再应用本地覆盖。 */
138
160
  export function mergeCatalog(sources, overrides = new Map()) {
139
161
  const merged = new Map();
package/lib/constants.js CHANGED
@@ -16,9 +16,16 @@ export const JSON_CONNECTOR_ID = '__json__';
16
16
  /** 保留 id 前缀:社区 PR 注册表拒绝占用 */
17
17
  export const RESERVED_ID_PREFIXES = ['qcc-', 'dsh-', 'mcp-connector-'];
18
18
 
19
- /** 请求 / 刷新默认值 */
19
+ /** 远程 registry 主目录源(jsDelivr CDN,国内网络更稳定) */
20
20
  export const DEFAULT_CATALOG_URL =
21
- 'https://raw.githubusercontent.com/duhu2000/dsh-mcp-connector-registry/main/catalog.json';
21
+ 'https://cdn.jsdelivr.net/gh/duhu2000/dsh-mcp-connector-registry@main/catalog.json';
22
+
23
+ /** 默认目录源失败时依次尝试;仅用于未自定义 catalogUrl 的配置。 */
24
+ export const DEFAULT_CATALOG_FALLBACK_URLS = [
25
+ 'https://raw.githubusercontent.com/duhu2000/dsh-mcp-connector-registry/main/catalog.json',
26
+ ];
27
+
28
+ /** 请求 / 刷新默认值 */
22
29
  export const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
23
30
  export const DEFAULT_REFRESH_SKEW_MS = 300_000;
24
31
  export const DEFAULT_CATALOG_TTL_MS = 3_600_000;
package/lib/index.js CHANGED
@@ -13,13 +13,14 @@ import z from '@deepseek-ai/schemastery';
13
13
  import {
14
14
  DEFAULT_ACCOUNT,
15
15
  DEFAULT_CATALOG_URL,
16
+ DEFAULT_CATALOG_FALLBACK_URLS,
16
17
  DEFAULT_REQUEST_TIMEOUT_MS,
17
18
  DEFAULT_REFRESH_SKEW_MS,
18
19
  DEFAULT_CATALOG_TTL_MS,
19
20
  } from './constants.js';
20
21
  import { defineConnectorDomain, ConnectionStore, GrantStore, CatalogStore } from './stores.js';
21
22
  import { normalizeConnectorDescriptor, normalizeConnectionRecord } from './schema.js';
22
- import { loadBundledCatalog, fetchRemoteCatalog, mergeCatalog, listCatalog, auditDescriptor, auditRawDescriptor, readLimitedJson } from './catalog.js';
23
+ import { loadBundledCatalog, fetchRemoteCatalogWithFallback, mergeCatalog, listCatalog, auditDescriptor, auditRawDescriptor, readLimitedJson } from './catalog.js';
23
24
  import { oauthAuthorize } from './connectors/oauth-connector.js';
24
25
  import { buildManualRecord } from './connectors/manual-connector.js';
25
26
  import { normalizeJsonImport } from './connectors/json-connector.js';
@@ -91,6 +92,15 @@ export async function apply(ctx, config) {
91
92
  state.merged = mergeCatalog(sources, state.overrides);
92
93
  }
93
94
 
95
+ function catalogFallbackUrls() {
96
+ return config.catalogUrl === DEFAULT_CATALOG_URL ? DEFAULT_CATALOG_FALLBACK_URLS : [];
97
+ }
98
+
99
+ function warnCatalogSourceFailure({ index, error }) {
100
+ const source = index === 0 ? 'primary' : `fallback ${index}`;
101
+ logger.warn(`catalog fetch ${source} failed: ${error instanceof Error ? error.message : String(error)}`);
102
+ }
103
+
94
104
  async function loadCatalog() {
95
105
  const dynamicRec = await catalogStore.getDynamic().catch(() => null);
96
106
  if (dynamicRec?.connectors) {
@@ -116,10 +126,15 @@ export async function apply(ctx, config) {
116
126
  if (config.catalogUrl) {
117
127
  const cached = await catalogStore.getRemote().catch(() => null);
118
128
  try {
119
- const fetched = await fetchRemoteCatalog(config.catalogUrl, {
120
- requestTimeoutMs: config.requestTimeoutMs,
121
- etag: cached?.etag,
122
- });
129
+ const fetched = await fetchRemoteCatalogWithFallback(
130
+ config.catalogUrl,
131
+ catalogFallbackUrls(),
132
+ {
133
+ requestTimeoutMs: config.requestTimeoutMs,
134
+ etag: cached?.etag,
135
+ onSourceError: warnCatalogSourceFailure,
136
+ },
137
+ );
123
138
  if (fetched.notModified) {
124
139
  state.remoteConnectors = cached?.connectors ?? [];
125
140
  } else {
@@ -816,7 +831,14 @@ export async function apply(ctx, config) {
816
831
  return { ok: true, message: `市场已刷新,共 ${visibleCount} 个连接器` };
817
832
  }
818
833
  try {
819
- const fetched = await fetchRemoteCatalog(config.catalogUrl, { requestTimeoutMs: config.requestTimeoutMs });
834
+ const fetched = await fetchRemoteCatalogWithFallback(
835
+ config.catalogUrl,
836
+ catalogFallbackUrls(),
837
+ {
838
+ requestTimeoutMs: config.requestTimeoutMs,
839
+ onSourceError: warnCatalogSourceFailure,
840
+ },
841
+ );
820
842
  state.remoteConnectors = fetched.connectors;
821
843
  await catalogStore.putRemote({ etag: fetched.etag, connectors: fetched.connectors }).catch(() => {});
822
844
  await recomputeMerged();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mcp-connector",
3
- "version": "0.2.19",
3
+ "version": "0.2.20",
4
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",