dsh-mcp 1.2.0 → 1.3.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 +12 -0
- package/CHANGELOG.md +12 -0
- package/lib/client.js +19 -24
- package/lib/index.js +1 -1
- package/package.json +1 -1
- package/src/client/ServerForm.tsx +26 -17
- package/src/client/mcp-store.ts +0 -11
package/CHANGELOG.en.md
CHANGED
|
@@ -7,6 +7,18 @@ 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
|
+
## [1.3.0] - 2026-08-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Windows working-directory support**: stdio servers now accept drive-letter absolute paths for `cwd` (e.g. `C:\Users\...`, `C:/...`), consistent with POSIX `/` and UNC `\\` paths (PR #2, thanks @coding-chong)
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Form operations now surface the real error: save / delete / test-connection failures show `code: message` (e.g. `MCP_SERVER_NAME_CONFLICT: serverName "x" is already used...`) instead of a generic message, making failures diagnosable
|
|
19
|
+
- Refresh is decoupled from save/delete: a `refresh()` failure no longer misreports the save/delete outcome — the editor stays open and shows the refresh failure reason (`refresh()` keeps its try/catch and returns a result)
|
|
20
|
+
- Removed the now-unused `failureLocaleKey` dead code (error display shows `code: message` directly)
|
|
21
|
+
|
|
10
22
|
## [1.2.0] - 2026-08-16
|
|
11
23
|
|
|
12
24
|
### Added
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@
|
|
|
7
7
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
8
8
|
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
9
9
|
|
|
10
|
+
## [1.3.0] - 2026-08-16
|
|
11
|
+
|
|
12
|
+
### 新增
|
|
13
|
+
|
|
14
|
+
- **Windows 工作目录支持**:stdio 服务器的 cwd 接受 Windows 盘符绝对路径(如 `C:\Users\...`、`C:/...`),与 POSIX `/`、UNC `\\` 路径一致(PR #2,感谢 @coding-chong)
|
|
15
|
+
|
|
16
|
+
### 修复
|
|
17
|
+
|
|
18
|
+
- 表单操作失败时展示真实错误信息:保存 / 删除 / 测试连接失败不再只显示笼统文案,直接展示 `code: message`(如 `MCP_SERVER_NAME_CONFLICT: serverName "x" is already used...`),便于定位问题
|
|
19
|
+
- 刷新与保存/删除解耦:`refresh()` 失败不再误报保存结果,停留编辑页并显示刷新失败原因(`refresh()` 保留 try/catch 并返回结果)
|
|
20
|
+
- 清理死代码:移除已无引用的 `failureLocaleKey`(错误展示改为直接显示 `code: message`)
|
|
21
|
+
|
|
10
22
|
## [1.2.0] - 2026-08-16
|
|
11
23
|
|
|
12
24
|
### 新增
|
package/lib/client.js
CHANGED
|
@@ -14971,20 +14971,6 @@ function draftToSubmission(draft) {
|
|
|
14971
14971
|
}))
|
|
14972
14972
|
};
|
|
14973
14973
|
}
|
|
14974
|
-
function failureLocaleKey(code) {
|
|
14975
|
-
switch (code) {
|
|
14976
|
-
case "MCP_SERVER_NAME_CONFLICT":
|
|
14977
|
-
return "serverNameConflict";
|
|
14978
|
-
case "MCP_SERVER_NOT_FOUND":
|
|
14979
|
-
return "notFound";
|
|
14980
|
-
case "MCP_INVALID_SPEC":
|
|
14981
|
-
return "invalidSpec";
|
|
14982
|
-
case "MCP_MOUNT_FAILED":
|
|
14983
|
-
return "mountFailed";
|
|
14984
|
-
default:
|
|
14985
|
-
return "failureTitle";
|
|
14986
|
-
}
|
|
14987
|
-
}
|
|
14988
14974
|
function createMcpManagerStore() {
|
|
14989
14975
|
return (0, import_client.defineStore)({
|
|
14990
14976
|
init: () => ({
|
|
@@ -15065,13 +15051,16 @@ function McpServerForm(props) {
|
|
|
15065
15051
|
const addEnvRow = () => {
|
|
15066
15052
|
update({ env: [...draft.env, createEnvRowDraft()] });
|
|
15067
15053
|
};
|
|
15054
|
+
const errorText = (error51) => error51 instanceof Error ? error51.message : String(error51);
|
|
15068
15055
|
const runTest = async () => {
|
|
15069
15056
|
setSaveError(null);
|
|
15070
15057
|
actions.setTestRunning(true);
|
|
15071
15058
|
try {
|
|
15072
15059
|
actions.setTest(await injected.test(draft));
|
|
15073
|
-
} catch {
|
|
15060
|
+
} catch (error51) {
|
|
15061
|
+
console.error("[dsh-mcp] test failed:", error51);
|
|
15074
15062
|
actions.setTest(null);
|
|
15063
|
+
setSaveError(errorText(error51));
|
|
15075
15064
|
} finally {
|
|
15076
15065
|
actions.setTestRunning(false);
|
|
15077
15066
|
}
|
|
@@ -15082,14 +15071,15 @@ function McpServerForm(props) {
|
|
|
15082
15071
|
try {
|
|
15083
15072
|
const failure = await injected.save(draft);
|
|
15084
15073
|
if (failure !== null) {
|
|
15085
|
-
setSaveError(
|
|
15074
|
+
setSaveError(`${failure.code}: ${failure.message}`);
|
|
15086
15075
|
return;
|
|
15087
15076
|
}
|
|
15088
|
-
await refresh();
|
|
15077
|
+
if (!await refresh()) return;
|
|
15089
15078
|
actions.cancelEdit();
|
|
15090
15079
|
onSaved?.();
|
|
15091
|
-
} catch {
|
|
15092
|
-
|
|
15080
|
+
} catch (error51) {
|
|
15081
|
+
console.error("[dsh-mcp] save failed:", error51);
|
|
15082
|
+
setSaveError(errorText(error51));
|
|
15093
15083
|
} finally {
|
|
15094
15084
|
actions.setBusy(null);
|
|
15095
15085
|
}
|
|
@@ -15101,13 +15091,14 @@ function McpServerForm(props) {
|
|
|
15101
15091
|
try {
|
|
15102
15092
|
const failure = await injected.remove(draft.id);
|
|
15103
15093
|
if (failure !== null) {
|
|
15104
|
-
setSaveError(failure.message);
|
|
15094
|
+
setSaveError(`${failure.code}: ${failure.message}`);
|
|
15105
15095
|
return;
|
|
15106
15096
|
}
|
|
15107
|
-
await refresh();
|
|
15097
|
+
if (!await refresh()) return;
|
|
15108
15098
|
actions.cancelEdit();
|
|
15109
|
-
} catch {
|
|
15110
|
-
|
|
15099
|
+
} catch (error51) {
|
|
15100
|
+
console.error("[dsh-mcp] remove failed:", error51);
|
|
15101
|
+
setSaveError(errorText(error51));
|
|
15111
15102
|
} finally {
|
|
15112
15103
|
actions.setBusy(null);
|
|
15113
15104
|
}
|
|
@@ -15115,7 +15106,11 @@ function McpServerForm(props) {
|
|
|
15115
15106
|
const refresh = async () => {
|
|
15116
15107
|
try {
|
|
15117
15108
|
actions.setServers(await injected.list());
|
|
15118
|
-
|
|
15109
|
+
return true;
|
|
15110
|
+
} catch (error51) {
|
|
15111
|
+
console.error("[dsh-mcp] refresh failed:", error51);
|
|
15112
|
+
setSaveError(errorText(error51));
|
|
15113
|
+
return false;
|
|
15119
15114
|
}
|
|
15120
15115
|
};
|
|
15121
15116
|
const stdio = draft.transport === "stdio";
|
package/lib/index.js
CHANGED
|
@@ -105,7 +105,7 @@ function validateServerInput(server, env) {
|
|
|
105
105
|
if (!serverNameSchema.safeParse(server.serverName).success) throw new McpServerValidationError("serverName must match [A-Za-z0-9_-]{1,32}");
|
|
106
106
|
if (server.transport === "stdio") {
|
|
107
107
|
if (server.command.trim().length === 0) throw new McpServerValidationError("stdio transport requires a command");
|
|
108
|
-
if (server.cwd.length > 0 && !/^[/\\]/.test(server.cwd)) throw new McpServerValidationError("cwd must be an absolute path or empty");
|
|
108
|
+
if (server.cwd.length > 0 && !/^(?:[A-Za-z]:[\\/]|[/\\])/.test(server.cwd)) throw new McpServerValidationError("cwd must be an absolute path or empty");
|
|
109
109
|
} else try {
|
|
110
110
|
const parsed = new URL(server.url);
|
|
111
111
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("non-http protocol");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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",
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { useState, type ReactNode } from 'react'
|
|
2
|
-
import type { McpServerId, McpServerView } from './types.ts'
|
|
2
|
+
import type { McpManagerFailure, McpServerId, McpServerView } from './types.ts'
|
|
3
3
|
import type {
|
|
4
4
|
McpDraft, McpEnvRowDraft, McpTestOutcome,
|
|
5
5
|
} from './mcp-store.ts'
|
|
6
|
-
import { createEnvRowDraft
|
|
6
|
+
import { createEnvRowDraft } from './mcp-store.ts'
|
|
7
7
|
import type { McpSettingsLocaleKey } from './locales.ts'
|
|
8
8
|
import css from './ServerForm.module.css'
|
|
9
9
|
|
|
10
10
|
/** The Remote verbs the editor needs; structurally the section's inject face. */
|
|
11
11
|
export interface McpServerFormRemote {
|
|
12
|
-
save: (draft: McpDraft) => Promise<
|
|
13
|
-
remove: (id: McpServerId) => Promise<
|
|
12
|
+
save: (draft: McpDraft) => Promise<McpManagerFailure | null>
|
|
13
|
+
remove: (id: McpServerId) => Promise<McpManagerFailure | null>
|
|
14
14
|
test: (draft: McpDraft) => Promise<McpTestOutcome>
|
|
15
15
|
list: () => Promise<readonly McpServerView[]>
|
|
16
16
|
}
|
|
@@ -60,13 +60,18 @@ export function McpServerForm(props: McpServerFormProps): ReactNode {
|
|
|
60
60
|
update({ env: [...draft.env, createEnvRowDraft()] })
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
const errorText = (error: unknown): string =>
|
|
64
|
+
error instanceof Error ? error.message : String(error)
|
|
65
|
+
|
|
63
66
|
const runTest = async (): Promise<void> => {
|
|
64
67
|
setSaveError(null)
|
|
65
68
|
actions.setTestRunning(true)
|
|
66
69
|
try {
|
|
67
70
|
actions.setTest(await injected.test(draft))
|
|
68
|
-
} catch {
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error('[dsh-mcp] test failed:', error)
|
|
69
73
|
actions.setTest(null)
|
|
74
|
+
setSaveError(errorText(error))
|
|
70
75
|
} finally {
|
|
71
76
|
actions.setTestRunning(false)
|
|
72
77
|
}
|
|
@@ -78,14 +83,15 @@ export function McpServerForm(props: McpServerFormProps): ReactNode {
|
|
|
78
83
|
try {
|
|
79
84
|
const failure = await injected.save(draft)
|
|
80
85
|
if (failure !== null) {
|
|
81
|
-
setSaveError(
|
|
86
|
+
setSaveError(`${failure.code}: ${failure.message}`)
|
|
82
87
|
return
|
|
83
88
|
}
|
|
84
|
-
await refresh()
|
|
89
|
+
if (!await refresh()) return
|
|
85
90
|
actions.cancelEdit()
|
|
86
91
|
onSaved?.()
|
|
87
|
-
} catch {
|
|
88
|
-
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('[dsh-mcp] save failed:', error)
|
|
94
|
+
setSaveError(errorText(error))
|
|
89
95
|
} finally {
|
|
90
96
|
actions.setBusy(null)
|
|
91
97
|
}
|
|
@@ -98,24 +104,27 @@ export function McpServerForm(props: McpServerFormProps): ReactNode {
|
|
|
98
104
|
try {
|
|
99
105
|
const failure = await injected.remove(draft.id)
|
|
100
106
|
if (failure !== null) {
|
|
101
|
-
setSaveError(failure.message)
|
|
107
|
+
setSaveError(`${failure.code}: ${failure.message}`)
|
|
102
108
|
return
|
|
103
109
|
}
|
|
104
|
-
await refresh()
|
|
110
|
+
if (!await refresh()) return
|
|
105
111
|
actions.cancelEdit()
|
|
106
|
-
} catch {
|
|
107
|
-
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error('[dsh-mcp] remove failed:', error)
|
|
114
|
+
setSaveError(errorText(error))
|
|
108
115
|
} finally {
|
|
109
116
|
actions.setBusy(null)
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
|
|
113
|
-
const refresh = async (): Promise<
|
|
120
|
+
const refresh = async (): Promise<boolean> => {
|
|
114
121
|
try {
|
|
115
122
|
actions.setServers(await injected.list())
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
return true
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('[dsh-mcp] refresh failed:', error)
|
|
126
|
+
setSaveError(errorText(error))
|
|
127
|
+
return false
|
|
119
128
|
}
|
|
120
129
|
}
|
|
121
130
|
|
package/src/client/mcp-store.ts
CHANGED
|
@@ -188,17 +188,6 @@ export function draftToSubmission(draft: McpDraft): McpSubmission {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
/** Map a Remote failure code to a locale key for user-facing copy. */
|
|
192
|
-
export function failureLocaleKey(code: string): string {
|
|
193
|
-
switch (code) {
|
|
194
|
-
case 'MCP_SERVER_NAME_CONFLICT': return 'serverNameConflict'
|
|
195
|
-
case 'MCP_SERVER_NOT_FOUND': return 'notFound'
|
|
196
|
-
case 'MCP_INVALID_SPEC': return 'invalidSpec'
|
|
197
|
-
case 'MCP_MOUNT_FAILED': return 'mountFailed'
|
|
198
|
-
default: return 'failureTitle'
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
191
|
/**
|
|
203
192
|
* Declares the MCP management page state and write surface.
|
|
204
193
|
* @returns the store handle.
|