@sammorrowdrums/mcpi-ext 0.2.0 → 0.2.1
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/dist/code-mode/code-mode-integration.test.d.ts +2 -0
- package/dist/code-mode/code-mode-integration.test.d.ts.map +1 -0
- package/dist/code-mode/code-mode-integration.test.js +116 -0
- package/dist/code-mode/code-mode-integration.test.js.map +1 -0
- package/dist/code-mode/eligibility.d.ts +10 -0
- package/dist/code-mode/eligibility.d.ts.map +1 -0
- package/dist/code-mode/eligibility.js +14 -0
- package/dist/code-mode/eligibility.js.map +1 -0
- package/dist/code-mode/eligibility.test.d.ts +2 -0
- package/dist/code-mode/eligibility.test.d.ts.map +1 -0
- package/dist/code-mode/eligibility.test.js +53 -0
- package/dist/code-mode/eligibility.test.js.map +1 -0
- package/dist/code-mode/executor.d.ts +35 -0
- package/dist/code-mode/executor.d.ts.map +1 -0
- package/dist/code-mode/executor.js +120 -0
- package/dist/code-mode/executor.js.map +1 -0
- package/dist/code-mode/executor.test.d.ts +2 -0
- package/dist/code-mode/executor.test.d.ts.map +1 -0
- package/dist/code-mode/executor.test.js +130 -0
- package/dist/code-mode/executor.test.js.map +1 -0
- package/dist/code-mode/index.d.ts +65 -0
- package/dist/code-mode/index.d.ts.map +1 -0
- package/dist/code-mode/index.js +175 -0
- package/dist/code-mode/index.js.map +1 -0
- package/dist/code-mode/tools.d.ts +45 -0
- package/dist/code-mode/tools.d.ts.map +1 -0
- package/dist/code-mode/tools.js +70 -0
- package/dist/code-mode/tools.js.map +1 -0
- package/dist/code-mode/type-hints.d.ts +20 -0
- package/dist/code-mode/type-hints.d.ts.map +1 -0
- package/dist/code-mode/type-hints.js +189 -0
- package/dist/code-mode/type-hints.js.map +1 -0
- package/dist/code-mode/type-hints.test.d.ts +2 -0
- package/dist/code-mode/type-hints.test.d.ts.map +1 -0
- package/dist/code-mode/type-hints.test.js +157 -0
- package/dist/code-mode/type-hints.test.js.map +1 -0
- package/dist/docker-e2e.d.ts +55 -0
- package/dist/docker-e2e.d.ts.map +1 -0
- package/dist/docker-e2e.js +235 -0
- package/dist/docker-e2e.js.map +1 -0
- package/dist/docker-e2e.test.d.ts +2 -0
- package/dist/docker-e2e.test.d.ts.map +1 -0
- package/dist/docker-e2e.test.js +176 -0
- package/dist/docker-e2e.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +8 -0
- package/dist/index.test.js.map +1 -0
- package/dist/mcp/client-manager.d.ts +46 -0
- package/dist/mcp/client-manager.d.ts.map +1 -0
- package/dist/mcp/client-manager.js +148 -0
- package/dist/mcp/client-manager.js.map +1 -0
- package/dist/mcp/client-manager.test.d.ts +2 -0
- package/dist/mcp/client-manager.test.d.ts.map +1 -0
- package/dist/mcp/client-manager.test.js +204 -0
- package/dist/mcp/client-manager.test.js.map +1 -0
- package/dist/mcp/config-loader.d.ts +8 -0
- package/dist/mcp/config-loader.d.ts.map +1 -0
- package/dist/mcp/config-loader.js +43 -0
- package/dist/mcp/config-loader.js.map +1 -0
- package/dist/mcp/config-loader.test.d.ts +2 -0
- package/dist/mcp/config-loader.test.d.ts.map +1 -0
- package/dist/mcp/config-loader.test.js +93 -0
- package/dist/mcp/config-loader.test.js.map +1 -0
- package/dist/mcp/config.d.ts +46 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +28 -0
- package/dist/mcp/config.js.map +1 -0
- package/{src/mcp/index.ts → dist/mcp/index.d.ts} +1 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +4 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/skills/discover.d.ts +10 -0
- package/dist/skills/discover.d.ts.map +1 -0
- package/dist/skills/discover.js +60 -0
- package/dist/skills/discover.js.map +1 -0
- package/dist/skills/format.d.ts +10 -0
- package/dist/skills/format.d.ts.map +1 -0
- package/dist/skills/format.js +38 -0
- package/dist/skills/format.js.map +1 -0
- package/dist/skills/format.test.d.ts +2 -0
- package/dist/skills/format.test.d.ts.map +1 -0
- package/dist/skills/format.test.js +63 -0
- package/dist/skills/format.test.js.map +1 -0
- package/{src/skills/index.ts → dist/skills/index.d.ts} +2 -5
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +6 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/load-skill-tool.d.ts +40 -0
- package/dist/skills/load-skill-tool.d.ts.map +1 -0
- package/dist/skills/load-skill-tool.js +104 -0
- package/dist/skills/load-skill-tool.js.map +1 -0
- package/dist/skills/mcp-tool-proxy.d.ts +11 -0
- package/dist/skills/mcp-tool-proxy.d.ts.map +1 -0
- package/dist/skills/mcp-tool-proxy.js +116 -0
- package/dist/skills/mcp-tool-proxy.js.map +1 -0
- package/dist/skills/skill-integration.test.d.ts +2 -0
- package/dist/skills/skill-integration.test.d.ts.map +1 -0
- package/dist/skills/skill-integration.test.js +119 -0
- package/dist/skills/skill-integration.test.js.map +1 -0
- package/dist/skills/skill-registry.d.ts +39 -0
- package/dist/skills/skill-registry.d.ts.map +1 -0
- package/dist/skills/skill-registry.js +48 -0
- package/dist/skills/skill-registry.js.map +1 -0
- package/dist/skills/skill-registry.test.d.ts +2 -0
- package/dist/skills/skill-registry.test.d.ts.map +1 -0
- package/dist/skills/skill-registry.test.js +67 -0
- package/dist/skills/skill-registry.test.js.map +1 -0
- package/dist/test-servers/weather-server.d.ts +12 -0
- package/dist/test-servers/weather-server.d.ts.map +1 -0
- package/dist/test-servers/weather-server.js +139 -0
- package/dist/test-servers/weather-server.js.map +1 -0
- package/dist/test-servers/weather-stdio.d.ts +2 -0
- package/dist/test-servers/weather-stdio.d.ts.map +1 -0
- package/{src/test-servers/weather-stdio.ts → dist/test-servers/weather-stdio.js} +1 -1
- package/dist/test-servers/weather-stdio.js.map +1 -0
- package/dist/tool-cli/cli.d.ts +3 -0
- package/dist/tool-cli/cli.d.ts.map +1 -0
- package/dist/tool-cli/cli.js +179 -0
- package/dist/tool-cli/cli.js.map +1 -0
- package/dist/tool-cli/constants.d.ts +7 -0
- package/dist/tool-cli/constants.d.ts.map +1 -0
- package/dist/tool-cli/constants.js +15 -0
- package/dist/tool-cli/constants.js.map +1 -0
- package/dist/tool-cli/format.d.ts +8 -0
- package/dist/tool-cli/format.d.ts.map +1 -0
- package/{src/tool-cli/format.ts → dist/tool-cli/format.js} +5 -4
- package/dist/tool-cli/format.js.map +1 -0
- package/dist/tool-cli/format.test.d.ts +2 -0
- package/dist/tool-cli/format.test.d.ts.map +1 -0
- package/dist/tool-cli/format.test.js +30 -0
- package/dist/tool-cli/format.test.js.map +1 -0
- package/{src/tool-cli/index.ts → dist/tool-cli/index.d.ts} +1 -0
- package/dist/tool-cli/index.d.ts.map +1 -0
- package/dist/tool-cli/index.js +3 -0
- package/dist/tool-cli/index.js.map +1 -0
- package/dist/tool-cli/rpc-client.d.ts +6 -0
- package/dist/tool-cli/rpc-client.d.ts.map +1 -0
- package/dist/tool-cli/rpc-client.js +25 -0
- package/dist/tool-cli/rpc-client.js.map +1 -0
- package/dist/tool-cli/rpc-server.d.ts +48 -0
- package/dist/tool-cli/rpc-server.d.ts.map +1 -0
- package/dist/tool-cli/rpc-server.js +220 -0
- package/dist/tool-cli/rpc-server.js.map +1 -0
- package/dist/tool-cli/rpc-server.test.d.ts +2 -0
- package/dist/tool-cli/rpc-server.test.d.ts.map +1 -0
- package/dist/tool-cli/rpc-server.test.js +154 -0
- package/dist/tool-cli/rpc-server.test.js.map +1 -0
- package/package.json +5 -1
- package/.dockerignore +0 -6
- package/.github/dependabot.yml +0 -25
- package/.github/workflows/ci.yml +0 -50
- package/.hadolint.yaml +0 -6
- package/.prettierignore +0 -4
- package/.prettierrc +0 -7
- package/AGENTS.md +0 -118
- package/DECISIONS.md +0 -99
- package/Dockerfile +0 -39
- package/aube-lock.yaml +0 -4146
- package/docs/code-mode.md +0 -38
- package/docs/skills.md +0 -57
- package/docs/tool-cli.md +0 -54
- package/eslint.config.js +0 -27
- package/images/banner.webp +0 -0
- package/images/code-c-maude.webp +0 -0
- package/images/code-mode-histogram.png +0 -0
- package/images/nuclear-mcp-football.webp +0 -0
- package/images/skills-enabling-mcp-tools.png +0 -0
- package/images/the-skill-dealer.webp +0 -0
- package/images/tool-cli-grep.png +0 -0
- package/mise.toml +0 -53
- package/scripts/docker-entrypoint.sh +0 -29
- package/scripts/test-echo-server.mjs +0 -28
- package/scripts/test-mcp-integration.mjs +0 -59
- package/scripts/test-tool-cli-smoke.mjs +0 -93
- package/src/code-mode/code-mode-integration.test.ts +0 -135
- package/src/code-mode/eligibility.test.ts +0 -60
- package/src/code-mode/eligibility.ts +0 -16
- package/src/code-mode/executor.test.ts +0 -168
- package/src/code-mode/executor.ts +0 -163
- package/src/code-mode/index.ts +0 -208
- package/src/code-mode/tools.ts +0 -110
- package/src/code-mode/type-hints.test.ts +0 -187
- package/src/code-mode/type-hints.ts +0 -249
- package/src/docker-e2e.test.ts +0 -195
- package/src/docker-e2e.ts +0 -277
- package/src/index.test.ts +0 -8
- package/src/index.ts +0 -179
- package/src/mcp/client-manager.test.ts +0 -250
- package/src/mcp/client-manager.ts +0 -199
- package/src/mcp/config-loader.test.ts +0 -115
- package/src/mcp/config-loader.ts +0 -46
- package/src/mcp/config.ts +0 -40
- package/src/skills/discover.ts +0 -81
- package/src/skills/format.test.ts +0 -70
- package/src/skills/format.ts +0 -42
- package/src/skills/load-skill-tool.ts +0 -136
- package/src/skills/mcp-tool-proxy.ts +0 -143
- package/src/skills/skill-integration.test.ts +0 -139
- package/src/skills/skill-registry.test.ts +0 -83
- package/src/skills/skill-registry.ts +0 -69
- package/src/test-servers/weather-server.ts +0 -171
- package/src/tool-cli/format.test.ts +0 -33
- package/tsconfig.json +0 -21
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-mode-integration.test.d.ts","sourceRoot":"","sources":["../../src/code-mode/code-mode-integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import { McpClientManager } from "../mcp/client-manager.js";
|
|
3
|
+
import { CodeModeManager } from "./index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Integration test: connect to the test weather server, verify code mode
|
|
6
|
+
* eligibility, type hint generation, and sandboxed execution with real
|
|
7
|
+
* MCP tool calls returning structuredContent.
|
|
8
|
+
*/
|
|
9
|
+
describe("code mode integration (weather server)", () => {
|
|
10
|
+
const manager = new McpClientManager();
|
|
11
|
+
const codeMode = new CodeModeManager();
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
await manager.connectAll({
|
|
14
|
+
mcpServers: {
|
|
15
|
+
"test-weather": {
|
|
16
|
+
type: "stdio",
|
|
17
|
+
command: "node",
|
|
18
|
+
args: ["dist/test-servers/weather-stdio.js"],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
codeMode.initialize(manager);
|
|
23
|
+
});
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
await manager.disconnectAll();
|
|
26
|
+
});
|
|
27
|
+
it("discovers eligible tools with readOnlyHint + outputSchema", () => {
|
|
28
|
+
const eligible = codeMode.getEligibleTools();
|
|
29
|
+
expect(eligible.length).toBeGreaterThanOrEqual(2);
|
|
30
|
+
const names = eligible.map((t) => t.name).sort();
|
|
31
|
+
expect(names).toContain("check_weather_for_city");
|
|
32
|
+
expect(names).toContain("check_weekly_forecast_for_city");
|
|
33
|
+
expect(names).toContain("echo");
|
|
34
|
+
});
|
|
35
|
+
it("generates type hints for eligible tools", () => {
|
|
36
|
+
const hints = codeMode.getTypeHints();
|
|
37
|
+
expect(hints).toContain("declare const codemode");
|
|
38
|
+
expect(hints).toContain("check_weather_for_city");
|
|
39
|
+
expect(hints).toContain("check_weekly_forecast_for_city");
|
|
40
|
+
expect(hints).toContain("echo");
|
|
41
|
+
expect(hints).toContain("listTools");
|
|
42
|
+
});
|
|
43
|
+
it("reports as active", () => {
|
|
44
|
+
expect(codeMode.isActive).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
it("generates system prompt section", () => {
|
|
47
|
+
const section = codeMode.formatSystemPromptSection();
|
|
48
|
+
expect(section).toContain("<code_mode>");
|
|
49
|
+
expect(section).toContain("code_search");
|
|
50
|
+
expect(section).toContain("code_execute");
|
|
51
|
+
expect(section).toContain("declare const codemode");
|
|
52
|
+
});
|
|
53
|
+
it("executes code that calls a single tool", async () => {
|
|
54
|
+
const result = await codeMode.executeCode(`
|
|
55
|
+
const weather = await codemode.check_weather_for_city({ city: "Tokyo" });
|
|
56
|
+
return weather;
|
|
57
|
+
`);
|
|
58
|
+
expect(result.error).toBeUndefined();
|
|
59
|
+
expect(result.result).toHaveProperty("temperature", 26);
|
|
60
|
+
expect(result.result).toHaveProperty("conditions", "Sunny");
|
|
61
|
+
expect(result.result).toHaveProperty("humidity", 55);
|
|
62
|
+
});
|
|
63
|
+
it("executes code that chains multiple tool calls", async () => {
|
|
64
|
+
const result = await codeMode.executeCode(`
|
|
65
|
+
const cities = ["London", "Tokyo"];
|
|
66
|
+
const results = [];
|
|
67
|
+
for (const city of cities) {
|
|
68
|
+
const w = await codemode.check_weather_for_city({ city });
|
|
69
|
+
results.push({ city, temp: w.temperature });
|
|
70
|
+
}
|
|
71
|
+
return results;
|
|
72
|
+
`);
|
|
73
|
+
expect(result.error).toBeUndefined();
|
|
74
|
+
expect(result.result).toEqual([
|
|
75
|
+
{ city: "London", temp: 14 },
|
|
76
|
+
{ city: "Tokyo", temp: 26 },
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
it("executes code that uses listTools", async () => {
|
|
80
|
+
const result = await codeMode.executeCode(`
|
|
81
|
+
const tools = await codemode.listTools();
|
|
82
|
+
return tools.sort();
|
|
83
|
+
`);
|
|
84
|
+
expect(result.error).toBeUndefined();
|
|
85
|
+
const names = result.result;
|
|
86
|
+
expect(names).toContain("check_weather_for_city");
|
|
87
|
+
expect(names).toContain("echo");
|
|
88
|
+
});
|
|
89
|
+
it("executes code that calls echo tool", async () => {
|
|
90
|
+
const result = await codeMode.executeCode(`
|
|
91
|
+
const r = await codemode.echo({ message: "hello code mode" });
|
|
92
|
+
return r;
|
|
93
|
+
`);
|
|
94
|
+
expect(result.error).toBeUndefined();
|
|
95
|
+
expect(result.result).toEqual({ echo: "hello code mode" });
|
|
96
|
+
});
|
|
97
|
+
it("handles tool errors gracefully", async () => {
|
|
98
|
+
const result = await codeMode.executeCode(`
|
|
99
|
+
const w = await codemode.check_weather_for_city({ city: "Atlantis" });
|
|
100
|
+
return w;
|
|
101
|
+
`);
|
|
102
|
+
// Atlantis isn't in the data — server returns text-only content (no structuredContent)
|
|
103
|
+
// The executor should still return something (parsed text or raw string)
|
|
104
|
+
expect(result.error).toBeUndefined();
|
|
105
|
+
expect(result.result).toBeDefined();
|
|
106
|
+
});
|
|
107
|
+
it("search mode works for tool discovery", async () => {
|
|
108
|
+
const result = await codeMode.searchTools(`
|
|
109
|
+
const tools = await codemode.listTools();
|
|
110
|
+
return tools;
|
|
111
|
+
`);
|
|
112
|
+
expect(result.error).toBeUndefined();
|
|
113
|
+
expect(Array.isArray(result.result)).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=code-mode-integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-mode-integration.test.js","sourceRoot":"","sources":["../../src/code-mode/code-mode-integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { McpClientManager, McpTool } from "../mcp/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* A tool is eligible for code mode when:
|
|
4
|
+
* 1. annotations.readOnlyHint === true — does not modify its environment
|
|
5
|
+
* 2. outputSchema is defined — results are typed and parseable
|
|
6
|
+
*/
|
|
7
|
+
export declare function isEligibleForCodeMode(tool: McpTool): boolean;
|
|
8
|
+
/** Get all tools eligible for code mode across all connected MCP servers. */
|
|
9
|
+
export declare function getEligibleTools(mcpManager: McpClientManager): McpTool[];
|
|
10
|
+
//# sourceMappingURL=eligibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../src/code-mode/eligibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAG5D;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,EAAE,CAExE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tool is eligible for code mode when:
|
|
3
|
+
* 1. annotations.readOnlyHint === true — does not modify its environment
|
|
4
|
+
* 2. outputSchema is defined — results are typed and parseable
|
|
5
|
+
*/
|
|
6
|
+
export function isEligibleForCodeMode(tool) {
|
|
7
|
+
const annotations = tool.annotations;
|
|
8
|
+
return annotations?.readOnlyHint === true && tool.outputSchema != null;
|
|
9
|
+
}
|
|
10
|
+
/** Get all tools eligible for code mode across all connected MCP servers. */
|
|
11
|
+
export function getEligibleTools(mcpManager) {
|
|
12
|
+
return mcpManager.getTools().filter(isEligibleForCodeMode);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=eligibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligibility.js","sourceRoot":"","sources":["../../src/code-mode/eligibility.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IACjD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAqD,CAAC;IAC/E,OAAO,WAAW,EAAE,YAAY,KAAK,IAAI,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;AACzE,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,UAA4B;IAC3D,OAAO,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligibility.test.d.ts","sourceRoot":"","sources":["../../src/code-mode/eligibility.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { isEligibleForCodeMode } from "./eligibility.js";
|
|
3
|
+
function makeTool(overrides = {}) {
|
|
4
|
+
return {
|
|
5
|
+
name: "test_tool",
|
|
6
|
+
description: "A test tool",
|
|
7
|
+
inputSchema: { type: "object", properties: {} },
|
|
8
|
+
serverName: "test-server",
|
|
9
|
+
...overrides,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
describe("isEligibleForCodeMode", () => {
|
|
13
|
+
it("returns true when readOnlyHint is true and outputSchema is defined", () => {
|
|
14
|
+
const tool = makeTool({
|
|
15
|
+
annotations: { readOnlyHint: true },
|
|
16
|
+
outputSchema: { type: "object", properties: { result: { type: "string" } } },
|
|
17
|
+
});
|
|
18
|
+
expect(isEligibleForCodeMode(tool)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it("returns false when readOnlyHint is false", () => {
|
|
21
|
+
const tool = makeTool({
|
|
22
|
+
annotations: { readOnlyHint: false },
|
|
23
|
+
outputSchema: { type: "object", properties: {} },
|
|
24
|
+
});
|
|
25
|
+
expect(isEligibleForCodeMode(tool)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
it("returns false when readOnlyHint is missing", () => {
|
|
28
|
+
const tool = makeTool({
|
|
29
|
+
annotations: {},
|
|
30
|
+
outputSchema: { type: "object", properties: {} },
|
|
31
|
+
});
|
|
32
|
+
expect(isEligibleForCodeMode(tool)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it("returns false when annotations is undefined", () => {
|
|
35
|
+
const tool = makeTool({
|
|
36
|
+
annotations: undefined,
|
|
37
|
+
outputSchema: { type: "object", properties: {} },
|
|
38
|
+
});
|
|
39
|
+
expect(isEligibleForCodeMode(tool)).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
it("returns false when outputSchema is missing", () => {
|
|
42
|
+
const tool = makeTool({
|
|
43
|
+
annotations: { readOnlyHint: true },
|
|
44
|
+
outputSchema: undefined,
|
|
45
|
+
});
|
|
46
|
+
expect(isEligibleForCodeMode(tool)).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
it("returns false when both readOnlyHint and outputSchema are missing", () => {
|
|
49
|
+
const tool = makeTool();
|
|
50
|
+
expect(isEligibleForCodeMode(tool)).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=eligibility.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligibility.test.js","sourceRoot":"","sources":["../../src/code-mode/eligibility.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,SAAS,QAAQ,CAAC,YAA8B,EAAE;IAChD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,aAAa;QAC1B,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;QAC/C,UAAU,EAAE,aAAa;QACzB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;YACnC,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;SAC7E,CAAC,CAAC;QACH,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE;YACpC,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;SACjD,CAAC,CAAC;QACH,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,WAAW,EAAE,EAAE;YACf,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;SACjD,CAAC,CAAC;QACH,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,WAAW,EAAE,SAAS;YACtB,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;SACjD,CAAC,CAAC;QACH,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;YACnC,YAAY,EAAE,SAAS;SACxB,CAAC,CAAC;QACH,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;QACxB,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Result of code execution. */
|
|
2
|
+
export interface ExecuteResult {
|
|
3
|
+
result: unknown;
|
|
4
|
+
error?: string;
|
|
5
|
+
logs: string[];
|
|
6
|
+
}
|
|
7
|
+
/** A function the sandbox can call to invoke an MCP tool. */
|
|
8
|
+
export type ToolDispatchFn = (toolName: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
9
|
+
export interface ExecutorOptions {
|
|
10
|
+
/** Memory limit in MB for the V8 isolate. Default: 128. */
|
|
11
|
+
memoryLimit?: number;
|
|
12
|
+
/** Execution timeout in milliseconds. Default: 30000. */
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Execute model-generated JavaScript code in an isolated V8 sandbox.
|
|
17
|
+
*
|
|
18
|
+
* The code runs in a fresh `isolated-vm` isolate with:
|
|
19
|
+
* - Enforced memory limit (default 128MB)
|
|
20
|
+
* - Execution timeout (default 30s)
|
|
21
|
+
* - No access to Node.js APIs, filesystem, or network
|
|
22
|
+
* - Only access to provided tool dispatch functions via `codemode.*` proxy
|
|
23
|
+
*
|
|
24
|
+
* Tool calls are dispatched to the host via `Reference` callbacks —
|
|
25
|
+
* actual MCP tool execution happens outside the sandbox.
|
|
26
|
+
*/
|
|
27
|
+
export declare function executeInSandbox(code: string, toolNames: string[], dispatch: ToolDispatchFn, options?: ExecutorOptions): Promise<ExecuteResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Normalize model-generated code:
|
|
30
|
+
* - Strip markdown code fences
|
|
31
|
+
* - Handle arrow functions, function declarations, export default
|
|
32
|
+
* - Otherwise leave code as a statement block; callers must explicitly `return` a value
|
|
33
|
+
*/
|
|
34
|
+
export declare function normalizeCode(code: string): string;
|
|
35
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/code-mode/executor.ts"],"names":[],"mappings":"AAGA,gCAAgC;AAChC,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,6DAA6D;AAC7D,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEnG,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EAAE,EACnB,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,aAAa,CAAC,CAUxB;AA6ED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA6BlD"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import ivm from "isolated-vm";
|
|
2
|
+
import { sanitizeToolName } from "./type-hints.js";
|
|
3
|
+
const DEFAULT_MEMORY_LIMIT = 128;
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
5
|
+
/**
|
|
6
|
+
* Execute model-generated JavaScript code in an isolated V8 sandbox.
|
|
7
|
+
*
|
|
8
|
+
* The code runs in a fresh `isolated-vm` isolate with:
|
|
9
|
+
* - Enforced memory limit (default 128MB)
|
|
10
|
+
* - Execution timeout (default 30s)
|
|
11
|
+
* - No access to Node.js APIs, filesystem, or network
|
|
12
|
+
* - Only access to provided tool dispatch functions via `codemode.*` proxy
|
|
13
|
+
*
|
|
14
|
+
* Tool calls are dispatched to the host via `Reference` callbacks —
|
|
15
|
+
* actual MCP tool execution happens outside the sandbox.
|
|
16
|
+
*/
|
|
17
|
+
export async function executeInSandbox(code, toolNames, dispatch, options = {}) {
|
|
18
|
+
const memoryLimit = options.memoryLimit ?? DEFAULT_MEMORY_LIMIT;
|
|
19
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
20
|
+
const isolate = new ivm.Isolate({ memoryLimit });
|
|
21
|
+
try {
|
|
22
|
+
return await runInIsolate(isolate, code, toolNames, dispatch, timeoutMs);
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
isolate.dispose();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function runInIsolate(isolate, code, toolNames, dispatch, timeoutMs) {
|
|
29
|
+
const ctx = await isolate.createContext();
|
|
30
|
+
const jail = ctx.global;
|
|
31
|
+
const logs = [];
|
|
32
|
+
// Inject console.log that captures to logs array
|
|
33
|
+
const logCallback = new ivm.Callback((...args) => {
|
|
34
|
+
logs.push(args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" "));
|
|
35
|
+
});
|
|
36
|
+
await jail.set("__log", logCallback);
|
|
37
|
+
// Inject tool dispatcher Reference (async callback)
|
|
38
|
+
const dispatchRef = new ivm.Reference(async (toolName, argsJson) => {
|
|
39
|
+
const args = JSON.parse(argsJson);
|
|
40
|
+
const result = await dispatch(toolName, args);
|
|
41
|
+
return JSON.stringify(result === undefined ? null : result);
|
|
42
|
+
});
|
|
43
|
+
await jail.set("__dispatch", dispatchRef);
|
|
44
|
+
// Build the tool call helper and codemode proxy as setup code
|
|
45
|
+
const toolProxyEntries = toolNames
|
|
46
|
+
.map((name) => {
|
|
47
|
+
const safe = sanitizeToolName(name);
|
|
48
|
+
return ` ${safe}: async (args) => __callTool(${JSON.stringify(name)}, args)`;
|
|
49
|
+
})
|
|
50
|
+
.join(",\n");
|
|
51
|
+
const normalized = normalizeCode(code);
|
|
52
|
+
// Use ctx.eval with { promise: true } instead of compileModule,
|
|
53
|
+
// because module.evaluate() can resolve prematurely with multiple
|
|
54
|
+
// sequential async Reference.apply calls.
|
|
55
|
+
const wrappedCode = `
|
|
56
|
+
(async () => {
|
|
57
|
+
const console = { log: (...args) => __log(args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ')) };
|
|
58
|
+
const __callTool = async (name, args) => {
|
|
59
|
+
const r = await __dispatch.apply(undefined, [name, JSON.stringify(args ?? {})], { arguments: { copy: true }, result: { promise: true, copy: true } });
|
|
60
|
+
return JSON.parse(r);
|
|
61
|
+
};
|
|
62
|
+
const codemode = {
|
|
63
|
+
listTools: async () => ${JSON.stringify(toolNames)},
|
|
64
|
+
describeTools: async (names) => "Use the typed codemode.toolName(args) methods instead.",
|
|
65
|
+
${toolProxyEntries}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const __userFn = async () => {
|
|
69
|
+
${normalized}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return JSON.stringify({ value: await __userFn() });
|
|
73
|
+
})()
|
|
74
|
+
`;
|
|
75
|
+
try {
|
|
76
|
+
const rawResult = (await ctx.eval(wrappedCode, {
|
|
77
|
+
promise: true,
|
|
78
|
+
copy: true,
|
|
79
|
+
timeout: timeoutMs,
|
|
80
|
+
}));
|
|
81
|
+
const parsed = JSON.parse(rawResult);
|
|
82
|
+
return { result: parsed.value, logs };
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
86
|
+
return { result: undefined, error: message, logs };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Normalize model-generated code:
|
|
91
|
+
* - Strip markdown code fences
|
|
92
|
+
* - Handle arrow functions, function declarations, export default
|
|
93
|
+
* - Otherwise leave code as a statement block; callers must explicitly `return` a value
|
|
94
|
+
*/
|
|
95
|
+
export function normalizeCode(code) {
|
|
96
|
+
let normalized = code.trim();
|
|
97
|
+
// Strip markdown code fences
|
|
98
|
+
normalized = normalized.replace(/^```(?:js|javascript|typescript|ts)?\s*\n?/i, "");
|
|
99
|
+
normalized = normalized.replace(/\n?```\s*$/i, "");
|
|
100
|
+
normalized = normalized.trim();
|
|
101
|
+
// If it's an arrow function or function expression, invoke it
|
|
102
|
+
if (/^(?:async\s+)?(?:\([^)]*\)|[a-zA-Z_$][a-zA-Z0-9_$]*)\s*=>/.test(normalized)) {
|
|
103
|
+
return `return (${normalized})();`;
|
|
104
|
+
}
|
|
105
|
+
// If it's a function declaration, invoke it
|
|
106
|
+
if (/^(?:async\s+)?function\s+/.test(normalized)) {
|
|
107
|
+
const match = normalized.match(/^(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
108
|
+
if (match) {
|
|
109
|
+
return `${normalized}\nreturn ${match[1]}();`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// If it starts with export default, strip the export default
|
|
113
|
+
if (/^export\s+default\s+/.test(normalized)) {
|
|
114
|
+
normalized = normalized.replace(/^export\s+default\s+/, "");
|
|
115
|
+
return `return (${normalized})();`;
|
|
116
|
+
}
|
|
117
|
+
// Otherwise, treat as a code block — return the last expression
|
|
118
|
+
return normalized;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/code-mode/executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.test.d.ts","sourceRoot":"","sources":["../../src/code-mode/executor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { executeInSandbox, normalizeCode } from "./executor.js";
|
|
3
|
+
describe("normalizeCode", () => {
|
|
4
|
+
it("strips markdown code fences", () => {
|
|
5
|
+
expect(normalizeCode("```js\nconst x = 1;\n```")).toBe("const x = 1;");
|
|
6
|
+
});
|
|
7
|
+
it("strips typescript fences", () => {
|
|
8
|
+
expect(normalizeCode("```typescript\nconst x = 1;\n```")).toBe("const x = 1;");
|
|
9
|
+
});
|
|
10
|
+
it("wraps arrow function in invocation", () => {
|
|
11
|
+
expect(normalizeCode("async () => { return 42; }")).toBe("return (async () => { return 42; })();");
|
|
12
|
+
});
|
|
13
|
+
it("wraps function declaration in invocation", () => {
|
|
14
|
+
const code = "async function main() { return 42; }";
|
|
15
|
+
const result = normalizeCode(code);
|
|
16
|
+
expect(result).toContain("async function main()");
|
|
17
|
+
expect(result).toContain("return main();");
|
|
18
|
+
});
|
|
19
|
+
it("strips export default", () => {
|
|
20
|
+
const result = normalizeCode("export default async () => { return 42; }");
|
|
21
|
+
expect(result).toContain("return (async () => { return 42; })();");
|
|
22
|
+
});
|
|
23
|
+
it("leaves bare code as-is", () => {
|
|
24
|
+
expect(normalizeCode("const x = 1;\nreturn x;")).toBe("const x = 1;\nreturn x;");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("executeInSandbox", () => {
|
|
28
|
+
it("executes simple code and returns result", async () => {
|
|
29
|
+
const result = await executeInSandbox("return 2 + 2;", [], async () => ({}), {
|
|
30
|
+
timeoutMs: 5000,
|
|
31
|
+
});
|
|
32
|
+
expect(result.result).toBe(4);
|
|
33
|
+
expect(result.error).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
it("captures console.log output", async () => {
|
|
36
|
+
const result = await executeInSandbox('console.log("hello", "world"); return 1;', [], async () => ({}), { timeoutMs: 5000 });
|
|
37
|
+
expect(result.result).toBe(1);
|
|
38
|
+
expect(result.logs).toContain("hello world");
|
|
39
|
+
});
|
|
40
|
+
it("dispatches tool calls through the callback", async () => {
|
|
41
|
+
const dispatched = [];
|
|
42
|
+
const dispatch = async (name, args) => {
|
|
43
|
+
dispatched.push({ name, args });
|
|
44
|
+
return { results: ["doc1", "doc2"] };
|
|
45
|
+
};
|
|
46
|
+
const code = `
|
|
47
|
+
const result = await codemode.search_docs({ query: "test" });
|
|
48
|
+
return result;
|
|
49
|
+
`;
|
|
50
|
+
const result = await executeInSandbox(code, ["search_docs"], dispatch, { timeoutMs: 5000 });
|
|
51
|
+
expect(result.error).toBeUndefined();
|
|
52
|
+
expect(result.result).toEqual({ results: ["doc1", "doc2"] });
|
|
53
|
+
expect(dispatched).toHaveLength(1);
|
|
54
|
+
expect(dispatched[0].name).toBe("search_docs");
|
|
55
|
+
expect(dispatched[0].args).toEqual({ query: "test" });
|
|
56
|
+
});
|
|
57
|
+
it("handles tools with special characters in names", async () => {
|
|
58
|
+
const dispatched = [];
|
|
59
|
+
const dispatch = async (name) => {
|
|
60
|
+
dispatched.push(name);
|
|
61
|
+
return { ok: true };
|
|
62
|
+
};
|
|
63
|
+
const code = `
|
|
64
|
+
const result = await codemode.github_list_repos({});
|
|
65
|
+
return result;
|
|
66
|
+
`;
|
|
67
|
+
const result = await executeInSandbox(code, ["github.list-repos"], dispatch, {
|
|
68
|
+
timeoutMs: 5000,
|
|
69
|
+
});
|
|
70
|
+
expect(result.error).toBeUndefined();
|
|
71
|
+
expect(dispatched).toContain("github.list-repos");
|
|
72
|
+
});
|
|
73
|
+
it("returns error for invalid code", async () => {
|
|
74
|
+
const result = await executeInSandbox("throw new Error('boom');", [], async () => ({}), {
|
|
75
|
+
timeoutMs: 5000,
|
|
76
|
+
});
|
|
77
|
+
expect(result.error).toContain("boom");
|
|
78
|
+
expect(result.result).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
it("enforces timeout", async () => {
|
|
81
|
+
const result = await executeInSandbox("while(true) {}", [], async () => ({}), {
|
|
82
|
+
timeoutMs: 100,
|
|
83
|
+
});
|
|
84
|
+
expect(result.error).toBeDefined();
|
|
85
|
+
expect(result.result).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
it("chains multiple tool calls", async () => {
|
|
88
|
+
const calls = [];
|
|
89
|
+
const dispatch = async (name, args) => {
|
|
90
|
+
calls.push(name);
|
|
91
|
+
if (name === "list_items")
|
|
92
|
+
return { items: ["a", "b", "c"] };
|
|
93
|
+
if (name === "get_details")
|
|
94
|
+
return { detail: `info for ${args.id}` };
|
|
95
|
+
return {};
|
|
96
|
+
};
|
|
97
|
+
const code = `
|
|
98
|
+
const { items } = await codemode.list_items({});
|
|
99
|
+
const details = [];
|
|
100
|
+
for (const id of items) {
|
|
101
|
+
const d = await codemode.get_details({ id });
|
|
102
|
+
details.push(d.detail);
|
|
103
|
+
}
|
|
104
|
+
return details;
|
|
105
|
+
`;
|
|
106
|
+
const result = await executeInSandbox(code, ["list_items", "get_details"], dispatch, {
|
|
107
|
+
timeoutMs: 5000,
|
|
108
|
+
});
|
|
109
|
+
expect(result.error).toBeUndefined();
|
|
110
|
+
expect(result.result).toEqual(["info for a", "info for b", "info for c"]);
|
|
111
|
+
expect(calls).toEqual(["list_items", "get_details", "get_details", "get_details"]);
|
|
112
|
+
});
|
|
113
|
+
it("prevents imports", async () => {
|
|
114
|
+
const code = 'import("fs")';
|
|
115
|
+
const result = await executeInSandbox(code, [], async () => ({}), { timeoutMs: 5000 });
|
|
116
|
+
expect(result.error).toBeDefined();
|
|
117
|
+
});
|
|
118
|
+
it("listTools returns available tools", async () => {
|
|
119
|
+
const code = `
|
|
120
|
+
const tools = await codemode.listTools();
|
|
121
|
+
return tools;
|
|
122
|
+
`;
|
|
123
|
+
const result = await executeInSandbox(code, ["search", "execute"], async () => ({}), {
|
|
124
|
+
timeoutMs: 5000,
|
|
125
|
+
});
|
|
126
|
+
expect(result.error).toBeUndefined();
|
|
127
|
+
expect(result.result).toEqual(["search", "execute"]);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=executor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.test.js","sourceRoot":"","sources":["../../src/code-mode/executor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { McpClientManager, McpTool } from "../mcp/index.js";
|
|
2
|
+
import type { ExecuteResult, ExecutorOptions } from "./executor.js";
|
|
3
|
+
export { getEligibleTools, isEligibleForCodeMode } from "./eligibility.js";
|
|
4
|
+
export type { ExecuteResult, ExecutorOptions } from "./executor.js";
|
|
5
|
+
export { executeInSandbox, normalizeCode } from "./executor.js";
|
|
6
|
+
export { createCodeExecuteTool, createCodeSearchTool } from "./tools.js";
|
|
7
|
+
export { generateTypeHints, jsonSchemaToTypeString, sanitizeToolName } from "./type-hints.js";
|
|
8
|
+
export interface CodeModeManagerOptions extends ExecutorOptions {
|
|
9
|
+
/** Log function for status messages. */
|
|
10
|
+
log?: (msg: string) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Orchestrates code mode: discovers eligible tools, generates type hints,
|
|
14
|
+
* and executes model-generated code in a sandbox with tool dispatch.
|
|
15
|
+
*/
|
|
16
|
+
export declare class CodeModeManager {
|
|
17
|
+
private mcpManager;
|
|
18
|
+
private eligibleTools;
|
|
19
|
+
private typeHints;
|
|
20
|
+
private options;
|
|
21
|
+
constructor(options?: CodeModeManagerOptions);
|
|
22
|
+
/** Initialize with MCP manager, discover eligible tools, generate type hints. */
|
|
23
|
+
initialize(mcpManager: McpClientManager): void;
|
|
24
|
+
/** Refresh eligible tools and type hints (call on tools/list_changed). */
|
|
25
|
+
refresh(): void;
|
|
26
|
+
/** Get the type hints string for injection into system prompt. */
|
|
27
|
+
getTypeHints(): string;
|
|
28
|
+
/** Get eligible tools. */
|
|
29
|
+
getEligibleTools(): McpTool[];
|
|
30
|
+
/** Whether code mode has any eligible tools. */
|
|
31
|
+
get isActive(): boolean;
|
|
32
|
+
/** Execute code in search mode (tool catalog queries). */
|
|
33
|
+
searchTools(code: string): Promise<ExecuteResult>;
|
|
34
|
+
/** Execute code that chains MCP tool calls. */
|
|
35
|
+
executeCode(code: string): Promise<ExecuteResult>;
|
|
36
|
+
/** Create the Pi tool definitions for code_search and code_execute. */
|
|
37
|
+
createTools(): {
|
|
38
|
+
codeSearch: {
|
|
39
|
+
name: string;
|
|
40
|
+
label: string;
|
|
41
|
+
description: string;
|
|
42
|
+
parameters: import("typebox").TObject<{
|
|
43
|
+
code: import("typebox").TString;
|
|
44
|
+
}>;
|
|
45
|
+
execute(_toolCallId: string, params: {
|
|
46
|
+
code: string;
|
|
47
|
+
}, _signal: AbortSignal | undefined, _onUpdate: undefined, _ctx: import("@sammorrowdrums/mcpi").ExtensionContext): Promise<import("@sammorrowdrums/mcpi-agent").AgentToolResult<import("./tools.js").CodeModeToolDetails>>;
|
|
48
|
+
};
|
|
49
|
+
codeExecute: {
|
|
50
|
+
name: string;
|
|
51
|
+
label: string;
|
|
52
|
+
description: string;
|
|
53
|
+
parameters: import("typebox").TObject<{
|
|
54
|
+
code: import("typebox").TString;
|
|
55
|
+
}>;
|
|
56
|
+
execute(_toolCallId: string, params: {
|
|
57
|
+
code: string;
|
|
58
|
+
}, _signal: AbortSignal | undefined, _onUpdate: undefined, _ctx: import("@sammorrowdrums/mcpi").ExtensionContext): Promise<import("@sammorrowdrums/mcpi-agent").AgentToolResult<import("./tools.js").CodeModeToolDetails>>;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/** Format a system prompt section for code mode. */
|
|
62
|
+
formatSystemPromptSection(): string;
|
|
63
|
+
private execute;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/code-mode/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEjE,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAKpE,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE9F,MAAM,WAAW,sBAAuB,SAAQ,eAAe;IAC7D,wCAAwC;IACxC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAyB;gBAE5B,OAAO,GAAE,sBAA2B;IAIhD,iFAAiF;IACjF,UAAU,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI;IAK9C,0EAA0E;IAC1E,OAAO,IAAI,IAAI;IASf,kEAAkE;IAClE,YAAY,IAAI,MAAM;IAItB,0BAA0B;IAC1B,gBAAgB,IAAI,OAAO,EAAE;IAI7B,gDAAgD;IAChD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,0DAA0D;IACpD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAIvD,+CAA+C;IACzC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAIvD,uEAAuE;IACvE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;IAOX,oDAAoD;IACpD,yBAAyB,IAAI,MAAM;YAwErB,OAAO;CAqDtB"}
|