modality-mcp-kit 1.5.1 → 1.6.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.
package/dist/types/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export { setupAITools, ModalityFastMCP } from "./util_mcp_tools_converter";
|
|
|
3
3
|
export type { AITools, AITool, FastMCPTool, } from "./schemas/schemas_tool_config";
|
|
4
4
|
export type { FastMCPCompatible, BasePrompt, } from "./util_mcp_tools_converter";
|
|
5
5
|
export { FastHonoMcp } from "./FastHonoMcp";
|
|
6
|
-
export { mcpProxyHandler, mcpProxyListHandler, mcpProxyCacheHandler, type McpProxyConfig, } from "./util_mcp_proxy";
|
|
6
|
+
export { mcpProxyHandler, mcpProxyListHandler, mcpProxyCacheHandler, type McpProxyConfig, type OAuthAllowAccessFn, } from "./util_mcp_proxy";
|
|
@@ -12,12 +12,16 @@ interface MCPServerConfig {
|
|
|
12
12
|
description?: string;
|
|
13
13
|
}
|
|
14
14
|
export type McpProxyConfig = Record<string, MCPServerConfig>;
|
|
15
|
+
export type OAuthAllowAccessFn = (serverUrl: string, mcpName: string) => Promise<{
|
|
16
|
+
status: string;
|
|
17
|
+
message?: string;
|
|
18
|
+
}>;
|
|
15
19
|
/**
|
|
16
20
|
* Hono handler for MCP proxy
|
|
17
21
|
* @param c - Hono context with mcpName param
|
|
18
22
|
* @returns Response proxied from MCP server
|
|
19
23
|
*/
|
|
20
|
-
export declare const mcpProxyHandler: (MCP_SERVERS: McpProxyConfig) => (c: any) => Promise<any>;
|
|
24
|
+
export declare const mcpProxyHandler: (MCP_SERVERS: McpProxyConfig, oauthAllowAccess?: OAuthAllowAccessFn) => (c: any) => Promise<any>;
|
|
21
25
|
/**
|
|
22
26
|
* Hono handler for listing available MCP servers
|
|
23
27
|
* @param c - Hono context
|
package/dist/util_mcp_proxy.js
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
* Example: /proxy/figma → http://127.0.0.1:3845/mcp
|
|
9
9
|
*/
|
|
10
10
|
import { SimpleCache } from "modality-kit";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
11
15
|
// ============================================
|
|
12
16
|
// CACHE CONFIGURATION
|
|
13
17
|
// ============================================
|
|
@@ -195,14 +199,39 @@ function getAnyCacheForMethod(cache, method) {
|
|
|
195
199
|
}
|
|
196
200
|
}
|
|
197
201
|
// ============================================
|
|
198
|
-
//
|
|
202
|
+
// OAUTH TOKEN HELPERS
|
|
199
203
|
// ============================================
|
|
204
|
+
function getOAuthCachePath(serverUrl) {
|
|
205
|
+
const key = createHash("sha1").update(serverUrl).digest("hex").slice(0, 12);
|
|
206
|
+
return join(homedir(), ".cache", "counter", `${key}.json`);
|
|
207
|
+
}
|
|
208
|
+
function getStoredOAuthToken(serverUrl) {
|
|
209
|
+
try {
|
|
210
|
+
const data = JSON.parse(readFileSync(getOAuthCachePath(serverUrl), "utf8"));
|
|
211
|
+
return data.tokens?.access_token ?? null;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function clearStoredOAuthTokens(serverUrl) {
|
|
218
|
+
const cachePath = getOAuthCachePath(serverUrl);
|
|
219
|
+
try {
|
|
220
|
+
const data = JSON.parse(readFileSync(cachePath, "utf8"));
|
|
221
|
+
delete data.tokens;
|
|
222
|
+
writeFileSync(cachePath, JSON.stringify(data, null, 2));
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
200
229
|
/**
|
|
201
230
|
* Hono handler for MCP proxy
|
|
202
231
|
* @param c - Hono context with mcpName param
|
|
203
232
|
* @returns Response proxied from MCP server
|
|
204
233
|
*/
|
|
205
|
-
export const mcpProxyHandler = (MCP_SERVERS) => async (c) => {
|
|
234
|
+
export const mcpProxyHandler = (MCP_SERVERS, oauthAllowAccess) => async (c) => {
|
|
206
235
|
const mcpName = c.req.param("mcpName");
|
|
207
236
|
if (!mcpName) {
|
|
208
237
|
return c.json({
|
|
@@ -210,6 +239,38 @@ export const mcpProxyHandler = (MCP_SERVERS) => async (c) => {
|
|
|
210
239
|
availableServers: Object.keys(MCP_SERVERS),
|
|
211
240
|
}, 400);
|
|
212
241
|
}
|
|
242
|
+
// Handle /_allow sub-route (OAuth access flow)
|
|
243
|
+
if (c.req.path.endsWith("/_allow")) {
|
|
244
|
+
const serverConfig = MCP_SERVERS[mcpName];
|
|
245
|
+
if (!serverConfig) {
|
|
246
|
+
return c.json({ error: "MCP server not found", availableServers: Object.keys(MCP_SERVERS) }, 404);
|
|
247
|
+
}
|
|
248
|
+
if (!oauthAllowAccess) {
|
|
249
|
+
return c.json({ error: "OAuth not configured for this proxy" }, 501);
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const result = await oauthAllowAccess(serverConfig.url, mcpName);
|
|
253
|
+
return c.json(result);
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
return c.json({ error: "OAuth failed", message: err.message }, 500);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Handle /_clear-auth sub-route
|
|
260
|
+
if (c.req.path.endsWith("/_clear-auth")) {
|
|
261
|
+
const serverConfig = MCP_SERVERS[mcpName];
|
|
262
|
+
if (!serverConfig) {
|
|
263
|
+
return c.json({ error: "MCP server not found", availableServers: Object.keys(MCP_SERVERS) }, 404);
|
|
264
|
+
}
|
|
265
|
+
const cleared = clearStoredOAuthTokens(serverConfig.url);
|
|
266
|
+
return c.json({
|
|
267
|
+
status: cleared ? "cleared" : "no_cache",
|
|
268
|
+
mcpName,
|
|
269
|
+
message: cleared
|
|
270
|
+
? "OAuth tokens cleared"
|
|
271
|
+
: "No cached OAuth state found",
|
|
272
|
+
});
|
|
273
|
+
}
|
|
213
274
|
// Handle /_cache sub-routes (matched via app.use prefix routing)
|
|
214
275
|
const cachePathMatch = c.req.path.match(/\/_cache(?:\/(.+))?$/);
|
|
215
276
|
if (cachePathMatch) {
|
|
@@ -272,10 +333,18 @@ export const mcpProxyHandler = (MCP_SERVERS) => async (c) => {
|
|
|
272
333
|
upstreamHeaders[header] = value;
|
|
273
334
|
}
|
|
274
335
|
}
|
|
336
|
+
// Auto-inject stored OAuth token if no Authorization header present
|
|
337
|
+
if (!upstreamHeaders.authorization) {
|
|
338
|
+
const storedToken = getStoredOAuthToken(serverConfig.url);
|
|
339
|
+
if (storedToken) {
|
|
340
|
+
upstreamHeaders.authorization = `Bearer ${storedToken}`;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
275
343
|
try {
|
|
276
344
|
const upstreamResponse = await fetch(serverConfig.url, {
|
|
277
345
|
method: "GET",
|
|
278
346
|
headers: upstreamHeaders,
|
|
347
|
+
signal: c.req.raw.signal,
|
|
279
348
|
});
|
|
280
349
|
// Forward response headers
|
|
281
350
|
const responseHeaders = new Headers();
|
|
@@ -308,11 +377,22 @@ export const mcpProxyHandler = (MCP_SERVERS) => async (c) => {
|
|
|
308
377
|
}
|
|
309
378
|
// Regular GET - return server info
|
|
310
379
|
const cache = getServerCache(mcpName);
|
|
380
|
+
const basePath = `/proxy/${mcpName}`;
|
|
311
381
|
return c.json({
|
|
312
382
|
mcpName,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
383
|
+
upstream: serverConfig.url,
|
|
384
|
+
description: serverConfig.description,
|
|
385
|
+
endpoints: {
|
|
386
|
+
sse: `${basePath} (GET, Accept: text/event-stream)`,
|
|
387
|
+
rpc: `${basePath} (POST)`,
|
|
388
|
+
allow: `${basePath}/_allow`,
|
|
389
|
+
clearAuth: `${basePath}/_clear-auth`,
|
|
390
|
+
cache: `${basePath}/_cache`,
|
|
391
|
+
},
|
|
392
|
+
cache: {
|
|
393
|
+
keys: cache.keys(),
|
|
394
|
+
size: cache.keys().length,
|
|
395
|
+
},
|
|
316
396
|
});
|
|
317
397
|
}
|
|
318
398
|
// POST request - proxy to MCP server with streaming support
|
|
@@ -384,6 +464,14 @@ export const mcpProxyHandler = (MCP_SERVERS) => async (c) => {
|
|
|
384
464
|
console.log(`[MCP-PROXY] Forwarding header: ${header}=${value.substring(0, 50)}`);
|
|
385
465
|
}
|
|
386
466
|
}
|
|
467
|
+
// Auto-inject stored OAuth token if no Authorization header present
|
|
468
|
+
if (!upstreamHeaders.authorization) {
|
|
469
|
+
const storedToken = getStoredOAuthToken(serverConfig.url);
|
|
470
|
+
if (storedToken) {
|
|
471
|
+
upstreamHeaders.authorization = `Bearer ${storedToken}`;
|
|
472
|
+
console.log(`[MCP-PROXY] Using stored OAuth token for ${mcpName}`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
387
475
|
console.log(`[MCP-PROXY] Upstream headers:`, Object.keys(upstreamHeaders));
|
|
388
476
|
const upstreamResponse = await fetch(serverConfig.url, {
|
|
389
477
|
method: "POST",
|
package/package.json
CHANGED