mcp-use 1.2.4 → 1.2.5-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 Express integration
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 Express methods while preserving MCP server functionality.
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 Express methods
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 = express();
188
- this.app.use(express.json());
262
+ this.app = new Hono();
189
263
  this.app.use(
264
+ "*",
190
265
  cors({
191
266
  origin: "*",
192
- methods: ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"],
193
- allowedHeaders: [
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 process.env.NODE_ENV === "production";
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
- process.cwd(),
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(process.cwd(), resourcesDir);
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(process.cwd(), TMP_MCP_USE_DIR);
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(process.cwd(), resourcesDir);
836
- const { relative } = await import("path");
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(process.cwd(), resourcesDir)
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
- this.app.use(baseRoute, (req, res, next) => {
900
- const urlPath = req.url || "";
901
- const [pathname, queryString] = urlPath.split("?");
902
- const widgetMatch = pathname.match(/^\/([^/]+)/);
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
- if (pathname === `/${widgetName}` || pathname === `/${widgetName}/`) {
908
- req.url = `/${widgetName}/index.html${queryString ? "?" + queryString : ""}`;
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, viteServer.middlewares);
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(join(tempDir, widget.name, "index.html"), "utf8");
951
- const mcpUrl = process.env.MCP_URL || "/";
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(process.cwd(), "dist", "resources", "widgets");
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 widgets = readdirSync(widgetsDir).filter((name) => {
1060
- const widgetPath = join(widgetsDir, name);
1061
- const indexPath = join(widgetPath, "index.html");
1062
- return existsSync(indexPath);
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 = process.env.MCP_URL || "/";
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
- this.app.post(endpoint, express.json(), async (req, res) => {
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
- res.on("close", () => {
1216
- transport.close();
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(req, res, req.body);
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 (req, res) => {
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
- res.on("close", () => {
1503
+ c.req.raw.signal?.addEventListener("abort", () => {
1227
1504
  transport.close();
1228
1505
  });
1229
1506
  await this.server.connect(transport);
1230
- await transport.handleRequest(req, res);
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 (req, res) => {
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
- res.on("close", () => {
1521
+ c.req.raw.signal?.addEventListener("abort", () => {
1238
1522
  transport.close();
1239
1523
  });
1240
1524
  await this.server.connect(transport);
1241
- await transport.handleRequest(req, res);
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 Express server with MCP endpoints
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 Express server to listen
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
- this.serverPort = port || (process.env.PORT ? parseInt(process.env.PORT, 10) : 3001);
1269
- if (process.env.HOST) {
1270
- this.serverHost = process.env.HOST;
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
- this.app.listen(this.serverPort, () => {
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
- import("@mcp-use/inspector").then(({ mountInspector }) => {
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
- }).catch(() => {
1327
- });
1684
+ } catch {
1685
+ }
1328
1686
  }
1329
1687
  /**
1330
1688
  * Setup default widget serving routes
1331
1689
  *
1332
- * Configures Express routes to serve MCP UI widgets and their static assets.
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/*", (req, res, next) => {
1352
- const widget = req.params.widget;
1353
- const assetFile = req.params[0];
1354
- const assetPath = join(
1355
- process.cwd(),
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
- res.sendFile(assetPath, (err) => err ? next() : void 0);
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/*", (req, res, next) => {
1366
- const assetFile = req.params[0];
1367
- const widgetsDir = join(process.cwd(), "dist", "resources", "widgets");
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
- return res.sendFile(assetPath);
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
- next();
1753
+ return c.notFound();
1377
1754
  } catch {
1378
- next();
1755
+ return c.notFound();
1379
1756
  }
1380
1757
  });
1381
- this.app.get("/mcp-use/widgets/:widget", (req, res, next) => {
1382
- const filePath = join(
1383
- process.cwd(),
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
- req.params.widget,
1765
+ widget,
1388
1766
  "index.html"
1389
1767
  );
1390
- let html = readFileSync(filePath, "utf8");
1391
- html = html.replace(
1392
- /src="\/mcp-use\/widgets\/([^"]+)"/g,
1393
- `src="${this.serverBaseUrl}/mcp-use/widgets/$1"`
1394
- );
1395
- html = html.replace(
1396
- /href="\/mcp-use\/widgets\/([^"]+)"/g,
1397
- `href="${this.serverBaseUrl}/mcp-use/widgets/$1"`
1398
- );
1399
- html = html.replace(
1400
- /<head[^>]*>/i,
1401
- `<head>
1402
- <script>window.__getFile = (filename) => { return "${this.serverBaseUrl}/mcp-use/widgets/${req.params.widget}/"+filename }</script>`
1403
- );
1404
- res.send(html);
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
  /**