mcp-use 1.6.3-canary.0 → 1.7.0-canary.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/.tsbuildinfo +1 -1
- package/dist/chunk-3R5PDYIN.js +403 -0
- package/dist/{chunk-BWOTID2D.js → chunk-AGKMD2ZM.js} +7 -350
- package/dist/{chunk-SJEHVCPM.js → chunk-BG2APH43.js} +120 -26
- package/dist/{chunk-YURRUCIM.js → chunk-CPG2WZUL.js} +9 -11
- package/dist/chunk-F4UHAA5L.js +854 -0
- package/dist/chunk-JQKKMUCT.js +0 -0
- package/dist/chunk-MTHLLDCX.js +97 -0
- package/dist/{chunk-MCF5P6GJ.js → chunk-S6K5QZBJ.js} +739 -29
- package/dist/{display-YIYC6WJE.js → display-A5IEINAP.js} +79 -17
- package/dist/index.cjs +1055 -136
- package/dist/index.js +14 -10
- package/dist/{langfuse-C4HKZ3NL.js → langfuse-N5Y5BSXK.js} +1 -1
- package/dist/oauth-U4NNKN4B.js +30 -0
- package/dist/src/agents/display.d.ts.map +1 -1
- package/dist/src/agents/index.cjs +854 -78
- package/dist/src/agents/index.js +3 -2
- 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 +421 -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 +924 -98
- package/dist/src/browser.js +8 -5
- package/dist/src/connectors/base.d.ts +52 -121
- package/dist/src/connectors/base.d.ts.map +1 -1
- package/dist/src/connectors/http.d.ts.map +1 -1
- package/dist/src/managers/server_manager.d.ts.map +1 -1
- package/dist/src/managers/tools/acquire_active_mcp_server.d.ts +2 -2
- package/dist/src/managers/tools/acquire_active_mcp_server.d.ts.map +1 -1
- package/dist/src/managers/tools/add_server_from_config.d.ts +1 -7
- package/dist/src/managers/tools/add_server_from_config.d.ts.map +1 -1
- package/dist/src/managers/tools/connect_mcp_server.d.ts +2 -10
- package/dist/src/managers/tools/connect_mcp_server.d.ts.map +1 -1
- package/dist/src/managers/tools/list_mcp_servers.d.ts +2 -2
- package/dist/src/managers/tools/list_mcp_servers.d.ts.map +1 -1
- package/dist/src/managers/tools/release_mcp_server_connection.d.ts +2 -2
- package/dist/src/managers/tools/release_mcp_server_connection.d.ts.map +1 -1
- package/dist/src/observability/langfuse.d.ts +4 -0
- package/dist/src/observability/langfuse.d.ts.map +1 -1
- package/dist/src/react/McpUseProvider.d.ts.map +1 -1
- package/dist/src/react/index.cjs +189 -41
- package/dist/src/react/index.js +4 -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/connect-adapter.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 +1413 -418
- 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 +426 -420
- 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/src/utils/json-schema-to-zod/JSONSchemaToZod.d.ts +270 -0
- package/dist/src/utils/json-schema-to-zod/JSONSchemaToZod.d.ts.map +1 -0
- package/dist/src/utils/json-schema-to-zod/Type.d.ts +24 -0
- package/dist/src/utils/json-schema-to-zod/Type.d.ts.map +1 -0
- package/dist/src/utils/json-schema-to-zod/index.d.ts +3 -0
- package/dist/src/utils/json-schema-to-zod/index.d.ts.map +1 -0
- package/dist/src/utils/url-sanitize.d.ts +17 -0
- package/dist/src/utils/url-sanitize.d.ts.map +1 -0
- package/dist/tsup.config.d.ts.map +1 -1
- package/package.json +30 -38
|
@@ -6,6 +6,9 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
9
12
|
var __export = (target, all) => {
|
|
10
13
|
for (var name in all)
|
|
11
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -16,44 +19,1082 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
19
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
20
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
21
|
}
|
|
19
|
-
return to;
|
|
20
|
-
};
|
|
21
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
-
mod
|
|
28
|
-
));
|
|
29
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
|
|
34
|
+
// src/server/utils/runtime.ts
|
|
35
|
+
function getEnv2(key) {
|
|
36
|
+
if (isDeno2) {
|
|
37
|
+
return globalThis.Deno.env.get(key);
|
|
38
|
+
}
|
|
39
|
+
return process.env[key];
|
|
40
|
+
}
|
|
41
|
+
function getCwd() {
|
|
42
|
+
if (isDeno2) {
|
|
43
|
+
return globalThis.Deno.cwd();
|
|
44
|
+
}
|
|
45
|
+
return process.cwd();
|
|
46
|
+
}
|
|
47
|
+
function generateUUID() {
|
|
48
|
+
return globalThis.crypto.randomUUID();
|
|
49
|
+
}
|
|
50
|
+
var isDeno2, fsHelpers, pathHelpers;
|
|
51
|
+
var init_runtime = __esm({
|
|
52
|
+
"src/server/utils/runtime.ts"() {
|
|
53
|
+
"use strict";
|
|
54
|
+
isDeno2 = typeof globalThis.Deno !== "undefined";
|
|
55
|
+
__name(getEnv2, "getEnv");
|
|
56
|
+
__name(getCwd, "getCwd");
|
|
57
|
+
fsHelpers = {
|
|
58
|
+
async readFileSync(path, encoding = "utf8") {
|
|
59
|
+
if (isDeno2) {
|
|
60
|
+
return await globalThis.Deno.readTextFile(path);
|
|
61
|
+
}
|
|
62
|
+
const { readFileSync } = await import("fs");
|
|
63
|
+
const result = readFileSync(path, encoding);
|
|
64
|
+
return typeof result === "string" ? result : result.toString(encoding);
|
|
65
|
+
},
|
|
66
|
+
async readFile(path) {
|
|
67
|
+
if (isDeno2) {
|
|
68
|
+
const data = await globalThis.Deno.readFile(path);
|
|
69
|
+
return data.buffer;
|
|
70
|
+
}
|
|
71
|
+
const { readFileSync } = await import("fs");
|
|
72
|
+
const buffer = readFileSync(path);
|
|
73
|
+
return buffer.buffer.slice(
|
|
74
|
+
buffer.byteOffset,
|
|
75
|
+
buffer.byteOffset + buffer.byteLength
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
async existsSync(path) {
|
|
79
|
+
if (isDeno2) {
|
|
80
|
+
try {
|
|
81
|
+
await globalThis.Deno.stat(path);
|
|
82
|
+
return true;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const { existsSync } = await import("fs");
|
|
88
|
+
return existsSync(path);
|
|
89
|
+
},
|
|
90
|
+
async readdirSync(path) {
|
|
91
|
+
if (isDeno2) {
|
|
92
|
+
const entries = [];
|
|
93
|
+
for await (const entry of globalThis.Deno.readDir(path)) {
|
|
94
|
+
entries.push(entry.name);
|
|
95
|
+
}
|
|
96
|
+
return entries;
|
|
97
|
+
}
|
|
98
|
+
const { readdirSync } = await import("fs");
|
|
99
|
+
return readdirSync(path);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
pathHelpers = {
|
|
103
|
+
join(...paths) {
|
|
104
|
+
if (isDeno2) {
|
|
105
|
+
return paths.join("/").replace(/\/+/g, "/");
|
|
106
|
+
}
|
|
107
|
+
return paths.join("/").replace(/\/+/g, "/");
|
|
108
|
+
},
|
|
109
|
+
relative(from, to) {
|
|
110
|
+
const fromParts = from.split("/").filter((p) => p);
|
|
111
|
+
const toParts = to.split("/").filter((p) => p);
|
|
112
|
+
let i = 0;
|
|
113
|
+
while (i < fromParts.length && i < toParts.length && fromParts[i] === toParts[i]) {
|
|
114
|
+
i++;
|
|
115
|
+
}
|
|
116
|
+
const upCount = fromParts.length - i;
|
|
117
|
+
const relativeParts = [...Array(upCount).fill(".."), ...toParts.slice(i)];
|
|
118
|
+
return relativeParts.join("/");
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
__name(generateUUID, "generateUUID");
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// src/server/oauth/providers/supabase.ts
|
|
126
|
+
var import_jose, SupabaseOAuthProvider;
|
|
127
|
+
var init_supabase = __esm({
|
|
128
|
+
"src/server/oauth/providers/supabase.ts"() {
|
|
129
|
+
"use strict";
|
|
130
|
+
import_jose = require("jose");
|
|
131
|
+
SupabaseOAuthProvider = class {
|
|
132
|
+
static {
|
|
133
|
+
__name(this, "SupabaseOAuthProvider");
|
|
134
|
+
}
|
|
135
|
+
config;
|
|
136
|
+
supabaseUrl;
|
|
137
|
+
supabaseAuthUrl;
|
|
138
|
+
issuer;
|
|
139
|
+
jwks = null;
|
|
140
|
+
constructor(config) {
|
|
141
|
+
this.config = config;
|
|
142
|
+
this.supabaseUrl = `https://${config.projectId}.supabase.co`;
|
|
143
|
+
this.supabaseAuthUrl = `${this.supabaseUrl}/auth/v1`;
|
|
144
|
+
this.issuer = `${this.supabaseUrl}/auth/v1`;
|
|
145
|
+
}
|
|
146
|
+
getJWKS() {
|
|
147
|
+
if (!this.jwks) {
|
|
148
|
+
this.jwks = (0, import_jose.createRemoteJWKSet)(
|
|
149
|
+
new URL(`${this.issuer}/.well-known/jwks.json`)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return this.jwks;
|
|
153
|
+
}
|
|
154
|
+
async verifyToken(token) {
|
|
155
|
+
if (this.config.skipVerification) {
|
|
156
|
+
console.warn(
|
|
157
|
+
"[Supabase OAuth] \u26A0\uFE0F SKIPPING VERIFICATION (DEVELOPMENT MODE)"
|
|
158
|
+
);
|
|
159
|
+
console.warn(
|
|
160
|
+
"[Supabase OAuth] This is NOT secure! Only use for testing!"
|
|
161
|
+
);
|
|
162
|
+
const payload = (0, import_jose.decodeJwt)(token);
|
|
163
|
+
return { payload, protectedHeader: (0, import_jose.decodeProtectedHeader)(token) };
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const header = (0, import_jose.decodeProtectedHeader)(token);
|
|
167
|
+
if (header.alg === "HS256") {
|
|
168
|
+
if (!this.config.jwtSecret) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
"JWT Secret is required for HS256 tokens. Get it from: Supabase Dashboard \u2192 Project Settings \u2192 API \u2192 JWT Settings"
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
const secret = new TextEncoder().encode(this.config.jwtSecret);
|
|
174
|
+
const result = await (0, import_jose.jwtVerify)(token, secret, {
|
|
175
|
+
issuer: this.issuer,
|
|
176
|
+
audience: "authenticated"
|
|
177
|
+
});
|
|
178
|
+
return result;
|
|
179
|
+
} else if (header.alg === "ES256") {
|
|
180
|
+
const result = await (0, import_jose.jwtVerify)(token, this.getJWKS(), {
|
|
181
|
+
issuer: this.issuer,
|
|
182
|
+
audience: "authenticated"
|
|
183
|
+
});
|
|
184
|
+
return result;
|
|
185
|
+
} else {
|
|
186
|
+
throw new Error(`Unsupported algorithm: ${header.alg}`);
|
|
187
|
+
}
|
|
188
|
+
} catch (error2) {
|
|
189
|
+
throw new Error(`Supabase JWT verification failed: ${error2}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
getUserInfo(payload) {
|
|
193
|
+
return {
|
|
194
|
+
userId: payload.sub || payload.user_id,
|
|
195
|
+
email: payload.email,
|
|
196
|
+
name: payload.user_metadata?.name || payload.user_metadata?.full_name,
|
|
197
|
+
username: payload.user_metadata?.username,
|
|
198
|
+
picture: payload.user_metadata?.avatar_url,
|
|
199
|
+
roles: payload.role ? [payload.role] : [],
|
|
200
|
+
permissions: payload.aal ? [`aal:${payload.aal}`] : [],
|
|
201
|
+
// Include Supabase-specific claims
|
|
202
|
+
aal: payload.aal,
|
|
203
|
+
// Authentication Assurance Level
|
|
204
|
+
amr: payload.amr,
|
|
205
|
+
// Authentication Methods References
|
|
206
|
+
session_id: payload.session_id
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
getIssuer() {
|
|
210
|
+
return this.issuer;
|
|
211
|
+
}
|
|
212
|
+
getAuthEndpoint() {
|
|
213
|
+
return `${this.supabaseAuthUrl}/authorize`;
|
|
214
|
+
}
|
|
215
|
+
getTokenEndpoint() {
|
|
216
|
+
return `${this.supabaseAuthUrl}/token`;
|
|
217
|
+
}
|
|
218
|
+
getScopesSupported() {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
getGrantTypesSupported() {
|
|
222
|
+
return ["authorization_code", "refresh_token"];
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// src/server/oauth/providers/auth0.ts
|
|
229
|
+
var import_jose2, Auth0OAuthProvider;
|
|
230
|
+
var init_auth0 = __esm({
|
|
231
|
+
"src/server/oauth/providers/auth0.ts"() {
|
|
232
|
+
"use strict";
|
|
233
|
+
import_jose2 = require("jose");
|
|
234
|
+
Auth0OAuthProvider = class {
|
|
235
|
+
static {
|
|
236
|
+
__name(this, "Auth0OAuthProvider");
|
|
237
|
+
}
|
|
238
|
+
config;
|
|
239
|
+
issuer;
|
|
240
|
+
jwks = null;
|
|
241
|
+
constructor(config) {
|
|
242
|
+
this.config = config;
|
|
243
|
+
this.issuer = `https://${config.domain}`;
|
|
244
|
+
}
|
|
245
|
+
getJWKS() {
|
|
246
|
+
if (!this.jwks) {
|
|
247
|
+
this.jwks = (0, import_jose2.createRemoteJWKSet)(
|
|
248
|
+
new URL(`${this.issuer}/.well-known/jwks.json`)
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return this.jwks;
|
|
252
|
+
}
|
|
253
|
+
async verifyToken(token) {
|
|
254
|
+
if (this.config.verifyJwt === false) {
|
|
255
|
+
console.warn("[Auth0 OAuth] \u26A0\uFE0F JWT verification is disabled");
|
|
256
|
+
console.warn("[Auth0 OAuth] Enable verifyJwt: true for production");
|
|
257
|
+
const parts = token.split(".");
|
|
258
|
+
if (parts.length !== 3) {
|
|
259
|
+
throw new Error("Invalid JWT format");
|
|
260
|
+
}
|
|
261
|
+
const payload = JSON.parse(
|
|
262
|
+
Buffer.from(parts[1], "base64url").toString("utf8")
|
|
263
|
+
);
|
|
264
|
+
return { payload };
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
const result = await (0, import_jose2.jwtVerify)(token, this.getJWKS(), {
|
|
268
|
+
issuer: this.issuer,
|
|
269
|
+
audience: this.config.audience
|
|
270
|
+
});
|
|
271
|
+
return result;
|
|
272
|
+
} catch (error2) {
|
|
273
|
+
throw new Error(`Auth0 JWT verification failed: ${error2}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
getUserInfo(payload) {
|
|
277
|
+
return {
|
|
278
|
+
userId: payload.sub,
|
|
279
|
+
email: payload.email,
|
|
280
|
+
name: payload.name,
|
|
281
|
+
username: payload.username,
|
|
282
|
+
nickname: payload.nickname,
|
|
283
|
+
picture: payload.picture,
|
|
284
|
+
// Auth0 includes permissions directly in the token
|
|
285
|
+
permissions: payload.permissions || [],
|
|
286
|
+
// Auth0 can include roles (if configured)
|
|
287
|
+
roles: payload.roles || payload["https://your-app.com/roles"] || [],
|
|
288
|
+
// Include scope as well
|
|
289
|
+
scopes: payload.scope ? payload.scope.split(" ") : [],
|
|
290
|
+
// Additional Auth0-specific claims
|
|
291
|
+
email_verified: payload.email_verified,
|
|
292
|
+
updated_at: payload.updated_at
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
getIssuer() {
|
|
296
|
+
return this.issuer;
|
|
297
|
+
}
|
|
298
|
+
getAuthEndpoint() {
|
|
299
|
+
return `${this.issuer}/authorize`;
|
|
300
|
+
}
|
|
301
|
+
getTokenEndpoint() {
|
|
302
|
+
return `${this.issuer}/oauth/token`;
|
|
303
|
+
}
|
|
304
|
+
getScopesSupported() {
|
|
305
|
+
return ["openid", "profile", "email", "offline_access"];
|
|
306
|
+
}
|
|
307
|
+
getGrantTypesSupported() {
|
|
308
|
+
return ["authorization_code", "refresh_token"];
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// src/server/oauth/providers/keycloak.ts
|
|
315
|
+
var import_jose3, KeycloakOAuthProvider;
|
|
316
|
+
var init_keycloak = __esm({
|
|
317
|
+
"src/server/oauth/providers/keycloak.ts"() {
|
|
318
|
+
"use strict";
|
|
319
|
+
import_jose3 = require("jose");
|
|
320
|
+
KeycloakOAuthProvider = class {
|
|
321
|
+
static {
|
|
322
|
+
__name(this, "KeycloakOAuthProvider");
|
|
323
|
+
}
|
|
324
|
+
config;
|
|
325
|
+
issuer;
|
|
326
|
+
jwks = null;
|
|
327
|
+
constructor(config) {
|
|
328
|
+
this.config = config;
|
|
329
|
+
const serverUrl = config.serverUrl.replace(/\/$/, "");
|
|
330
|
+
this.issuer = `${serverUrl}/realms/${config.realm}`;
|
|
331
|
+
}
|
|
332
|
+
getJWKS() {
|
|
333
|
+
if (!this.jwks) {
|
|
334
|
+
this.jwks = (0, import_jose3.createRemoteJWKSet)(
|
|
335
|
+
new URL(`${this.issuer}/protocol/openid-connect/certs`)
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
return this.jwks;
|
|
339
|
+
}
|
|
340
|
+
async verifyToken(token) {
|
|
341
|
+
if (this.config.verifyJwt === false) {
|
|
342
|
+
console.warn("[Keycloak OAuth] \u26A0\uFE0F JWT verification is disabled");
|
|
343
|
+
console.warn(
|
|
344
|
+
"[Keycloak OAuth] Enable verifyJwt: true for production"
|
|
345
|
+
);
|
|
346
|
+
const parts = token.split(".");
|
|
347
|
+
if (parts.length !== 3) {
|
|
348
|
+
throw new Error("Invalid JWT format");
|
|
349
|
+
}
|
|
350
|
+
const payload = JSON.parse(
|
|
351
|
+
Buffer.from(parts[1], "base64url").toString("utf8")
|
|
352
|
+
);
|
|
353
|
+
return { payload };
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
const result = await (0, import_jose3.jwtVerify)(token, this.getJWKS(), {
|
|
357
|
+
issuer: this.issuer,
|
|
358
|
+
// Don't verify audience if not specified
|
|
359
|
+
...this.config.clientId && { audience: this.config.clientId }
|
|
360
|
+
});
|
|
361
|
+
return result;
|
|
362
|
+
} catch (error2) {
|
|
363
|
+
throw new Error(`Keycloak JWT verification failed: ${error2}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
getUserInfo(payload) {
|
|
367
|
+
const realmRoles = payload.realm_access?.roles || [];
|
|
368
|
+
const clientRoles = this.config.clientId && payload.resource_access?.[this.config.clientId]?.roles || [];
|
|
369
|
+
const allRoles = [...realmRoles, ...clientRoles];
|
|
370
|
+
const permissions = [];
|
|
371
|
+
if (payload.resource_access) {
|
|
372
|
+
Object.entries(payload.resource_access).forEach(
|
|
373
|
+
([resource2, access]) => {
|
|
374
|
+
if (access.roles) {
|
|
375
|
+
access.roles.forEach((role) => {
|
|
376
|
+
permissions.push(`${resource2}:${role}`);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
userId: payload.sub,
|
|
384
|
+
email: payload.email,
|
|
385
|
+
name: payload.name,
|
|
386
|
+
username: payload.preferred_username,
|
|
387
|
+
nickname: payload.preferred_username,
|
|
388
|
+
picture: payload.picture,
|
|
389
|
+
roles: allRoles,
|
|
390
|
+
permissions,
|
|
391
|
+
// Include scope as well
|
|
392
|
+
scopes: payload.scope ? payload.scope.split(" ") : [],
|
|
393
|
+
// Keycloak-specific claims
|
|
394
|
+
email_verified: payload.email_verified,
|
|
395
|
+
given_name: payload.given_name,
|
|
396
|
+
family_name: payload.family_name,
|
|
397
|
+
realm_access: payload.realm_access,
|
|
398
|
+
resource_access: payload.resource_access
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
getIssuer() {
|
|
402
|
+
return this.issuer;
|
|
403
|
+
}
|
|
404
|
+
getAuthEndpoint() {
|
|
405
|
+
return `${this.issuer}/protocol/openid-connect/auth`;
|
|
406
|
+
}
|
|
407
|
+
getTokenEndpoint() {
|
|
408
|
+
return `${this.issuer}/protocol/openid-connect/token`;
|
|
409
|
+
}
|
|
410
|
+
getScopesSupported() {
|
|
411
|
+
return ["openid", "profile", "email", "offline_access", "roles"];
|
|
412
|
+
}
|
|
413
|
+
getGrantTypesSupported() {
|
|
414
|
+
return ["authorization_code", "refresh_token", "client_credentials"];
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// src/server/oauth/providers/workos.ts
|
|
421
|
+
var import_jose4, WorkOSOAuthProvider;
|
|
422
|
+
var init_workos = __esm({
|
|
423
|
+
"src/server/oauth/providers/workos.ts"() {
|
|
424
|
+
"use strict";
|
|
425
|
+
import_jose4 = require("jose");
|
|
426
|
+
WorkOSOAuthProvider = class {
|
|
427
|
+
static {
|
|
428
|
+
__name(this, "WorkOSOAuthProvider");
|
|
429
|
+
}
|
|
430
|
+
config;
|
|
431
|
+
issuer;
|
|
432
|
+
jwks = null;
|
|
433
|
+
constructor(config) {
|
|
434
|
+
this.config = config;
|
|
435
|
+
this.issuer = `https://${config.subdomain}.authkit.app`;
|
|
436
|
+
}
|
|
437
|
+
getJWKS() {
|
|
438
|
+
if (!this.jwks) {
|
|
439
|
+
this.jwks = (0, import_jose4.createRemoteJWKSet)(new URL(`${this.issuer}/oauth2/jwks`));
|
|
440
|
+
}
|
|
441
|
+
return this.jwks;
|
|
442
|
+
}
|
|
443
|
+
async verifyToken(token) {
|
|
444
|
+
if (this.config.verifyJwt === false) {
|
|
445
|
+
console.warn("[WorkOS OAuth] \u26A0\uFE0F JWT verification is disabled");
|
|
446
|
+
console.warn("[WorkOS OAuth] Enable verifyJwt: true for production");
|
|
447
|
+
const parts = token.split(".");
|
|
448
|
+
if (parts.length !== 3) {
|
|
449
|
+
throw new Error("Invalid JWT format");
|
|
450
|
+
}
|
|
451
|
+
const payload = (0, import_jose4.decodeJwt)(token);
|
|
452
|
+
return { payload };
|
|
453
|
+
}
|
|
454
|
+
try {
|
|
455
|
+
const result = await (0, import_jose4.jwtVerify)(token, this.getJWKS(), {
|
|
456
|
+
issuer: this.issuer
|
|
457
|
+
});
|
|
458
|
+
return result;
|
|
459
|
+
} catch (error2) {
|
|
460
|
+
throw new Error(`WorkOS JWT verification failed: ${error2}`);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
getUserInfo(payload) {
|
|
464
|
+
return {
|
|
465
|
+
userId: payload.sub,
|
|
466
|
+
email: payload.email,
|
|
467
|
+
name: payload.name,
|
|
468
|
+
username: payload.preferred_username,
|
|
469
|
+
picture: payload.picture,
|
|
470
|
+
// WorkOS includes permissions and roles in token
|
|
471
|
+
permissions: payload.permissions || [],
|
|
472
|
+
roles: payload.roles || [],
|
|
473
|
+
// Include scope as well
|
|
474
|
+
scopes: payload.scope ? payload.scope.split(" ") : [],
|
|
475
|
+
// Additional WorkOS-specific claims
|
|
476
|
+
email_verified: payload.email_verified,
|
|
477
|
+
organization_id: payload.org_id,
|
|
478
|
+
sid: payload.sid
|
|
479
|
+
// Session ID
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
getIssuer() {
|
|
483
|
+
return this.issuer;
|
|
484
|
+
}
|
|
485
|
+
getAuthEndpoint() {
|
|
486
|
+
return `${this.issuer}/oauth2/authorize`;
|
|
487
|
+
}
|
|
488
|
+
getTokenEndpoint() {
|
|
489
|
+
return `${this.issuer}/oauth2/token`;
|
|
490
|
+
}
|
|
491
|
+
getScopesSupported() {
|
|
492
|
+
return ["email", "offline_access", "openid", "profile"];
|
|
493
|
+
}
|
|
494
|
+
getGrantTypesSupported() {
|
|
495
|
+
return ["authorization_code", "refresh_token"];
|
|
496
|
+
}
|
|
497
|
+
getMode() {
|
|
498
|
+
if (this.config.clientId) {
|
|
499
|
+
console.log("[WorkOS OAuth] Using proxy mode (pre-registered client)");
|
|
500
|
+
return "proxy";
|
|
501
|
+
}
|
|
502
|
+
console.log(
|
|
503
|
+
"[WorkOS OAuth] Using direct mode (Dynamic Client Registration)"
|
|
504
|
+
);
|
|
505
|
+
return "direct";
|
|
506
|
+
}
|
|
507
|
+
getRegistrationEndpoint() {
|
|
508
|
+
if (this.config.clientId) {
|
|
509
|
+
return void 0;
|
|
510
|
+
}
|
|
511
|
+
return `${this.issuer}/oauth2/register`;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// src/server/oauth/providers/custom.ts
|
|
518
|
+
var CustomOAuthProvider;
|
|
519
|
+
var init_custom = __esm({
|
|
520
|
+
"src/server/oauth/providers/custom.ts"() {
|
|
521
|
+
"use strict";
|
|
522
|
+
CustomOAuthProvider = class {
|
|
523
|
+
static {
|
|
524
|
+
__name(this, "CustomOAuthProvider");
|
|
525
|
+
}
|
|
526
|
+
config;
|
|
527
|
+
constructor(config) {
|
|
528
|
+
this.config = config;
|
|
529
|
+
}
|
|
530
|
+
async verifyToken(token) {
|
|
531
|
+
try {
|
|
532
|
+
const result = await this.config.verifyToken(token);
|
|
533
|
+
return { payload: result };
|
|
534
|
+
} catch (error2) {
|
|
535
|
+
throw new Error(`Custom OAuth verification failed: ${error2}`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
getUserInfo(payload) {
|
|
539
|
+
if (this.config.getUserInfo) {
|
|
540
|
+
return this.config.getUserInfo(payload);
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
userId: payload.sub || payload.user_id || payload.id,
|
|
544
|
+
email: payload.email,
|
|
545
|
+
name: payload.name,
|
|
546
|
+
username: payload.username || payload.preferred_username,
|
|
547
|
+
nickname: payload.nickname,
|
|
548
|
+
picture: payload.picture || payload.avatar_url,
|
|
549
|
+
roles: payload.roles || [],
|
|
550
|
+
permissions: payload.permissions || [],
|
|
551
|
+
scopes: payload.scope ? payload.scope.split(" ") : []
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
getIssuer() {
|
|
555
|
+
return this.config.issuer;
|
|
556
|
+
}
|
|
557
|
+
getAuthEndpoint() {
|
|
558
|
+
return this.config.authEndpoint;
|
|
559
|
+
}
|
|
560
|
+
getTokenEndpoint() {
|
|
561
|
+
return this.config.tokenEndpoint;
|
|
562
|
+
}
|
|
563
|
+
getScopesSupported() {
|
|
564
|
+
return this.config.scopesSupported || ["openid", "profile", "email"];
|
|
565
|
+
}
|
|
566
|
+
getGrantTypesSupported() {
|
|
567
|
+
return this.config.grantTypesSupported || ["authorization_code", "refresh_token"];
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// src/server/oauth/providers.ts
|
|
574
|
+
function oauthSupabaseProvider(config = {}) {
|
|
575
|
+
const projectId = config.projectId ?? getEnv2("MCP_USE_OAUTH_SUPABASE_PROJECT_ID");
|
|
576
|
+
const jwtSecret = config.jwtSecret ?? getEnv2("MCP_USE_OAUTH_SUPABASE_JWT_SECRET");
|
|
577
|
+
if (!projectId) {
|
|
578
|
+
throw new Error(
|
|
579
|
+
"Supabase projectId is required. Set MCP_USE_OAUTH_SUPABASE_PROJECT_ID environment variable or pass projectId in config."
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
return new SupabaseOAuthProvider({
|
|
583
|
+
provider: "supabase",
|
|
584
|
+
projectId,
|
|
585
|
+
jwtSecret,
|
|
586
|
+
skipVerification: config.skipVerification
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
function oauthAuth0Provider(config = {}) {
|
|
590
|
+
const domain = config.domain ?? getEnv2("MCP_USE_OAUTH_AUTH0_DOMAIN");
|
|
591
|
+
const audience = config.audience ?? getEnv2("MCP_USE_OAUTH_AUTH0_AUDIENCE");
|
|
592
|
+
if (!domain) {
|
|
593
|
+
throw new Error(
|
|
594
|
+
"Auth0 domain is required. Set MCP_USE_OAUTH_AUTH0_DOMAIN environment variable or pass domain in config."
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
if (!audience) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
"Auth0 audience is required. Set MCP_USE_OAUTH_AUTH0_AUDIENCE environment variable or pass audience in config."
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
return new Auth0OAuthProvider({
|
|
603
|
+
provider: "auth0",
|
|
604
|
+
domain,
|
|
605
|
+
audience,
|
|
606
|
+
verifyJwt: config.verifyJwt
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
function oauthKeycloakProvider(config = {}) {
|
|
610
|
+
const serverUrl = config.serverUrl ?? getEnv2("MCP_USE_OAUTH_KEYCLOAK_SERVER_URL");
|
|
611
|
+
const realm = config.realm ?? getEnv2("MCP_USE_OAUTH_KEYCLOAK_REALM");
|
|
612
|
+
const clientId = config.clientId ?? getEnv2("MCP_USE_OAUTH_KEYCLOAK_CLIENT_ID");
|
|
613
|
+
if (!serverUrl) {
|
|
614
|
+
throw new Error(
|
|
615
|
+
"Keycloak serverUrl is required. Set MCP_USE_OAUTH_KEYCLOAK_SERVER_URL environment variable or pass serverUrl in config."
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
if (!realm) {
|
|
619
|
+
throw new Error(
|
|
620
|
+
"Keycloak realm is required. Set MCP_USE_OAUTH_KEYCLOAK_REALM environment variable or pass realm in config."
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
return new KeycloakOAuthProvider({
|
|
624
|
+
provider: "keycloak",
|
|
625
|
+
serverUrl,
|
|
626
|
+
realm,
|
|
627
|
+
clientId,
|
|
628
|
+
verifyJwt: config.verifyJwt
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
function oauthWorkOSProvider(config = {}) {
|
|
632
|
+
const subdomain = config.subdomain ?? getEnv2("MCP_USE_OAUTH_WORKOS_SUBDOMAIN");
|
|
633
|
+
const clientId = config.clientId ?? getEnv2("MCP_USE_OAUTH_WORKOS_CLIENT_ID");
|
|
634
|
+
const apiKey = config.apiKey ?? getEnv2("MCP_USE_OAUTH_WORKOS_API_KEY");
|
|
635
|
+
if (!subdomain) {
|
|
636
|
+
throw new Error(
|
|
637
|
+
"WorkOS subdomain is required. Set MCP_USE_OAUTH_WORKOS_SUBDOMAIN environment variable or pass subdomain in config."
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
if (clientId) {
|
|
641
|
+
console.log("[WorkOS OAuth] Using pre-registered OAuth client mode");
|
|
642
|
+
console.log(`[WorkOS OAuth] - Client ID: ${clientId}`);
|
|
643
|
+
console.log(
|
|
644
|
+
"[WorkOS OAuth] - Make sure this client exists in WorkOS Dashboard"
|
|
645
|
+
);
|
|
646
|
+
console.log(
|
|
647
|
+
"[WorkOS OAuth] - Configure redirect URIs to match your MCP client"
|
|
648
|
+
);
|
|
649
|
+
} else {
|
|
650
|
+
console.log("[WorkOS OAuth] Using Dynamic Client Registration (DCR) mode");
|
|
651
|
+
console.log(
|
|
652
|
+
"[WorkOS OAuth] - MCP clients will register themselves automatically"
|
|
653
|
+
);
|
|
654
|
+
console.log(
|
|
655
|
+
"[WorkOS OAuth] - Make sure DCR is enabled in WorkOS Dashboard"
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
return new WorkOSOAuthProvider({
|
|
659
|
+
provider: "workos",
|
|
660
|
+
subdomain,
|
|
661
|
+
clientId,
|
|
662
|
+
apiKey,
|
|
663
|
+
verifyJwt: config.verifyJwt
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
function oauthCustomProvider(config) {
|
|
667
|
+
return new CustomOAuthProvider({
|
|
668
|
+
provider: "custom",
|
|
669
|
+
...config
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
var init_providers = __esm({
|
|
673
|
+
"src/server/oauth/providers.ts"() {
|
|
674
|
+
"use strict";
|
|
675
|
+
init_supabase();
|
|
676
|
+
init_auth0();
|
|
677
|
+
init_keycloak();
|
|
678
|
+
init_workos();
|
|
679
|
+
init_custom();
|
|
680
|
+
init_runtime();
|
|
681
|
+
__name(oauthSupabaseProvider, "oauthSupabaseProvider");
|
|
682
|
+
__name(oauthAuth0Provider, "oauthAuth0Provider");
|
|
683
|
+
__name(oauthKeycloakProvider, "oauthKeycloakProvider");
|
|
684
|
+
__name(oauthWorkOSProvider, "oauthWorkOSProvider");
|
|
685
|
+
__name(oauthCustomProvider, "oauthCustomProvider");
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// src/server/oauth/middleware.ts
|
|
690
|
+
function createBearerAuthMiddleware(provider, baseUrl) {
|
|
691
|
+
return async (c, next) => {
|
|
692
|
+
const authHeader = c.req.header("Authorization");
|
|
693
|
+
const getWWWAuthenticateHeader = /* @__PURE__ */ __name(() => {
|
|
694
|
+
const base = baseUrl || new URL(c.req.url).origin;
|
|
695
|
+
const parts = [
|
|
696
|
+
'Bearer error="unauthorized"',
|
|
697
|
+
'error_description="Authorization needed"'
|
|
698
|
+
];
|
|
699
|
+
parts.push(
|
|
700
|
+
`resource_metadata="${base}/.well-known/oauth-protected-resource"`
|
|
701
|
+
);
|
|
702
|
+
return parts.join(", ");
|
|
703
|
+
}, "getWWWAuthenticateHeader");
|
|
704
|
+
if (!authHeader) {
|
|
705
|
+
c.header("WWW-Authenticate", getWWWAuthenticateHeader());
|
|
706
|
+
return c.json({ error: "Missing Authorization header" }, 401);
|
|
707
|
+
}
|
|
708
|
+
const [type, token] = authHeader.split(" ");
|
|
709
|
+
if (type.toLowerCase() !== "bearer" || !token) {
|
|
710
|
+
c.header("WWW-Authenticate", getWWWAuthenticateHeader());
|
|
711
|
+
return c.json(
|
|
712
|
+
{
|
|
713
|
+
error: 'Invalid Authorization header format, expected "Bearer TOKEN"'
|
|
714
|
+
},
|
|
715
|
+
401
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
try {
|
|
719
|
+
const result = await provider.verifyToken(token);
|
|
720
|
+
const payload = result.payload;
|
|
721
|
+
const user = provider.getUserInfo(payload);
|
|
722
|
+
const authInfo = {
|
|
723
|
+
user,
|
|
724
|
+
payload,
|
|
725
|
+
accessToken: token,
|
|
726
|
+
// Extract scopes from scope claim (OAuth standard)
|
|
727
|
+
scopes: payload.scope ? payload.scope.split(" ") : [],
|
|
728
|
+
// Extract permissions (Auth0 style, or custom)
|
|
729
|
+
permissions: payload.permissions || []
|
|
730
|
+
};
|
|
731
|
+
c.set("auth", authInfo);
|
|
732
|
+
c.auth = authInfo;
|
|
733
|
+
c.set("user", user);
|
|
734
|
+
c.set("payload", payload);
|
|
735
|
+
c.set("accessToken", token);
|
|
736
|
+
await next();
|
|
737
|
+
} catch (error2) {
|
|
738
|
+
c.header("WWW-Authenticate", getWWWAuthenticateHeader());
|
|
739
|
+
return c.json({ error: `Invalid token: ${error2}` }, 401);
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
var init_middleware = __esm({
|
|
744
|
+
"src/server/oauth/middleware.ts"() {
|
|
745
|
+
"use strict";
|
|
746
|
+
__name(createBearerAuthMiddleware, "createBearerAuthMiddleware");
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
// src/server/oauth/routes.ts
|
|
751
|
+
function setupOAuthRoutes(app, provider, baseUrl) {
|
|
752
|
+
const mode = provider.getMode?.() || "proxy";
|
|
753
|
+
app.use(
|
|
754
|
+
"/.well-known/*",
|
|
755
|
+
(0, import_cors.cors)({
|
|
756
|
+
origin: "*",
|
|
757
|
+
// Allow all origins for metadata discovery
|
|
758
|
+
allowMethods: ["GET", "OPTIONS"],
|
|
759
|
+
allowHeaders: ["Content-Type", "Authorization"],
|
|
760
|
+
exposeHeaders: ["Content-Type"],
|
|
761
|
+
maxAge: 86400
|
|
762
|
+
// Cache preflight for 24 hours
|
|
763
|
+
})
|
|
764
|
+
);
|
|
765
|
+
if (mode === "proxy") {
|
|
766
|
+
app.use(
|
|
767
|
+
"/authorize",
|
|
768
|
+
(0, import_cors.cors)({
|
|
769
|
+
origin: "*",
|
|
770
|
+
allowMethods: ["GET", "POST", "OPTIONS"],
|
|
771
|
+
allowHeaders: ["Content-Type", "Authorization"],
|
|
772
|
+
maxAge: 86400
|
|
773
|
+
})
|
|
774
|
+
);
|
|
775
|
+
app.use(
|
|
776
|
+
"/token",
|
|
777
|
+
(0, import_cors.cors)({
|
|
778
|
+
origin: "*",
|
|
779
|
+
allowMethods: ["POST", "OPTIONS"],
|
|
780
|
+
allowHeaders: ["Content-Type", "Authorization"],
|
|
781
|
+
maxAge: 86400
|
|
782
|
+
})
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
if (mode === "proxy") {
|
|
786
|
+
const handleAuthorize = /* @__PURE__ */ __name(async (c) => {
|
|
787
|
+
const params = c.req.method === "POST" ? await c.req.parseBody() : c.req.query();
|
|
788
|
+
const clientId = params.client_id;
|
|
789
|
+
const redirectUri = params.redirect_uri;
|
|
790
|
+
const responseType = params.response_type;
|
|
791
|
+
const codeChallenge = params.code_challenge;
|
|
792
|
+
const codeChallengeMethod = params.code_challenge_method;
|
|
793
|
+
const state = params.state;
|
|
794
|
+
const scope = params.scope;
|
|
795
|
+
const audience = params.audience;
|
|
796
|
+
if (!clientId || !redirectUri || !responseType || !codeChallenge) {
|
|
797
|
+
return c.json(
|
|
798
|
+
{
|
|
799
|
+
error: "invalid_request",
|
|
800
|
+
error_description: "Missing required parameters"
|
|
801
|
+
},
|
|
802
|
+
400
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
const authUrl = new URL(provider.getAuthEndpoint());
|
|
806
|
+
authUrl.searchParams.set("client_id", clientId);
|
|
807
|
+
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
808
|
+
authUrl.searchParams.set("response_type", responseType);
|
|
809
|
+
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
810
|
+
authUrl.searchParams.set(
|
|
811
|
+
"code_challenge_method",
|
|
812
|
+
codeChallengeMethod || "S256"
|
|
813
|
+
);
|
|
814
|
+
if (state) authUrl.searchParams.set("state", state);
|
|
815
|
+
if (scope) authUrl.searchParams.set("scope", scope);
|
|
816
|
+
if (audience) authUrl.searchParams.set("audience", audience);
|
|
817
|
+
return c.redirect(authUrl.toString(), 302);
|
|
818
|
+
}, "handleAuthorize");
|
|
819
|
+
app.get("/authorize", handleAuthorize);
|
|
820
|
+
app.post("/authorize", handleAuthorize);
|
|
821
|
+
app.post("/token", async (c) => {
|
|
822
|
+
try {
|
|
823
|
+
const body = await c.req.parseBody();
|
|
824
|
+
const response = await fetch(provider.getTokenEndpoint(), {
|
|
825
|
+
method: "POST",
|
|
826
|
+
headers: {
|
|
827
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
828
|
+
},
|
|
829
|
+
body: new URLSearchParams(body).toString()
|
|
830
|
+
});
|
|
831
|
+
const data = await response.json();
|
|
832
|
+
if (!response.ok) {
|
|
833
|
+
return c.json(data, response.status);
|
|
834
|
+
}
|
|
835
|
+
return c.json(data);
|
|
836
|
+
} catch (error2) {
|
|
837
|
+
return c.json(
|
|
838
|
+
{
|
|
839
|
+
error: "server_error",
|
|
840
|
+
error_description: `Token exchange failed: ${error2}`
|
|
841
|
+
},
|
|
842
|
+
500
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
const handleAuthorizationServerMetadata = /* @__PURE__ */ __name(async (c) => {
|
|
848
|
+
const requestPath = new URL(c.req.url).pathname;
|
|
849
|
+
console.log(`[OAuth] Metadata request: ${requestPath} (mode: ${mode})`);
|
|
850
|
+
if (mode === "direct") {
|
|
851
|
+
try {
|
|
852
|
+
const metadataUrl = `${provider.getIssuer()}/.well-known/oauth-authorization-server`;
|
|
853
|
+
console.log(`[OAuth] Fetching metadata from provider: ${metadataUrl}`);
|
|
854
|
+
const response = await fetch(metadataUrl);
|
|
855
|
+
if (!response.ok) {
|
|
856
|
+
console.error(
|
|
857
|
+
`[OAuth] Failed to fetch provider metadata: ${response.status}`
|
|
858
|
+
);
|
|
859
|
+
return c.json(
|
|
860
|
+
{
|
|
861
|
+
error: "server_error",
|
|
862
|
+
error_description: `Failed to fetch provider metadata: ${response.status}`
|
|
863
|
+
},
|
|
864
|
+
500
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
const metadata = await response.json();
|
|
868
|
+
const hasRegisteredClient = provider.getRegistrationEndpoint && provider.config?.clientId;
|
|
869
|
+
if (hasRegisteredClient) {
|
|
870
|
+
console.log(
|
|
871
|
+
`[OAuth] Provider has pre-registered client - removing DCR endpoint`
|
|
872
|
+
);
|
|
873
|
+
delete metadata.registration_endpoint;
|
|
874
|
+
}
|
|
875
|
+
console.log(`[OAuth] Provider metadata retrieved successfully`);
|
|
876
|
+
console.log(`[OAuth] - Issuer: ${metadata.issuer}`);
|
|
877
|
+
console.log(
|
|
878
|
+
`[OAuth] - Registration endpoint: ${metadata.registration_endpoint || "not available (using pre-registered client)"}`
|
|
879
|
+
);
|
|
880
|
+
return c.json(metadata);
|
|
881
|
+
} catch (error2) {
|
|
882
|
+
console.error(`[OAuth] Error fetching provider metadata:`, error2);
|
|
883
|
+
return c.json(
|
|
884
|
+
{
|
|
885
|
+
error: "server_error",
|
|
886
|
+
error_description: `Failed to fetch provider metadata: ${error2}`
|
|
887
|
+
},
|
|
888
|
+
500
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
} else {
|
|
892
|
+
console.log(`[OAuth] Returning proxy mode metadata`);
|
|
893
|
+
return c.json({
|
|
894
|
+
issuer: provider.getIssuer(),
|
|
895
|
+
authorization_endpoint: `${baseUrl}/authorize`,
|
|
896
|
+
token_endpoint: `${baseUrl}/token`,
|
|
897
|
+
response_types_supported: ["code"],
|
|
898
|
+
grant_types_supported: provider.getGrantTypesSupported(),
|
|
899
|
+
code_challenge_methods_supported: ["S256"],
|
|
900
|
+
token_endpoint_auth_methods_supported: [
|
|
901
|
+
"client_secret_post",
|
|
902
|
+
"client_secret_basic",
|
|
903
|
+
"none"
|
|
904
|
+
],
|
|
905
|
+
scopes_supported: provider.getScopesSupported()
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
}, "handleAuthorizationServerMetadata");
|
|
909
|
+
app.get(
|
|
910
|
+
"/.well-known/oauth-authorization-server",
|
|
911
|
+
handleAuthorizationServerMetadata
|
|
912
|
+
);
|
|
913
|
+
app.get(
|
|
914
|
+
"/.well-known/openid-configuration",
|
|
915
|
+
handleAuthorizationServerMetadata
|
|
916
|
+
);
|
|
917
|
+
app.get("/.well-known/oauth-protected-resource", (c) => {
|
|
918
|
+
console.log(`[OAuth] Protected resource metadata request (mode: ${mode})`);
|
|
919
|
+
console.log(`[OAuth] - Resource: ${baseUrl}`);
|
|
920
|
+
console.log(`[OAuth] - Authorization server: ${provider.getIssuer()}`);
|
|
921
|
+
return c.json({
|
|
922
|
+
resource: baseUrl,
|
|
923
|
+
authorization_servers: [provider.getIssuer()],
|
|
924
|
+
bearer_methods_supported: ["header"],
|
|
925
|
+
resource_documentation: mode === "direct" ? "This resource uses direct OAuth flow. Clients communicate directly with the authorization server." : void 0
|
|
926
|
+
});
|
|
927
|
+
});
|
|
928
|
+
app.get("/.well-known/oauth-protected-resource/mcp", (c) => {
|
|
929
|
+
return c.json({
|
|
930
|
+
resource: `${baseUrl}/mcp`,
|
|
931
|
+
authorization_servers: [provider.getIssuer()],
|
|
932
|
+
bearer_methods_supported: ["header"]
|
|
933
|
+
});
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
var import_cors;
|
|
937
|
+
var init_routes = __esm({
|
|
938
|
+
"src/server/oauth/routes.ts"() {
|
|
939
|
+
"use strict";
|
|
940
|
+
import_cors = require("hono/cors");
|
|
941
|
+
__name(setupOAuthRoutes, "setupOAuthRoutes");
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
// src/server/oauth/utils.ts
|
|
946
|
+
function getAuth(context) {
|
|
947
|
+
return context.get("auth");
|
|
948
|
+
}
|
|
949
|
+
function hasScope(context, needed) {
|
|
950
|
+
const { scopes, permissions } = getAuth(context);
|
|
951
|
+
const requiredScopes = Array.isArray(needed) ? needed : [needed];
|
|
952
|
+
return requiredScopes.every(
|
|
953
|
+
(scope) => scopes.includes(scope) || permissions.includes(scope)
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
function hasAnyScope(context, needed) {
|
|
957
|
+
const { scopes, permissions } = getAuth(context);
|
|
958
|
+
return needed.some(
|
|
959
|
+
(scope) => scopes.includes(scope) || permissions.includes(scope)
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
function requireScope(needed) {
|
|
963
|
+
return async (c, next) => {
|
|
964
|
+
if (!hasScope(c, needed)) {
|
|
965
|
+
const { scopes, permissions } = getAuth(c);
|
|
966
|
+
const requiredScopes = Array.isArray(needed) ? needed : [needed];
|
|
967
|
+
return c.json(
|
|
968
|
+
{
|
|
969
|
+
error: "insufficient_scope",
|
|
970
|
+
required: requiredScopes,
|
|
971
|
+
granted_scopes: scopes,
|
|
972
|
+
granted_permissions: permissions,
|
|
973
|
+
message: `Missing required scope(s): ${requiredScopes.join(", ")}`
|
|
974
|
+
},
|
|
975
|
+
403
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
await next();
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function requireAnyScope(needed) {
|
|
982
|
+
return async (c, next) => {
|
|
983
|
+
if (!hasAnyScope(c, needed)) {
|
|
984
|
+
const { scopes, permissions } = getAuth(c);
|
|
985
|
+
return c.json(
|
|
986
|
+
{
|
|
987
|
+
error: "insufficient_scope",
|
|
988
|
+
required_any: needed,
|
|
989
|
+
granted_scopes: scopes,
|
|
990
|
+
granted_permissions: permissions,
|
|
991
|
+
message: `Missing at least one required scope from: ${needed.join(", ")}`
|
|
992
|
+
},
|
|
993
|
+
403
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
await next();
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
var init_utils = __esm({
|
|
1000
|
+
"src/server/oauth/utils.ts"() {
|
|
1001
|
+
"use strict";
|
|
1002
|
+
__name(getAuth, "getAuth");
|
|
1003
|
+
__name(hasScope, "hasScope");
|
|
1004
|
+
__name(hasAnyScope, "hasAnyScope");
|
|
1005
|
+
__name(requireScope, "requireScope");
|
|
1006
|
+
__name(requireAnyScope, "requireAnyScope");
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
// src/server/oauth/index.ts
|
|
1011
|
+
var oauth_exports = {};
|
|
1012
|
+
__export(oauth_exports, {
|
|
1013
|
+
createBearerAuthMiddleware: () => createBearerAuthMiddleware,
|
|
1014
|
+
getAuth: () => getAuth,
|
|
1015
|
+
hasAnyScope: () => hasAnyScope,
|
|
1016
|
+
hasScope: () => hasScope,
|
|
1017
|
+
oauthAuth0Provider: () => oauthAuth0Provider,
|
|
1018
|
+
oauthCustomProvider: () => oauthCustomProvider,
|
|
1019
|
+
oauthKeycloakProvider: () => oauthKeycloakProvider,
|
|
1020
|
+
oauthSupabaseProvider: () => oauthSupabaseProvider,
|
|
1021
|
+
oauthWorkOSProvider: () => oauthWorkOSProvider,
|
|
1022
|
+
requireAnyScope: () => requireAnyScope,
|
|
1023
|
+
requireScope: () => requireScope,
|
|
1024
|
+
setupOAuthRoutes: () => setupOAuthRoutes
|
|
1025
|
+
});
|
|
1026
|
+
var init_oauth = __esm({
|
|
1027
|
+
"src/server/oauth/index.ts"() {
|
|
1028
|
+
"use strict";
|
|
1029
|
+
init_providers();
|
|
1030
|
+
init_middleware();
|
|
1031
|
+
init_routes();
|
|
1032
|
+
init_utils();
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
30
1035
|
|
|
31
1036
|
// src/server/index.ts
|
|
32
1037
|
var server_exports = {};
|
|
33
1038
|
__export(server_exports, {
|
|
34
1039
|
adaptConnectMiddleware: () => adaptConnectMiddleware,
|
|
35
1040
|
adaptMiddleware: () => adaptMiddleware,
|
|
1041
|
+
array: () => array,
|
|
36
1042
|
buildWidgetUrl: () => buildWidgetUrl,
|
|
37
1043
|
createExternalUrlResource: () => createExternalUrlResource,
|
|
38
1044
|
createMCPServer: () => createMCPServer,
|
|
39
1045
|
createRawHtmlResource: () => createRawHtmlResource,
|
|
40
1046
|
createRemoteDomResource: () => createRemoteDomResource,
|
|
41
1047
|
createUIResourceFromDefinition: () => createUIResourceFromDefinition,
|
|
42
|
-
|
|
1048
|
+
error: () => error,
|
|
1049
|
+
getAuth: () => getAuth,
|
|
1050
|
+
getRequestContext: () => getRequestContext,
|
|
1051
|
+
hasAnyScope: () => hasAnyScope,
|
|
1052
|
+
hasRequestContext: () => hasRequestContext,
|
|
1053
|
+
hasScope: () => hasScope,
|
|
1054
|
+
image: () => image,
|
|
1055
|
+
isExpressMiddleware: () => isExpressMiddleware,
|
|
1056
|
+
oauthAuth0Provider: () => oauthAuth0Provider,
|
|
1057
|
+
oauthCustomProvider: () => oauthCustomProvider,
|
|
1058
|
+
oauthKeycloakProvider: () => oauthKeycloakProvider,
|
|
1059
|
+
oauthSupabaseProvider: () => oauthSupabaseProvider,
|
|
1060
|
+
oauthWorkOSProvider: () => oauthWorkOSProvider,
|
|
1061
|
+
object: () => object,
|
|
1062
|
+
requireAnyScope: () => requireAnyScope,
|
|
1063
|
+
requireScope: () => requireScope,
|
|
1064
|
+
resource: () => resource,
|
|
1065
|
+
runWithContext: () => runWithContext,
|
|
1066
|
+
text: () => text,
|
|
1067
|
+
widget: () => widget
|
|
43
1068
|
});
|
|
44
1069
|
module.exports = __toCommonJS(server_exports);
|
|
45
1070
|
|
|
46
1071
|
// src/server/mcp-server.ts
|
|
47
1072
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
48
|
-
var import_hono = require("hono");
|
|
49
|
-
var import_cors = require("hono/cors");
|
|
50
1073
|
var import_zod = require("zod");
|
|
1074
|
+
var import_hono = require("hono");
|
|
1075
|
+
var import_cors2 = require("hono/cors");
|
|
1076
|
+
|
|
1077
|
+
// src/server/context-storage.ts
|
|
1078
|
+
var import_node_async_hooks = require("async_hooks");
|
|
1079
|
+
var requestContextStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
1080
|
+
async function runWithContext(context, fn) {
|
|
1081
|
+
return requestContextStorage.run(context, fn);
|
|
1082
|
+
}
|
|
1083
|
+
__name(runWithContext, "runWithContext");
|
|
1084
|
+
function getRequestContext() {
|
|
1085
|
+
return requestContextStorage.getStore();
|
|
1086
|
+
}
|
|
1087
|
+
__name(getRequestContext, "getRequestContext");
|
|
1088
|
+
function hasRequestContext() {
|
|
1089
|
+
return requestContextStorage.getStore() !== void 0;
|
|
1090
|
+
}
|
|
1091
|
+
__name(hasRequestContext, "hasRequestContext");
|
|
51
1092
|
|
|
52
1093
|
// src/server/adapters/mcp-ui-adapter.ts
|
|
53
1094
|
var import_server = require("@mcp-ui/server");
|
|
54
|
-
function buildWidgetUrl(
|
|
1095
|
+
function buildWidgetUrl(widget2, props, config) {
|
|
55
1096
|
const url = new URL(
|
|
56
|
-
`/mcp-use/widgets/${
|
|
1097
|
+
`/mcp-use/widgets/${widget2}`,
|
|
57
1098
|
`${config.baseUrl}:${config.port}`
|
|
58
1099
|
);
|
|
59
1100
|
if (props && Object.keys(props).length > 0) {
|
|
@@ -93,17 +1134,17 @@ function createRemoteDomResource(uri, script, framework = "react", encoding = "t
|
|
|
93
1134
|
}
|
|
94
1135
|
__name(createRemoteDomResource, "createRemoteDomResource");
|
|
95
1136
|
function createAppsSdkResource(uri, htmlTemplate, metadata) {
|
|
96
|
-
const
|
|
1137
|
+
const resource2 = {
|
|
97
1138
|
uri,
|
|
98
1139
|
mimeType: "text/html+skybridge",
|
|
99
1140
|
text: htmlTemplate
|
|
100
1141
|
};
|
|
101
1142
|
if (metadata && Object.keys(metadata).length > 0) {
|
|
102
|
-
|
|
1143
|
+
resource2._meta = metadata;
|
|
103
1144
|
}
|
|
104
1145
|
return {
|
|
105
1146
|
type: "resource",
|
|
106
|
-
resource
|
|
1147
|
+
resource: resource2
|
|
107
1148
|
};
|
|
108
1149
|
}
|
|
109
1150
|
__name(createAppsSdkResource, "createAppsSdkResource");
|
|
@@ -199,11 +1240,10 @@ async function adaptConnectMiddleware(connectMiddleware, middlewarePath) {
|
|
|
199
1240
|
const httpMocks = await import("node-mocks-http");
|
|
200
1241
|
createRequest = httpMocks.createRequest;
|
|
201
1242
|
createResponse = httpMocks.createResponse;
|
|
202
|
-
} catch (
|
|
203
|
-
|
|
204
|
-
"
|
|
1243
|
+
} catch (error2) {
|
|
1244
|
+
throw new Error(
|
|
1245
|
+
"\u274C Widget middleware dependencies not installed!\n\nTo use Connect middleware adapters with MCP widgets, you need to install:\n\n npm install node-mocks-http\n # or\n pnpm add node-mocks-http\n\nThis dependency is automatically included in projects created with 'create-mcp-use-app'."
|
|
205
1246
|
);
|
|
206
|
-
throw error;
|
|
207
1247
|
}
|
|
208
1248
|
let normalizedPath = middlewarePath;
|
|
209
1249
|
if (normalizedPath.endsWith("*")) {
|
|
@@ -416,7 +1456,7 @@ async function requestLogger(c, next) {
|
|
|
416
1456
|
} else {
|
|
417
1457
|
console.log("\x1B[33mResponse Body:\x1B[0m (no body)");
|
|
418
1458
|
}
|
|
419
|
-
} catch (
|
|
1459
|
+
} catch (error2) {
|
|
420
1460
|
console.log("\x1B[33mResponse Body:\x1B[0m (unable to read)");
|
|
421
1461
|
}
|
|
422
1462
|
console.log("\x1B[36m" + "=".repeat(80) + "\x1B[0m\n");
|
|
@@ -425,90 +1465,8 @@ async function requestLogger(c, next) {
|
|
|
425
1465
|
__name(requestLogger, "requestLogger");
|
|
426
1466
|
|
|
427
1467
|
// src/server/mcp-server.ts
|
|
428
|
-
|
|
429
|
-
return globalThis.crypto.randomUUID();
|
|
430
|
-
}
|
|
431
|
-
__name(generateUUID, "generateUUID");
|
|
1468
|
+
init_runtime();
|
|
432
1469
|
var TMP_MCP_USE_DIR = ".mcp-use";
|
|
433
|
-
var isDeno2 = typeof globalThis.Deno !== "undefined";
|
|
434
|
-
function getEnv2(key) {
|
|
435
|
-
if (isDeno2) {
|
|
436
|
-
return globalThis.Deno.env.get(key);
|
|
437
|
-
}
|
|
438
|
-
return process.env[key];
|
|
439
|
-
}
|
|
440
|
-
__name(getEnv2, "getEnv");
|
|
441
|
-
function getCwd() {
|
|
442
|
-
if (isDeno2) {
|
|
443
|
-
return globalThis.Deno.cwd();
|
|
444
|
-
}
|
|
445
|
-
return process.cwd();
|
|
446
|
-
}
|
|
447
|
-
__name(getCwd, "getCwd");
|
|
448
|
-
var fsHelpers = {
|
|
449
|
-
async readFileSync(path, encoding = "utf8") {
|
|
450
|
-
if (isDeno2) {
|
|
451
|
-
return await globalThis.Deno.readTextFile(path);
|
|
452
|
-
}
|
|
453
|
-
const { readFileSync } = await import("fs");
|
|
454
|
-
const result = readFileSync(path, encoding);
|
|
455
|
-
return typeof result === "string" ? result : result.toString(encoding);
|
|
456
|
-
},
|
|
457
|
-
async readFile(path) {
|
|
458
|
-
if (isDeno2) {
|
|
459
|
-
const data = await globalThis.Deno.readFile(path);
|
|
460
|
-
return data.buffer;
|
|
461
|
-
}
|
|
462
|
-
const { readFileSync } = await import("fs");
|
|
463
|
-
const buffer = readFileSync(path);
|
|
464
|
-
return buffer.buffer.slice(
|
|
465
|
-
buffer.byteOffset,
|
|
466
|
-
buffer.byteOffset + buffer.byteLength
|
|
467
|
-
);
|
|
468
|
-
},
|
|
469
|
-
async existsSync(path) {
|
|
470
|
-
if (isDeno2) {
|
|
471
|
-
try {
|
|
472
|
-
await globalThis.Deno.stat(path);
|
|
473
|
-
return true;
|
|
474
|
-
} catch {
|
|
475
|
-
return false;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
const { existsSync } = await import("fs");
|
|
479
|
-
return existsSync(path);
|
|
480
|
-
},
|
|
481
|
-
async readdirSync(path) {
|
|
482
|
-
if (isDeno2) {
|
|
483
|
-
const entries = [];
|
|
484
|
-
for await (const entry of globalThis.Deno.readDir(path)) {
|
|
485
|
-
entries.push(entry.name);
|
|
486
|
-
}
|
|
487
|
-
return entries;
|
|
488
|
-
}
|
|
489
|
-
const { readdirSync } = await import("fs");
|
|
490
|
-
return readdirSync(path);
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
var pathHelpers = {
|
|
494
|
-
join(...paths) {
|
|
495
|
-
if (isDeno2) {
|
|
496
|
-
return paths.join("/").replace(/\/+/g, "/");
|
|
497
|
-
}
|
|
498
|
-
return paths.join("/").replace(/\/+/g, "/");
|
|
499
|
-
},
|
|
500
|
-
relative(from, to) {
|
|
501
|
-
const fromParts = from.split("/").filter((p) => p);
|
|
502
|
-
const toParts = to.split("/").filter((p) => p);
|
|
503
|
-
let i = 0;
|
|
504
|
-
while (i < fromParts.length && i < toParts.length && fromParts[i] === toParts[i]) {
|
|
505
|
-
i++;
|
|
506
|
-
}
|
|
507
|
-
const upCount = fromParts.length - i;
|
|
508
|
-
const relativeParts = [...Array(upCount).fill(".."), ...toParts.slice(i)];
|
|
509
|
-
return relativeParts.join("/");
|
|
510
|
-
}
|
|
511
|
-
};
|
|
512
1470
|
var McpServer = class {
|
|
513
1471
|
static {
|
|
514
1472
|
__name(this, "McpServer");
|
|
@@ -527,6 +1485,13 @@ var McpServer = class {
|
|
|
527
1485
|
buildId;
|
|
528
1486
|
sessions = /* @__PURE__ */ new Map();
|
|
529
1487
|
idleCleanupInterval;
|
|
1488
|
+
oauthProvider;
|
|
1489
|
+
// OAuthProvider from oauth/index.js
|
|
1490
|
+
oauthMiddleware;
|
|
1491
|
+
// Bearer auth middleware
|
|
1492
|
+
oauthConfig;
|
|
1493
|
+
// Store OAuth config for lazy initialization
|
|
1494
|
+
oauthSetupComplete = false;
|
|
530
1495
|
/**
|
|
531
1496
|
* Creates a new MCP server instance with Hono integration
|
|
532
1497
|
*
|
|
@@ -548,7 +1513,7 @@ var McpServer = class {
|
|
|
548
1513
|
this.app = new import_hono.Hono();
|
|
549
1514
|
this.app.use(
|
|
550
1515
|
"*",
|
|
551
|
-
(0,
|
|
1516
|
+
(0, import_cors2.cors)({
|
|
552
1517
|
origin: "*",
|
|
553
1518
|
allowMethods: ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
554
1519
|
allowHeaders: [
|
|
@@ -565,6 +1530,9 @@ var McpServer = class {
|
|
|
565
1530
|
})
|
|
566
1531
|
);
|
|
567
1532
|
this.app.use("*", requestLogger);
|
|
1533
|
+
if (config.oauth) {
|
|
1534
|
+
this.oauthConfig = config.oauth;
|
|
1535
|
+
}
|
|
568
1536
|
return new Proxy(this, {
|
|
569
1537
|
get(target, prop) {
|
|
570
1538
|
if (prop === "use") {
|
|
@@ -645,6 +1613,42 @@ var McpServer = class {
|
|
|
645
1613
|
console.log("[CSP] Parsed CSP URLs:", urls);
|
|
646
1614
|
return urls;
|
|
647
1615
|
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Setup OAuth authentication
|
|
1618
|
+
*
|
|
1619
|
+
* Initializes OAuth provider, creates bearer auth middleware,
|
|
1620
|
+
* sets up OAuth routes, and applies auth to /mcp endpoints.
|
|
1621
|
+
*
|
|
1622
|
+
* @private
|
|
1623
|
+
*/
|
|
1624
|
+
async setupOAuth(oauthProvider) {
|
|
1625
|
+
if (this.oauthSetupComplete) {
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
const { setupOAuthRoutes: setupOAuthRoutes2, createBearerAuthMiddleware: createBearerAuthMiddleware2 } = await Promise.resolve().then(() => (init_oauth(), oauth_exports));
|
|
1629
|
+
this.oauthProvider = oauthProvider;
|
|
1630
|
+
console.log(`[OAuth] OAuth provider initialized`);
|
|
1631
|
+
const baseUrl = this.getServerBaseUrl();
|
|
1632
|
+
this.oauthMiddleware = createBearerAuthMiddleware2(
|
|
1633
|
+
this.oauthProvider,
|
|
1634
|
+
baseUrl
|
|
1635
|
+
);
|
|
1636
|
+
setupOAuthRoutes2(this.app, this.oauthProvider, baseUrl);
|
|
1637
|
+
const mode = this.oauthProvider.getMode?.() || "proxy";
|
|
1638
|
+
if (mode === "direct") {
|
|
1639
|
+
console.log(
|
|
1640
|
+
"[OAuth] Direct mode: Clients will authenticate with provider directly"
|
|
1641
|
+
);
|
|
1642
|
+
console.log("[OAuth] Metadata endpoints: /.well-known/*");
|
|
1643
|
+
} else {
|
|
1644
|
+
console.log(
|
|
1645
|
+
"[OAuth] Proxy mode: Routes at /authorize, /token, /.well-known/*"
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
this.app.use("/mcp/*", this.oauthMiddleware);
|
|
1649
|
+
console.log("[OAuth] Bearer authentication enabled on /mcp routes");
|
|
1650
|
+
this.oauthSetupComplete = true;
|
|
1651
|
+
}
|
|
648
1652
|
/**
|
|
649
1653
|
* Define a static resource that can be accessed by clients
|
|
650
1654
|
*
|
|
@@ -771,46 +1775,22 @@ var McpServer = class {
|
|
|
771
1775
|
this.registeredResources.push(resourceTemplateDefinition.name);
|
|
772
1776
|
return this;
|
|
773
1777
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
*
|
|
791
|
-
* @example
|
|
792
|
-
* ```typescript
|
|
793
|
-
* server.tool({
|
|
794
|
-
* name: 'calculate',
|
|
795
|
-
* description: 'Performs mathematical calculations',
|
|
796
|
-
* inputs: [
|
|
797
|
-
* { name: 'expression', type: 'string', required: true },
|
|
798
|
-
* { name: 'precision', type: 'number', required: false }
|
|
799
|
-
* ],
|
|
800
|
-
* cb: async ({ expression, precision = 2 }) => {
|
|
801
|
-
* const result = eval(expression)
|
|
802
|
-
* return { result: Number(result.toFixed(precision)) }
|
|
803
|
-
* },
|
|
804
|
-
* _meta: {
|
|
805
|
-
* 'openai/outputTemplate': 'ui://widgets/calculator',
|
|
806
|
-
* 'openai/toolInvocation/invoking': 'Calculating...',
|
|
807
|
-
* 'openai/toolInvocation/invoked': 'Calculation complete'
|
|
808
|
-
* }
|
|
809
|
-
* })
|
|
810
|
-
* ```
|
|
811
|
-
*/
|
|
812
|
-
tool(toolDefinition) {
|
|
813
|
-
const inputSchema = this.createParamsSchema(toolDefinition.inputs || []);
|
|
1778
|
+
// Implementation
|
|
1779
|
+
tool(toolDefinition, callback) {
|
|
1780
|
+
const actualCallback = callback || toolDefinition.cb;
|
|
1781
|
+
if (!actualCallback) {
|
|
1782
|
+
throw new Error(
|
|
1783
|
+
`Tool '${toolDefinition.name}' must have either a cb property or a callback parameter`
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
let inputSchema;
|
|
1787
|
+
if (toolDefinition.schema) {
|
|
1788
|
+
inputSchema = this.convertZodSchemaToParams(toolDefinition.schema);
|
|
1789
|
+
} else if (toolDefinition.inputs && toolDefinition.inputs.length > 0) {
|
|
1790
|
+
inputSchema = this.createParamsSchema(toolDefinition.inputs);
|
|
1791
|
+
} else {
|
|
1792
|
+
inputSchema = {};
|
|
1793
|
+
}
|
|
814
1794
|
this.server.registerTool(
|
|
815
1795
|
toolDefinition.name,
|
|
816
1796
|
{
|
|
@@ -821,101 +1801,95 @@ var McpServer = class {
|
|
|
821
1801
|
_meta: toolDefinition._meta
|
|
822
1802
|
},
|
|
823
1803
|
async (params, extra) => {
|
|
1804
|
+
let requestContext = getRequestContext();
|
|
1805
|
+
if (!requestContext) {
|
|
1806
|
+
for (const [, session] of this.sessions.entries()) {
|
|
1807
|
+
if (session.context) {
|
|
1808
|
+
requestContext = session.context;
|
|
1809
|
+
break;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
824
1813
|
const progressToken = extra?._meta?.progressToken;
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
onProgress
|
|
845
|
-
} = options ?? {};
|
|
846
|
-
let progressCount = 0;
|
|
847
|
-
let completed = false;
|
|
848
|
-
let progressInterval = null;
|
|
849
|
-
if (progressToken && extra?.sendNotification) {
|
|
850
|
-
progressInterval = setInterval(async () => {
|
|
851
|
-
if (completed) return;
|
|
852
|
-
progressCount++;
|
|
853
|
-
const progressData = {
|
|
854
|
-
progress: progressCount,
|
|
855
|
-
total: void 0,
|
|
856
|
-
message: `Waiting for LLM response... (${progressCount * Math.round(progressIntervalMs / 1e3)}s elapsed)`
|
|
857
|
-
};
|
|
858
|
-
if (onProgress) {
|
|
859
|
-
try {
|
|
860
|
-
onProgress(progressData);
|
|
861
|
-
} catch {
|
|
862
|
-
}
|
|
863
|
-
}
|
|
1814
|
+
const enhancedContext = requestContext ? Object.create(requestContext) : {};
|
|
1815
|
+
enhancedContext.sample = async (sampleParams, options) => {
|
|
1816
|
+
const {
|
|
1817
|
+
timeout,
|
|
1818
|
+
progressIntervalMs = 5e3,
|
|
1819
|
+
onProgress
|
|
1820
|
+
} = options ?? {};
|
|
1821
|
+
let progressCount = 0;
|
|
1822
|
+
let completed = false;
|
|
1823
|
+
let progressInterval = null;
|
|
1824
|
+
if (progressToken && extra?.sendNotification) {
|
|
1825
|
+
progressInterval = setInterval(async () => {
|
|
1826
|
+
if (completed) return;
|
|
1827
|
+
progressCount++;
|
|
1828
|
+
const progressData = {
|
|
1829
|
+
progress: progressCount,
|
|
1830
|
+
total: void 0,
|
|
1831
|
+
message: `Waiting for LLM response... (${progressCount * Math.round(progressIntervalMs / 1e3)}s elapsed)`
|
|
1832
|
+
};
|
|
1833
|
+
if (onProgress) {
|
|
864
1834
|
try {
|
|
865
|
-
|
|
866
|
-
method: "notifications/progress",
|
|
867
|
-
params: {
|
|
868
|
-
progressToken,
|
|
869
|
-
progress: progressData.progress,
|
|
870
|
-
total: progressData.total,
|
|
871
|
-
message: progressData.message
|
|
872
|
-
}
|
|
873
|
-
});
|
|
1835
|
+
onProgress(progressData);
|
|
874
1836
|
} catch {
|
|
875
1837
|
}
|
|
876
|
-
}, progressIntervalMs);
|
|
877
|
-
}
|
|
878
|
-
try {
|
|
879
|
-
const samplePromise = this.createMessage(sampleParams);
|
|
880
|
-
if (timeout && timeout !== Infinity) {
|
|
881
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
882
|
-
setTimeout(
|
|
883
|
-
() => reject(
|
|
884
|
-
new Error(`Sampling timed out after ${timeout}ms`)
|
|
885
|
-
),
|
|
886
|
-
timeout
|
|
887
|
-
);
|
|
888
|
-
});
|
|
889
|
-
return await Promise.race([samplePromise, timeoutPromise]);
|
|
890
1838
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1839
|
+
try {
|
|
1840
|
+
await extra.sendNotification({
|
|
1841
|
+
method: "notifications/progress",
|
|
1842
|
+
params: {
|
|
1843
|
+
progressToken,
|
|
1844
|
+
progress: progressData.progress,
|
|
1845
|
+
total: progressData.total,
|
|
1846
|
+
message: progressData.message
|
|
1847
|
+
}
|
|
1848
|
+
});
|
|
1849
|
+
} catch {
|
|
896
1850
|
}
|
|
1851
|
+
}, progressIntervalMs);
|
|
1852
|
+
}
|
|
1853
|
+
try {
|
|
1854
|
+
const samplePromise = this.createMessage(sampleParams);
|
|
1855
|
+
if (timeout && timeout !== Infinity) {
|
|
1856
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1857
|
+
setTimeout(
|
|
1858
|
+
() => reject(new Error(`Sampling timed out after ${timeout}ms`)),
|
|
1859
|
+
timeout
|
|
1860
|
+
);
|
|
1861
|
+
});
|
|
1862
|
+
return await Promise.race([samplePromise, timeoutPromise]);
|
|
897
1863
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
method: "notifications/progress",
|
|
906
|
-
params: {
|
|
907
|
-
progressToken,
|
|
908
|
-
progress,
|
|
909
|
-
total,
|
|
910
|
-
message
|
|
911
|
-
}
|
|
912
|
-
});
|
|
913
|
-
} : void 0
|
|
1864
|
+
return await samplePromise;
|
|
1865
|
+
} finally {
|
|
1866
|
+
completed = true;
|
|
1867
|
+
if (progressInterval) {
|
|
1868
|
+
clearInterval(progressInterval);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
914
1871
|
};
|
|
915
|
-
|
|
916
|
-
|
|
1872
|
+
enhancedContext.reportProgress = progressToken && extra?.sendNotification ? async (progress, total, message) => {
|
|
1873
|
+
await extra.sendNotification({
|
|
1874
|
+
method: "notifications/progress",
|
|
1875
|
+
params: {
|
|
1876
|
+
progressToken,
|
|
1877
|
+
progress,
|
|
1878
|
+
total,
|
|
1879
|
+
message
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
} : void 0;
|
|
1883
|
+
const executeCallback = /* @__PURE__ */ __name(async () => {
|
|
1884
|
+
if (actualCallback.length >= 2) {
|
|
1885
|
+
return await actualCallback(params, enhancedContext);
|
|
1886
|
+
}
|
|
1887
|
+
return await actualCallback(params);
|
|
1888
|
+
}, "executeCallback");
|
|
1889
|
+
if (requestContext) {
|
|
1890
|
+
return await runWithContext(requestContext, executeCallback);
|
|
917
1891
|
}
|
|
918
|
-
return await
|
|
1892
|
+
return await executeCallback();
|
|
919
1893
|
}
|
|
920
1894
|
);
|
|
921
1895
|
this.registeredTools.push(toolDefinition.name);
|
|
@@ -963,6 +1937,7 @@ var McpServer = class {
|
|
|
963
1937
|
title: promptDefinition.title,
|
|
964
1938
|
description: promptDefinition.description ?? "",
|
|
965
1939
|
argsSchema
|
|
1940
|
+
// Type assertion for Zod v4 compatibility
|
|
966
1941
|
},
|
|
967
1942
|
async (params) => {
|
|
968
1943
|
return await promptDefinition.cb(params);
|
|
@@ -1266,35 +2241,6 @@ var McpServer = class {
|
|
|
1266
2241
|
}
|
|
1267
2242
|
return `ui://widget/${parts.join("-")}${extension}`;
|
|
1268
2243
|
}
|
|
1269
|
-
/**
|
|
1270
|
-
* Build a complete URL for a widget including query parameters
|
|
1271
|
-
*
|
|
1272
|
-
* Constructs the full URL to access a widget's iframe, encoding any provided
|
|
1273
|
-
* parameters as query string parameters. Complex objects are JSON-stringified
|
|
1274
|
-
* for transmission.
|
|
1275
|
-
*
|
|
1276
|
-
* @private
|
|
1277
|
-
* @param widget - Widget name/identifier
|
|
1278
|
-
* @param params - Parameters to encode in the URL
|
|
1279
|
-
* @returns Complete URL with encoded parameters
|
|
1280
|
-
*/
|
|
1281
|
-
buildWidgetUrl(widget, params) {
|
|
1282
|
-
const baseUrl = `http://${this.serverHost}:${this.serverPort}/mcp-use/widgets/${widget}`;
|
|
1283
|
-
if (Object.keys(params).length === 0) {
|
|
1284
|
-
return baseUrl;
|
|
1285
|
-
}
|
|
1286
|
-
const queryParams = new URLSearchParams();
|
|
1287
|
-
for (const [key, value] of Object.entries(params)) {
|
|
1288
|
-
if (value !== void 0 && value !== null) {
|
|
1289
|
-
if (typeof value === "object") {
|
|
1290
|
-
queryParams.append(key, JSON.stringify(value));
|
|
1291
|
-
} else {
|
|
1292
|
-
queryParams.append(key, String(value));
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
return `${baseUrl}?${queryParams.toString()}`;
|
|
1297
|
-
}
|
|
1298
2244
|
/**
|
|
1299
2245
|
* Convert widget props definition to tool input schema
|
|
1300
2246
|
*
|
|
@@ -1403,7 +2349,7 @@ var McpServer = class {
|
|
|
1403
2349
|
const srcDir = pathHelpers.join(getCwd(), resourcesDir);
|
|
1404
2350
|
try {
|
|
1405
2351
|
await fs.access(srcDir);
|
|
1406
|
-
} catch (
|
|
2352
|
+
} catch (error2) {
|
|
1407
2353
|
console.log(
|
|
1408
2354
|
`[WIDGETS] No ${resourcesDir}/ directory found - skipping widget serving`
|
|
1409
2355
|
);
|
|
@@ -1437,7 +2383,7 @@ var McpServer = class {
|
|
|
1437
2383
|
}
|
|
1438
2384
|
}
|
|
1439
2385
|
}
|
|
1440
|
-
} catch (
|
|
2386
|
+
} catch (error2) {
|
|
1441
2387
|
console.log(`[WIDGETS] No widgets found in ${resourcesDir}/ directory`);
|
|
1442
2388
|
return;
|
|
1443
2389
|
}
|
|
@@ -1462,14 +2408,10 @@ var McpServer = class {
|
|
|
1462
2408
|
'return import("@tailwindcss/vite")'
|
|
1463
2409
|
)();
|
|
1464
2410
|
tailwindcss = tailwindModule.default;
|
|
1465
|
-
} catch (
|
|
1466
|
-
|
|
1467
|
-
"
|
|
1468
|
-
);
|
|
1469
|
-
console.error(
|
|
1470
|
-
"[WIDGETS] For production, use 'mcp-use build' to pre-build widgets."
|
|
2411
|
+
} catch (error2) {
|
|
2412
|
+
throw new Error(
|
|
2413
|
+
"\u274C Widget dependencies not installed!\n\nTo use MCP widgets with resources folder, you need to install the required dependencies:\n\n npm install vite @vitejs/plugin-react @tailwindcss/vite\n # or\n pnpm add vite @vitejs/plugin-react @tailwindcss/vite\n\nThese dependencies are automatically included in projects created with 'create-mcp-use-app'.\nFor production, pre-build your widgets using 'mcp-use build'."
|
|
1471
2414
|
);
|
|
1472
|
-
return;
|
|
1473
2415
|
}
|
|
1474
2416
|
const widgets = entries.map((entry) => {
|
|
1475
2417
|
return {
|
|
@@ -1478,8 +2420,8 @@ var McpServer = class {
|
|
|
1478
2420
|
entry: entry.path
|
|
1479
2421
|
};
|
|
1480
2422
|
});
|
|
1481
|
-
for (const
|
|
1482
|
-
const widgetTempDir = pathHelpers.join(tempDir,
|
|
2423
|
+
for (const widget2 of widgets) {
|
|
2424
|
+
const widgetTempDir = pathHelpers.join(tempDir, widget2.name);
|
|
1483
2425
|
await fs.mkdir(widgetTempDir, { recursive: true });
|
|
1484
2426
|
const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
|
|
1485
2427
|
const relativeResourcesPath = pathHelpers.relative(widgetTempDir, resourcesPath).replace(/\\/g, "/");
|
|
@@ -1496,7 +2438,7 @@ var McpServer = class {
|
|
|
1496
2438
|
const entryContent = `import React from 'react'
|
|
1497
2439
|
import { createRoot } from 'react-dom/client'
|
|
1498
2440
|
import './styles.css'
|
|
1499
|
-
import Component from '${
|
|
2441
|
+
import Component from '${widget2.entry}'
|
|
1500
2442
|
|
|
1501
2443
|
const container = document.getElementById('widget-root')
|
|
1502
2444
|
if (container && Component) {
|
|
@@ -1509,11 +2451,11 @@ if (container && Component) {
|
|
|
1509
2451
|
<head>
|
|
1510
2452
|
<meta charset="UTF-8" />
|
|
1511
2453
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
1512
|
-
<title>${
|
|
2454
|
+
<title>${widget2.name} Widget</title>
|
|
1513
2455
|
</head>
|
|
1514
2456
|
<body>
|
|
1515
2457
|
<div id="widget-root"></div>
|
|
1516
|
-
<script type="module" src="${baseRoute}/${
|
|
2458
|
+
<script type="module" src="${baseRoute}/${widget2.name}/entry.tsx"></script>
|
|
1517
2459
|
</body>
|
|
1518
2460
|
</html>`;
|
|
1519
2461
|
await fs.writeFile(
|
|
@@ -1603,8 +2545,8 @@ if (container && Component) {
|
|
|
1603
2545
|
const widgetMatch = pathname.replace(baseRoute, "").match(/^\/([^/]+)/);
|
|
1604
2546
|
if (widgetMatch) {
|
|
1605
2547
|
const widgetName = widgetMatch[1];
|
|
1606
|
-
const
|
|
1607
|
-
if (
|
|
2548
|
+
const widget2 = widgets.find((w) => w.name === widgetName);
|
|
2549
|
+
if (widget2) {
|
|
1608
2550
|
const relativePath = pathname.replace(baseRoute, "");
|
|
1609
2551
|
if (relativePath === `/${widgetName}` || relativePath === `/${widgetName}/`) {
|
|
1610
2552
|
const newUrl = new URL(c.req.url);
|
|
@@ -1655,42 +2597,42 @@ if (container && Component) {
|
|
|
1655
2597
|
const message = isAsset ? "Widget asset not found" : "Widget not found";
|
|
1656
2598
|
return c.text(message, 404);
|
|
1657
2599
|
});
|
|
1658
|
-
widgets.forEach((
|
|
2600
|
+
widgets.forEach((widget2) => {
|
|
1659
2601
|
console.log(
|
|
1660
|
-
`[WIDGET] ${
|
|
2602
|
+
`[WIDGET] ${widget2.name} mounted at ${baseRoute}/${widget2.name}`
|
|
1661
2603
|
);
|
|
1662
2604
|
});
|
|
1663
|
-
for (const
|
|
2605
|
+
for (const widget2 of widgets) {
|
|
1664
2606
|
const type = "appsSdk";
|
|
1665
2607
|
let metadata = {};
|
|
1666
2608
|
let props = {};
|
|
1667
|
-
let description =
|
|
2609
|
+
let description = widget2.description;
|
|
1668
2610
|
try {
|
|
1669
|
-
const mod = await viteServer.ssrLoadModule(
|
|
2611
|
+
const mod = await viteServer.ssrLoadModule(widget2.entry);
|
|
1670
2612
|
if (mod.widgetMetadata) {
|
|
1671
2613
|
metadata = mod.widgetMetadata;
|
|
1672
|
-
description = metadata.description ||
|
|
2614
|
+
description = metadata.description || widget2.description;
|
|
1673
2615
|
if (metadata.inputs) {
|
|
1674
2616
|
try {
|
|
1675
2617
|
props = metadata.inputs.shape || {};
|
|
1676
|
-
} catch (
|
|
2618
|
+
} catch (error2) {
|
|
1677
2619
|
console.warn(
|
|
1678
|
-
`[WIDGET] Failed to extract props schema for ${
|
|
1679
|
-
|
|
2620
|
+
`[WIDGET] Failed to extract props schema for ${widget2.name}:`,
|
|
2621
|
+
error2
|
|
1680
2622
|
);
|
|
1681
2623
|
}
|
|
1682
2624
|
}
|
|
1683
2625
|
}
|
|
1684
|
-
} catch (
|
|
2626
|
+
} catch (error2) {
|
|
1685
2627
|
console.warn(
|
|
1686
|
-
`[WIDGET] Failed to load metadata for ${
|
|
1687
|
-
|
|
2628
|
+
`[WIDGET] Failed to load metadata for ${widget2.name}:`,
|
|
2629
|
+
error2
|
|
1688
2630
|
);
|
|
1689
2631
|
}
|
|
1690
2632
|
let html = "";
|
|
1691
2633
|
try {
|
|
1692
2634
|
html = await fsHelpers.readFileSync(
|
|
1693
|
-
pathHelpers.join(tempDir,
|
|
2635
|
+
pathHelpers.join(tempDir, widget2.name, "index.html"),
|
|
1694
2636
|
"utf8"
|
|
1695
2637
|
);
|
|
1696
2638
|
const mcpUrl = this.getServerBaseUrl();
|
|
@@ -1728,25 +2670,25 @@ if (container && Component) {
|
|
|
1728
2670
|
html = html.replace(
|
|
1729
2671
|
/<head[^>]*>/i,
|
|
1730
2672
|
`<head>
|
|
1731
|
-
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${
|
|
2673
|
+
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${widget2.name}/"+filename }; window.__mcpPublicUrl = "${baseUrl}/mcp-use/public";</script>`
|
|
1732
2674
|
);
|
|
1733
|
-
} catch (
|
|
2675
|
+
} catch (error2) {
|
|
1734
2676
|
console.error(
|
|
1735
|
-
`Failed to read html template for widget ${
|
|
1736
|
-
|
|
2677
|
+
`Failed to read html template for widget ${widget2.name}`,
|
|
2678
|
+
error2
|
|
1737
2679
|
);
|
|
1738
2680
|
}
|
|
1739
2681
|
const mcp_connect_domain = this.getServerBaseUrl() ? new URL(this.getServerBaseUrl() || "").origin : null;
|
|
1740
2682
|
this.uiResource({
|
|
1741
|
-
name:
|
|
1742
|
-
title: metadata.title ||
|
|
2683
|
+
name: widget2.name,
|
|
2684
|
+
title: metadata.title || widget2.name,
|
|
1743
2685
|
description,
|
|
1744
2686
|
type,
|
|
1745
2687
|
props,
|
|
1746
2688
|
_meta: {
|
|
1747
2689
|
"mcp-use/widget": {
|
|
1748
|
-
name:
|
|
1749
|
-
title: metadata.title ||
|
|
2690
|
+
name: widget2.name,
|
|
2691
|
+
title: metadata.title || widget2.name,
|
|
1750
2692
|
description,
|
|
1751
2693
|
type,
|
|
1752
2694
|
props,
|
|
@@ -1758,8 +2700,8 @@ if (container && Component) {
|
|
|
1758
2700
|
htmlTemplate: html,
|
|
1759
2701
|
appsSdkMetadata: {
|
|
1760
2702
|
"openai/widgetDescription": description,
|
|
1761
|
-
"openai/toolInvocation/invoking": `Loading ${
|
|
1762
|
-
"openai/toolInvocation/invoked": `${
|
|
2703
|
+
"openai/toolInvocation/invoking": `Loading ${widget2.name}...`,
|
|
2704
|
+
"openai/toolInvocation/invoked": `${widget2.name} ready`,
|
|
1763
2705
|
"openai/widgetAccessible": true,
|
|
1764
2706
|
"openai/resultCanProduceWidget": true,
|
|
1765
2707
|
...metadata.appsSdkMetadata || {},
|
|
@@ -1832,10 +2774,10 @@ if (container && Component) {
|
|
|
1832
2774
|
} else {
|
|
1833
2775
|
console.log("[WIDGETS] No widgets found in manifest");
|
|
1834
2776
|
}
|
|
1835
|
-
} catch (
|
|
2777
|
+
} catch (error2) {
|
|
1836
2778
|
console.log(
|
|
1837
2779
|
"[WIDGETS] Could not read manifest file, falling back to directory listing:",
|
|
1838
|
-
|
|
2780
|
+
error2
|
|
1839
2781
|
);
|
|
1840
2782
|
try {
|
|
1841
2783
|
const allEntries = await fsHelpers.readdirSync(widgetsDir);
|
|
@@ -1900,10 +2842,10 @@ if (container && Component) {
|
|
|
1900
2842
|
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${widgetName}/"+filename }; window.__mcpPublicUrl = "${baseUrl}/mcp-use/public";</script>`
|
|
1901
2843
|
);
|
|
1902
2844
|
}
|
|
1903
|
-
} catch (
|
|
2845
|
+
} catch (error2) {
|
|
1904
2846
|
console.error(
|
|
1905
2847
|
`[WIDGET] Failed to read ${widgetName}/index.html:`,
|
|
1906
|
-
|
|
2848
|
+
error2
|
|
1907
2849
|
);
|
|
1908
2850
|
continue;
|
|
1909
2851
|
}
|
|
@@ -2030,7 +2972,7 @@ if (container && Component) {
|
|
|
2030
2972
|
if (closeOldSessionId && this.sessions.has(closeOldSessionId)) {
|
|
2031
2973
|
try {
|
|
2032
2974
|
this.sessions.get(closeOldSessionId).transport.close();
|
|
2033
|
-
} catch (
|
|
2975
|
+
} catch (error2) {
|
|
2034
2976
|
}
|
|
2035
2977
|
this.sessions.delete(closeOldSessionId);
|
|
2036
2978
|
}
|
|
@@ -2161,7 +3103,7 @@ if (container && Component) {
|
|
|
2161
3103
|
if (now - session.lastAccessedAt > idleTimeoutMs) {
|
|
2162
3104
|
try {
|
|
2163
3105
|
session.transport.close();
|
|
2164
|
-
} catch (
|
|
3106
|
+
} catch (error2) {
|
|
2165
3107
|
}
|
|
2166
3108
|
this.sessions.delete(sessionId);
|
|
2167
3109
|
}
|
|
@@ -2317,19 +3259,23 @@ if (container && Component) {
|
|
|
2317
3259
|
}
|
|
2318
3260
|
}
|
|
2319
3261
|
if (sessionId && this.sessions.has(sessionId)) {
|
|
2320
|
-
this.sessions.get(sessionId)
|
|
3262
|
+
const session = this.sessions.get(sessionId);
|
|
3263
|
+
session.lastAccessedAt = Date.now();
|
|
3264
|
+
session.context = c;
|
|
2321
3265
|
}
|
|
2322
3266
|
if (expressRes._closeHandler) {
|
|
2323
3267
|
c.req.raw.signal?.addEventListener("abort", () => {
|
|
2324
3268
|
transport.close();
|
|
2325
3269
|
});
|
|
2326
3270
|
}
|
|
2327
|
-
await
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
3271
|
+
await runWithContext(c, async () => {
|
|
3272
|
+
await this.waitForRequestComplete(
|
|
3273
|
+
transport,
|
|
3274
|
+
expressReq,
|
|
3275
|
+
expressRes,
|
|
3276
|
+
expressReq.body
|
|
3277
|
+
);
|
|
3278
|
+
});
|
|
2333
3279
|
const response = getResponse();
|
|
2334
3280
|
if (response) {
|
|
2335
3281
|
return response;
|
|
@@ -2573,6 +3519,9 @@ if (container && Component) {
|
|
|
2573
3519
|
if (hostEnv) {
|
|
2574
3520
|
this.serverHost = hostEnv;
|
|
2575
3521
|
}
|
|
3522
|
+
if (this.oauthConfig && !this.oauthSetupComplete) {
|
|
3523
|
+
await this.setupOAuth(this.oauthConfig);
|
|
3524
|
+
}
|
|
2576
3525
|
await this.mountWidgets({
|
|
2577
3526
|
baseRoute: "/mcp-use/widgets",
|
|
2578
3527
|
resourcesDir: "resources"
|
|
@@ -2681,6 +3630,9 @@ if (container && Component) {
|
|
|
2681
3630
|
* ```
|
|
2682
3631
|
*/
|
|
2683
3632
|
async getHandler(options) {
|
|
3633
|
+
if (this.oauthConfig && !this.oauthSetupComplete) {
|
|
3634
|
+
await this.setupOAuth(this.oauthConfig);
|
|
3635
|
+
}
|
|
2684
3636
|
console.log("[MCP] Mounting widgets");
|
|
2685
3637
|
await this.mountWidgets({
|
|
2686
3638
|
baseRoute: "/mcp-use/widgets",
|
|
@@ -2784,10 +3736,10 @@ if (container && Component) {
|
|
|
2784
3736
|
for (const [sessionId, session] of this.sessions.entries()) {
|
|
2785
3737
|
try {
|
|
2786
3738
|
await session.transport.send(notification);
|
|
2787
|
-
} catch (
|
|
3739
|
+
} catch (error2) {
|
|
2788
3740
|
console.warn(
|
|
2789
3741
|
`[MCP] Failed to send notification to session ${sessionId}:`,
|
|
2790
|
-
|
|
3742
|
+
error2
|
|
2791
3743
|
);
|
|
2792
3744
|
}
|
|
2793
3745
|
}
|
|
@@ -2832,10 +3784,10 @@ if (container && Component) {
|
|
|
2832
3784
|
try {
|
|
2833
3785
|
await session.transport.send(notification);
|
|
2834
3786
|
return true;
|
|
2835
|
-
} catch (
|
|
3787
|
+
} catch (error2) {
|
|
2836
3788
|
console.warn(
|
|
2837
3789
|
`[MCP] Failed to send notification to session ${sessionId}:`,
|
|
2838
|
-
|
|
3790
|
+
error2
|
|
2839
3791
|
);
|
|
2840
3792
|
return false;
|
|
2841
3793
|
}
|
|
@@ -2903,10 +3855,10 @@ if (container && Component) {
|
|
|
2903
3855
|
return response.roots;
|
|
2904
3856
|
}
|
|
2905
3857
|
return [];
|
|
2906
|
-
} catch (
|
|
3858
|
+
} catch (error2) {
|
|
2907
3859
|
console.warn(
|
|
2908
3860
|
`[MCP] Failed to list roots from session ${sessionId}:`,
|
|
2909
|
-
|
|
3861
|
+
error2
|
|
2910
3862
|
);
|
|
2911
3863
|
return null;
|
|
2912
3864
|
}
|
|
@@ -2984,14 +3936,14 @@ if (container && Component) {
|
|
|
2984
3936
|
*/
|
|
2985
3937
|
setupWidgetRoutes() {
|
|
2986
3938
|
this.app.get("/mcp-use/widgets/:widget/assets/*", async (c) => {
|
|
2987
|
-
const
|
|
3939
|
+
const widget2 = c.req.param("widget");
|
|
2988
3940
|
const assetFile = c.req.path.split("/assets/")[1];
|
|
2989
3941
|
const assetPath = pathHelpers.join(
|
|
2990
3942
|
getCwd(),
|
|
2991
3943
|
"dist",
|
|
2992
3944
|
"resources",
|
|
2993
3945
|
"widgets",
|
|
2994
|
-
|
|
3946
|
+
widget2,
|
|
2995
3947
|
"assets",
|
|
2996
3948
|
assetFile
|
|
2997
3949
|
);
|
|
@@ -3020,10 +3972,10 @@ if (container && Component) {
|
|
|
3020
3972
|
);
|
|
3021
3973
|
try {
|
|
3022
3974
|
const widgets = await fsHelpers.readdirSync(widgetsDir);
|
|
3023
|
-
for (const
|
|
3975
|
+
for (const widget2 of widgets) {
|
|
3024
3976
|
const assetPath = pathHelpers.join(
|
|
3025
3977
|
widgetsDir,
|
|
3026
|
-
|
|
3978
|
+
widget2,
|
|
3027
3979
|
"assets",
|
|
3028
3980
|
assetFile
|
|
3029
3981
|
);
|
|
@@ -3043,13 +3995,13 @@ if (container && Component) {
|
|
|
3043
3995
|
}
|
|
3044
3996
|
});
|
|
3045
3997
|
this.app.get("/mcp-use/widgets/:widget", async (c) => {
|
|
3046
|
-
const
|
|
3998
|
+
const widget2 = c.req.param("widget");
|
|
3047
3999
|
const filePath = pathHelpers.join(
|
|
3048
4000
|
getCwd(),
|
|
3049
4001
|
"dist",
|
|
3050
4002
|
"resources",
|
|
3051
4003
|
"widgets",
|
|
3052
|
-
|
|
4004
|
+
widget2,
|
|
3053
4005
|
"index.html"
|
|
3054
4006
|
);
|
|
3055
4007
|
try {
|
|
@@ -3066,7 +4018,7 @@ if (container && Component) {
|
|
|
3066
4018
|
html = html.replace(
|
|
3067
4019
|
/<head[^>]*>/i,
|
|
3068
4020
|
`<head>
|
|
3069
|
-
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${
|
|
4021
|
+
<script>window.__getFile = (filename) => { return "${baseUrl}/mcp-use/widgets/${widget2}/"+filename }</script>`
|
|
3070
4022
|
);
|
|
3071
4023
|
return c.html(html);
|
|
3072
4024
|
} catch {
|
|
@@ -3093,28 +4045,21 @@ if (container && Component) {
|
|
|
3093
4045
|
});
|
|
3094
4046
|
}
|
|
3095
4047
|
/**
|
|
3096
|
-
*
|
|
3097
|
-
*
|
|
3098
|
-
* Parses a URI template string to extract parameter names and generates a Zod
|
|
3099
|
-
* validation schema for those parameters. Used internally for validating resource
|
|
3100
|
-
* template parameters before processing requests.
|
|
4048
|
+
* Convert a Zod object schema to the internal Record<string, z.ZodSchema> format
|
|
3101
4049
|
*
|
|
3102
|
-
* @param
|
|
3103
|
-
* @returns Object mapping parameter names to Zod
|
|
3104
|
-
*
|
|
3105
|
-
* @example
|
|
3106
|
-
* ```typescript
|
|
3107
|
-
* const schema = this.createInputSchema("/users/{id}/posts/{postId}")
|
|
3108
|
-
* // Returns: { id: z.string(), postId: z.string() }
|
|
3109
|
-
* ```
|
|
4050
|
+
* @param zodSchema - Zod object schema to convert
|
|
4051
|
+
* @returns Object mapping parameter names to Zod validation schemas
|
|
3110
4052
|
*/
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
}
|
|
3117
|
-
|
|
4053
|
+
convertZodSchemaToParams(zodSchema) {
|
|
4054
|
+
if (!(zodSchema instanceof import_zod.z.ZodObject)) {
|
|
4055
|
+
throw new Error("schema must be a Zod object schema (z.object({...}))");
|
|
4056
|
+
}
|
|
4057
|
+
const shape = zodSchema.shape;
|
|
4058
|
+
const params = {};
|
|
4059
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
4060
|
+
params[key] = value;
|
|
4061
|
+
}
|
|
4062
|
+
return params;
|
|
3118
4063
|
}
|
|
3119
4064
|
/**
|
|
3120
4065
|
* Create input schema for tools
|
|
@@ -3168,75 +4113,6 @@ if (container && Component) {
|
|
|
3168
4113
|
});
|
|
3169
4114
|
return schema;
|
|
3170
4115
|
}
|
|
3171
|
-
/**
|
|
3172
|
-
* Create arguments schema for prompts
|
|
3173
|
-
*
|
|
3174
|
-
* Converts prompt argument definitions into Zod validation schemas for runtime validation.
|
|
3175
|
-
* Supports common data types (string, number, boolean, object, array) and optional
|
|
3176
|
-
* parameters. Used internally when registering prompt templates with the MCP server.
|
|
3177
|
-
*
|
|
3178
|
-
* @param inputs - Array of argument definitions with name, type, and optional flag
|
|
3179
|
-
* @returns Object mapping argument names to Zod validation schemas
|
|
3180
|
-
*
|
|
3181
|
-
* @example
|
|
3182
|
-
* ```typescript
|
|
3183
|
-
* const schema = this.createPromptArgsSchema([
|
|
3184
|
-
* { name: 'topic', type: 'string', required: true },
|
|
3185
|
-
* { name: 'style', type: 'string', required: false }
|
|
3186
|
-
* ])
|
|
3187
|
-
* // Returns: { topic: z.string(), style: z.string().optional() }
|
|
3188
|
-
* ```
|
|
3189
|
-
*/
|
|
3190
|
-
createPromptArgsSchema(inputs) {
|
|
3191
|
-
const schema = {};
|
|
3192
|
-
inputs.forEach((input) => {
|
|
3193
|
-
let zodType;
|
|
3194
|
-
switch (input.type) {
|
|
3195
|
-
case "string":
|
|
3196
|
-
zodType = import_zod.z.string();
|
|
3197
|
-
break;
|
|
3198
|
-
case "number":
|
|
3199
|
-
zodType = import_zod.z.number();
|
|
3200
|
-
break;
|
|
3201
|
-
case "boolean":
|
|
3202
|
-
zodType = import_zod.z.boolean();
|
|
3203
|
-
break;
|
|
3204
|
-
case "object":
|
|
3205
|
-
zodType = import_zod.z.object({});
|
|
3206
|
-
break;
|
|
3207
|
-
case "array":
|
|
3208
|
-
zodType = import_zod.z.array(import_zod.z.any());
|
|
3209
|
-
break;
|
|
3210
|
-
default:
|
|
3211
|
-
zodType = import_zod.z.any();
|
|
3212
|
-
}
|
|
3213
|
-
if (!input.required) {
|
|
3214
|
-
zodType = zodType.optional();
|
|
3215
|
-
}
|
|
3216
|
-
schema[input.name] = zodType;
|
|
3217
|
-
});
|
|
3218
|
-
return schema;
|
|
3219
|
-
}
|
|
3220
|
-
/**
|
|
3221
|
-
* Extract parameter names from URI template
|
|
3222
|
-
*
|
|
3223
|
-
* Parses a URI template string to extract parameter names enclosed in curly braces.
|
|
3224
|
-
* Used internally to identify dynamic parameters in resource templates and generate
|
|
3225
|
-
* appropriate validation schemas.
|
|
3226
|
-
*
|
|
3227
|
-
* @param uriTemplate - URI template string with parameter placeholders (e.g., "/users/{id}/posts/{postId}")
|
|
3228
|
-
* @returns Array of parameter names found in the template
|
|
3229
|
-
*
|
|
3230
|
-
* @example
|
|
3231
|
-
* ```typescript
|
|
3232
|
-
* const params = this.extractTemplateParams("/users/{id}/posts/{postId}")
|
|
3233
|
-
* // Returns: ["id", "postId"]
|
|
3234
|
-
* ```
|
|
3235
|
-
*/
|
|
3236
|
-
extractTemplateParams(uriTemplate) {
|
|
3237
|
-
const matches = uriTemplate.match(/\{([^}]+)\}/g);
|
|
3238
|
-
return matches ? matches.map((match) => match.slice(1, -1)) : [];
|
|
3239
|
-
}
|
|
3240
4116
|
/**
|
|
3241
4117
|
* Parse parameter values from a URI based on a template
|
|
3242
4118
|
*
|
|
@@ -3279,8 +4155,127 @@ function createMCPServer(name, config = {}) {
|
|
|
3279
4155
|
host: config.host,
|
|
3280
4156
|
baseUrl: config.baseUrl,
|
|
3281
4157
|
allowedOrigins: config.allowedOrigins,
|
|
3282
|
-
sessionIdleTimeoutMs: config.sessionIdleTimeoutMs
|
|
4158
|
+
sessionIdleTimeoutMs: config.sessionIdleTimeoutMs,
|
|
4159
|
+
autoCreateSessionOnInvalidId: config.autoCreateSessionOnInvalidId,
|
|
4160
|
+
oauth: config.oauth
|
|
3283
4161
|
});
|
|
3284
4162
|
return instance;
|
|
3285
4163
|
}
|
|
3286
4164
|
__name(createMCPServer, "createMCPServer");
|
|
4165
|
+
|
|
4166
|
+
// src/server/utils/response-helpers.ts
|
|
4167
|
+
function text(content) {
|
|
4168
|
+
return {
|
|
4169
|
+
content: [
|
|
4170
|
+
{
|
|
4171
|
+
type: "text",
|
|
4172
|
+
text: content
|
|
4173
|
+
}
|
|
4174
|
+
]
|
|
4175
|
+
};
|
|
4176
|
+
}
|
|
4177
|
+
__name(text, "text");
|
|
4178
|
+
function image(data, mimeType = "image/png") {
|
|
4179
|
+
return {
|
|
4180
|
+
content: [
|
|
4181
|
+
{
|
|
4182
|
+
type: "image",
|
|
4183
|
+
data,
|
|
4184
|
+
mimeType
|
|
4185
|
+
}
|
|
4186
|
+
]
|
|
4187
|
+
};
|
|
4188
|
+
}
|
|
4189
|
+
__name(image, "image");
|
|
4190
|
+
function resource(uri, mimeType, text2) {
|
|
4191
|
+
const resourceContent = {
|
|
4192
|
+
type: "resource",
|
|
4193
|
+
resource: {
|
|
4194
|
+
uri,
|
|
4195
|
+
...mimeType && { mimeType },
|
|
4196
|
+
...text2 && { text: text2 }
|
|
4197
|
+
}
|
|
4198
|
+
};
|
|
4199
|
+
return {
|
|
4200
|
+
content: [resourceContent]
|
|
4201
|
+
};
|
|
4202
|
+
}
|
|
4203
|
+
__name(resource, "resource");
|
|
4204
|
+
function error(message) {
|
|
4205
|
+
return {
|
|
4206
|
+
isError: true,
|
|
4207
|
+
content: [
|
|
4208
|
+
{
|
|
4209
|
+
type: "text",
|
|
4210
|
+
text: message
|
|
4211
|
+
}
|
|
4212
|
+
]
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
__name(error, "error");
|
|
4216
|
+
function object(data) {
|
|
4217
|
+
return Array.isArray(data) ? array(data) : {
|
|
4218
|
+
content: [
|
|
4219
|
+
{
|
|
4220
|
+
type: "text",
|
|
4221
|
+
text: JSON.stringify(data, null, 2)
|
|
4222
|
+
}
|
|
4223
|
+
],
|
|
4224
|
+
structuredContent: data
|
|
4225
|
+
};
|
|
4226
|
+
}
|
|
4227
|
+
__name(object, "object");
|
|
4228
|
+
function array(data) {
|
|
4229
|
+
return {
|
|
4230
|
+
content: [
|
|
4231
|
+
{
|
|
4232
|
+
type: "text",
|
|
4233
|
+
text: JSON.stringify(data, null, 2)
|
|
4234
|
+
}
|
|
4235
|
+
],
|
|
4236
|
+
structuredContent: { data }
|
|
4237
|
+
};
|
|
4238
|
+
}
|
|
4239
|
+
__name(array, "array");
|
|
4240
|
+
function widget(config) {
|
|
4241
|
+
const {
|
|
4242
|
+
name,
|
|
4243
|
+
data,
|
|
4244
|
+
message,
|
|
4245
|
+
invoking,
|
|
4246
|
+
invoked,
|
|
4247
|
+
widgetAccessible = true,
|
|
4248
|
+
resultCanProduceWidget = true,
|
|
4249
|
+
buildId
|
|
4250
|
+
} = config;
|
|
4251
|
+
const randomId = Math.random().toString(36).substring(2, 15);
|
|
4252
|
+
const buildIdPart = buildId ? `-${buildId}` : "";
|
|
4253
|
+
const uniqueUri = `ui://widget/${name}${buildIdPart}-${randomId}.html`;
|
|
4254
|
+
const metadata = {
|
|
4255
|
+
"openai/outputTemplate": uniqueUri,
|
|
4256
|
+
"openai/widgetAccessible": widgetAccessible,
|
|
4257
|
+
"openai/resultCanProduceWidget": resultCanProduceWidget
|
|
4258
|
+
};
|
|
4259
|
+
if (invoking) {
|
|
4260
|
+
metadata["openai/toolInvocation/invoking"] = invoking;
|
|
4261
|
+
}
|
|
4262
|
+
if (invoked) {
|
|
4263
|
+
metadata["openai/toolInvocation/invoked"] = invoked;
|
|
4264
|
+
}
|
|
4265
|
+
const displayMessage = message || `Displaying ${name}`;
|
|
4266
|
+
return {
|
|
4267
|
+
_meta: metadata,
|
|
4268
|
+
content: [
|
|
4269
|
+
{
|
|
4270
|
+
type: "text",
|
|
4271
|
+
text: displayMessage
|
|
4272
|
+
}
|
|
4273
|
+
],
|
|
4274
|
+
// structuredContent will be injected as window.openai.toolOutput by Apps SDK
|
|
4275
|
+
structuredContent: data
|
|
4276
|
+
};
|
|
4277
|
+
}
|
|
4278
|
+
__name(widget, "widget");
|
|
4279
|
+
|
|
4280
|
+
// src/server/index.ts
|
|
4281
|
+
init_oauth();
|