@toolsdk.ai/registry 1.0.113 → 1.0.114

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.
@@ -1,345 +1,138 @@
1
- import { Sandbox } from "@e2b/code-interpreter";
2
- import { getPackageConfigByKey } from "../helper";
1
+ import { Daytona, Image } from "@daytonaio/sdk";
2
+ import { extractLastOuterJSON, getPackageConfigByKey } from "../helper";
3
3
  export class MCPSandboxClient {
4
- constructor(apiKey) {
4
+ constructor(runtime = "node", provider = "DAYTONA") {
5
5
  this.sandbox = null;
6
6
  this.initializing = null;
7
- this.toolCache = new Map();
8
- this.E2B_SANDBOX_TIMEOUT_MS = 300000;
9
- this.TOOL_CACHE_TTL = 30 * 60 * 1000;
10
- this.TOOL_EXECUTION_TIMEOUT = 300000;
11
- this.lastTouchTime = null;
12
- this.THROTTLE_DELAY_MS = 10 * 1000;
13
- // Lifecycle and Auto-Recovery
14
- this.createdAt = null;
15
- this.lastUsedAt = null;
16
- this.ttlTimer = null;
17
- this.autoCloseOnIdleTimer = null;
18
- this.idleCloseMs = null;
19
- this.apiKey = apiKey || process.env.E2B_API_KEY || "e2b-api-key-placeholder";
7
+ this.runtime = "node";
8
+ this.apiKey = process.env.DAYTONA_API_KEY || "daytona-api-key-placeholder";
9
+ this.runtime = runtime;
10
+ this.provider = provider;
20
11
  }
21
12
  // Safe initialize: ensures concurrent calls don't create duplicate sandboxes
22
13
  async initialize() {
23
14
  if (this.sandbox) {
24
- // this.touch();
25
- // console.log("[MCPSandboxClient] Sandbox already initialized");
26
15
  return;
27
16
  }
28
17
  if (this.initializing) {
29
18
  // Wait for existing initialization to complete
30
19
  await this.initializing;
31
- // this.touch();
32
20
  return;
33
21
  }
34
- const initLabel = `[MCPSandboxClient] Sandbox initialization ${Date.now()}-${(Math.random() * 1000000) | 0}`;
35
- console.time(initLabel);
36
22
  this.initializing = (async () => {
37
23
  try {
38
- this.sandbox = await Sandbox.create(`mcp-sandbox-01`, {
24
+ const daytonaConfig = {
39
25
  apiKey: this.apiKey,
40
- timeoutMs: this.E2B_SANDBOX_TIMEOUT_MS,
26
+ };
27
+ if (this.provider === "SANDOCK") {
28
+ daytonaConfig.apiUrl = process.env.SANDOCK_DAYTONA_API_URL || process.env.DAYTONA_API_URL;
29
+ if (!daytonaConfig.apiUrl) {
30
+ console.warn("[MCPSandboxClient] SANDOCK provider selected but SANDOCK_DAYTONA_API_URL is not set. Falling back to default Daytona API URL.");
31
+ }
32
+ }
33
+ const daytona = new Daytona(daytonaConfig);
34
+ // Create image with required dependencies
35
+ const declarativeImage = Image.base("node:20")
36
+ .runCommands("npm install -g pnpm", "mkdir -p /workspace", "cd /workspace && npm init -y", "cd /workspace && pnpm add @modelcontextprotocol/sdk")
37
+ .workdir("/workspace");
38
+ // const pnpmStoreVolume = await daytona.volume.get("pnpm-store-shared", true);
39
+ this.sandbox = await daytona.create({
40
+ language: "javascript",
41
+ image: declarativeImage,
42
+ // volumes: [
43
+ // {
44
+ // volumeId: pnpmStoreVolume.id,
45
+ // mountPath: "/pnpm-store",
46
+ // },
47
+ // ],
41
48
  });
42
- this.createdAt = Date.now();
43
- this.touch();
44
- // After 1-hour forced session termination
45
- this.setupTTLTimer();
46
- console.log("[MCPSandboxClient] Sandbox created successfully");
49
+ console.log("[MCPSandboxClient] Daytona Sandbox created successfully");
47
50
  }
48
51
  finally {
49
52
  this.initializing = null;
50
53
  }
51
54
  })();
52
55
  await this.initializing;
53
- console.timeEnd(initLabel);
54
- }
55
- setupTTLTimer() {
56
- // Clean up existing timer
57
- if (this.ttlTimer) {
58
- clearTimeout(this.ttlTimer);
59
- this.ttlTimer = null;
60
- }
61
- // E2B supports maximum 1-hour sessions, force close at 59m 30s for safety
62
- const TTL_MS = 60 * 60 * 1000;
63
- const safetyMs = 30 * 1000;
64
- const remaining = Math.max(0, TTL_MS - (Date.now() - (this.createdAt || Date.now())));
65
- this.ttlTimer = setTimeout(async () => {
66
- console.warn("[MCPSandboxClient] Sandbox TTL reached, closing sandbox to avoid exceeding 1 hour.");
67
- try {
68
- await this.kill();
69
- }
70
- catch (err) {
71
- console.error("[MCPSandboxClient] Error while killing sandbox after TTL:", err);
72
- }
73
- }, remaining - safetyMs);
74
- }
75
- /**
76
- * Update sandbox last used time
77
- */
78
- touch() {
79
- const now = Date.now();
80
- // If this is the first call, or more than 10 seconds have passed since the last call
81
- if (!this.lastTouchTime || now - this.lastTouchTime >= this.THROTTLE_DELAY_MS) {
82
- this.lastTouchTime = now;
83
- this.performTouch();
84
- }
85
- }
86
- /**
87
- * Actually perform the touch operation
88
- */
89
- async performTouch() {
90
- this.lastUsedAt = Date.now();
91
- console.log(`[MCPSandboxClient] Sandbox touched at ${this.lastUsedAt}`);
92
- // Reset E2B sandbox timeout
93
- if (this.sandbox) {
94
- console.log("[MCPSandboxClient] Resetting E2B sandbox timeout");
95
- // const info = await this.sandbox.getInfo();
96
- // console.log(`[MCPSandboxClient] E2B sandbox info: ${JSON.stringify(info, null, 2)}`);
97
- this.sandbox.setTimeout(this.E2B_SANDBOX_TIMEOUT_MS).catch((err) => {
98
- console.error("[MCPSandboxClient] Failed to reset E2B sandbox timeout:", err);
99
- });
100
- }
101
- // Refresh timer if waiting for idle close
102
- if (this.autoCloseOnIdleTimer && this.idleCloseMs) {
103
- clearTimeout(this.autoCloseOnIdleTimer);
104
- // Reschedule idle close (refresh timer)
105
- this.autoCloseOnIdleTimer = setTimeout(async () => {
106
- console.log("[MCPSandboxClient] Idle TTL reached, closing sandbox due to inactivity.");
107
- try {
108
- await this.kill();
109
- }
110
- catch (err) {
111
- console.error("[MCPSandboxClient] Error while killing sandbox on idle:", err);
112
- }
113
- }, this.idleCloseMs);
114
- }
115
- }
116
- // Schedule idle auto-close (called by PackageSO when refCount reaches 0)
117
- scheduleIdleClose(idleMs) {
118
- // Save idle close time for refresh
119
- this.idleCloseMs = idleMs;
120
- // Clear existing idle timer
121
- if (this.autoCloseOnIdleTimer) {
122
- clearTimeout(this.autoCloseOnIdleTimer);
123
- }
124
- // Set new idle timer
125
- this.autoCloseOnIdleTimer = setTimeout(async () => {
126
- console.log("[MCPSandboxClient] Idle TTL reached, closing sandbox due to inactivity.");
127
- try {
128
- await this.kill();
129
- }
130
- catch (err) {
131
- console.error("[MCPSandboxClient] Error while killing sandbox on idle:", err);
132
- }
133
- }, idleMs);
134
56
  }
135
- // Force close and cleanup timers & cache
57
+ // Force close and cleanup
136
58
  async kill() {
137
- const killLabel = `[MCPSandboxClient] Sandbox closing ${Date.now()}-${(Math.random() * 1000000) | 0}`;
138
- console.time(killLabel);
139
59
  try {
140
- if (this.ttlTimer) {
141
- clearTimeout(this.ttlTimer);
142
- this.ttlTimer = null;
143
- }
144
- if (this.autoCloseOnIdleTimer) {
145
- clearTimeout(this.autoCloseOnIdleTimer);
146
- this.autoCloseOnIdleTimer = null;
147
- }
148
60
  if (this.sandbox) {
149
- try {
150
- await this.sandbox.kill();
151
- }
152
- catch (err) {
153
- // sandbox.kill may throw, log error but continue cleaning local state
154
- console.error("[MCPSandboxClient] Error during sandbox.kill():", err);
155
- }
156
- this.sandbox = null;
157
- }
158
- else {
159
- console.log("[MCPSandboxClient] No sandbox to close");
61
+ await this.sandbox.delete();
160
62
  }
161
63
  }
64
+ catch (err) {
65
+ console.log(err.message);
66
+ }
162
67
  finally {
163
- // clear cache and reset timestamps
164
- this.toolCache.clear();
165
- this.createdAt = null;
166
- this.lastUsedAt = null;
167
- console.timeEnd(killLabel);
68
+ this.sandbox = null;
69
+ // clear cache
70
+ // this.toolCache.clear();
168
71
  }
169
72
  }
170
- // Clear cache for specific package
171
- clearPackageCache(packageKey) {
172
- this.toolCache.delete(packageKey);
173
- }
174
- // Clear all tool caches
175
- clearAllCache() {
176
- this.toolCache.clear();
177
- }
178
73
  async listTools(packageKey) {
179
- var _a;
180
- const cached = this.toolCache.get(packageKey);
181
- if (cached) {
182
- const now = Date.now();
183
- if (now - cached.timestamp < this.TOOL_CACHE_TTL) {
184
- console.log(`[MCPSandboxClient] Returning cached tools for package: ${packageKey}`);
185
- // Refresh cache expiration time
186
- this.toolCache.set(packageKey, {
187
- tools: cached.tools,
188
- timestamp: Date.now(),
189
- });
190
- this.touch();
191
- return cached.tools;
192
- }
193
- else {
194
- // Cache expired, remove it
195
- this.toolCache.delete(packageKey);
196
- }
197
- }
198
74
  if (!this.sandbox) {
199
75
  throw new Error("Sandbox not initialized. Call initialize() first.");
200
76
  }
201
77
  const mcpServerConfig = await getPackageConfigByKey(packageKey);
202
78
  const testCode = this.generateMCPTestCode(mcpServerConfig, "listTools");
203
- const testResult = await this.runCodeWithTimeout(testCode, { language: "javascript" });
204
- if (testResult.error) {
205
- console.error("[MCPSandboxClient] Failed to list tools:", testResult.error);
206
- throw new Error(`Failed to list tools: ${testResult.error}`);
79
+ try {
80
+ const response = await this.sandbox.process.codeRun(testCode);
81
+ if (response.exitCode !== 0) {
82
+ throw new Error(`Failed to list tools: ${response.result}`);
83
+ }
84
+ const parsedResultStr = extractLastOuterJSON(response.result);
85
+ const result = JSON.parse(parsedResultStr);
86
+ return result.tools;
87
+ }
88
+ catch (err) {
89
+ console.error("[MCPSandboxClient] Error listing tools:", err);
90
+ throw err;
207
91
  }
208
- const stdout = ((_a = testResult.logs) === null || _a === void 0 ? void 0 : _a.stdout) || [];
209
- const last = stdout[stdout.length - 1] || "{}";
210
- const result = JSON.parse(last);
211
- this.toolCache.set(packageKey, {
212
- tools: result.tools,
213
- timestamp: Date.now(),
214
- });
215
- this.touch();
216
- return result.tools;
217
92
  }
218
93
  async executeTool(packageKey, toolName, argumentsObj, envs) {
219
94
  if (!this.sandbox) {
220
95
  throw new Error("Sandbox not initialized. Call initialize() first.");
221
96
  }
222
- const execLabel = `[MCPSandboxClient] Execute tool: ${toolName} from package: ${packageKey} ${Date.now()}-${(Math.random() * 1000000) | 0}`;
223
- console.time(execLabel);
97
+ const mcpServerConfig = await getPackageConfigByKey(packageKey);
98
+ const testCode = this.generateMCPTestCode(mcpServerConfig, "executeTool", toolName, argumentsObj, envs);
224
99
  try {
225
- const mcpServerConfig = await getPackageConfigByKey(packageKey);
226
- const testCode = this.generateMCPTestCode(mcpServerConfig, "executeTool", toolName, argumentsObj, envs);
227
- const testResult = await this.runCodeWithTimeout(testCode, { language: "javascript" });
228
- if (testResult.error) {
229
- console.error("[MCPSandboxClient] Failed to execute tool:", testResult.error);
230
- throw new Error(`Failed to execute tool: ${testResult.error}`);
231
- }
232
- // Handle stderr output, log if there are error messages
233
- if (testResult.logs.stderr && testResult.logs.stderr.length > 0) {
234
- const stderrOutput = testResult.logs.stderr.join("\n");
235
- console.error("[MCPSandboxClient] Tool execution stderr output:", stderrOutput);
100
+ const response = await this.sandbox.process.codeRun(testCode);
101
+ if (response.exitCode !== 0) {
102
+ throw new Error(`Failed to execute tool: ${response.result}`);
236
103
  }
237
- const result = JSON.parse(testResult.logs.stdout[testResult.logs.stdout.length - 1] || "{}");
104
+ const parsedResultStr = extractLastOuterJSON(response.result);
105
+ const result = JSON.parse(parsedResultStr);
238
106
  if (result.isError) {
239
107
  console.error("[MCPSandboxClient] Tool execution error:", result.errorMessage);
240
108
  throw new Error(result.errorMessage);
241
109
  }
242
- this.touch();
243
110
  return result;
244
111
  }
245
112
  catch (err) {
246
- if (err instanceof Error &&
247
- (err.message.includes("sandbox was not found") || err.message.includes("terminated"))) {
248
- console.warn("[MCPSandboxClient] Sandbox not found, cleaning up state and reinitializing");
249
- await this.kill();
250
- throw new Error("Sandbox was not found. Please retry the operation.");
251
- }
252
113
  console.error("[MCPSandboxClient] Error executing tool:", err);
253
114
  throw err;
254
115
  }
255
- finally {
256
- console.timeEnd(execLabel);
257
- }
258
- }
259
- // Add timeout protection to sandbox.runCode, kill sandbox on timeout
260
- async runCodeWithTimeout(code, options) {
261
- if (!this.sandbox)
262
- throw new Error("Sandbox not initialized.");
263
- const execPromise = this.sandbox.runCode(code, options);
264
- const timeoutMs = this.TOOL_EXECUTION_TIMEOUT;
265
- let timeoutHandle = null;
266
- const timeoutPromise = new Promise((_, reject) => {
267
- timeoutHandle = setTimeout(async () => {
268
- const msg = `[MCPSandboxClient] runCode timeout after ${timeoutMs}ms`;
269
- console.error(msg);
270
- // After timeout, try to force kill sandbox to recycle resources and avoid subsequent calls using this stuck instance
271
- try {
272
- await this.kill();
273
- }
274
- catch (err) {
275
- console.error("[MCPSandboxClient] Error killing sandbox after run timeout:", err);
276
- }
277
- reject(new Error(msg));
278
- }, timeoutMs);
279
- });
280
- const label = `[MCPSandboxClient] Tool execution time ${Date.now()}-${(Math.random() * 1000000) | 0}`;
281
- try {
282
- console.time(label);
283
- const result = (await Promise.race([execPromise, timeoutPromise]));
284
- return result;
285
- }
286
- catch (err) {
287
- console.error("[MCPSandboxClient] runCodeWithTimeout error:", err);
288
- throw err;
289
- }
290
- finally {
291
- if (timeoutHandle)
292
- clearTimeout(timeoutHandle);
293
- console.timeEnd(label);
294
- }
295
116
  }
296
117
  generateMCPTestCode(mcpServerConfig, operation, toolName, argumentsObj, envs) {
297
118
  const commonCode = `
298
119
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
299
120
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
300
- import fs from "node:fs";
301
-
302
- function getPackageJSON(packageName) {
303
- const packageJSONFilePath = \`/home/node_modules/\${packageName}/package.json\`;
304
-
305
- if (!fs.existsSync(packageJSONFilePath)) {
306
- throw new Error(\`Package '\${packageName}' not found in node_modules.\`);
307
- }
308
-
309
- const packageJSONStr = fs.readFileSync(packageJSONFilePath, "utf8");
310
- const packageJSON = JSON.parse(packageJSONStr);
311
- return packageJSON;
312
- }
313
121
 
314
122
  async function runMCP() {
315
123
  let client;
316
124
  try {
317
125
  const packageName = "${mcpServerConfig.packageName}";
318
- const packageJSON = getPackageJSON(packageName);
319
-
320
- let binPath;
321
- if (typeof packageJSON.bin === "string") {
322
- binPath = packageJSON.bin;
323
- } else if (typeof packageJSON.bin === "object") {
324
- binPath = Object.values(packageJSON.bin)[0];
325
- } else {
326
- binPath = packageJSON.main;
327
- }
328
-
329
- if (!binPath) {
330
- throw new Error(\`Package \${packageName} does not have a valid bin path in package.json.\`);
331
- }
332
-
333
- const binFilePath = \`/home/node_modules/\${packageName}/\${binPath}\`;
334
- const binArgs = ${JSON.stringify(mcpServerConfig.binArgs || [])};
335
126
 
336
127
  const transport = new StdioClientTransport({
337
- command: "node",
338
- args: [binFilePath, ...binArgs],
128
+ command: "pnpx",
129
+ args: ["--silent", packageName],
339
130
  env: {
340
- ...(Object.fromEntries(
131
+ ...Object.fromEntries(
341
132
  Object.entries(process.env).filter(([_, v]) => v !== undefined)
342
- ) as Record<string, string>),
133
+ ),
134
+ PNPM_HOME: "/root/.local/share/pnpm",
135
+ PNPM_STORE_PATH: "/pnpm-store",
343
136
  ${this.generateEnvVariables(mcpServerConfig.env, envs)}
344
137
  },
345
138
  });
@@ -359,7 +152,7 @@ async function runMCP() {
359
152
  await client.connect(transport);
360
153
  `;
361
154
  if (operation === "listTools") {
362
- return `${commonCode}
155
+ const toolsCode = `${commonCode}
363
156
  const toolsObj = await client.listTools();
364
157
 
365
158
  const result = {
@@ -367,50 +160,58 @@ async function runMCP() {
367
160
  tools: toolsObj.tools
368
161
  };
369
162
 
370
- console.log(JSON.stringify(result));
371
- if (client) {
372
- client.close();
373
- }
374
- return;
163
+ process.stdout.write(JSON.stringify(result));
375
164
  } catch (error) {
376
165
  console.error("Error in MCP test:", error);
166
+ process.exitCode = 1;
167
+ process.stdout.write(JSON.stringify({ error: error.message || "Unknown error occurred" }));
168
+ } finally {
377
169
  if (client) {
378
- client.close();
170
+ try {
171
+ await client.close();
172
+ } catch (closeError) {
173
+ console.error("Error closing MCP client:", closeError);
174
+ }
379
175
  }
380
- throw error;
381
176
  }
382
177
  }
383
178
 
384
179
  runMCP();
385
- `;
180
+ `;
181
+ return toolsCode;
182
+ // } else if (operation === "executeTool" && toolName) {
386
183
  }
387
184
  else {
388
- return `${commonCode}
185
+ const toolExecuteCode = `${commonCode}
389
186
 
390
187
  const result = await client.callTool({
391
188
  name: "${toolName}",
392
189
  arguments: ${JSON.stringify(argumentsObj)}
393
190
  });
394
191
 
395
- console.log(JSON.stringify(result))
396
- if (client) {
397
- client.close();
398
- }
399
- return;
192
+ process.stdout.write(JSON.stringify(result));
400
193
  } catch (error) {
401
- if (client) {
402
- client.close();
403
- }
404
- console.log(JSON.stringify({
194
+ console.error("Error in MCP test:", error);
195
+ process.exitCode = 1;
196
+ process.stdout.write(JSON.stringify({
405
197
  result: null,
406
198
  isError: true,
407
199
  errorMessage: error.message
408
200
  }));
201
+ } finally {
202
+ if (client) {
203
+ try {
204
+ await client.close();
205
+ } catch (closeError) {
206
+ console.error("Error closing MCP client:", closeError);
207
+ }
208
+ }
409
209
  }
410
210
  }
411
211
 
412
212
  runMCP();
413
- `;
213
+ `;
214
+ return toolExecuteCode;
414
215
  }
415
216
  }
416
217
  generateEnvVariables(env, realEnvs) {
@@ -0,0 +1 @@
1
+ export {};