mcp-use 2.0.0-beta.12 → 2.0.0-beta.13

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.
Files changed (40) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/branding.d.ts +7 -10
  3. package/dist/branding.d.ts.map +1 -1
  4. package/dist/{chunk-GTOOHAI2.js → chunk-JEVBOMCE.js} +4 -3
  5. package/dist/chunk-JEVBOMCE.js.map +1 -0
  6. package/dist/{chunk-DQ7LE4ZA.js → chunk-XKJP5S7C.js} +17 -14
  7. package/dist/chunk-XKJP5S7C.js.map +1 -0
  8. package/dist/cli/build.d.ts +1 -1
  9. package/dist/cli/dev.d.ts.map +1 -1
  10. package/dist/cli/mcp-env-declaration.d.ts +20 -0
  11. package/dist/cli/mcp-env-declaration.d.ts.map +1 -0
  12. package/dist/cli/tunnel.d.ts.map +1 -1
  13. package/dist/cli/views.d.ts +7 -5
  14. package/dist/cli/views.d.ts.map +1 -1
  15. package/dist/commands/build.js +4 -4
  16. package/dist/commands/build.js.map +1 -1
  17. package/dist/commands/dev.js +79 -12
  18. package/dist/commands/dev.js.map +1 -1
  19. package/dist/config.d.ts +8 -8
  20. package/dist/index.js +32 -51
  21. package/dist/index.js.map +1 -1
  22. package/dist/inspector-shell.d.ts +3 -0
  23. package/dist/inspector-shell.d.ts.map +1 -1
  24. package/dist/{landing-handler-3VN4BMR2.js → landing-handler-Z7WDR4S2.js} +6 -3
  25. package/dist/landing-handler-Z7WDR4S2.js.map +1 -0
  26. package/dist/landing-handler.d.ts +1 -1
  27. package/dist/landing-handler.d.ts.map +1 -1
  28. package/dist/landing.d.ts +2 -0
  29. package/dist/landing.d.ts.map +1 -1
  30. package/dist/landing.js +1 -1
  31. package/dist/react/types/register.d.ts +1 -1
  32. package/dist/react/types/register.d.ts.map +1 -1
  33. package/dist/server.d.ts +2 -3
  34. package/dist/server.d.ts.map +1 -1
  35. package/package.json +3 -3
  36. package/dist/chunk-DQ7LE4ZA.js.map +0 -1
  37. package/dist/chunk-GTOOHAI2.js.map +0 -1
  38. package/dist/cli/tools-declaration.d.ts +0 -22
  39. package/dist/cli/tools-declaration.d.ts.map +0 -1
  40. package/dist/landing-handler-3VN4BMR2.js.map +0 -1
@@ -5,13 +5,13 @@ import {
5
5
  buildDevViewsManifest,
6
6
  discoverEntry,
7
7
  discoverViews,
8
- ensureToolsDeclaration,
8
+ ensureMcpEnvDeclaration,
9
9
  isViewPath,
10
10
  mcpUseViewsPlugin,
11
11
  resolveTailwindCss,
12
12
  resolveUserViteConfig,
13
13
  resolveWorkspacePaths
14
- } from "../chunk-DQ7LE4ZA.js";
14
+ } from "../chunk-XKJP5S7C.js";
15
15
 
16
16
  // src/cli/dev.ts
17
17
  import { spawn as spawn2 } from "child_process";
@@ -100,10 +100,19 @@ var DEFAULT_TUNNEL_API = "https://local.mcp-use.run";
100
100
  function tunnelApiBase() {
101
101
  return process.env["MCP_USE_TUNNEL_API"] ?? DEFAULT_TUNNEL_API;
102
102
  }
103
+ var RESPAWN_BACKOFF_INITIAL_MS = 1e3;
104
+ var RESPAWN_BACKOFF_MAX_MS = 3e4;
103
105
  function createTunnelManager(stateFilePath) {
104
106
  let proc;
105
107
  let currentUrl = null;
106
108
  let currentSubdomain;
109
+ let activePort;
110
+ let intentionalStop = false;
111
+ let respawnInFlight;
112
+ let respawnBackoffMs = RESPAWN_BACKOFF_INITIAL_MS;
113
+ const sleep = (ms) => new Promise((resolve) => {
114
+ setTimeout(resolve, ms);
115
+ });
107
116
  const markShutdown = () => {
108
117
  if (proc !== void 0 && "markShutdown" in proc && typeof proc.markShutdown === "function") {
109
118
  proc.markShutdown();
@@ -141,6 +150,8 @@ function createTunnelManager(stateFilePath) {
141
150
  );
142
151
  }
143
152
  };
153
+ let scheduleRespawn = () => {
154
+ };
144
155
  const spawnTunnel = (port, subdomain) => new Promise((resolve, reject) => {
145
156
  console.log(`[mcp-use] starting tunnel for port ${port}\u2026`);
146
157
  const userAgent = process.env["npm_config_user_agent"] ?? "";
@@ -208,7 +219,11 @@ function createTunnelManager(stateFilePath) {
208
219
  if (proc === child) {
209
220
  proc = void 0;
210
221
  currentUrl = null;
211
- currentSubdomain = void 0;
222
+ if (intentionalStop) {
223
+ currentSubdomain = void 0;
224
+ } else {
225
+ scheduleRespawn();
226
+ }
212
227
  }
213
228
  });
214
229
  const setupTimeout = setTimeout(() => {
@@ -218,11 +233,62 @@ function createTunnelManager(stateFilePath) {
218
233
  }
219
234
  }, 3e4);
220
235
  });
236
+ scheduleRespawn = () => {
237
+ if (intentionalStop || activePort === void 0 || currentUrl !== null) {
238
+ return;
239
+ }
240
+ if (respawnInFlight !== void 0) {
241
+ return;
242
+ }
243
+ respawnInFlight = (async () => {
244
+ while (!intentionalStop && activePort !== void 0 && currentUrl === null) {
245
+ console.log("[mcp-use] tunnel disconnected, restarting\u2026");
246
+ const subdomain = currentSubdomain ?? await loadSavedSubdomain();
247
+ try {
248
+ if (subdomain !== void 0) {
249
+ await releaseSubdomain(subdomain);
250
+ }
251
+ let tunnelInfo;
252
+ try {
253
+ tunnelInfo = await spawnTunnel(activePort, subdomain);
254
+ } catch (error) {
255
+ if (subdomain !== void 0) {
256
+ console.log(
257
+ `[mcp-use] subdomain "${subdomain}" unavailable, requesting a new one\u2026`
258
+ );
259
+ tunnelInfo = await spawnTunnel(activePort, void 0);
260
+ } else {
261
+ throw error;
262
+ }
263
+ }
264
+ proc = tunnelInfo.process;
265
+ currentUrl = tunnelInfo.url;
266
+ currentSubdomain = tunnelInfo.subdomain;
267
+ await persistSubdomain(tunnelInfo.subdomain);
268
+ respawnBackoffMs = RESPAWN_BACKOFF_INITIAL_MS;
269
+ return;
270
+ } catch (error) {
271
+ console.warn(
272
+ `[mcp-use] tunnel restart failed: ${error instanceof Error ? error.message : String(error)}`
273
+ );
274
+ await sleep(respawnBackoffMs);
275
+ respawnBackoffMs = Math.min(
276
+ respawnBackoffMs * 2,
277
+ RESPAWN_BACKOFF_MAX_MS
278
+ );
279
+ }
280
+ }
281
+ })().finally(() => {
282
+ respawnInFlight = void 0;
283
+ });
284
+ };
221
285
  return {
222
286
  status() {
223
287
  return { url: currentUrl };
224
288
  },
225
289
  async start(port) {
290
+ intentionalStop = false;
291
+ activePort = port;
226
292
  if (currentUrl !== null) {
227
293
  return {
228
294
  url: currentUrl,
@@ -254,7 +320,11 @@ function createTunnelManager(stateFilePath) {
254
320
  return { url: tunnelInfo.url, subdomain: tunnelInfo.subdomain };
255
321
  },
256
322
  async stop() {
323
+ intentionalStop = true;
257
324
  markShutdown();
325
+ if (respawnInFlight !== void 0) {
326
+ await respawnInFlight;
327
+ }
258
328
  if (currentSubdomain !== void 0) {
259
329
  await releaseSubdomain(currentSubdomain);
260
330
  }
@@ -262,8 +332,10 @@ function createTunnelManager(stateFilePath) {
262
332
  proc.kill("SIGINT");
263
333
  proc = void 0;
264
334
  }
335
+ activePort = void 0;
265
336
  currentUrl = null;
266
337
  currentSubdomain = void 0;
338
+ respawnBackoffMs = RESPAWN_BACKOFF_INITIAL_MS;
267
339
  }
268
340
  };
269
341
  }
@@ -359,8 +431,8 @@ async function runDev(options) {
359
431
  process.env["PORT"] = String(port);
360
432
  const localFallbackMcpUrl = process.env["MCP_URL"] === void 0 && (host === "127.0.0.1" || host === "localhost" || host === "::1") ? `http://localhost:${port}` : void 0;
361
433
  const entry = discoverEntry(options.cwd, options.entry);
362
- if (await ensureToolsDeclaration(options.cwd, entry)) {
363
- console.log("[mcp-use] created tools.d.ts");
434
+ if (await ensureMcpEnvDeclaration(options.cwd, entry)) {
435
+ console.log("[mcp-use] created mcp-env.d.ts");
364
436
  }
365
437
  let currentViews = discoverViews(options.cwd);
366
438
  const viewsAtStartup = currentViews.length > 0;
@@ -530,15 +602,10 @@ async function runDev(options) {
530
602
  const tunnelUrl = tunnelManager.status().url;
531
603
  const tunnelHost = tunnelUrl !== null ? new URL(tunnelUrl).hostname : null;
532
604
  const extra = tunnelHost !== null ? [tunnelHost] : [];
533
- const readOnly = req.method === "GET" || req.method === "HEAD";
534
- const hostResult = validateHostHeader(req.headers.host, [
605
+ const result = validateHostHeader(req.headers.host, [
535
606
  ...localhostAllowedHostnames(),
536
607
  ...extra
537
608
  ]);
538
- const result = hostResult.ok && !readOnly ? validateOriginHeader(req.headers.origin, [
539
- ...localhostAllowedOrigins(),
540
- ...extra
541
- ]) : hostResult;
542
609
  if (result.ok) {
543
610
  return false;
544
611
  }
@@ -578,7 +645,7 @@ async function runDev(options) {
578
645
  const url = req.url ?? "/";
579
646
  const pathname = new URL(url, "http://127.0.0.1").pathname;
580
647
  const viewsEnabled = viewsAtStartup && currentViews.length > 0;
581
- const isViteRequest = req.method === "GET" && (pathname.startsWith("/@") || pathname.startsWith("/node_modules/") || pathname.startsWith("/.mcp-use/") || viewsEnabled && pathname.startsWith("/resources/"));
648
+ const isViteRequest = req.method === "GET" && (pathname.startsWith("/@") || pathname.startsWith("/node_modules/") || pathname.startsWith("/.mcp-use/") || viewsEnabled && pathname.startsWith("/views/"));
582
649
  if (viewsEnabled && isViteRequest) {
583
650
  applyViteModuleCors(req, res, {
584
651
  tunnelActive: tunnelManager.status().url !== null,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/dev.ts","../../src/cli/port.ts","../../src/cli/dev-api.ts","../../src/cli/tunnel.ts"],"sourcesContent":["/**\n * `mcp-use dev` — a single long-lived dev process (CLI_SPEC.md § Commands →\n * dev): a Vite dev server (Environment API, node/SSR environment only) loads\n * the entry through the module runner; one HTTP listener delegates every\n * request to an atomically swappable handler reference.\n *\n * When views exist, the same Vite server gains a client environment with real\n * HMR for view files; the CLI primes views on each entry reload via the\n * internal {@link registerViews} API (VIEWS_SPEC.md § Dev).\n *\n * Reload, not HMR: on file change the entry is re-imported and the handler\n * reference swapped — no registration diffing or running-registry mutation.\n * All handler generations share one event bus, so a successful swap invalidates\n * the tool, prompt, and resource lists for subscribed development clients.\n *\n * This module is reached only through the bin's dedicated dynamic dev import,\n * so library consumers and production startup never evaluate Vite.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createServer as createNodeServer } from \"node:http\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync } from \"node:fs\";\nimport { networkInterfaces } from \"node:os\";\nimport { join } from \"node:path\";\nimport tailwindcss from \"@tailwindcss/vite\";\nimport react from \"@vitejs/plugin-react\";\nimport { createServer, createServerModuleRunner } from \"vite\";\nimport {\n InMemoryServerEventBus,\n localhostAllowedHostnames,\n localhostAllowedOrigins,\n type ServerEventBus,\n validateHostHeader,\n validateOriginHeader,\n} from \"@modelcontextprotocol/server\";\n\nimport { resolvePort as resolvePreferredPort } from \"../bin/args.js\";\nimport { discoverEntry } from \"./entry.js\";\nimport { resolvePort } from \"./port.js\";\nimport { resolveTailwindCss, resolveUserViteConfig } from \"./vite-config.js\";\nimport { createDevApiHandler } from \"./dev-api.js\";\nimport { createTunnelManager } from \"./tunnel.js\";\nimport { ensureToolsDeclaration } from \"./tools-declaration.js\";\nimport { resolveWorkspacePaths } from \"./workspace.js\";\nimport { mcpUseViewsPlugin } from \"./views-plugin.js\";\nimport {\n buildDevViewsManifest,\n discoverViews,\n isViewPath,\n type DiscoveredView,\n} from \"./views.js\";\nimport type { ViewsManifest } from \"../views/types.js\";\n\n/** Universal web handler returned by `MCPServer.getHandler()`. */\ntype FrameworkHandler = (\n input: Request | { req: { raw: Request } }\n) => Promise<Response>;\n\n/**\n * The duck-typed shape the entry's default export must satisfy: an\n * `MCPServer` instance (checked structurally so the runner may load its own\n * copy of `mcp-use`).\n */\ninterface ServerLike {\n getHandler(options?: { bus?: ServerEventBus }): FrameworkHandler;\n /** URL path prefix the MCP endpoint is mounted at (default `\"/mcp\"`). */\n readonly basePath?: string;\n __primeViews(\n views: ViewsManifest,\n options?: { dev?: boolean; projectRoot?: string }\n ): void;\n}\n\n/**\n * Options for {@link runDev}.\n *\n * @internal\n */\nexport interface DevOptions {\n /** Absolute path to the project root. */\n cwd: string;\n /**\n * Explicit entry path (the `--entry` flag), absolute or relative to `cwd`.\n *\n * @defaultValue Conventional discovery: `src/index.ts`, `src/server.ts`,\n * `index.ts`, `server.ts` — first hit wins.\n */\n entry?: string;\n /**\n * Preferred port. When taken, the next free port upward is used (and the\n * substitution logged).\n *\n * @defaultValue The `PORT` environment variable, else `3000`.\n */\n port?: number;\n /**\n * Interface address to bind — `\"0.0.0.0\"` exposes the dev server to the\n * local network (phones, containers, teammates); the default keeps it\n * machine-local. Printed and auto-opened URLs use `localhost` for\n * loopback/wildcard binds and the given host verbatim otherwise.\n *\n * Localhost-class binds get DNS-rebinding protection: every request's\n * `Host` is validated (plus the active tunnel hostname); `Origin` only on\n * non-GET/HEAD. Non-localhost binds skip validation — the legitimate\n * hostnames are unknowable here — and log a warning instead.\n *\n * @defaultValue `\"127.0.0.1\"` (matching the server's localhost-first posture).\n */\n host?: string;\n /**\n * Abort signal for embedding and tests: aborting shuts the dev process\n * down gracefully (same path as SIGINT/SIGTERM). The CLI itself does not\n * pass this.\n */\n signal?: AbortSignal;\n /**\n * When `true`, start a public tunnel as soon as the HTTP listener is bound\n * (same as the inspector \"Start Tunnel\" control, but at startup).\n *\n * @defaultValue `false`\n */\n tunnel?: boolean;\n /**\n * Auto-open the inspector in the default browser once the listener is\n * bound (`--no-open` sets this to `false`). Opening is additionally\n * skipped when stdout is not a TTY — agents and CI never get a spurious\n * browser launch or a \"failed to open\" error.\n *\n * @defaultValue `true`\n */\n open?: boolean;\n}\n\n/**\n * Best-effort open of `url` in the platform's default browser.\n *\n * Dependency-free (`open`/`start`/`xdg-open` via spawn), detached, and\n * error-swallowing: a missing opener (headless Linux, containers) must never\n * crash or log noise into the dev process.\n */\nfunction openInBrowser(url: string): void {\n const [command, args]: [string, string[]] =\n process.platform === \"darwin\"\n ? [\"open\", [url]]\n : process.platform === \"win32\"\n ? // `start` is a cmd built-in; the empty string is the window title.\n [\"cmd\", [\"/c\", \"start\", \"\", url]]\n : [\"xdg-open\", [url]];\n try {\n const child = spawn(command, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {\n // Swallow: auto-open is a convenience, never a failure.\n });\n child.unref();\n } catch {\n // Synchronous spawn failures are equally non-fatal.\n }\n}\n\n/**\n * First non-internal IPv4 address of this machine, for the \"Network:\" line\n * printed on wildcard binds — `undefined` when offline.\n */\nfunction lanAddress(): string | undefined {\n for (const addresses of Object.values(networkInterfaces())) {\n for (const address of addresses ?? []) {\n if (!address.internal && address.family === \"IPv4\") {\n return address.address;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Merge `Origin` into an existing `Vary` header without duplicating it.\n *\n * @param res - Node response whose `Vary` may already list other tokens.\n */\nfunction appendVaryOrigin(res: ServerResponse): void {\n const existing = res.getHeader(\"Vary\");\n const current =\n typeof existing === \"string\"\n ? existing\n : Array.isArray(existing)\n ? existing.join(\", \")\n : existing !== undefined\n ? String(existing)\n : \"\";\n if (\n current\n .split(\",\")\n .map((token) => token.trim().toLowerCase())\n .includes(\"origin\")\n ) {\n return;\n }\n res.setHeader(\"Vary\", current === \"\" ? \"Origin\" : `${current}, Origin`);\n}\n\n/**\n * CORS for Vite-served module-graph URLs on the dev listener.\n *\n * Tunnel active → `Access-Control-Allow-Origin: *` (foreign / opaque hosts).\n * No tunnel on a localhost bind → reflect a validated loopback `Origin`\n * (exact value) and set `Vary: Origin`; reflect `null` for opaque sandbox\n * iframes; foreign or missing Origin get no ACAO.\n *\n * @param req - Incoming request (reads `Origin`).\n * @param res - Response to receive CORS headers.\n * @param options - `tunnelActive` when a public tunnel URL is set;\n * `localhostBind` when the listener is on a loopback host.\n */\nfunction applyViteModuleCors(\n req: IncomingMessage,\n res: ServerResponse,\n options: { tunnelActive: boolean; localhostBind: boolean }\n): void {\n if (options.tunnelActive) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n return;\n }\n if (!options.localhostBind) {\n return;\n }\n const originHeader = req.headers.origin;\n // Sandboxed MCP App iframes (opaque origin) send `Origin: null` on module GETs.\n if (originHeader === \"null\") {\n res.setHeader(\"Access-Control-Allow-Origin\", \"null\");\n return;\n }\n // validateOriginHeader treats a missing Origin as ok (no header to check);\n // CORS reflection requires a concrete loopback origin string.\n if (typeof originHeader !== \"string\" || originHeader === \"\") {\n return;\n }\n const result = validateOriginHeader(originHeader, localhostAllowedOrigins());\n if (!result.ok || result.origin === undefined) {\n return;\n }\n res.setHeader(\"Access-Control-Allow-Origin\", result.origin);\n appendVaryOrigin(res);\n}\n\n/**\n * Validate the entry module's default export and return it as a\n * {@link ServerLike}.\n */\nfunction serverFrom(moduleExports: Record<string, unknown>): ServerLike {\n const server = moduleExports[\"default\"];\n if (server === null || typeof server !== \"object\") {\n throw new Error(\n \"The server entry must default-export the MCPServer instance \" +\n \"(`export default server`) and never call listen() itself — \" +\n \"`mcp-use dev` owns the socket.\"\n );\n }\n const candidate = server as Partial<ServerLike>;\n if (typeof candidate.getHandler !== \"function\") {\n throw new Error(\n \"The entry's default export has no getHandler() — it must be the \" +\n \"MCPServer instance (`export default server`).\"\n );\n }\n return candidate as ServerLike;\n}\n\n/**\n * Run the dev server: import the entry through Vite's module runner (full\n * TS/alias support), serve `server.getHandler()` on one long-lived HTTP\n * listener, and swap the handler on file change.\n *\n * A throwing re-import keeps the previous handler alive and prints the error\n * — the dev process never crashes on a bad save. `.env` (if present) is\n * loaded from `cwd` via `process.loadEnvFile()` before the entry is first\n * imported.\n *\n * The returned promise resolves after a graceful shutdown (SIGINT/SIGTERM or\n * `options.signal` aborting).\n *\n * @param options - Project root, optional entry override, port and host.\n * @throws If no entry is found, if the initial import fails, if the entry's\n * default export is not an `MCPServer` (see the entry contract in\n * CLI_SPEC.md), or if `vite` is not installed (`mcp-use dev` requires it as a\n * devDependency).\n *\n * @internal Reached only via the bin's dedicated dev command chunk — not\n * re-exported from the package's \".\" entry.\n */\nexport async function runDev(options: DevOptions): Promise<void> {\n const host = options.host ?? \"127.0.0.1\";\n const paths = resolveWorkspacePaths(options.cwd);\n const eventBus = new InMemoryServerEventBus((error) => {\n console.error(\"[mcp-use] notification delivery failed:\", error);\n });\n\n const envPath = join(options.cwd, \".env\");\n if (existsSync(envPath)) {\n process.loadEnvFile(envPath);\n }\n\n // Resolve the listener before importing the entry so module-scope OAuth\n // configuration observes the canonical port that this CLI will own.\n // The HTTP listener is a raw node:http server with the vendored\n // toNodeHandler bridge — same role as the old @hono/node-server\n // getRequestListener, without a hono dependency.\n // Connect-style ((req, res, next)) with no fetch-shaped equivalent, so\n // splicing it in front of the swappable fetch handler needs the raw Node\n // request boundary. Creating the (not-yet-listening) server up front also\n // lets Vite attach its HMR websocket to this same socket (`hmr.server`\n // below) — one port total, so several `mcp-use dev` processes coexist\n // without websocket port collisions.\n const httpServer = createNodeServer();\n\n const { port, requested } = await resolvePort(\n resolvePreferredPort(options.port),\n host\n );\n if (port !== requested) {\n console.log(`[mcp-use] port ${requested} is taken, using ${port}`);\n }\n process.env[\"PORT\"] = String(port);\n\n const localFallbackMcpUrl =\n process.env[\"MCP_URL\"] === undefined &&\n (host === \"127.0.0.1\" || host === \"localhost\" || host === \"::1\")\n ? `http://localhost:${port}`\n : undefined;\n\n const entry = discoverEntry(options.cwd, options.entry);\n if (await ensureToolsDeclaration(options.cwd, entry)) {\n console.log(\"[mcp-use] created tools.d.ts\");\n }\n let currentViews: DiscoveredView[] = discoverViews(options.cwd);\n // The Vite client environment (views plugin, Fast Refresh, HMR socket,\n // asset origin) is configured once, from this snapshot. `currentViews`\n // stays live for request routing and re-priming, but a project that starts\n // with zero views needs a dev-server restart to pick up its first view.\n const viewsAtStartup = currentViews.length > 0;\n const userViteConfig = resolveUserViteConfig(options.cwd);\n\n // The bind address is not always a browsable address: `0.0.0.0`/`::`\n // accept connections on every interface but are not valid request hosts\n // in every browser, and `127.0.0.1` reads worse than `localhost` in logs.\n // Anything else (a LAN IP, a hostname) is browsable as itself; bare IPv6\n // addresses need brackets in URLs.\n const loopbackOrWildcard = [\"127.0.0.1\", \"localhost\", \"0.0.0.0\", \"::\", \"::1\"];\n const browsableHost = loopbackOrWildcard.includes(host) ? \"localhost\" : host;\n const devOrigin = `http://${\n browsableHost.includes(\":\") ? `[${browsableHost}]` : browsableHost\n }:${port}`;\n\n const vite = await createServer({\n root: options.cwd,\n configFile: viewsAtStartup ? userViteConfig : false,\n envFile: false,\n logLevel: \"warn\",\n cacheDir: paths.cache,\n resolve: { alias: { tailwindcss: resolveTailwindCss() } },\n plugins: viewsAtStartup\n ? [\n tailwindcss(),\n mcpUseViewsPlugin({\n getViews: () => currentViews,\n dev: { reactRefresh: true },\n }),\n react(),\n ]\n : [],\n server: {\n middlewareMode: true,\n // Absolute asset URLs in dev: without `origin`, Vite emits root-relative\n // paths that resolve against the host page inside srcdoc iframes.\n ...(viewsAtStartup && { origin: devOrigin }),\n // CORS on module URLs is owned by onRequest below (permissive ACAO\n // only while the tunnel is active), not by Vite's own middleware —\n // whose default localhost-only policy would block tunnel-rendering\n // hosts, and whose headers would fight the tunnel-gated ones.\n cors: false,\n // View HMR rides the one HTTP listener: Vite attaches its websocket\n // upgrade handler to our server, so no dedicated HMR port exists to\n // collide when several dev processes run side by side.\n hmr: viewsAtStartup ? { server: httpServer } : false,\n },\n ssr: {\n external: true,\n },\n });\n\n const ssrEnvironment = vite.environments.ssr;\n const runner = createServerModuleRunner(ssrEnvironment, {\n hmr: false,\n sourcemapInterceptor: \"node\",\n });\n\n const importServer = async (): Promise<ServerLike> => {\n const load = async (): Promise<ServerLike> => {\n const moduleExports = (await runner.import(entry)) as Record<\n string,\n unknown\n >;\n const server = serverFrom(moduleExports);\n\n if (currentViews.length > 0) {\n const viewsManifest = buildDevViewsManifest(currentViews);\n if (typeof server.__primeViews !== \"function\") {\n throw new Error(\n \"Loaded MCPServer instance does not support __primeViews.\"\n );\n }\n server.__primeViews(viewsManifest, {\n dev: true,\n projectRoot: options.cwd,\n });\n }\n\n return server;\n };\n\n if (localFallbackMcpUrl === undefined) {\n return load();\n }\n\n // This is safe only because this CLI selected and will bind this local\n // listener. Never derive OAuth identity from an untrusted request Host.\n // Scope it to entry evaluation: MCPServer freezes the trusted canonical\n // resource during construction, while later runtime code must not inherit\n // this CLI-owned synthetic environment value.\n const previousMcpUrl = process.env[\"MCP_URL\"];\n try {\n process.env[\"MCP_URL\"] = localFallbackMcpUrl;\n return await load();\n } finally {\n if (previousMcpUrl === undefined) {\n delete process.env[\"MCP_URL\"];\n } else {\n process.env[\"MCP_URL\"] = previousMcpUrl;\n }\n }\n };\n\n let currentHandler: FrameworkHandler;\n let basePath: string;\n try {\n const server = await importServer();\n currentHandler = server.getHandler({ bus: eventBus });\n basePath = server.basePath ?? \"/mcp\";\n } catch (error) {\n await runner.close();\n await vite.close();\n throw error;\n }\n\n let reloading = false;\n let dirty = false;\n const reload = (): void => {\n if (reloading) {\n dirty = true;\n return;\n }\n reloading = true;\n void (async () => {\n do {\n dirty = false;\n try {\n runner.evaluatedModules.clear();\n const server = await importServer();\n const nextHandler = server.getHandler({ bus: eventBus });\n currentHandler = nextHandler;\n basePath = server.basePath ?? \"/mcp\";\n eventBus.publish({ kind: \"tools_list_changed\" });\n eventBus.publish({ kind: \"prompts_list_changed\" });\n eventBus.publish({ kind: \"resources_list_changed\" });\n console.log(\"[mcp-use] reloaded server entry\");\n } catch (error) {\n console.error(\n \"[mcp-use] reload failed — keeping the previous server:\\n\",\n error\n );\n }\n } while (dirty);\n reloading = false;\n })();\n };\n\n const onSsrFileEvent = (file: string): void => {\n if (isViewPath(file, options.cwd)) {\n return;\n }\n const modules = ssrEnvironment.moduleGraph.getModulesByFile(file);\n if (modules === undefined || modules.size === 0) {\n return;\n }\n for (const mod of modules) {\n ssrEnvironment.moduleGraph.invalidateModule(mod);\n }\n reload();\n };\n\n const onViewFilesystemEvent = (file: string): void => {\n if (!isViewPath(file, options.cwd)) {\n return;\n }\n\n const previousViews = currentViews;\n currentViews = discoverViews(options.cwd);\n\n const viewsChanged =\n previousViews.length !== currentViews.length ||\n previousViews.some(\n (v, i) =>\n v.name !== currentViews[i]?.name ||\n v.entryPath !== currentViews[i]?.entryPath\n );\n\n if (viewsChanged) {\n reload();\n }\n };\n\n const onFileAddOrUnlink = (file: string): void => {\n onViewFilesystemEvent(file);\n onSsrFileEvent(file);\n };\n\n // A `change` event cannot add or remove a view directory, so only\n // `add`/`unlink` rescan `resources/` — content edits never pay for the\n // synchronous filesystem walk in discoverViews().\n vite.watcher.on(\"change\", onSsrFileEvent);\n vite.watcher.on(\"add\", onFileAddOrUnlink);\n vite.watcher.on(\"unlink\", onFileAddOrUnlink);\n\n // --- One long-lived HTTP listener delegating to the current handler. -----\n const tunnelManager = createTunnelManager(paths.tunnel);\n\n // Vite owns the upgrade listener and validates Host before our HTTP request\n // guard runs. The public tunnel has already been validated by the proxy and\n // tunnel manager, so present only that active host as the local listener.\n httpServer.prependListener(\"upgrade\", (req) => {\n const tunnelUrl = tunnelManager.status().url;\n if (tunnelUrl === null) return;\n const tunnelHost = new URL(tunnelUrl).hostname;\n const requestHost = req.headers.host?.split(\":\")[0];\n if (requestHost === tunnelHost) {\n req.headers.host = `localhost:${port}`;\n }\n });\n\n // --- DNS-rebinding protection. --------------------------------------------\n // getHandler() applies no Host/Origin validation (its contract assumes a\n // platform edge in front); in dev this process *is* the edge, so\n // localhost-class binds get the same localhost-allowlist checks listen()\n // applies — extended with the active tunnel hostname, since tunnel traffic\n // arrives with the tunnel's public Host. The check runs before any routing,\n // covering the MCP endpoint, the dev API (tunnel control), and Vite-served\n // module URLs alike. Non-localhost binds get no validation (the legitimate\n // hostnames are unknowable here) — a startup warning below says so.\n //\n // Host is validated on every request: rebinding works by making the\n // attacker's page same-origin with this server, and the Host header is\n // where that shows up. Origin is validated only on side-effect-bearing\n // methods (the MCP wire and the dev API are all POST): sandboxed view\n // iframes have an opaque origin, so their module/asset GETs legitimately\n // carry `Origin: null` — and external hosts rendering views through the\n // tunnel fetch assets with their own origins. Those cross-origin loads also\n // need CORS: the MCP server's view asset/public routes always emit\n // `Access-Control-Allow-Origin: *`. Vite-served module URLs (onRequest\n // below) emit `*` while a tunnel is active; without a tunnel, localhost\n // binds reflect a validated loopback Origin (exact value + `Vary: Origin`)\n // so a local MCP host can load the module graph, while foreign / opaque /\n // missing Origin get no ACAO.\n const localhostBind = [\"127.0.0.1\", \"localhost\", \"::1\"].includes(host);\n const rejectDisallowedRequest = (\n req: IncomingMessage,\n res: ServerResponse\n ): boolean => {\n const tunnelUrl = tunnelManager.status().url;\n const tunnelHost = tunnelUrl !== null ? new URL(tunnelUrl).hostname : null;\n const extra = tunnelHost !== null ? [tunnelHost] : [];\n const readOnly = req.method === \"GET\" || req.method === \"HEAD\";\n const hostResult = validateHostHeader(req.headers.host, [\n ...localhostAllowedHostnames(),\n ...extra,\n ]);\n const result =\n hostResult.ok && !readOnly\n ? validateOriginHeader(req.headers.origin, [\n ...localhostAllowedOrigins(),\n ...extra,\n ])\n : hostResult;\n if (result.ok) {\n return false;\n }\n // Same JSON-RPC 403 shape the SDK's own validation responses use.\n res.writeHead(403, { \"content-type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code: -32000, message: result.message },\n id: null,\n })\n );\n return true;\n };\n\n /**\n * Tear down everything the running dev process owns, in dependency order:\n * watcher subscriptions, tunnel, HTTP listener, module runner, Vite.\n */\n const teardown = async (): Promise<void> => {\n vite.watcher.off(\"change\", onSsrFileEvent);\n vite.watcher.off(\"add\", onFileAddOrUnlink);\n vite.watcher.off(\"unlink\", onFileAddOrUnlink);\n await tunnelManager.stop();\n await new Promise<void>((done) => httpServer.close(() => done()));\n await runner.close();\n await vite.close();\n };\n\n const devFetch = createDevApiHandler(\n {\n getBasePath: () => basePath,\n port,\n tunnel: tunnelManager,\n },\n (request) => currentHandler(request)\n );\n\n // Vendored Node bridge — same adapter serve() used internally.\n const { toNodeHandler } = await import(\"../node-bridge.js\");\n const nodeListener = toNodeHandler({ fetch: devFetch });\n\n const onRequest = (req: IncomingMessage, res: ServerResponse): void => {\n if (localhostBind && rejectDisallowedRequest(req, res)) {\n return;\n }\n const url = req.url ?? \"/\";\n const pathname = new URL(url, \"http://127.0.0.1\").pathname;\n // Routing to Vite requires both the client environment (configured only\n // when views existed at startup) and a currently non-empty registry.\n const viewsEnabled = viewsAtStartup && currentViews.length > 0;\n // Vite sees module-graph URLs (/@vite/client, /@id/virtual:…,\n // /.mcp-use/cache/deps/…, view files under /resources/…) plus standard\n // node_modules pre-bundles; everything else — the MCP endpoint included —\n // goes straight to the fetch handler.\n const isViteRequest =\n req.method === \"GET\" &&\n (pathname.startsWith(\"/@\") ||\n pathname.startsWith(\"/node_modules/\") ||\n pathname.startsWith(\"/.mcp-use/\") ||\n (viewsEnabled && pathname.startsWith(\"/resources/\")));\n\n if (viewsEnabled && isViteRequest) {\n // CORS for Vite module URLs: tunnel → `*`; else localhost bind with a\n // validated loopback Origin → reflect that origin (+ Vary). Foreign /\n // opaque / missing Origin stay without ACAO so the source module graph\n // is not readable to arbitrary websites.\n applyViteModuleCors(req, res, {\n tunnelActive: tunnelManager.status().url !== null,\n localhostBind,\n });\n vite.middlewares(req, res, () => {\n void nodeListener(req, res);\n });\n } else {\n void nodeListener(req, res);\n }\n };\n httpServer.on(\"request\", onRequest);\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once(\"error\", reject);\n httpServer.listen(port, host, () => resolve());\n });\n\n console.log(`[mcp-use] dev server ready`);\n if (currentViews.length > 0) {\n console.log(\n ` ➜ Views: ${currentViews.map((v) => v.name).join(\", \")}`\n );\n }\n console.log(` ➜ MCP endpoint: ${devOrigin}${basePath}`);\n console.log(` ➜ Inspector: ${devOrigin}${basePath}/inspector`);\n if (host === \"0.0.0.0\" || host === \"::\") {\n const lan = lanAddress();\n if (lan !== undefined) {\n console.log(` ➜ Network: http://${lan}:${port}${basePath}`);\n }\n }\n if (!localhostBind) {\n console.warn(\n `[mcp-use] --host ${host} serves beyond this machine without Host ` +\n `validation or authentication — anyone with network access can call ` +\n `the MCP endpoint and dev routes.`\n );\n }\n\n if (options.tunnel === true) {\n try {\n const { url } = await tunnelManager.start(port);\n console.log(` ➜ Tunnel: ${url}${basePath}`);\n } catch (error) {\n await teardown();\n throw error;\n }\n }\n\n // Auto-open the inspector — unless disabled (`--no-open`) or stdout is not\n // a TTY (agents/CI: no browser to open, and no error to fail on).\n if (options.open !== false && process.stdout.isTTY === true) {\n openInBrowser(`${devOrigin}${basePath}/inspector`);\n }\n\n // --- Graceful shutdown (SIGINT/SIGTERM or options.signal). ---------------\n await new Promise<void>((resolve) => {\n let closing = false;\n const shutdown = (): void => {\n if (closing) return;\n closing = true;\n process.off(\"SIGINT\", shutdown);\n process.off(\"SIGTERM\", shutdown);\n void (async () => {\n await teardown();\n resolve();\n })();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n if (options.signal !== undefined) {\n if (options.signal.aborted) {\n shutdown();\n } else {\n options.signal.addEventListener(\"abort\", shutdown, { once: true });\n }\n }\n });\n}\n","/**\n * Port resolution for `mcp-use dev`: `--port`, else `PORT` env, else 3000 — probing\n * upward when the requested port is taken.\n */\n\nimport { createServer } from \"node:net\";\n\n/** How many consecutive ports to probe before giving up. */\nconst MAX_PROBES = 100;\n\n/**\n * Outcome of {@link resolvePort}.\n *\n * @internal\n */\nexport interface ResolvedPort {\n /** The free port to bind. */\n port: number;\n /** The port that was originally requested (differs when probing moved on). */\n requested: number;\n}\n\n/** Check whether `port` is free to bind on `host`. */\nfunction isPortFree(port: number, host: string): Promise<boolean> {\n return new Promise((resolve) => {\n const probe = createServer();\n probe.unref();\n probe.once(\"error\", () => resolve(false));\n probe.listen({ port, host }, () => {\n probe.close(() => resolve(true));\n });\n });\n}\n\n/**\n * Resolve the port to bind: take `requested` (already reduced from\n * `--port` / `PORT` / the default by the caller) and probe upward until a\n * free port is found.\n *\n * @param requested - The preferred port.\n * @param host - The host the server will bind (probing binds the same host).\n * @returns The first free port at or above `requested`, plus the original\n * request so callers can log the substitution.\n * @throws If no free port is found within {@link MAX_PROBES} attempts.\n *\n * @internal\n */\nexport async function resolvePort(\n requested: number,\n host: string\n): Promise<ResolvedPort> {\n for (let port = requested; port < requested + MAX_PROBES; port++) {\n if (await isPortFree(port, host)) {\n return { port, requested };\n }\n }\n throw new Error(\n `No free port found between ${requested} and ${requested + MAX_PROBES - 1} on ${host}.`\n );\n}\n","/**\n * Dev-only inspector API routes intercepted by `mcp-use dev` before the MCP\n * handler — tunnel controls and session metadata for the inspector UI.\n */\n\nimport type { TunnelManager } from \"./tunnel.js\";\n\n/** Web-standard request handler, as returned by `MCPServer.getHandler()`. */\ntype FetchHandler = (request: Request) => Promise<Response>;\n\n/**\n * Dependencies for {@link createDevApiHandler}.\n *\n * @internal\n */\nexport interface DevApiHandlerOptions {\n /** Returns the current MCP mount path (may change after a dev reload). */\n getBasePath: () => string;\n /** Port the dev HTTP listener is bound to. */\n port: number;\n /** Tunnel lifecycle manager owned by the dev process. */\n tunnel: TunnelManager;\n}\n\n/** JSON body for `GET …/inspector/api/dev/info`. */\nexport interface DevInfoResponse {\n /** Full MCP endpoint URL when a tunnel is active; otherwise `null`. */\n mcpUrl: string | null;\n /** Port the dev server listens on. */\n port: number;\n /** Whether the request is served by `mcp-use dev` (always `true` here). */\n fromCli: true;\n /** Public tunnel origin URL, or `null` when no tunnel is running. */\n tunnelUrl: string | null;\n}\n\n/**\n * Wrap an MCP {@link FetchHandler} so dev-only inspector API routes are\n * answered in-process before delegating everything else.\n *\n * @param options - Base path, bound port, and tunnel manager.\n * @param fallback - Handler to invoke when the request is not a dev API route.\n *\n * @example\n * ```ts\n * const handler = createDevApiHandler(\n * { getBasePath: () => \"/mcp\", port: 3000, tunnel },\n * server.getHandler()\n * );\n * ```\n */\nexport function createDevApiHandler(\n options: DevApiHandlerOptions,\n fallback: FetchHandler\n): FetchHandler {\n const devInfo = (): DevInfoResponse => {\n const basePath = options.getBasePath();\n const { url: tunnelUrl } = options.tunnel.status();\n return {\n mcpUrl:\n tunnelUrl !== null\n ? `${tunnelUrl.replace(/\\/+$/, \"\")}${basePath}`\n : null,\n port: options.port,\n fromCli: true,\n tunnelUrl,\n };\n };\n\n return async (request: Request): Promise<Response> => {\n const basePath = options.getBasePath();\n const infoPath = `${basePath}/inspector/api/dev/info`;\n const startPath = `${basePath}/inspector/api/dev/start-tunnel`;\n const stopPath = `${basePath}/inspector/api/dev/stop-tunnel`;\n const pathname = new URL(request.url).pathname;\n\n if (request.method === \"GET\" && pathname === infoPath) {\n return Response.json(devInfo());\n }\n\n if (request.method === \"POST\" && pathname === startPath) {\n try {\n await options.tunnel.start(options.port);\n return Response.json({ ok: true, restarting: false });\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Failed to start tunnel\";\n return Response.json({ error: message }, { status: 500 });\n }\n }\n\n if (request.method === \"POST\" && pathname === stopPath) {\n await options.tunnel.stop();\n return Response.json({ ok: true });\n }\n\n return fallback(request);\n };\n}\n","/**\n * Tunnel lifecycle for `mcp-use dev` — spawns `@mcp-use/tunnel`,\n * parses the public URL from stdout, persists the subdomain under\n * `.mcp-use/state/tunnel.json`, and releases it through the tunnel API before\n * reuse.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { dirname } from \"node:path\";\n\n/** Default base URL for the tunnel reservation/release API. */\nconst DEFAULT_TUNNEL_API = \"https://local.mcp-use.run\";\n\n/**\n * Base URL of the tunnel API (subdomain reservation/release). Overridable via\n * the `MCP_USE_TUNNEL_API` environment variable.\n */\nexport function tunnelApiBase(): string {\n return process.env[\"MCP_USE_TUNNEL_API\"] ?? DEFAULT_TUNNEL_API;\n}\n\n/** On-disk shape of `.mcp-use/state/tunnel.json`. */\ninterface TunnelStateFile {\n /** Last successfully assigned tunnel subdomain. */\n subdomain: string;\n}\n\n/**\n * Minimal tunnel manager surface used by {@link createDevApiHandler} and\n * {@link runDev}.\n */\nexport interface TunnelManager {\n /**\n * Start (or attach) a tunnel targeting `port` and return the public origin\n * URL and assigned subdomain.\n */\n start(port: number): Promise<{ url: string; subdomain: string }>;\n /** Stop the tunnel child process and release the subdomain. */\n stop(): Promise<void>;\n /** Current tunnel public origin URL, or `null` when no tunnel is active. */\n status(): { url: string | null };\n}\n\n/**\n * Create a tunnel manager that reads and writes subdomain state at\n * `stateFilePath` (typically `.mcp-use/state/tunnel.json`).\n *\n * @param stateFilePath - Absolute path to the tunnel state JSON file.\n *\n * @example\n * ```ts\n * const tunnel = createTunnelManager(paths.tunnel);\n * const { url } = await tunnel.start(3000);\n * console.log(url); // https://happy-cat.local.mcp-use.run\n * ```\n */\nexport function createTunnelManager(stateFilePath: string): TunnelManager {\n let proc: ChildProcess | undefined;\n let currentUrl: string | null = null;\n let currentSubdomain: string | undefined;\n\n const markShutdown = (): void => {\n if (\n proc !== undefined &&\n \"markShutdown\" in proc &&\n typeof (proc as { markShutdown?: () => void }).markShutdown === \"function\"\n ) {\n (proc as { markShutdown: () => void }).markShutdown();\n }\n };\n\n const releaseSubdomain = async (subdomain: string): Promise<void> => {\n try {\n await fetch(`${tunnelApiBase()}/api/tunnels/${subdomain}`, {\n method: \"DELETE\",\n signal: AbortSignal.timeout(2_000),\n });\n } catch {\n // Best-effort cleanup; ignore DELETE failures.\n }\n };\n\n const loadSavedSubdomain = async (): Promise<string | undefined> => {\n try {\n const content = await readFile(stateFilePath, \"utf8\");\n const state = JSON.parse(content) as Partial<TunnelStateFile>;\n return typeof state.subdomain === \"string\" ? state.subdomain : undefined;\n } catch {\n return undefined;\n }\n };\n\n const persistSubdomain = async (subdomain: string): Promise<void> => {\n try {\n await mkdir(dirname(stateFilePath), { recursive: true });\n await writeFile(\n stateFilePath,\n JSON.stringify({ subdomain } satisfies TunnelStateFile, null, 2),\n \"utf8\"\n );\n } catch (error) {\n console.warn(\n `[mcp-use] failed to save tunnel subdomain: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n };\n\n const spawnTunnel = (\n port: number,\n subdomain?: string\n ): Promise<{ url: string; subdomain: string; process: ChildProcess }> =>\n new Promise((resolve, reject) => {\n console.log(`[mcp-use] starting tunnel for port ${port}…`);\n\n const userAgent = process.env[\"npm_config_user_agent\"] ?? \"\";\n const [tunnelCommand, tunnelArgs] = userAgent.startsWith(\"pnpm/\")\n ? [\"pnpm\", [\"--silent\", \"dlx\", \"@mcp-use/tunnel\", String(port)]]\n : userAgent.startsWith(\"bun/\")\n ? [\"bunx\", [\"@mcp-use/tunnel\", String(port)]]\n : [\n \"npx\",\n [\"--yes\", \"--prefer-offline\", \"@mcp-use/tunnel\", String(port)],\n ];\n if (subdomain !== undefined) {\n tunnelArgs.push(\"--subdomain\", subdomain);\n }\n\n const child = spawn(tunnelCommand, tunnelArgs, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n });\n\n let resolved = false;\n let shuttingDown = false;\n\n const markChildShutdown = (): void => {\n shuttingDown = true;\n };\n (child as unknown as { markShutdown: () => void }).markShutdown =\n markChildShutdown;\n\n child.stdout.on(\"data\", (data: Buffer) => {\n const text = data.toString();\n const isShutdownMessage =\n text.includes(\"Shutting down\") || text.includes(\"🛑\");\n const isErrorMessage = text.includes(\"✖\") || text.includes(\"Error:\");\n\n if (!shuttingDown && !isShutdownMessage && !isErrorMessage) {\n process.stdout.write(text);\n }\n\n const urlMatch = text.match(/https?:\\/\\/([a-z0-9-]+\\.[a-z0-9.-]+)/i);\n if (urlMatch !== null && !resolved) {\n const url = urlMatch[0];\n const fullDomain = urlMatch[1] ?? \"\";\n const subdomainMatch = fullDomain.match(/^([a-z0-9-]+)\\./i);\n let extractedSubdomain =\n subdomainMatch?.[1] ?? fullDomain.split(\".\")[0] ?? \"\";\n if (!/^[a-z0-9-]+$/i.test(extractedSubdomain)) {\n console.warn(\n `[mcp-use] warning: extracted subdomain \"${extractedSubdomain}\" does not match expected format`\n );\n extractedSubdomain = \"\";\n }\n resolved = true;\n clearTimeout(setupTimeout);\n // No log here — the tunnel child's own stdout (passed through\n // above) already announces the public URL.\n resolve({ url, subdomain: extractedSubdomain, process: child });\n }\n });\n\n child.stderr.on(\"data\", (data: Buffer) => {\n const text = data.toString();\n if (\n !shuttingDown &&\n !text.includes(\"INFO\") &&\n !text.includes(\"bore_cli\") &&\n !text.includes(\"Shutting down\") &&\n // npx inherits pnpm-only npm_config_* env vars (e.g.\n // minimum-release-age, verify-deps-before-run) and warns about\n // every key it doesn't recognize — pure noise for the user.\n !/npm warn Unknown (?:env|project|user|builtin) config/.test(text)\n ) {\n process.stderr.write(data);\n }\n });\n\n child.on(\"error\", (error) => {\n if (!resolved) {\n clearTimeout(setupTimeout);\n reject(new Error(`Failed to start tunnel: ${error.message}`));\n }\n });\n\n child.on(\"exit\", (code) => {\n if (code !== 0 && code !== null && !resolved) {\n clearTimeout(setupTimeout);\n reject(new Error(`Tunnel process exited with code ${code}`));\n }\n // The tunnel CLI can exit after setup when its bore connection or\n // server-side registration disappears. Do not keep reporting a dead\n // public URL through dev/info.\n if (proc === child) {\n proc = undefined;\n currentUrl = null;\n currentSubdomain = undefined;\n }\n });\n\n const setupTimeout = setTimeout(() => {\n if (!resolved) {\n child.kill();\n reject(new Error(\"Tunnel setup timed out\"));\n }\n }, 30_000);\n });\n\n return {\n status(): { url: string | null } {\n return { url: currentUrl };\n },\n\n async start(port: number): Promise<{ url: string; subdomain: string }> {\n if (currentUrl !== null) {\n return {\n url: currentUrl,\n subdomain: currentSubdomain ?? \"\",\n };\n }\n\n const existingSubdomain = await loadSavedSubdomain();\n if (existingSubdomain !== undefined) {\n console.log(`[mcp-use] found existing subdomain: ${existingSubdomain}`);\n await releaseSubdomain(existingSubdomain);\n }\n\n let tunnelInfo: Awaited<ReturnType<typeof spawnTunnel>>;\n try {\n tunnelInfo = await spawnTunnel(port, existingSubdomain);\n } catch (error) {\n if (existingSubdomain !== undefined) {\n console.log(\n `[mcp-use] subdomain \"${existingSubdomain}\" unavailable, requesting a new one…`\n );\n tunnelInfo = await spawnTunnel(port, undefined);\n } else {\n throw error;\n }\n }\n\n proc = tunnelInfo.process;\n currentUrl = tunnelInfo.url;\n currentSubdomain = tunnelInfo.subdomain;\n await persistSubdomain(tunnelInfo.subdomain);\n\n return { url: tunnelInfo.url, subdomain: tunnelInfo.subdomain };\n },\n\n async stop(): Promise<void> {\n markShutdown();\n\n if (currentSubdomain !== undefined) {\n await releaseSubdomain(currentSubdomain);\n }\n\n if (proc !== undefined) {\n proc.kill(\"SIGINT\");\n proc = undefined;\n }\n\n currentUrl = null;\n currentSubdomain = undefined;\n },\n };\n}\n"],"mappings":"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","names":["spawn","createServer","resolvePort","spawn","resolvePort","createServer"]}
1
+ {"version":3,"sources":["../../src/cli/dev.ts","../../src/cli/port.ts","../../src/cli/dev-api.ts","../../src/cli/tunnel.ts"],"sourcesContent":["/**\n * `mcp-use dev` — a single long-lived dev process (CLI_SPEC.md § Commands →\n * dev): a Vite dev server (Environment API, node/SSR environment only) loads\n * the entry through the module runner; one HTTP listener delegates every\n * request to an atomically swappable handler reference.\n *\n * When views exist, the same Vite server gains a client environment with real\n * HMR for view files; the CLI primes views on each entry reload via the\n * internal {@link registerViews} API (VIEWS_SPEC.md § Dev).\n *\n * Reload, not HMR: on file change the entry is re-imported and the handler\n * reference swapped — no registration diffing or running-registry mutation.\n * All handler generations share one event bus, so a successful swap invalidates\n * the tool, prompt, and resource lists for subscribed development clients.\n *\n * This module is reached only through the bin's dedicated dynamic dev import,\n * so library consumers and production startup never evaluate Vite.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createServer as createNodeServer } from \"node:http\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { existsSync } from \"node:fs\";\nimport { networkInterfaces } from \"node:os\";\nimport { join } from \"node:path\";\nimport tailwindcss from \"@tailwindcss/vite\";\nimport react from \"@vitejs/plugin-react\";\nimport { createServer, createServerModuleRunner } from \"vite\";\nimport {\n InMemoryServerEventBus,\n localhostAllowedHostnames,\n localhostAllowedOrigins,\n type ServerEventBus,\n validateHostHeader,\n validateOriginHeader,\n} from \"@modelcontextprotocol/server\";\n\nimport { resolvePort as resolvePreferredPort } from \"../bin/args.js\";\nimport { discoverEntry } from \"./entry.js\";\nimport { resolvePort } from \"./port.js\";\nimport { resolveTailwindCss, resolveUserViteConfig } from \"./vite-config.js\";\nimport { createDevApiHandler } from \"./dev-api.js\";\nimport { createTunnelManager } from \"./tunnel.js\";\nimport { ensureMcpEnvDeclaration } from \"./mcp-env-declaration.js\";\nimport { resolveWorkspacePaths } from \"./workspace.js\";\nimport { mcpUseViewsPlugin } from \"./views-plugin.js\";\nimport {\n buildDevViewsManifest,\n discoverViews,\n isViewPath,\n type DiscoveredView,\n} from \"./views.js\";\nimport type { ViewsManifest } from \"../views/types.js\";\n\n/** Universal web handler returned by `MCPServer.getHandler()`. */\ntype FrameworkHandler = (\n input: Request | { req: { raw: Request } }\n) => Promise<Response>;\n\n/**\n * The duck-typed shape the entry's default export must satisfy: an\n * `MCPServer` instance (checked structurally so the runner may load its own\n * copy of `mcp-use`).\n */\ninterface ServerLike {\n getHandler(options?: { bus?: ServerEventBus }): FrameworkHandler;\n /** URL path prefix the MCP endpoint is mounted at (default `\"/mcp\"`). */\n readonly basePath?: string;\n __primeViews(\n views: ViewsManifest,\n options?: { dev?: boolean; projectRoot?: string }\n ): void;\n}\n\n/**\n * Options for {@link runDev}.\n *\n * @internal\n */\nexport interface DevOptions {\n /** Absolute path to the project root. */\n cwd: string;\n /**\n * Explicit entry path (the `--entry` flag), absolute or relative to `cwd`.\n *\n * @defaultValue Conventional discovery: `src/index.ts`, `src/server.ts`,\n * `index.ts`, `server.ts` — first hit wins.\n */\n entry?: string;\n /**\n * Preferred port. When taken, the next free port upward is used (and the\n * substitution logged).\n *\n * @defaultValue The `PORT` environment variable, else `3000`.\n */\n port?: number;\n /**\n * Interface address to bind — `\"0.0.0.0\"` exposes the dev server to the\n * local network (phones, containers, teammates); the default keeps it\n * machine-local. Printed and auto-opened URLs use `localhost` for\n * loopback/wildcard binds and the given host verbatim otherwise.\n *\n * Localhost-class binds get DNS-rebinding protection: every request's\n * `Host` is validated (plus the active tunnel hostname); `Origin` only on\n * non-GET/HEAD. Non-localhost binds skip validation — the legitimate\n * hostnames are unknowable here — and log a warning instead.\n *\n * @defaultValue `\"127.0.0.1\"` (matching the server's localhost-first posture).\n */\n host?: string;\n /**\n * Abort signal for embedding and tests: aborting shuts the dev process\n * down gracefully (same path as SIGINT/SIGTERM). The CLI itself does not\n * pass this.\n */\n signal?: AbortSignal;\n /**\n * When `true`, start a public tunnel as soon as the HTTP listener is bound\n * (same as the inspector \"Start Tunnel\" control, but at startup).\n *\n * @defaultValue `false`\n */\n tunnel?: boolean;\n /**\n * Auto-open the inspector in the default browser once the listener is\n * bound (`--no-open` sets this to `false`). Opening is additionally\n * skipped when stdout is not a TTY — agents and CI never get a spurious\n * browser launch or a \"failed to open\" error.\n *\n * @defaultValue `true`\n */\n open?: boolean;\n}\n\n/**\n * Best-effort open of `url` in the platform's default browser.\n *\n * Dependency-free (`open`/`start`/`xdg-open` via spawn), detached, and\n * error-swallowing: a missing opener (headless Linux, containers) must never\n * crash or log noise into the dev process.\n */\nfunction openInBrowser(url: string): void {\n const [command, args]: [string, string[]] =\n process.platform === \"darwin\"\n ? [\"open\", [url]]\n : process.platform === \"win32\"\n ? // `start` is a cmd built-in; the empty string is the window title.\n [\"cmd\", [\"/c\", \"start\", \"\", url]]\n : [\"xdg-open\", [url]];\n try {\n const child = spawn(command, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {\n // Swallow: auto-open is a convenience, never a failure.\n });\n child.unref();\n } catch {\n // Synchronous spawn failures are equally non-fatal.\n }\n}\n\n/**\n * First non-internal IPv4 address of this machine, for the \"Network:\" line\n * printed on wildcard binds — `undefined` when offline.\n */\nfunction lanAddress(): string | undefined {\n for (const addresses of Object.values(networkInterfaces())) {\n for (const address of addresses ?? []) {\n if (!address.internal && address.family === \"IPv4\") {\n return address.address;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Merge `Origin` into an existing `Vary` header without duplicating it.\n *\n * @param res - Node response whose `Vary` may already list other tokens.\n */\nfunction appendVaryOrigin(res: ServerResponse): void {\n const existing = res.getHeader(\"Vary\");\n const current =\n typeof existing === \"string\"\n ? existing\n : Array.isArray(existing)\n ? existing.join(\", \")\n : existing !== undefined\n ? String(existing)\n : \"\";\n if (\n current\n .split(\",\")\n .map((token) => token.trim().toLowerCase())\n .includes(\"origin\")\n ) {\n return;\n }\n res.setHeader(\"Vary\", current === \"\" ? \"Origin\" : `${current}, Origin`);\n}\n\n/**\n * CORS for Vite-served module-graph URLs on the dev listener.\n *\n * Tunnel active → `Access-Control-Allow-Origin: *` (foreign / opaque hosts).\n * No tunnel on a localhost bind → reflect a validated loopback `Origin`\n * (exact value) and set `Vary: Origin`; reflect `null` for opaque sandbox\n * iframes; foreign or missing Origin get no ACAO.\n *\n * @param req - Incoming request (reads `Origin`).\n * @param res - Response to receive CORS headers.\n * @param options - `tunnelActive` when a public tunnel URL is set;\n * `localhostBind` when the listener is on a loopback host.\n */\nfunction applyViteModuleCors(\n req: IncomingMessage,\n res: ServerResponse,\n options: { tunnelActive: boolean; localhostBind: boolean }\n): void {\n if (options.tunnelActive) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n return;\n }\n if (!options.localhostBind) {\n return;\n }\n const originHeader = req.headers.origin;\n // Sandboxed MCP App iframes (opaque origin) send `Origin: null` on module GETs.\n if (originHeader === \"null\") {\n res.setHeader(\"Access-Control-Allow-Origin\", \"null\");\n return;\n }\n // validateOriginHeader treats a missing Origin as ok (no header to check);\n // CORS reflection requires a concrete loopback origin string.\n if (typeof originHeader !== \"string\" || originHeader === \"\") {\n return;\n }\n const result = validateOriginHeader(originHeader, localhostAllowedOrigins());\n if (!result.ok || result.origin === undefined) {\n return;\n }\n res.setHeader(\"Access-Control-Allow-Origin\", result.origin);\n appendVaryOrigin(res);\n}\n\n/**\n * Validate the entry module's default export and return it as a\n * {@link ServerLike}.\n */\nfunction serverFrom(moduleExports: Record<string, unknown>): ServerLike {\n const server = moduleExports[\"default\"];\n if (server === null || typeof server !== \"object\") {\n throw new Error(\n \"The server entry must default-export the MCPServer instance \" +\n \"(`export default server`) and never call listen() itself — \" +\n \"`mcp-use dev` owns the socket.\"\n );\n }\n const candidate = server as Partial<ServerLike>;\n if (typeof candidate.getHandler !== \"function\") {\n throw new Error(\n \"The entry's default export has no getHandler() — it must be the \" +\n \"MCPServer instance (`export default server`).\"\n );\n }\n return candidate as ServerLike;\n}\n\n/**\n * Run the dev server: import the entry through Vite's module runner (full\n * TS/alias support), serve `server.getHandler()` on one long-lived HTTP\n * listener, and swap the handler on file change.\n *\n * A throwing re-import keeps the previous handler alive and prints the error\n * — the dev process never crashes on a bad save. `.env` (if present) is\n * loaded from `cwd` via `process.loadEnvFile()` before the entry is first\n * imported.\n *\n * The returned promise resolves after a graceful shutdown (SIGINT/SIGTERM or\n * `options.signal` aborting).\n *\n * @param options - Project root, optional entry override, port and host.\n * @throws If no entry is found, if the initial import fails, if the entry's\n * default export is not an `MCPServer` (see the entry contract in\n * CLI_SPEC.md), or if `vite` is not installed (`mcp-use dev` requires it as a\n * devDependency).\n *\n * @internal Reached only via the bin's dedicated dev command chunk — not\n * re-exported from the package's \".\" entry.\n */\nexport async function runDev(options: DevOptions): Promise<void> {\n const host = options.host ?? \"127.0.0.1\";\n const paths = resolveWorkspacePaths(options.cwd);\n const eventBus = new InMemoryServerEventBus((error) => {\n console.error(\"[mcp-use] notification delivery failed:\", error);\n });\n\n const envPath = join(options.cwd, \".env\");\n if (existsSync(envPath)) {\n process.loadEnvFile(envPath);\n }\n\n // Resolve the listener before importing the entry so module-scope OAuth\n // configuration observes the canonical port that this CLI will own.\n // The HTTP listener is a raw node:http server with the vendored\n // toNodeHandler bridge — same role as the old @hono/node-server\n // getRequestListener, without a hono dependency.\n // Connect-style ((req, res, next)) with no fetch-shaped equivalent, so\n // splicing it in front of the swappable fetch handler needs the raw Node\n // request boundary. Creating the (not-yet-listening) server up front also\n // lets Vite attach its HMR websocket to this same socket (`hmr.server`\n // below) — one port total, so several `mcp-use dev` processes coexist\n // without websocket port collisions.\n const httpServer = createNodeServer();\n\n const { port, requested } = await resolvePort(\n resolvePreferredPort(options.port),\n host\n );\n if (port !== requested) {\n console.log(`[mcp-use] port ${requested} is taken, using ${port}`);\n }\n process.env[\"PORT\"] = String(port);\n\n const localFallbackMcpUrl =\n process.env[\"MCP_URL\"] === undefined &&\n (host === \"127.0.0.1\" || host === \"localhost\" || host === \"::1\")\n ? `http://localhost:${port}`\n : undefined;\n\n const entry = discoverEntry(options.cwd, options.entry);\n if (await ensureMcpEnvDeclaration(options.cwd, entry)) {\n console.log(\"[mcp-use] created mcp-env.d.ts\");\n }\n let currentViews: DiscoveredView[] = discoverViews(options.cwd);\n // The Vite client environment (views plugin, Fast Refresh, HMR socket,\n // asset origin) is configured once, from this snapshot. `currentViews`\n // stays live for request routing and re-priming, but a project that starts\n // with zero views needs a dev-server restart to pick up its first view.\n const viewsAtStartup = currentViews.length > 0;\n const userViteConfig = resolveUserViteConfig(options.cwd);\n\n // The bind address is not always a browsable address: `0.0.0.0`/`::`\n // accept connections on every interface but are not valid request hosts\n // in every browser, and `127.0.0.1` reads worse than `localhost` in logs.\n // Anything else (a LAN IP, a hostname) is browsable as itself; bare IPv6\n // addresses need brackets in URLs.\n const loopbackOrWildcard = [\"127.0.0.1\", \"localhost\", \"0.0.0.0\", \"::\", \"::1\"];\n const browsableHost = loopbackOrWildcard.includes(host) ? \"localhost\" : host;\n const devOrigin = `http://${\n browsableHost.includes(\":\") ? `[${browsableHost}]` : browsableHost\n }:${port}`;\n\n const vite = await createServer({\n root: options.cwd,\n configFile: viewsAtStartup ? userViteConfig : false,\n envFile: false,\n logLevel: \"warn\",\n cacheDir: paths.cache,\n resolve: { alias: { tailwindcss: resolveTailwindCss() } },\n plugins: viewsAtStartup\n ? [\n tailwindcss(),\n mcpUseViewsPlugin({\n getViews: () => currentViews,\n dev: { reactRefresh: true },\n }),\n react(),\n ]\n : [],\n server: {\n middlewareMode: true,\n // Absolute asset URLs in dev: without `origin`, Vite emits root-relative\n // paths that resolve against the host page inside srcdoc iframes.\n ...(viewsAtStartup && { origin: devOrigin }),\n // CORS on module URLs is owned by onRequest below (permissive ACAO\n // only while the tunnel is active), not by Vite's own middleware —\n // whose default localhost-only policy would block tunnel-rendering\n // hosts, and whose headers would fight the tunnel-gated ones.\n cors: false,\n // View HMR rides the one HTTP listener: Vite attaches its websocket\n // upgrade handler to our server, so no dedicated HMR port exists to\n // collide when several dev processes run side by side.\n hmr: viewsAtStartup ? { server: httpServer } : false,\n },\n ssr: {\n external: true,\n },\n });\n\n const ssrEnvironment = vite.environments.ssr;\n const runner = createServerModuleRunner(ssrEnvironment, {\n hmr: false,\n sourcemapInterceptor: \"node\",\n });\n\n const importServer = async (): Promise<ServerLike> => {\n const load = async (): Promise<ServerLike> => {\n const moduleExports = (await runner.import(entry)) as Record<\n string,\n unknown\n >;\n const server = serverFrom(moduleExports);\n\n if (currentViews.length > 0) {\n const viewsManifest = buildDevViewsManifest(currentViews);\n if (typeof server.__primeViews !== \"function\") {\n throw new Error(\n \"Loaded MCPServer instance does not support __primeViews.\"\n );\n }\n server.__primeViews(viewsManifest, {\n dev: true,\n projectRoot: options.cwd,\n });\n }\n\n return server;\n };\n\n if (localFallbackMcpUrl === undefined) {\n return load();\n }\n\n // This is safe only because this CLI selected and will bind this local\n // listener. Never derive OAuth identity from an untrusted request Host.\n // Scope it to entry evaluation: MCPServer freezes the trusted canonical\n // resource during construction, while later runtime code must not inherit\n // this CLI-owned synthetic environment value.\n const previousMcpUrl = process.env[\"MCP_URL\"];\n try {\n process.env[\"MCP_URL\"] = localFallbackMcpUrl;\n return await load();\n } finally {\n if (previousMcpUrl === undefined) {\n delete process.env[\"MCP_URL\"];\n } else {\n process.env[\"MCP_URL\"] = previousMcpUrl;\n }\n }\n };\n\n let currentHandler: FrameworkHandler;\n let basePath: string;\n try {\n const server = await importServer();\n currentHandler = server.getHandler({ bus: eventBus });\n basePath = server.basePath ?? \"/mcp\";\n } catch (error) {\n await runner.close();\n await vite.close();\n throw error;\n }\n\n let reloading = false;\n let dirty = false;\n const reload = (): void => {\n if (reloading) {\n dirty = true;\n return;\n }\n reloading = true;\n void (async () => {\n do {\n dirty = false;\n try {\n runner.evaluatedModules.clear();\n const server = await importServer();\n const nextHandler = server.getHandler({ bus: eventBus });\n currentHandler = nextHandler;\n basePath = server.basePath ?? \"/mcp\";\n eventBus.publish({ kind: \"tools_list_changed\" });\n eventBus.publish({ kind: \"prompts_list_changed\" });\n eventBus.publish({ kind: \"resources_list_changed\" });\n console.log(\"[mcp-use] reloaded server entry\");\n } catch (error) {\n console.error(\n \"[mcp-use] reload failed — keeping the previous server:\\n\",\n error\n );\n }\n } while (dirty);\n reloading = false;\n })();\n };\n\n const onSsrFileEvent = (file: string): void => {\n if (isViewPath(file, options.cwd)) {\n return;\n }\n const modules = ssrEnvironment.moduleGraph.getModulesByFile(file);\n if (modules === undefined || modules.size === 0) {\n return;\n }\n for (const mod of modules) {\n ssrEnvironment.moduleGraph.invalidateModule(mod);\n }\n reload();\n };\n\n const onViewFilesystemEvent = (file: string): void => {\n if (!isViewPath(file, options.cwd)) {\n return;\n }\n\n const previousViews = currentViews;\n currentViews = discoverViews(options.cwd);\n\n const viewsChanged =\n previousViews.length !== currentViews.length ||\n previousViews.some(\n (v, i) =>\n v.name !== currentViews[i]?.name ||\n v.entryPath !== currentViews[i]?.entryPath\n );\n\n if (viewsChanged) {\n reload();\n }\n };\n\n const onFileAddOrUnlink = (file: string): void => {\n onViewFilesystemEvent(file);\n onSsrFileEvent(file);\n };\n\n // A `change` event cannot add or remove a view directory, so only\n // `add`/`unlink` rescan `views/` — content edits never pay for the\n // synchronous filesystem walk in discoverViews().\n vite.watcher.on(\"change\", onSsrFileEvent);\n vite.watcher.on(\"add\", onFileAddOrUnlink);\n vite.watcher.on(\"unlink\", onFileAddOrUnlink);\n\n // --- One long-lived HTTP listener delegating to the current handler. -----\n const tunnelManager = createTunnelManager(paths.tunnel);\n\n // Vite owns the upgrade listener and validates Host before our HTTP request\n // guard runs. The public tunnel has already been validated by the proxy and\n // tunnel manager, so present only that active host as the local listener.\n httpServer.prependListener(\"upgrade\", (req) => {\n const tunnelUrl = tunnelManager.status().url;\n if (tunnelUrl === null) return;\n const tunnelHost = new URL(tunnelUrl).hostname;\n const requestHost = req.headers.host?.split(\":\")[0];\n if (requestHost === tunnelHost) {\n req.headers.host = `localhost:${port}`;\n }\n });\n\n // --- DNS-rebinding protection. --------------------------------------------\n // getHandler() applies no Host/Origin validation (its contract assumes a\n // platform edge in front); in dev this process *is* the edge, so\n // localhost-class binds get the same localhost-allowlist checks listen()\n // applies — extended with the active tunnel hostname, since tunnel traffic\n // arrives with the tunnel's public Host. The check runs before any routing,\n // covering the MCP endpoint, the dev API (tunnel control), and Vite-served\n // module URLs alike. Non-localhost binds get no validation (the legitimate\n // hostnames are unknowable here) — a startup warning below says so.\n //\n // Host is validated on every request: rebinding works by making the\n // attacker's page same-origin with this server, and the Host header is\n // where that shows up. Origin is not validated here (SDK-aligned default);\n // set `allowedOrigins` on the MCPServer to opt in — the handler middleware\n // enforces it. Sandboxed view iframes have an opaque origin, so their\n // module/asset GETs legitimately carry `Origin: null` — and external hosts\n // rendering views through the tunnel fetch assets with their own origins.\n // Those cross-origin loads also need CORS: the MCP server's view asset/public\n // routes always emit `Access-Control-Allow-Origin: *`. Vite-served module\n // URLs (onRequest below) emit `*` while a tunnel is active; without a tunnel,\n // localhost binds reflect a validated loopback Origin (exact value +\n // `Vary: Origin`) so a local MCP host can load the module graph, while\n // foreign / opaque / missing Origin get no ACAO.\n const localhostBind = [\"127.0.0.1\", \"localhost\", \"::1\"].includes(host);\n const rejectDisallowedRequest = (\n req: IncomingMessage,\n res: ServerResponse\n ): boolean => {\n const tunnelUrl = tunnelManager.status().url;\n const tunnelHost = tunnelUrl !== null ? new URL(tunnelUrl).hostname : null;\n const extra = tunnelHost !== null ? [tunnelHost] : [];\n const result = validateHostHeader(req.headers.host, [\n ...localhostAllowedHostnames(),\n ...extra,\n ]);\n if (result.ok) {\n return false;\n }\n // Same JSON-RPC 403 shape the SDK's own validation responses use.\n res.writeHead(403, { \"content-type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code: -32000, message: result.message },\n id: null,\n })\n );\n return true;\n };\n\n /**\n * Tear down everything the running dev process owns, in dependency order:\n * watcher subscriptions, tunnel, HTTP listener, module runner, Vite.\n */\n const teardown = async (): Promise<void> => {\n vite.watcher.off(\"change\", onSsrFileEvent);\n vite.watcher.off(\"add\", onFileAddOrUnlink);\n vite.watcher.off(\"unlink\", onFileAddOrUnlink);\n await tunnelManager.stop();\n await new Promise<void>((done) => httpServer.close(() => done()));\n await runner.close();\n await vite.close();\n };\n\n const devFetch = createDevApiHandler(\n {\n getBasePath: () => basePath,\n port,\n tunnel: tunnelManager,\n },\n (request) => currentHandler(request)\n );\n\n // Vendored Node bridge — same adapter serve() used internally.\n const { toNodeHandler } = await import(\"../node-bridge.js\");\n const nodeListener = toNodeHandler({ fetch: devFetch });\n\n const onRequest = (req: IncomingMessage, res: ServerResponse): void => {\n if (localhostBind && rejectDisallowedRequest(req, res)) {\n return;\n }\n const url = req.url ?? \"/\";\n const pathname = new URL(url, \"http://127.0.0.1\").pathname;\n // Routing to Vite requires both the client environment (configured only\n // when views existed at startup) and a currently non-empty registry.\n const viewsEnabled = viewsAtStartup && currentViews.length > 0;\n // Vite sees module-graph URLs (/@vite/client, /@id/virtual:…,\n // /.mcp-use/cache/deps/…, view files under /views/…) plus standard\n // node_modules pre-bundles; everything else — the MCP endpoint included —\n // goes straight to the fetch handler.\n const isViteRequest =\n req.method === \"GET\" &&\n (pathname.startsWith(\"/@\") ||\n pathname.startsWith(\"/node_modules/\") ||\n pathname.startsWith(\"/.mcp-use/\") ||\n (viewsEnabled && pathname.startsWith(\"/views/\")));\n\n if (viewsEnabled && isViteRequest) {\n // CORS for Vite module URLs: tunnel → `*`; else localhost bind with a\n // validated loopback Origin → reflect that origin (+ Vary). Foreign /\n // opaque / missing Origin stay without ACAO so the source module graph\n // is not readable to arbitrary websites.\n applyViteModuleCors(req, res, {\n tunnelActive: tunnelManager.status().url !== null,\n localhostBind,\n });\n vite.middlewares(req, res, () => {\n void nodeListener(req, res);\n });\n } else {\n void nodeListener(req, res);\n }\n };\n httpServer.on(\"request\", onRequest);\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once(\"error\", reject);\n httpServer.listen(port, host, () => resolve());\n });\n\n console.log(`[mcp-use] dev server ready`);\n if (currentViews.length > 0) {\n console.log(\n ` ➜ Views: ${currentViews.map((v) => v.name).join(\", \")}`\n );\n }\n console.log(` ➜ MCP endpoint: ${devOrigin}${basePath}`);\n console.log(` ➜ Inspector: ${devOrigin}${basePath}/inspector`);\n if (host === \"0.0.0.0\" || host === \"::\") {\n const lan = lanAddress();\n if (lan !== undefined) {\n console.log(` ➜ Network: http://${lan}:${port}${basePath}`);\n }\n }\n if (!localhostBind) {\n console.warn(\n `[mcp-use] --host ${host} serves beyond this machine without Host ` +\n `validation or authentication — anyone with network access can call ` +\n `the MCP endpoint and dev routes.`\n );\n }\n\n if (options.tunnel === true) {\n try {\n const { url } = await tunnelManager.start(port);\n console.log(` ➜ Tunnel: ${url}${basePath}`);\n } catch (error) {\n await teardown();\n throw error;\n }\n }\n\n // Auto-open the inspector — unless disabled (`--no-open`) or stdout is not\n // a TTY (agents/CI: no browser to open, and no error to fail on).\n if (options.open !== false && process.stdout.isTTY === true) {\n openInBrowser(`${devOrigin}${basePath}/inspector`);\n }\n\n // --- Graceful shutdown (SIGINT/SIGTERM or options.signal). ---------------\n await new Promise<void>((resolve) => {\n let closing = false;\n const shutdown = (): void => {\n if (closing) return;\n closing = true;\n process.off(\"SIGINT\", shutdown);\n process.off(\"SIGTERM\", shutdown);\n void (async () => {\n await teardown();\n resolve();\n })();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n if (options.signal !== undefined) {\n if (options.signal.aborted) {\n shutdown();\n } else {\n options.signal.addEventListener(\"abort\", shutdown, { once: true });\n }\n }\n });\n}\n","/**\n * Port resolution for `mcp-use dev`: `--port`, else `PORT` env, else 3000 — probing\n * upward when the requested port is taken.\n */\n\nimport { createServer } from \"node:net\";\n\n/** How many consecutive ports to probe before giving up. */\nconst MAX_PROBES = 100;\n\n/**\n * Outcome of {@link resolvePort}.\n *\n * @internal\n */\nexport interface ResolvedPort {\n /** The free port to bind. */\n port: number;\n /** The port that was originally requested (differs when probing moved on). */\n requested: number;\n}\n\n/** Check whether `port` is free to bind on `host`. */\nfunction isPortFree(port: number, host: string): Promise<boolean> {\n return new Promise((resolve) => {\n const probe = createServer();\n probe.unref();\n probe.once(\"error\", () => resolve(false));\n probe.listen({ port, host }, () => {\n probe.close(() => resolve(true));\n });\n });\n}\n\n/**\n * Resolve the port to bind: take `requested` (already reduced from\n * `--port` / `PORT` / the default by the caller) and probe upward until a\n * free port is found.\n *\n * @param requested - The preferred port.\n * @param host - The host the server will bind (probing binds the same host).\n * @returns The first free port at or above `requested`, plus the original\n * request so callers can log the substitution.\n * @throws If no free port is found within {@link MAX_PROBES} attempts.\n *\n * @internal\n */\nexport async function resolvePort(\n requested: number,\n host: string\n): Promise<ResolvedPort> {\n for (let port = requested; port < requested + MAX_PROBES; port++) {\n if (await isPortFree(port, host)) {\n return { port, requested };\n }\n }\n throw new Error(\n `No free port found between ${requested} and ${requested + MAX_PROBES - 1} on ${host}.`\n );\n}\n","/**\n * Dev-only inspector API routes intercepted by `mcp-use dev` before the MCP\n * handler — tunnel controls and session metadata for the inspector UI.\n */\n\nimport type { TunnelManager } from \"./tunnel.js\";\n\n/** Web-standard request handler, as returned by `MCPServer.getHandler()`. */\ntype FetchHandler = (request: Request) => Promise<Response>;\n\n/**\n * Dependencies for {@link createDevApiHandler}.\n *\n * @internal\n */\nexport interface DevApiHandlerOptions {\n /** Returns the current MCP mount path (may change after a dev reload). */\n getBasePath: () => string;\n /** Port the dev HTTP listener is bound to. */\n port: number;\n /** Tunnel lifecycle manager owned by the dev process. */\n tunnel: TunnelManager;\n}\n\n/** JSON body for `GET …/inspector/api/dev/info`. */\nexport interface DevInfoResponse {\n /** Full MCP endpoint URL when a tunnel is active; otherwise `null`. */\n mcpUrl: string | null;\n /** Port the dev server listens on. */\n port: number;\n /** Whether the request is served by `mcp-use dev` (always `true` here). */\n fromCli: true;\n /** Public tunnel origin URL, or `null` when no tunnel is running. */\n tunnelUrl: string | null;\n}\n\n/**\n * Wrap an MCP {@link FetchHandler} so dev-only inspector API routes are\n * answered in-process before delegating everything else.\n *\n * @param options - Base path, bound port, and tunnel manager.\n * @param fallback - Handler to invoke when the request is not a dev API route.\n *\n * @example\n * ```ts\n * const handler = createDevApiHandler(\n * { getBasePath: () => \"/mcp\", port: 3000, tunnel },\n * server.getHandler()\n * );\n * ```\n */\nexport function createDevApiHandler(\n options: DevApiHandlerOptions,\n fallback: FetchHandler\n): FetchHandler {\n const devInfo = (): DevInfoResponse => {\n const basePath = options.getBasePath();\n const { url: tunnelUrl } = options.tunnel.status();\n return {\n mcpUrl:\n tunnelUrl !== null\n ? `${tunnelUrl.replace(/\\/+$/, \"\")}${basePath}`\n : null,\n port: options.port,\n fromCli: true,\n tunnelUrl,\n };\n };\n\n return async (request: Request): Promise<Response> => {\n const basePath = options.getBasePath();\n const infoPath = `${basePath}/inspector/api/dev/info`;\n const startPath = `${basePath}/inspector/api/dev/start-tunnel`;\n const stopPath = `${basePath}/inspector/api/dev/stop-tunnel`;\n const pathname = new URL(request.url).pathname;\n\n if (request.method === \"GET\" && pathname === infoPath) {\n return Response.json(devInfo());\n }\n\n if (request.method === \"POST\" && pathname === startPath) {\n try {\n await options.tunnel.start(options.port);\n return Response.json({ ok: true, restarting: false });\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Failed to start tunnel\";\n return Response.json({ error: message }, { status: 500 });\n }\n }\n\n if (request.method === \"POST\" && pathname === stopPath) {\n await options.tunnel.stop();\n return Response.json({ ok: true });\n }\n\n return fallback(request);\n };\n}\n","/**\n * Tunnel lifecycle for `mcp-use dev` — spawns `@mcp-use/tunnel`,\n * parses the public URL from stdout, persists the subdomain under\n * `.mcp-use/state/tunnel.json`, and releases it through the tunnel API before\n * reuse.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { dirname } from \"node:path\";\n\n/** Default base URL for the tunnel reservation/release API. */\nconst DEFAULT_TUNNEL_API = \"https://local.mcp-use.run\";\n\n/**\n * Base URL of the tunnel API (subdomain reservation/release). Overridable via\n * the `MCP_USE_TUNNEL_API` environment variable.\n */\nexport function tunnelApiBase(): string {\n return process.env[\"MCP_USE_TUNNEL_API\"] ?? DEFAULT_TUNNEL_API;\n}\n\n/** On-disk shape of `.mcp-use/state/tunnel.json`. */\ninterface TunnelStateFile {\n /** Last successfully assigned tunnel subdomain. */\n subdomain: string;\n}\n\n/**\n * Minimal tunnel manager surface used by {@link createDevApiHandler} and\n * {@link runDev}.\n */\nexport interface TunnelManager {\n /**\n * Start (or attach) a tunnel targeting `port` and return the public origin\n * URL and assigned subdomain.\n */\n start(port: number): Promise<{ url: string; subdomain: string }>;\n /** Stop the tunnel child process and release the subdomain. */\n stop(): Promise<void>;\n /** Current tunnel public origin URL, or `null` when no tunnel is active. */\n status(): { url: string | null };\n}\n\nconst RESPAWN_BACKOFF_INITIAL_MS = 1_000;\nconst RESPAWN_BACKOFF_MAX_MS = 30_000;\n\n/**\n * Create a tunnel manager that reads and writes subdomain state at\n * `stateFilePath` (typically `.mcp-use/state/tunnel.json`).\n *\n * @param stateFilePath - Absolute path to the tunnel state JSON file.\n *\n * @example\n * ```ts\n * const tunnel = createTunnelManager(paths.tunnel);\n * const { url } = await tunnel.start(3000);\n * console.log(url); // https://happy-cat.local.mcp-use.run\n * ```\n */\nexport function createTunnelManager(stateFilePath: string): TunnelManager {\n let proc: ChildProcess | undefined;\n let currentUrl: string | null = null;\n let currentSubdomain: string | undefined;\n let activePort: number | undefined;\n let intentionalStop = false;\n let respawnInFlight: Promise<void> | undefined;\n let respawnBackoffMs = RESPAWN_BACKOFF_INITIAL_MS;\n\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n const markShutdown = (): void => {\n if (\n proc !== undefined &&\n \"markShutdown\" in proc &&\n typeof (proc as { markShutdown?: () => void }).markShutdown === \"function\"\n ) {\n (proc as { markShutdown: () => void }).markShutdown();\n }\n };\n\n const releaseSubdomain = async (subdomain: string): Promise<void> => {\n try {\n await fetch(`${tunnelApiBase()}/api/tunnels/${subdomain}`, {\n method: \"DELETE\",\n signal: AbortSignal.timeout(2_000),\n });\n } catch {\n // Best-effort cleanup; ignore DELETE failures.\n }\n };\n\n const loadSavedSubdomain = async (): Promise<string | undefined> => {\n try {\n const content = await readFile(stateFilePath, \"utf8\");\n const state = JSON.parse(content) as Partial<TunnelStateFile>;\n return typeof state.subdomain === \"string\" ? state.subdomain : undefined;\n } catch {\n return undefined;\n }\n };\n\n const persistSubdomain = async (subdomain: string): Promise<void> => {\n try {\n await mkdir(dirname(stateFilePath), { recursive: true });\n await writeFile(\n stateFilePath,\n JSON.stringify({ subdomain } satisfies TunnelStateFile, null, 2),\n \"utf8\"\n );\n } catch (error) {\n console.warn(\n `[mcp-use] failed to save tunnel subdomain: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n };\n\n let scheduleRespawn: () => void = () => {};\n\n const spawnTunnel = (\n port: number,\n subdomain?: string\n ): Promise<{ url: string; subdomain: string; process: ChildProcess }> =>\n new Promise((resolve, reject) => {\n console.log(`[mcp-use] starting tunnel for port ${port}…`);\n\n const userAgent = process.env[\"npm_config_user_agent\"] ?? \"\";\n const [tunnelCommand, tunnelArgs] = userAgent.startsWith(\"pnpm/\")\n ? [\"pnpm\", [\"--silent\", \"dlx\", \"@mcp-use/tunnel\", String(port)]]\n : userAgent.startsWith(\"bun/\")\n ? [\"bunx\", [\"@mcp-use/tunnel\", String(port)]]\n : [\n \"npx\",\n [\"--yes\", \"--prefer-offline\", \"@mcp-use/tunnel\", String(port)],\n ];\n if (subdomain !== undefined) {\n tunnelArgs.push(\"--subdomain\", subdomain);\n }\n\n const child = spawn(tunnelCommand, tunnelArgs, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: process.platform === \"win32\",\n });\n\n let resolved = false;\n let shuttingDown = false;\n\n const markChildShutdown = (): void => {\n shuttingDown = true;\n };\n (child as unknown as { markShutdown: () => void }).markShutdown =\n markChildShutdown;\n\n child.stdout.on(\"data\", (data: Buffer) => {\n const text = data.toString();\n const isShutdownMessage =\n text.includes(\"Shutting down\") || text.includes(\"🛑\");\n const isErrorMessage = text.includes(\"✖\") || text.includes(\"Error:\");\n\n if (!shuttingDown && !isShutdownMessage && !isErrorMessage) {\n process.stdout.write(text);\n }\n\n const urlMatch = text.match(/https?:\\/\\/([a-z0-9-]+\\.[a-z0-9.-]+)/i);\n if (urlMatch !== null && !resolved) {\n const url = urlMatch[0];\n const fullDomain = urlMatch[1] ?? \"\";\n const subdomainMatch = fullDomain.match(/^([a-z0-9-]+)\\./i);\n let extractedSubdomain =\n subdomainMatch?.[1] ?? fullDomain.split(\".\")[0] ?? \"\";\n if (!/^[a-z0-9-]+$/i.test(extractedSubdomain)) {\n console.warn(\n `[mcp-use] warning: extracted subdomain \"${extractedSubdomain}\" does not match expected format`\n );\n extractedSubdomain = \"\";\n }\n resolved = true;\n clearTimeout(setupTimeout);\n // No log here — the tunnel child's own stdout (passed through\n // above) already announces the public URL.\n resolve({ url, subdomain: extractedSubdomain, process: child });\n }\n });\n\n child.stderr.on(\"data\", (data: Buffer) => {\n const text = data.toString();\n if (\n !shuttingDown &&\n !text.includes(\"INFO\") &&\n !text.includes(\"bore_cli\") &&\n !text.includes(\"Shutting down\") &&\n // npx inherits pnpm-only npm_config_* env vars (e.g.\n // minimum-release-age, verify-deps-before-run) and warns about\n // every key it doesn't recognize — pure noise for the user.\n !/npm warn Unknown (?:env|project|user|builtin) config/.test(text)\n ) {\n process.stderr.write(data);\n }\n });\n\n child.on(\"error\", (error) => {\n if (!resolved) {\n clearTimeout(setupTimeout);\n reject(new Error(`Failed to start tunnel: ${error.message}`));\n }\n });\n\n child.on(\"exit\", (code) => {\n if (code !== 0 && code !== null && !resolved) {\n clearTimeout(setupTimeout);\n reject(new Error(`Tunnel process exited with code ${code}`));\n }\n // The tunnel CLI can exit after setup when its bore connection or\n // server-side registration disappears. Do not keep reporting a dead\n // public URL through dev/info; respawn while dev is still running.\n if (proc === child) {\n proc = undefined;\n currentUrl = null;\n if (intentionalStop) {\n currentSubdomain = undefined;\n } else {\n scheduleRespawn();\n }\n }\n });\n\n const setupTimeout = setTimeout(() => {\n if (!resolved) {\n child.kill();\n reject(new Error(\"Tunnel setup timed out\"));\n }\n }, 30_000);\n });\n\n scheduleRespawn = (): void => {\n if (intentionalStop || activePort === undefined || currentUrl !== null) {\n return;\n }\n if (respawnInFlight !== undefined) {\n return;\n }\n\n respawnInFlight = (async (): Promise<void> => {\n while (\n !intentionalStop &&\n activePort !== undefined &&\n currentUrl === null\n ) {\n console.log(\"[mcp-use] tunnel disconnected, restarting…\");\n const subdomain = currentSubdomain ?? (await loadSavedSubdomain());\n try {\n if (subdomain !== undefined) {\n await releaseSubdomain(subdomain);\n }\n\n let tunnelInfo: Awaited<ReturnType<typeof spawnTunnel>>;\n try {\n tunnelInfo = await spawnTunnel(activePort, subdomain);\n } catch (error) {\n if (subdomain !== undefined) {\n console.log(\n `[mcp-use] subdomain \"${subdomain}\" unavailable, requesting a new one…`\n );\n tunnelInfo = await spawnTunnel(activePort, undefined);\n } else {\n throw error;\n }\n }\n\n proc = tunnelInfo.process;\n currentUrl = tunnelInfo.url;\n currentSubdomain = tunnelInfo.subdomain;\n await persistSubdomain(tunnelInfo.subdomain);\n respawnBackoffMs = RESPAWN_BACKOFF_INITIAL_MS;\n return;\n } catch (error) {\n console.warn(\n `[mcp-use] tunnel restart failed: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n await sleep(respawnBackoffMs);\n respawnBackoffMs = Math.min(\n respawnBackoffMs * 2,\n RESPAWN_BACKOFF_MAX_MS\n );\n }\n }\n })().finally(() => {\n respawnInFlight = undefined;\n });\n };\n\n return {\n status(): { url: string | null } {\n return { url: currentUrl };\n },\n\n async start(port: number): Promise<{ url: string; subdomain: string }> {\n intentionalStop = false;\n activePort = port;\n\n if (currentUrl !== null) {\n return {\n url: currentUrl,\n subdomain: currentSubdomain ?? \"\",\n };\n }\n\n const existingSubdomain = await loadSavedSubdomain();\n if (existingSubdomain !== undefined) {\n console.log(`[mcp-use] found existing subdomain: ${existingSubdomain}`);\n await releaseSubdomain(existingSubdomain);\n }\n\n let tunnelInfo: Awaited<ReturnType<typeof spawnTunnel>>;\n try {\n tunnelInfo = await spawnTunnel(port, existingSubdomain);\n } catch (error) {\n if (existingSubdomain !== undefined) {\n console.log(\n `[mcp-use] subdomain \"${existingSubdomain}\" unavailable, requesting a new one…`\n );\n tunnelInfo = await spawnTunnel(port, undefined);\n } else {\n throw error;\n }\n }\n\n proc = tunnelInfo.process;\n currentUrl = tunnelInfo.url;\n currentSubdomain = tunnelInfo.subdomain;\n await persistSubdomain(tunnelInfo.subdomain);\n\n return { url: tunnelInfo.url, subdomain: tunnelInfo.subdomain };\n },\n\n async stop(): Promise<void> {\n intentionalStop = true;\n markShutdown();\n\n if (respawnInFlight !== undefined) {\n await respawnInFlight;\n }\n\n if (currentSubdomain !== undefined) {\n await releaseSubdomain(currentSubdomain);\n }\n\n if (proc !== undefined) {\n proc.kill(\"SIGINT\");\n proc = undefined;\n }\n\n activePort = undefined;\n currentUrl = null;\n currentSubdomain = undefined;\n respawnBackoffMs = RESPAWN_BACKOFF_INITIAL_MS;\n },\n };\n}\n"],"mappings":"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","names":["spawn","createServer","resolvePort","spawn","resolvePort","createServer"]}
package/dist/config.d.ts CHANGED
@@ -52,9 +52,9 @@ interface BaseServerConfig {
52
52
  *
53
53
  * Accepts a safe path relative to the project `public/` directory, an
54
54
  * absolute HTTP(S) URL, or an image data URL. The selected image is served
55
- * at the root-level `/favicon.ico` route. When omitted, the `icons` field
56
- * selects the favicon using ICO, small PNG, any PNG, then first-icon order.
57
- * An explicit value always wins; an empty string is invalid.
55
+ * at the root-level `/favicon.ico` route. When omitted, the first entry in
56
+ * `icons` selects the favicon. An explicit value always wins; an empty
57
+ * string is invalid.
58
58
  *
59
59
  * @defaultValue Inferred from `icons`, or no favicon when `icons` is absent
60
60
  * or empty.
@@ -78,8 +78,8 @@ interface BaseServerConfig {
78
78
  * relative to `public/`, an absolute HTTP(S) URL, or an image data URL.
79
79
  * Local paths become request-scoped absolute URLs under
80
80
  * `${basePath}/_mcp-use/public/`; author order is preserved on the wire.
81
- * When `favicon` is omitted, these icons also select `/favicon.ico`.
82
- * An empty array is valid and selects no favicon.
81
+ * When `favicon` is omitted, the first icon in this array also selects
82
+ * `/favicon.ico`. An empty array is valid and selects no favicon.
83
83
  *
84
84
  * @defaultValue `undefined`
85
85
  * @throws TypeError When an icon has an invalid source, MIME type, sizes,
@@ -149,9 +149,9 @@ interface BaseServerConfig {
149
149
  /**
150
150
  * Extra allowed origin hostnames for Origin-header validation
151
151
  * (port-agnostic, additive to the localhost-class origins). When unset,
152
- * mirrors the effective Host allowlist. Origin is validated only on
153
- * non-GET/HEAD requests (sandboxed view iframes send `Origin: null` on
154
- * asset GETs; the MCP wire is POST). Requests without an `Origin` header
152
+ * Origin validation is off (SDK-aligned). When set, Origin is validated
153
+ * only on non-GET/HEAD requests (sandboxed view iframes send `Origin: null`
154
+ * on asset GETs; the MCP wire is POST). Requests without an `Origin` header
155
155
  * always pass (non-browser MCP clients don't send one).
156
156
  */
157
157
  allowedOrigins?: string[];