@actiondock/mcp 2.0.9 → 2.0.11-beta.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/package.json +3 -3
- package/src/adapter.ts +77 -3
- package/src/stdio.ts +39 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actiondock/mcp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.11-beta.0",
|
|
4
4
|
"description": "ActionDock MCP Adapter for exposing Actions as Model Context Protocol (MCP) Tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"test": "bun test"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@actiondock/core": "
|
|
31
|
-
"@actiondock/sdk": "
|
|
30
|
+
"@actiondock/core": "2.0.11-beta.0",
|
|
31
|
+
"@actiondock/sdk": "2.0.11-beta.0",
|
|
32
32
|
"@modelcontextprotocol/server": "^2.0.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
package/src/adapter.ts
CHANGED
|
@@ -17,6 +17,19 @@ import { McpServer } from "@modelcontextprotocol/server";
|
|
|
17
17
|
import { toMcpSchema } from "./schemas";
|
|
18
18
|
import { toMcpTaskPayload, type ActionDockMcpOptions } from "./types";
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* 判断目标值是否为普通对象(Plain Object)。
|
|
22
|
+
*
|
|
23
|
+
* @param value 待检查的值
|
|
24
|
+
*/
|
|
25
|
+
export function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
26
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const proto = Object.getPrototypeOf(value);
|
|
30
|
+
return proto === Object.prototype || proto === null;
|
|
31
|
+
}
|
|
32
|
+
|
|
20
33
|
/**
|
|
21
34
|
* 将 ActionDock 标准的 ExecutionResult 信封结构转换为 MCP 协议规范的 Tool Call 返回结果。
|
|
22
35
|
*
|
|
@@ -24,6 +37,10 @@ import { toMcpTaskPayload, type ActionDockMcpOptions } from "./types";
|
|
|
24
37
|
*/
|
|
25
38
|
export function toMcpResult(result: ExecutionResult) {
|
|
26
39
|
if (result.ok) {
|
|
40
|
+
const structuredContent = isPlainObject(result.data)
|
|
41
|
+
? (result.data as Record<string, unknown>)
|
|
42
|
+
: { value: result.data };
|
|
43
|
+
|
|
27
44
|
return {
|
|
28
45
|
content: [
|
|
29
46
|
{
|
|
@@ -31,7 +48,7 @@ export function toMcpResult(result: ExecutionResult) {
|
|
|
31
48
|
text: JSON.stringify(result),
|
|
32
49
|
},
|
|
33
50
|
],
|
|
34
|
-
structuredContent
|
|
51
|
+
structuredContent,
|
|
35
52
|
};
|
|
36
53
|
}
|
|
37
54
|
|
|
@@ -102,12 +119,16 @@ function createMcpToolCallback(
|
|
|
102
119
|
};
|
|
103
120
|
}
|
|
104
121
|
|
|
122
|
+
export type ActionDockMcpServer = McpServer & {
|
|
123
|
+
close: () => Promise<void>;
|
|
124
|
+
};
|
|
125
|
+
|
|
105
126
|
/**
|
|
106
127
|
* Creates and configures an McpServer instance bound to one or more ActionDock packages with Tasks extension support.
|
|
107
128
|
*/
|
|
108
129
|
export async function createActionDockMcpServer(
|
|
109
130
|
options: ActionDockMcpOptions = {}
|
|
110
|
-
): Promise<
|
|
131
|
+
): Promise<ActionDockMcpServer> {
|
|
111
132
|
const targetRoots: string[] = [];
|
|
112
133
|
if (options.projectRoot) {
|
|
113
134
|
targetRoots.push(options.projectRoot);
|
|
@@ -359,5 +380,58 @@ export async function createActionDockMcpServer(
|
|
|
359
380
|
};
|
|
360
381
|
});
|
|
361
382
|
|
|
362
|
-
|
|
383
|
+
const originalClose = server.close.bind(server);
|
|
384
|
+
let isClosed = false;
|
|
385
|
+
|
|
386
|
+
const closeFn = async (): Promise<void> => {
|
|
387
|
+
if (isClosed) return;
|
|
388
|
+
isClosed = true;
|
|
389
|
+
|
|
390
|
+
process.removeListener("exit", onProcessExit);
|
|
391
|
+
|
|
392
|
+
for (const storage of storages) {
|
|
393
|
+
try {
|
|
394
|
+
storage.close();
|
|
395
|
+
} catch {
|
|
396
|
+
// ignore
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (!options.runtimeRegistry) {
|
|
401
|
+
try {
|
|
402
|
+
runtimeRegistry.close();
|
|
403
|
+
} catch {
|
|
404
|
+
// ignore
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
await originalClose();
|
|
410
|
+
} catch {
|
|
411
|
+
// ignore
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const onProcessExit = () => {
|
|
416
|
+
for (const storage of storages) {
|
|
417
|
+
try {
|
|
418
|
+
storage.close();
|
|
419
|
+
} catch {
|
|
420
|
+
// ignore
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (!options.runtimeRegistry) {
|
|
424
|
+
try {
|
|
425
|
+
runtimeRegistry.close();
|
|
426
|
+
} catch {
|
|
427
|
+
// ignore
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
process.once("exit", onProcessExit);
|
|
433
|
+
|
|
434
|
+
(server as any).close = closeFn;
|
|
435
|
+
|
|
436
|
+
return server as ActionDockMcpServer;
|
|
363
437
|
}
|
package/src/stdio.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
2
|
-
import { createActionDockMcpServer } from "./adapter";
|
|
2
|
+
import { createActionDockMcpServer, type ActionDockMcpServer } from "./adapter";
|
|
3
3
|
import type { ActionDockMcpOptions } from "./types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -8,9 +8,44 @@ import type { ActionDockMcpOptions } from "./types";
|
|
|
8
8
|
export async function startMcpStdio(
|
|
9
9
|
options: ActionDockMcpOptions = {}
|
|
10
10
|
): Promise<void> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
let activeServer: ActionDockMcpServer | undefined;
|
|
12
|
+
|
|
13
|
+
const stdioHandler = serveStdio(
|
|
14
|
+
async () => {
|
|
15
|
+
const server = await createActionDockMcpServer(options);
|
|
16
|
+
activeServer = server;
|
|
17
|
+
return server;
|
|
14
18
|
},
|
|
19
|
+
{
|
|
20
|
+
onerror: (err) => {
|
|
21
|
+
process.stderr.write(`[MCP Error] ${err?.message || String(err)}\n`);
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const cleanup = async () => {
|
|
27
|
+
if (activeServer) {
|
|
28
|
+
try {
|
|
29
|
+
await activeServer.close();
|
|
30
|
+
} catch {
|
|
31
|
+
// ignore
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
process.once("SIGINT", async () => {
|
|
37
|
+
await cleanup();
|
|
38
|
+
try {
|
|
39
|
+
await stdioHandler.close();
|
|
40
|
+
} catch {}
|
|
41
|
+
process.exit(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
process.once("SIGTERM", async () => {
|
|
45
|
+
await cleanup();
|
|
46
|
+
try {
|
|
47
|
+
await stdioHandler.close();
|
|
48
|
+
} catch {}
|
|
49
|
+
process.exit(0);
|
|
15
50
|
});
|
|
16
51
|
}
|