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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/branding.d.ts +7 -10
- package/dist/branding.d.ts.map +1 -1
- package/dist/{chunk-GTOOHAI2.js → chunk-JEVBOMCE.js} +4 -3
- package/dist/chunk-JEVBOMCE.js.map +1 -0
- package/dist/{chunk-DQ7LE4ZA.js → chunk-XKJP5S7C.js} +17 -14
- package/dist/chunk-XKJP5S7C.js.map +1 -0
- package/dist/cli/build.d.ts +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/mcp-env-declaration.d.ts +20 -0
- package/dist/cli/mcp-env-declaration.d.ts.map +1 -0
- package/dist/cli/tunnel.d.ts.map +1 -1
- package/dist/cli/views.d.ts +7 -5
- package/dist/cli/views.d.ts.map +1 -1
- package/dist/commands/build.js +4 -4
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.js +79 -12
- package/dist/commands/dev.js.map +1 -1
- package/dist/config.d.ts +8 -8
- package/dist/index.js +32 -51
- package/dist/index.js.map +1 -1
- package/dist/inspector-shell.d.ts +3 -0
- package/dist/inspector-shell.d.ts.map +1 -1
- package/dist/{landing-handler-3VN4BMR2.js → landing-handler-Z7WDR4S2.js} +6 -3
- package/dist/landing-handler-Z7WDR4S2.js.map +1 -0
- package/dist/landing-handler.d.ts +1 -1
- package/dist/landing-handler.d.ts.map +1 -1
- package/dist/landing.d.ts +2 -0
- package/dist/landing.d.ts.map +1 -1
- package/dist/landing.js +1 -1
- package/dist/react/types/register.d.ts +1 -1
- package/dist/react/types/register.d.ts.map +1 -1
- package/dist/server.d.ts +2 -3
- package/dist/server.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-DQ7LE4ZA.js.map +0 -1
- package/dist/chunk-GTOOHAI2.js.map +0 -1
- package/dist/cli/tools-declaration.d.ts +0 -22
- package/dist/cli/tools-declaration.d.ts.map +0 -1
- package/dist/landing-handler-3VN4BMR2.js.map +0 -1
package/dist/commands/dev.js
CHANGED
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
buildDevViewsManifest,
|
|
6
6
|
discoverEntry,
|
|
7
7
|
discoverViews,
|
|
8
|
-
|
|
8
|
+
ensureMcpEnvDeclaration,
|
|
9
9
|
isViewPath,
|
|
10
10
|
mcpUseViewsPlugin,
|
|
11
11
|
resolveTailwindCss,
|
|
12
12
|
resolveUserViteConfig,
|
|
13
13
|
resolveWorkspacePaths
|
|
14
|
-
} from "../chunk-
|
|
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
|
-
|
|
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
|
|
363
|
-
console.log("[mcp-use] created
|
|
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
|
|
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("/
|
|
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,
|
package/dist/commands/dev.js.map
CHANGED
|
@@ -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
|
|
56
|
-
* selects the favicon
|
|
57
|
-
*
|
|
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,
|
|
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
|
-
*
|
|
153
|
-
* non-GET/HEAD requests (sandboxed view iframes send `Origin: null`
|
|
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[];
|