@toolsdk.ai/registry 1.0.116 → 1.0.118

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.
@@ -5,6 +5,22 @@ import { getDirname } from "../../../shared/utils/file-util";
5
5
  import { extractLastOuterJSON } from "../../../shared/utils/string-util";
6
6
  import { PackageRepository } from "../../package/package-repository";
7
7
  import { generateMCPTestCode } from "../sandbox-utils";
8
+ // Singleton Daytona client shared across all instances to prevent memory leaks
9
+ let sharedDaytonaClient = null;
10
+ function getDaytonaClient() {
11
+ if (!sharedDaytonaClient) {
12
+ const config = getDaytonaConfig();
13
+ const daytonaConfig = {
14
+ apiKey: config.apiKey,
15
+ };
16
+ if (config.apiUrl) {
17
+ daytonaConfig.apiUrl = config.apiUrl;
18
+ }
19
+ sharedDaytonaClient = new Daytona(daytonaConfig);
20
+ console.log("[DaytonaSandboxClient] Shared Daytona client initialized");
21
+ }
22
+ return sharedDaytonaClient;
23
+ }
8
24
  /**
9
25
  * Daytona Sandbox Client
10
26
  * Implements SandboxClient interface for Daytona provider
@@ -27,14 +43,8 @@ export class DaytonaSandboxClient {
27
43
  }
28
44
  this.initializing = (async () => {
29
45
  try {
30
- const config = getDaytonaConfig();
31
- const daytonaConfig = {
32
- apiKey: config.apiKey,
33
- };
34
- if (config.apiUrl) {
35
- daytonaConfig.apiUrl = config.apiUrl;
36
- }
37
- const daytona = new Daytona(daytonaConfig);
46
+ // Use shared singleton Daytona client instead of creating new one per initialization
47
+ const daytona = getDaytonaClient();
38
48
  const declarativeImage = Image.base("node:20")
39
49
  .runCommands("npm install -g pnpm", "mkdir -p /workspace", "cd /workspace && npm init -y", "cd /workspace && pnpm add @modelcontextprotocol/sdk")
40
50
  .workdir("/workspace");
@@ -13,7 +13,9 @@ export declare class SandockSandboxClient implements SandboxClient {
13
13
  initialize(): Promise<void>;
14
14
  private executeShellCommand;
15
15
  private executeCode;
16
+ private cleanupTempFileAsync;
16
17
  listTools(packageKey: string): Promise<Tool[]>;
17
18
  executeTool(packageKey: string, toolName: string, argumentsObj: Record<string, unknown>, envs?: Record<string, string>): Promise<unknown>;
18
19
  destroy(): Promise<void>;
20
+ private destroySandboxAsync;
19
21
  }
@@ -5,6 +5,21 @@ import { getDirname } from "../../../shared/utils/file-util";
5
5
  import { extractLastOuterJSON } from "../../../shared/utils/string-util";
6
6
  import { PackageRepository } from "../../package/package-repository";
7
7
  import { generateMCPTestCode } from "../sandbox-utils";
8
+ // Singleton HTTP client shared across all instances to prevent memory leaks
9
+ let sharedSandockClient = null;
10
+ function getSandockClient() {
11
+ if (!sharedSandockClient) {
12
+ const config = getSandockConfig();
13
+ sharedSandockClient = createSandockClient({
14
+ baseUrl: config.apiUrl,
15
+ headers: {
16
+ Authorization: `Bearer ${config.apiKey}`,
17
+ },
18
+ });
19
+ console.log("[SandockSandboxClient] Shared HTTP client initialized");
20
+ }
21
+ return sharedSandockClient;
22
+ }
8
23
  /**
9
24
  * Sandock Sandbox Client
10
25
  * Implements SandboxClient interface for Sandock provider
@@ -16,13 +31,8 @@ export class SandockSandboxClient {
16
31
  const __dirname = getDirname(import.meta.url);
17
32
  const packagesDir = path.join(__dirname, "../../../../packages");
18
33
  this.packageRepository = new PackageRepository(packagesDir);
19
- const config = getSandockConfig();
20
- this.client = createSandockClient({
21
- baseUrl: config.apiUrl,
22
- headers: {
23
- Authorization: `Bearer ${config.apiKey}`,
24
- },
25
- });
34
+ // Use shared singleton client instead of creating new one per instance
35
+ this.client = getSandockClient();
26
36
  }
27
37
  async initialize() {
28
38
  if (this.sandboxId) {
@@ -39,6 +49,7 @@ export class SandockSandboxClient {
39
49
  body: {
40
50
  image: "seey/sandock-mcp:latest",
41
51
  workdir: "/mcpspace",
52
+ usePersistentCache: true,
42
53
  },
43
54
  });
44
55
  if (error) {
@@ -97,23 +108,42 @@ export class SandockSandboxClient {
97
108
  content: code,
98
109
  },
99
110
  });
100
- const output = await this.executeShellCommand(`cd /mcpspace && node ${tempFile}`);
101
- // Asynchronously clean up temp file without blocking result return
102
- this.client
103
- .DELETE("/api/sandbox/{id}/fs", {
104
- params: {
105
- path: { id: this.sandboxId },
106
- query: { path: tempFile },
107
- },
108
- })
109
- .catch((error) => {
110
- console.warn("[SandockSandboxClient] Warning: Could not delete temp file:", error);
111
+ // Set custom pnpm store directory (volume-mounted cache directory) before executing code
112
+ const output = await this.executeShellCommand(`cd /mcpspace && pnpm config set store-dir /data/pnpm-store && node ${tempFile}`);
113
+ // Fire-and-forget: cleanup temp file in background without blocking result return
114
+ // The cleanup will complete asynchronously, and logs will appear when it does
115
+ this.cleanupTempFileAsync(tempFile, this.sandboxId).catch((err) => {
116
+ // This should never happen due to internal error handling, but just in case
117
+ console.error("[SandockSandboxClient] Unexpected error in cleanupTempFileAsync:", err);
111
118
  });
112
119
  return {
113
120
  exitCode: 0,
114
121
  result: output,
115
122
  };
116
123
  }
124
+ async cleanupTempFileAsync(tempFile, sandboxId) {
125
+ if (!sandboxId) {
126
+ console.warn("[SandockSandboxClient] Sandbox ID is null, skipping temp file cleanup");
127
+ return;
128
+ }
129
+ try {
130
+ const { error } = await this.client.DELETE("/api/sandbox/{id}/fs", {
131
+ params: {
132
+ path: { id: sandboxId },
133
+ query: { path: tempFile },
134
+ },
135
+ });
136
+ if (error) {
137
+ console.warn(`[SandockSandboxClient] Warning: Could not delete temp file ${tempFile}:`, error);
138
+ }
139
+ else {
140
+ console.log(`[SandockSandboxClient] Temp file ${tempFile} deleted successfully`);
141
+ }
142
+ }
143
+ catch (cleanupError) {
144
+ console.warn(`[SandockSandboxClient] Error during temp file cleanup for ${tempFile}:`, cleanupError);
145
+ }
146
+ }
117
147
  async listTools(packageKey) {
118
148
  const mcpServerConfig = this.packageRepository.getPackageConfig(packageKey);
119
149
  const testCode = generateMCPTestCode(mcpServerConfig, "listTools");
@@ -146,30 +176,45 @@ export class SandockSandboxClient {
146
176
  }
147
177
  const sandboxIdToDelete = this.sandboxId;
148
178
  this.sandboxId = null; // Clear immediately to avoid duplicate calls
149
- // Asynchronously delete sandbox without blocking result return
150
- this.client
151
- .DELETE("/api/sandbox/{id}/fs", {
152
- params: {
153
- path: { id: sandboxIdToDelete },
154
- query: { path: "/" },
155
- },
156
- })
157
- .then(({ error }) => {
179
+ // Fire-and-forget: destroy sandbox in background without blocking
180
+ // The cleanup will complete asynchronously, and logs will appear when it does
181
+ this.destroySandboxAsync(sandboxIdToDelete).catch((err) => {
182
+ // This should never happen due to internal error handling, but just in case
183
+ console.error("[SandockSandboxClient] Unexpected error in destroySandboxAsync:", err);
184
+ });
185
+ }
186
+ async destroySandboxAsync(sandboxId) {
187
+ try {
188
+ // Delete sandbox completely using the fs delete API with root path
189
+ // This removes all files and effectively shuts down the sandbox
190
+ const { error } = await this.client.DELETE("/api/sandbox/{id}/fs", {
191
+ params: {
192
+ path: { id: sandboxId },
193
+ query: { path: "/" },
194
+ },
195
+ });
158
196
  if (error) {
159
- console.warn(`[SandockSandboxClient] Warning: Could not delete sandbox: ${JSON.stringify(error)}`);
197
+ // Check if error is "not found" (already deleted)
198
+ const errorStr = JSON.stringify(error);
199
+ if (errorStr.includes("not found") || errorStr.includes("404")) {
200
+ console.log(`[SandockSandboxClient] Sandbox ${sandboxId} already deleted (not found on platform)`);
201
+ }
202
+ else {
203
+ console.warn(`[SandockSandboxClient] Warning: Could not delete sandbox ${sandboxId}: ${errorStr}`);
204
+ }
160
205
  }
161
206
  else {
162
- console.log("[SandockSandboxClient] Sandbox deleted successfully");
207
+ console.log(`[SandockSandboxClient] Sandbox ${sandboxId} deleted successfully`);
163
208
  }
164
- })
165
- .catch((err) => {
209
+ }
210
+ catch (err) {
166
211
  const errorMessage = err.message;
167
212
  if (errorMessage.includes("not found") || errorMessage.includes("404")) {
168
- console.log("[SandockSandboxClient] Sandbox already deleted (not found on platform)");
213
+ console.log(`[SandockSandboxClient] Sandbox ${sandboxId} already deleted (not found on platform)`);
169
214
  }
170
215
  else {
171
- console.warn("[SandockSandboxClient] Warning: Could not delete sandbox:", errorMessage);
216
+ console.warn(`[SandockSandboxClient] Warning: Could not delete sandbox ${sandboxId}: ${errorMessage}`);
172
217
  }
173
- });
218
+ }
174
219
  }
175
220
  }
@@ -17,10 +17,11 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
17
17
 
18
18
  async function runMCP() {
19
19
  let client;
20
+ let transport;
20
21
  try {
21
22
  const packageName = "${mcpServerConfig.packageName}";
22
23
 
23
- const transport = new StdioClientTransport({
24
+ transport = new StdioClientTransport({
24
25
  command: "pnpx",
25
26
  args: ["--silent", packageName],
26
27
  env: {
@@ -28,7 +29,6 @@ async function runMCP() {
28
29
  Object.entries(process.env).filter(([_, v]) => v !== undefined)
29
30
  ),
30
31
  PNPM_HOME: "/root/.local/share/pnpm",
31
- PNPM_STORE_PATH: "/pnpm-store",
32
32
  ${generateEnvVariables(mcpServerConfig.env, envs)}
33
33
  },
34
34
  });
@@ -69,6 +69,13 @@ async function runMCP() {
69
69
  console.error("Error closing MCP client:", closeError);
70
70
  }
71
71
  }
72
+ if (transport) {
73
+ try {
74
+ await transport.close();
75
+ } catch (transportError) {
76
+ console.error("Error closing transport:", transportError);
77
+ }
78
+ }
72
79
  }
73
80
  }
74
81
 
@@ -100,6 +107,13 @@ runMCP();
100
107
  console.error("Error closing MCP client:", closeError);
101
108
  }
102
109
  }
110
+ if (transport) {
111
+ try {
112
+ await transport.close();
113
+ } catch (transportError) {
114
+ console.error("Error closing transport:", transportError);
115
+ }
116
+ }
103
117
  }
104
118
  }
105
119
 
@@ -32,6 +32,13 @@ async function createMcpClient(mcpServerConfig, transport) {
32
32
  catch (e) {
33
33
  console.warn(`${packageName} mcp client close failure.`, e);
34
34
  }
35
+ // Close transport to release child process and file descriptors
36
+ try {
37
+ await transport.close();
38
+ }
39
+ catch (e) {
40
+ console.warn(`${packageName} mcp transport close failure.`, e);
41
+ }
35
42
  };
36
43
  return { client, transport, closeConnection };
37
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolsdk.ai/registry",
3
- "version": "1.0.116",
3
+ "version": "1.0.118",
4
4
  "description": "An Open, Structured, and Standard Registry for MCP Servers and Packages.",
5
5
  "keywords": [
6
6
  "mcp",