@robinbraemer/codemode 0.1.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/LICENSE +21 -0
- package/README.md +278 -0
- package/dist/chunk-NSUQUO7S.js +115 -0
- package/dist/chunk-NSUQUO7S.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-ZWSO33DZ.js +148 -0
- package/dist/chunk-ZWSO33DZ.js.map +1 -0
- package/dist/codemode-DbXujxeq.d.ts +198 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +308 -0
- package/dist/index.js.map +1 -0
- package/dist/isolated-vm-57EIYEJM.js +8 -0
- package/dist/isolated-vm-57EIYEJM.js.map +1 -0
- package/dist/mcp.d.ts +38 -0
- package/dist/mcp.js +4063 -0
- package/dist/mcp.js.map +1 -0
- package/dist/quickjs-BGVQS2YE.js +8 -0
- package/dist/quickjs-BGVQS2YE.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result from executing sandboxed code.
|
|
3
|
+
*/
|
|
4
|
+
interface ExecuteResult {
|
|
5
|
+
result: unknown;
|
|
6
|
+
error?: string;
|
|
7
|
+
logs: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Sandbox executor interface. Implement this to use a custom sandbox runtime.
|
|
11
|
+
*
|
|
12
|
+
* Built-in implementations:
|
|
13
|
+
* - `IsolatedVMExecutor` (requires `isolated-vm` peer dependency)
|
|
14
|
+
* - `QuickJSExecutor` (requires `quickjs-emscripten` peer dependency)
|
|
15
|
+
*/
|
|
16
|
+
interface Executor {
|
|
17
|
+
/**
|
|
18
|
+
* Execute JavaScript code in a sandboxed environment.
|
|
19
|
+
*
|
|
20
|
+
* @param code - An async arrow function as a string, e.g. `async () => { ... }`
|
|
21
|
+
* @param globals - Named globals to inject into the sandbox. Each value is either:
|
|
22
|
+
* - A plain object/array/primitive (injected as a frozen read-only value)
|
|
23
|
+
* - A function (injected as a callable host function)
|
|
24
|
+
* - An object with function values (injected as a namespace with callable methods)
|
|
25
|
+
*/
|
|
26
|
+
execute(code: string, globals: Record<string, unknown>): Promise<ExecuteResult>;
|
|
27
|
+
/** Clean up resources. */
|
|
28
|
+
dispose?(): void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Options for configuring the sandbox executor.
|
|
32
|
+
*/
|
|
33
|
+
interface SandboxOptions {
|
|
34
|
+
/** Memory limit in MB (default: 64) */
|
|
35
|
+
memoryMB?: number;
|
|
36
|
+
/** Execution timeout in ms (default: 30000) */
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A fetch-compatible request handler.
|
|
41
|
+
* Works with Hono's `app.request()`, standard `fetch`, or any function
|
|
42
|
+
* that takes a Request and returns a Response.
|
|
43
|
+
*/
|
|
44
|
+
type RequestHandler = (input: string | URL | Request, init?: RequestInit) => Response | Promise<Response>;
|
|
45
|
+
/**
|
|
46
|
+
* OpenAPI specification object (JSON-parsed OpenAPI 3.x document).
|
|
47
|
+
*/
|
|
48
|
+
type OpenAPISpec = {
|
|
49
|
+
openapi?: string;
|
|
50
|
+
info?: {
|
|
51
|
+
title?: string;
|
|
52
|
+
version?: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
};
|
|
55
|
+
paths?: Record<string, unknown>;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Spec provider: a static spec, a URL to fetch, or an async getter function.
|
|
60
|
+
*/
|
|
61
|
+
type SpecProvider = OpenAPISpec | (() => OpenAPISpec | Promise<OpenAPISpec>);
|
|
62
|
+
/**
|
|
63
|
+
* Options for creating a CodeMode instance.
|
|
64
|
+
*/
|
|
65
|
+
interface CodeModeOptions {
|
|
66
|
+
/**
|
|
67
|
+
* OpenAPI spec or async getter that returns one.
|
|
68
|
+
* The spec is made available inside the `search()` tool as a `spec` global.
|
|
69
|
+
*/
|
|
70
|
+
spec: SpecProvider;
|
|
71
|
+
/**
|
|
72
|
+
* Fetch-compatible request handler for API calls from the `execute()` tool.
|
|
73
|
+
*
|
|
74
|
+
* For Hono: `app.request.bind(app)` (in-process, no network hop)
|
|
75
|
+
* For standard fetch: `fetch` or any `(Request) => Response` function
|
|
76
|
+
*/
|
|
77
|
+
request: RequestHandler;
|
|
78
|
+
/**
|
|
79
|
+
* Namespace for the client object inside the execute sandbox.
|
|
80
|
+
* Default: `"api"`
|
|
81
|
+
*
|
|
82
|
+
* Example: with namespace "cnap", sandbox code calls `cnap.request(...)`.
|
|
83
|
+
*/
|
|
84
|
+
namespace?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Base URL prepended to relative paths in sandbox requests.
|
|
87
|
+
* Default: `"http://localhost"`
|
|
88
|
+
*
|
|
89
|
+
* Only used when the sandbox code provides a relative path like `/v1/clusters`.
|
|
90
|
+
* For Hono app.request(), any base URL works since it doesn't hit the network.
|
|
91
|
+
*/
|
|
92
|
+
baseUrl?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Sandbox configuration.
|
|
95
|
+
*/
|
|
96
|
+
sandbox?: SandboxOptions;
|
|
97
|
+
/**
|
|
98
|
+
* Custom executor instance. If not provided, auto-detects
|
|
99
|
+
* isolated-vm or quickjs-emscripten from installed peer dependencies.
|
|
100
|
+
*/
|
|
101
|
+
executor?: Executor;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* MCP tool definition (compatible with @modelcontextprotocol/sdk).
|
|
105
|
+
*/
|
|
106
|
+
interface ToolDefinition {
|
|
107
|
+
name: string;
|
|
108
|
+
description: string;
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object";
|
|
111
|
+
properties: Record<string, unknown>;
|
|
112
|
+
required?: string[];
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* MCP tool call result.
|
|
117
|
+
*/
|
|
118
|
+
interface ToolCallResult {
|
|
119
|
+
content: Array<{
|
|
120
|
+
type: "text";
|
|
121
|
+
text: string;
|
|
122
|
+
}>;
|
|
123
|
+
isError?: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* CodeMode provides `search` and `execute` MCP tools that let an AI agent
|
|
128
|
+
* discover and call your API by writing JavaScript code in a sandboxed runtime.
|
|
129
|
+
*
|
|
130
|
+
* Instead of defining hundreds of individual MCP tools (one per API endpoint),
|
|
131
|
+
* CodeMode exposes just two tools:
|
|
132
|
+
* - `search` — the agent writes JS to filter your OpenAPI spec
|
|
133
|
+
* - `execute` — the agent writes JS to call your API via a typed client
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* import { CodeMode } from 'codemode';
|
|
138
|
+
* import { Hono } from 'hono';
|
|
139
|
+
*
|
|
140
|
+
* const app = new Hono();
|
|
141
|
+
* // ... define routes ...
|
|
142
|
+
*
|
|
143
|
+
* const codemode = new CodeMode({
|
|
144
|
+
* spec: () => generateOpenAPISpec(app),
|
|
145
|
+
* request: app.request.bind(app),
|
|
146
|
+
* namespace: 'cnap',
|
|
147
|
+
* });
|
|
148
|
+
*
|
|
149
|
+
* // Get MCP tool definitions
|
|
150
|
+
* const tools = codemode.tools();
|
|
151
|
+
*
|
|
152
|
+
* // Handle a tool call
|
|
153
|
+
* const result = await codemode.callTool('search', { code: 'async () => ...' });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
declare class CodeMode {
|
|
157
|
+
private specProvider;
|
|
158
|
+
private requestBridge;
|
|
159
|
+
private namespace;
|
|
160
|
+
private executor;
|
|
161
|
+
private executorPromise;
|
|
162
|
+
private options;
|
|
163
|
+
private searchToolName;
|
|
164
|
+
private executeToolName;
|
|
165
|
+
constructor(options: CodeModeOptions);
|
|
166
|
+
/**
|
|
167
|
+
* Override the default tool names.
|
|
168
|
+
*/
|
|
169
|
+
setToolNames(search: string, execute: string): this;
|
|
170
|
+
/**
|
|
171
|
+
* Returns MCP tool definitions for search and execute.
|
|
172
|
+
*/
|
|
173
|
+
tools(): ToolDefinition[];
|
|
174
|
+
/**
|
|
175
|
+
* Handle an MCP tool call.
|
|
176
|
+
*/
|
|
177
|
+
callTool(toolName: string, args: {
|
|
178
|
+
code: string;
|
|
179
|
+
}): Promise<ToolCallResult>;
|
|
180
|
+
/**
|
|
181
|
+
* Execute a search against the OpenAPI spec.
|
|
182
|
+
* The code runs in a sandbox with `spec` available as a global.
|
|
183
|
+
*/
|
|
184
|
+
search(code: string): Promise<ToolCallResult>;
|
|
185
|
+
/**
|
|
186
|
+
* Execute API calls in the sandbox.
|
|
187
|
+
* The code runs with `{namespace}.request()` available as a global.
|
|
188
|
+
*/
|
|
189
|
+
execute(code: string): Promise<ToolCallResult>;
|
|
190
|
+
/**
|
|
191
|
+
* Clean up sandbox resources.
|
|
192
|
+
*/
|
|
193
|
+
dispose(): void;
|
|
194
|
+
private resolveSpec;
|
|
195
|
+
private getExecutor;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { CodeMode as C, type Executor as E, type OpenAPISpec as O, type RequestHandler as R, type SandboxOptions as S, type ToolCallResult as T, type ExecuteResult as a, type CodeModeOptions as b, type SpecProvider as c, type ToolDefinition as d };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { E as Executor, S as SandboxOptions, a as ExecuteResult, R as RequestHandler } from './codemode-DbXujxeq.js';
|
|
2
|
+
export { C as CodeMode, b as CodeModeOptions, O as OpenAPISpec, c as SpecProvider, T as ToolCallResult, d as ToolDefinition } from './codemode-DbXujxeq.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Executor implementation using isolated-vm (V8 isolates).
|
|
6
|
+
* Requires `isolated-vm` v6+ as a peer dependency.
|
|
7
|
+
*
|
|
8
|
+
* Each execute() call creates a fresh V8 isolate with its own heap — no state
|
|
9
|
+
* leaks between calls. The sandbox has zero I/O capabilities by default (no
|
|
10
|
+
* fetch, no fs, no require). The only way out is through injected host functions.
|
|
11
|
+
*/
|
|
12
|
+
declare class IsolatedVMExecutor implements Executor {
|
|
13
|
+
private memoryMB;
|
|
14
|
+
private timeoutMs;
|
|
15
|
+
constructor(options?: SandboxOptions);
|
|
16
|
+
execute(code: string, globals: Record<string, unknown>): Promise<ExecuteResult>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Executor implementation using quickjs-emscripten (QuickJS compiled to WASM).
|
|
21
|
+
* Requires `quickjs-emscripten` as a peer dependency.
|
|
22
|
+
*
|
|
23
|
+
* Advantages over isolated-vm:
|
|
24
|
+
* - Pure WASM, no native dependencies (works everywhere including Bun)
|
|
25
|
+
* - WASM-level sandbox isolation
|
|
26
|
+
*
|
|
27
|
+
* Tradeoffs:
|
|
28
|
+
* - ~3-5x slower than V8 for compute (negligible for API orchestration)
|
|
29
|
+
* - Only one async suspension at a time per module
|
|
30
|
+
*/
|
|
31
|
+
declare class QuickJSExecutor implements Executor {
|
|
32
|
+
private memoryBytes;
|
|
33
|
+
private timeoutMs;
|
|
34
|
+
constructor(options?: SandboxOptions);
|
|
35
|
+
execute(code: string, globals: Record<string, unknown>): Promise<ExecuteResult>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Auto-detect and create an executor from available peer dependencies.
|
|
40
|
+
* Tries isolated-vm first, then quickjs-emscripten.
|
|
41
|
+
*/
|
|
42
|
+
declare function createExecutor(options?: SandboxOptions): Promise<Executor>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Options for a sandbox request call.
|
|
46
|
+
* This is what the LLM-generated code passes to `{namespace}.request()`.
|
|
47
|
+
*/
|
|
48
|
+
interface SandboxRequestOptions {
|
|
49
|
+
method: string;
|
|
50
|
+
path: string;
|
|
51
|
+
query?: Record<string, string | number | boolean>;
|
|
52
|
+
body?: unknown;
|
|
53
|
+
headers?: Record<string, string>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Response returned to the sandbox from a request call.
|
|
57
|
+
*/
|
|
58
|
+
interface SandboxResponse {
|
|
59
|
+
status: number;
|
|
60
|
+
headers: Record<string, string>;
|
|
61
|
+
body: unknown;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates the `request()` function that gets injected into the execute sandbox.
|
|
65
|
+
* Bridges sandbox API calls to the host request handler (Hono app.request, fetch, etc.).
|
|
66
|
+
*/
|
|
67
|
+
declare function createRequestBridge(handler: RequestHandler, baseUrl: string): (options: SandboxRequestOptions) => Promise<SandboxResponse>;
|
|
68
|
+
|
|
69
|
+
export { ExecuteResult, Executor, IsolatedVMExecutor, QuickJSExecutor, RequestHandler, SandboxOptions, type SandboxRequestOptions, type SandboxResponse, createExecutor, createRequestBridge };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IsolatedVMExecutor
|
|
3
|
+
} from "./chunk-NSUQUO7S.js";
|
|
4
|
+
import {
|
|
5
|
+
QuickJSExecutor
|
|
6
|
+
} from "./chunk-ZWSO33DZ.js";
|
|
7
|
+
import "./chunk-PZ5AY32C.js";
|
|
8
|
+
|
|
9
|
+
// src/executor/auto.ts
|
|
10
|
+
async function createExecutor(options = {}) {
|
|
11
|
+
try {
|
|
12
|
+
await import("isolated-vm");
|
|
13
|
+
const { IsolatedVMExecutor: IsolatedVMExecutor2 } = await import("./isolated-vm-57EIYEJM.js");
|
|
14
|
+
return new IsolatedVMExecutor2(options);
|
|
15
|
+
} catch {
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
await import("quickjs-emscripten");
|
|
19
|
+
const { QuickJSExecutor: QuickJSExecutor2 } = await import("./quickjs-BGVQS2YE.js");
|
|
20
|
+
return new QuickJSExecutor2(options);
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
throw new Error(
|
|
24
|
+
"No sandbox runtime found. Install one of:\n npm install isolated-vm # V8 isolates, fastest (Node.js only)\n npm install quickjs-emscripten # WASM sandbox, portable (Node.js, Bun, browsers)"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/request-bridge.ts
|
|
29
|
+
function createRequestBridge(handler, baseUrl) {
|
|
30
|
+
return async (options) => {
|
|
31
|
+
const { method, path, query, body, headers } = options;
|
|
32
|
+
const url = new URL(path, baseUrl);
|
|
33
|
+
if (query) {
|
|
34
|
+
for (const [key, value] of Object.entries(query)) {
|
|
35
|
+
url.searchParams.set(key, String(value));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const init = {
|
|
39
|
+
method: method.toUpperCase(),
|
|
40
|
+
headers: {
|
|
41
|
+
...headers
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
if (body !== void 0 && body !== null) {
|
|
45
|
+
init.body = JSON.stringify(body);
|
|
46
|
+
init.headers["content-type"] = init.headers["content-type"] ?? "application/json";
|
|
47
|
+
}
|
|
48
|
+
const response = await handler(url.toString(), init);
|
|
49
|
+
const responseHeaders = {};
|
|
50
|
+
response.headers.forEach((value, key) => {
|
|
51
|
+
responseHeaders[key] = value;
|
|
52
|
+
});
|
|
53
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
54
|
+
let responseBody;
|
|
55
|
+
if (contentType.includes("application/json")) {
|
|
56
|
+
try {
|
|
57
|
+
responseBody = await response.json();
|
|
58
|
+
} catch {
|
|
59
|
+
responseBody = await response.text();
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
responseBody = await response.text();
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
status: response.status,
|
|
66
|
+
headers: responseHeaders,
|
|
67
|
+
body: responseBody
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/tools.ts
|
|
73
|
+
function createSearchToolDefinition(toolName) {
|
|
74
|
+
return {
|
|
75
|
+
name: toolName,
|
|
76
|
+
description: `Search the API specification to discover available endpoints.
|
|
77
|
+
|
|
78
|
+
Write an async JavaScript arrow function that filters and explores the \`spec\` object (an OpenAPI 3.x document). The full spec is available as a global variable.
|
|
79
|
+
|
|
80
|
+
Common patterns:
|
|
81
|
+
- Find endpoints by path: \`spec.paths\` is an object keyed by path string
|
|
82
|
+
- Each path has HTTP methods (get, post, put, delete, patch) as keys
|
|
83
|
+
- Each operation has: summary, description, parameters, requestBody, responses
|
|
84
|
+
- Use spec.components.schemas for data models
|
|
85
|
+
|
|
86
|
+
Examples:
|
|
87
|
+
// Find all cluster-related endpoints
|
|
88
|
+
async () => {
|
|
89
|
+
return Object.entries(spec.paths)
|
|
90
|
+
.filter(([p]) => p.includes('/clusters'))
|
|
91
|
+
.flatMap(([path, methods]) =>
|
|
92
|
+
Object.entries(methods)
|
|
93
|
+
.filter(([m]) => ['get','post','put','delete','patch'].includes(m))
|
|
94
|
+
.map(([method, op]) => ({
|
|
95
|
+
method: method.toUpperCase(), path, summary: op.summary
|
|
96
|
+
}))
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Get the schema for a specific model
|
|
101
|
+
async () => {
|
|
102
|
+
return spec.components?.schemas?.Product;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
Return the matching endpoints/schemas as a structured result the agent can use to plan execute() calls.`,
|
|
106
|
+
inputSchema: {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: {
|
|
109
|
+
code: {
|
|
110
|
+
type: "string",
|
|
111
|
+
description: "An async JavaScript arrow function that searches the `spec` object. Must return a value."
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
required: ["code"]
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function createExecuteToolDefinition(toolName, namespace) {
|
|
119
|
+
return {
|
|
120
|
+
name: toolName,
|
|
121
|
+
description: `Execute API calls by writing JavaScript code.
|
|
122
|
+
|
|
123
|
+
Write an async JavaScript arrow function that uses \`${namespace}.request()\` to make API calls. The request function handles authentication automatically.
|
|
124
|
+
|
|
125
|
+
\`${namespace}.request(options)\` takes:
|
|
126
|
+
- method: HTTP method string ("GET", "POST", "PUT", "DELETE", "PATCH")
|
|
127
|
+
- path: API path string (e.g. "/v1/clusters")
|
|
128
|
+
- query: optional object of query parameters
|
|
129
|
+
- body: optional request body (will be JSON-serialized)
|
|
130
|
+
- headers: optional object of additional headers
|
|
131
|
+
|
|
132
|
+
Returns: { status: number, headers: object, body: unknown }
|
|
133
|
+
|
|
134
|
+
Examples:
|
|
135
|
+
// List resources
|
|
136
|
+
async () => {
|
|
137
|
+
const res = await ${namespace}.request({ method: "GET", path: "/v1/clusters" });
|
|
138
|
+
return res.body;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Create a resource
|
|
142
|
+
async () => {
|
|
143
|
+
return ${namespace}.request({
|
|
144
|
+
method: "POST",
|
|
145
|
+
path: "/v1/products",
|
|
146
|
+
body: { name: "My Product", chart: "nginx" }
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Chain multiple calls
|
|
151
|
+
async () => {
|
|
152
|
+
const clusters = await ${namespace}.request({ method: "GET", path: "/v1/clusters" });
|
|
153
|
+
const details = await Promise.all(
|
|
154
|
+
clusters.body.map(c =>
|
|
155
|
+
${namespace}.request({ method: "GET", path: \`/v1/clusters/\${c.id}\` })
|
|
156
|
+
)
|
|
157
|
+
);
|
|
158
|
+
return details.map(d => d.body);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
Write clean, focused code. Return the data the user needs.`,
|
|
162
|
+
inputSchema: {
|
|
163
|
+
type: "object",
|
|
164
|
+
properties: {
|
|
165
|
+
code: {
|
|
166
|
+
type: "string",
|
|
167
|
+
description: `An async JavaScript arrow function that uses \`${namespace}.request()\` to make API calls.`
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
required: ["code"]
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/codemode.ts
|
|
176
|
+
var CodeMode = class {
|
|
177
|
+
specProvider;
|
|
178
|
+
requestBridge;
|
|
179
|
+
namespace;
|
|
180
|
+
executor;
|
|
181
|
+
executorPromise = null;
|
|
182
|
+
options;
|
|
183
|
+
searchToolName;
|
|
184
|
+
executeToolName;
|
|
185
|
+
constructor(options) {
|
|
186
|
+
this.options = options;
|
|
187
|
+
this.specProvider = options.spec;
|
|
188
|
+
this.namespace = options.namespace ?? "api";
|
|
189
|
+
this.executor = options.executor ?? null;
|
|
190
|
+
this.searchToolName = "search";
|
|
191
|
+
this.executeToolName = "execute";
|
|
192
|
+
const baseUrl = options.baseUrl ?? "http://localhost";
|
|
193
|
+
const bridge = createRequestBridge(options.request, baseUrl);
|
|
194
|
+
this.requestBridge = (...args) => bridge(args[0]);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Override the default tool names.
|
|
198
|
+
*/
|
|
199
|
+
setToolNames(search, execute) {
|
|
200
|
+
this.searchToolName = search;
|
|
201
|
+
this.executeToolName = execute;
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Returns MCP tool definitions for search and execute.
|
|
206
|
+
*/
|
|
207
|
+
tools() {
|
|
208
|
+
return [
|
|
209
|
+
createSearchToolDefinition(this.searchToolName),
|
|
210
|
+
createExecuteToolDefinition(this.executeToolName, this.namespace)
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Handle an MCP tool call.
|
|
215
|
+
*/
|
|
216
|
+
async callTool(toolName, args) {
|
|
217
|
+
if (toolName === this.searchToolName) {
|
|
218
|
+
return this.search(args.code);
|
|
219
|
+
}
|
|
220
|
+
if (toolName === this.executeToolName) {
|
|
221
|
+
return this.execute(args.code);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
|
|
225
|
+
isError: true
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Execute a search against the OpenAPI spec.
|
|
230
|
+
* The code runs in a sandbox with `spec` available as a global.
|
|
231
|
+
*/
|
|
232
|
+
async search(code) {
|
|
233
|
+
const executor = await this.getExecutor();
|
|
234
|
+
const spec = await this.resolveSpec();
|
|
235
|
+
const result = await executor.execute(code, { spec });
|
|
236
|
+
return formatResult(result);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Execute API calls in the sandbox.
|
|
240
|
+
* The code runs with `{namespace}.request()` available as a global.
|
|
241
|
+
*/
|
|
242
|
+
async execute(code) {
|
|
243
|
+
const executor = await this.getExecutor();
|
|
244
|
+
const client = {
|
|
245
|
+
request: this.requestBridge
|
|
246
|
+
};
|
|
247
|
+
const result = await executor.execute(code, {
|
|
248
|
+
[this.namespace]: client
|
|
249
|
+
});
|
|
250
|
+
return formatResult(result);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Clean up sandbox resources.
|
|
254
|
+
*/
|
|
255
|
+
dispose() {
|
|
256
|
+
this.executor?.dispose?.();
|
|
257
|
+
}
|
|
258
|
+
async resolveSpec() {
|
|
259
|
+
if (typeof this.specProvider === "function") {
|
|
260
|
+
return await this.specProvider();
|
|
261
|
+
}
|
|
262
|
+
return this.specProvider;
|
|
263
|
+
}
|
|
264
|
+
async getExecutor() {
|
|
265
|
+
if (this.executor) return this.executor;
|
|
266
|
+
if (!this.executorPromise) {
|
|
267
|
+
this.executorPromise = createExecutor(this.options.sandbox).then(
|
|
268
|
+
(executor) => {
|
|
269
|
+
this.executor = executor;
|
|
270
|
+
return executor;
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
return this.executorPromise;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
function formatResult(result) {
|
|
278
|
+
if (result.error) {
|
|
279
|
+
const parts2 = [];
|
|
280
|
+
if (result.logs.length > 0) {
|
|
281
|
+
parts2.push(`Console output:
|
|
282
|
+
${result.logs.join("\n")}`);
|
|
283
|
+
}
|
|
284
|
+
parts2.push(`Error: ${result.error}`);
|
|
285
|
+
return {
|
|
286
|
+
content: [{ type: "text", text: parts2.join("\n\n") }],
|
|
287
|
+
isError: true
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const parts = [];
|
|
291
|
+
if (result.logs.length > 0) {
|
|
292
|
+
parts.push(`Console output:
|
|
293
|
+
${result.logs.join("\n")}`);
|
|
294
|
+
}
|
|
295
|
+
const resultText = typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2);
|
|
296
|
+
parts.push(resultText);
|
|
297
|
+
return {
|
|
298
|
+
content: [{ type: "text", text: parts.join("\n\n") }]
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
export {
|
|
302
|
+
CodeMode,
|
|
303
|
+
IsolatedVMExecutor,
|
|
304
|
+
QuickJSExecutor,
|
|
305
|
+
createExecutor,
|
|
306
|
+
createRequestBridge
|
|
307
|
+
};
|
|
308
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/executor/auto.ts","../src/request-bridge.ts","../src/tools.ts","../src/codemode.ts"],"sourcesContent":["import type { Executor, SandboxOptions } from \"../types.js\";\n\n/**\n * Auto-detect and create an executor from available peer dependencies.\n * Tries isolated-vm first, then quickjs-emscripten.\n */\nexport async function createExecutor(\n options: SandboxOptions = {},\n): Promise<Executor> {\n // Try isolated-vm first (faster, V8-native)\n try {\n // @ts-ignore — optional peer dependency\n await import(\"isolated-vm\");\n const { IsolatedVMExecutor } = await import(\"./isolated-vm.js\");\n return new IsolatedVMExecutor(options);\n } catch {\n // Not available\n }\n\n // Try quickjs-emscripten (portable WASM)\n try {\n await import(\"quickjs-emscripten\");\n const { QuickJSExecutor } = await import(\"./quickjs.js\");\n return new QuickJSExecutor(options);\n } catch {\n // Not available\n }\n\n throw new Error(\n \"No sandbox runtime found. Install one of:\\n\" +\n \" npm install isolated-vm # V8 isolates, fastest (Node.js only)\\n\" +\n \" npm install quickjs-emscripten # WASM sandbox, portable (Node.js, Bun, browsers)\",\n );\n}\n","import type { RequestHandler } from \"./types.js\";\n\n/**\n * Options for a sandbox request call.\n * This is what the LLM-generated code passes to `{namespace}.request()`.\n */\nexport interface SandboxRequestOptions {\n method: string;\n path: string;\n query?: Record<string, string | number | boolean>;\n body?: unknown;\n headers?: Record<string, string>;\n}\n\n/**\n * Response returned to the sandbox from a request call.\n */\nexport interface SandboxResponse {\n status: number;\n headers: Record<string, string>;\n body: unknown;\n}\n\n/**\n * Creates the `request()` function that gets injected into the execute sandbox.\n * Bridges sandbox API calls to the host request handler (Hono app.request, fetch, etc.).\n */\nexport function createRequestBridge(\n handler: RequestHandler,\n baseUrl: string,\n): (options: SandboxRequestOptions) => Promise<SandboxResponse> {\n return async (options: SandboxRequestOptions): Promise<SandboxResponse> => {\n const { method, path, query, body, headers } = options;\n\n // Build URL\n const url = new URL(path, baseUrl);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n url.searchParams.set(key, String(value));\n }\n }\n\n // Build request init\n const init: RequestInit = {\n method: method.toUpperCase(),\n headers: {\n ...headers,\n },\n };\n\n if (body !== undefined && body !== null) {\n init.body = JSON.stringify(body);\n (init.headers as Record<string, string>)[\"content-type\"] =\n (init.headers as Record<string, string>)[\"content-type\"] ?? \"application/json\";\n }\n\n // Call the host handler\n const response = await handler(url.toString(), init);\n\n // Parse response\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n let responseBody: unknown;\n if (contentType.includes(\"application/json\")) {\n try {\n responseBody = await response.json();\n } catch {\n responseBody = await response.text();\n }\n } else {\n responseBody = await response.text();\n }\n\n return {\n status: response.status,\n headers: responseHeaders,\n body: responseBody,\n };\n };\n}\n","import type { ToolDefinition } from \"./types.js\";\n\nexport function createSearchToolDefinition(toolName: string): ToolDefinition {\n return {\n name: toolName,\n description: `Search the API specification to discover available endpoints.\n\nWrite an async JavaScript arrow function that filters and explores the \\`spec\\` object (an OpenAPI 3.x document). The full spec is available as a global variable.\n\nCommon patterns:\n- Find endpoints by path: \\`spec.paths\\` is an object keyed by path string\n- Each path has HTTP methods (get, post, put, delete, patch) as keys\n- Each operation has: summary, description, parameters, requestBody, responses\n- Use spec.components.schemas for data models\n\nExamples:\n // Find all cluster-related endpoints\n async () => {\n return Object.entries(spec.paths)\n .filter(([p]) => p.includes('/clusters'))\n .flatMap(([path, methods]) =>\n Object.entries(methods)\n .filter(([m]) => ['get','post','put','delete','patch'].includes(m))\n .map(([method, op]) => ({\n method: method.toUpperCase(), path, summary: op.summary\n }))\n );\n }\n\n // Get the schema for a specific model\n async () => {\n return spec.components?.schemas?.Product;\n }\n\nReturn the matching endpoints/schemas as a structured result the agent can use to plan execute() calls.`,\n inputSchema: {\n type: \"object\",\n properties: {\n code: {\n type: \"string\",\n description:\n \"An async JavaScript arrow function that searches the `spec` object. Must return a value.\",\n },\n },\n required: [\"code\"],\n },\n };\n}\n\nexport function createExecuteToolDefinition(\n toolName: string,\n namespace: string,\n): ToolDefinition {\n return {\n name: toolName,\n description: `Execute API calls by writing JavaScript code.\n\nWrite an async JavaScript arrow function that uses \\`${namespace}.request()\\` to make API calls. The request function handles authentication automatically.\n\n\\`${namespace}.request(options)\\` takes:\n - method: HTTP method string (\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\")\n - path: API path string (e.g. \"/v1/clusters\")\n - query: optional object of query parameters\n - body: optional request body (will be JSON-serialized)\n - headers: optional object of additional headers\n\nReturns: { status: number, headers: object, body: unknown }\n\nExamples:\n // List resources\n async () => {\n const res = await ${namespace}.request({ method: \"GET\", path: \"/v1/clusters\" });\n return res.body;\n }\n\n // Create a resource\n async () => {\n return ${namespace}.request({\n method: \"POST\",\n path: \"/v1/products\",\n body: { name: \"My Product\", chart: \"nginx\" }\n });\n }\n\n // Chain multiple calls\n async () => {\n const clusters = await ${namespace}.request({ method: \"GET\", path: \"/v1/clusters\" });\n const details = await Promise.all(\n clusters.body.map(c =>\n ${namespace}.request({ method: \"GET\", path: \\`/v1/clusters/\\${c.id}\\` })\n )\n );\n return details.map(d => d.body);\n }\n\nWrite clean, focused code. Return the data the user needs.`,\n inputSchema: {\n type: \"object\",\n properties: {\n code: {\n type: \"string\",\n description: `An async JavaScript arrow function that uses \\`${namespace}.request()\\` to make API calls.`,\n },\n },\n required: [\"code\"],\n },\n };\n}\n","import { createExecutor } from \"./executor/auto.js\";\nimport { createRequestBridge } from \"./request-bridge.js\";\nimport { createExecuteToolDefinition, createSearchToolDefinition } from \"./tools.js\";\nimport type {\n CodeModeOptions,\n Executor,\n OpenAPISpec,\n SpecProvider,\n ToolCallResult,\n ToolDefinition,\n} from \"./types.js\";\n\n/**\n * CodeMode provides `search` and `execute` MCP tools that let an AI agent\n * discover and call your API by writing JavaScript code in a sandboxed runtime.\n *\n * Instead of defining hundreds of individual MCP tools (one per API endpoint),\n * CodeMode exposes just two tools:\n * - `search` — the agent writes JS to filter your OpenAPI spec\n * - `execute` — the agent writes JS to call your API via a typed client\n *\n * @example\n * ```ts\n * import { CodeMode } from 'codemode';\n * import { Hono } from 'hono';\n *\n * const app = new Hono();\n * // ... define routes ...\n *\n * const codemode = new CodeMode({\n * spec: () => generateOpenAPISpec(app),\n * request: app.request.bind(app),\n * namespace: 'cnap',\n * });\n *\n * // Get MCP tool definitions\n * const tools = codemode.tools();\n *\n * // Handle a tool call\n * const result = await codemode.callTool('search', { code: 'async () => ...' });\n * ```\n */\nexport class CodeMode {\n private specProvider: SpecProvider;\n private requestBridge: (...args: unknown[]) => Promise<unknown>;\n private namespace: string;\n private executor: Executor | null;\n private executorPromise: Promise<Executor> | null = null;\n private options: CodeModeOptions;\n private searchToolName: string;\n private executeToolName: string;\n\n constructor(options: CodeModeOptions) {\n this.options = options;\n this.specProvider = options.spec;\n this.namespace = options.namespace ?? \"api\";\n this.executor = options.executor ?? null;\n this.searchToolName = \"search\";\n this.executeToolName = \"execute\";\n\n const baseUrl = options.baseUrl ?? \"http://localhost\";\n const bridge = createRequestBridge(options.request, baseUrl);\n this.requestBridge = (...args: unknown[]) => bridge(args[0] as any);\n }\n\n /**\n * Override the default tool names.\n */\n setToolNames(search: string, execute: string): this {\n this.searchToolName = search;\n this.executeToolName = execute;\n return this;\n }\n\n /**\n * Returns MCP tool definitions for search and execute.\n */\n tools(): ToolDefinition[] {\n return [\n createSearchToolDefinition(this.searchToolName),\n createExecuteToolDefinition(this.executeToolName, this.namespace),\n ];\n }\n\n /**\n * Handle an MCP tool call.\n */\n async callTool(\n toolName: string,\n args: { code: string },\n ): Promise<ToolCallResult> {\n if (toolName === this.searchToolName) {\n return this.search(args.code);\n }\n if (toolName === this.executeToolName) {\n return this.execute(args.code);\n }\n return {\n content: [{ type: \"text\", text: `Unknown tool: ${toolName}` }],\n isError: true,\n };\n }\n\n /**\n * Execute a search against the OpenAPI spec.\n * The code runs in a sandbox with `spec` available as a global.\n */\n async search(code: string): Promise<ToolCallResult> {\n const executor = await this.getExecutor();\n const spec = await this.resolveSpec();\n\n const result = await executor.execute(code, { spec });\n\n return formatResult(result);\n }\n\n /**\n * Execute API calls in the sandbox.\n * The code runs with `{namespace}.request()` available as a global.\n */\n async execute(code: string): Promise<ToolCallResult> {\n const executor = await this.getExecutor();\n\n const client = {\n request: this.requestBridge,\n };\n\n const result = await executor.execute(code, {\n [this.namespace]: client,\n });\n\n return formatResult(result);\n }\n\n /**\n * Clean up sandbox resources.\n */\n dispose(): void {\n this.executor?.dispose?.();\n }\n\n private async resolveSpec(): Promise<OpenAPISpec> {\n if (typeof this.specProvider === \"function\") {\n return await this.specProvider();\n }\n return this.specProvider;\n }\n\n private async getExecutor(): Promise<Executor> {\n if (this.executor) return this.executor;\n\n // Lazy-init with deduplication\n if (!this.executorPromise) {\n this.executorPromise = createExecutor(this.options.sandbox).then(\n (executor) => {\n this.executor = executor;\n return executor;\n },\n );\n }\n return this.executorPromise;\n }\n}\n\nfunction formatResult(result: {\n result: unknown;\n error?: string;\n logs: string[];\n}): ToolCallResult {\n if (result.error) {\n const parts: string[] = [];\n if (result.logs.length > 0) {\n parts.push(`Console output:\\n${result.logs.join(\"\\n\")}`);\n }\n parts.push(`Error: ${result.error}`);\n return {\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n isError: true,\n };\n }\n\n const parts: string[] = [];\n if (result.logs.length > 0) {\n parts.push(`Console output:\\n${result.logs.join(\"\\n\")}`);\n }\n\n const resultText =\n typeof result.result === \"string\"\n ? result.result\n : JSON.stringify(result.result, null, 2);\n parts.push(resultText);\n\n return {\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n };\n}\n"],"mappings":"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","names":["IsolatedVMExecutor","QuickJSExecutor","parts"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { C as CodeMode } from './codemode-DbXujxeq.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MCP server integration for CodeMode.
|
|
6
|
+
*
|
|
7
|
+
* This module provides helpers for integrating CodeMode with
|
|
8
|
+
* `@modelcontextprotocol/sdk`. Import from `codemode/mcp`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
13
|
+
* import { CodeMode } from 'codemode';
|
|
14
|
+
* import { registerTools } from 'codemode/mcp';
|
|
15
|
+
*
|
|
16
|
+
* const codemode = new CodeMode({ spec, request: app.request.bind(app) });
|
|
17
|
+
* const server = new McpServer({ name: 'my-api', version: '1.0.0' });
|
|
18
|
+
*
|
|
19
|
+
* registerTools(codemode, server);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register CodeMode's search and execute tools on an MCP server.
|
|
25
|
+
*
|
|
26
|
+
* This uses the `@modelcontextprotocol/sdk` `McpServer.tool()` API
|
|
27
|
+
* to register both tools with proper Zod schemas.
|
|
28
|
+
*/
|
|
29
|
+
declare function registerTools(codemode: CodeMode, server: McpServerLike): void;
|
|
30
|
+
/**
|
|
31
|
+
* Minimal interface for MCP server tool registration.
|
|
32
|
+
* Compatible with `McpServer` from `@modelcontextprotocol/sdk`.
|
|
33
|
+
*/
|
|
34
|
+
interface McpServerLike {
|
|
35
|
+
tool(name: string, description: string, schema: Record<string, z.ZodType>, handler: (args: Record<string, unknown>) => Promise<unknown>): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { registerTools };
|