modality-mcp-kit 1.6.0 → 1.6.2
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/dist/util_mcp_proxy.js +108 -0
- package/package.json +1 -1
package/dist/util_mcp_proxy.js
CHANGED
|
@@ -226,6 +226,93 @@ function clearStoredOAuthTokens(serverUrl) {
|
|
|
226
226
|
return false;
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
|
+
// ============================================
|
|
230
|
+
// TOOL PREFETCH HELPERS
|
|
231
|
+
// ============================================
|
|
232
|
+
function parseToolsFromBody(body) {
|
|
233
|
+
try {
|
|
234
|
+
const parsed = JSON.parse(body);
|
|
235
|
+
if (parsed?.result?.tools)
|
|
236
|
+
return parsed.result.tools;
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// Try SSE format
|
|
240
|
+
const lines = body.split("\n");
|
|
241
|
+
for (const line of lines) {
|
|
242
|
+
if (line.startsWith("data: ")) {
|
|
243
|
+
try {
|
|
244
|
+
const parsed = JSON.parse(line.substring(6));
|
|
245
|
+
if (parsed?.result?.tools)
|
|
246
|
+
return parsed.result.tools;
|
|
247
|
+
}
|
|
248
|
+
catch { }
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
async function prefetchAndCacheTools(mcpName, serverUrl, cache, storedToken) {
|
|
255
|
+
const cacheKey = "tools/list";
|
|
256
|
+
const cached = cache.get(cacheKey);
|
|
257
|
+
if (cached) {
|
|
258
|
+
const dataLine = cached.match(/^data: (.+)$/m)?.[1] ?? cached;
|
|
259
|
+
return { tools: parseToolsFromBody(dataLine), fromCache: true };
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const headers = {
|
|
263
|
+
"Content-Type": "application/json",
|
|
264
|
+
Accept: "application/json, text/event-stream",
|
|
265
|
+
};
|
|
266
|
+
if (storedToken) {
|
|
267
|
+
headers.authorization = `Bearer ${storedToken}`;
|
|
268
|
+
}
|
|
269
|
+
// Step 1: initialize session (required by MCP protocol before tools/list)
|
|
270
|
+
const initResponse = await fetch(serverUrl, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers,
|
|
273
|
+
body: JSON.stringify({
|
|
274
|
+
jsonrpc: "2.0",
|
|
275
|
+
id: 1,
|
|
276
|
+
method: "initialize",
|
|
277
|
+
params: {
|
|
278
|
+
protocolVersion: "2024-11-05",
|
|
279
|
+
capabilities: {},
|
|
280
|
+
clientInfo: { name: "mcp-proxy", version: "1.0" },
|
|
281
|
+
},
|
|
282
|
+
}),
|
|
283
|
+
});
|
|
284
|
+
if (!initResponse.ok) {
|
|
285
|
+
console.warn(`[MCP-PROXY] Initialize failed for ${mcpName} — status: ${initResponse.status}`);
|
|
286
|
+
return { tools: null, fromCache: false };
|
|
287
|
+
}
|
|
288
|
+
const sessionId = initResponse.headers.get("mcp-session-id");
|
|
289
|
+
if (sessionId) {
|
|
290
|
+
headers["mcp-session-id"] = sessionId;
|
|
291
|
+
}
|
|
292
|
+
// Step 2: fetch tools/list
|
|
293
|
+
const response = await fetch(serverUrl, {
|
|
294
|
+
method: "POST",
|
|
295
|
+
headers,
|
|
296
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} }),
|
|
297
|
+
});
|
|
298
|
+
const body = await response.text();
|
|
299
|
+
const tools = parseToolsFromBody(body);
|
|
300
|
+
if (response.ok && tools !== null) {
|
|
301
|
+
const ttl = METHOD_TTL_MS["tools/list"];
|
|
302
|
+
const cacheValue = body.includes("event:") ? body : `event: message\ndata: ${body}\n\n`;
|
|
303
|
+
cache.set(cacheKey, cacheValue, ttl);
|
|
304
|
+
console.log(`[MCP-PROXY] Prefetched and cached tools/list for ${mcpName}`);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
console.warn(`[MCP-PROXY] Skipping cache for ${mcpName} tools/list — status: ${response.status}, tools parsed: ${tools !== null}`);
|
|
308
|
+
}
|
|
309
|
+
return { tools, fromCache: false };
|
|
310
|
+
}
|
|
311
|
+
catch (e) {
|
|
312
|
+
console.error(`[MCP-PROXY] Failed to prefetch tools for ${mcpName}:`, e);
|
|
313
|
+
return { tools: null, fromCache: false };
|
|
314
|
+
}
|
|
315
|
+
}
|
|
229
316
|
/**
|
|
230
317
|
* Hono handler for MCP proxy
|
|
231
318
|
* @param c - Hono context with mcpName param
|
|
@@ -271,6 +358,26 @@ export const mcpProxyHandler = (MCP_SERVERS, oauthAllowAccess) => async (c) => {
|
|
|
271
358
|
: "No cached OAuth state found",
|
|
272
359
|
});
|
|
273
360
|
}
|
|
361
|
+
// Handle /_tools sub-route — prefetch and return cached tool list
|
|
362
|
+
if (c.req.path.endsWith("/_tools")) {
|
|
363
|
+
const serverConfig = MCP_SERVERS[mcpName];
|
|
364
|
+
if (!serverConfig) {
|
|
365
|
+
return c.json({ error: "MCP server not found", availableServers: Object.keys(MCP_SERVERS) }, 404);
|
|
366
|
+
}
|
|
367
|
+
const cache = getServerCache(mcpName);
|
|
368
|
+
const storedToken = getStoredOAuthToken(serverConfig.url);
|
|
369
|
+
const { tools, fromCache } = await prefetchAndCacheTools(mcpName, serverConfig.url, cache, storedToken);
|
|
370
|
+
const namesOnly = c.req.query("names") !== undefined;
|
|
371
|
+
if (namesOnly) {
|
|
372
|
+
return c.json(tools?.map((t) => t.name) ?? []);
|
|
373
|
+
}
|
|
374
|
+
return c.json({
|
|
375
|
+
mcpName,
|
|
376
|
+
fromCache,
|
|
377
|
+
count: tools?.length ?? 0,
|
|
378
|
+
tools: tools ?? [],
|
|
379
|
+
});
|
|
380
|
+
}
|
|
274
381
|
// Handle /_cache sub-routes (matched via app.use prefix routing)
|
|
275
382
|
const cachePathMatch = c.req.path.match(/\/_cache(?:\/(.+))?$/);
|
|
276
383
|
if (cachePathMatch) {
|
|
@@ -385,6 +492,7 @@ export const mcpProxyHandler = (MCP_SERVERS, oauthAllowAccess) => async (c) => {
|
|
|
385
492
|
endpoints: {
|
|
386
493
|
sse: `${basePath} (GET, Accept: text/event-stream)`,
|
|
387
494
|
rpc: `${basePath} (POST)`,
|
|
495
|
+
tools: `${basePath}/_tools`,
|
|
388
496
|
allow: `${basePath}/_allow`,
|
|
389
497
|
clearAuth: `${basePath}/_clear-auth`,
|
|
390
498
|
cache: `${basePath}/_cache`,
|
package/package.json
CHANGED