dsh-mcp-skills 0.2.0 → 0.2.2
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/README.md +2 -4
- package/lib/client.js +67 -14
- package/lib/index.js +36 -14
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -67,16 +67,14 @@ dsh plugin --profile web remove dsh-mcp-skills
|
|
|
67
67
|
发布到npm
|
|
68
68
|
|
|
69
69
|
```shell
|
|
70
|
-
# 1.
|
|
70
|
+
# 1. 用官方 registry 重新登录(务必带 --registry,否则会登到镜像站)
|
|
71
|
+
npm login --registry=https://registry.npmjs.org --auth-type=web
|
|
71
72
|
# 2. 确认包名可用
|
|
72
73
|
npm view dsh-mcp-skills version
|
|
73
74
|
# 3. 检查 tarball(务必含 cordis.patch.yml)
|
|
74
75
|
npm pack --dry-run
|
|
75
76
|
# 4. 发布(稳定版,不要 prerelease)
|
|
76
77
|
npm publish
|
|
77
|
-
# 5. 安装并重启
|
|
78
|
-
dsh plugin --profile web add dsh-mcp-skills
|
|
79
|
-
dsh web
|
|
80
78
|
```
|
|
81
79
|
|
|
82
80
|
|
package/lib/client.js
CHANGED
|
@@ -14,6 +14,22 @@ window.__ModuleLoader__.load({
|
|
|
14
14
|
let react = require('react')
|
|
15
15
|
let _deepseek_ai_dsh_client_ui_primitives = require('@deepseek-ai/dsh-client-ui-primitives')
|
|
16
16
|
|
|
17
|
+
// Icon exports were renamed across host releases: older builds spell them
|
|
18
|
+
// `Icon<Name>Outline16`, current ones `Icon<Name>OutlineRegular`. Resolve
|
|
19
|
+
// each once so one bundle renders on either host (an undefined component
|
|
20
|
+
// type is React error #130).
|
|
21
|
+
var manageIcon = (name) =>
|
|
22
|
+
_deepseek_ai_dsh_client_ui_primitives['Icon' + name + 'OutlineRegular'] ||
|
|
23
|
+
_deepseek_ai_dsh_client_ui_primitives['Icon' + name + 'Outline16']
|
|
24
|
+
var ManageIcons = {
|
|
25
|
+
edit: manageIcon('Edit'),
|
|
26
|
+
loading: manageIcon('Loading'),
|
|
27
|
+
play: manageIcon('Play'),
|
|
28
|
+
plus: manageIcon('Plus'),
|
|
29
|
+
refresh: manageIcon('Refresh'),
|
|
30
|
+
trash: manageIcon('Trash'),
|
|
31
|
+
}
|
|
32
|
+
|
|
17
33
|
// ── styles ────────────────────────────────────────────────────────────────
|
|
18
34
|
var manageStylesId = 'dsh-mcp-skills/manage.module.css'
|
|
19
35
|
var manageCss =
|
|
@@ -56,6 +72,35 @@ window.__ModuleLoader__.load({
|
|
|
56
72
|
return error instanceof Error ? error.message : String(error)
|
|
57
73
|
}
|
|
58
74
|
|
|
75
|
+
/** Render a section failure on screen instead of leaving a blank pane. */
|
|
76
|
+
function manageFailure(error) {
|
|
77
|
+
return react_jsx_runtime.jsx('div', {
|
|
78
|
+
className: manageStyles.error,
|
|
79
|
+
children: 'mcp-skills: ' + manageMessageOf(error),
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Keeps one section's render failure visible. The host retires a
|
|
85
|
+
* registration whose component throws (its entry-error supervision
|
|
86
|
+
* channel), which shows up as an empty pane with no console error.
|
|
87
|
+
*/
|
|
88
|
+
class ManageBoundary extends react.Component {
|
|
89
|
+
constructor(props) {
|
|
90
|
+
super(props)
|
|
91
|
+
this.state = { error: null }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static getDerivedStateFromError(error) {
|
|
95
|
+
return { error: error }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
render() {
|
|
99
|
+
if (this.state.error !== null) return manageFailure(this.state.error)
|
|
100
|
+
return this.props.children
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
59
104
|
/** One labeled field row inside a management form. */
|
|
60
105
|
function ManageField({ label, children }) {
|
|
61
106
|
return react_jsx_runtime.jsxs('label', {
|
|
@@ -316,7 +361,7 @@ window.__ModuleLoader__.load({
|
|
|
316
361
|
react_jsx_runtime.jsx(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
317
362
|
variant: 'ghost',
|
|
318
363
|
size: 'sm',
|
|
319
|
-
icon: react_jsx_runtime.jsx(
|
|
364
|
+
icon: react_jsx_runtime.jsx(ManageIcons.refresh, { size: 14 }),
|
|
320
365
|
disabled: busy,
|
|
321
366
|
onClick: () => {
|
|
322
367
|
load()
|
|
@@ -326,7 +371,7 @@ window.__ModuleLoader__.load({
|
|
|
326
371
|
react_jsx_runtime.jsx(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
327
372
|
variant: 'primary',
|
|
328
373
|
size: 'sm',
|
|
329
|
-
icon: react_jsx_runtime.jsx(
|
|
374
|
+
icon: react_jsx_runtime.jsx(ManageIcons.plus, { size: 14 }),
|
|
330
375
|
disabled: busy,
|
|
331
376
|
onClick: () => {
|
|
332
377
|
if (fileInputRef.current !== null) fileInputRef.current.click()
|
|
@@ -387,7 +432,7 @@ window.__ModuleLoader__.load({
|
|
|
387
432
|
react_jsx_runtime.jsx(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
388
433
|
variant: 'ghost',
|
|
389
434
|
size: 'sm',
|
|
390
|
-
icon: react_jsx_runtime.jsx(
|
|
435
|
+
icon: react_jsx_runtime.jsx(ManageIcons.trash, { size: 14 }),
|
|
391
436
|
disabled: busy,
|
|
392
437
|
onClick: () => {
|
|
393
438
|
remove(skill.name)
|
|
@@ -558,7 +603,7 @@ window.__ModuleLoader__.load({
|
|
|
558
603
|
react_jsx_runtime.jsx(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
559
604
|
variant: 'ghost',
|
|
560
605
|
size: 'sm',
|
|
561
|
-
icon: react_jsx_runtime.jsx(
|
|
606
|
+
icon: react_jsx_runtime.jsx(ManageIcons.refresh, { size: 14 }),
|
|
562
607
|
disabled: busy,
|
|
563
608
|
onClick: () => {
|
|
564
609
|
load()
|
|
@@ -568,7 +613,7 @@ window.__ModuleLoader__.load({
|
|
|
568
613
|
react_jsx_runtime.jsx(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
569
614
|
variant: 'primary',
|
|
570
615
|
size: 'sm',
|
|
571
|
-
icon: react_jsx_runtime.jsx(
|
|
616
|
+
icon: react_jsx_runtime.jsx(ManageIcons.plus, { size: 14 }),
|
|
572
617
|
onClick: () => {
|
|
573
618
|
openForm(null)
|
|
574
619
|
},
|
|
@@ -808,8 +853,8 @@ window.__ModuleLoader__.load({
|
|
|
808
853
|
size: 'sm',
|
|
809
854
|
icon:
|
|
810
855
|
testing === server.name
|
|
811
|
-
? react_jsx_runtime.jsx(
|
|
812
|
-
: react_jsx_runtime.jsx(
|
|
856
|
+
? react_jsx_runtime.jsx(ManageIcons.loading, { size: 14 })
|
|
857
|
+
: react_jsx_runtime.jsx(ManageIcons.play, { size: 14 }),
|
|
813
858
|
disabled: busy || testing !== null,
|
|
814
859
|
onClick: () => {
|
|
815
860
|
test(server.name)
|
|
@@ -819,7 +864,7 @@ window.__ModuleLoader__.load({
|
|
|
819
864
|
react_jsx_runtime.jsx(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
820
865
|
variant: 'ghost',
|
|
821
866
|
size: 'sm',
|
|
822
|
-
icon: react_jsx_runtime.jsx(
|
|
867
|
+
icon: react_jsx_runtime.jsx(ManageIcons.edit, { size: 14 }),
|
|
823
868
|
disabled: busy || testing !== null,
|
|
824
869
|
onClick: () => {
|
|
825
870
|
openForm(server)
|
|
@@ -829,7 +874,7 @@ window.__ModuleLoader__.load({
|
|
|
829
874
|
react_jsx_runtime.jsx(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
830
875
|
variant: 'ghost',
|
|
831
876
|
size: 'sm',
|
|
832
|
-
icon: react_jsx_runtime.jsx(
|
|
877
|
+
icon: react_jsx_runtime.jsx(ManageIcons.trash, { size: 14 }),
|
|
833
878
|
disabled: busy || testing !== null,
|
|
834
879
|
onClick: () => {
|
|
835
880
|
remove(server.name)
|
|
@@ -979,7 +1024,17 @@ window.__ModuleLoader__.load({
|
|
|
979
1024
|
)
|
|
980
1025
|
var t = ctx.locale.bind(MANAGE_NS)
|
|
981
1026
|
var api = buildManageApi()
|
|
982
|
-
|
|
1027
|
+
/**
|
|
1028
|
+
* Hand each section its api and locale through the closure: a
|
|
1029
|
+
* registration's `inject` result is spread into props by the host, that
|
|
1030
|
+
* delivery has differed between host releases, and a registration whose
|
|
1031
|
+
* injected value cannot be built is retired without a console error (an
|
|
1032
|
+
* empty pane sitting next to a working nav entry).
|
|
1033
|
+
*/
|
|
1034
|
+
var deliver = (Component) => (props) =>
|
|
1035
|
+
react_jsx_runtime.jsx(ManageBoundary, {
|
|
1036
|
+
children: react_jsx_runtime.jsx(Component, { api: api, t: t, close: props !== null && typeof props === 'object' ? props.close : undefined }),
|
|
1037
|
+
})
|
|
983
1038
|
ctx.slots.inject(
|
|
984
1039
|
'settings.section',
|
|
985
1040
|
() =>
|
|
@@ -990,9 +1045,8 @@ window.__ModuleLoader__.load({
|
|
|
990
1045
|
order: 30,
|
|
991
1046
|
label: () => t('skills.nav'),
|
|
992
1047
|
locale: MANAGE_NS,
|
|
993
|
-
inject: injected,
|
|
994
1048
|
},
|
|
995
|
-
SkillsSection,
|
|
1049
|
+
deliver(SkillsSection),
|
|
996
1050
|
),
|
|
997
1051
|
)
|
|
998
1052
|
ctx.slots.inject(
|
|
@@ -1005,9 +1059,8 @@ window.__ModuleLoader__.load({
|
|
|
1005
1059
|
order: 40,
|
|
1006
1060
|
label: () => t('mcp.nav'),
|
|
1007
1061
|
locale: MANAGE_NS,
|
|
1008
|
-
inject: injected,
|
|
1009
1062
|
},
|
|
1010
|
-
McpSection,
|
|
1063
|
+
deliver(McpSection),
|
|
1011
1064
|
),
|
|
1012
1065
|
)
|
|
1013
1066
|
}
|
package/lib/index.js
CHANGED
|
@@ -19,7 +19,7 @@ export const inject = []
|
|
|
19
19
|
|
|
20
20
|
const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
21
21
|
/** Kept in step with package.json (the smoke test reports it to the server). */
|
|
22
|
-
const PLUGIN_VERSION = '0.2.
|
|
22
|
+
const PLUGIN_VERSION = '0.2.2'
|
|
23
23
|
const MCP_CONFIG_FILENAME = 'mcp-servers.yaml'
|
|
24
24
|
const MCP_PROFILE_ROW_PREFIX = 'mcp-'
|
|
25
25
|
/** Same grammar as @deepseek-ai/dsh-mcp-client's SERVER_NAME_PATTERN. */
|
|
@@ -520,28 +520,50 @@ function scrubbedParentEnv() {
|
|
|
520
520
|
* The MCP SDK is an optional peer dependency the host installation provides,
|
|
521
521
|
* loaded on demand so a missing copy disables only the connection smoke test
|
|
522
522
|
* instead of breaking the whole plugin at import time.
|
|
523
|
+
*
|
|
524
|
+
* The package was renamed across host releases, so both spellings are tried:
|
|
525
|
+
* `@modelcontextprotocol/client` (current) and `@modelcontextprotocol/sdk`
|
|
526
|
+
* (older). Both expose the same Client/transport API.
|
|
523
527
|
*/
|
|
524
528
|
let mcpSdkPromise
|
|
525
529
|
function loadMcpSdk() {
|
|
526
530
|
if (mcpSdkPromise === undefined) {
|
|
527
|
-
mcpSdkPromise =
|
|
528
|
-
|
|
529
|
-
import('@modelcontextprotocol/sdk/client/stdio.js'),
|
|
530
|
-
import('@modelcontextprotocol/sdk/client/streamableHttp.js'),
|
|
531
|
-
])
|
|
532
|
-
.then(([index, stdio, streamableHttp]) => ({
|
|
533
|
-
Client: index.Client,
|
|
534
|
-
StdioClientTransport: stdio.StdioClientTransport,
|
|
535
|
-
StreamableHTTPClientTransport: streamableHttp.StreamableHTTPClientTransport,
|
|
536
|
-
}))
|
|
537
|
-
.catch((error) => {
|
|
531
|
+
mcpSdkPromise = loadCurrentMcpSdk().catch((currentError) =>
|
|
532
|
+
loadLegacyMcpSdk().catch((legacyError) => {
|
|
538
533
|
mcpSdkPromise = undefined // let a later call retry after a transient failure
|
|
539
|
-
|
|
540
|
-
|
|
534
|
+
const reason = legacyError instanceof Error ? legacyError.message : String(legacyError)
|
|
535
|
+
const current = currentError instanceof Error ? currentError.message : String(currentError)
|
|
536
|
+
throw new ManageError('mcp-sdk-unavailable', `no MCP SDK in this installation (tried @modelcontextprotocol/client: ${current}; @modelcontextprotocol/sdk: ${reason})`)
|
|
537
|
+
}),
|
|
538
|
+
)
|
|
541
539
|
}
|
|
542
540
|
return mcpSdkPromise
|
|
543
541
|
}
|
|
544
542
|
|
|
543
|
+
/** The current `@modelcontextprotocol/client` spelling. */
|
|
544
|
+
async function loadCurrentMcpSdk() {
|
|
545
|
+
const [main, stdio] = await Promise.all([import('@modelcontextprotocol/client'), import('@modelcontextprotocol/client/stdio')])
|
|
546
|
+
return {
|
|
547
|
+
Client: main.Client,
|
|
548
|
+
StdioClientTransport: stdio.StdioClientTransport,
|
|
549
|
+
StreamableHTTPClientTransport: main.StreamableHTTPClientTransport,
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/** The older `@modelcontextprotocol/sdk` spelling. */
|
|
554
|
+
async function loadLegacyMcpSdk() {
|
|
555
|
+
const [index, stdio, streamableHttp] = await Promise.all([
|
|
556
|
+
import('@modelcontextprotocol/sdk/client/index.js'),
|
|
557
|
+
import('@modelcontextprotocol/sdk/client/stdio.js'),
|
|
558
|
+
import('@modelcontextprotocol/sdk/client/streamableHttp.js'),
|
|
559
|
+
])
|
|
560
|
+
return {
|
|
561
|
+
Client: index.Client,
|
|
562
|
+
StdioClientTransport: stdio.StdioClientTransport,
|
|
563
|
+
StreamableHTTPClientTransport: streamableHttp.StreamableHTTPClientTransport,
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
545
567
|
/** Connect to a server and list its tools (connection smoke test). */
|
|
546
568
|
async function testMcpConnection(server) {
|
|
547
569
|
const { Client, StdioClientTransport, StreamableHTTPClientTransport } = await loadMcpSdk()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mcp-skills",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "DeepSeek Harness web plugin: settings pages for managing user skills and MCP servers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -41,9 +41,13 @@
|
|
|
41
41
|
"yaml": "^2.4.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
44
45
|
"@modelcontextprotocol/sdk": "^1.30.0"
|
|
45
46
|
},
|
|
46
47
|
"peerDependenciesMeta": {
|
|
48
|
+
"@modelcontextprotocol/client": {
|
|
49
|
+
"optional": true
|
|
50
|
+
},
|
|
47
51
|
"@modelcontextprotocol/sdk": {
|
|
48
52
|
"optional": true
|
|
49
53
|
}
|