dsh-mcp 1.11.0 → 1.12.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.en.md CHANGED
@@ -7,6 +7,34 @@ All notable changes to this project are documented in this file.
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
8
8
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
+ ## Compatibility
11
+
12
+ - **Supported DSH version**: **dsh `0.1.6-alpha.2`** — developed and verified on `0.1.6-alpha.2` (declared in `package.json` → `dsh.supported`). When the DSH and plugin versions do not match, the Settings page reports a diagnosis (check the `cordis.patch.yml` row → restart `dsh web` → hard-refresh → upgrade both sides).
13
+ - **Release convention**: every version entry states `- **Supported DSH version**: dsh <version>`, mirrored in the GitHub Release notes.
14
+
15
+ ## [1.12.0] - 2026-09-18
16
+
17
+ - **Supported DSH version**: **dsh `0.1.6-alpha.2`**
18
+
19
+ ### Changed
20
+
21
+ - **Migrated to the official split MCP SDK 2.0 packages**: since DSH `0.1.6-alpha.1`, the in-box `dsh-mcp-client` derives from `@modelcontextprotocol/{client,server,node}@2.0.0` instead of the monolithic `@modelcontextprotocol/sdk@1.x`, and the old package is no longer a DSH dependency. This plugin migrates with it (`lib/transport.js`, `lib/probe.js`, `lib/mcp-client.js`, `lib/oauth.js`) and declares `@modelcontextprotocol/client@2.0.0` and `zod@^4.2.0` explicitly in `package.json` instead of relying on the host profile's dependency hoisting — without this, DSH `0.1.6-alpha.1` fails the plugin load with `ERR_MODULE_NOT_FOUND: Cannot find package '@modelcontextprotocol/sdk'`, taking the Settings page and tool search down with it
22
+ - **Adapted to two breaking changes in SDK 2.0**: `Client` options no longer accept `authProvider` (the OAuth provider now travels on the **transport**; this plugin's `createTransport` already reads `config.authProvider` and passes it to the transport, so the OAuth flow is unchanged), and `setNotificationHandler`'s first argument is now a **method name** rather than a schema, so the tool-list-changed notification registers as `notifications/tools/list_changed`
23
+ - **Result schemas now come from the new package's `specTypeSchemas`**: `ListToolsResultSchema` → `specTypeSchemas.ListToolsResult`; `ToolListChangedNotificationSchema` is no longer needed since notifications register by method name
24
+ - **Regenerated the client half for the Typert `create()` contract**: the `@deepseek-ai/dsh-typert-generator` output in `src/client/remote-contribution.js` carried only `schema: TypertSchema`, but since DSH `0.1.6-alpha.1` that field is `create: () => TypertSchema` and the registry calls it unconditionally (`record.value ??= record.create()`) — so the Settings page's Remote descriptors threw `create is not a function` on mount and the whole MCP settings page failed. Regenerated with the current generator (all 21 strict descriptors now carry `create`) and rebuilt `lib/client.js` to match. This stayed hidden because the long-running host had been on `0.1.5-rc.1` since Sep 14, whose contract was still `schema`
25
+
26
+ ### Fixed
27
+
28
+ - **The "do not open a browser from a tool call" OAuth guard works again**: `startConnection`'s `opts` never carried `url` or `authProvider`, so the branch that should return an authorization link when a stored token is missing or expired never ran — the SDK opened a browser mid-tool-call instead (a single session could open several tabs). `opts` now carries both fields, restoring the documented behavior
29
+
30
+ ## [1.11.1] - 2026-09-13
31
+
32
+ ### Fixed
33
+
34
+ - **OAuth is now an explicit switch instead of a header guess (no more repeated browser authorizations)**: the previous rule was "streamable-http without an `Authorization` header gets an OAuth provider", so servers that authenticate with static tokens or custom headers (`x-bbzai-mcp-token`, `X-Mcp-Token`, `Private-Token`) were treated as OAuth servers. When such a server answered 401 (a stale token, a gateway challenge), the SDK asked for authorization, the plugin opened the browser and started its loopback callback (`127.0.0.1:<port>/callback`) — a flow that could never satisfy those servers, so every reconnect or remount opened the browser again. A stored server now has an **`oauth: true`** switch (the form's "Use OAuth authorization" checkbox, off by default; taking over a declaration without an `Authorization` header ticks it, and it stays editable)
35
+ - **Upgrade migration**: servers that already hold OAuth credentials (a token or a client registration) keep `oauth: true`, everything else is turned off — so servers that genuinely need OAuth keep working while the misdetected ones stop opening the browser
36
+ - **Automatic-authorization circuit breaker**: the plugin opens the browser **at most once per server per process**; later automatic attempts return the authorization-link error instead (the 1.8.0 behaviour), and "Test connection" can still start an authorization on demand
37
+
10
38
  ## [1.11.0] - 2026-09-13
11
39
 
12
40
  ### Added
package/CHANGELOG.md CHANGED
@@ -7,6 +7,34 @@
7
7
  格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
8
8
  版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
9
9
 
10
+ ## 兼容性
11
+
12
+ - **支持版本**:**dsh `0.1.6-alpha.2`** —— 本插件在 `0.1.6-alpha.2` 上开发与验证(`package.json` → `dsh.supported` 同步声明)。DSH 与插件版本不匹配时,设置页会给出排查诊断(核对 `cordis.patch.yml` 注册行 → 重启 `dsh web` → 硬刷新 → 同步升级)。
13
+ - **发版约定**:每个版本条目都要声明 `- **支持版本**:dsh <版本>`,并在 GitHub Release notes 中同步。
14
+
15
+ ## [1.12.0] - 2026-09-18
16
+
17
+ - **支持版本**:**dsh `0.1.6-alpha.2`**
18
+
19
+ ### 变更
20
+
21
+ - **迁移到官方 MCP SDK 2.0 拆分包**:DSH `0.1.6-alpha.1` 起,框架内的 `dsh-mcp-client` 已从单体包 `@modelcontextprotocol/sdk@1.x` 迁到 `@modelcontextprotocol/{client,server,node}@2.0.0`,旧包不再是 DSH 的依赖。本插件同步迁移 `lib/transport.js`、`lib/probe.js`、`lib/mcp-client.js`、`lib/oauth.js`,并在 `package.json` 显式声明 `@modelcontextprotocol/client@2.0.0` 与 `zod@^4.2.0`,不再依赖宿主 profile 的依赖提升——否则在 DSH `0.1.6-alpha.1` 上会以 `ERR_MODULE_NOT_FOUND: Cannot find package '@modelcontextprotocol/sdk'` 加载失败,设置页与 tool search 一并不可用
22
+ - **适配 SDK 2.0 的两处破坏性变更**:其一,`Client` 选项不再接受 `authProvider`,OAuth provider 改由 **transport** 携带(本插件的 `createTransport` 一直从 `config.authProvider` 读取并传给 transport,因此 OAuth 流程行为不变);其二,`setNotificationHandler` 的第一个参数由 schema 改为**方法名**,工具列表变更通知改用 `notifications/tools/list_changed`
23
+ - **结果 schema 改从新包的 `specTypeSchemas` 取**:`ListToolsResultSchema` → `specTypeSchemas.ListToolsResult`;`ToolListChangedNotificationSchema` 不再需要(通知按方法名注册)
24
+ - **重新生成客户端半以适配 Typert `create()` 契约**:`@deepseek-ai/dsh-typert-generator` 生成的 `src/client/remote-contribution.js` 此前只携带 `schema: TypertSchema`,而 DSH 自 `0.1.6-alpha.1` 起把该字段改成 `create: () => TypertSchema`,registry 以 `record.value ??= record.create()` **无条件调用**它——于是设置页的 Remote 描述符在挂载时抛 `create is not a function`,整块 MCP 设置页失效。现已用当前生成器重新生成(21 个 strict 描述符全部补上 `create`)并重建 `lib/client.js`。此前一直没暴露,是因为常驻宿主自 9/14 起跑的是 `0.1.5-rc.1`(当时契约仍是 `schema`)
25
+
26
+ ### 修复
27
+
28
+ - **OAuth 工具调用的「不弹浏览器」保护恢复生效**:`startConnection` 的 `opts` 一直缺少 `url` 与 `authProvider` 两个字段,于是「已存 token 缺失/过期时不要从工具调用里打开浏览器,改为返回授权链接」这段判断从未触发——实际行为是让 SDK 在工具调用中弹浏览器(同一会话可能连开多个标签)。现在在 `opts` 中补上这两项,行为回到代码注释所述
29
+
30
+ ## [1.11.1] - 2026-09-13
31
+
32
+ ### 修复
33
+
34
+ - **OAuth 改为显式开关,不再凭请求头猜测(停止反复弹出浏览器授权)**:此前规则是"streamable-http 且没有 `Authorization` 头 → 挂 OAuth provider",于是用静态 token/自定义头(`x-bbzai-mcp-token`、`X-Mcp-Token`、`Private-Token`)认证的服务器也被当成 OAuth 服务器。这类服务器返回 401 时(token 失效、网关挑战等),SDK 会要求授权 → 插件打开浏览器并起回环回调(`127.0.0.1:<port>/callback`),而该流程永远无法让它们通过 → 每次重连/重挂载就再弹一次。现在服务器配置新增 **`oauth: true`** 开关(设置页勾选「使用 OAuth 授权」,默认关;接管声明时若该声明没有 `Authorization` 头则默认勾上,之后仍可修改)
35
+ - **升级自动迁移**:已存在 OAuth 凭据(token 或 client 注册记录)的服务器会保留 `oauth: true`,其余一律关闭——因此**现有真正需要 OAuth 的服务器不受影响,误判的那批立刻停止弹窗**
36
+ - **自动弹窗熔断**:同一服务器**每个进程最多自动打开一次**浏览器授权;之后的自动尝试不再弹窗,改为返回带授权链接的错误(沿用 1.8.0 的能力),手动点「测试连接」仍可随时发起授权
37
+
10
38
  ## [1.11.0] - 2026-09-13
11
39
 
12
40
  ### 新增
package/README.en.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [![dshfind](https://dshfind.com/api/badge/ArvinQi/dsh-mcp?lang=en)](https://dshfind.com/en/plugins/ArvinQi/dsh-mcp?ref=badge)
4
4
 
5
+ > **Supported DSH version**: `dsh 0.1.6-alpha.2` — developed and verified on `0.1.6-alpha.2`, declared in `package.json` → `dsh.supported`. When the DSH and plugin versions do not match, the Settings page shows a diagnosis (check the registration row → restart `dsh web` → hard-refresh → upgrade both sides).
6
+
5
7
  ![Settings preview](static/snapshot.en.webp)
6
8
 
7
9
  ## Why dsh-mcp?
@@ -78,6 +80,7 @@ Since 1.11.0 those declarations appear in Settings → MCP, badged "cordis decla
78
80
  - **Imported into storages as mirrors**: at startup and on every refresh the declarations are imported into the storage domain (id `cordis:<rowId>`, with `origin/declaredIn/declaredRowId`), **import only** — a row created in the plugin is never overwritten; a mirror whose declaration disappeared is **removed automatically** (a mirror is only a copy of a declaration, so an ownerless one must not pile up); a declaration containing a `!!js` expression is skipped and flagged with the reason (its value only resolves inside the Loader). Mirrors are never mounted — the composition owns the mount.
79
81
  - **Take over / give back**: clicking "Take over" makes the plugin write an id-targeted `disabled: true` into its own **managed block** in `cordis.patch.yml` (a `.dsh-mcp.bak` backup is taken before the first write; atomic, idempotent, reversible, confined to that block). The declaration releases the `serverName` and the plugin mounts its own row instead — enabling **OAuth authorization, managed credentials, `${VAR}` header substitution and connection tests**. A hard mount failure rolls back automatically (the block is removed and the row returns to mirror state) and reports why; "Give back" hands the mount back to the declaration and deletes the managed row. A declaration without an explicit `id`, or one containing a `!!js` expression, cannot be taken over.
80
82
  - **OAuth / placeholder limit (`needsPlugin`)**: a declaration can carry neither an OAuth provider nor `${VAR}` / bare-name placeholder substitution — only the plugin does those. Such a declaration can therefore only work while the plugin owns the mount: the page reports it as **failed with the reason** (not "connecting"), "Give back" makes its tools disappear (the confirmation and the result warning both say so), and taking it over again restores them.
83
+ - **OAuth is an explicit switch (since 1.11.1)**: an OAuth provider is attached only when the server sets `oauth: true` (the form's "Use OAuth authorization" checkbox, off by default). **Do not tick it for servers that authenticate with static tokens or custom headers** — otherwise a 401 is treated as an OAuth challenge and the plugin keeps opening the browser (with its `127.0.0.1:<port>/callback` loopback listener). The plugin also opens the browser **at most once per server per process**; later automatic attempts return the authorization-link error, and "Test connection" can still start one on demand. Upgrading migrates automatically from existing OAuth credentials, so servers that genuinely need OAuth are unaffected.
81
84
  - **Safe degradation when the hot reload does not commit**: if the native tools have not unregistered within 5 seconds of writing the disable block (that profile's patch hot reload did not commit — a sibling entry failing its re-apply rolls the whole generation back), the plugin **registers the takeover without mounting** (the managed row is marked `pendingTakeover`); the next dsh start mounts it, and "Give back" reports the same need for a restart. A mount is also always skipped when same-named `mcp__<server>__` tools already exist without a local mount, so the plugin never fights a still-mounted declaration for one name.
82
85
  - **Tool search covers them**: search/hot injection and per-tool switches work by `mcp__` prefix across the whole tool set, so declared tools need no extra configuration.
83
86
 
@@ -296,6 +299,9 @@ Check in order:
296
299
  reinstalling (link install).
297
300
  - Configuration changes (bundle additions/removals, new plugin rows) require restarting `dsh web` to enter
298
301
  the client roster.
302
+ - **Every release must declare its supported DSH version**: add a
303
+ `- **Supported DSH version**: dsh <version>` line to the CHANGELOG entry and the GitHub Release notes, and
304
+ update `package.json` → `dsh.supported` plus the "Supported DSH version" note at the top of this README.
299
305
 
300
306
  ## Changelog
301
307
 
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  [![dshfind](https://dshfind.com/api/badge/ArvinQi/dsh-mcp?lang=zh)](https://dshfind.com/zh/plugins/ArvinQi/dsh-mcp?ref=badge)
6
6
 
7
+ > **支持版本**:`dsh 0.1.6-alpha.2` —— 本插件在 `0.1.6-alpha.2` 上开发与验证,`package.json` → `dsh.supported` 同步声明。DSH 与插件两侧版本不匹配时,设置页会给出排查诊断(检查注册行 → 重启 `dsh web` → 硬刷新 → 同步升级)。
8
+
7
9
  ![设置页预览](static/snapshot.webp)
8
10
 
9
11
  ## 为什么用 dsh-mcp?
@@ -76,6 +78,11 @@ DSH 原生支持在组合里直接声明 MCP 服务器:**一行一台**,`nam
76
78
  `${VAR}`/裸变量名占位符——这两件事只有插件会做。因此这类声明**只能由插件挂载**:页面会把它们标为
77
79
  **挂载失败并说明原因**(不是"连接中"),「释放」会让工具消失(确认框与结果告警都会提示),恢复只需
78
80
  重新「接管」。
81
+ - **OAuth 是显式开关(1.11.1 起)**:服务器配置里的 `oauth: true`(设置页「使用 OAuth 授权」勾选,默认关)
82
+ 才会挂 OAuth provider。**用静态 token 或自定义头认证的服务器不要勾**——否则它返回 401 时会被当成 OAuth
83
+ 挑战,插件会反复打开浏览器授权(并起 `127.0.0.1:<port>/callback` 回环回调)。另外,同一服务器**每个进程
84
+ 最多自动弹一次**浏览器,之后只返回带授权链接的错误;手动「测试连接」仍可随时发起授权。升级时会按已有
85
+ OAuth 凭据自动迁移,真正需要 OAuth 的服务器不受影响。
79
86
  - **热重载未提交时的安全降级**:写入停用块后若原生工具 5 秒内没有注销(该 profile 的 patch 热重载未提交,
80
87
  例如某个兄弟条目重建失败导致整代回滚),插件会**登记接管但暂不挂载**(管理行标记 `pendingTakeover`),
81
88
  重启 dsh 后由插件挂载;「释放」在同样情况下会提示需要重启。此外,挂载前若发现同名 `mcp__<server>__`
@@ -278,5 +285,7 @@ dsh plugin --profile web add link:<本仓库绝对路径>
278
285
  - 浏览器半部改 `src/client/*` 后重新 `node scripts/build.mjs`;host 半部改动无需重装
279
286
  (link 安装直接生效)。
280
287
  - 配置变更(bundles 增删、新插件行)需重启 `dsh web` 才进入 client roster。
288
+ - **每次发版都必须声明支持的 DSH 版本**:在 CHANGELOG 对应条目与 GitHub Release notes 里加一行
289
+ `- **支持版本**:dsh <版本>`,并同步更新 `package.json` 的 `dsh.supported` 与 README 顶部的「支持版本」。
281
290
 
282
291
  [![dshfind](https://dshfind.com/api/card/ArvinQi/dsh-mcp?lang=zh)](https://dshfind.com/zh/plugins/ArvinQi/dsh-mcp?ref=badge)
package/lib/client.js CHANGED
@@ -14577,6 +14577,7 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_list_result$schema = external_export
14577
14577
  })).readonly(),
14578
14578
  "toolCallTimeoutMs": external_exports.number().readonly(),
14579
14579
  "failOnStartupError": external_exports.boolean().readonly(),
14580
+ "oauth": external_exports.boolean().readonly().optional(),
14580
14581
  "status": external_exports.object({
14581
14582
  "phase": external_exports.union([external_exports.literal("failed"), external_exports.literal("stopped"), external_exports.literal("mounting"), external_exports.literal("live")]).readonly(),
14582
14583
  "tools": external_exports.array(external_exports.string()).readonly(),
@@ -14622,7 +14623,8 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_test_parameter_0$schema = external_e
14622
14623
  "value": external_exports.string().readonly()
14623
14624
  })).readonly(),
14624
14625
  "toolCallTimeoutMs": external_exports.number().readonly(),
14625
- "failOnStartupError": external_exports.boolean().readonly()
14626
+ "failOnStartupError": external_exports.boolean().readonly(),
14627
+ "oauth": external_exports.boolean().readonly().optional()
14626
14628
  }).readonly(),
14627
14629
  "env": external_exports.array(external_exports.object({
14628
14630
  "name": external_exports.string().readonly(),
@@ -14659,7 +14661,8 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_parameter_0$schema = external
14659
14661
  "value": external_exports.string().readonly()
14660
14662
  })).readonly(),
14661
14663
  "toolCallTimeoutMs": external_exports.number().readonly(),
14662
- "failOnStartupError": external_exports.boolean().readonly()
14664
+ "failOnStartupError": external_exports.boolean().readonly(),
14665
+ "oauth": external_exports.boolean().readonly().optional()
14663
14666
  }).readonly(),
14664
14667
  "env": external_exports.array(external_exports.object({
14665
14668
  "name": external_exports.string().readonly(),
@@ -14690,6 +14693,7 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema = external_expo
14690
14693
  })).readonly(),
14691
14694
  "toolCallTimeoutMs": external_exports.number().readonly(),
14692
14695
  "failOnStartupError": external_exports.boolean().readonly(),
14696
+ "oauth": external_exports.boolean().readonly().optional(),
14693
14697
  "status": external_exports.object({
14694
14698
  "phase": external_exports.union([external_exports.literal("failed"), external_exports.literal("stopped"), external_exports.literal("mounting"), external_exports.literal("live")]).readonly(),
14695
14699
  "tools": external_exports.array(external_exports.string()).readonly(),
@@ -14731,7 +14735,8 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema = exte
14731
14735
  "value": external_exports.string().readonly()
14732
14736
  })).readonly()]).readonly().optional(),
14733
14737
  "toolCallTimeoutMs": external_exports.number().readonly().optional(),
14734
- "failOnStartupError": external_exports.boolean().readonly().optional()
14738
+ "failOnStartupError": external_exports.boolean().readonly().optional(),
14739
+ "oauth": external_exports.boolean().readonly().optional()
14735
14740
  }).readonly(),
14736
14741
  "env": external_exports.array(external_exports.object({
14737
14742
  "name": external_exports.string().readonly(),
@@ -14767,6 +14772,7 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema = external_
14767
14772
  })).readonly(),
14768
14773
  "toolCallTimeoutMs": external_exports.number().readonly(),
14769
14774
  "failOnStartupError": external_exports.boolean().readonly(),
14775
+ "oauth": external_exports.boolean().readonly().optional(),
14770
14776
  "status": external_exports.object({
14771
14777
  "phase": external_exports.union([external_exports.literal("failed"), external_exports.literal("stopped"), external_exports.literal("mounting"), external_exports.literal("live")]).readonly(),
14772
14778
  "tools": external_exports.array(external_exports.string()).readonly(),
@@ -14887,6 +14893,7 @@ var _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_result$schema = external_expor
14887
14893
  })).readonly(),
14888
14894
  "toolCallTimeoutMs": external_exports.number().readonly(),
14889
14895
  "failOnStartupError": external_exports.boolean().readonly(),
14896
+ "oauth": external_exports.boolean().readonly().optional(),
14890
14897
  "status": external_exports.object({
14891
14898
  "phase": external_exports.union([external_exports.literal("failed"), external_exports.literal("stopped"), external_exports.literal("mounting"), external_exports.literal("live")]).readonly(),
14892
14899
  "tools": external_exports.array(external_exports.string()).readonly(),
@@ -14945,14 +14952,16 @@ var TYPERT_REMOTE = {
14945
14952
  codec: {
14946
14953
  mode: "strict",
14947
14954
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerRemoveRequest",
14948
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_delete_parameter_0$schema
14955
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_delete_parameter_0$schema,
14956
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_delete_parameter_0$schema
14949
14957
  }
14950
14958
  }
14951
14959
  ],
14952
14960
  result: {
14953
14961
  mode: "strict",
14954
14962
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerRemoveResult",
14955
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_delete_result$schema
14963
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_delete_result$schema,
14964
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_delete_result$schema
14956
14965
  },
14957
14966
  sourceLocation: { "file": "packages/mcp/mcp-manager/src/index.ts", "line": 399, "column": 9 }
14958
14967
  },
@@ -14966,7 +14975,8 @@ var TYPERT_REMOTE = {
14966
14975
  result: {
14967
14976
  mode: "strict",
14968
14977
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerListResult",
14969
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_list_result$schema
14978
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_list_result$schema,
14979
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_list_result$schema
14970
14980
  },
14971
14981
  sourceLocation: { "file": "packages/mcp/mcp-manager/src/index.ts", "line": 314, "column": 9 }
14972
14982
  },
@@ -14984,14 +14994,16 @@ var TYPERT_REMOTE = {
14984
14994
  codec: {
14985
14995
  mode: "strict",
14986
14996
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerTestRequest",
14987
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_test_parameter_0$schema
14997
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_test_parameter_0$schema,
14998
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_test_parameter_0$schema
14988
14999
  }
14989
15000
  }
14990
15001
  ],
14991
15002
  result: {
14992
15003
  mode: "strict",
14993
15004
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerTestResult",
14994
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_test_result$schema
15005
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_test_result$schema,
15006
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_test_result$schema
14995
15007
  },
14996
15008
  sourceLocation: { "file": "packages/mcp/mcp-manager/src/index.ts", "line": 422, "column": 9 }
14997
15009
  },
@@ -15009,14 +15021,16 @@ var TYPERT_REMOTE = {
15009
15021
  codec: {
15010
15022
  mode: "strict",
15011
15023
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertRequest",
15012
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_parameter_0$schema
15024
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_parameter_0$schema,
15025
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_parameter_0$schema
15013
15026
  }
15014
15027
  }
15015
15028
  ],
15016
15029
  result: {
15017
15030
  mode: "strict",
15018
15031
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertResult",
15019
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema
15032
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema,
15033
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema
15020
15034
  },
15021
15035
  sourceLocation: { "file": "packages/mcp/mcp-manager/src/index.ts", "line": 329, "column": 9 }
15022
15036
  },
@@ -15034,14 +15048,16 @@ var TYPERT_REMOTE = {
15034
15048
  codec: {
15035
15049
  mode: "strict",
15036
15050
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertJsonRequest",
15037
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema
15051
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema,
15052
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema
15038
15053
  }
15039
15054
  }
15040
15055
  ],
15041
15056
  result: {
15042
15057
  mode: "strict",
15043
15058
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertJsonResult",
15044
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema
15059
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema,
15060
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema
15045
15061
  },
15046
15062
  sourceLocation: { "file": "lib/index.js" }
15047
15063
  },
@@ -15055,7 +15071,8 @@ var TYPERT_REMOTE = {
15055
15071
  result: {
15056
15072
  mode: "strict",
15057
15073
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsListResult",
15058
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsList_result$schema
15074
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsList_result$schema,
15075
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsList_result$schema
15059
15076
  },
15060
15077
  sourceLocation: { "file": "lib/index.js" }
15061
15078
  },
@@ -15073,14 +15090,16 @@ var TYPERT_REMOTE = {
15073
15090
  codec: {
15074
15091
  mode: "strict",
15075
15092
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsSetRequest",
15076
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_parameter_0$schema
15093
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_parameter_0$schema,
15094
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_parameter_0$schema
15077
15095
  }
15078
15096
  }
15079
15097
  ],
15080
15098
  result: {
15081
15099
  mode: "strict",
15082
15100
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsSetResult",
15083
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_result$schema
15101
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_result$schema,
15102
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_result$schema
15084
15103
  },
15085
15104
  sourceLocation: { "file": "lib/index.js" }
15086
15105
  },
@@ -15098,14 +15117,16 @@ var TYPERT_REMOTE = {
15098
15117
  codec: {
15099
15118
  mode: "strict",
15100
15119
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsModeRequest",
15101
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_parameter_0$schema
15120
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_parameter_0$schema,
15121
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_parameter_0$schema
15102
15122
  }
15103
15123
  }
15104
15124
  ],
15105
15125
  result: {
15106
15126
  mode: "strict",
15107
15127
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsModeResult",
15108
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_result$schema
15128
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_result$schema,
15129
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_result$schema
15109
15130
  },
15110
15131
  sourceLocation: { "file": "lib/index.js" }
15111
15132
  },
@@ -15119,7 +15140,8 @@ var TYPERT_REMOTE = {
15119
15140
  result: {
15120
15141
  mode: "strict",
15121
15142
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvListResult",
15122
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envList_result$schema
15143
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envList_result$schema,
15144
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_envList_result$schema
15123
15145
  },
15124
15146
  sourceLocation: { "file": "lib/index.js" }
15125
15147
  },
@@ -15137,14 +15159,16 @@ var TYPERT_REMOTE = {
15137
15159
  codec: {
15138
15160
  mode: "strict",
15139
15161
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvSetRequest",
15140
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_parameter_0$schema
15162
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_parameter_0$schema,
15163
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_parameter_0$schema
15141
15164
  }
15142
15165
  }
15143
15166
  ],
15144
15167
  result: {
15145
15168
  mode: "strict",
15146
15169
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvSetResult",
15147
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_result$schema
15170
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_result$schema,
15171
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_result$schema
15148
15172
  },
15149
15173
  sourceLocation: { "file": "lib/index.js" }
15150
15174
  },
@@ -15162,14 +15186,16 @@ var TYPERT_REMOTE = {
15162
15186
  codec: {
15163
15187
  mode: "strict",
15164
15188
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerAdoptRequest",
15165
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_parameter_0$schema
15189
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_parameter_0$schema,
15190
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_parameter_0$schema
15166
15191
  }
15167
15192
  }
15168
15193
  ],
15169
15194
  result: {
15170
15195
  mode: "strict",
15171
15196
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerAdoptResult",
15172
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_result$schema
15197
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_result$schema,
15198
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_result$schema
15173
15199
  },
15174
15200
  sourceLocation: { "file": "lib/index.js" }
15175
15201
  },
@@ -15187,14 +15213,16 @@ var TYPERT_REMOTE = {
15187
15213
  codec: {
15188
15214
  mode: "strict",
15189
15215
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerReleaseRequest",
15190
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_release_parameter_0$schema
15216
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_release_parameter_0$schema,
15217
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_release_parameter_0$schema
15191
15218
  }
15192
15219
  }
15193
15220
  ],
15194
15221
  result: {
15195
15222
  mode: "strict",
15196
15223
  typeSymbol: "@deepseek-ai/dsh-mcp-manager/types#McpManagerReleaseResult",
15197
- schema: _deepseek_ai_dsh_mcp_manager_mcpManager_release_result$schema
15224
+ schema: _deepseek_ai_dsh_mcp_manager_mcpManager_release_result$schema,
15225
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_release_result$schema
15198
15226
  },
15199
15227
  sourceLocation: { "file": "lib/index.js" }
15200
15228
  }
@@ -15225,7 +15253,10 @@ function emptyDraft() {
15225
15253
  toolCallTimeoutMs: String(DEFAULT_TOOL_CALL_TIMEOUT_MS),
15226
15254
  // Reject the mount by default when the startup connection fails, so a
15227
15255
  // broken server never registers stale tools.
15228
- failOnStartupError: true
15256
+ failOnStartupError: true,
15257
+ // OAuth is opt-in: guessing it from the headers opened the browser for
15258
+ // servers that authenticate with static tokens.
15259
+ oauth: false
15229
15260
  };
15230
15261
  }
15231
15262
  function draftFromServer(server) {
@@ -15240,6 +15271,7 @@ function draftFromServer(server) {
15240
15271
  url: server.url,
15241
15272
  headersText: server.headers.map((header) => `${header.name}: ${header.value}`).join("\n"),
15242
15273
  toolCallTimeoutMs: String(server.toolCallTimeoutMs),
15274
+ oauth: server.oauth === true,
15243
15275
  failOnStartupError: server.failOnStartupError
15244
15276
  };
15245
15277
  }
@@ -15326,6 +15358,7 @@ function draftToSubmission(draft) {
15326
15358
  url: draft.url.trim(),
15327
15359
  headers: parseHeaders(draft.headersText),
15328
15360
  toolCallTimeoutMs: parseTimeout(draft.toolCallTimeoutMs),
15361
+ oauth: draft.oauth,
15329
15362
  failOnStartupError: draft.failOnStartupError
15330
15363
  }
15331
15364
  };
@@ -15591,6 +15624,18 @@ function McpServerForm(props) {
15591
15624
  }
15592
15625
  )
15593
15626
  ] }),
15627
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: ServerForm_default.checkRow, children: [
15628
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
15629
+ "input",
15630
+ {
15631
+ type: "checkbox",
15632
+ checked: draft.oauth,
15633
+ onChange: (event) => update({ oauth: event.currentTarget.checked })
15634
+ }
15635
+ ),
15636
+ t("oauthAuth")
15637
+ ] }),
15638
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: ServerForm_default.hint, children: t("oauthAuthHint") }),
15594
15639
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: ServerForm_default.checkRow, children: [
15595
15640
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
15596
15641
  "input",
@@ -15663,6 +15708,7 @@ function serversToJsonText(servers) {
15663
15708
  }
15664
15709
  entry.toolCallTimeoutMs = server.toolCallTimeoutMs;
15665
15710
  entry.failOnStartupError = server.failOnStartupError;
15711
+ if (server.oauth === true) entry.oauth = true;
15666
15712
  entry.env = server.env.map((row) => row.secret ? { name: row.name, secret: true, configured: row.configured } : { name: row.name, secret: false, ...row.value !== void 0 && row.value.length > 0 ? { value: row.value } : {} });
15667
15713
  document2[server.serverName] = entry;
15668
15714
  }
@@ -15730,6 +15776,7 @@ function parseServersJson(text) {
15730
15776
  url: typeof cfg.url === "string" ? cfg.url : "",
15731
15777
  headers: cfg.headers,
15732
15778
  toolCallTimeoutMs: typeof cfg.toolCallTimeoutMs === "number" ? cfg.toolCallTimeoutMs : 6e4,
15779
+ oauth: cfg.oauth === true,
15733
15780
  failOnStartupError: cfg.failOnStartupError !== false
15734
15781
  };
15735
15782
  servers.push({ server, env });
@@ -16093,6 +16140,7 @@ function viewToDraft(server) {
16093
16140
  url: server.url,
16094
16141
  headersText: server.headers.map((header) => `${header.name}: ${header.value}`).join("\n"),
16095
16142
  toolCallTimeoutMs: String(server.toolCallTimeoutMs),
16143
+ oauth: server.oauth === true,
16096
16144
  failOnStartupError: server.failOnStartupError
16097
16145
  };
16098
16146
  }
@@ -16515,6 +16563,8 @@ var zh = {
16515
16563
  removeEnvVar: "\u79FB\u9664\u8BE5\u53D8\u91CF",
16516
16564
  toolCallTimeoutMs: "\u5DE5\u5177\u8C03\u7528\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",
16517
16565
  failOnStartupError: "\u542F\u52A8\u8FDE\u63A5\u5931\u8D25\u65F6\u62D2\u7EDD\u6302\u8F7D",
16566
+ oauthAuth: "\u4F7F\u7528 OAuth \u6388\u6743\uFF08\u6D4F\u89C8\u5668\u6388\u6743 + token \u6258\u7BA1\uFF09",
16567
+ oauthAuthHint: "\u4EC5\u5F53\u8BE5\u670D\u52A1\u5668\u786E\u5B9E\u4F7F\u7528 OAuth\uFF08MCP \u6388\u6743\u7801 + PKCE\uFF09\u65F6\u52FE\u9009\u3002\u7528\u9759\u6001 token \u6216\u81EA\u5B9A\u4E49\u8BF7\u6C42\u5934\u8BA4\u8BC1\u7684\u670D\u52A1\u5668\u8BF7\u52FF\u52FE\u9009\u2014\u2014\u5426\u5219\u670D\u52A1\u5668\u8FD4\u56DE 401 \u65F6\u4F1A\u88AB\u5F53\u6210 OAuth \u6311\u6218\uFF0C\u4ECE\u800C\u53CD\u590D\u5F39\u51FA\u6D4F\u89C8\u5668\u6388\u6743\u3002",
16518
16568
  statusMounting: "\u8FDE\u63A5\u4E2D",
16519
16569
  statusLive: "\u5DF2\u8FDE\u63A5",
16520
16570
  statusFailed: "\u6302\u8F7D\u5931\u8D25",
@@ -16638,6 +16688,8 @@ var en = {
16638
16688
  removeEnvVar: "Remove this variable",
16639
16689
  toolCallTimeoutMs: "Tool-call timeout (ms)",
16640
16690
  failOnStartupError: "Reject the mount when the startup connection fails",
16691
+ oauthAuth: "Use OAuth authorization (browser authorization + token storage)",
16692
+ oauthAuthHint: "Tick only when the server really uses OAuth (MCP authorization-code + PKCE). Servers that authenticate with static tokens or custom headers must leave this off \u2014 otherwise a 401 is treated as an OAuth challenge and the browser authorization opens again and again.",
16641
16693
  statusMounting: "Connecting",
16642
16694
  statusLive: "Connected",
16643
16695
  statusFailed: "Mount failed",
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@ import { randomBytes } from "node:crypto";
2
2
  import { Service } from "@deepseek-ai/cordis";
3
3
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
4
4
  import * as mcpClient from "./mcp-client.js";
5
- import { createOAuthProvider } from "./oauth.js";
5
+ import { createOAuthProvider, oauthCredentialRefs } from "./oauth.js";
6
6
  import { probeConnection } from "./probe.js";
7
7
  import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
8
8
  import { hostText } from "./host-locales.js";
@@ -56,6 +56,7 @@ const mcpServerRowSchema = z$1.object({
56
56
  env: z$1.array(envEntrySchema).default([]),
57
57
  toolCallTimeoutMs: positiveIntegerSchema,
58
58
  failOnStartupError: z$1.boolean().default(false),
59
+ oauth: z$1.boolean().default(false),
59
60
  origin: z$1.union([z$1.literal("plugin"), z$1.literal("cordis")]).default("plugin"),
60
61
  declaredIn: z$1.string().optional(),
61
62
  declaredRowId: z$1.string().optional(),
@@ -184,6 +185,9 @@ function toServerRow(id, server, env, previous) {
184
185
  }),
185
186
  toolCallTimeoutMs: server.toolCallTimeoutMs,
186
187
  failOnStartupError: server.failOnStartupError,
188
+ // The OAuth switch is explicit: a caller that states nothing (the JSON
189
+ // editor omitting the field) keeps the previous choice.
190
+ oauth: server.oauth === true ? true : server.oauth === false ? false : previous?.oauth === true,
187
191
  // Rows created here are the manager's own; mirrors imported from a
188
192
  // Cordis patch layer are written by syncDeclared with origin "cordis".
189
193
  origin: "plugin",
@@ -327,7 +331,7 @@ function placeholderName(value, envNames) {
327
331
  }
328
332
  /** Whether two server records carry the same content (mirror refresh guard). */
329
333
  function sameRecord(left, right) {
330
- return left.serverName === right.serverName && left.transport === right.transport && left.enabled === right.enabled && left.command === right.command && left.cwd === right.cwd && left.url === right.url && left.toolCallTimeoutMs === right.toolCallTimeoutMs && left.failOnStartupError === right.failOnStartupError && left.declaredIn === right.declaredIn && left.declaredRowId === right.declaredRowId && left.stale === right.stale && JSON.stringify(left.args) === JSON.stringify(right.args) && JSON.stringify(left.headers) === JSON.stringify(right.headers) && JSON.stringify(left.env) === JSON.stringify(right.env);
334
+ return left.serverName === right.serverName && left.transport === right.transport && left.enabled === right.enabled && left.command === right.command && left.cwd === right.cwd && left.url === right.url && left.toolCallTimeoutMs === right.toolCallTimeoutMs && left.failOnStartupError === right.failOnStartupError && left.oauth === right.oauth && left.declaredIn === right.declaredIn && left.declaredRowId === right.declaredRowId && left.stale === right.stale && JSON.stringify(left.args) === JSON.stringify(right.args) && JSON.stringify(left.headers) === JSON.stringify(right.headers) && JSON.stringify(left.env) === JSON.stringify(right.env);
331
335
  }
332
336
  /**
333
337
  * The managed server id a credential reference belongs to, when the reference
@@ -561,6 +565,11 @@ let McpManagerService = (() => {
561
565
  * disabled and re-enabled.
562
566
  */
563
567
  everMounted = /* @__PURE__ */ new Set();
568
+ /**
569
+ * Server names whose one automatic browser authorization this process
570
+ * already consumed (see {@link claimAutoBrowser}).
571
+ */
572
+ autoBrowserOpened = /* @__PURE__ */ new Set();
564
573
  /** Per-id operation chain serializing reconcile, remove, and restart jobs. */
565
574
  operationTails = /* @__PURE__ */ new Map();
566
575
  /** Server ids whose secret writes are this manager's own; skip restart echoes. */
@@ -594,6 +603,7 @@ let McpManagerService = (() => {
594
603
  }, "mcp-manager.domainClose");
595
604
  this.table = domain.table("servers");
596
605
  this.envTable = domain.table("global_env");
606
+ await this.migrateOAuthFlags();
597
607
  await this.refreshDeclared(false);
598
608
  this.reconcileDeclared();
599
609
  this.installToolControl();
@@ -808,7 +818,11 @@ let McpManagerService = (() => {
808
818
  ...toMirrorRecord(entry),
809
819
  origin: "plugin",
810
820
  enabled: true,
811
- stale: false
821
+ stale: false,
822
+ // A declaration with no `Authorization` header is the shape an
823
+ // OAuth-protected server has; default the switch on for the
824
+ // takeover (it stays visible and editable in the form).
825
+ oauth: entry.transport === "streamable-http" && !entry.headers.some((header) => header.name.toLowerCase() === "authorization")
812
826
  });
813
827
  if (!parsed.success) return this.adoptFailure("MCP_ADOPT_FAILED", parsed.error.issues[0]?.message ?? "invalid declaration");
814
828
  const disable = writeManagedIds(entry.declaredIn, [...managedIds(entry.declaredIn), entry.rowId]);
@@ -1036,19 +1050,64 @@ let McpManagerService = (() => {
1036
1050
  };
1037
1051
  }
1038
1052
  /**
1039
- * Whether one resolved client config needs an OAuth provider. Only
1040
- * streamable-http servers WITHOUT a static `Authorization` header do:
1041
- * a static bearer token means the server authenticates directly, and
1042
- * attaching an OAuth provider would make the SDK treat any 401 as an
1043
- * authorization-code challenge — opening the browser / blocking tool
1044
- * calls for servers that never intended OAuth. stdio never needs it.
1045
- * @param config - the resolved client config from {@link toClientConfig}.
1053
+ * Whether one stored server asks for an OAuth provider. The switch is
1054
+ * explicit (`oauth: true`, set in the settings form): a server that
1055
+ * authenticates with static headers must never be guessed into OAuth,
1056
+ * because a 401 from it is not a challenge and starting the
1057
+ * authorization-code flow for it only opens the browser forever.
1058
+ * stdio never needs the provider.
1059
+ * @param row - the stored definition (or the submitted spec).
1046
1060
  * @returns true when OAuth (authorization-code + PKCE) should be enabled.
1047
1061
  */
1048
- wantsOAuthProvider(config) {
1049
- if (config.transport !== "streamable-http") return false;
1050
- const names = Object.keys(config.headers ?? {});
1051
- return !names.some((name) => name.toLowerCase() === "authorization");
1062
+ wantsOAuthProvider(row) {
1063
+ return row?.transport === "streamable-http" && row.oauth === true;
1064
+ }
1065
+ /**
1066
+ * Claim the one automatic browser authorization this process allows per
1067
+ * server. Without it a server whose 401 can never be satisfied by the
1068
+ * authorization-code flow (or whose token keeps being rejected) reopens
1069
+ * the browser on every mount and reconnect; later attempts surface the
1070
+ * authorization link instead, and `test` still opens the browser because
1071
+ * the user asked for it.
1072
+ * @param serverName - the server the provider belongs to.
1073
+ * @returns true when this process may open the browser automatically.
1074
+ */
1075
+ claimAutoBrowser(serverName) {
1076
+ if (this.autoBrowserOpened.has(serverName)) return false;
1077
+ this.autoBrowserOpened.add(serverName);
1078
+ return true;
1079
+ }
1080
+ /**
1081
+ * Give stored rows that completed an OAuth flow before the explicit
1082
+ * switch existed their provider back (`oauth: true`). The switch replaced
1083
+ * a header heuristic, so a row with stored OAuth credentials really does
1084
+ * need authorization; rows without them stay off and stop opening the
1085
+ * browser for a flow that can never succeed.
1086
+ */
1087
+ async migrateOAuthFlags() {
1088
+ const table = this.requireTable();
1089
+ for (const [id, row] of table.entries()) {
1090
+ if (row.oauth === true || row.origin === "cordis") continue;
1091
+ let configured = false;
1092
+ for (const ref of oauthCredentialRefs(row.serverName)) {
1093
+ try {
1094
+ if ((await this.ctx.credentials.describe(ref)).configured) {
1095
+ configured = true;
1096
+ break;
1097
+ }
1098
+ } catch {
1099
+ /* an absent credential document is not an error */
1100
+ }
1101
+ }
1102
+ if (!configured) continue;
1103
+ const parsed = mcpServerRowSchema.safeParse({
1104
+ ...row,
1105
+ oauth: true
1106
+ });
1107
+ if (!parsed.success) continue;
1108
+ await table.put(id, parsed.data);
1109
+ this.ctx.logger.info(`mcp-manager: "${row.serverName}" keeps OAuth (stored credentials) after the explicit switch`);
1110
+ }
1052
1111
  }
1053
1112
  /**
1054
1113
  * Mount one server's mcp-client instance without awaiting activation, so a
@@ -1071,15 +1130,17 @@ let McpManagerService = (() => {
1071
1130
  const env = await this.resolveEnv(row);
1072
1131
  const globalEnv = await this.resolveGlobalEnv();
1073
1132
  const config = this.toClientConfig(row, env, globalEnv);
1074
- // OAuth servers (authorization-code flow) trigger browser
1075
- // auth on connect (unless allowBrowserOnMount is off, the
1076
- // default); static-credential servers get no provider so
1077
- // their 401s surface as authentication failures instead of
1078
- // an OAuth flow the server never asked for. Tokens are
1079
- // keyed by serverName so a test-connection authorization
1080
- // is reused by the mount.
1081
- const authProvider = this.wantsOAuthProvider(config)
1082
- ? createOAuthProvider(row.serverName, this.ctx, { allowBrowser: this.allowBrowserOnMount, serverUrl: row.url })
1133
+ // The OAuth switch is explicit per server (`oauth: true`), and
1134
+ // automatic browser authorization happens at most once per
1135
+ // server per process: later attempts surface the
1136
+ // authorization link instead of opening the browser again.
1137
+ // Tokens are keyed by serverName, so a test-connection
1138
+ // authorization is reused by the mount.
1139
+ const authProvider = this.wantsOAuthProvider(row)
1140
+ ? createOAuthProvider(row.serverName, this.ctx, {
1141
+ allowBrowser: this.allowBrowserOnMount && this.claimAutoBrowser(row.serverName),
1142
+ serverUrl: row.url
1143
+ })
1083
1144
  : void 0;
1084
1145
  handle = this.ctx.plugin(MCP_CLIENT_PLUGIN, { ...config, ...authProvider === void 0 ? {} : { authProvider } });
1085
1146
  } catch (error) {
@@ -1182,6 +1243,7 @@ let McpManagerService = (() => {
1182
1243
  env,
1183
1244
  toolCallTimeoutMs: row.toolCallTimeoutMs,
1184
1245
  failOnStartupError: row.failOnStartupError,
1246
+ ...(mirror ? {} : { oauth: row.oauth === true }),
1185
1247
  ...(mirrorNeedsPlugin ? { needsPlugin: true } : {}),
1186
1248
  status: {
1187
1249
  // A mirror never mounts here: its phase follows the declaration
@@ -1393,7 +1455,8 @@ let McpManagerService = (() => {
1393
1455
  url: typeof item.server.url === "string" ? item.server.url : "",
1394
1456
  headers: normalizeHeaders(item.server.headers),
1395
1457
  toolCallTimeoutMs: Number.isInteger(item.server.toolCallTimeoutMs) ? item.server.toolCallTimeoutMs : 60e3,
1396
- failOnStartupError: item.server.failOnStartupError !== false
1458
+ failOnStartupError: item.server.failOnStartupError !== false,
1459
+ oauth: item.server.oauth === void 0 ? void 0 : item.server.oauth === true
1397
1460
  };
1398
1461
  try {
1399
1462
  validateServerInput(spec, env);
@@ -1595,12 +1658,13 @@ let McpManagerService = (() => {
1595
1658
  // Test connection also supports OAuth: an OAuth-protected server
1596
1659
  // opens the browser for authorization and the probe continues after
1597
1660
  // tokens are stored (keyed by serverName, matching the mount path).
1598
- // Static-credential servers (configured Authorization header) skip
1599
- // the provider so a 401 probe reports the authentication failure
1600
- // instead of starting an OAuth flow the server never asked for.
1661
+ // The user asked for this probe, so it may open the browser even
1662
+ // when the mount-time circuit breaker already fired; servers without
1663
+ // the explicit switch skip the provider so a 401 reports an
1664
+ // authentication failure instead of starting a bogus flow.
1601
1665
  const probe = await probeConnection(probeConfig, {
1602
1666
  timeoutMs: this.probeTimeoutMs,
1603
- ...(this.wantsOAuthProvider(probeConfig)
1667
+ ...(this.wantsOAuthProvider(request.server)
1604
1668
  ? { authProvider: createOAuthProvider(request.server.serverName, this.ctx, { serverUrl: request.server.url }) }
1605
1669
  : {})
1606
1670
  });
package/lib/mcp-client.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
2
  import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
3
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
- import { ListToolsResultSchema, ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js";
5
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
6
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
+ import { Client, specTypeSchemas } from "@modelcontextprotocol/client";
4
+ import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
5
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
7
6
  import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
8
7
  import { createHash } from "node:crypto";
9
8
  import { isDeepStrictEqual } from "node:util";
@@ -92,7 +91,7 @@ function listToolsUncached(client, cursor) {
92
91
  return client.request({
93
92
  method: "tools/list",
94
93
  ...cursor === void 0 ? {} : { params: { cursor } }
95
- }, ListToolsResultSchema);
94
+ }, specTypeSchemas.ListToolsResult);
96
95
  }
97
96
  /** Call without the SDK pre-validating an output schema the bridge may not support. */
98
97
  function callToolUncached(client, rawName, args, exec, opts) {
@@ -558,7 +557,11 @@ function startConnection(ctx, config, policy) {
558
557
  const opts = {
559
558
  registrationFailure: "contain",
560
559
  serverName: config.serverName,
561
- toolCallTimeoutMs: config.toolCallTimeoutMs
560
+ toolCallTimeoutMs: config.toolCallTimeoutMs,
561
+ // Read by the per-tool-call OAuth guard: the server URL drives discovery,
562
+ // and the provider reports whether a stored token is still usable.
563
+ url: config.url,
564
+ authProvider: config.authProvider
562
565
  };
563
566
  const startupOpts = config.failOnStartupError ? {
564
567
  ...opts,
@@ -659,13 +662,12 @@ function startConnection(ctx, config, policy) {
659
662
  * @param startup - Whether this is the plugin's activation attempt.
660
663
  */
661
664
  async function connectGeneration(startup) {
665
+ // SDK 2.0 no longer accepts `authProvider` in ClientOptions; the transport
666
+ // created by createTransport(config) carries it (config.authProvider).
662
667
  const generation = new Client({
663
668
  name: "dsh-mcp-client",
664
669
  version: "0.0.1"
665
- }, {
666
- capabilities: {},
667
- ...(opts.authProvider === void 0 ? {} : { authProvider: opts.authProvider })
668
- });
670
+ }, { capabilities: {} });
669
671
  const closed = Promise.withResolvers();
670
672
  let attemptSettled = false;
671
673
  let closeObserved = false;
@@ -677,7 +679,7 @@ function startConnection(ctx, config, policy) {
677
679
  closed.resolve();
678
680
  if (attemptSettled) generationDown(generation);
679
681
  };
680
- generation.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
682
+ generation.setNotificationHandler("notifications/tools/list_changed", async () => {
681
683
  if (!isCurrent(generation)) return;
682
684
  ctx.logger.info(`${label}: tool list changed, re-syncing`);
683
685
  try {
@@ -783,7 +785,7 @@ async function runProbe(client, config) {
783
785
  const response = await client.request({
784
786
  method: "tools/list",
785
787
  ...cursor === void 0 ? {} : { params: { cursor } }
786
- }, ListToolsResultSchema);
788
+ }, specTypeSchemas.ListToolsResult);
787
789
  for (const tool of response.tools) tools.push({
788
790
  name: tool.name,
789
791
  ...tool.description === void 0 ? {} : { description: tool.description }
package/lib/oauth.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  exchangeAuthorization,
20
20
  registerClient,
21
21
  startAuthorization,
22
- } from "@modelcontextprotocol/sdk/client/auth.js";
22
+ } from "@modelcontextprotocol/client";
23
23
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
24
24
  import { hostText } from "./host-locales.js";
25
25
 
@@ -77,6 +77,19 @@ function oauthClientRef(serverId) {
77
77
  return oauthRef(OAUTH_CLIENT_REF_PREFIX, serverId);
78
78
  }
79
79
 
80
+ /**
81
+ * The credential references one server's OAuth flow writes (token + client).
82
+ * Recognising servers that demonstrably completed a flow is how stored rows
83
+ * migrate to the explicit OAuth switch: the switch replaced a header
84
+ * heuristic, and a server with stored OAuth credentials really does need
85
+ * authorization.
86
+ * @param serverId - the server name the flow is keyed by.
87
+ * @returns both credential reference names.
88
+ */
89
+ export function oauthCredentialRefs(serverId) {
90
+ return [oauthTokenRef(serverId), oauthClientRef(serverId)];
91
+ }
92
+
80
93
  /** Open the system browser to one URL, best effort. */
81
94
  function openBrowser(url) {
82
95
  const command =
package/lib/probe.js CHANGED
@@ -1,5 +1,4 @@
1
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
- import { ListToolsResultSchema } from "@modelcontextprotocol/sdk/types.js";
1
+ import { Client, specTypeSchemas } from "@modelcontextprotocol/client";
3
2
  import { createTransport } from "./transport.js";
4
3
  const DEFAULT_PROBE_TIMEOUT_MS = 15e3;
5
4
  function probeErrorMessage(error) {
@@ -12,7 +11,7 @@ async function runProbe(client, config) {
12
11
  do {
13
12
  const response = await client.request(
14
13
  { method: "tools/list", ...cursor === void 0 ? {} : { params: { cursor } } },
15
- ListToolsResultSchema
14
+ specTypeSchemas.ListToolsResult
16
15
  );
17
16
  for (const tool of response.tools) {
18
17
  tools.push({
@@ -37,12 +36,11 @@ async function probeConnection(config, options = {}) {
37
36
  // the tokens and the next probe succeeds.
38
37
  const OAUTH_PROBE_BUDGET_MS = 5 * 60e3;
39
38
  const budgetMs = options.authProvider === void 0 ? timeoutMs : Math.max(timeoutMs, OAUTH_PROBE_BUDGET_MS);
39
+ // Client options no longer carry `authProvider`; the SDK reads it from the
40
+ // transport (see the transportConfig below).
40
41
  const client = new Client(
41
42
  { name: "dsh-mcp-client", version: "0.0.1" },
42
- {
43
- capabilities: {},
44
- ...(options.authProvider === void 0 ? {} : { authProvider: options.authProvider })
45
- }
43
+ { capabilities: {} }
46
44
  );
47
45
  const timeout = new Promise((_, reject) => {
48
46
  const timer = setTimeout(() => reject(new Error(`connection probe timed out after ${budgetMs}ms`)), budgetMs);
@@ -50,7 +48,7 @@ async function probeConnection(config, options = {}) {
50
48
  });
51
49
  try {
52
50
  // The SDK performs OAuth in the transport layer, so the authProvider must
53
- // reach the transport config (not only the Client options).
51
+ // reach the transport config.
54
52
  const transportConfig = {
55
53
  ...config,
56
54
  ...(options.authProvider === void 0 ? {} : { authProvider: options.authProvider })
package/lib/transport.js CHANGED
@@ -1,5 +1,5 @@
1
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
2
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
1
+ import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
2
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
3
3
  import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
4
4
  function buildChildEnv(extra) {
5
5
  return { ...scrubbedParentEnv(), ...extra };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mcp",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "MCP 管理界面 + tool search:可视化配置/启停/刷新 MCP 服务器与工具勾选,按需检索(tool search)热注入;工具列表稳定命中缓存、不撑爆上下文。DeepSeek Harness plugin: MCP management UI + tool search — stable tool list, cache-friendly, no context bloat.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -24,6 +24,10 @@
24
24
  "build": "node scripts/build.mjs",
25
25
  "test": "node scripts/test.mjs"
26
26
  },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/client": "2.0.0",
29
+ "zod": "^4.2.0"
30
+ },
27
31
  "author": "Arvin.qi <arvin.qi@qq.com>",
28
32
  "license": "MIT",
29
33
  "repository": {
@@ -35,6 +39,7 @@
35
39
  "url": "https://github.com/ArvinQi/dsh-mcp/issues"
36
40
  },
37
41
  "dsh": {
42
+ "supported": "0.1.6-alpha.2",
38
43
  "client": {
39
44
  "inject": [
40
45
  "@deepseek-ai/dsh-api-remotes",
@@ -73,6 +73,7 @@ function viewToDraft(server: McpServerView): McpDraft {
73
73
  url: server.url,
74
74
  headersText: server.headers.map(header => `${header.name}: ${header.value}`).join('\n'),
75
75
  toolCallTimeoutMs: String(server.toolCallTimeoutMs),
76
+ oauth: server.oauth === true,
76
77
  failOnStartupError: server.failOnStartupError,
77
78
  }
78
79
  }
@@ -225,6 +225,16 @@ export function McpServerForm(props: McpServerFormProps): ReactNode {
225
225
  />
226
226
  </label>
227
227
 
228
+ <label className={css.checkRow}>
229
+ <input
230
+ type="checkbox"
231
+ checked={draft.oauth}
232
+ onChange={(event) => update({ oauth: event.currentTarget.checked })}
233
+ />
234
+ {t('oauthAuth')}
235
+ </label>
236
+ <p className={css.hint}>{t('oauthAuthHint')}</p>
237
+
228
238
  <label className={css.checkRow}>
229
239
  <input
230
240
  type="checkbox"
@@ -77,6 +77,7 @@ export function serversToJsonText(servers: readonly McpServerView[]): string {
77
77
  }
78
78
  entry.toolCallTimeoutMs = server.toolCallTimeoutMs
79
79
  entry.failOnStartupError = server.failOnStartupError
80
+ if (server.oauth === true) entry.oauth = true
80
81
  entry.env = server.env.map(row => row.secret
81
82
  ? { name: row.name, secret: true, configured: row.configured }
82
83
  : { name: row.name, secret: false, ...(row.value !== undefined && row.value.length > 0 ? { value: row.value } : {}) })
@@ -150,6 +151,7 @@ export function parseServersJson(text: string): { ok: true; servers: readonly Se
150
151
  url: typeof cfg.url === 'string' ? cfg.url : '',
151
152
  headers: cfg.headers,
152
153
  toolCallTimeoutMs: typeof cfg.toolCallTimeoutMs === 'number' ? cfg.toolCallTimeoutMs : 60000,
154
+ oauth: cfg.oauth === true,
153
155
  failOnStartupError: cfg.failOnStartupError !== false,
154
156
  }
155
157
  servers.push({ server, env })
@@ -39,6 +39,8 @@ export const zh = {
39
39
  removeEnvVar: '移除该变量',
40
40
  toolCallTimeoutMs: '工具调用超时(毫秒)',
41
41
  failOnStartupError: '启动连接失败时拒绝挂载',
42
+ oauthAuth: '使用 OAuth 授权(浏览器授权 + token 托管)',
43
+ oauthAuthHint: '仅当该服务器确实使用 OAuth(MCP 授权码 + PKCE)时勾选。用静态 token 或自定义请求头认证的服务器请勿勾选——否则服务器返回 401 时会被当成 OAuth 挑战,从而反复弹出浏览器授权。',
42
44
  statusMounting: '连接中',
43
45
  statusLive: '已连接',
44
46
  statusFailed: '挂载失败',
@@ -167,6 +169,8 @@ export const en = {
167
169
  removeEnvVar: 'Remove this variable',
168
170
  toolCallTimeoutMs: 'Tool-call timeout (ms)',
169
171
  failOnStartupError: 'Reject the mount when the startup connection fails',
172
+ oauthAuth: 'Use OAuth authorization (browser authorization + token storage)',
173
+ oauthAuthHint: 'Tick only when the server really uses OAuth (MCP authorization-code + PKCE). Servers that authenticate with static tokens or custom headers must leave this off — otherwise a 401 is treated as an OAuth challenge and the browser authorization opens again and again.',
170
174
  statusMounting: 'Connecting',
171
175
  statusLive: 'Connected',
172
176
  statusFailed: 'Mount failed',
@@ -25,6 +25,8 @@ export interface McpDraft {
25
25
  readonly headersText: string
26
26
  /** Timeout as editor text; blank falls back to the default. */
27
27
  readonly toolCallTimeoutMs: string
28
+ /** Explicit OAuth switch: only then does the Host attach an OAuth provider. */
29
+ readonly oauth: boolean
28
30
  readonly failOnStartupError: boolean
29
31
  }
30
32
 
@@ -78,6 +80,9 @@ export function emptyDraft(): McpDraft {
78
80
  // Reject the mount by default when the startup connection fails, so a
79
81
  // broken server never registers stale tools.
80
82
  failOnStartupError: true,
83
+ // OAuth is opt-in: guessing it from the headers opened the browser for
84
+ // servers that authenticate with static tokens.
85
+ oauth: false,
81
86
  }
82
87
  }
83
88
 
@@ -94,6 +99,7 @@ export function draftFromServer(server: McpServerView): McpDraft {
94
99
  url: server.url,
95
100
  headersText: server.headers.map(header => `${header.name}: ${header.value}`).join('\n'),
96
101
  toolCallTimeoutMs: String(server.toolCallTimeoutMs),
102
+ oauth: server.oauth === true,
97
103
  failOnStartupError: server.failOnStartupError,
98
104
  }
99
105
  }
@@ -205,6 +211,7 @@ export function draftToSubmission(draft: McpDraft): McpSubmission {
205
211
  url: draft.url.trim(),
206
212
  headers: parseHeaders(draft.headersText),
207
213
  toolCallTimeoutMs: parseTimeout(draft.toolCallTimeoutMs),
214
+ oauth: draft.oauth,
208
215
  failOnStartupError: draft.failOnStartupError,
209
216
  },
210
217
  }
@@ -36,6 +36,7 @@ const _deepseek_ai_dsh_mcp_manager_mcpManager_list_result$schema = z.object({
36
36
  })).readonly(),
37
37
  'toolCallTimeoutMs': z.number().readonly(),
38
38
  'failOnStartupError': z.boolean().readonly(),
39
+ 'oauth': z.boolean().readonly().optional(),
39
40
  'status': z.object({
40
41
  'phase': z.union([z.literal("failed"), z.literal("stopped"), z.literal("mounting"), z.literal("live")]).readonly(),
41
42
  'tools': z.array(z.string()).readonly(),
@@ -82,6 +83,7 @@ const _deepseek_ai_dsh_mcp_manager_mcpManager_test_parameter_0$schema = z.object
82
83
  })).readonly(),
83
84
  'toolCallTimeoutMs': z.number().readonly(),
84
85
  'failOnStartupError': z.boolean().readonly(),
86
+ 'oauth': z.boolean().readonly().optional(),
85
87
  }).readonly(),
86
88
  'env': z.array(z.object({
87
89
  'name': z.string().readonly(),
@@ -119,6 +121,7 @@ const _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_parameter_0$schema = z.obje
119
121
  })).readonly(),
120
122
  'toolCallTimeoutMs': z.number().readonly(),
121
123
  'failOnStartupError': z.boolean().readonly(),
124
+ 'oauth': z.boolean().readonly().optional(),
122
125
  }).readonly(),
123
126
  'env': z.array(z.object({
124
127
  'name': z.string().readonly(),
@@ -149,6 +152,7 @@ const _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema = z.union([z.
149
152
  })).readonly(),
150
153
  'toolCallTimeoutMs': z.number().readonly(),
151
154
  'failOnStartupError': z.boolean().readonly(),
155
+ 'oauth': z.boolean().readonly().optional(),
152
156
  'status': z.object({
153
157
  'phase': z.union([z.literal("failed"), z.literal("stopped"), z.literal("mounting"), z.literal("live")]).readonly(),
154
158
  'tools': z.array(z.string()).readonly(),
@@ -191,6 +195,7 @@ const _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema = z.
191
195
  })).readonly()]).readonly().optional(),
192
196
  'toolCallTimeoutMs': z.number().readonly().optional(),
193
197
  'failOnStartupError': z.boolean().readonly().optional(),
198
+ 'oauth': z.boolean().readonly().optional(),
194
199
  }).readonly(),
195
200
  'env': z.array(z.object({
196
201
  'name': z.string().readonly(),
@@ -226,6 +231,7 @@ const _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema = z.union
226
231
  })).readonly(),
227
232
  'toolCallTimeoutMs': z.number().readonly(),
228
233
  'failOnStartupError': z.boolean().readonly(),
234
+ 'oauth': z.boolean().readonly().optional(),
229
235
  'status': z.object({
230
236
  'phase': z.union([z.literal("failed"), z.literal("stopped"), z.literal("mounting"), z.literal("live")]).readonly(),
231
237
  'tools': z.array(z.string()).readonly(),
@@ -347,6 +353,7 @@ const _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_result$schema = z.union([z.o
347
353
  })).readonly(),
348
354
  'toolCallTimeoutMs': z.number().readonly(),
349
355
  'failOnStartupError': z.boolean().readonly(),
356
+ 'oauth': z.boolean().readonly().optional(),
350
357
  'status': z.object({
351
358
  'phase': z.union([z.literal("failed"), z.literal("stopped"), z.literal("mounting"), z.literal("live")]).readonly(),
352
359
  'tools': z.array(z.string()).readonly(),
@@ -407,6 +414,7 @@ export const TYPERT_REMOTE = {
407
414
  mode: 'strict',
408
415
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerRemoveRequest',
409
416
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_delete_parameter_0$schema,
417
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_delete_parameter_0$schema,
410
418
  },
411
419
  },
412
420
  ],
@@ -414,6 +422,7 @@ export const TYPERT_REMOTE = {
414
422
  mode: 'strict',
415
423
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerRemoveResult',
416
424
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_delete_result$schema,
425
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_delete_result$schema,
417
426
  },
418
427
  sourceLocation: {"file":"packages/mcp/mcp-manager/src/index.ts","line":399,"column":9},
419
428
  },
@@ -429,6 +438,7 @@ export const TYPERT_REMOTE = {
429
438
  mode: 'strict',
430
439
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerListResult',
431
440
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_list_result$schema,
441
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_list_result$schema,
432
442
  },
433
443
  sourceLocation: {"file":"packages/mcp/mcp-manager/src/index.ts","line":314,"column":9},
434
444
  },
@@ -447,6 +457,7 @@ export const TYPERT_REMOTE = {
447
457
  mode: 'strict',
448
458
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerTestRequest',
449
459
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_test_parameter_0$schema,
460
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_test_parameter_0$schema,
450
461
  },
451
462
  },
452
463
  ],
@@ -454,6 +465,7 @@ export const TYPERT_REMOTE = {
454
465
  mode: 'strict',
455
466
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerTestResult',
456
467
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_test_result$schema,
468
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_test_result$schema,
457
469
  },
458
470
  sourceLocation: {"file":"packages/mcp/mcp-manager/src/index.ts","line":422,"column":9},
459
471
  },
@@ -472,6 +484,7 @@ export const TYPERT_REMOTE = {
472
484
  mode: 'strict',
473
485
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertRequest',
474
486
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_parameter_0$schema,
487
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_parameter_0$schema,
475
488
  },
476
489
  },
477
490
  ],
@@ -479,6 +492,7 @@ export const TYPERT_REMOTE = {
479
492
  mode: 'strict',
480
493
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertResult',
481
494
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema,
495
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_upsert_result$schema,
482
496
  },
483
497
  sourceLocation: {"file":"packages/mcp/mcp-manager/src/index.ts","line":329,"column":9},
484
498
  },
@@ -497,6 +511,7 @@ export const TYPERT_REMOTE = {
497
511
  mode: 'strict',
498
512
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertJsonRequest',
499
513
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema,
514
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_parameter_0$schema,
500
515
  },
501
516
  },
502
517
  ],
@@ -504,6 +519,7 @@ export const TYPERT_REMOTE = {
504
519
  mode: 'strict',
505
520
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerUpsertJsonResult',
506
521
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema,
522
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_upsertJson_result$schema,
507
523
  },
508
524
  sourceLocation: {"file":"lib/index.js"},
509
525
  },
@@ -519,6 +535,7 @@ export const TYPERT_REMOTE = {
519
535
  mode: 'strict',
520
536
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsListResult',
521
537
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsList_result$schema,
538
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsList_result$schema,
522
539
  },
523
540
  sourceLocation: {"file":"lib/index.js"},
524
541
  },
@@ -537,6 +554,7 @@ export const TYPERT_REMOTE = {
537
554
  mode: 'strict',
538
555
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsSetRequest',
539
556
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_parameter_0$schema,
557
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_parameter_0$schema,
540
558
  },
541
559
  },
542
560
  ],
@@ -544,6 +562,7 @@ export const TYPERT_REMOTE = {
544
562
  mode: 'strict',
545
563
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsSetResult',
546
564
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_result$schema,
565
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsSet_result$schema,
547
566
  },
548
567
  sourceLocation: {"file":"lib/index.js"},
549
568
  },
@@ -562,6 +581,7 @@ export const TYPERT_REMOTE = {
562
581
  mode: 'strict',
563
582
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsModeRequest',
564
583
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_parameter_0$schema,
584
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_parameter_0$schema,
565
585
  },
566
586
  },
567
587
  ],
@@ -569,6 +589,7 @@ export const TYPERT_REMOTE = {
569
589
  mode: 'strict',
570
590
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerToolsModeResult',
571
591
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_result$schema,
592
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_toolsMode_result$schema,
572
593
  },
573
594
  sourceLocation: {"file":"lib/index.js"},
574
595
  },
@@ -584,6 +605,7 @@ export const TYPERT_REMOTE = {
584
605
  mode: 'strict',
585
606
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvListResult',
586
607
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envList_result$schema,
608
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_envList_result$schema,
587
609
  },
588
610
  sourceLocation: {"file":"lib/index.js"},
589
611
  },
@@ -602,6 +624,7 @@ export const TYPERT_REMOTE = {
602
624
  mode: 'strict',
603
625
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvSetRequest',
604
626
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_parameter_0$schema,
627
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_parameter_0$schema,
605
628
  },
606
629
  },
607
630
  ],
@@ -609,6 +632,7 @@ export const TYPERT_REMOTE = {
609
632
  mode: 'strict',
610
633
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerEnvSetResult',
611
634
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_result$schema,
635
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_envSet_result$schema,
612
636
  },
613
637
  sourceLocation: {"file":"lib/index.js"},
614
638
  },
@@ -627,6 +651,7 @@ export const TYPERT_REMOTE = {
627
651
  mode: 'strict',
628
652
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerAdoptRequest',
629
653
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_parameter_0$schema,
654
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_parameter_0$schema,
630
655
  },
631
656
  },
632
657
  ],
@@ -634,6 +659,7 @@ export const TYPERT_REMOTE = {
634
659
  mode: 'strict',
635
660
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerAdoptResult',
636
661
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_result$schema,
662
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_adopt_result$schema,
637
663
  },
638
664
  sourceLocation: {"file":"lib/index.js"},
639
665
  },
@@ -652,6 +678,7 @@ export const TYPERT_REMOTE = {
652
678
  mode: 'strict',
653
679
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerReleaseRequest',
654
680
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_release_parameter_0$schema,
681
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_release_parameter_0$schema,
655
682
  },
656
683
  },
657
684
  ],
@@ -659,6 +686,7 @@ export const TYPERT_REMOTE = {
659
686
  mode: 'strict',
660
687
  typeSymbol: '@deepseek-ai/dsh-mcp-manager/types#McpManagerReleaseResult',
661
688
  schema: _deepseek_ai_dsh_mcp_manager_mcpManager_release_result$schema,
689
+ create: () => _deepseek_ai_dsh_mcp_manager_mcpManager_release_result$schema,
662
690
  },
663
691
  sourceLocation: {"file":"lib/index.js"},
664
692
  },
@@ -44,6 +44,8 @@ export interface McpServerInput {
44
44
  readonly url: string
45
45
  readonly headers: readonly McpHeaderEntry[]
46
46
  readonly toolCallTimeoutMs: number
47
+ /** Explicit OAuth switch: only then does the Host attach an OAuth provider. */
48
+ readonly oauth: boolean
47
49
  readonly failOnStartupError: boolean
48
50
  }
49
51
 
@@ -86,6 +88,8 @@ export interface McpServerView {
86
88
  readonly env: readonly McpEnvVarView[]
87
89
  readonly toolCallTimeoutMs: number
88
90
  readonly failOnStartupError: boolean
91
+ /** Explicit OAuth switch of a managed row (absent for declared rows). */
92
+ readonly oauth?: boolean
89
93
  readonly status: McpServerStatus
90
94
  /** `plugin` = stored and mounted here; `cordis` = declared in a patch layer. */
91
95
  readonly source?: McpServerSource