mcp-use 1.6.2 → 1.7.0-canary.1
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/.tsbuildinfo +1 -1
- package/dist/chunk-JQKKMUCT.js +0 -0
- package/dist/chunk-PE7UMCVO.js +377 -0
- package/dist/{chunk-BWOTID2D.js → chunk-QSLJXXMG.js} +3 -346
- package/dist/chunk-R5DJJ4IV.js +942 -0
- package/dist/{chunk-SJEHVCPM.js → chunk-XN2PU4PS.js} +100 -23
- package/dist/index.cjs +132 -29
- package/dist/index.js +8 -5
- package/dist/oauth-CNGBFOZW.js +29 -0
- package/dist/src/agents/index.js +1 -1
- package/dist/src/auth/browser-provider.d.ts +2 -0
- package/dist/src/auth/browser-provider.d.ts.map +1 -1
- package/dist/src/auth/callback.d.ts.map +1 -1
- package/dist/src/auth/index.cjs +396 -0
- package/dist/src/auth/index.js +10 -0
- package/dist/src/auth/types.d.ts +3 -1
- package/dist/src/auth/types.d.ts.map +1 -1
- package/dist/src/browser.cjs +36 -8
- package/dist/src/browser.js +6 -4
- package/dist/src/connectors/http.d.ts.map +1 -1
- package/dist/src/react/index.cjs +132 -29
- package/dist/src/react/index.js +3 -2
- package/dist/src/react/types.d.ts +12 -1
- package/dist/src/react/types.d.ts.map +1 -1
- package/dist/src/react/useMcp.d.ts.map +1 -1
- package/dist/src/server/context-storage.d.ts +54 -0
- package/dist/src/server/context-storage.d.ts.map +1 -0
- package/dist/src/server/index.cjs +1409 -410
- package/dist/src/server/index.d.ts +4 -1
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +420 -412
- package/dist/src/server/mcp-server.d.ts +50 -81
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/oauth/index.d.ts +13 -0
- package/dist/src/server/oauth/index.d.ts.map +1 -0
- package/dist/src/server/oauth/middleware.d.ts +19 -0
- package/dist/src/server/oauth/middleware.d.ts.map +1 -0
- package/dist/src/server/oauth/providers/auth0.d.ts +22 -0
- package/dist/src/server/oauth/providers/auth0.d.ts.map +1 -0
- package/dist/src/server/oauth/providers/custom.d.ts +19 -0
- package/dist/src/server/oauth/providers/custom.d.ts.map +1 -0
- package/dist/src/server/oauth/providers/keycloak.d.ts +22 -0
- package/dist/src/server/oauth/providers/keycloak.d.ts.map +1 -0
- package/dist/src/server/oauth/providers/supabase.d.ts +24 -0
- package/dist/src/server/oauth/providers/supabase.d.ts.map +1 -0
- package/dist/src/server/oauth/providers/types.d.ts +138 -0
- package/dist/src/server/oauth/providers/types.d.ts.map +1 -0
- package/dist/src/server/oauth/providers/workos.d.ts +30 -0
- package/dist/src/server/oauth/providers/workos.d.ts.map +1 -0
- package/dist/src/server/oauth/providers.d.ts +208 -0
- package/dist/src/server/oauth/providers.d.ts.map +1 -0
- package/dist/src/server/oauth/routes.d.ts +33 -0
- package/dist/src/server/oauth/routes.d.ts.map +1 -0
- package/dist/src/server/oauth/utils.d.ts +155 -0
- package/dist/src/server/oauth/utils.d.ts.map +1 -0
- package/dist/src/server/types/common.d.ts +47 -0
- package/dist/src/server/types/common.d.ts.map +1 -1
- package/dist/src/server/types/context.d.ts +34 -0
- package/dist/src/server/types/context.d.ts.map +1 -0
- package/dist/src/server/types/index.d.ts +2 -1
- package/dist/src/server/types/index.d.ts.map +1 -1
- package/dist/src/server/types/tool.d.ts +82 -9
- package/dist/src/server/types/tool.d.ts.map +1 -1
- package/dist/src/server/utils/index.d.ts +6 -0
- package/dist/src/server/utils/index.d.ts.map +1 -0
- package/dist/src/server/utils/response-helpers.d.ts +151 -0
- package/dist/src/server/utils/response-helpers.d.ts.map +1 -0
- package/dist/src/server/utils/runtime.d.ts +25 -0
- package/dist/src/server/utils/runtime.d.ts.map +1 -0
- package/dist/src/task_managers/streamable_http.d.ts +1 -0
- package/dist/src/task_managers/streamable_http.d.ts.map +1 -1
- package/dist/tsup.config.d.ts.map +1 -1
- package/package.json +14 -5
- /package/dist/{chunk-MCF5P6GJ.js → chunk-GVVPUU5K.js} +0 -0
package/dist/src/server/index.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fsHelpers,
|
|
3
|
+
generateUUID,
|
|
4
|
+
getAuth,
|
|
5
|
+
getCwd,
|
|
6
|
+
getEnv,
|
|
7
|
+
hasAnyScope,
|
|
8
|
+
hasScope,
|
|
9
|
+
isDeno,
|
|
10
|
+
oauthAuth0Provider,
|
|
11
|
+
oauthCustomProvider,
|
|
12
|
+
oauthKeycloakProvider,
|
|
13
|
+
oauthSupabaseProvider,
|
|
14
|
+
oauthWorkOSProvider,
|
|
15
|
+
pathHelpers,
|
|
16
|
+
requireAnyScope,
|
|
17
|
+
requireScope
|
|
18
|
+
} from "../../chunk-R5DJJ4IV.js";
|
|
1
19
|
import {
|
|
2
20
|
__name
|
|
3
21
|
} from "../../chunk-3GQAWCBQ.js";
|
|
@@ -7,15 +25,31 @@ import {
|
|
|
7
25
|
McpServer as OfficialMcpServer,
|
|
8
26
|
ResourceTemplate
|
|
9
27
|
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28
|
+
import { z } from "zod";
|
|
10
29
|
import { Hono } from "hono";
|
|
11
30
|
import { cors } from "hono/cors";
|
|
12
|
-
|
|
31
|
+
|
|
32
|
+
// src/server/context-storage.ts
|
|
33
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
34
|
+
var requestContextStorage = new AsyncLocalStorage();
|
|
35
|
+
async function runWithContext(context, fn) {
|
|
36
|
+
return requestContextStorage.run(context, fn);
|
|
37
|
+
}
|
|
38
|
+
__name(runWithContext, "runWithContext");
|
|
39
|
+
function getRequestContext() {
|
|
40
|
+
return requestContextStorage.getStore();
|
|
41
|
+
}
|
|
42
|
+
__name(getRequestContext, "getRequestContext");
|
|
43
|
+
function hasRequestContext() {
|
|
44
|
+
return requestContextStorage.getStore() !== void 0;
|
|
45
|
+
}
|
|
46
|
+
__name(hasRequestContext, "hasRequestContext");
|
|
13
47
|
|
|
14
48
|
// src/server/adapters/mcp-ui-adapter.ts
|
|
15
49
|
import { createUIResource } from "@mcp-ui/server";
|
|
16
|
-
function buildWidgetUrl(
|
|
50
|
+
function buildWidgetUrl(widget2, props, config) {
|
|
17
51
|
const url = new URL(
|
|
18
|
-
`/mcp-use/widgets/${
|
|
52
|
+
`/mcp-use/widgets/${widget2}`,
|
|
19
53
|
`${config.baseUrl}:${config.port}`
|
|
20
54
|
);
|
|
21
55
|
if (props && Object.keys(props).length > 0) {
|
|
@@ -55,17 +89,17 @@ function createRemoteDomResource(uri, script, framework = "react", encoding = "t
|
|
|
55
89
|
}
|
|
56
90
|
__name(createRemoteDomResource, "createRemoteDomResource");
|
|
57
91
|
function createAppsSdkResource(uri, htmlTemplate, metadata) {
|
|
58
|
-
const
|
|
92
|
+
const resource2 = {
|
|
59
93
|
uri,
|
|
60
94
|
mimeType: "text/html+skybridge",
|
|
61
95
|
text: htmlTemplate
|
|
62
96
|
};
|
|
63
97
|
if (metadata && Object.keys(metadata).length > 0) {
|
|
64
|
-
|
|
98
|
+
resource2._meta = metadata;
|
|
65
99
|
}
|
|
66
100
|
return {
|
|
67
101
|
type: "resource",
|
|
68
|
-
resource
|
|
102
|
+
resource: resource2
|
|
69
103
|
};
|
|
70
104
|
}
|
|
71
105
|
__name(createAppsSdkResource, "createAppsSdkResource");
|
|
@@ -161,11 +195,11 @@ async function adaptConnectMiddleware(connectMiddleware, middlewarePath) {
|
|
|
161
195
|
const httpMocks = await import("node-mocks-http");
|
|
162
196
|
createRequest = httpMocks.createRequest;
|
|
163
197
|
createResponse = httpMocks.createResponse;
|
|
164
|
-
} catch (
|
|
198
|
+
} catch (error2) {
|
|
165
199
|
console.error(
|
|
166
200
|
"[WIDGETS] node-mocks-http not available. Install connect and node-mocks-http for Vite middleware support."
|
|
167
201
|
);
|
|
168
|
-
throw
|
|
202
|
+
throw error2;
|
|
169
203
|
}
|
|
170
204
|
let normalizedPath = middlewarePath;
|
|
171
205
|
if (normalizedPath.endsWith("*")) {
|
|
@@ -263,16 +297,16 @@ async function adaptConnectMiddleware(connectMiddleware, middlewarePath) {
|
|
|
263
297
|
__name(adaptConnectMiddleware, "adaptConnectMiddleware");
|
|
264
298
|
|
|
265
299
|
// src/server/logging.ts
|
|
266
|
-
var
|
|
267
|
-
function
|
|
268
|
-
if (
|
|
300
|
+
var isDeno2 = typeof globalThis.Deno !== "undefined";
|
|
301
|
+
function getEnv2(key) {
|
|
302
|
+
if (isDeno2) {
|
|
269
303
|
return globalThis.Deno.env.get(key);
|
|
270
304
|
}
|
|
271
305
|
return typeof process !== "undefined" && process.env ? process.env[key] : void 0;
|
|
272
306
|
}
|
|
273
|
-
__name(
|
|
307
|
+
__name(getEnv2, "getEnv");
|
|
274
308
|
function isDebugMode() {
|
|
275
|
-
const debugEnv =
|
|
309
|
+
const debugEnv = getEnv2("DEBUG");
|
|
276
310
|
return debugEnv !== void 0 && debugEnv !== "" && debugEnv !== "0" && debugEnv.toLowerCase() !== "false";
|
|
277
311
|
}
|
|
278
312
|
__name(isDebugMode, "isDebugMode");
|
|
@@ -378,7 +412,7 @@ async function requestLogger(c, next) {
|
|
|
378
412
|
} else {
|
|
379
413
|
console.log("\x1B[33mResponse Body:\x1B[0m (no body)");
|
|
380
414
|
}
|
|
381
|
-
} catch (
|
|
415
|
+
} catch (error2) {
|
|
382
416
|
console.log("\x1B[33mResponse Body:\x1B[0m (unable to read)");
|
|
383
417
|
}
|
|
384
418
|
console.log("\x1B[36m" + "=".repeat(80) + "\x1B[0m\n");
|
|
@@ -387,90 +421,7 @@ async function requestLogger(c, next) {
|
|
|
387
421
|
__name(requestLogger, "requestLogger");
|
|
388
422
|
|
|
389
423
|
// src/server/mcp-server.ts
|
|
390
|
-
function generateUUID() {
|
|
391
|
-
return globalThis.crypto.randomUUID();
|
|
392
|
-
}
|
|
393
|
-
__name(generateUUID, "generateUUID");
|
|
394
424
|
var TMP_MCP_USE_DIR = ".mcp-use";
|
|
395
|
-
var isDeno2 = typeof globalThis.Deno !== "undefined";
|
|
396
|
-
function getEnv2(key) {
|
|
397
|
-
if (isDeno2) {
|
|
398
|
-
return globalThis.Deno.env.get(key);
|
|
399
|
-
}
|
|
400
|
-
return process.env[key];
|
|
401
|
-
}
|
|
402
|
-
__name(getEnv2, "getEnv");
|
|
403
|
-
function getCwd() {
|
|
404
|
-
if (isDeno2) {
|
|
405
|
-
return globalThis.Deno.cwd();
|
|
406
|
-
}
|
|
407
|
-
return process.cwd();
|
|
408
|
-
}
|
|
409
|
-
__name(getCwd, "getCwd");
|
|
410
|
-
var fsHelpers = {
|
|
411
|
-
async readFileSync(path, encoding = "utf8") {
|
|
412
|
-
if (isDeno2) {
|
|
413
|
-
return await globalThis.Deno.readTextFile(path);
|
|
414
|
-
}
|
|
415
|
-
const { readFileSync } = await import("fs");
|
|
416
|
-
const result = readFileSync(path, encoding);
|
|
417
|
-
return typeof result === "string" ? result : result.toString(encoding);
|
|
418
|
-
},
|
|
419
|
-
async readFile(path) {
|
|
420
|
-
if (isDeno2) {
|
|
421
|
-
const data = await globalThis.Deno.readFile(path);
|
|
422
|
-
return data.buffer;
|
|
423
|
-
}
|
|
424
|
-
const { readFileSync } = await import("fs");
|
|
425
|
-
const buffer = readFileSync(path);
|
|
426
|
-
return buffer.buffer.slice(
|
|
427
|
-
buffer.byteOffset,
|
|
428
|
-
buffer.byteOffset + buffer.byteLength
|
|
429
|
-
);
|
|
430
|
-
},
|
|
431
|
-
async existsSync(path) {
|
|
432
|
-
if (isDeno2) {
|
|
433
|
-
try {
|
|
434
|
-
await globalThis.Deno.stat(path);
|
|
435
|
-
return true;
|
|
436
|
-
} catch {
|
|
437
|
-
return false;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
const { existsSync } = await import("fs");
|
|
441
|
-
return existsSync(path);
|
|
442
|
-
},
|
|
443
|
-
async readdirSync(path) {
|
|
444
|
-
if (isDeno2) {
|
|
445
|
-
const entries = [];
|
|
446
|
-
for await (const entry of globalThis.Deno.readDir(path)) {
|
|
447
|
-
entries.push(entry.name);
|
|
448
|
-
}
|
|
449
|
-
return entries;
|
|
450
|
-
}
|
|
451
|
-
const { readdirSync } = await import("fs");
|
|
452
|
-
return readdirSync(path);
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
var pathHelpers = {
|
|
456
|
-
join(...paths) {
|
|
457
|
-
if (isDeno2) {
|
|
458
|
-
return paths.join("/").replace(/\/+/g, "/");
|
|
459
|
-
}
|
|
460
|
-
return paths.join("/").replace(/\/+/g, "/");
|
|
461
|
-
},
|
|
462
|
-
relative(from, to) {
|
|
463
|
-
const fromParts = from.split("/").filter((p) => p);
|
|
464
|
-
const toParts = to.split("/").filter((p) => p);
|
|
465
|
-
let i = 0;
|
|
466
|
-
while (i < fromParts.length && i < toParts.length && fromParts[i] === toParts[i]) {
|
|
467
|
-
i++;
|
|
468
|
-
}
|
|
469
|
-
const upCount = fromParts.length - i;
|
|
470
|
-
const relativeParts = [...Array(upCount).fill(".."), ...toParts.slice(i)];
|
|
471
|
-
return relativeParts.join("/");
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
425
|
var McpServer = class {
|
|
475
426
|
static {
|
|
476
427
|
__name(this, "McpServer");
|
|
@@ -489,6 +440,13 @@ var McpServer = class {
|
|
|
489
440
|
buildId;
|
|
490
441
|
sessions = /* @__PURE__ */ new Map();
|
|
491
442
|
idleCleanupInterval;
|
|
443
|
+
oauthProvider;
|
|
444
|
+
// OAuthProvider from oauth/index.js
|
|
445
|
+
oauthMiddleware;
|
|
446
|
+
// Bearer auth middleware
|
|
447
|
+
oauthConfig;
|
|
448
|
+
// Store OAuth config for lazy initialization
|
|
449
|
+
oauthSetupComplete = false;
|
|
492
450
|
/**
|
|
493
451
|
* Creates a new MCP server instance with Hono integration
|
|
494
452
|
*
|
|
@@ -527,6 +485,9 @@ var McpServer = class {
|
|
|
527
485
|
})
|
|
528
486
|
);
|
|
529
487
|
this.app.use("*", requestLogger);
|
|
488
|
+
if (config.oauth) {
|
|
489
|
+
this.oauthConfig = config.oauth;
|
|
490
|
+
}
|
|
530
491
|
return new Proxy(this, {
|
|
531
492
|
get(target, prop) {
|
|
532
493
|
if (prop === "use") {
|
|
@@ -586,7 +547,7 @@ var McpServer = class {
|
|
|
586
547
|
if (this.serverBaseUrl) {
|
|
587
548
|
return this.serverBaseUrl;
|
|
588
549
|
}
|
|
589
|
-
const mcpUrl =
|
|
550
|
+
const mcpUrl = getEnv("MCP_URL");
|
|
590
551
|
if (mcpUrl) {
|
|
591
552
|
return mcpUrl;
|
|
592
553
|
}
|
|
@@ -598,7 +559,7 @@ var McpServer = class {
|
|
|
598
559
|
* @returns Array of URLs to add to CSP resource_domains
|
|
599
560
|
*/
|
|
600
561
|
getCSPUrls() {
|
|
601
|
-
const cspUrlsEnv =
|
|
562
|
+
const cspUrlsEnv = getEnv("CSP_URLS");
|
|
602
563
|
if (!cspUrlsEnv) {
|
|
603
564
|
console.log("[CSP] No CSP_URLS environment variable found");
|
|
604
565
|
return [];
|
|
@@ -607,6 +568,42 @@ var McpServer = class {
|
|
|
607
568
|
console.log("[CSP] Parsed CSP URLs:", urls);
|
|
608
569
|
return urls;
|
|
609
570
|
}
|
|
571
|
+
/**
|
|
572
|
+
* Setup OAuth authentication
|
|
573
|
+
*
|
|
574
|
+
* Initializes OAuth provider, creates bearer auth middleware,
|
|
575
|
+
* sets up OAuth routes, and applies auth to /mcp endpoints.
|
|
576
|
+
*
|
|
577
|
+
* @private
|
|
578
|
+
*/
|
|
579
|
+
async setupOAuth(oauthProvider) {
|
|
580
|
+
if (this.oauthSetupComplete) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const { setupOAuthRoutes, createBearerAuthMiddleware } = await import("../../oauth-CNGBFOZW.js");
|
|
584
|
+
this.oauthProvider = oauthProvider;
|
|
585
|
+
console.log(`[OAuth] OAuth provider initialized`);
|
|
586
|
+
const baseUrl = this.getServerBaseUrl();
|
|
587
|
+
this.oauthMiddleware = createBearerAuthMiddleware(
|
|
588
|
+
this.oauthProvider,
|
|
589
|
+
baseUrl
|
|
590
|
+
);
|
|
591
|
+
setupOAuthRoutes(this.app, this.oauthProvider, baseUrl);
|
|
592
|
+
const mode = this.oauthProvider.getMode?.() || "proxy";
|
|
593
|
+
if (mode === "direct") {
|
|
594
|
+
console.log(
|
|
595
|
+
"[OAuth] Direct mode: Clients will authenticate with provider directly"
|
|
596
|
+
);
|
|
597
|
+
console.log("[OAuth] Metadata endpoints: /.well-known/*");
|
|
598
|
+
} else {
|
|
599
|
+
console.log(
|
|
600
|
+
"[OAuth] Proxy mode: Routes at /authorize, /token, /.well-known/*"
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
this.app.use("/mcp/*", this.oauthMiddleware);
|
|
604
|
+
console.log("[OAuth] Bearer authentication enabled on /mcp routes");
|
|
605
|
+
this.oauthSetupComplete = true;
|
|
606
|
+
}
|
|
610
607
|
/**
|
|
611
608
|
* Define a static resource that can be accessed by clients
|
|
612
609
|
*
|
|
@@ -733,46 +730,22 @@ var McpServer = class {
|
|
|
733
730
|
this.registeredResources.push(resourceTemplateDefinition.name);
|
|
734
731
|
return this;
|
|
735
732
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
*
|
|
753
|
-
* @example
|
|
754
|
-
* ```typescript
|
|
755
|
-
* server.tool({
|
|
756
|
-
* name: 'calculate',
|
|
757
|
-
* description: 'Performs mathematical calculations',
|
|
758
|
-
* inputs: [
|
|
759
|
-
* { name: 'expression', type: 'string', required: true },
|
|
760
|
-
* { name: 'precision', type: 'number', required: false }
|
|
761
|
-
* ],
|
|
762
|
-
* cb: async ({ expression, precision = 2 }) => {
|
|
763
|
-
* const result = eval(expression)
|
|
764
|
-
* return { result: Number(result.toFixed(precision)) }
|
|
765
|
-
* },
|
|
766
|
-
* _meta: {
|
|
767
|
-
* 'openai/outputTemplate': 'ui://widgets/calculator',
|
|
768
|
-
* 'openai/toolInvocation/invoking': 'Calculating...',
|
|
769
|
-
* 'openai/toolInvocation/invoked': 'Calculation complete'
|
|
770
|
-
* }
|
|
771
|
-
* })
|
|
772
|
-
* ```
|
|
773
|
-
*/
|
|
774
|
-
tool(toolDefinition) {
|
|
775
|
-
const inputSchema = this.createParamsSchema(toolDefinition.inputs || []);
|
|
733
|
+
// Implementation
|
|
734
|
+
tool(toolDefinition, callback) {
|
|
735
|
+
const actualCallback = callback || toolDefinition.cb;
|
|
736
|
+
if (!actualCallback) {
|
|
737
|
+
throw new Error(
|
|
738
|
+
`Tool '${toolDefinition.name}' must have either a cb property or a callback parameter`
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
let inputSchema;
|
|
742
|
+
if (toolDefinition.schema) {
|
|
743
|
+
inputSchema = this.convertZodSchemaToParams(toolDefinition.schema);
|
|
744
|
+
} else if (toolDefinition.inputs && toolDefinition.inputs.length > 0) {
|
|
745
|
+
inputSchema = this.createParamsSchema(toolDefinition.inputs);
|
|
746
|
+
} else {
|
|
747
|
+
inputSchema = {};
|
|
748
|
+
}
|
|
776
749
|
this.server.registerTool(
|
|
777
750
|
toolDefinition.name,
|
|
778
751
|
{
|
|
@@ -783,101 +756,95 @@ var McpServer = class {
|
|
|
783
756
|
_meta: toolDefinition._meta
|
|
784
757
|
},
|
|
785
758
|
async (params, extra) => {
|
|
759
|
+
let requestContext = getRequestContext();
|
|
760
|
+
if (!requestContext) {
|
|
761
|
+
for (const [, session] of this.sessions.entries()) {
|
|
762
|
+
if (session.context) {
|
|
763
|
+
requestContext = session.context;
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
786
768
|
const progressToken = extra?._meta?.progressToken;
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
onProgress
|
|
807
|
-
} = options ?? {};
|
|
808
|
-
let progressCount = 0;
|
|
809
|
-
let completed = false;
|
|
810
|
-
let progressInterval = null;
|
|
811
|
-
if (progressToken && extra?.sendNotification) {
|
|
812
|
-
progressInterval = setInterval(async () => {
|
|
813
|
-
if (completed) return;
|
|
814
|
-
progressCount++;
|
|
815
|
-
const progressData = {
|
|
816
|
-
progress: progressCount,
|
|
817
|
-
total: void 0,
|
|
818
|
-
message: `Waiting for LLM response... (${progressCount * Math.round(progressIntervalMs / 1e3)}s elapsed)`
|
|
819
|
-
};
|
|
820
|
-
if (onProgress) {
|
|
821
|
-
try {
|
|
822
|
-
onProgress(progressData);
|
|
823
|
-
} catch {
|
|
824
|
-
}
|
|
825
|
-
}
|
|
769
|
+
const enhancedContext = requestContext ? Object.create(requestContext) : {};
|
|
770
|
+
enhancedContext.sample = async (sampleParams, options) => {
|
|
771
|
+
const {
|
|
772
|
+
timeout,
|
|
773
|
+
progressIntervalMs = 5e3,
|
|
774
|
+
onProgress
|
|
775
|
+
} = options ?? {};
|
|
776
|
+
let progressCount = 0;
|
|
777
|
+
let completed = false;
|
|
778
|
+
let progressInterval = null;
|
|
779
|
+
if (progressToken && extra?.sendNotification) {
|
|
780
|
+
progressInterval = setInterval(async () => {
|
|
781
|
+
if (completed) return;
|
|
782
|
+
progressCount++;
|
|
783
|
+
const progressData = {
|
|
784
|
+
progress: progressCount,
|
|
785
|
+
total: void 0,
|
|
786
|
+
message: `Waiting for LLM response... (${progressCount * Math.round(progressIntervalMs / 1e3)}s elapsed)`
|
|
787
|
+
};
|
|
788
|
+
if (onProgress) {
|
|
826
789
|
try {
|
|
827
|
-
|
|
828
|
-
method: "notifications/progress",
|
|
829
|
-
params: {
|
|
830
|
-
progressToken,
|
|
831
|
-
progress: progressData.progress,
|
|
832
|
-
total: progressData.total,
|
|
833
|
-
message: progressData.message
|
|
834
|
-
}
|
|
835
|
-
});
|
|
790
|
+
onProgress(progressData);
|
|
836
791
|
} catch {
|
|
837
792
|
}
|
|
838
|
-
}, progressIntervalMs);
|
|
839
|
-
}
|
|
840
|
-
try {
|
|
841
|
-
const samplePromise = this.createMessage(sampleParams);
|
|
842
|
-
if (timeout && timeout !== Infinity) {
|
|
843
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
844
|
-
setTimeout(
|
|
845
|
-
() => reject(
|
|
846
|
-
new Error(`Sampling timed out after ${timeout}ms`)
|
|
847
|
-
),
|
|
848
|
-
timeout
|
|
849
|
-
);
|
|
850
|
-
});
|
|
851
|
-
return await Promise.race([samplePromise, timeoutPromise]);
|
|
852
793
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
794
|
+
try {
|
|
795
|
+
await extra.sendNotification({
|
|
796
|
+
method: "notifications/progress",
|
|
797
|
+
params: {
|
|
798
|
+
progressToken,
|
|
799
|
+
progress: progressData.progress,
|
|
800
|
+
total: progressData.total,
|
|
801
|
+
message: progressData.message
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
} catch {
|
|
858
805
|
}
|
|
806
|
+
}, progressIntervalMs);
|
|
807
|
+
}
|
|
808
|
+
try {
|
|
809
|
+
const samplePromise = this.createMessage(sampleParams);
|
|
810
|
+
if (timeout && timeout !== Infinity) {
|
|
811
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
812
|
+
setTimeout(
|
|
813
|
+
() => reject(new Error(`Sampling timed out after ${timeout}ms`)),
|
|
814
|
+
timeout
|
|
815
|
+
);
|
|
816
|
+
});
|
|
817
|
+
return await Promise.race([samplePromise, timeoutPromise]);
|
|
859
818
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
method: "notifications/progress",
|
|
868
|
-
params: {
|
|
869
|
-
progressToken,
|
|
870
|
-
progress,
|
|
871
|
-
total,
|
|
872
|
-
message
|
|
873
|
-
}
|
|
874
|
-
});
|
|
875
|
-
} : void 0
|
|
819
|
+
return await samplePromise;
|
|
820
|
+
} finally {
|
|
821
|
+
completed = true;
|
|
822
|
+
if (progressInterval) {
|
|
823
|
+
clearInterval(progressInterval);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
876
826
|
};
|
|
877
|
-
|
|
878
|
-
|
|
827
|
+
enhancedContext.reportProgress = progressToken && extra?.sendNotification ? async (progress, total, message) => {
|
|
828
|
+
await extra.sendNotification({
|
|
829
|
+
method: "notifications/progress",
|
|
830
|
+
params: {
|
|
831
|
+
progressToken,
|
|
832
|
+
progress,
|
|
833
|
+
total,
|
|
834
|
+
message
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
} : void 0;
|
|
838
|
+
const executeCallback = /* @__PURE__ */ __name(async () => {
|
|
839
|
+
if (actualCallback.length >= 2) {
|
|
840
|
+
return await actualCallback(params, enhancedContext);
|
|
841
|
+
}
|
|
842
|
+
return await actualCallback(params);
|
|
843
|
+
}, "executeCallback");
|
|
844
|
+
if (requestContext) {
|
|
845
|
+
return await runWithContext(requestContext, executeCallback);
|
|
879
846
|
}
|
|
880
|
-
return await
|
|
847
|
+
return await executeCallback();
|
|
881
848
|
}
|
|
882
849
|
);
|
|
883
850
|
this.registeredTools.push(toolDefinition.name);
|
|
@@ -1228,35 +1195,6 @@ var McpServer = class {
|
|
|
1228
1195
|
}
|
|
1229
1196
|
return `ui://widget/${parts.join("-")}${extension}`;
|
|
1230
1197
|
}
|
|
1231
|
-
/**
|
|
1232
|
-
* Build a complete URL for a widget including query parameters
|
|
1233
|
-
*
|
|
1234
|
-
* Constructs the full URL to access a widget's iframe, encoding any provided
|
|
1235
|
-
* parameters as query string parameters. Complex objects are JSON-stringified
|
|
1236
|
-
* for transmission.
|
|
1237
|
-
*
|
|
1238
|
-
* @private
|
|
1239
|
-
* @param widget - Widget name/identifier
|
|
1240
|
-
* @param params - Parameters to encode in the URL
|
|
1241
|
-
* @returns Complete URL with encoded parameters
|
|
1242
|
-
*/
|
|
1243
|
-
buildWidgetUrl(widget, params) {
|
|
1244
|
-
const baseUrl = `http://${this.serverHost}:${this.serverPort}/mcp-use/widgets/${widget}`;
|
|
1245
|
-
if (Object.keys(params).length === 0) {
|
|
1246
|
-
return baseUrl;
|
|
1247
|
-
}
|
|
1248
|
-
const queryParams = new URLSearchParams();
|
|
1249
|
-
for (const [key, value] of Object.entries(params)) {
|
|
1250
|
-
if (value !== void 0 && value !== null) {
|
|
1251
|
-
if (typeof value === "object") {
|
|
1252
|
-
queryParams.append(key, JSON.stringify(value));
|
|
1253
|
-
} else {
|
|
1254
|
-
queryParams.append(key, String(value));
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
return `${baseUrl}?${queryParams.toString()}`;
|
|
1259
|
-
}
|
|
1260
1198
|
/**
|
|
1261
1199
|
* Convert widget props definition to tool input schema
|
|
1262
1200
|
*
|
|
@@ -1304,7 +1242,7 @@ var McpServer = class {
|
|
|
1304
1242
|
* @returns true if in production mode, false otherwise
|
|
1305
1243
|
*/
|
|
1306
1244
|
isProductionMode() {
|
|
1307
|
-
return
|
|
1245
|
+
return getEnv("NODE_ENV") === "production";
|
|
1308
1246
|
}
|
|
1309
1247
|
/**
|
|
1310
1248
|
* Read build manifest file
|
|
@@ -1315,7 +1253,7 @@ var McpServer = class {
|
|
|
1315
1253
|
async readBuildManifest() {
|
|
1316
1254
|
try {
|
|
1317
1255
|
const manifestPath = pathHelpers.join(
|
|
1318
|
-
|
|
1256
|
+
isDeno ? "." : getCwd(),
|
|
1319
1257
|
"dist",
|
|
1320
1258
|
"mcp-use.json"
|
|
1321
1259
|
);
|
|
@@ -1337,7 +1275,7 @@ var McpServer = class {
|
|
|
1337
1275
|
* @returns Promise that resolves when all widgets are mounted
|
|
1338
1276
|
*/
|
|
1339
1277
|
async mountWidgets(options) {
|
|
1340
|
-
if (this.isProductionMode() ||
|
|
1278
|
+
if (this.isProductionMode() || isDeno) {
|
|
1341
1279
|
console.log("[WIDGETS] Mounting widgets in production mode");
|
|
1342
1280
|
await this.mountWidgetsProduction(options);
|
|
1343
1281
|
} else {
|
|
@@ -1365,7 +1303,7 @@ var McpServer = class {
|
|
|
1365
1303
|
const srcDir = pathHelpers.join(getCwd(), resourcesDir);
|
|
1366
1304
|
try {
|
|
1367
1305
|
await fs.access(srcDir);
|
|
1368
|
-
} catch (
|
|
1306
|
+
} catch (error2) {
|
|
1369
1307
|
console.log(
|
|
1370
1308
|
`[WIDGETS] No ${resourcesDir}/ directory found - skipping widget serving`
|
|
1371
1309
|
);
|
|
@@ -1399,7 +1337,7 @@ var McpServer = class {
|
|
|
1399
1337
|
}
|
|
1400
1338
|
}
|
|
1401
1339
|
}
|
|
1402
|
-
} catch (
|
|
1340
|
+
} catch (error2) {
|
|
1403
1341
|
console.log(`[WIDGETS] No widgets found in ${resourcesDir}/ directory`);
|
|
1404
1342
|
return;
|
|
1405
1343
|
}
|
|
@@ -1424,7 +1362,7 @@ var McpServer = class {
|
|
|
1424
1362
|
'return import("@tailwindcss/vite")'
|
|
1425
1363
|
)();
|
|
1426
1364
|
tailwindcss = tailwindModule.default;
|
|
1427
|
-
} catch (
|
|
1365
|
+
} catch (error2) {
|
|
1428
1366
|
console.error(
|
|
1429
1367
|
"[WIDGETS] Dev dependencies not available. Install vite, @vitejs/plugin-react, and @tailwindcss/vite for widget development."
|
|
1430
1368
|
);
|
|
@@ -1440,8 +1378,8 @@ var McpServer = class {
|
|
|
1440
1378
|
entry: entry.path
|
|
1441
1379
|
};
|
|
1442
1380
|
});
|
|
1443
|
-
for (const
|
|
1444
|
-
const widgetTempDir = pathHelpers.join(tempDir,
|
|
1381
|
+
for (const widget2 of widgets) {
|
|
1382
|
+
const widgetTempDir = pathHelpers.join(tempDir, widget2.name);
|
|
1445
1383
|
await fs.mkdir(widgetTempDir, { recursive: true });
|
|
1446
1384
|
const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
|
|
1447
1385
|
const relativeResourcesPath = pathHelpers.relative(widgetTempDir, resourcesPath).replace(/\\/g, "/");
|
|
@@ -1458,7 +1396,7 @@ var McpServer = class {
|
|
|
1458
1396
|
const entryContent = `import React from 'react'
|
|
1459
1397
|
import { createRoot } from 'react-dom/client'
|
|
1460
1398
|
import './styles.css'
|
|
1461
|
-
import Component from '${
|
|
1399
|
+
import Component from '${widget2.entry}'
|
|
1462
1400
|
|
|
1463
1401
|
const container = document.getElementById('widget-root')
|
|
1464
1402
|
if (container && Component) {
|
|
@@ -1471,11 +1409,11 @@ if (container && Component) {
|
|
|
1471
1409
|
<head>
|
|
1472
1410
|
<meta charset="UTF-8" />
|
|
1473
1411
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
1474
|
-
<title>${
|
|
1412
|
+
<title>${widget2.name} Widget</title>
|
|
1475
1413
|
</head>
|
|
1476
1414
|
<body>
|
|
1477
1415
|
<div id="widget-root"></div>
|
|
1478
|
-
<script type="module" src="${baseRoute}/${
|
|
1416
|
+
<script type="module" src="${baseRoute}/${widget2.name}/entry.tsx"></script>
|
|
1479
1417
|
</body>
|
|
1480
1418
|
</html>`;
|
|
1481
1419
|
await fs.writeFile(
|
|
@@ -1565,8 +1503,8 @@ if (container && Component) {
|
|
|
1565
1503
|
const widgetMatch = pathname.replace(baseRoute, "").match(/^\/([^/]+)/);
|
|
1566
1504
|
if (widgetMatch) {
|
|
1567
1505
|
const widgetName = widgetMatch[1];
|
|
1568
|
-
const
|
|
1569
|
-
if (
|
|
1506
|
+
const widget2 = widgets.find((w) => w.name === widgetName);
|
|
1507
|
+
if (widget2) {
|
|
1570
1508
|
const relativePath = pathname.replace(baseRoute, "");
|
|
1571
1509
|
if (relativePath === `/${widgetName}` || relativePath === `/${widgetName}/`) {
|
|
1572
1510
|
const newUrl = new URL(c.req.url);
|
|
@@ -1617,42 +1555,42 @@ if (container && Component) {
|
|
|
1617
1555
|
const message = isAsset ? "Widget asset not found" : "Widget not found";
|
|
1618
1556
|
return c.text(message, 404);
|
|
1619
1557
|
});
|
|
1620
|
-
widgets.forEach((
|
|
1558
|
+
widgets.forEach((widget2) => {
|
|
1621
1559
|
console.log(
|
|
1622
|
-
`[WIDGET] ${
|
|
1560
|
+
`[WIDGET] ${widget2.name} mounted at ${baseRoute}/${widget2.name}`
|
|
1623
1561
|
);
|
|
1624
1562
|
});
|
|
1625
|
-
for (const
|
|
1563
|
+
for (const widget2 of widgets) {
|
|
1626
1564
|
const type = "appsSdk";
|
|
1627
1565
|
let metadata = {};
|
|
1628
1566
|
let props = {};
|
|
1629
|
-
let description =
|
|
1567
|
+
let description = widget2.description;
|
|
1630
1568
|
try {
|
|
1631
|
-
const mod = await viteServer.ssrLoadModule(
|
|
1569
|
+
const mod = await viteServer.ssrLoadModule(widget2.entry);
|
|
1632
1570
|
if (mod.widgetMetadata) {
|
|
1633
1571
|
metadata = mod.widgetMetadata;
|
|
1634
|
-
description = metadata.description ||
|
|
1572
|
+
description = metadata.description || widget2.description;
|
|
1635
1573
|
if (metadata.inputs) {
|
|
1636
1574
|
try {
|
|
1637
1575
|
props = metadata.inputs.shape || {};
|
|
1638
|
-
} catch (
|
|
1576
|
+
} catch (error2) {
|
|
1639
1577
|
console.warn(
|
|
1640
|
-
`[WIDGET] Failed to extract props schema for ${
|
|
1641
|
-
|
|
1578
|
+
`[WIDGET] Failed to extract props schema for ${widget2.name}:`,
|
|
1579
|
+
error2
|
|
1642
1580
|
);
|
|
1643
1581
|
}
|
|
1644
1582
|
}
|
|
1645
1583
|
}
|
|
1646
|
-
} catch (
|
|
1584
|
+
} catch (error2) {
|
|
1647
1585
|
console.warn(
|
|
1648
|
-
`[WIDGET] Failed to load metadata for ${
|
|
1649
|
-
|
|
1586
|
+
`[WIDGET] Failed to load metadata for ${widget2.name}:`,
|
|
1587
|
+
error2
|
|
1650
1588
|
);
|
|
1651
1589
|
}
|
|
1652
1590
|
let html = "";
|
|
1653
1591
|
try {
|
|
1654
1592
|
html = await fsHelpers.readFileSync(
|
|
1655
|
-
pathHelpers.join(tempDir,
|
|
1593
|
+
pathHelpers.join(tempDir, widget2.name, "index.html"),
|
|
1656
1594
|
"utf8"
|
|
1657
1595
|
);
|
|
1658
1596
|
const mcpUrl = this.getServerBaseUrl();
|
|
@@ -1690,25 +1628,25 @@ if (container && Component) {
|
|
|
1690
1628
|
html = html.replace(
|
|
1691
1629
|
/<head[^>]*>/i,
|
|
1692
1630
|
`<head>
|
|
1693
|
-
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${
|
|
1631
|
+
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${widget2.name}/"+filename }; window.__mcpPublicUrl = "${baseUrl}/mcp-use/public";</script>`
|
|
1694
1632
|
);
|
|
1695
|
-
} catch (
|
|
1633
|
+
} catch (error2) {
|
|
1696
1634
|
console.error(
|
|
1697
|
-
`Failed to read html template for widget ${
|
|
1698
|
-
|
|
1635
|
+
`Failed to read html template for widget ${widget2.name}`,
|
|
1636
|
+
error2
|
|
1699
1637
|
);
|
|
1700
1638
|
}
|
|
1701
1639
|
const mcp_connect_domain = this.getServerBaseUrl() ? new URL(this.getServerBaseUrl() || "").origin : null;
|
|
1702
1640
|
this.uiResource({
|
|
1703
|
-
name:
|
|
1704
|
-
title: metadata.title ||
|
|
1641
|
+
name: widget2.name,
|
|
1642
|
+
title: metadata.title || widget2.name,
|
|
1705
1643
|
description,
|
|
1706
1644
|
type,
|
|
1707
1645
|
props,
|
|
1708
1646
|
_meta: {
|
|
1709
1647
|
"mcp-use/widget": {
|
|
1710
|
-
name:
|
|
1711
|
-
title: metadata.title ||
|
|
1648
|
+
name: widget2.name,
|
|
1649
|
+
title: metadata.title || widget2.name,
|
|
1712
1650
|
description,
|
|
1713
1651
|
type,
|
|
1714
1652
|
props,
|
|
@@ -1720,8 +1658,8 @@ if (container && Component) {
|
|
|
1720
1658
|
htmlTemplate: html,
|
|
1721
1659
|
appsSdkMetadata: {
|
|
1722
1660
|
"openai/widgetDescription": description,
|
|
1723
|
-
"openai/toolInvocation/invoking": `Loading ${
|
|
1724
|
-
"openai/toolInvocation/invoked": `${
|
|
1661
|
+
"openai/toolInvocation/invoking": `Loading ${widget2.name}...`,
|
|
1662
|
+
"openai/toolInvocation/invoked": `${widget2.name} ready`,
|
|
1725
1663
|
"openai/widgetAccessible": true,
|
|
1726
1664
|
"openai/resultCanProduceWidget": true,
|
|
1727
1665
|
...metadata.appsSdkMetadata || {},
|
|
@@ -1760,7 +1698,7 @@ if (container && Component) {
|
|
|
1760
1698
|
async mountWidgetsProduction(options) {
|
|
1761
1699
|
const baseRoute = options?.baseRoute || "/mcp-use/widgets";
|
|
1762
1700
|
const widgetsDir = pathHelpers.join(
|
|
1763
|
-
|
|
1701
|
+
isDeno ? "." : getCwd(),
|
|
1764
1702
|
"dist",
|
|
1765
1703
|
"resources",
|
|
1766
1704
|
"widgets"
|
|
@@ -1794,10 +1732,10 @@ if (container && Component) {
|
|
|
1794
1732
|
} else {
|
|
1795
1733
|
console.log("[WIDGETS] No widgets found in manifest");
|
|
1796
1734
|
}
|
|
1797
|
-
} catch (
|
|
1735
|
+
} catch (error2) {
|
|
1798
1736
|
console.log(
|
|
1799
1737
|
"[WIDGETS] Could not read manifest file, falling back to directory listing:",
|
|
1800
|
-
|
|
1738
|
+
error2
|
|
1801
1739
|
);
|
|
1802
1740
|
try {
|
|
1803
1741
|
const allEntries = await fsHelpers.readdirSync(widgetsDir);
|
|
@@ -1862,10 +1800,10 @@ if (container && Component) {
|
|
|
1862
1800
|
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${widgetName}/"+filename }; window.__mcpPublicUrl = "${baseUrl}/mcp-use/public";</script>`
|
|
1863
1801
|
);
|
|
1864
1802
|
}
|
|
1865
|
-
} catch (
|
|
1803
|
+
} catch (error2) {
|
|
1866
1804
|
console.error(
|
|
1867
1805
|
`[WIDGET] Failed to read ${widgetName}/index.html:`,
|
|
1868
|
-
|
|
1806
|
+
error2
|
|
1869
1807
|
);
|
|
1870
1808
|
continue;
|
|
1871
1809
|
}
|
|
@@ -1992,7 +1930,7 @@ if (container && Component) {
|
|
|
1992
1930
|
if (closeOldSessionId && this.sessions.has(closeOldSessionId)) {
|
|
1993
1931
|
try {
|
|
1994
1932
|
this.sessions.get(closeOldSessionId).transport.close();
|
|
1995
|
-
} catch (
|
|
1933
|
+
} catch (error2) {
|
|
1996
1934
|
}
|
|
1997
1935
|
this.sessions.delete(closeOldSessionId);
|
|
1998
1936
|
}
|
|
@@ -2123,7 +2061,7 @@ if (container && Component) {
|
|
|
2123
2061
|
if (now - session.lastAccessedAt > idleTimeoutMs) {
|
|
2124
2062
|
try {
|
|
2125
2063
|
session.transport.close();
|
|
2126
|
-
} catch (
|
|
2064
|
+
} catch (error2) {
|
|
2127
2065
|
}
|
|
2128
2066
|
this.sessions.delete(sessionId);
|
|
2129
2067
|
}
|
|
@@ -2221,7 +2159,7 @@ if (container && Component) {
|
|
|
2221
2159
|
getResponse: /* @__PURE__ */ __name(() => {
|
|
2222
2160
|
if (ended) {
|
|
2223
2161
|
if (responseBody.length > 0) {
|
|
2224
|
-
const body =
|
|
2162
|
+
const body = isDeno ? Buffer.concat(responseBody) : Buffer.concat(responseBody);
|
|
2225
2163
|
return new Response(body, {
|
|
2226
2164
|
status: statusCode,
|
|
2227
2165
|
headers
|
|
@@ -2279,19 +2217,23 @@ if (container && Component) {
|
|
|
2279
2217
|
}
|
|
2280
2218
|
}
|
|
2281
2219
|
if (sessionId && this.sessions.has(sessionId)) {
|
|
2282
|
-
this.sessions.get(sessionId)
|
|
2220
|
+
const session = this.sessions.get(sessionId);
|
|
2221
|
+
session.lastAccessedAt = Date.now();
|
|
2222
|
+
session.context = c;
|
|
2283
2223
|
}
|
|
2284
2224
|
if (expressRes._closeHandler) {
|
|
2285
2225
|
c.req.raw.signal?.addEventListener("abort", () => {
|
|
2286
2226
|
transport.close();
|
|
2287
2227
|
});
|
|
2288
2228
|
}
|
|
2289
|
-
await
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2229
|
+
await runWithContext(c, async () => {
|
|
2230
|
+
await this.waitForRequestComplete(
|
|
2231
|
+
transport,
|
|
2232
|
+
expressReq,
|
|
2233
|
+
expressRes,
|
|
2234
|
+
expressReq.body
|
|
2235
|
+
);
|
|
2236
|
+
});
|
|
2295
2237
|
const response = getResponse();
|
|
2296
2238
|
if (response) {
|
|
2297
2239
|
return response;
|
|
@@ -2529,12 +2471,15 @@ if (container && Component) {
|
|
|
2529
2471
|
console.log("");
|
|
2530
2472
|
}
|
|
2531
2473
|
async listen(port) {
|
|
2532
|
-
const portEnv =
|
|
2474
|
+
const portEnv = getEnv("PORT");
|
|
2533
2475
|
this.serverPort = port || (portEnv ? parseInt(portEnv, 10) : 3001);
|
|
2534
|
-
const hostEnv =
|
|
2476
|
+
const hostEnv = getEnv("HOST");
|
|
2535
2477
|
if (hostEnv) {
|
|
2536
2478
|
this.serverHost = hostEnv;
|
|
2537
2479
|
}
|
|
2480
|
+
if (this.oauthConfig && !this.oauthSetupComplete) {
|
|
2481
|
+
await this.setupOAuth(this.oauthConfig);
|
|
2482
|
+
}
|
|
2538
2483
|
await this.mountWidgets({
|
|
2539
2484
|
baseRoute: "/mcp-use/widgets",
|
|
2540
2485
|
resourcesDir: "resources"
|
|
@@ -2542,7 +2487,7 @@ if (container && Component) {
|
|
|
2542
2487
|
await this.mountMcp();
|
|
2543
2488
|
await this.mountInspector();
|
|
2544
2489
|
this.logRegisteredItems();
|
|
2545
|
-
if (
|
|
2490
|
+
if (isDeno) {
|
|
2546
2491
|
const corsHeaders = {
|
|
2547
2492
|
"Access-Control-Allow-Origin": "*",
|
|
2548
2493
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
|
|
@@ -2643,6 +2588,9 @@ if (container && Component) {
|
|
|
2643
2588
|
* ```
|
|
2644
2589
|
*/
|
|
2645
2590
|
async getHandler(options) {
|
|
2591
|
+
if (this.oauthConfig && !this.oauthSetupComplete) {
|
|
2592
|
+
await this.setupOAuth(this.oauthConfig);
|
|
2593
|
+
}
|
|
2646
2594
|
console.log("[MCP] Mounting widgets");
|
|
2647
2595
|
await this.mountWidgets({
|
|
2648
2596
|
baseRoute: "/mcp-use/widgets",
|
|
@@ -2746,10 +2694,10 @@ if (container && Component) {
|
|
|
2746
2694
|
for (const [sessionId, session] of this.sessions.entries()) {
|
|
2747
2695
|
try {
|
|
2748
2696
|
await session.transport.send(notification);
|
|
2749
|
-
} catch (
|
|
2697
|
+
} catch (error2) {
|
|
2750
2698
|
console.warn(
|
|
2751
2699
|
`[MCP] Failed to send notification to session ${sessionId}:`,
|
|
2752
|
-
|
|
2700
|
+
error2
|
|
2753
2701
|
);
|
|
2754
2702
|
}
|
|
2755
2703
|
}
|
|
@@ -2794,10 +2742,10 @@ if (container && Component) {
|
|
|
2794
2742
|
try {
|
|
2795
2743
|
await session.transport.send(notification);
|
|
2796
2744
|
return true;
|
|
2797
|
-
} catch (
|
|
2745
|
+
} catch (error2) {
|
|
2798
2746
|
console.warn(
|
|
2799
2747
|
`[MCP] Failed to send notification to session ${sessionId}:`,
|
|
2800
|
-
|
|
2748
|
+
error2
|
|
2801
2749
|
);
|
|
2802
2750
|
return false;
|
|
2803
2751
|
}
|
|
@@ -2865,10 +2813,10 @@ if (container && Component) {
|
|
|
2865
2813
|
return response.roots;
|
|
2866
2814
|
}
|
|
2867
2815
|
return [];
|
|
2868
|
-
} catch (
|
|
2816
|
+
} catch (error2) {
|
|
2869
2817
|
console.warn(
|
|
2870
2818
|
`[MCP] Failed to list roots from session ${sessionId}:`,
|
|
2871
|
-
|
|
2819
|
+
error2
|
|
2872
2820
|
);
|
|
2873
2821
|
return null;
|
|
2874
2822
|
}
|
|
@@ -2946,14 +2894,14 @@ if (container && Component) {
|
|
|
2946
2894
|
*/
|
|
2947
2895
|
setupWidgetRoutes() {
|
|
2948
2896
|
this.app.get("/mcp-use/widgets/:widget/assets/*", async (c) => {
|
|
2949
|
-
const
|
|
2897
|
+
const widget2 = c.req.param("widget");
|
|
2950
2898
|
const assetFile = c.req.path.split("/assets/")[1];
|
|
2951
2899
|
const assetPath = pathHelpers.join(
|
|
2952
2900
|
getCwd(),
|
|
2953
2901
|
"dist",
|
|
2954
2902
|
"resources",
|
|
2955
2903
|
"widgets",
|
|
2956
|
-
|
|
2904
|
+
widget2,
|
|
2957
2905
|
"assets",
|
|
2958
2906
|
assetFile
|
|
2959
2907
|
);
|
|
@@ -2982,10 +2930,10 @@ if (container && Component) {
|
|
|
2982
2930
|
);
|
|
2983
2931
|
try {
|
|
2984
2932
|
const widgets = await fsHelpers.readdirSync(widgetsDir);
|
|
2985
|
-
for (const
|
|
2933
|
+
for (const widget2 of widgets) {
|
|
2986
2934
|
const assetPath = pathHelpers.join(
|
|
2987
2935
|
widgetsDir,
|
|
2988
|
-
|
|
2936
|
+
widget2,
|
|
2989
2937
|
"assets",
|
|
2990
2938
|
assetFile
|
|
2991
2939
|
);
|
|
@@ -3005,13 +2953,13 @@ if (container && Component) {
|
|
|
3005
2953
|
}
|
|
3006
2954
|
});
|
|
3007
2955
|
this.app.get("/mcp-use/widgets/:widget", async (c) => {
|
|
3008
|
-
const
|
|
2956
|
+
const widget2 = c.req.param("widget");
|
|
3009
2957
|
const filePath = pathHelpers.join(
|
|
3010
2958
|
getCwd(),
|
|
3011
2959
|
"dist",
|
|
3012
2960
|
"resources",
|
|
3013
2961
|
"widgets",
|
|
3014
|
-
|
|
2962
|
+
widget2,
|
|
3015
2963
|
"index.html"
|
|
3016
2964
|
);
|
|
3017
2965
|
try {
|
|
@@ -3028,7 +2976,7 @@ if (container && Component) {
|
|
|
3028
2976
|
html = html.replace(
|
|
3029
2977
|
/<head[^>]*>/i,
|
|
3030
2978
|
`<head>
|
|
3031
|
-
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${
|
|
2979
|
+
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${widget2}/"+filename }</script>`
|
|
3032
2980
|
);
|
|
3033
2981
|
return c.html(html);
|
|
3034
2982
|
} catch {
|
|
@@ -3055,28 +3003,21 @@ if (container && Component) {
|
|
|
3055
3003
|
});
|
|
3056
3004
|
}
|
|
3057
3005
|
/**
|
|
3058
|
-
*
|
|
3059
|
-
*
|
|
3060
|
-
* Parses a URI template string to extract parameter names and generates a Zod
|
|
3061
|
-
* validation schema for those parameters. Used internally for validating resource
|
|
3062
|
-
* template parameters before processing requests.
|
|
3063
|
-
*
|
|
3064
|
-
* @param uriTemplate - URI template string with parameter placeholders (e.g., "/users/{id}/posts/{postId}")
|
|
3065
|
-
* @returns Object mapping parameter names to Zod string schemas
|
|
3006
|
+
* Convert a Zod object schema to the internal Record<string, z.ZodSchema> format
|
|
3066
3007
|
*
|
|
3067
|
-
* @
|
|
3068
|
-
*
|
|
3069
|
-
* const schema = this.createInputSchema("/users/{id}/posts/{postId}")
|
|
3070
|
-
* // Returns: { id: z.string(), postId: z.string() }
|
|
3071
|
-
* ```
|
|
3008
|
+
* @param zodSchema - Zod object schema to convert
|
|
3009
|
+
* @returns Object mapping parameter names to Zod validation schemas
|
|
3072
3010
|
*/
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
}
|
|
3079
|
-
|
|
3011
|
+
convertZodSchemaToParams(zodSchema) {
|
|
3012
|
+
if (!(zodSchema instanceof z.ZodObject)) {
|
|
3013
|
+
throw new Error("schema must be a Zod object schema (z.object({...}))");
|
|
3014
|
+
}
|
|
3015
|
+
const shape = zodSchema.shape;
|
|
3016
|
+
const params = {};
|
|
3017
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
3018
|
+
params[key] = value;
|
|
3019
|
+
}
|
|
3020
|
+
return params;
|
|
3080
3021
|
}
|
|
3081
3022
|
/**
|
|
3082
3023
|
* Create input schema for tools
|
|
@@ -3130,75 +3071,6 @@ if (container && Component) {
|
|
|
3130
3071
|
});
|
|
3131
3072
|
return schema;
|
|
3132
3073
|
}
|
|
3133
|
-
/**
|
|
3134
|
-
* Create arguments schema for prompts
|
|
3135
|
-
*
|
|
3136
|
-
* Converts prompt argument definitions into Zod validation schemas for runtime validation.
|
|
3137
|
-
* Supports common data types (string, number, boolean, object, array) and optional
|
|
3138
|
-
* parameters. Used internally when registering prompt templates with the MCP server.
|
|
3139
|
-
*
|
|
3140
|
-
* @param inputs - Array of argument definitions with name, type, and optional flag
|
|
3141
|
-
* @returns Object mapping argument names to Zod validation schemas
|
|
3142
|
-
*
|
|
3143
|
-
* @example
|
|
3144
|
-
* ```typescript
|
|
3145
|
-
* const schema = this.createPromptArgsSchema([
|
|
3146
|
-
* { name: 'topic', type: 'string', required: true },
|
|
3147
|
-
* { name: 'style', type: 'string', required: false }
|
|
3148
|
-
* ])
|
|
3149
|
-
* // Returns: { topic: z.string(), style: z.string().optional() }
|
|
3150
|
-
* ```
|
|
3151
|
-
*/
|
|
3152
|
-
createPromptArgsSchema(inputs) {
|
|
3153
|
-
const schema = {};
|
|
3154
|
-
inputs.forEach((input) => {
|
|
3155
|
-
let zodType;
|
|
3156
|
-
switch (input.type) {
|
|
3157
|
-
case "string":
|
|
3158
|
-
zodType = z.string();
|
|
3159
|
-
break;
|
|
3160
|
-
case "number":
|
|
3161
|
-
zodType = z.number();
|
|
3162
|
-
break;
|
|
3163
|
-
case "boolean":
|
|
3164
|
-
zodType = z.boolean();
|
|
3165
|
-
break;
|
|
3166
|
-
case "object":
|
|
3167
|
-
zodType = z.object({});
|
|
3168
|
-
break;
|
|
3169
|
-
case "array":
|
|
3170
|
-
zodType = z.array(z.any());
|
|
3171
|
-
break;
|
|
3172
|
-
default:
|
|
3173
|
-
zodType = z.any();
|
|
3174
|
-
}
|
|
3175
|
-
if (!input.required) {
|
|
3176
|
-
zodType = zodType.optional();
|
|
3177
|
-
}
|
|
3178
|
-
schema[input.name] = zodType;
|
|
3179
|
-
});
|
|
3180
|
-
return schema;
|
|
3181
|
-
}
|
|
3182
|
-
/**
|
|
3183
|
-
* Extract parameter names from URI template
|
|
3184
|
-
*
|
|
3185
|
-
* Parses a URI template string to extract parameter names enclosed in curly braces.
|
|
3186
|
-
* Used internally to identify dynamic parameters in resource templates and generate
|
|
3187
|
-
* appropriate validation schemas.
|
|
3188
|
-
*
|
|
3189
|
-
* @param uriTemplate - URI template string with parameter placeholders (e.g., "/users/{id}/posts/{postId}")
|
|
3190
|
-
* @returns Array of parameter names found in the template
|
|
3191
|
-
*
|
|
3192
|
-
* @example
|
|
3193
|
-
* ```typescript
|
|
3194
|
-
* const params = this.extractTemplateParams("/users/{id}/posts/{postId}")
|
|
3195
|
-
* // Returns: ["id", "postId"]
|
|
3196
|
-
* ```
|
|
3197
|
-
*/
|
|
3198
|
-
extractTemplateParams(uriTemplate) {
|
|
3199
|
-
const matches = uriTemplate.match(/\{([^}]+)\}/g);
|
|
3200
|
-
return matches ? matches.map((match) => match.slice(1, -1)) : [];
|
|
3201
|
-
}
|
|
3202
3074
|
/**
|
|
3203
3075
|
* Parse parameter values from a URI based on a template
|
|
3204
3076
|
*
|
|
@@ -3241,19 +3113,155 @@ function createMCPServer(name, config = {}) {
|
|
|
3241
3113
|
host: config.host,
|
|
3242
3114
|
baseUrl: config.baseUrl,
|
|
3243
3115
|
allowedOrigins: config.allowedOrigins,
|
|
3244
|
-
sessionIdleTimeoutMs: config.sessionIdleTimeoutMs
|
|
3116
|
+
sessionIdleTimeoutMs: config.sessionIdleTimeoutMs,
|
|
3117
|
+
autoCreateSessionOnInvalidId: config.autoCreateSessionOnInvalidId,
|
|
3118
|
+
oauth: config.oauth
|
|
3245
3119
|
});
|
|
3246
3120
|
return instance;
|
|
3247
3121
|
}
|
|
3248
3122
|
__name(createMCPServer, "createMCPServer");
|
|
3123
|
+
|
|
3124
|
+
// src/server/utils/response-helpers.ts
|
|
3125
|
+
function text(content) {
|
|
3126
|
+
return {
|
|
3127
|
+
content: [
|
|
3128
|
+
{
|
|
3129
|
+
type: "text",
|
|
3130
|
+
text: content
|
|
3131
|
+
}
|
|
3132
|
+
]
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
__name(text, "text");
|
|
3136
|
+
function image(data, mimeType = "image/png") {
|
|
3137
|
+
return {
|
|
3138
|
+
content: [
|
|
3139
|
+
{
|
|
3140
|
+
type: "image",
|
|
3141
|
+
data,
|
|
3142
|
+
mimeType
|
|
3143
|
+
}
|
|
3144
|
+
]
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
__name(image, "image");
|
|
3148
|
+
function resource(uri, mimeType, text2) {
|
|
3149
|
+
const resourceContent = {
|
|
3150
|
+
type: "resource",
|
|
3151
|
+
resource: {
|
|
3152
|
+
uri,
|
|
3153
|
+
...mimeType && { mimeType },
|
|
3154
|
+
...text2 && { text: text2 }
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
return {
|
|
3158
|
+
content: [resourceContent]
|
|
3159
|
+
};
|
|
3160
|
+
}
|
|
3161
|
+
__name(resource, "resource");
|
|
3162
|
+
function error(message) {
|
|
3163
|
+
return {
|
|
3164
|
+
isError: true,
|
|
3165
|
+
content: [
|
|
3166
|
+
{
|
|
3167
|
+
type: "text",
|
|
3168
|
+
text: message
|
|
3169
|
+
}
|
|
3170
|
+
]
|
|
3171
|
+
};
|
|
3172
|
+
}
|
|
3173
|
+
__name(error, "error");
|
|
3174
|
+
function object(data) {
|
|
3175
|
+
return Array.isArray(data) ? array(data) : {
|
|
3176
|
+
content: [
|
|
3177
|
+
{
|
|
3178
|
+
type: "text",
|
|
3179
|
+
text: JSON.stringify(data, null, 2)
|
|
3180
|
+
}
|
|
3181
|
+
],
|
|
3182
|
+
structuredContent: data
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
__name(object, "object");
|
|
3186
|
+
function array(data) {
|
|
3187
|
+
return {
|
|
3188
|
+
content: [
|
|
3189
|
+
{
|
|
3190
|
+
type: "text",
|
|
3191
|
+
text: JSON.stringify(data, null, 2)
|
|
3192
|
+
}
|
|
3193
|
+
],
|
|
3194
|
+
structuredContent: { data }
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
__name(array, "array");
|
|
3198
|
+
function widget(config) {
|
|
3199
|
+
const {
|
|
3200
|
+
name,
|
|
3201
|
+
data,
|
|
3202
|
+
message,
|
|
3203
|
+
invoking,
|
|
3204
|
+
invoked,
|
|
3205
|
+
widgetAccessible = true,
|
|
3206
|
+
resultCanProduceWidget = true,
|
|
3207
|
+
buildId
|
|
3208
|
+
} = config;
|
|
3209
|
+
const randomId = Math.random().toString(36).substring(2, 15);
|
|
3210
|
+
const buildIdPart = buildId ? `-${buildId}` : "";
|
|
3211
|
+
const uniqueUri = `ui://widget/${name}${buildIdPart}-${randomId}.html`;
|
|
3212
|
+
const metadata = {
|
|
3213
|
+
"openai/outputTemplate": uniqueUri,
|
|
3214
|
+
"openai/widgetAccessible": widgetAccessible,
|
|
3215
|
+
"openai/resultCanProduceWidget": resultCanProduceWidget
|
|
3216
|
+
};
|
|
3217
|
+
if (invoking) {
|
|
3218
|
+
metadata["openai/toolInvocation/invoking"] = invoking;
|
|
3219
|
+
}
|
|
3220
|
+
if (invoked) {
|
|
3221
|
+
metadata["openai/toolInvocation/invoked"] = invoked;
|
|
3222
|
+
}
|
|
3223
|
+
const displayMessage = message || `Displaying ${name}`;
|
|
3224
|
+
return {
|
|
3225
|
+
_meta: metadata,
|
|
3226
|
+
content: [
|
|
3227
|
+
{
|
|
3228
|
+
type: "text",
|
|
3229
|
+
text: displayMessage
|
|
3230
|
+
}
|
|
3231
|
+
],
|
|
3232
|
+
// structuredContent will be injected as window.openai.toolOutput by Apps SDK
|
|
3233
|
+
structuredContent: data
|
|
3234
|
+
};
|
|
3235
|
+
}
|
|
3236
|
+
__name(widget, "widget");
|
|
3249
3237
|
export {
|
|
3250
3238
|
adaptConnectMiddleware,
|
|
3251
3239
|
adaptMiddleware,
|
|
3240
|
+
array,
|
|
3252
3241
|
buildWidgetUrl,
|
|
3253
3242
|
createExternalUrlResource,
|
|
3254
3243
|
createMCPServer,
|
|
3255
3244
|
createRawHtmlResource,
|
|
3256
3245
|
createRemoteDomResource,
|
|
3257
3246
|
createUIResourceFromDefinition,
|
|
3258
|
-
|
|
3247
|
+
error,
|
|
3248
|
+
getAuth,
|
|
3249
|
+
getRequestContext,
|
|
3250
|
+
hasAnyScope,
|
|
3251
|
+
hasRequestContext,
|
|
3252
|
+
hasScope,
|
|
3253
|
+
image,
|
|
3254
|
+
isExpressMiddleware,
|
|
3255
|
+
oauthAuth0Provider,
|
|
3256
|
+
oauthCustomProvider,
|
|
3257
|
+
oauthKeycloakProvider,
|
|
3258
|
+
oauthSupabaseProvider,
|
|
3259
|
+
oauthWorkOSProvider,
|
|
3260
|
+
object,
|
|
3261
|
+
requireAnyScope,
|
|
3262
|
+
requireScope,
|
|
3263
|
+
resource,
|
|
3264
|
+
runWithContext,
|
|
3265
|
+
text,
|
|
3266
|
+
widget
|
|
3259
3267
|
};
|