mcp-use 1.2.4-canary.1 → 1.2.5-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.cjs +0 -38
- package/dist/src/agents/index.cjs +0 -6
- package/dist/src/browser.cjs +0 -20
- package/dist/src/react/index.cjs +0 -9
- package/dist/src/server/index.cjs +525 -152
- package/dist/src/server/index.js +523 -141
- package/dist/src/server/logging.d.ts +4 -5
- package/dist/src/server/logging.d.ts.map +1 -1
- package/dist/src/server/mcp-server.d.ts +41 -9
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/tsup.config.d.ts.map +1 -1
- package/package.json +5 -11
package/dist/src/server/index.js
CHANGED
|
@@ -7,42 +7,9 @@ import {
|
|
|
7
7
|
McpServer as OfficialMcpServer,
|
|
8
8
|
ResourceTemplate
|
|
9
9
|
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { Hono } from "hono";
|
|
11
|
+
import { cors } from "hono/cors";
|
|
10
12
|
import { z } from "zod";
|
|
11
|
-
import express from "express";
|
|
12
|
-
import cors from "cors";
|
|
13
|
-
import { existsSync, readdirSync } from "fs";
|
|
14
|
-
import { join } from "path";
|
|
15
|
-
import { readFileSync } from "fs";
|
|
16
|
-
|
|
17
|
-
// src/server/logging.ts
|
|
18
|
-
function requestLogger(req, res, next) {
|
|
19
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().substring(11, 23);
|
|
20
|
-
const method = req.method;
|
|
21
|
-
const url = req.url;
|
|
22
|
-
const originalEnd = res.end.bind(res);
|
|
23
|
-
res.end = function(chunk, encoding, cb) {
|
|
24
|
-
const statusCode = res.statusCode;
|
|
25
|
-
let statusColor = "";
|
|
26
|
-
if (statusCode >= 200 && statusCode < 300) {
|
|
27
|
-
statusColor = "\x1B[32m";
|
|
28
|
-
} else if (statusCode >= 300 && statusCode < 400) {
|
|
29
|
-
statusColor = "\x1B[33m";
|
|
30
|
-
} else if (statusCode >= 400 && statusCode < 500) {
|
|
31
|
-
statusColor = "\x1B[31m";
|
|
32
|
-
} else if (statusCode >= 500) {
|
|
33
|
-
statusColor = "\x1B[35m";
|
|
34
|
-
}
|
|
35
|
-
let logMessage = `[${timestamp}] ${method} \x1B[1m${url}\x1B[0m`;
|
|
36
|
-
if (method === "POST" && url === "/mcp" && req.body?.method) {
|
|
37
|
-
logMessage += ` \x1B[1m[${req.body.method}]\x1B[0m`;
|
|
38
|
-
}
|
|
39
|
-
logMessage += ` ${statusColor}${statusCode}\x1B[0m`;
|
|
40
|
-
console.log(logMessage);
|
|
41
|
-
return originalEnd(chunk, encoding, cb);
|
|
42
|
-
};
|
|
43
|
-
next();
|
|
44
|
-
}
|
|
45
|
-
__name(requestLogger, "requestLogger");
|
|
46
13
|
|
|
47
14
|
// src/server/adapters/mcp-ui-adapter.ts
|
|
48
15
|
import { createUIResource } from "@mcp-ui/server";
|
|
@@ -151,9 +118,117 @@ function createUIResourceFromDefinition(definition, params, config) {
|
|
|
151
118
|
}
|
|
152
119
|
__name(createUIResourceFromDefinition, "createUIResourceFromDefinition");
|
|
153
120
|
|
|
121
|
+
// src/server/logging.ts
|
|
122
|
+
async function requestLogger(c, next) {
|
|
123
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().substring(11, 23);
|
|
124
|
+
const method = c.req.method;
|
|
125
|
+
const url = c.req.url;
|
|
126
|
+
let body = null;
|
|
127
|
+
if (method === "POST" && url.includes("/mcp")) {
|
|
128
|
+
try {
|
|
129
|
+
body = await c.req.json().catch(() => null);
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
await next();
|
|
134
|
+
const statusCode = c.res.status;
|
|
135
|
+
let statusColor = "";
|
|
136
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
137
|
+
statusColor = "\x1B[32m";
|
|
138
|
+
} else if (statusCode >= 300 && statusCode < 400) {
|
|
139
|
+
statusColor = "\x1B[33m";
|
|
140
|
+
} else if (statusCode >= 400 && statusCode < 500) {
|
|
141
|
+
statusColor = "\x1B[31m";
|
|
142
|
+
} else if (statusCode >= 500) {
|
|
143
|
+
statusColor = "\x1B[35m";
|
|
144
|
+
}
|
|
145
|
+
let logMessage = `[${timestamp}] ${method} \x1B[1m${new URL(url).pathname}\x1B[0m`;
|
|
146
|
+
if (method === "POST" && url.includes("/mcp") && body?.method) {
|
|
147
|
+
logMessage += ` \x1B[1m[${body.method}]\x1B[0m`;
|
|
148
|
+
}
|
|
149
|
+
logMessage += ` ${statusColor}${statusCode}\x1B[0m`;
|
|
150
|
+
console.log(logMessage);
|
|
151
|
+
}
|
|
152
|
+
__name(requestLogger, "requestLogger");
|
|
153
|
+
|
|
154
154
|
// src/server/mcp-server.ts
|
|
155
|
-
import { createServer } from "vite";
|
|
156
155
|
var TMP_MCP_USE_DIR = ".mcp-use";
|
|
156
|
+
var isDeno = typeof globalThis.Deno !== "undefined";
|
|
157
|
+
function getEnv(key) {
|
|
158
|
+
if (isDeno) {
|
|
159
|
+
return globalThis.Deno.env.get(key);
|
|
160
|
+
}
|
|
161
|
+
return process.env[key];
|
|
162
|
+
}
|
|
163
|
+
__name(getEnv, "getEnv");
|
|
164
|
+
function getCwd() {
|
|
165
|
+
if (isDeno) {
|
|
166
|
+
return globalThis.Deno.cwd();
|
|
167
|
+
}
|
|
168
|
+
return process.cwd();
|
|
169
|
+
}
|
|
170
|
+
__name(getCwd, "getCwd");
|
|
171
|
+
var fsHelpers = {
|
|
172
|
+
async readFileSync(path, encoding = "utf8") {
|
|
173
|
+
if (isDeno) {
|
|
174
|
+
return await globalThis.Deno.readTextFile(path);
|
|
175
|
+
}
|
|
176
|
+
const { readFileSync } = await import("fs");
|
|
177
|
+
const result = readFileSync(path, encoding);
|
|
178
|
+
return typeof result === "string" ? result : result.toString(encoding);
|
|
179
|
+
},
|
|
180
|
+
async readFile(path) {
|
|
181
|
+
if (isDeno) {
|
|
182
|
+
const data = await globalThis.Deno.readFile(path);
|
|
183
|
+
return data.buffer;
|
|
184
|
+
}
|
|
185
|
+
const { readFileSync } = await import("fs");
|
|
186
|
+
const buffer = readFileSync(path);
|
|
187
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
188
|
+
},
|
|
189
|
+
async existsSync(path) {
|
|
190
|
+
if (isDeno) {
|
|
191
|
+
try {
|
|
192
|
+
await globalThis.Deno.stat(path);
|
|
193
|
+
return true;
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const { existsSync } = await import("fs");
|
|
199
|
+
return existsSync(path);
|
|
200
|
+
},
|
|
201
|
+
async readdirSync(path) {
|
|
202
|
+
if (isDeno) {
|
|
203
|
+
const entries = [];
|
|
204
|
+
for await (const entry of globalThis.Deno.readDir(path)) {
|
|
205
|
+
entries.push(entry.name);
|
|
206
|
+
}
|
|
207
|
+
return entries;
|
|
208
|
+
}
|
|
209
|
+
const { readdirSync } = await import("fs");
|
|
210
|
+
return readdirSync(path);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
var pathHelpers = {
|
|
214
|
+
join(...paths) {
|
|
215
|
+
if (isDeno) {
|
|
216
|
+
return paths.join("/").replace(/\/+/g, "/");
|
|
217
|
+
}
|
|
218
|
+
return paths.join("/").replace(/\/+/g, "/");
|
|
219
|
+
},
|
|
220
|
+
relative(from, to) {
|
|
221
|
+
const fromParts = from.split("/").filter((p) => p);
|
|
222
|
+
const toParts = to.split("/").filter((p) => p);
|
|
223
|
+
let i = 0;
|
|
224
|
+
while (i < fromParts.length && i < toParts.length && fromParts[i] === toParts[i]) {
|
|
225
|
+
i++;
|
|
226
|
+
}
|
|
227
|
+
const upCount = fromParts.length - i;
|
|
228
|
+
const relativeParts = [...Array(upCount).fill(".."), ...toParts.slice(i)];
|
|
229
|
+
return relativeParts.join("/");
|
|
230
|
+
}
|
|
231
|
+
};
|
|
157
232
|
var McpServer = class {
|
|
158
233
|
static {
|
|
159
234
|
__name(this, "McpServer");
|
|
@@ -167,14 +242,14 @@ var McpServer = class {
|
|
|
167
242
|
serverHost;
|
|
168
243
|
serverBaseUrl;
|
|
169
244
|
/**
|
|
170
|
-
* Creates a new MCP server instance with
|
|
245
|
+
* Creates a new MCP server instance with Hono integration
|
|
171
246
|
*
|
|
172
247
|
* Initializes the server with the provided configuration, sets up CORS headers,
|
|
173
248
|
* configures widget serving routes, and creates a proxy that allows direct
|
|
174
|
-
* access to
|
|
249
|
+
* access to Hono methods while preserving MCP server functionality.
|
|
175
250
|
*
|
|
176
251
|
* @param config - Server configuration including name, version, and description
|
|
177
|
-
* @returns A proxied McpServer instance that supports both MCP and
|
|
252
|
+
* @returns A proxied McpServer instance that supports both MCP and Hono methods
|
|
178
253
|
*/
|
|
179
254
|
constructor(config) {
|
|
180
255
|
this.config = config;
|
|
@@ -184,13 +259,13 @@ var McpServer = class {
|
|
|
184
259
|
name: config.name,
|
|
185
260
|
version: config.version
|
|
186
261
|
});
|
|
187
|
-
this.app =
|
|
188
|
-
this.app.use(express.json());
|
|
262
|
+
this.app = new Hono();
|
|
189
263
|
this.app.use(
|
|
264
|
+
"*",
|
|
190
265
|
cors({
|
|
191
266
|
origin: "*",
|
|
192
|
-
|
|
193
|
-
|
|
267
|
+
allowMethods: ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
268
|
+
allowHeaders: [
|
|
194
269
|
"Content-Type",
|
|
195
270
|
"Accept",
|
|
196
271
|
"Authorization",
|
|
@@ -201,7 +276,7 @@ var McpServer = class {
|
|
|
201
276
|
]
|
|
202
277
|
})
|
|
203
278
|
);
|
|
204
|
-
this.app.use(requestLogger);
|
|
279
|
+
this.app.use("*", requestLogger);
|
|
205
280
|
return new Proxy(this, {
|
|
206
281
|
get(target, prop) {
|
|
207
282
|
if (prop in target) {
|
|
@@ -737,7 +812,7 @@ var McpServer = class {
|
|
|
737
812
|
* @returns true if in production mode, false otherwise
|
|
738
813
|
*/
|
|
739
814
|
isProductionMode() {
|
|
740
|
-
return
|
|
815
|
+
return getEnv("NODE_ENV") === "production";
|
|
741
816
|
}
|
|
742
817
|
/**
|
|
743
818
|
* Read build manifest file
|
|
@@ -745,14 +820,14 @@ var McpServer = class {
|
|
|
745
820
|
* @private
|
|
746
821
|
* @returns Build manifest or null if not found
|
|
747
822
|
*/
|
|
748
|
-
readBuildManifest() {
|
|
823
|
+
async readBuildManifest() {
|
|
749
824
|
try {
|
|
750
|
-
const manifestPath = join(
|
|
751
|
-
|
|
825
|
+
const manifestPath = pathHelpers.join(
|
|
826
|
+
getCwd(),
|
|
752
827
|
"dist",
|
|
753
828
|
".mcp-use-manifest.json"
|
|
754
829
|
);
|
|
755
|
-
const content = readFileSync(manifestPath, "utf8");
|
|
830
|
+
const content = await fsHelpers.readFileSync(manifestPath, "utf8");
|
|
756
831
|
return JSON.parse(content);
|
|
757
832
|
} catch {
|
|
758
833
|
return null;
|
|
@@ -773,6 +848,10 @@ var McpServer = class {
|
|
|
773
848
|
if (this.isProductionMode()) {
|
|
774
849
|
await this.mountWidgetsProduction(options);
|
|
775
850
|
} else {
|
|
851
|
+
if (isDeno) {
|
|
852
|
+
console.log("[WIDGETS] Skipping dev mode widget mounting in Deno runtime (use production build)");
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
776
855
|
await this.mountWidgetsDev(options);
|
|
777
856
|
}
|
|
778
857
|
}
|
|
@@ -793,7 +872,7 @@ var McpServer = class {
|
|
|
793
872
|
const { promises: fs } = await import("fs");
|
|
794
873
|
const baseRoute = options?.baseRoute || "/mcp-use/widgets";
|
|
795
874
|
const resourcesDir = options?.resourcesDir || "resources";
|
|
796
|
-
const srcDir = join(
|
|
875
|
+
const srcDir = pathHelpers.join(getCwd(), resourcesDir);
|
|
797
876
|
try {
|
|
798
877
|
await fs.access(srcDir);
|
|
799
878
|
} catch (error) {
|
|
@@ -805,7 +884,7 @@ var McpServer = class {
|
|
|
805
884
|
let entries = [];
|
|
806
885
|
try {
|
|
807
886
|
const files = await fs.readdir(srcDir);
|
|
808
|
-
entries = files.filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => join(srcDir, f));
|
|
887
|
+
entries = files.filter((f) => f.endsWith(".tsx") || f.endsWith(".ts")).map((f) => pathHelpers.join(srcDir, f));
|
|
809
888
|
} catch (error) {
|
|
810
889
|
console.log(`[WIDGETS] No widgets found in ${resourcesDir}/ directory`);
|
|
811
890
|
return;
|
|
@@ -814,9 +893,10 @@ var McpServer = class {
|
|
|
814
893
|
console.log(`[WIDGETS] No widgets found in ${resourcesDir}/ directory`);
|
|
815
894
|
return;
|
|
816
895
|
}
|
|
817
|
-
const tempDir = join(
|
|
896
|
+
const tempDir = pathHelpers.join(getCwd(), TMP_MCP_USE_DIR);
|
|
818
897
|
await fs.mkdir(tempDir, { recursive: true }).catch(() => {
|
|
819
898
|
});
|
|
899
|
+
const { createServer } = await import("vite");
|
|
820
900
|
const react = (await import("@vitejs/plugin-react")).default;
|
|
821
901
|
const tailwindcss = (await import("@tailwindcss/vite")).default;
|
|
822
902
|
console.log(react, tailwindcss);
|
|
@@ -830,11 +910,10 @@ var McpServer = class {
|
|
|
830
910
|
};
|
|
831
911
|
});
|
|
832
912
|
for (const widget of widgets) {
|
|
833
|
-
const widgetTempDir = join(tempDir, widget.name);
|
|
913
|
+
const widgetTempDir = pathHelpers.join(tempDir, widget.name);
|
|
834
914
|
await fs.mkdir(widgetTempDir, { recursive: true });
|
|
835
|
-
const resourcesPath = join(
|
|
836
|
-
const
|
|
837
|
-
const relativeResourcesPath = relative(
|
|
915
|
+
const resourcesPath = pathHelpers.join(getCwd(), resourcesDir);
|
|
916
|
+
const relativeResourcesPath = pathHelpers.relative(
|
|
838
917
|
widgetTempDir,
|
|
839
918
|
resourcesPath
|
|
840
919
|
).replace(/\\/g, "/");
|
|
@@ -843,7 +922,7 @@ var McpServer = class {
|
|
|
843
922
|
/* Configure Tailwind to scan the resources directory */
|
|
844
923
|
@source "${relativeResourcesPath}";
|
|
845
924
|
`;
|
|
846
|
-
await fs.writeFile(join(widgetTempDir, "styles.css"), cssContent, "utf8");
|
|
925
|
+
await fs.writeFile(pathHelpers.join(widgetTempDir, "styles.css"), cssContent, "utf8");
|
|
847
926
|
const entryContent = `import React from 'react'
|
|
848
927
|
import { createRoot } from 'react-dom/client'
|
|
849
928
|
import './styles.css'
|
|
@@ -868,12 +947,12 @@ if (container && Component) {
|
|
|
868
947
|
</body>
|
|
869
948
|
</html>`;
|
|
870
949
|
await fs.writeFile(
|
|
871
|
-
join(widgetTempDir, "entry.tsx"),
|
|
950
|
+
pathHelpers.join(widgetTempDir, "entry.tsx"),
|
|
872
951
|
entryContent,
|
|
873
952
|
"utf8"
|
|
874
953
|
);
|
|
875
954
|
await fs.writeFile(
|
|
876
|
-
join(widgetTempDir, "index.html"),
|
|
955
|
+
pathHelpers.join(widgetTempDir, "index.html"),
|
|
877
956
|
htmlContent,
|
|
878
957
|
"utf8"
|
|
879
958
|
);
|
|
@@ -888,7 +967,7 @@ if (container && Component) {
|
|
|
888
967
|
plugins: [tailwindcss(), react()],
|
|
889
968
|
resolve: {
|
|
890
969
|
alias: {
|
|
891
|
-
"@": join(
|
|
970
|
+
"@": pathHelpers.join(getCwd(), resourcesDir)
|
|
892
971
|
}
|
|
893
972
|
},
|
|
894
973
|
server: {
|
|
@@ -896,22 +975,95 @@ if (container && Component) {
|
|
|
896
975
|
origin: serverOrigin
|
|
897
976
|
}
|
|
898
977
|
});
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
978
|
+
const adaptExpressMiddleware = /* @__PURE__ */ __name((middleware) => {
|
|
979
|
+
return async (c, next) => {
|
|
980
|
+
const req = c.req.raw;
|
|
981
|
+
let handled = false;
|
|
982
|
+
const responseBody = [];
|
|
983
|
+
let statusCode = 200;
|
|
984
|
+
const headers = {};
|
|
985
|
+
const res = {
|
|
986
|
+
statusCode: 200,
|
|
987
|
+
status: /* @__PURE__ */ __name((code) => {
|
|
988
|
+
statusCode = code;
|
|
989
|
+
res.statusCode = code;
|
|
990
|
+
return res;
|
|
991
|
+
}, "status"),
|
|
992
|
+
setHeader: /* @__PURE__ */ __name((name, value) => {
|
|
993
|
+
headers[name] = value;
|
|
994
|
+
}, "setHeader"),
|
|
995
|
+
getHeader: /* @__PURE__ */ __name((name) => headers[name], "getHeader"),
|
|
996
|
+
write: /* @__PURE__ */ __name((chunk) => {
|
|
997
|
+
responseBody.push(typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk);
|
|
998
|
+
}, "write"),
|
|
999
|
+
end: /* @__PURE__ */ __name((chunk) => {
|
|
1000
|
+
if (chunk) {
|
|
1001
|
+
responseBody.push(typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk);
|
|
1002
|
+
}
|
|
1003
|
+
handled = true;
|
|
1004
|
+
}, "end"),
|
|
1005
|
+
on: /* @__PURE__ */ __name(() => {
|
|
1006
|
+
}, "on"),
|
|
1007
|
+
once: /* @__PURE__ */ __name(() => {
|
|
1008
|
+
}, "once"),
|
|
1009
|
+
removeListener: /* @__PURE__ */ __name(() => {
|
|
1010
|
+
}, "removeListener")
|
|
1011
|
+
};
|
|
1012
|
+
const expressReq = {
|
|
1013
|
+
...req,
|
|
1014
|
+
url: new URL(req.url).pathname + new URL(req.url).search,
|
|
1015
|
+
originalUrl: req.url,
|
|
1016
|
+
baseUrl: "",
|
|
1017
|
+
path: new URL(req.url).pathname,
|
|
1018
|
+
query: Object.fromEntries(new URL(req.url).searchParams),
|
|
1019
|
+
params: {},
|
|
1020
|
+
body: {},
|
|
1021
|
+
headers: Object.fromEntries(req.headers.entries())
|
|
1022
|
+
};
|
|
1023
|
+
await new Promise((resolve, reject) => {
|
|
1024
|
+
middleware(expressReq, res, (err) => {
|
|
1025
|
+
if (err) reject(err);
|
|
1026
|
+
else resolve();
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
if (handled) {
|
|
1030
|
+
const body = Buffer.concat(responseBody);
|
|
1031
|
+
return new Response(body, {
|
|
1032
|
+
status: statusCode,
|
|
1033
|
+
headers
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
return next();
|
|
1037
|
+
};
|
|
1038
|
+
}, "adaptExpressMiddleware");
|
|
1039
|
+
this.app.use(`${baseRoute}/*`, async (c, next) => {
|
|
1040
|
+
const url = new URL(c.req.url);
|
|
1041
|
+
const pathname = url.pathname;
|
|
1042
|
+
const widgetMatch = pathname.replace(baseRoute, "").match(/^\/([^/]+)/);
|
|
903
1043
|
if (widgetMatch) {
|
|
904
1044
|
const widgetName = widgetMatch[1];
|
|
905
1045
|
const widget = widgets.find((w) => w.name === widgetName);
|
|
906
1046
|
if (widget) {
|
|
907
|
-
|
|
908
|
-
|
|
1047
|
+
const relativePath = pathname.replace(baseRoute, "");
|
|
1048
|
+
if (relativePath === `/${widgetName}` || relativePath === `/${widgetName}/`) {
|
|
1049
|
+
const newUrl = new URL(c.req.url);
|
|
1050
|
+
newUrl.pathname = `${baseRoute}/${widgetName}/index.html`;
|
|
1051
|
+
const newRequest = new Request(newUrl.toString(), c.req.raw);
|
|
1052
|
+
Object.defineProperty(c, "req", {
|
|
1053
|
+
value: {
|
|
1054
|
+
...c.req,
|
|
1055
|
+
url: newUrl.toString(),
|
|
1056
|
+
raw: newRequest
|
|
1057
|
+
},
|
|
1058
|
+
writable: false,
|
|
1059
|
+
configurable: true
|
|
1060
|
+
});
|
|
909
1061
|
}
|
|
910
1062
|
}
|
|
911
1063
|
}
|
|
912
|
-
next();
|
|
1064
|
+
await next();
|
|
913
1065
|
});
|
|
914
|
-
this.app.use(baseRoute
|
|
1066
|
+
this.app.use(`${baseRoute}/*`, adaptExpressMiddleware(viteServer.middlewares));
|
|
915
1067
|
widgets.forEach((widget) => {
|
|
916
1068
|
console.log(
|
|
917
1069
|
`[WIDGET] ${widget.name} mounted at ${baseRoute}/${widget.name}`
|
|
@@ -947,8 +1099,11 @@ if (container && Component) {
|
|
|
947
1099
|
console.log("[WIDGET dev] Metadata:", metadata);
|
|
948
1100
|
let html = "";
|
|
949
1101
|
try {
|
|
950
|
-
html = readFileSync(
|
|
951
|
-
|
|
1102
|
+
html = await fsHelpers.readFileSync(
|
|
1103
|
+
pathHelpers.join(tempDir, widget.name, "index.html"),
|
|
1104
|
+
"utf8"
|
|
1105
|
+
);
|
|
1106
|
+
const mcpUrl = getEnv("MCP_URL") || "/";
|
|
952
1107
|
if (mcpUrl && html) {
|
|
953
1108
|
const htmlWithoutComments = html.replace(/<!--[\s\S]*?-->/g, "");
|
|
954
1109
|
const baseTagRegex = /<base\s+[^>]*\/?>/i;
|
|
@@ -1048,19 +1203,23 @@ if (container && Component) {
|
|
|
1048
1203
|
*/
|
|
1049
1204
|
async mountWidgetsProduction(options) {
|
|
1050
1205
|
const baseRoute = options?.baseRoute || "/mcp-use/widgets";
|
|
1051
|
-
const widgetsDir = join(
|
|
1052
|
-
if (!existsSync(widgetsDir)) {
|
|
1206
|
+
const widgetsDir = pathHelpers.join(getCwd(), "dist", "resources", "widgets");
|
|
1207
|
+
if (!await fsHelpers.existsSync(widgetsDir)) {
|
|
1053
1208
|
console.log(
|
|
1054
1209
|
"[WIDGETS] No dist/resources/widgets/ directory found - skipping widget serving"
|
|
1055
1210
|
);
|
|
1056
1211
|
return;
|
|
1057
1212
|
}
|
|
1058
1213
|
this.setupWidgetRoutes();
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1214
|
+
const allEntries = await fsHelpers.readdirSync(widgetsDir);
|
|
1215
|
+
const widgets = [];
|
|
1216
|
+
for (const name of allEntries) {
|
|
1217
|
+
const widgetPath = pathHelpers.join(widgetsDir, name);
|
|
1218
|
+
const indexPath = pathHelpers.join(widgetPath, "index.html");
|
|
1219
|
+
if (await fsHelpers.existsSync(indexPath)) {
|
|
1220
|
+
widgets.push(name);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1064
1223
|
if (widgets.length === 0) {
|
|
1065
1224
|
console.log(
|
|
1066
1225
|
"[WIDGETS] No built widgets found in dist/resources/widgets/"
|
|
@@ -1071,13 +1230,13 @@ if (container && Component) {
|
|
|
1071
1230
|
`[WIDGETS] Serving ${widgets.length} pre-built widget(s) from dist/resources/widgets/`
|
|
1072
1231
|
);
|
|
1073
1232
|
for (const widgetName of widgets) {
|
|
1074
|
-
const widgetPath = join(widgetsDir, widgetName);
|
|
1075
|
-
const indexPath = join(widgetPath, "index.html");
|
|
1076
|
-
const metadataPath = join(widgetPath, "metadata.json");
|
|
1233
|
+
const widgetPath = pathHelpers.join(widgetsDir, widgetName);
|
|
1234
|
+
const indexPath = pathHelpers.join(widgetPath, "index.html");
|
|
1235
|
+
const metadataPath = pathHelpers.join(widgetPath, "metadata.json");
|
|
1077
1236
|
let html = "";
|
|
1078
1237
|
try {
|
|
1079
|
-
html = readFileSync(indexPath, "utf8");
|
|
1080
|
-
const mcpUrl =
|
|
1238
|
+
html = await fsHelpers.readFileSync(indexPath, "utf8");
|
|
1239
|
+
const mcpUrl = getEnv("MCP_URL") || "/";
|
|
1081
1240
|
if (mcpUrl && html) {
|
|
1082
1241
|
const htmlWithoutComments = html.replace(/<!--[\s\S]*?-->/g, "");
|
|
1083
1242
|
const baseTagRegex = /<base\s+[^>]*\/?>/i;
|
|
@@ -1124,7 +1283,7 @@ if (container && Component) {
|
|
|
1124
1283
|
let props = {};
|
|
1125
1284
|
let description = `Widget: ${widgetName}`;
|
|
1126
1285
|
try {
|
|
1127
|
-
const metadataContent = readFileSync(metadataPath, "utf8");
|
|
1286
|
+
const metadataContent = await fsHelpers.readFileSync(metadataPath, "utf8");
|
|
1128
1287
|
metadata = JSON.parse(metadataContent);
|
|
1129
1288
|
if (metadata.description) {
|
|
1130
1289
|
description = metadata.description;
|
|
@@ -1207,47 +1366,178 @@ if (container && Component) {
|
|
|
1207
1366
|
if (this.mcpMounted) return;
|
|
1208
1367
|
const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
1209
1368
|
const endpoint = "/mcp";
|
|
1210
|
-
|
|
1369
|
+
const createExpressLikeObjects = /* @__PURE__ */ __name((c) => {
|
|
1370
|
+
const req = c.req.raw;
|
|
1371
|
+
const responseBody = [];
|
|
1372
|
+
let statusCode = 200;
|
|
1373
|
+
const headers = {};
|
|
1374
|
+
let ended = false;
|
|
1375
|
+
let headersSent = false;
|
|
1376
|
+
const expressReq = {
|
|
1377
|
+
...req,
|
|
1378
|
+
url: new URL(req.url).pathname + new URL(req.url).search,
|
|
1379
|
+
originalUrl: req.url,
|
|
1380
|
+
baseUrl: "",
|
|
1381
|
+
path: new URL(req.url).pathname,
|
|
1382
|
+
query: Object.fromEntries(new URL(req.url).searchParams),
|
|
1383
|
+
params: {},
|
|
1384
|
+
body: {},
|
|
1385
|
+
headers: Object.fromEntries(req.headers.entries()),
|
|
1386
|
+
method: req.method
|
|
1387
|
+
};
|
|
1388
|
+
const expressRes = {
|
|
1389
|
+
statusCode: 200,
|
|
1390
|
+
headersSent: false,
|
|
1391
|
+
status: /* @__PURE__ */ __name((code) => {
|
|
1392
|
+
statusCode = code;
|
|
1393
|
+
expressRes.statusCode = code;
|
|
1394
|
+
return expressRes;
|
|
1395
|
+
}, "status"),
|
|
1396
|
+
setHeader: /* @__PURE__ */ __name((name, value) => {
|
|
1397
|
+
if (!headersSent) {
|
|
1398
|
+
headers[name] = Array.isArray(value) ? value.join(", ") : value;
|
|
1399
|
+
}
|
|
1400
|
+
}, "setHeader"),
|
|
1401
|
+
getHeader: /* @__PURE__ */ __name((name) => headers[name], "getHeader"),
|
|
1402
|
+
write: /* @__PURE__ */ __name((chunk, encoding, callback) => {
|
|
1403
|
+
if (!ended) {
|
|
1404
|
+
const data = typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk instanceof Uint8Array ? chunk : Buffer.from(chunk);
|
|
1405
|
+
responseBody.push(data);
|
|
1406
|
+
}
|
|
1407
|
+
if (typeof encoding === "function") {
|
|
1408
|
+
encoding();
|
|
1409
|
+
} else if (callback) {
|
|
1410
|
+
callback();
|
|
1411
|
+
}
|
|
1412
|
+
return true;
|
|
1413
|
+
}, "write"),
|
|
1414
|
+
end: /* @__PURE__ */ __name((chunk, encoding, callback) => {
|
|
1415
|
+
if (chunk && !ended) {
|
|
1416
|
+
const data = typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk instanceof Uint8Array ? chunk : Buffer.from(chunk);
|
|
1417
|
+
responseBody.push(data);
|
|
1418
|
+
}
|
|
1419
|
+
ended = true;
|
|
1420
|
+
if (typeof encoding === "function") {
|
|
1421
|
+
encoding();
|
|
1422
|
+
} else if (callback) {
|
|
1423
|
+
callback();
|
|
1424
|
+
}
|
|
1425
|
+
}, "end"),
|
|
1426
|
+
on: /* @__PURE__ */ __name((event, handler) => {
|
|
1427
|
+
if (event === "close") {
|
|
1428
|
+
expressRes._closeHandler = handler;
|
|
1429
|
+
}
|
|
1430
|
+
}, "on"),
|
|
1431
|
+
once: /* @__PURE__ */ __name(() => {
|
|
1432
|
+
}, "once"),
|
|
1433
|
+
removeListener: /* @__PURE__ */ __name(() => {
|
|
1434
|
+
}, "removeListener"),
|
|
1435
|
+
writeHead: /* @__PURE__ */ __name((code, _headers) => {
|
|
1436
|
+
statusCode = code;
|
|
1437
|
+
expressRes.statusCode = code;
|
|
1438
|
+
headersSent = true;
|
|
1439
|
+
if (_headers) {
|
|
1440
|
+
Object.assign(headers, _headers);
|
|
1441
|
+
}
|
|
1442
|
+
return expressRes;
|
|
1443
|
+
}, "writeHead"),
|
|
1444
|
+
flushHeaders: /* @__PURE__ */ __name(() => {
|
|
1445
|
+
headersSent = true;
|
|
1446
|
+
}, "flushHeaders"),
|
|
1447
|
+
send: /* @__PURE__ */ __name((body) => {
|
|
1448
|
+
if (!ended) {
|
|
1449
|
+
expressRes.write(body);
|
|
1450
|
+
expressRes.end();
|
|
1451
|
+
}
|
|
1452
|
+
}, "send")
|
|
1453
|
+
};
|
|
1454
|
+
return { expressReq, expressRes, getResponse: /* @__PURE__ */ __name(() => {
|
|
1455
|
+
if (ended) {
|
|
1456
|
+
if (responseBody.length > 0) {
|
|
1457
|
+
const body = isDeno ? Buffer.concat(responseBody) : Buffer.concat(responseBody);
|
|
1458
|
+
return new Response(body, {
|
|
1459
|
+
status: statusCode,
|
|
1460
|
+
headers
|
|
1461
|
+
});
|
|
1462
|
+
} else {
|
|
1463
|
+
return new Response(null, {
|
|
1464
|
+
status: statusCode,
|
|
1465
|
+
headers
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return null;
|
|
1470
|
+
}, "getResponse") };
|
|
1471
|
+
}, "createExpressLikeObjects");
|
|
1472
|
+
this.app.post(endpoint, async (c) => {
|
|
1473
|
+
const { expressReq, expressRes, getResponse } = createExpressLikeObjects(c);
|
|
1474
|
+
try {
|
|
1475
|
+
expressReq.body = await c.req.json();
|
|
1476
|
+
} catch {
|
|
1477
|
+
expressReq.body = {};
|
|
1478
|
+
}
|
|
1211
1479
|
const transport = new StreamableHTTPServerTransport({
|
|
1212
1480
|
sessionIdGenerator: void 0,
|
|
1213
1481
|
enableJsonResponse: true
|
|
1214
1482
|
});
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1483
|
+
if (expressRes._closeHandler) {
|
|
1484
|
+
c.req.raw.signal?.addEventListener("abort", () => {
|
|
1485
|
+
transport.close();
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1218
1488
|
await this.server.connect(transport);
|
|
1219
|
-
await transport.handleRequest(
|
|
1489
|
+
await transport.handleRequest(expressReq, expressRes, expressReq.body);
|
|
1490
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
1491
|
+
const response = getResponse();
|
|
1492
|
+
if (response) {
|
|
1493
|
+
return response;
|
|
1494
|
+
}
|
|
1495
|
+
return c.text("", 200);
|
|
1220
1496
|
});
|
|
1221
|
-
this.app.get(endpoint, async (
|
|
1497
|
+
this.app.get(endpoint, async (c) => {
|
|
1498
|
+
const { expressReq, expressRes, getResponse } = createExpressLikeObjects(c);
|
|
1222
1499
|
const transport = new StreamableHTTPServerTransport({
|
|
1223
1500
|
sessionIdGenerator: void 0,
|
|
1224
1501
|
enableJsonResponse: true
|
|
1225
1502
|
});
|
|
1226
|
-
|
|
1503
|
+
c.req.raw.signal?.addEventListener("abort", () => {
|
|
1227
1504
|
transport.close();
|
|
1228
1505
|
});
|
|
1229
1506
|
await this.server.connect(transport);
|
|
1230
|
-
await transport.handleRequest(
|
|
1507
|
+
await transport.handleRequest(expressReq, expressRes);
|
|
1508
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
1509
|
+
const response = getResponse();
|
|
1510
|
+
if (response) {
|
|
1511
|
+
return response;
|
|
1512
|
+
}
|
|
1513
|
+
return c.text("", 200);
|
|
1231
1514
|
});
|
|
1232
|
-
this.app.delete(endpoint, async (
|
|
1515
|
+
this.app.delete(endpoint, async (c) => {
|
|
1516
|
+
const { expressReq, expressRes, getResponse } = createExpressLikeObjects(c);
|
|
1233
1517
|
const transport = new StreamableHTTPServerTransport({
|
|
1234
1518
|
sessionIdGenerator: void 0,
|
|
1235
1519
|
enableJsonResponse: true
|
|
1236
1520
|
});
|
|
1237
|
-
|
|
1521
|
+
c.req.raw.signal?.addEventListener("abort", () => {
|
|
1238
1522
|
transport.close();
|
|
1239
1523
|
});
|
|
1240
1524
|
await this.server.connect(transport);
|
|
1241
|
-
await transport.handleRequest(
|
|
1525
|
+
await transport.handleRequest(expressReq, expressRes);
|
|
1526
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
1527
|
+
const response = getResponse();
|
|
1528
|
+
if (response) {
|
|
1529
|
+
return response;
|
|
1530
|
+
}
|
|
1531
|
+
return c.text("", 200);
|
|
1242
1532
|
});
|
|
1243
1533
|
this.mcpMounted = true;
|
|
1244
1534
|
console.log(`[MCP] Server mounted at ${endpoint}`);
|
|
1245
1535
|
}
|
|
1246
1536
|
/**
|
|
1247
|
-
* Start the
|
|
1537
|
+
* Start the Hono server with MCP endpoints
|
|
1248
1538
|
*
|
|
1249
1539
|
* Initiates the server startup process by mounting MCP endpoints, configuring
|
|
1250
|
-
* the inspector UI (if available), and starting the
|
|
1540
|
+
* the inspector UI (if available), and starting the server to listen
|
|
1251
1541
|
* for incoming connections. This is the main entry point for running the server.
|
|
1252
1542
|
*
|
|
1253
1543
|
* The server will be accessible at the specified port with MCP endpoints at /mcp
|
|
@@ -1265,24 +1555,91 @@ if (container && Component) {
|
|
|
1265
1555
|
* ```
|
|
1266
1556
|
*/
|
|
1267
1557
|
async listen(port) {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1558
|
+
const portEnv = getEnv("PORT");
|
|
1559
|
+
this.serverPort = port || (portEnv ? parseInt(portEnv, 10) : 3001);
|
|
1560
|
+
const hostEnv = getEnv("HOST");
|
|
1561
|
+
if (hostEnv) {
|
|
1562
|
+
this.serverHost = hostEnv;
|
|
1271
1563
|
}
|
|
1272
1564
|
await this.mountWidgets({
|
|
1273
1565
|
baseRoute: "/mcp-use/widgets",
|
|
1274
1566
|
resourcesDir: "resources"
|
|
1275
1567
|
});
|
|
1276
1568
|
await this.mountMcp();
|
|
1277
|
-
this.mountInspector();
|
|
1278
|
-
|
|
1569
|
+
await this.mountInspector();
|
|
1570
|
+
if (isDeno) {
|
|
1571
|
+
globalThis.Deno.serve(
|
|
1572
|
+
{ port: this.serverPort, hostname: this.serverHost },
|
|
1573
|
+
this.app.fetch
|
|
1574
|
+
);
|
|
1279
1575
|
console.log(
|
|
1280
1576
|
`[SERVER] Listening on http://${this.serverHost}:${this.serverPort}`
|
|
1281
1577
|
);
|
|
1282
1578
|
console.log(
|
|
1283
1579
|
`[MCP] Endpoints: http://${this.serverHost}:${this.serverPort}/mcp`
|
|
1284
1580
|
);
|
|
1581
|
+
} else {
|
|
1582
|
+
const { serve } = await import("@hono/node-server");
|
|
1583
|
+
serve(
|
|
1584
|
+
{
|
|
1585
|
+
fetch: this.app.fetch,
|
|
1586
|
+
port: this.serverPort,
|
|
1587
|
+
hostname: this.serverHost
|
|
1588
|
+
},
|
|
1589
|
+
(_info) => {
|
|
1590
|
+
console.log(
|
|
1591
|
+
`[SERVER] Listening on http://${this.serverHost}:${this.serverPort}`
|
|
1592
|
+
);
|
|
1593
|
+
console.log(
|
|
1594
|
+
`[MCP] Endpoints: http://${this.serverHost}:${this.serverPort}/mcp`
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Get the fetch handler for the server after mounting all endpoints
|
|
1602
|
+
*
|
|
1603
|
+
* This method prepares the server by mounting MCP endpoints, widgets, and inspector
|
|
1604
|
+
* (if available), then returns the fetch handler. This is useful for integrating
|
|
1605
|
+
* with external server frameworks like Supabase Edge Functions, Cloudflare Workers,
|
|
1606
|
+
* or other platforms that handle the server lifecycle themselves.
|
|
1607
|
+
*
|
|
1608
|
+
* Unlike `listen()`, this method does not start a server - it only prepares the
|
|
1609
|
+
* routes and returns the handler function that can be used with external servers.
|
|
1610
|
+
*
|
|
1611
|
+
* @returns Promise that resolves to the fetch handler function
|
|
1612
|
+
*
|
|
1613
|
+
* @example
|
|
1614
|
+
* ```typescript
|
|
1615
|
+
* // For Supabase Edge Functions
|
|
1616
|
+
* const server = createMCPServer('my-server');
|
|
1617
|
+
* server.tool({ ... });
|
|
1618
|
+
* const handler = await server.getHandler();
|
|
1619
|
+
* Deno.serve(handler);
|
|
1620
|
+
* ```
|
|
1621
|
+
*
|
|
1622
|
+
* @example
|
|
1623
|
+
* ```typescript
|
|
1624
|
+
* // For Cloudflare Workers
|
|
1625
|
+
* const server = createMCPServer('my-server');
|
|
1626
|
+
* server.tool({ ... });
|
|
1627
|
+
* const handler = await server.getHandler();
|
|
1628
|
+
* export default { fetch: handler };
|
|
1629
|
+
* ```
|
|
1630
|
+
*/
|
|
1631
|
+
async getHandler() {
|
|
1632
|
+
await this.mountWidgets({
|
|
1633
|
+
baseRoute: "/mcp-use/widgets",
|
|
1634
|
+
resourcesDir: "resources"
|
|
1285
1635
|
});
|
|
1636
|
+
await this.mountMcp();
|
|
1637
|
+
await this.mountInspector();
|
|
1638
|
+
const fetchHandler = this.app.fetch.bind(this.app);
|
|
1639
|
+
return async (req) => {
|
|
1640
|
+
const result = await fetchHandler(req);
|
|
1641
|
+
return result;
|
|
1642
|
+
};
|
|
1286
1643
|
}
|
|
1287
1644
|
/**
|
|
1288
1645
|
* Mount MCP Inspector UI at /inspector
|
|
@@ -1306,10 +1663,10 @@ if (container && Component) {
|
|
|
1306
1663
|
* - Server continues to function normally
|
|
1307
1664
|
* - No inspector UI available
|
|
1308
1665
|
*/
|
|
1309
|
-
mountInspector() {
|
|
1666
|
+
async mountInspector() {
|
|
1310
1667
|
if (this.inspectorMounted) return;
|
|
1311
1668
|
if (this.isProductionMode()) {
|
|
1312
|
-
const manifest = this.readBuildManifest();
|
|
1669
|
+
const manifest = await this.readBuildManifest();
|
|
1313
1670
|
if (!manifest?.includeInspector) {
|
|
1314
1671
|
console.log(
|
|
1315
1672
|
"[INSPECTOR] Skipped in production (use --with-inspector flag during build)"
|
|
@@ -1317,19 +1674,20 @@ if (container && Component) {
|
|
|
1317
1674
|
return;
|
|
1318
1675
|
}
|
|
1319
1676
|
}
|
|
1320
|
-
|
|
1677
|
+
try {
|
|
1678
|
+
const { mountInspector } = await import("@mcp-use/inspector");
|
|
1321
1679
|
mountInspector(this.app);
|
|
1322
1680
|
this.inspectorMounted = true;
|
|
1323
1681
|
console.log(
|
|
1324
1682
|
`[INSPECTOR] UI available at http://${this.serverHost}:${this.serverPort}/inspector`
|
|
1325
1683
|
);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1684
|
+
} catch {
|
|
1685
|
+
}
|
|
1328
1686
|
}
|
|
1329
1687
|
/**
|
|
1330
1688
|
* Setup default widget serving routes
|
|
1331
1689
|
*
|
|
1332
|
-
* Configures
|
|
1690
|
+
* Configures Hono routes to serve MCP UI widgets and their static assets.
|
|
1333
1691
|
* Widgets are served from the dist/resources/widgets directory and can
|
|
1334
1692
|
* be accessed via HTTP endpoints for embedding in web applications.
|
|
1335
1693
|
*
|
|
@@ -1348,11 +1706,11 @@ if (container && Component) {
|
|
|
1348
1706
|
* - http://localhost:3001/mcp-use/widgets/assets/script.js (auto-discovered)
|
|
1349
1707
|
*/
|
|
1350
1708
|
setupWidgetRoutes() {
|
|
1351
|
-
this.app.get("/mcp-use/widgets/:widget/assets/*", (
|
|
1352
|
-
const widget = req.
|
|
1353
|
-
const assetFile = req.
|
|
1354
|
-
const assetPath = join(
|
|
1355
|
-
|
|
1709
|
+
this.app.get("/mcp-use/widgets/:widget/assets/*", async (c) => {
|
|
1710
|
+
const widget = c.req.param("widget");
|
|
1711
|
+
const assetFile = c.req.path.split("/assets/")[1];
|
|
1712
|
+
const assetPath = pathHelpers.join(
|
|
1713
|
+
getCwd(),
|
|
1356
1714
|
"dist",
|
|
1357
1715
|
"resources",
|
|
1358
1716
|
"widgets",
|
|
@@ -1360,48 +1718,72 @@ if (container && Component) {
|
|
|
1360
1718
|
"assets",
|
|
1361
1719
|
assetFile
|
|
1362
1720
|
);
|
|
1363
|
-
|
|
1721
|
+
try {
|
|
1722
|
+
if (await fsHelpers.existsSync(assetPath)) {
|
|
1723
|
+
const content = await fsHelpers.readFile(assetPath);
|
|
1724
|
+
const ext = assetFile.split(".").pop()?.toLowerCase();
|
|
1725
|
+
const contentType = ext === "js" ? "application/javascript" : ext === "css" ? "text/css" : ext === "png" ? "image/png" : ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "svg" ? "image/svg+xml" : "application/octet-stream";
|
|
1726
|
+
return new Response(content, {
|
|
1727
|
+
status: 200,
|
|
1728
|
+
headers: { "Content-Type": contentType }
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
return c.notFound();
|
|
1732
|
+
} catch {
|
|
1733
|
+
return c.notFound();
|
|
1734
|
+
}
|
|
1364
1735
|
});
|
|
1365
|
-
this.app.get("/mcp-use/widgets/assets/*", (
|
|
1366
|
-
const assetFile = req.
|
|
1367
|
-
const widgetsDir = join(
|
|
1736
|
+
this.app.get("/mcp-use/widgets/assets/*", async (c) => {
|
|
1737
|
+
const assetFile = c.req.path.split("/assets/")[1];
|
|
1738
|
+
const widgetsDir = pathHelpers.join(getCwd(), "dist", "resources", "widgets");
|
|
1368
1739
|
try {
|
|
1369
|
-
const widgets = readdirSync(widgetsDir);
|
|
1740
|
+
const widgets = await fsHelpers.readdirSync(widgetsDir);
|
|
1370
1741
|
for (const widget of widgets) {
|
|
1371
|
-
const assetPath = join(widgetsDir, widget, "assets", assetFile);
|
|
1372
|
-
if (existsSync(assetPath)) {
|
|
1373
|
-
|
|
1742
|
+
const assetPath = pathHelpers.join(widgetsDir, widget, "assets", assetFile);
|
|
1743
|
+
if (await fsHelpers.existsSync(assetPath)) {
|
|
1744
|
+
const content = await fsHelpers.readFile(assetPath);
|
|
1745
|
+
const ext = assetFile.split(".").pop()?.toLowerCase();
|
|
1746
|
+
const contentType = ext === "js" ? "application/javascript" : ext === "css" ? "text/css" : ext === "png" ? "image/png" : ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "svg" ? "image/svg+xml" : "application/octet-stream";
|
|
1747
|
+
return new Response(content, {
|
|
1748
|
+
status: 200,
|
|
1749
|
+
headers: { "Content-Type": contentType }
|
|
1750
|
+
});
|
|
1374
1751
|
}
|
|
1375
1752
|
}
|
|
1376
|
-
|
|
1753
|
+
return c.notFound();
|
|
1377
1754
|
} catch {
|
|
1378
|
-
|
|
1755
|
+
return c.notFound();
|
|
1379
1756
|
}
|
|
1380
1757
|
});
|
|
1381
|
-
this.app.get("/mcp-use/widgets/:widget", (
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1758
|
+
this.app.get("/mcp-use/widgets/:widget", async (c) => {
|
|
1759
|
+
const widget = c.req.param("widget");
|
|
1760
|
+
const filePath = pathHelpers.join(
|
|
1761
|
+
getCwd(),
|
|
1384
1762
|
"dist",
|
|
1385
1763
|
"resources",
|
|
1386
1764
|
"widgets",
|
|
1387
|
-
|
|
1765
|
+
widget,
|
|
1388
1766
|
"index.html"
|
|
1389
1767
|
);
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1768
|
+
try {
|
|
1769
|
+
let html = await fsHelpers.readFileSync(filePath, "utf8");
|
|
1770
|
+
html = html.replace(
|
|
1771
|
+
/src="\/mcp-use\/widgets\/([^"]+)"/g,
|
|
1772
|
+
`src="${this.serverBaseUrl}/mcp-use/widgets/$1"`
|
|
1773
|
+
);
|
|
1774
|
+
html = html.replace(
|
|
1775
|
+
/href="\/mcp-use\/widgets\/([^"]+)"/g,
|
|
1776
|
+
`href="${this.serverBaseUrl}/mcp-use/widgets/$1"`
|
|
1777
|
+
);
|
|
1778
|
+
html = html.replace(
|
|
1779
|
+
/<head[^>]*>/i,
|
|
1780
|
+
`<head>
|
|
1781
|
+
<script>window.__getFile = (filename) => { return "${this.serverBaseUrl}/mcp-use/widgets/${widget}/"+filename }</script>`
|
|
1782
|
+
);
|
|
1783
|
+
return c.html(html);
|
|
1784
|
+
} catch {
|
|
1785
|
+
return c.notFound();
|
|
1786
|
+
}
|
|
1405
1787
|
});
|
|
1406
1788
|
}
|
|
1407
1789
|
/**
|