dtc-mcp 0.2.0 → 1.0.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.
Files changed (89) hide show
  1. package/README.md +169 -402
  2. package/data/docs.json +4338 -0
  3. package/dist/docs/loader.d.ts +40 -0
  4. package/dist/docs/loader.js +110 -0
  5. package/dist/docs/loader.js.map +1 -0
  6. package/dist/docs/search.d.ts +47 -0
  7. package/dist/docs/search.js +101 -0
  8. package/dist/docs/search.js.map +1 -0
  9. package/dist/index.js +3 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/sandbox/bridge.d.ts +2 -0
  12. package/dist/sandbox/bridge.js +101 -0
  13. package/dist/sandbox/bridge.js.map +1 -0
  14. package/dist/sandbox/node-discovery.d.ts +7 -0
  15. package/dist/sandbox/node-discovery.js +228 -0
  16. package/dist/sandbox/node-discovery.js.map +1 -0
  17. package/dist/sandbox/protocol.d.ts +76 -0
  18. package/dist/sandbox/protocol.js +20 -0
  19. package/dist/sandbox/protocol.js.map +1 -0
  20. package/dist/sandbox/proxy-template.d.ts +19 -0
  21. package/dist/sandbox/proxy-template.js +83 -0
  22. package/dist/sandbox/proxy-template.js.map +1 -0
  23. package/dist/sandbox/runner.d.ts +20 -0
  24. package/dist/sandbox/runner.js +99 -0
  25. package/dist/sandbox/runner.js.map +1 -0
  26. package/dist/sandbox/sandbox-helpers.d.ts +14 -0
  27. package/dist/sandbox/sandbox-helpers.js +98 -0
  28. package/dist/sandbox/sandbox-helpers.js.map +1 -0
  29. package/dist/sandbox/sidecar/index.d.ts +16 -0
  30. package/dist/sandbox/sidecar/index.js +346 -0
  31. package/dist/sandbox/sidecar/index.js.map +1 -0
  32. package/dist/sandbox/sidecar-runner.d.ts +32 -0
  33. package/dist/sandbox/sidecar-runner.js +325 -0
  34. package/dist/sandbox/sidecar-runner.js.map +1 -0
  35. package/dist/sandbox/timeout.d.ts +16 -0
  36. package/dist/sandbox/timeout.js +38 -0
  37. package/dist/sandbox/timeout.js.map +1 -0
  38. package/dist/sandbox/vm-runner.d.ts +35 -0
  39. package/dist/sandbox/vm-runner.js +182 -0
  40. package/dist/sandbox/vm-runner.js.map +1 -0
  41. package/dist/sdk/klaviyo/host.d.ts +43 -0
  42. package/dist/sdk/klaviyo/host.js +218 -0
  43. package/dist/sdk/klaviyo/host.js.map +1 -0
  44. package/dist/sdk/shopify/host.d.ts +23 -0
  45. package/dist/sdk/shopify/host.js +175 -0
  46. package/dist/sdk/shopify/host.js.map +1 -0
  47. package/dist/server.js +7 -7
  48. package/dist/server.js.map +1 -1
  49. package/dist/shared/errors.d.ts +0 -14
  50. package/dist/shared/errors.js +0 -73
  51. package/dist/shared/errors.js.map +1 -1
  52. package/dist/{platforms/klaviyo/tools.d.ts → tools/execute_code.d.ts} +1 -1
  53. package/dist/tools/execute_code.js +70 -0
  54. package/dist/tools/execute_code.js.map +1 -0
  55. package/dist/{platforms/shopify/tools.d.ts → tools/read_doc.d.ts} +1 -1
  56. package/dist/tools/read_doc.js +70 -0
  57. package/dist/tools/read_doc.js.map +1 -0
  58. package/dist/tools/search_docs.d.ts +2 -0
  59. package/dist/tools/search_docs.js +55 -0
  60. package/dist/tools/search_docs.js.map +1 -0
  61. package/package.json +16 -5
  62. package/dist/cross-platform/correlator.d.ts +0 -10
  63. package/dist/cross-platform/correlator.js +0 -166
  64. package/dist/cross-platform/correlator.js.map +0 -1
  65. package/dist/cross-platform/tools.d.ts +0 -2
  66. package/dist/cross-platform/tools.js +0 -30
  67. package/dist/cross-platform/tools.js.map +0 -1
  68. package/dist/platforms/klaviyo/client.d.ts +0 -91
  69. package/dist/platforms/klaviyo/client.js +0 -389
  70. package/dist/platforms/klaviyo/client.js.map +0 -1
  71. package/dist/platforms/klaviyo/tools.js +0 -363
  72. package/dist/platforms/klaviyo/tools.js.map +0 -1
  73. package/dist/platforms/klaviyo/transforms.d.ts +0 -59
  74. package/dist/platforms/klaviyo/transforms.js +0 -326
  75. package/dist/platforms/klaviyo/transforms.js.map +0 -1
  76. package/dist/platforms/shopify/client.d.ts +0 -51
  77. package/dist/platforms/shopify/client.js +0 -352
  78. package/dist/platforms/shopify/client.js.map +0 -1
  79. package/dist/platforms/shopify/tools.js +0 -368
  80. package/dist/platforms/shopify/tools.js.map +0 -1
  81. package/dist/platforms/shopify/transforms.d.ts +0 -83
  82. package/dist/platforms/shopify/transforms.js +0 -308
  83. package/dist/platforms/shopify/transforms.js.map +0 -1
  84. package/dist/shared/pagination.d.ts +0 -21
  85. package/dist/shared/pagination.js +0 -36
  86. package/dist/shared/pagination.js.map +0 -1
  87. package/dist/shared/types.d.ts +0 -318
  88. package/dist/shared/types.js +0 -3
  89. package/dist/shared/types.js.map +0 -1
@@ -0,0 +1,228 @@
1
+ import { spawn } from "node:child_process";
2
+ import { access, readdir } from "node:fs/promises";
3
+ import { constants as FS } from "node:fs";
4
+ import { homedir, platform } from "node:os";
5
+ import { join, resolve } from "node:path";
6
+ /**
7
+ * Discover a usable Node binary on the user's system, scanning common
8
+ * install locations and version managers. Returns the first one whose
9
+ * `--version` reports >= minMajor. Null if none found.
10
+ *
11
+ * Order of preference:
12
+ * 1. DTC_MCP_NODE_PATH env var (explicit override)
13
+ * 2. PATH (via `which` / `where`)
14
+ * 3. Homebrew (macOS Intel + Apple Silicon)
15
+ * 4. Standard system locations
16
+ * 5. nvm (POSIX + Windows)
17
+ * 6. Volta
18
+ * 7. fnm (POSIX + Windows)
19
+ * 8. asdf
20
+ *
21
+ * Why exhaustive: a developer who installed Node via nvm or fnm has it
22
+ * available in their shell but NOT in the GUI app's PATH (Claude Desktop
23
+ * launches without a login shell). Walking the common per-version-manager
24
+ * directories catches those.
25
+ */
26
+ const MIN_MAJOR = 20;
27
+ const MAX_CANDIDATES_TO_PROBE = 12;
28
+ export async function discoverNode() {
29
+ const candidates = await collectCandidates();
30
+ let probed = 0;
31
+ for (const candidate of candidates) {
32
+ if (probed >= MAX_CANDIDATES_TO_PROBE)
33
+ break;
34
+ if (!candidate.path)
35
+ continue;
36
+ if (!(await exists(candidate.path)))
37
+ continue;
38
+ probed++;
39
+ const version = await probeNodeVersion(candidate.path);
40
+ if (!version)
41
+ continue;
42
+ const major = parseInt(version.replace(/^v/, "").split(".")[0], 10);
43
+ if (!Number.isFinite(major) || major < MIN_MAJOR)
44
+ continue;
45
+ return {
46
+ path: candidate.path,
47
+ version,
48
+ major,
49
+ source: candidate.source,
50
+ };
51
+ }
52
+ return null;
53
+ }
54
+ async function collectCandidates() {
55
+ const out = [];
56
+ const isWindows = platform() === "win32";
57
+ const home = homedir();
58
+ // 1. Explicit override
59
+ const override = process.env.DTC_MCP_NODE_PATH;
60
+ if (override)
61
+ out.push({ path: override, source: "DTC_MCP_NODE_PATH" });
62
+ // 2. PATH lookup (via shell)
63
+ const fromPath = await whichNode();
64
+ if (fromPath)
65
+ out.push({ path: fromPath, source: "PATH" });
66
+ if (isWindows) {
67
+ // 3. Windows common locations
68
+ const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
69
+ const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
70
+ const localAppData = process.env.LOCALAPPDATA ?? join(home, "AppData", "Local");
71
+ const userProfile = process.env.USERPROFILE ?? home;
72
+ out.push({ path: join(programFiles, "nodejs", "node.exe"), source: "Program Files" }, { path: join(programFilesX86, "nodejs", "node.exe"), source: "Program Files (x86)" }, { path: join(userProfile, ".volta", "bin", "node.exe"), source: "volta" });
73
+ // nvm-windows: %LOCALAPPDATA%\nvm\v22.x.x\node.exe — scan dir
74
+ out.push(...(await scanDir(join(localAppData, "nvm"), "node.exe", "nvm-windows")));
75
+ // fnm: %LOCALAPPDATA%\fnm_multishells\<pid>_<ts>\node.exe — scan dir
76
+ out.push(...(await scanDir(join(localAppData, "fnm_multishells"), "node.exe", "fnm-windows")));
77
+ // fnm node-versions: %LOCALAPPDATA%\fnm\node-versions\v22.x.x\installation\node.exe
78
+ out.push(...(await scanFnmNodeVersions(join(localAppData, "fnm", "node-versions"), true)));
79
+ }
80
+ else {
81
+ // 3. POSIX common locations
82
+ out.push({ path: "/opt/homebrew/bin/node", source: "homebrew-arm64" }, { path: "/usr/local/bin/node", source: "homebrew-x64" }, { path: "/usr/bin/node", source: "system" });
83
+ // 4. nvm (~/.nvm/versions/node/v22.x.x/bin/node) — pick latest
84
+ out.push(...(await scanNvm(join(home, ".nvm", "versions", "node"))));
85
+ // 5. Volta
86
+ out.push({ path: join(home, ".volta", "bin", "node"), source: "volta" });
87
+ // 6. fnm (~/.fnm/node-versions/v22.x.x/installation/bin/node)
88
+ out.push(...(await scanFnmNodeVersions(join(home, ".fnm", "node-versions"), false)));
89
+ // fnm alternate: ~/.local/share/fnm/node-versions/v22.x.x/installation/bin/node
90
+ out.push(...(await scanFnmNodeVersions(join(home, ".local", "share", "fnm", "node-versions"), false)));
91
+ // 7. asdf (~/.asdf/installs/nodejs/22.x.x/bin/node)
92
+ out.push(...(await scanAsdf(join(home, ".asdf", "installs", "nodejs"))));
93
+ }
94
+ return dedupe(out);
95
+ }
96
+ function dedupe(items) {
97
+ const seen = new Set();
98
+ const out = [];
99
+ for (const item of items) {
100
+ const key = resolve(item.path);
101
+ if (seen.has(key))
102
+ continue;
103
+ seen.add(key);
104
+ out.push({ ...item, path: key });
105
+ }
106
+ return out;
107
+ }
108
+ async function exists(p) {
109
+ try {
110
+ await access(p, FS.X_OK);
111
+ return true;
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ }
117
+ async function whichNode() {
118
+ const cmd = platform() === "win32" ? "where" : "which";
119
+ return new Promise((resolveP) => {
120
+ const proc = spawn(cmd, ["node"], { stdio: ["ignore", "pipe", "ignore"] });
121
+ let out = "";
122
+ proc.stdout.on("data", (d) => {
123
+ out += d.toString("utf8");
124
+ });
125
+ proc.on("close", () => {
126
+ // `where` on Windows can return multiple lines; take the first.
127
+ const first = out.split(/\r?\n/).map((s) => s.trim()).find(Boolean);
128
+ resolveP(first || null);
129
+ });
130
+ proc.on("error", () => resolveP(null));
131
+ });
132
+ }
133
+ async function probeNodeVersion(nodePath) {
134
+ return new Promise((resolveP) => {
135
+ const proc = spawn(nodePath, ["--version"], {
136
+ stdio: ["ignore", "pipe", "ignore"],
137
+ timeout: 3_000,
138
+ });
139
+ let out = "";
140
+ proc.stdout.on("data", (d) => {
141
+ out += d.toString("utf8");
142
+ });
143
+ proc.on("close", (code) => {
144
+ if (code !== 0)
145
+ return resolveP(null);
146
+ const version = out.trim();
147
+ resolveP(version.startsWith("v") ? version : null);
148
+ });
149
+ proc.on("error", () => resolveP(null));
150
+ });
151
+ }
152
+ async function scanDir(dir, binaryName, source) {
153
+ try {
154
+ const entries = await readdir(dir);
155
+ // Largest version-looking dir wins, so a user with multiple installed
156
+ // versions tries the most recent first.
157
+ const sorted = entries
158
+ .filter((e) => /v?\d/.test(e))
159
+ .sort((a, b) => semverDescend(a, b));
160
+ return sorted.slice(0, 5).map((entry) => ({
161
+ path: join(dir, entry, binaryName),
162
+ source: `${source}:${entry}`,
163
+ }));
164
+ }
165
+ catch {
166
+ return [];
167
+ }
168
+ }
169
+ async function scanNvm(nvmDir) {
170
+ try {
171
+ const entries = await readdir(nvmDir);
172
+ const sorted = entries
173
+ .filter((e) => /^v?\d/.test(e))
174
+ .sort((a, b) => semverDescend(a, b));
175
+ return sorted.slice(0, 5).map((entry) => ({
176
+ path: join(nvmDir, entry, "bin", "node"),
177
+ source: `nvm:${entry}`,
178
+ }));
179
+ }
180
+ catch {
181
+ return [];
182
+ }
183
+ }
184
+ async function scanFnmNodeVersions(versionsDir, isWindows) {
185
+ try {
186
+ const entries = await readdir(versionsDir);
187
+ const sorted = entries
188
+ .filter((e) => /^v?\d/.test(e))
189
+ .sort((a, b) => semverDescend(a, b));
190
+ const segments = isWindows
191
+ ? ["installation", "node.exe"]
192
+ : ["installation", "bin", "node"];
193
+ return sorted.slice(0, 5).map((entry) => ({
194
+ path: join(versionsDir, entry, ...segments),
195
+ source: `fnm:${entry}`,
196
+ }));
197
+ }
198
+ catch {
199
+ return [];
200
+ }
201
+ }
202
+ async function scanAsdf(asdfDir) {
203
+ try {
204
+ const entries = await readdir(asdfDir);
205
+ const sorted = entries
206
+ .filter((e) => /^\d/.test(e))
207
+ .sort((a, b) => semverDescend(a, b));
208
+ return sorted.slice(0, 5).map((entry) => ({
209
+ path: join(asdfDir, entry, "bin", "node"),
210
+ source: `asdf:${entry}`,
211
+ }));
212
+ }
213
+ catch {
214
+ return [];
215
+ }
216
+ }
217
+ function semverDescend(a, b) {
218
+ const ax = a.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
219
+ const bx = b.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
220
+ for (let i = 0; i < 3; i++) {
221
+ const av = ax[i] ?? 0;
222
+ const bv = bx[i] ?? 0;
223
+ if (av !== bv)
224
+ return bv - av;
225
+ }
226
+ return 0;
227
+ }
228
+ //# sourceMappingURL=node-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-discovery.js","sourceRoot":"","sources":["../../src/sandbox/node-discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Newline-delimited JSON-RPC protocol shared between the main MCP server
3
+ * and the spawned sidecar Node process. Each message is one JSON object
4
+ * per line on stdin/stdout.
5
+ *
6
+ * Flow:
7
+ * 1. Sidecar starts, tries to `require('isolated-vm')`.
8
+ * - On failure → sends FatalMessage and exits 1.
9
+ * - On success → sends ReadyMessage with isolated-vm version.
10
+ * 2. Main sends InitMessage with the method-path registry.
11
+ * 3. Main sends ExecuteRequestMessage to run user code.
12
+ * 4. During execution, the sandbox's bridge calls round-trip:
13
+ * sidecar → HostCallRequestMessage → main → HostCallResponseMessage → sidecar.
14
+ * 5. Sidecar sends ExecuteResponseMessage when done.
15
+ * 6. On shutdown, main sends ShutdownMessage (or just closes stdin).
16
+ */
17
+ export interface ReadyMessage {
18
+ type: "ready";
19
+ /** isolated-vm package version, for diagnostics. */
20
+ ivmVersion: string;
21
+ }
22
+ export interface FatalMessage {
23
+ type: "fatal";
24
+ reason: string;
25
+ }
26
+ export interface LogMessage {
27
+ type: "log";
28
+ level: "debug" | "info" | "warn" | "error";
29
+ message: string;
30
+ }
31
+ export interface InitMessage {
32
+ type: "init";
33
+ /** Method-path registry to mirror as the in-isolate SDK surface. */
34
+ methodPaths: string[];
35
+ }
36
+ export interface ExecuteRequestMessage {
37
+ type: "execute";
38
+ /** Correlation ID set by the main process. */
39
+ id: string;
40
+ code: string;
41
+ timeoutMs: number;
42
+ }
43
+ export interface HostCallRequestMessage {
44
+ type: "host-call";
45
+ /** ID of the in-flight execute that owns this call. */
46
+ execId: string;
47
+ /** Unique per host call within a single execute. */
48
+ callId: string;
49
+ path: string;
50
+ argsJson: string;
51
+ }
52
+ export interface HostCallResponseMessage {
53
+ type: "host-result";
54
+ execId: string;
55
+ callId: string;
56
+ /** JSON-encoded result, or `__ERROR__<msg>` sentinel for thrown errors. */
57
+ resultJson: string;
58
+ }
59
+ export interface ExecuteResponseMessage {
60
+ type: "execute-result";
61
+ id: string;
62
+ ok: boolean;
63
+ /** JSON-encoded user return value, only meaningful when ok=true. */
64
+ resultJson?: string;
65
+ stdout: string[];
66
+ error?: string;
67
+ durationMs: number;
68
+ /** True when the underlying isolate was recreated since the last call. */
69
+ sessionReset?: boolean;
70
+ }
71
+ export interface ShutdownMessage {
72
+ type: "shutdown";
73
+ }
74
+ export type MainToSidecar = InitMessage | ExecuteRequestMessage | HostCallResponseMessage | ShutdownMessage;
75
+ export type SidecarToMain = ReadyMessage | FatalMessage | LogMessage | ExecuteResponseMessage | HostCallRequestMessage;
76
+ export declare function encodeLine(msg: MainToSidecar | SidecarToMain): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Newline-delimited JSON-RPC protocol shared between the main MCP server
3
+ * and the spawned sidecar Node process. Each message is one JSON object
4
+ * per line on stdin/stdout.
5
+ *
6
+ * Flow:
7
+ * 1. Sidecar starts, tries to `require('isolated-vm')`.
8
+ * - On failure → sends FatalMessage and exits 1.
9
+ * - On success → sends ReadyMessage with isolated-vm version.
10
+ * 2. Main sends InitMessage with the method-path registry.
11
+ * 3. Main sends ExecuteRequestMessage to run user code.
12
+ * 4. During execution, the sandbox's bridge calls round-trip:
13
+ * sidecar → HostCallRequestMessage → main → HostCallResponseMessage → sidecar.
14
+ * 5. Sidecar sends ExecuteResponseMessage when done.
15
+ * 6. On shutdown, main sends ShutdownMessage (or just closes stdin).
16
+ */
17
+ export function encodeLine(msg) {
18
+ return JSON.stringify(msg) + "\n";
19
+ }
20
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/sandbox/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAiFH,MAAM,UAAU,UAAU,CAAC,GAAkC;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;AACpC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Build the JavaScript source that runs INSIDE the `node:vm` context to
3
+ * reconstruct the `klaviyo` and `shopify` namespace trees as thin async stubs.
4
+ *
5
+ * Each leaf method serializes its args to JSON, calls the host bridge function
6
+ * (`__host_invoke`) injected into the context, and awaits the host's promise.
7
+ * The host re-runs the request with real rate limiting, auth, and caching.
8
+ *
9
+ * JSON-encoded args are used (rather than passing the raw array) because:
10
+ * - `node:vm` shares the V8 heap with the host, so objects could leak the
11
+ * prototype chain across the boundary if passed directly. JSON normalizes
12
+ * everything to plain data.
13
+ * - Behavior matches what we'd do across a worker boundary if we later
14
+ * swap the runtime for a stricter sandbox.
15
+ *
16
+ * The bootstrap also installs the output-discipline helpers (`pick`,
17
+ * `summarize`, `topN`) — see ./sandbox-helpers.ts.
18
+ */
19
+ export declare function buildProxyScript(methodPaths: string[]): string;
@@ -0,0 +1,83 @@
1
+ import { SANDBOX_HELPERS_SOURCE } from "./sandbox-helpers.js";
2
+ /**
3
+ * Build the JavaScript source that runs INSIDE the `node:vm` context to
4
+ * reconstruct the `klaviyo` and `shopify` namespace trees as thin async stubs.
5
+ *
6
+ * Each leaf method serializes its args to JSON, calls the host bridge function
7
+ * (`__host_invoke`) injected into the context, and awaits the host's promise.
8
+ * The host re-runs the request with real rate limiting, auth, and caching.
9
+ *
10
+ * JSON-encoded args are used (rather than passing the raw array) because:
11
+ * - `node:vm` shares the V8 heap with the host, so objects could leak the
12
+ * prototype chain across the boundary if passed directly. JSON normalizes
13
+ * everything to plain data.
14
+ * - Behavior matches what we'd do across a worker boundary if we later
15
+ * swap the runtime for a stricter sandbox.
16
+ *
17
+ * The bootstrap also installs the output-discipline helpers (`pick`,
18
+ * `summarize`, `topN`) — see ./sandbox-helpers.ts.
19
+ */
20
+ export function buildProxyScript(methodPaths) {
21
+ const tree = {};
22
+ for (const path of methodPaths) {
23
+ const segments = path.split(".");
24
+ let cursor = tree;
25
+ for (let i = 0; i < segments.length - 1; i++) {
26
+ const key = segments[i];
27
+ if (typeof cursor[key] !== "object")
28
+ cursor[key] = {};
29
+ cursor = cursor[key];
30
+ }
31
+ cursor[segments[segments.length - 1]] = path;
32
+ }
33
+ function emit(node) {
34
+ const parts = [];
35
+ for (const [key, value] of Object.entries(node)) {
36
+ if (typeof value === "string") {
37
+ parts.push(`${JSON.stringify(key)}: (...args) => __invoke(${JSON.stringify(value)}, args)`);
38
+ }
39
+ else {
40
+ parts.push(`${JSON.stringify(key)}: ${emit(value)}`);
41
+ }
42
+ }
43
+ return `{${parts.join(",")}}`;
44
+ }
45
+ return `
46
+ (function () {
47
+ const __invoke = async function (path, args) {
48
+ const argsJson = JSON.stringify(args);
49
+ const resultJson = await __host_invoke(path, argsJson);
50
+ if (typeof resultJson === 'string' && resultJson.startsWith('__ERROR__')) {
51
+ throw new Error(resultJson.slice(9));
52
+ }
53
+ return resultJson ? JSON.parse(resultJson) : undefined;
54
+ };
55
+
56
+ const __stdout = [];
57
+ globalThis.console = {
58
+ log: (...args) => {
59
+ __stdout.push(args.map((a) => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
60
+ },
61
+ error: (...args) => {
62
+ __stdout.push('[err] ' + args.map((a) => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
63
+ },
64
+ warn: (...args) => {
65
+ __stdout.push('[warn] ' + args.map((a) => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
66
+ },
67
+ info: (...args) => {
68
+ __stdout.push(args.map((a) => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
69
+ },
70
+ };
71
+ globalThis.__getStdout = () => __stdout;
72
+ globalThis.__resetStdout = () => { __stdout.length = 0; };
73
+
74
+ const __sdk = ${emit(tree)};
75
+ for (const k of Object.keys(__sdk)) {
76
+ globalThis[k] = __sdk[k];
77
+ }
78
+ })();
79
+
80
+ ${SANDBOX_HELPERS_SOURCE}
81
+ `;
82
+ }
83
+ //# sourceMappingURL=proxy-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-template.js","sourceRoot":"","sources":["../../src/sandbox/proxy-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAqB;IAEpD,MAAM,IAAI,GAAS,EAAE,CAAC;IACtB,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;YACtD,MAAM,GAAG,MAAM,CAAC,GAAG,CAAS,CAAC;QAC/B,CAAC;QACD,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC/C,CAAC;IAED,SAAS,IAAI,CAAC,IAAU;QACtB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,KAAK,CAAC,IAAI,CACR,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAChF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QACD,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAChC,CAAC;IAED,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA6BS,IAAI,CAAC,IAAI,CAAC;;;;;;EAM1B,sBAAsB;CACvB,CAAC;AACF,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type RunResult } from "./vm-runner.js";
2
+ /**
3
+ * Public sandbox runner. Routes between the sidecar (isolated-vm) and the
4
+ * in-process vm-runner (node:vm) based on availability.
5
+ *
6
+ * Selection logic:
7
+ * - DTC_MCP_SANDBOX=vm → always use node:vm
8
+ * - DTC_MCP_SANDBOX=sidecar → require sidecar; error if unavailable
9
+ * - DTC_MCP_SANDBOX=auto → default: prefer sidecar, fall back to vm
10
+ *
11
+ * The active mode is logged once at first use so users can see in the debug
12
+ * log which sandbox is actually executing their code.
13
+ */
14
+ export type { RunResult } from "./vm-runner.js";
15
+ export type SandboxMode = "sidecar" | "vm";
16
+ export declare function runSandbox(code: string, options: {
17
+ timeoutMs: number;
18
+ }): Promise<RunResult & {
19
+ sandbox: SandboxMode;
20
+ }>;
@@ -0,0 +1,99 @@
1
+ import { runSandboxVm } from "./vm-runner.js";
2
+ import { getSidecarStatus, runSandboxSidecar } from "./sidecar-runner.js";
3
+ import { log } from "../config.js";
4
+ let activeMode = null;
5
+ async function pickMode() {
6
+ const override = (process.env.DTC_MCP_SANDBOX ?? "auto").toLowerCase();
7
+ if (override === "vm")
8
+ return "vm";
9
+ const status = await getSidecarStatus();
10
+ if (override === "sidecar") {
11
+ if (!status.available) {
12
+ // Caller asked for sidecar specifically; throwing here would break
13
+ // execute_code. We log the failure and fall back so the tool stays
14
+ // usable — but the log makes it obvious.
15
+ log("error", "DTC_MCP_SANDBOX=sidecar but sidecar unavailable", {
16
+ reason: status.reason,
17
+ });
18
+ }
19
+ return status.available ? "sidecar" : "vm";
20
+ }
21
+ // auto
22
+ return status.available ? "sidecar" : "vm";
23
+ }
24
+ /**
25
+ * Cap user code's return payload so an over-eager LLM that returns a raw
26
+ * 380 KB JSON blob doesn't burn through the conversation's context window.
27
+ * Stainless's own benchmark caps factuality at 53% across all code-mode
28
+ * MCPs because "models tend toward verbose responses beyond what's strictly
29
+ * necessary." This is the host-side enforcement; the in-sandbox `pick` /
30
+ * `summarize` / `topN` helpers are the LLM-friendly remediation.
31
+ */
32
+ const MAX_RESPONSE_BYTES = (() => {
33
+ const raw = process.env.DTC_MCP_MAX_RESPONSE_KB;
34
+ if (raw) {
35
+ const kb = parseInt(raw, 10);
36
+ if (Number.isFinite(kb) && kb > 0)
37
+ return kb * 1024;
38
+ }
39
+ return 100 * 1024;
40
+ })();
41
+ function applyResponseCap(result) {
42
+ if (!result.ok || result.result === undefined || result.result === null) {
43
+ return result;
44
+ }
45
+ let serialized;
46
+ try {
47
+ serialized = JSON.stringify(result.result);
48
+ }
49
+ catch {
50
+ // If the value isn't JSON-serializable, leave it for the upstream
51
+ // JSON.stringify (in execute_code.ts) to surface the error.
52
+ return result;
53
+ }
54
+ const bytes = Buffer.byteLength(serialized, "utf8");
55
+ if (bytes <= MAX_RESPONSE_BYTES)
56
+ return result;
57
+ // Capture a leading slice as a preview so the LLM can still see the shape.
58
+ // Truncate at ~90% of the cap to leave room for the truncation envelope.
59
+ const previewLimit = Math.floor(MAX_RESPONSE_BYTES * 0.9);
60
+ const preview = serialized.slice(0, previewLimit);
61
+ log("warn", "Response capped", {
62
+ originalBytes: bytes,
63
+ cap: MAX_RESPONSE_BYTES,
64
+ });
65
+ return {
66
+ ...result,
67
+ result: {
68
+ truncated: true,
69
+ originalBytes: bytes,
70
+ cap: MAX_RESPONSE_BYTES,
71
+ preview,
72
+ instructions: "Output exceeded the response cap. Use `pick(value, schema)` to project specific fields, `topN(arr, n, key)` for top items, or `summarize(arr, { by, topN })` for aggregate stats. See search_docs('output discipline') for examples.",
73
+ },
74
+ };
75
+ }
76
+ export async function runSandbox(code, options) {
77
+ if (!activeMode) {
78
+ activeMode = await pickMode();
79
+ log("info", "Sandbox mode selected", { mode: activeMode });
80
+ }
81
+ const result = activeMode === "sidecar"
82
+ ? await runSandboxSidecar(code, options)
83
+ : await runSandboxVm(code, options);
84
+ // If the sidecar died mid-session (e.g., child crashed), gracefully drop
85
+ // to vm-runner for THIS call and every future call.
86
+ if (activeMode === "sidecar" &&
87
+ !result.ok &&
88
+ typeof result.error === "string" &&
89
+ result.error.startsWith("Sidecar unavailable")) {
90
+ log("warn", "Sidecar dropped; falling back to vm-runner", {
91
+ error: result.error,
92
+ });
93
+ activeMode = "vm";
94
+ const fallback = await runSandboxVm(code, options);
95
+ return { ...applyResponseCap(fallback), sandbox: "vm" };
96
+ }
97
+ return { ...applyResponseCap(result), sandbox: activeMode };
98
+ }
99
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/sandbox/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * JS source for the output-discipline helpers that get injected into the
3
+ * sandbox global scope. Same code runs in both runners (node:vm and
4
+ * isolated-vm) — keeping this as a single string source-of-truth means the
5
+ * behavior the LLM observes is identical regardless of which runner is
6
+ * actually executing.
7
+ *
8
+ * Why these helpers exist: Stainless's published benchmark caps factuality
9
+ * at 53% across all code-mode MCPs, with the model "tending toward verbose
10
+ * responses beyond what's strictly necessary." These helpers give the LLM
11
+ * a vocabulary to project / aggregate / trim raw API responses before
12
+ * returning, and the docs explicitly point at them.
13
+ */
14
+ export declare const SANDBOX_HELPERS_SOURCE: string;