@seastudio/sdk 3.0.2 → 3.0.4

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.
Files changed (68) hide show
  1. package/README.md +14 -0
  2. package/bin/seastudio.js +0 -0
  3. package/dist/{chunk-TFOJLA2F.cjs → chunk-3I7UM66P.cjs} +9 -7
  4. package/dist/{chunk-SUF5BPSK.cjs → chunk-3IVOSJIO.cjs} +3 -3
  5. package/dist/chunk-AXT6ZLY2.js +98 -0
  6. package/dist/{chunk-XU5GEWWL.js → chunk-GPM5XGYM.js} +1 -1
  7. package/dist/chunk-PYYG6U4M.cjs +109 -0
  8. package/dist/{chunk-ANWOL7SM.js → chunk-TJ3CGHWJ.js} +9 -7
  9. package/dist/develop-tool/cli/index.cjs +79 -7
  10. package/dist/develop-tool/cli/index.js +79 -7
  11. package/dist/index.cjs +96 -246
  12. package/dist/index.d.cts +3 -8
  13. package/dist/index.d.ts +3 -8
  14. package/dist/index.js +3 -9
  15. package/dist/mcp/core/index.cjs +22 -22
  16. package/dist/mcp/core/index.d.cts +10 -7
  17. package/dist/mcp/core/index.d.ts +10 -7
  18. package/dist/mcp/core/index.js +1 -1
  19. package/dist/mcp/index.cjs +49 -199
  20. package/dist/mcp/index.d.cts +13 -84
  21. package/dist/mcp/index.d.ts +13 -84
  22. package/dist/mcp/index.js +3 -9
  23. package/dist/mcp/seastudio/index.cjs +19 -19
  24. package/dist/mcp/seastudio/index.d.cts +1 -1
  25. package/dist/mcp/seastudio/index.d.ts +1 -1
  26. package/dist/mcp/seastudio/index.js +2 -2
  27. package/dist/{types-B1Zqr-0Q.d.cts → types-CUFTi2bZ.d.cts} +10 -1
  28. package/dist/{types-B1Zqr-0Q.d.ts → types-CUFTi2bZ.d.ts} +10 -1
  29. package/package.json +2 -22
  30. package/src/develop-tool/templates/plugin/frontend/package.json.tmpl +2 -2
  31. package/src/develop-tool/templates/plugin/frontend/src/main.tsx +3 -0
  32. package/src/develop-tool/templates/plugin/frontend/src/mcp-entry.ts.tmpl +12 -0
  33. package/dist/chunk-4ITOR5QE.js +0 -901
  34. package/dist/chunk-CVF4QHS6.cjs +0 -436
  35. package/dist/chunk-DSOSHJH2.js +0 -643
  36. package/dist/chunk-FLATZQA2.js +0 -174
  37. package/dist/chunk-G66KY35N.js +0 -334
  38. package/dist/chunk-HJJTBVKQ.cjs +0 -909
  39. package/dist/chunk-ISI2OLPI.cjs +0 -179
  40. package/dist/chunk-MYURVLGP.cjs +0 -165
  41. package/dist/chunk-ORBVHAAS.cjs +0 -341
  42. package/dist/chunk-QD4KISXM.js +0 -160
  43. package/dist/chunk-S2UIBWKA.js +0 -99
  44. package/dist/chunk-SNGU4SHO.cjs +0 -654
  45. package/dist/chunk-XIPL7VSP.cjs +0 -110
  46. package/dist/chunk-Z7LV7DCO.js +0 -429
  47. package/dist/mcp/plugin-editor/index.cjs +0 -47
  48. package/dist/mcp/plugin-editor/index.d.cts +0 -98
  49. package/dist/mcp/plugin-editor/index.d.ts +0 -98
  50. package/dist/mcp/plugin-editor/index.js +0 -2
  51. package/dist/mcp/plugin-excel/index.cjs +0 -31
  52. package/dist/mcp/plugin-excel/index.d.cts +0 -86
  53. package/dist/mcp/plugin-excel/index.d.ts +0 -86
  54. package/dist/mcp/plugin-excel/index.js +0 -2
  55. package/dist/mcp/plugin-preview/index.cjs +0 -23
  56. package/dist/mcp/plugin-preview/index.d.cts +0 -109
  57. package/dist/mcp/plugin-preview/index.d.ts +0 -109
  58. package/dist/mcp/plugin-preview/index.js +0 -2
  59. package/dist/mcp/plugin-seaflow/index.cjs +0 -35
  60. package/dist/mcp/plugin-seaflow/index.d.cts +0 -318
  61. package/dist/mcp/plugin-seaflow/index.d.ts +0 -318
  62. package/dist/mcp/plugin-seaflow/index.js +0 -2
  63. package/dist/mcp/plugin-word/index.cjs +0 -31
  64. package/dist/mcp/plugin-word/index.d.cts +0 -86
  65. package/dist/mcp/plugin-word/index.d.ts +0 -86
  66. package/dist/mcp/plugin-word/index.js +0 -2
  67. package/dist/tools-LMW67LIY.js +0 -2
  68. package/dist/tools-TU7PBMDO.cjs +0 -23
package/README.md CHANGED
@@ -68,6 +68,20 @@ await preview.get('/path/to/file');
68
68
 
69
69
  说明:`@seastudio/sdk/mcp` 现在的官方稳定支持面聚焦在 raw MCP catalog、包索引和通用转换能力,不包含额外的上层适配逻辑。
70
70
 
71
+ ### 指定插件实例调用 Tool
72
+
73
+ ```typescript
74
+ import { callHostTool } from '@seastudio/sdk';
75
+
76
+ await callHostTool(
77
+ 'plugin-editor_open_file',
78
+ { path: '/tmp/demo.ts' },
79
+ { pluginInstanceId: 'plugin-123' }
80
+ );
81
+ ```
82
+
83
+ `pluginInstanceId` 是 `tools/call` 的可选协议字段,用于在同一插件存在多个实例时精确路由到目标实例。宿主在已知上下文时也可以自动补齐这个字段,因此大多数常规调用不需要手动传入。
84
+
71
85
  ### 动态加载插件 MCP
72
86
 
73
87
  ```typescript
package/bin/seastudio.js CHANGED
File without changes
@@ -294,10 +294,12 @@ var MCPClient = class {
294
294
  return this.timeout;
295
295
  }
296
296
  async callTool(name, args = {}, options) {
297
- return this.request("tools/call", {
297
+ const request = {
298
298
  name,
299
- arguments: args
300
- }, {
299
+ arguments: args,
300
+ ...typeof options?.pluginInstanceId === "string" && options.pluginInstanceId.trim() ? { pluginInstanceId: options.pluginInstanceId.trim() } : {}
301
+ };
302
+ return this.request("tools/call", request, {
301
303
  timeout: this.resolveToolCallTimeout(name, args, options)
302
304
  });
303
305
  }
@@ -379,11 +381,11 @@ function getDefaultClient() {
379
381
  function setDefaultClient(client) {
380
382
  defaultClient = client;
381
383
  }
382
- async function callHostTool(toolName, args = {}) {
383
- return getDefaultClient().callTool(toolName, args);
384
+ async function callHostTool(toolName, args = {}, options) {
385
+ return getDefaultClient().callTool(toolName, args, options);
384
386
  }
385
- async function callHostToolText(toolName, args = {}) {
386
- return getDefaultClient().callToolText(toolName, args);
387
+ async function callHostToolText(toolName, args = {}, options) {
388
+ return getDefaultClient().callToolText(toolName, args, options);
387
389
  }
388
390
 
389
391
  // src/mcp/core/server.ts
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkTFOJLA2F_cjs = require('./chunk-TFOJLA2F.cjs');
3
+ var chunk3I7UM66P_cjs = require('./chunk-3I7UM66P.cjs');
4
4
 
5
5
  // src/mcp/seastudio/tools-plugin.ts
6
6
  var pluginManagementTools = [
@@ -1127,10 +1127,10 @@ var allTools = [
1127
1127
  ];
1128
1128
  var tools = allTools;
1129
1129
  async function callTool(name, args = {}) {
1130
- return chunkTFOJLA2F_cjs.getDefaultClient().callTool(name, args);
1130
+ return chunk3I7UM66P_cjs.getDefaultClient().callTool(name, args);
1131
1131
  }
1132
1132
  async function request(method, params) {
1133
- return chunkTFOJLA2F_cjs.getDefaultClient().request(method, params);
1133
+ return chunk3I7UM66P_cjs.getDefaultClient().request(method, params);
1134
1134
  }
1135
1135
  function usesAbsolutePathMode(mode) {
1136
1136
  return mode === "absolute";
@@ -0,0 +1,98 @@
1
+ import { allTools } from './chunk-GPM5XGYM.js';
2
+ import { normalizeMCPTool, normalizeMCPToolObjectSchema, getDefaultClient } from './chunk-TJ3CGHWJ.js';
3
+
4
+ // src/mcp/index.ts
5
+ async function loadPlugin(pluginName) {
6
+ if (pluginName === "seastudio") {
7
+ return import('./mcp/seastudio/index.js');
8
+ }
9
+ throw new Error(`Unknown plugin: ${pluginName}. \u63D2\u4EF6 MCP \u4E0D\u518D\u901A\u8FC7 SDK \u9759\u6001\u5BFC\u5165\u3002`);
10
+ }
11
+ var MCP_PACKAGES = [
12
+ { id: "seastudio", name: "SeaStudio", description: "\u6587\u4EF6/Shell/Git \u57FA\u7840\u80FD\u529B", tools: allTools }
13
+ ];
14
+ function mcpToolToOpenAI(tool) {
15
+ const normalizedTool = normalizeMCPTool(tool);
16
+ const outputSchema = normalizedTool.outputSchema ? normalizeMCPToolObjectSchema(normalizedTool.outputSchema) : void 0;
17
+ return {
18
+ type: "function",
19
+ ...normalizedTool.annotations ? { annotations: { ...normalizedTool.annotations } } : {},
20
+ ...outputSchema ? { outputSchema } : {},
21
+ function: {
22
+ name: normalizedTool.name,
23
+ description: normalizedTool.description,
24
+ parameters: normalizeMCPToolObjectSchema(normalizedTool.inputSchema),
25
+ ...outputSchema ? { outputSchema } : {}
26
+ }
27
+ };
28
+ }
29
+ function listAllTools() {
30
+ return [...allTools];
31
+ }
32
+ function toPackageName(source) {
33
+ if (source === "seastudio") {
34
+ return "SeaStudio";
35
+ }
36
+ return source;
37
+ }
38
+ function toPackageDescription(source) {
39
+ if (source === "seastudio") {
40
+ return "SeaStudio \u539F\u751F MCP \u80FD\u529B";
41
+ }
42
+ return `\u6765\u81EA\u63D2\u4EF6 ${source} \u7684 MCP \u80FD\u529B`;
43
+ }
44
+ function normalizeAvailableTool(raw) {
45
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
46
+ return null;
47
+ }
48
+ const tool = raw;
49
+ if (typeof tool.name !== "string" || typeof tool.description !== "string" || typeof tool.inputSchema !== "object") {
50
+ return null;
51
+ }
52
+ const normalized = normalizeMCPTool(tool);
53
+ const source = typeof tool.source === "string" && tool.source.trim() ? tool.source.trim() : "seastudio";
54
+ return {
55
+ ...normalized,
56
+ source
57
+ };
58
+ }
59
+ var MCP_TOOL_PACKAGE_INDEX = new Map(
60
+ allTools.map((tool) => [tool.name, "seastudio"])
61
+ );
62
+ function getMCPToolPackageIndex() {
63
+ return new Map(MCP_TOOL_PACKAGE_INDEX);
64
+ }
65
+ async function listAvailableTools() {
66
+ const result = await getDefaultClient().request("tools/list");
67
+ const rawTools = Array.isArray(result?.tools) ? result.tools : [];
68
+ return rawTools.map(normalizeAvailableTool).filter((tool) => Boolean(tool));
69
+ }
70
+ async function getMCPPackageIdForTool(toolName) {
71
+ const tool = (await listAvailableTools()).find((item) => item.name === toolName);
72
+ return tool?.source ?? null;
73
+ }
74
+ async function listAvailableToolsForLLM() {
75
+ return (await listAvailableTools()).map(mcpToolToOpenAI);
76
+ }
77
+ async function getMCPPackages() {
78
+ const grouped = /* @__PURE__ */ new Map();
79
+ for (const tool of await listAvailableTools()) {
80
+ const existing = grouped.get(tool.source);
81
+ if (existing) {
82
+ existing.toolCount += 1;
83
+ continue;
84
+ }
85
+ grouped.set(tool.source, {
86
+ id: tool.source,
87
+ name: toPackageName(tool.source),
88
+ description: toPackageDescription(tool.source),
89
+ toolCount: 1
90
+ });
91
+ }
92
+ return [...grouped.values()];
93
+ }
94
+ async function getToolsForLLM(disabledPackages) {
95
+ return (await listAvailableTools()).filter((tool) => !disabledPackages.has(tool.source)).map(mcpToolToOpenAI);
96
+ }
97
+
98
+ export { MCP_PACKAGES, getMCPPackageIdForTool, getMCPPackages, getMCPToolPackageIndex, getToolsForLLM, listAllTools, listAvailableTools, listAvailableToolsForLLM, loadPlugin, mcpToolToOpenAI };
@@ -1,4 +1,4 @@
1
- import { getDefaultClient } from './chunk-ANWOL7SM.js';
1
+ import { getDefaultClient } from './chunk-TJ3CGHWJ.js';
2
2
 
3
3
  // src/mcp/seastudio/tools-plugin.ts
4
4
  var pluginManagementTools = [
@@ -0,0 +1,109 @@
1
+ 'use strict';
2
+
3
+ var chunk3IVOSJIO_cjs = require('./chunk-3IVOSJIO.cjs');
4
+ var chunk3I7UM66P_cjs = require('./chunk-3I7UM66P.cjs');
5
+
6
+ // src/mcp/index.ts
7
+ async function loadPlugin(pluginName) {
8
+ if (pluginName === "seastudio") {
9
+ return import('./mcp/seastudio/index.cjs');
10
+ }
11
+ throw new Error(`Unknown plugin: ${pluginName}. \u63D2\u4EF6 MCP \u4E0D\u518D\u901A\u8FC7 SDK \u9759\u6001\u5BFC\u5165\u3002`);
12
+ }
13
+ var MCP_PACKAGES = [
14
+ { id: "seastudio", name: "SeaStudio", description: "\u6587\u4EF6/Shell/Git \u57FA\u7840\u80FD\u529B", tools: chunk3IVOSJIO_cjs.allTools }
15
+ ];
16
+ function mcpToolToOpenAI(tool) {
17
+ const normalizedTool = chunk3I7UM66P_cjs.normalizeMCPTool(tool);
18
+ const outputSchema = normalizedTool.outputSchema ? chunk3I7UM66P_cjs.normalizeMCPToolObjectSchema(normalizedTool.outputSchema) : void 0;
19
+ return {
20
+ type: "function",
21
+ ...normalizedTool.annotations ? { annotations: { ...normalizedTool.annotations } } : {},
22
+ ...outputSchema ? { outputSchema } : {},
23
+ function: {
24
+ name: normalizedTool.name,
25
+ description: normalizedTool.description,
26
+ parameters: chunk3I7UM66P_cjs.normalizeMCPToolObjectSchema(normalizedTool.inputSchema),
27
+ ...outputSchema ? { outputSchema } : {}
28
+ }
29
+ };
30
+ }
31
+ function listAllTools() {
32
+ return [...chunk3IVOSJIO_cjs.allTools];
33
+ }
34
+ function toPackageName(source) {
35
+ if (source === "seastudio") {
36
+ return "SeaStudio";
37
+ }
38
+ return source;
39
+ }
40
+ function toPackageDescription(source) {
41
+ if (source === "seastudio") {
42
+ return "SeaStudio \u539F\u751F MCP \u80FD\u529B";
43
+ }
44
+ return `\u6765\u81EA\u63D2\u4EF6 ${source} \u7684 MCP \u80FD\u529B`;
45
+ }
46
+ function normalizeAvailableTool(raw) {
47
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
48
+ return null;
49
+ }
50
+ const tool = raw;
51
+ if (typeof tool.name !== "string" || typeof tool.description !== "string" || typeof tool.inputSchema !== "object") {
52
+ return null;
53
+ }
54
+ const normalized = chunk3I7UM66P_cjs.normalizeMCPTool(tool);
55
+ const source = typeof tool.source === "string" && tool.source.trim() ? tool.source.trim() : "seastudio";
56
+ return {
57
+ ...normalized,
58
+ source
59
+ };
60
+ }
61
+ var MCP_TOOL_PACKAGE_INDEX = new Map(
62
+ chunk3IVOSJIO_cjs.allTools.map((tool) => [tool.name, "seastudio"])
63
+ );
64
+ function getMCPToolPackageIndex() {
65
+ return new Map(MCP_TOOL_PACKAGE_INDEX);
66
+ }
67
+ async function listAvailableTools() {
68
+ const result = await chunk3I7UM66P_cjs.getDefaultClient().request("tools/list");
69
+ const rawTools = Array.isArray(result?.tools) ? result.tools : [];
70
+ return rawTools.map(normalizeAvailableTool).filter((tool) => Boolean(tool));
71
+ }
72
+ async function getMCPPackageIdForTool(toolName) {
73
+ const tool = (await listAvailableTools()).find((item) => item.name === toolName);
74
+ return tool?.source ?? null;
75
+ }
76
+ async function listAvailableToolsForLLM() {
77
+ return (await listAvailableTools()).map(mcpToolToOpenAI);
78
+ }
79
+ async function getMCPPackages() {
80
+ const grouped = /* @__PURE__ */ new Map();
81
+ for (const tool of await listAvailableTools()) {
82
+ const existing = grouped.get(tool.source);
83
+ if (existing) {
84
+ existing.toolCount += 1;
85
+ continue;
86
+ }
87
+ grouped.set(tool.source, {
88
+ id: tool.source,
89
+ name: toPackageName(tool.source),
90
+ description: toPackageDescription(tool.source),
91
+ toolCount: 1
92
+ });
93
+ }
94
+ return [...grouped.values()];
95
+ }
96
+ async function getToolsForLLM(disabledPackages) {
97
+ return (await listAvailableTools()).filter((tool) => !disabledPackages.has(tool.source)).map(mcpToolToOpenAI);
98
+ }
99
+
100
+ exports.MCP_PACKAGES = MCP_PACKAGES;
101
+ exports.getMCPPackageIdForTool = getMCPPackageIdForTool;
102
+ exports.getMCPPackages = getMCPPackages;
103
+ exports.getMCPToolPackageIndex = getMCPToolPackageIndex;
104
+ exports.getToolsForLLM = getToolsForLLM;
105
+ exports.listAllTools = listAllTools;
106
+ exports.listAvailableTools = listAvailableTools;
107
+ exports.listAvailableToolsForLLM = listAvailableToolsForLLM;
108
+ exports.loadPlugin = loadPlugin;
109
+ exports.mcpToolToOpenAI = mcpToolToOpenAI;
@@ -292,10 +292,12 @@ var MCPClient = class {
292
292
  return this.timeout;
293
293
  }
294
294
  async callTool(name, args = {}, options) {
295
- return this.request("tools/call", {
295
+ const request = {
296
296
  name,
297
- arguments: args
298
- }, {
297
+ arguments: args,
298
+ ...typeof options?.pluginInstanceId === "string" && options.pluginInstanceId.trim() ? { pluginInstanceId: options.pluginInstanceId.trim() } : {}
299
+ };
300
+ return this.request("tools/call", request, {
299
301
  timeout: this.resolveToolCallTimeout(name, args, options)
300
302
  });
301
303
  }
@@ -377,11 +379,11 @@ function getDefaultClient() {
377
379
  function setDefaultClient(client) {
378
380
  defaultClient = client;
379
381
  }
380
- async function callHostTool(toolName, args = {}) {
381
- return getDefaultClient().callTool(toolName, args);
382
+ async function callHostTool(toolName, args = {}, options) {
383
+ return getDefaultClient().callTool(toolName, args, options);
382
384
  }
383
- async function callHostToolText(toolName, args = {}) {
384
- return getDefaultClient().callToolText(toolName, args);
385
+ async function callHostToolText(toolName, args = {}, options) {
386
+ return getDefaultClient().callToolText(toolName, args, options);
385
387
  }
386
388
 
387
389
  // src/mcp/core/server.ts
@@ -9,8 +9,8 @@ var fs = require('fs');
9
9
  var url = require('url');
10
10
  var chalk = require('chalk');
11
11
  var ora2 = require('ora');
12
- var os = require('os');
13
12
  var child_process = require('child_process');
13
+ var os = require('os');
14
14
  var https = require('https');
15
15
  var promises$1 = require('stream/promises');
16
16
  var archiver = require('archiver');
@@ -438,6 +438,73 @@ app.listen(port, '127.0.0.1', () => {
438
438
  process.exit(1);
439
439
  }
440
440
  });
441
+ function normalizeTools(tools) {
442
+ return tools.map((tool) => JSON.parse(JSON.stringify({
443
+ name: tool.name,
444
+ description: tool.description,
445
+ inputSchema: tool.inputSchema,
446
+ ...tool.outputSchema ? { outputSchema: tool.outputSchema } : {},
447
+ ...tool.annotations ? { annotations: tool.annotations } : {}
448
+ })));
449
+ }
450
+ function loadManifestFromEntry(entryPath) {
451
+ const readerScript = `
452
+ import { pathToFileURL } from 'node:url';
453
+ const entryPath = process.argv[1];
454
+ const mod = await import(pathToFileURL(entryPath).href);
455
+ const manifest = mod.pluginMcpManifest ?? (Array.isArray(mod.mcpTools)
456
+ ? { schemaVersion: 1, tools: mod.mcpTools }
457
+ : null);
458
+ if (!manifest) {
459
+ throw new Error('frontend/src/mcp-entry.ts \u5FC5\u987B\u5BFC\u51FA pluginMcpManifest \u6216 mcpTools');
460
+ }
461
+ process.stdout.write(JSON.stringify(manifest));
462
+ `;
463
+ const result = child_process.spawnSync(process.execPath, [
464
+ "--experimental-strip-types",
465
+ "--experimental-specifier-resolution=node",
466
+ "--input-type=module",
467
+ "-e",
468
+ readerScript,
469
+ entryPath
470
+ ], {
471
+ encoding: "utf-8"
472
+ });
473
+ if (result.status !== 0) {
474
+ throw new Error((result.stderr || result.stdout || "\u8BFB\u53D6 frontend/src/mcp-entry.ts \u5931\u8D25").trim());
475
+ }
476
+ const parsed = JSON.parse(result.stdout || "{}");
477
+ return {
478
+ schemaVersion: 1,
479
+ tools: normalizeTools(parsed.tools ?? [])
480
+ };
481
+ }
482
+ async function generatePluginMcpManifest(projectDir) {
483
+ const frontendDir = fs.existsSync(path.join(projectDir, "src")) ? projectDir : path.join(projectDir, "frontend");
484
+ const entryPath = path.join(frontendDir, "src", "mcp-entry.ts");
485
+ const outputPath = path.join(frontendDir, "dist", "mcp-manifest.json");
486
+ if (!fs.existsSync(entryPath)) {
487
+ throw new Error(`\u672A\u627E\u5230\u6807\u51C6 MCP \u5165\u53E3\u6587\u4EF6: ${entryPath}`);
488
+ }
489
+ const manifest = loadManifestFromEntry(entryPath);
490
+ await promises.mkdir(path.dirname(outputPath), { recursive: true });
491
+ await promises.writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}
492
+ `, "utf-8");
493
+ return {
494
+ outputPath,
495
+ toolCount: manifest.tools.length
496
+ };
497
+ }
498
+ var generateMcpManifestCommand = new commander.Command("generate-mcp-manifest").description("\u4ECE frontend/src/mcp-entry.ts \u751F\u6210 dist/mcp-manifest.json").option("-d, --dir <directory>", "\u63D2\u4EF6\u9879\u76EE\u76EE\u5F55", ".").action(async (options) => {
499
+ try {
500
+ const projectDir = path.resolve(process.cwd(), options.dir || ".");
501
+ const result = await generatePluginMcpManifest(projectDir);
502
+ console.log(`Generated ${result.outputPath} (${result.toolCount} tools)`);
503
+ } catch (error) {
504
+ console.error(error instanceof Error ? error.message : String(error));
505
+ process.exit(1);
506
+ }
507
+ });
441
508
  var PYTHON_STANDALONE_RELEASE_API = "https://api.github.com/repos/astral-sh/python-build-standalone/releases/latest";
442
509
  var TARGET_PYTHON_MINOR = Number(process.env.SEASTUDIO_AGENT_PYTHON_MINOR) || 12;
443
510
  var MAX_PYTHON_MINOR = 13;
@@ -605,11 +672,11 @@ function pickPythonRuntimeAsset(assets, target) {
605
672
  return matches[0];
606
673
  }
607
674
  async function fetchText(url, headers) {
608
- return new Promise((resolve3, reject) => {
675
+ return new Promise((resolve4, reject) => {
609
676
  https__default.default.get(url, { headers }, (res) => {
610
677
  if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
611
678
  res.resume();
612
- fetchText(res.headers.location, headers).then(resolve3).catch(reject);
679
+ fetchText(res.headers.location, headers).then(resolve4).catch(reject);
613
680
  return;
614
681
  }
615
682
  if (res.statusCode && res.statusCode >= 400) {
@@ -619,13 +686,13 @@ async function fetchText(url, headers) {
619
686
  }
620
687
  const chunks = [];
621
688
  res.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
622
- res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
689
+ res.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
623
690
  res.on("error", reject);
624
691
  }).on("error", reject);
625
692
  });
626
693
  }
627
694
  async function sleep(ms) {
628
- await new Promise((resolve3) => setTimeout(resolve3, ms));
695
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
629
696
  }
630
697
  async function withRetries(label, task) {
631
698
  let lastError;
@@ -960,6 +1027,10 @@ var packCommand = new commander.Command("pack").description("\u6253\u5305\u63D2\
960
1027
  `);
961
1028
  process.exit(1);
962
1029
  }
1030
+ if (config.type === "plugin") {
1031
+ const generated = await generatePluginMcpManifest(projectDir);
1032
+ logPackStep(`\u751F\u6210 MCP manifest\uFF1A${generated.outputPath} (${generated.toolCount} tools)`);
1033
+ }
963
1034
  spinnerDist.succeed("frontend/dist/index.html \u5DF2\u627E\u5230");
964
1035
  } else {
965
1036
  const spinnerDist = ora2__default.default("\u8FDC\u7A0B\u63D2\u4EF6\uFF0C\u8DF3\u8FC7 frontend build \u68C0\u67E5").start();
@@ -1093,8 +1164,8 @@ var installCommand = new commander.Command("install").description("\u5B89\u88C5
1093
1164
  const tempDir = path.join(targetRoot, ".temp-install-" + Date.now());
1094
1165
  fs.mkdirSync(tempDir, { recursive: true });
1095
1166
  console.log(`\u{1F4E6} \u89E3\u538B\u5B89\u88C5\u5305: ${path.basename(archivePath)}`);
1096
- await new Promise((resolve3, reject) => {
1097
- fs.createReadStream(archivePath).pipe(unzipper.Extract({ path: tempDir })).on("close", resolve3).on("error", reject);
1167
+ await new Promise((resolve4, reject) => {
1168
+ fs.createReadStream(archivePath).pipe(unzipper.Extract({ path: tempDir })).on("close", resolve4).on("error", reject);
1098
1169
  });
1099
1170
  const configPath = path.join(tempDir, "seastudio.config.json");
1100
1171
  if (!fs.existsSync(configPath)) {
@@ -1133,6 +1204,7 @@ var installCommand = new commander.Command("install").description("\u5B89\u88C5
1133
1204
  var program = new commander.Command();
1134
1205
  program.name("seastudio").description("SeaStudio Plugin Development CLI").version("1.0.0");
1135
1206
  program.addCommand(createCommand);
1207
+ program.addCommand(generateMcpManifestCommand);
1136
1208
  program.addCommand(packCommand);
1137
1209
  program.addCommand(installCommand);
1138
1210
  program.parse();
@@ -7,8 +7,8 @@ import { existsSync, createWriteStream, mkdirSync, createReadStream } from 'fs';
7
7
  import { fileURLToPath } from 'url';
8
8
  import chalk from 'chalk';
9
9
  import ora2 from 'ora';
10
- import { tmpdir } from 'os';
11
10
  import { spawnSync, spawn } from 'child_process';
11
+ import { tmpdir } from 'os';
12
12
  import https from 'https';
13
13
  import { pipeline } from 'stream/promises';
14
14
  import archiver from 'archiver';
@@ -427,6 +427,73 @@ app.listen(port, '127.0.0.1', () => {
427
427
  process.exit(1);
428
428
  }
429
429
  });
430
+ function normalizeTools(tools) {
431
+ return tools.map((tool) => JSON.parse(JSON.stringify({
432
+ name: tool.name,
433
+ description: tool.description,
434
+ inputSchema: tool.inputSchema,
435
+ ...tool.outputSchema ? { outputSchema: tool.outputSchema } : {},
436
+ ...tool.annotations ? { annotations: tool.annotations } : {}
437
+ })));
438
+ }
439
+ function loadManifestFromEntry(entryPath) {
440
+ const readerScript = `
441
+ import { pathToFileURL } from 'node:url';
442
+ const entryPath = process.argv[1];
443
+ const mod = await import(pathToFileURL(entryPath).href);
444
+ const manifest = mod.pluginMcpManifest ?? (Array.isArray(mod.mcpTools)
445
+ ? { schemaVersion: 1, tools: mod.mcpTools }
446
+ : null);
447
+ if (!manifest) {
448
+ throw new Error('frontend/src/mcp-entry.ts \u5FC5\u987B\u5BFC\u51FA pluginMcpManifest \u6216 mcpTools');
449
+ }
450
+ process.stdout.write(JSON.stringify(manifest));
451
+ `;
452
+ const result = spawnSync(process.execPath, [
453
+ "--experimental-strip-types",
454
+ "--experimental-specifier-resolution=node",
455
+ "--input-type=module",
456
+ "-e",
457
+ readerScript,
458
+ entryPath
459
+ ], {
460
+ encoding: "utf-8"
461
+ });
462
+ if (result.status !== 0) {
463
+ throw new Error((result.stderr || result.stdout || "\u8BFB\u53D6 frontend/src/mcp-entry.ts \u5931\u8D25").trim());
464
+ }
465
+ const parsed = JSON.parse(result.stdout || "{}");
466
+ return {
467
+ schemaVersion: 1,
468
+ tools: normalizeTools(parsed.tools ?? [])
469
+ };
470
+ }
471
+ async function generatePluginMcpManifest(projectDir) {
472
+ const frontendDir = existsSync(join(projectDir, "src")) ? projectDir : join(projectDir, "frontend");
473
+ const entryPath = join(frontendDir, "src", "mcp-entry.ts");
474
+ const outputPath = join(frontendDir, "dist", "mcp-manifest.json");
475
+ if (!existsSync(entryPath)) {
476
+ throw new Error(`\u672A\u627E\u5230\u6807\u51C6 MCP \u5165\u53E3\u6587\u4EF6: ${entryPath}`);
477
+ }
478
+ const manifest = loadManifestFromEntry(entryPath);
479
+ await mkdir(dirname(outputPath), { recursive: true });
480
+ await writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}
481
+ `, "utf-8");
482
+ return {
483
+ outputPath,
484
+ toolCount: manifest.tools.length
485
+ };
486
+ }
487
+ var generateMcpManifestCommand = new Command("generate-mcp-manifest").description("\u4ECE frontend/src/mcp-entry.ts \u751F\u6210 dist/mcp-manifest.json").option("-d, --dir <directory>", "\u63D2\u4EF6\u9879\u76EE\u76EE\u5F55", ".").action(async (options) => {
488
+ try {
489
+ const projectDir = resolve(process.cwd(), options.dir || ".");
490
+ const result = await generatePluginMcpManifest(projectDir);
491
+ console.log(`Generated ${result.outputPath} (${result.toolCount} tools)`);
492
+ } catch (error) {
493
+ console.error(error instanceof Error ? error.message : String(error));
494
+ process.exit(1);
495
+ }
496
+ });
430
497
  var PYTHON_STANDALONE_RELEASE_API = "https://api.github.com/repos/astral-sh/python-build-standalone/releases/latest";
431
498
  var TARGET_PYTHON_MINOR = Number(process.env.SEASTUDIO_AGENT_PYTHON_MINOR) || 12;
432
499
  var MAX_PYTHON_MINOR = 13;
@@ -594,11 +661,11 @@ function pickPythonRuntimeAsset(assets, target) {
594
661
  return matches[0];
595
662
  }
596
663
  async function fetchText(url, headers) {
597
- return new Promise((resolve3, reject) => {
664
+ return new Promise((resolve4, reject) => {
598
665
  https.get(url, { headers }, (res) => {
599
666
  if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
600
667
  res.resume();
601
- fetchText(res.headers.location, headers).then(resolve3).catch(reject);
668
+ fetchText(res.headers.location, headers).then(resolve4).catch(reject);
602
669
  return;
603
670
  }
604
671
  if (res.statusCode && res.statusCode >= 400) {
@@ -608,13 +675,13 @@ async function fetchText(url, headers) {
608
675
  }
609
676
  const chunks = [];
610
677
  res.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
611
- res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
678
+ res.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
612
679
  res.on("error", reject);
613
680
  }).on("error", reject);
614
681
  });
615
682
  }
616
683
  async function sleep(ms) {
617
- await new Promise((resolve3) => setTimeout(resolve3, ms));
684
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
618
685
  }
619
686
  async function withRetries(label, task) {
620
687
  let lastError;
@@ -949,6 +1016,10 @@ var packCommand = new Command("pack").description("\u6253\u5305\u63D2\u4EF6\u621
949
1016
  `);
950
1017
  process.exit(1);
951
1018
  }
1019
+ if (config.type === "plugin") {
1020
+ const generated = await generatePluginMcpManifest(projectDir);
1021
+ logPackStep(`\u751F\u6210 MCP manifest\uFF1A${generated.outputPath} (${generated.toolCount} tools)`);
1022
+ }
952
1023
  spinnerDist.succeed("frontend/dist/index.html \u5DF2\u627E\u5230");
953
1024
  } else {
954
1025
  const spinnerDist = ora2("\u8FDC\u7A0B\u63D2\u4EF6\uFF0C\u8DF3\u8FC7 frontend build \u68C0\u67E5").start();
@@ -1082,8 +1153,8 @@ var installCommand = new Command("install").description("\u5B89\u88C5 .seaplugin
1082
1153
  const tempDir = join(targetRoot, ".temp-install-" + Date.now());
1083
1154
  mkdirSync(tempDir, { recursive: true });
1084
1155
  console.log(`\u{1F4E6} \u89E3\u538B\u5B89\u88C5\u5305: ${basename(archivePath)}`);
1085
- await new Promise((resolve3, reject) => {
1086
- createReadStream(archivePath).pipe(Extract({ path: tempDir })).on("close", resolve3).on("error", reject);
1156
+ await new Promise((resolve4, reject) => {
1157
+ createReadStream(archivePath).pipe(Extract({ path: tempDir })).on("close", resolve4).on("error", reject);
1087
1158
  });
1088
1159
  const configPath = join(tempDir, "seastudio.config.json");
1089
1160
  if (!existsSync(configPath)) {
@@ -1122,6 +1193,7 @@ var installCommand = new Command("install").description("\u5B89\u88C5 .seaplugin
1122
1193
  var program = new Command();
1123
1194
  program.name("seastudio").description("SeaStudio Plugin Development CLI").version("1.0.0");
1124
1195
  program.addCommand(createCommand);
1196
+ program.addCommand(generateMcpManifestCommand);
1125
1197
  program.addCommand(packCommand);
1126
1198
  program.addCommand(installCommand);
1127
1199
  program.parse();