@toolsdk.ai/registry 1.0.112 → 1.0.113
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/README.md +3793 -3793
- package/dist/api/package-handler.js +6 -3
- package/dist/api/package-so.d.ts +12 -0
- package/dist/api/package-so.js +203 -0
- package/dist/helper.js +1 -0
- package/dist/sandbox/mcp-sandbox-client.d.ts +37 -0
- package/dist/sandbox/mcp-sandbox-client.js +428 -0
- package/indexes/categories-list.json +3783 -3783
- package/indexes/packages-list.json +1 -1
- package/package.json +2 -1
- package/packages/search-data-extraction/newsnow-mcp-server.json +1 -0
- package/packages/support-service-management/hh-jira-mcp-server.json +14 -1
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { createErrorResponse, createResponse } from "../utils";
|
|
2
2
|
import { PackageSO } from "./package-so";
|
|
3
|
+
const shouldUseSandbox = () => {
|
|
4
|
+
return process.env.USE_MCP_SANDBOX === "true";
|
|
5
|
+
};
|
|
3
6
|
export const packageHandler = {
|
|
4
7
|
executeTool: async (c) => {
|
|
5
8
|
const requestBody = await c.req.json();
|
|
6
9
|
try {
|
|
7
|
-
const toolSO = new PackageSO();
|
|
10
|
+
const toolSO = new PackageSO(shouldUseSandbox());
|
|
8
11
|
const result = await toolSO.executeTool(requestBody);
|
|
9
12
|
const response = createResponse(result);
|
|
10
13
|
return c.json(response, 200);
|
|
@@ -33,7 +36,7 @@ export const packageHandler = {
|
|
|
33
36
|
return c.json(errorResponse, 200);
|
|
34
37
|
}
|
|
35
38
|
try {
|
|
36
|
-
const toolSO = new PackageSO();
|
|
39
|
+
const toolSO = new PackageSO(shouldUseSandbox());
|
|
37
40
|
const result = await toolSO.getPackageDetail(packageName);
|
|
38
41
|
const response = createResponse(result);
|
|
39
42
|
return c.json(response, 200);
|
|
@@ -53,7 +56,7 @@ export const packageHandler = {
|
|
|
53
56
|
return c.json(errorResponse, 200);
|
|
54
57
|
}
|
|
55
58
|
try {
|
|
56
|
-
const toolSO = new PackageSO();
|
|
59
|
+
const toolSO = new PackageSO(shouldUseSandbox());
|
|
57
60
|
const result = await toolSO.listTools(packageName);
|
|
58
61
|
const response = createResponse(result);
|
|
59
62
|
return c.json(response, 200);
|
package/dist/api/package-so.d.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { MCPSandboxClient } from "../sandbox/mcp-sandbox-client.js";
|
|
2
3
|
import type { MCPServerPackageConfig, ToolExecute } from "../types";
|
|
3
4
|
export declare class PackageSO {
|
|
5
|
+
private useSandbox;
|
|
6
|
+
private sandboxClient;
|
|
7
|
+
private static sandboxInstances;
|
|
8
|
+
private static MAX_SANDBOXES;
|
|
9
|
+
private static IDLE_CLOSE_MS;
|
|
10
|
+
constructor(useSandbox?: boolean);
|
|
11
|
+
static acquireSandbox(key?: string): Promise<MCPSandboxClient>;
|
|
12
|
+
static releaseSandbox(key?: string): Promise<void>;
|
|
13
|
+
static cleanupSandboxInstances(): Promise<void>;
|
|
4
14
|
executeTool(request: ToolExecute): Promise<unknown>;
|
|
15
|
+
private executeToolInSandbox;
|
|
5
16
|
listTools(packageName: string): Promise<Tool[]>;
|
|
17
|
+
private listToolsInSandbox;
|
|
6
18
|
getPackageDetail(packageName: string): Promise<MCPServerPackageConfig>;
|
|
7
19
|
}
|
package/dist/api/package-so.js
CHANGED
|
@@ -1,11 +1,144 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { getMcpClient, getPackageConfigByKey, typedAllPackagesList } from "../helper.js";
|
|
4
|
+
import { MCPSandboxClient } from "../sandbox/mcp-sandbox-client.js";
|
|
4
5
|
import { getDirname } from "../utils";
|
|
5
6
|
const __dirname = getDirname(import.meta.url);
|
|
6
7
|
export class PackageSO {
|
|
8
|
+
constructor(useSandbox = false) {
|
|
9
|
+
this.useSandbox = false;
|
|
10
|
+
this.sandboxClient = null;
|
|
11
|
+
this.useSandbox = useSandbox;
|
|
12
|
+
if (useSandbox) {
|
|
13
|
+
const sandboxKey = "default";
|
|
14
|
+
const record = PackageSO.sandboxInstances.get(sandboxKey);
|
|
15
|
+
if (record) {
|
|
16
|
+
record.refCount++;
|
|
17
|
+
record.lastUsedAt = Date.now();
|
|
18
|
+
this.sandboxClient = record.client;
|
|
19
|
+
// Cancel all idle close timer
|
|
20
|
+
record.client.touch();
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
// Create a new instance if none exists (subject to max limit)
|
|
24
|
+
this.sandboxClient = new MCPSandboxClient();
|
|
25
|
+
const newRecord = {
|
|
26
|
+
client: this.sandboxClient,
|
|
27
|
+
refCount: 1,
|
|
28
|
+
lastUsedAt: Date.now(),
|
|
29
|
+
idleCloseMs: PackageSO.IDLE_CLOSE_MS,
|
|
30
|
+
};
|
|
31
|
+
// Try to insert, evict LRU idle instance if MAX is reached
|
|
32
|
+
if (PackageSO.sandboxInstances.size >= PackageSO.MAX_SANDBOXES) {
|
|
33
|
+
// Find the oldest idle instance (refCount === 0)
|
|
34
|
+
let lruKey = null;
|
|
35
|
+
let lruTime = Infinity;
|
|
36
|
+
for (const [k, r] of PackageSO.sandboxInstances) {
|
|
37
|
+
if (r.refCount === 0 && r.lastUsedAt < lruTime) {
|
|
38
|
+
lruKey = k;
|
|
39
|
+
lruTime = r.lastUsedAt;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (lruKey) {
|
|
43
|
+
const evict = PackageSO.sandboxInstances.get(lruKey);
|
|
44
|
+
// Immediately close and delete
|
|
45
|
+
if (evict) {
|
|
46
|
+
evict.client.kill().catch((e) => {
|
|
47
|
+
console.error("[PackageSO] Error killing evicted sandbox:", e);
|
|
48
|
+
});
|
|
49
|
+
PackageSO.sandboxInstances.delete(lruKey);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// No recyclable instances -> throw error to prevent exceeding system limit
|
|
54
|
+
throw new Error("Cannot create new sandbox: max sandboxes reached and none are idle");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
PackageSO.sandboxInstances.set(sandboxKey, newRecord);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Acquire / Release explicit APIs (for external or future use)
|
|
62
|
+
static async acquireSandbox(key = "default") {
|
|
63
|
+
const record = PackageSO.sandboxInstances.get(key);
|
|
64
|
+
if (record) {
|
|
65
|
+
record.refCount++;
|
|
66
|
+
record.lastUsedAt = Date.now();
|
|
67
|
+
record.client.touch();
|
|
68
|
+
return record.client;
|
|
69
|
+
}
|
|
70
|
+
// Create new instance (note MAX_SANDBOXES check, consistent with constructor logic)
|
|
71
|
+
if (PackageSO.sandboxInstances.size >= PackageSO.MAX_SANDBOXES) {
|
|
72
|
+
// Try to evict LRU idle instance
|
|
73
|
+
let lruKey = null;
|
|
74
|
+
let lruTime = Infinity;
|
|
75
|
+
for (const [k, r] of PackageSO.sandboxInstances) {
|
|
76
|
+
if (r.refCount === 0 && r.lastUsedAt < lruTime) {
|
|
77
|
+
lruKey = k;
|
|
78
|
+
lruTime = r.lastUsedAt;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (lruKey) {
|
|
82
|
+
const evict = PackageSO.sandboxInstances.get(lruKey);
|
|
83
|
+
if (evict) {
|
|
84
|
+
await evict.client
|
|
85
|
+
.kill()
|
|
86
|
+
.catch((e) => console.error("[PackageSO] Error killing evicted sandbox:", e));
|
|
87
|
+
PackageSO.sandboxInstances.delete(lruKey);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
throw new Error("Cannot create new sandbox: max sandboxes reached and none are idle");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const client = new MCPSandboxClient();
|
|
95
|
+
const newRecord = {
|
|
96
|
+
client,
|
|
97
|
+
refCount: 1,
|
|
98
|
+
lastUsedAt: Date.now(),
|
|
99
|
+
idleCloseMs: PackageSO.IDLE_CLOSE_MS,
|
|
100
|
+
};
|
|
101
|
+
PackageSO.sandboxInstances.set(key, newRecord);
|
|
102
|
+
return client;
|
|
103
|
+
}
|
|
104
|
+
static async releaseSandbox(key = "default") {
|
|
105
|
+
const record = PackageSO.sandboxInstances.get(key);
|
|
106
|
+
if (!record)
|
|
107
|
+
return;
|
|
108
|
+
record.refCount = Math.max(0, record.refCount - 1);
|
|
109
|
+
record.lastUsedAt = Date.now();
|
|
110
|
+
if (record.refCount === 0) {
|
|
111
|
+
// schedule idle close after IDLE_CLOSE_MS
|
|
112
|
+
record.client.scheduleIdleClose(record.idleCloseMs || PackageSO.IDLE_CLOSE_MS);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Cleanup all static sandbox instances safely
|
|
116
|
+
static async cleanupSandboxInstances() {
|
|
117
|
+
const killers = [];
|
|
118
|
+
for (const [key, record] of PackageSO.sandboxInstances) {
|
|
119
|
+
killers.push((async () => {
|
|
120
|
+
try {
|
|
121
|
+
await record.client.kill();
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
console.error("[PackageSO] cleanup error for sandbox", key, err);
|
|
125
|
+
}
|
|
126
|
+
})());
|
|
127
|
+
PackageSO.sandboxInstances.delete(key);
|
|
128
|
+
}
|
|
129
|
+
await Promise.all(killers);
|
|
130
|
+
}
|
|
7
131
|
async executeTool(request) {
|
|
8
132
|
const mcpServerConfig = getPackageConfigByKey(request.packageName);
|
|
133
|
+
if (this.useSandbox && mcpServerConfig.runtime === "node") {
|
|
134
|
+
try {
|
|
135
|
+
const result = await this.executeToolInSandbox(request);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.warn(`[executeTool] Sandbox mode failed for tool ${request.toolKey} in package ${request.packageName}, falling back to local mode:`, error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
9
142
|
const { client, closeConnection } = await getMcpClient(mcpServerConfig, request.envs || {});
|
|
10
143
|
try {
|
|
11
144
|
const result = await client.callTool({
|
|
@@ -19,8 +152,47 @@ export class PackageSO {
|
|
|
19
152
|
await closeConnection();
|
|
20
153
|
}
|
|
21
154
|
}
|
|
155
|
+
async executeToolInSandbox(request) {
|
|
156
|
+
if (!this.sandboxClient) {
|
|
157
|
+
throw new Error("Sandbox client not initialized");
|
|
158
|
+
}
|
|
159
|
+
// Initialize if not already initialized (concurrency protection via MCPSandboxClient.initialize)
|
|
160
|
+
await this.sandboxClient.initialize();
|
|
161
|
+
// Mark usage
|
|
162
|
+
this.sandboxClient.touch();
|
|
163
|
+
try {
|
|
164
|
+
const result = await this.sandboxClient.executeTool(request.packageName, request.toolKey, request.inputData || {}, request.envs);
|
|
165
|
+
console.log(`Tool ${request.toolKey} executed successfully in sandbox`);
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
// If it's a sandbox not found error, try reinitializing and retrying once
|
|
170
|
+
if (error instanceof Error && error.message.includes("sandbox was not found")) {
|
|
171
|
+
console.log("[PackageSO] Retrying tool execution after sandbox failure");
|
|
172
|
+
await this.sandboxClient.initialize();
|
|
173
|
+
// Retry tool execution
|
|
174
|
+
const result = await this.sandboxClient.executeTool(request.packageName, request.toolKey, request.inputData || {}, request.envs);
|
|
175
|
+
console.log(`Tool ${request.toolKey} executed successfully in sandbox (retry)`);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
// Release reference count
|
|
182
|
+
await PackageSO.releaseSandbox("default");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
22
185
|
async listTools(packageName) {
|
|
23
186
|
const mcpServerConfig = getPackageConfigByKey(packageName);
|
|
187
|
+
if (this.useSandbox && mcpServerConfig.runtime === "node") {
|
|
188
|
+
try {
|
|
189
|
+
const tools = await this.listToolsInSandbox(packageName);
|
|
190
|
+
return tools;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
console.warn(`[listTools] Sandbox mode failed for package ${packageName}, falling back to local mode:`, error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
24
196
|
const mockEnvs = {};
|
|
25
197
|
if (mcpServerConfig.env) {
|
|
26
198
|
Object.keys(mcpServerConfig.env).forEach((key) => {
|
|
@@ -37,6 +209,34 @@ export class PackageSO {
|
|
|
37
209
|
await closeConnection();
|
|
38
210
|
}
|
|
39
211
|
}
|
|
212
|
+
async listToolsInSandbox(packageName) {
|
|
213
|
+
if (!this.sandboxClient) {
|
|
214
|
+
throw new Error("Sandbox client not initialized");
|
|
215
|
+
}
|
|
216
|
+
// Initialize only if sandbox is not initialized
|
|
217
|
+
await this.sandboxClient.initialize();
|
|
218
|
+
this.sandboxClient.touch();
|
|
219
|
+
try {
|
|
220
|
+
const tools = await this.sandboxClient.listTools(packageName);
|
|
221
|
+
console.log(`Tools list retrieved successfully for package ${packageName} in sandbox`);
|
|
222
|
+
return tools;
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
// If it's a sandbox not found error, try reinitializing and retrying once
|
|
226
|
+
if (error instanceof Error && error.message.includes("sandbox was not found")) {
|
|
227
|
+
console.log("[PackageSO] Retrying tools listing after sandbox failure");
|
|
228
|
+
await this.sandboxClient.initialize();
|
|
229
|
+
// Retry listing tools
|
|
230
|
+
const tools = await this.sandboxClient.listTools(packageName);
|
|
231
|
+
console.log(`Tools list retrieved successfully for package ${packageName} in sandbox (retry)`);
|
|
232
|
+
return tools;
|
|
233
|
+
}
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
await PackageSO.releaseSandbox("default");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
40
240
|
async getPackageDetail(packageName) {
|
|
41
241
|
const packageInfo = typedAllPackagesList[packageName];
|
|
42
242
|
if (!packageInfo) {
|
|
@@ -58,3 +258,6 @@ export class PackageSO {
|
|
|
58
258
|
return packageConfigWithTools;
|
|
59
259
|
}
|
|
60
260
|
}
|
|
261
|
+
PackageSO.sandboxInstances = new Map();
|
|
262
|
+
PackageSO.MAX_SANDBOXES = 10;
|
|
263
|
+
PackageSO.IDLE_CLOSE_MS = 5 * 60 * 1000;
|
package/dist/helper.js
CHANGED
|
@@ -101,6 +101,7 @@ export function updatePackageJsonDependencies({ packageDeps, enableValidation =
|
|
|
101
101
|
const packageJsonFile = "./package.json";
|
|
102
102
|
const packageJSONStr = fs.readFileSync(packageJsonFile, "utf-8");
|
|
103
103
|
const newDeps = {
|
|
104
|
+
"@e2b/code-interpreter": "^2.0.0",
|
|
104
105
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
105
106
|
"@hono/node-server": "1.15.0",
|
|
106
107
|
"@hono/swagger-ui": "^0.5.2",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export declare class MCPSandboxClient {
|
|
3
|
+
private sandbox;
|
|
4
|
+
private initializing;
|
|
5
|
+
private readonly apiKey;
|
|
6
|
+
private toolCache;
|
|
7
|
+
private readonly E2B_SANDBOX_TIMEOUT_MS;
|
|
8
|
+
private readonly TOOL_CACHE_TTL;
|
|
9
|
+
TOOL_EXECUTION_TIMEOUT: number;
|
|
10
|
+
private lastTouchTime;
|
|
11
|
+
private readonly THROTTLE_DELAY_MS;
|
|
12
|
+
createdAt: number | null;
|
|
13
|
+
lastUsedAt: number | null;
|
|
14
|
+
private ttlTimer;
|
|
15
|
+
private autoCloseOnIdleTimer;
|
|
16
|
+
private idleCloseMs;
|
|
17
|
+
constructor(apiKey?: string);
|
|
18
|
+
initialize(): Promise<void>;
|
|
19
|
+
private setupTTLTimer;
|
|
20
|
+
/**
|
|
21
|
+
* Update sandbox last used time
|
|
22
|
+
*/
|
|
23
|
+
touch(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Actually perform the touch operation
|
|
26
|
+
*/
|
|
27
|
+
private performTouch;
|
|
28
|
+
scheduleIdleClose(idleMs: number): void;
|
|
29
|
+
kill(): Promise<void>;
|
|
30
|
+
clearPackageCache(packageKey: string): void;
|
|
31
|
+
clearAllCache(): void;
|
|
32
|
+
listTools(packageKey: string): Promise<Tool[]>;
|
|
33
|
+
executeTool(packageKey: string, toolName: string, argumentsObj: Record<string, unknown>, envs?: Record<string, string>): Promise<unknown>;
|
|
34
|
+
private runCodeWithTimeout;
|
|
35
|
+
private generateMCPTestCode;
|
|
36
|
+
private generateEnvVariables;
|
|
37
|
+
}
|