@cyanmycelium/mcp-broker 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/bin.js +40 -5
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-WEUCJH6C.js → chunk-PMEOLA5L.js} +9 -2
- package/dist/chunk-PMEOLA5L.js.map +1 -0
- package/dist/{chunk-CR5GZBFT.js → chunk-TPRPPTWK.js} +128 -4
- package/dist/chunk-TPRPPTWK.js.map +1 -0
- package/dist/index.d.ts +44 -3
- package/dist/index.js +2 -2
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/{ws.tunnel.builder-DbKxtyki.d.ts → ws.tunnel.builder-C-1V0uPc.d.ts} +2 -1
- package/package.json +1 -1
- package/src/bin.ts +39 -3
- package/src/broker/broker.guides.ts +6 -0
- package/src/config.ts +6 -0
- package/src/index.ts +2 -0
- package/src/plugins.ts +176 -0
- package/src/ws/ws.tunnel.ts +6 -1
- package/dist/chunk-CR5GZBFT.js.map +0 -1
- package/dist/chunk-WEUCJH6C.js.map +0 -1
package/README.md
CHANGED
|
@@ -726,13 +726,17 @@ tunnel is built, so a wrong path fails before the port is bound.
|
|
|
726
726
|
|
|
727
727
|
The generated certificate covers `localhost`, `127.0.0.1`, `::1` for 365 days. Browsers will warn about an untrusted issuer on first visit. Click "Advanced → Proceed". MCP clients (Claude, Inspector) ignore certificate validation by default.
|
|
728
728
|
|
|
729
|
+
## Deployment plugins
|
|
730
|
+
|
|
731
|
+
Load local plugins through `plugins[]` without adding their packages to the broker dependencies. Deployments provide a separate plugin manifest and lockfile. See [the plugin API, configuration and Docker composition](docs/plugins.md).
|
|
732
|
+
|
|
729
733
|
## Docker
|
|
730
734
|
|
|
731
735
|
A multi-stage `Dockerfile` ships with the package: build stage compiles TypeScript and copies grammar JSON, runtime stage carries only the compiled `dist/`, production `node_modules`, and runs as a non-root user.
|
|
732
736
|
|
|
733
737
|
```sh
|
|
734
738
|
# From the node/ directory
|
|
735
|
-
docker build -t cyanmycelium/mcp-broker:0.1.0 .
|
|
739
|
+
docker build -f packages/broker/Dockerfile -t cyanmycelium/mcp-broker:0.1.0 .
|
|
736
740
|
|
|
737
741
|
# Run on host port 3000
|
|
738
742
|
docker run --rm -p 3000:3000 cyanmycelium/mcp-broker:0.1.0
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { loadBrokerConfig, loadSecurityConfig, BrokerConfigError, loadMcpbBundle, resolveOpenTarget } from './chunk-
|
|
3
|
-
import { PACKAGE_NAME, VERSION, compileRegex, WsTunnelBuilder, BROKER_AGGREGATE_NAME, BROKER_PROVIDER_NAME } from './chunk-
|
|
2
|
+
import { loadBrokerConfig, loadSecurityConfig, BrokerConfigError, loadMcpbBundle, startBrokerPlugins, resolveOpenTarget } from './chunk-TPRPPTWK.js';
|
|
3
|
+
import { PACKAGE_NAME, VERSION, compileRegex, WsTunnelBuilder, BROKER_AGGREGATE_NAME, BROKER_PROVIDER_NAME } from './chunk-PMEOLA5L.js';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import open from 'open';
|
|
@@ -41,6 +41,7 @@ COMMON ENVIRONMENT VARIABLES
|
|
|
41
41
|
MCP_BROKER_LOCALE Locale for _broker tool descriptions (default en)
|
|
42
42
|
MCP_BROKER_ALLOWED_ORIGINS Comma-separated browser origins allowed on /<slot>/mcp,
|
|
43
43
|
/<slot>/sse and /<slot>/messages (default: none pass)
|
|
44
|
+
MCP_BROKER_PLUGINS_DIR Directory of installed plugin packages
|
|
44
45
|
MCP_BROKER_WWW_DIR Serve this directory at "/"
|
|
45
46
|
MCP_BROKER_BUNDLE_DIR Serve this directory at "/bundle"
|
|
46
47
|
MCP_BROKER_OPEN "1" to open the root in a browser, or a path such as /app/
|
|
@@ -468,6 +469,29 @@ async function main() {
|
|
|
468
469
|
process.exit(1);
|
|
469
470
|
}
|
|
470
471
|
await tunnel.start();
|
|
472
|
+
let plugins;
|
|
473
|
+
try {
|
|
474
|
+
const pluginsDirectory = process.env["MCP_BROKER_PLUGINS_DIR"] ?? config.pluginsDirectory;
|
|
475
|
+
if (pluginsDirectory !== void 0 && typeof pluginsDirectory !== "string") throw new Error("pluginsDirectory must be a string");
|
|
476
|
+
plugins = await startBrokerPlugins(config.plugins === void 0 ? [] : config.plugins, {
|
|
477
|
+
tunnel,
|
|
478
|
+
baseDir,
|
|
479
|
+
moduleDirectory: pluginsDirectory ? path.resolve(baseDir, pluginsDirectory) : baseDir,
|
|
480
|
+
principals: new Map(
|
|
481
|
+
(security?.credentials ?? []).map((credential) => [
|
|
482
|
+
credential.id,
|
|
483
|
+
{
|
|
484
|
+
id: credential.id,
|
|
485
|
+
subjects: credential.subjects,
|
|
486
|
+
allowedResources: credential.allowedResources
|
|
487
|
+
}
|
|
488
|
+
])
|
|
489
|
+
)
|
|
490
|
+
});
|
|
491
|
+
} catch (error) {
|
|
492
|
+
await tunnel.stop();
|
|
493
|
+
throw error;
|
|
494
|
+
}
|
|
471
495
|
const httpScheme = useTls ? "https" : "http";
|
|
472
496
|
const wsScheme = useTls ? "wss" : "ws";
|
|
473
497
|
const hr = "\u2500".repeat(64);
|
|
@@ -536,14 +560,25 @@ async function main() {
|
|
|
536
560
|
}
|
|
537
561
|
}
|
|
538
562
|
}
|
|
563
|
+
let shuttingDown = false;
|
|
539
564
|
const shutdown = async (signal) => {
|
|
565
|
+
if (shuttingDown) return;
|
|
566
|
+
shuttingDown = true;
|
|
540
567
|
console.log(`
|
|
541
568
|
\u26D4 ${signal} received, shutting down\u2026`);
|
|
542
|
-
|
|
569
|
+
try {
|
|
570
|
+
await plugins.stop();
|
|
571
|
+
} finally {
|
|
572
|
+
await tunnel.stop();
|
|
573
|
+
}
|
|
543
574
|
process.exit(0);
|
|
544
575
|
};
|
|
545
|
-
|
|
546
|
-
|
|
576
|
+
const shutdownFailed = (error) => {
|
|
577
|
+
console.error(`[mcp-broker] shutdown failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
578
|
+
process.exit(1);
|
|
579
|
+
};
|
|
580
|
+
process.on("SIGINT", () => void shutdown("SIGINT").catch(shutdownFailed));
|
|
581
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM").catch(shutdownFailed));
|
|
547
582
|
}
|
|
548
583
|
main().catch((err) => {
|
|
549
584
|
const message = err instanceof Error ? err.message : String(err);
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bin.ts"],"names":[],"mappings":"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","file":"bin.js","sourcesContent":["#!/usr/bin/env node\n/**\n * Standalone entry-point that starts the mcp-broker WebSocket server.\n *\n * ## Configuration sources (highest priority first)\n *\n * 1. Environment variables (`MCP_BROKER_*`).\n * 2. JSON config file. Resolved in order:\n * a. `MCP_BROKER_CONFIG` env var.\n * b. `./.mcp-broker/config.json` in the current working directory.\n * c. `./mcp-broker.config.json` (legacy, with a deprecation warning).\n * 3. Built-in defaults.\n *\n * ## Path resolution\n *\n * - Env-var paths (`MCP_BROKER_*_DIR`, `MCP_BROKER_TLS_*`) are resolved\n * against `process.cwd()`.\n * - Config-file paths (`tls.cert`, `www.mounts[*].dir`, `stdioUpstreams[*]`,\n * `mcpbBundles[*]`) are resolved against the **config file's directory**: so a config in\n * `./.mcp-broker/config.json` referring to `\"certs/cert.pem\"` points at\n * `./.mcp-broker/certs/cert.pem`. The folder is self-contained.\n *\n * ## Local grammar overrides\n *\n * When `.mcp-broker/grammars/<userAgent>/<locale>.json` files exist next to\n * the config file, they are merged **on top of** the packaged grammars. Lets\n * users customize tool/resource descriptions for their organization without\n * forking the package.\n *\n * ## Environment variables\n *\n * | Variable | Default | Notes |\n * |--------------------------------|----------|--------------------------------------------------------|\n * | MCP_BROKER_CONFIG | (none) | Path to a JSON config file (see above). |\n * | MCP_BROKER_PORT | 3000 | |\n * | MCP_BROKER_HOST | 0.0.0.0 | |\n * | MCP_BROKER_PROVIDER_PATH | /provider| Prefix for one-slot-per-socket providers (plain frames)|\n * | MCP_BROKER_PROVIDERS_PATH |/providers| Exact path for multiplexed providers (envelope frames) |\n * | MCP_BROKER_CLIENT_PATH | / | Prefix for raw WS clients |\n * | MCP_BROKER_MCP_PATH | /mcp | Suffix for Streamable HTTP transport |\n * | MCP_BROKER_SSE_PATH | /sse | Suffix for the legacy SSE stream (GET) |\n * | MCP_BROKER_MESSAGES_PATH | /messages| Suffix for legacy SSE JSON-RPC posts |\n * | MCP_BROKER_WWW_DIR | (none) | Ergonomic shortcut: mount this directory at \"/\" |\n * | MCP_BROKER_BUNDLE_DIR | (none) | Ergonomic shortcut: mount this directory at \"/bundle\" |\n * | MCP_BROKER_OPEN | (unset) | \"1\" for the root URL, or a path/same-origin URL to open|\n * | MCP_BROKER_TLS_CERT | (none) | Path to a PEM TLS certificate |\n * | MCP_BROKER_TLS_KEY | (none) | Path to a PEM private key |\n * | MCP_BROKER_PROTOCOL | auto | \"http\" to force plain, \"https\" to force TLS, otherwise |\n * | | | TLS is enabled iff cert+key are both set |\n * | MCP_BROKER_STDIO_PROVIDER | (none) | When set, stdin/stdout carry JSON-RPC for the named |\n * | | | provider (Claude Desktop bridge). |\n * | MCP_BROKER_LOCALE | en | Locale used for tool descriptions on the `_broker` |\n * | | | slot. ISO 639-1 base; regional variants accepted. |\n * | MCP_BROKER_ALLOWED_ORIGINS | (none) | Comma-separated browser origins allowed on the client |\n * | | | HTTP surface (`/<slot>/mcp`, `/sse`, `/messages`). |\n * | | | None means no browser origin passes. |\n * | MCP_BROKER_PROVIDER_HEARTBEAT_MS | 30000 | Provider ws ping interval; 0 disables liveness. |\n * | MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS| 60000 | Deadline for one provider answer; 0 disables. |\n * | MCP_BROKER_PROVIDER_TAKEOVER | liveness | \"reject\" | \"liveness\" | \"always\" on slot contention |\n * | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | (none) | Full OTLP/HTTP traces endpoint. |\n * | OTEL_EXPORTER_OTLP_HEADERS | (none) | Comma-separated percent-encoded key=value. |\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport open from \"open\";\nimport { WsTunnelBuilder, VERSION, PACKAGE_NAME, BROKER_PROVIDER_NAME, BROKER_AGGREGATE_NAME, type ProviderTakeoverMode } from \"./index\";\nimport { BrokerConfigError, loadBrokerConfig, loadSecurityConfig, resolveOpenTarget, type ILoadedBrokerConfig, type ILoadedSecurityConfig } from \"./config\";\nimport { loadMcpbBundle } from \"./mcpb/mcpb.loader\";\n\n// ---------------------------------------------------------------------------\n// CLI flags\n// ---------------------------------------------------------------------------\n// Handled before ANY other work (config load, logging, server start) so that\n// `--help` / `--version` never boot a server or bind a port.\n\nconst argv = process.argv.slice(2);\n\nif (argv.includes(\"--help\") || argv.includes(\"-h\")) {\n printHelp();\n process.exit(0);\n}\nif (argv.includes(\"--version\") || argv.includes(\"-v\")) {\n process.stdout.write(`${PACKAGE_NAME} ${VERSION}\\n`);\n process.exit(0);\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n `\\n${PACKAGE_NAME} ${VERSION}\\n` +\n `WebSocket-based Model Context Protocol broker.\\n\\n` +\n `USAGE\\n` +\n ` npx @cyanmycelium/mcp-broker Start the broker (foreground)\\n` +\n ` npx @cyanmycelium/mcp-broker --help Show this help\\n` +\n ` npx @cyanmycelium/mcp-broker --version Print the version\\n\\n` +\n `The broker takes no positional arguments. Configure it with a\\n` +\n `.mcp-broker/config.json file or MCP_BROKER_* environment variables\\n` +\n `(env vars win over the file).\\n\\n` +\n `COMMON ENVIRONMENT VARIABLES\\n` +\n ` MCP_BROKER_CONFIG Path to a JSON config file\\n` +\n ` MCP_BROKER_PORT TCP port (default 3000)\\n` +\n ` MCP_BROKER_HOST Bind interface (default 0.0.0.0)\\n` +\n ` MCP_BROKER_PROTOCOL \"http\" | \"https\" (default: auto from TLS)\\n` +\n ` MCP_BROKER_TLS_CERT PEM certificate path (enables HTTPS/WSS)\\n` +\n ` MCP_BROKER_TLS_KEY PEM private key path\\n` +\n ` MCP_BROKER_STDIO_PROVIDER Bridge stdin/stdout to this provider (\"_all\" is the safe target)\\n` +\n ` MCP_BROKER_LOCALE Locale for _broker tool descriptions (default en)\\n` +\n ` MCP_BROKER_ALLOWED_ORIGINS Comma-separated browser origins allowed on /<slot>/mcp,\\n` +\n ` /<slot>/sse and /<slot>/messages (default: none pass)\\n` +\n ` MCP_BROKER_WWW_DIR Serve this directory at \"/\"\\n` +\n ` MCP_BROKER_BUNDLE_DIR Serve this directory at \"/bundle\"\\n` +\n ` MCP_BROKER_OPEN \"1\" to open the root in a browser, or a path such as /app/\\n\\n` +\n `ENDPOINT PATHS (change these only if every peer agrees)\\n` +\n ` MCP_BROKER_PROVIDER_PATH Prefix for one provider per socket (default /provider);\\n` +\n ` connect as <prefix>/<slot> with DirectTransport (plain frames)\\n` +\n ` MCP_BROKER_PROVIDERS_PATH Exact path for many providers on one socket (default /providers);\\n` +\n ` connect with MultiplexTransport (envelope frames)\\n` +\n ` MCP_BROKER_CLIENT_PATH Prefix for raw WebSocket clients (default /)\\n` +\n ` MCP_BROKER_MCP_PATH Streamable HTTP suffix (default /mcp)\\n` +\n ` MCP_BROKER_SSE_PATH Legacy SSE stream suffix (default /sse)\\n` +\n ` MCP_BROKER_MESSAGES_PATH Legacy SSE post suffix (default /messages)\\n\\n` +\n `PROVIDER LIVENESS\\n` +\n ` MCP_BROKER_PROVIDER_HEARTBEAT_MS Ping interval per provider socket (default 30000, 0 off)\\n` +\n ` MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS Deadline for one provider answer (default 60000, 0 off)\\n` +\n ` MCP_BROKER_PROVIDER_TAKEOVER reject | liveness | always (default liveness)\\n\\n` +\n `PROVIDER TELEMETRY (OpenTelemetry, opt-in)\\n` +\n ` OTEL_EXPORTER_OTLP_TRACES_ENDPOINT Full OTLP/HTTP endpoint, enables trace export\\n` +\n ` OTEL_EXPORTER_OTLP_HEADERS Comma-separated percent-encoded key=value headers\\n\\n` +\n `AUTHORIZATION (OAuth 2.1, opt-in)\\n` +\n ` MCP_BROKER_AUTH_ENABLED \"1\" to require bearer tokens on client endpoints\\n` +\n ` MCP_BROKER_PUBLIC_BASE_URL Public origin, e.g. https://mcp.example.com\\n` +\n ` MCP_BROKER_JWKS Authorization server JWKS URL\\n` +\n ` MCP_BROKER_ISSUER Expected token issuer\\n` +\n ` MCP_BROKER_PROVIDER_SECRET Shared secret required from providers\\n\\n` +\n `CONFIG-FILE ONLY (no env var; .mcp-broker/config.json)\\n` +\n ` stdioUpstreams[] Local MCP servers the broker spawns. NOT in _all unless \"aggregate\": true\\n` +\n ` mcpServers[] Remote MCP servers the broker dials out to. In _all unless \"aggregate\": false\\n` +\n ` mcpbBundles[] Signed .mcpb bundles, verified then spawned. In _all unless \"aggregate\": false\\n` +\n ` www.mounts[] Several urlPrefix -> dir mappings (the env vars above mount one each)\\n` +\n ` allowedOrigins Also accepts { \"pattern\": \"<regexp>\" }; the env var takes a list only\\n` +\n ` auth.* Roles, assignments, denies, per-slot and per-provider scopes\\n` +\n ` auth.dev Development tokens, security file only: { callers: [{ id, tokenEnv, user?, groups? }], networks?: [\"lan\"] }; loopback clients unless networks says more\\n` +\n ` brokerName Library-only: no builder setter exists, so the CLI cannot forward it\\n\\n` +\n `RESERVED SLOTS (always present, no provider needed)\\n` +\n ` _broker Introspection: broker_info, providers_list, provider_status,\\n` +\n ` broker_guide (integration docs), broker_diagnose (live problem report)\\n` +\n ` _all Live aggregate of every opted-in provider. The right target for\\n` +\n ` MCP_BROKER_STDIO_PROVIDER, since it exists before any provider connects.\\n\\n` +\n `Start here: call broker_guide on the _broker slot for the integration guide.\\n` +\n `Full reference: https://github.com/pandaGaume/mcp-broker/tree/main/node\\n\\n`\n );\n}\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\n/**\n * A config file that was designated or found but cannot be used stops the\n * process here, before anything listens. Carrying on with an empty config\n * would start a broker with no authentication and no authorization.\n */\nfunction loadConfigOrExit(): ILoadedBrokerConfig & { readonly security: ILoadedSecurityConfig | null } {\n try {\n const loaded = loadBrokerConfig();\n return { ...loaded, security: loadSecurityConfig(loaded) };\n } catch (error) {\n if (!(error instanceof BrokerConfigError)) throw error;\n process.stderr.write(`${error.message}\n`);\n process.exit(1);\n }\n}\n\nconst { config, baseDir, security } = loadConfigOrExit();\nconst cwd = process.cwd();\n\n/**\n * The `auth` block in force: the security file's when there is one (the\n * loader refused a `config.json` that carries one too), `config.json`'s\n * otherwise, for deployments that have not moved it yet.\n */\nconst authConfig = security?.security.auth ?? config.auth;\n\n/**\n * Fills an env var from the config file when the env var is not already set.\n * Used for **non-path** scalars, path-bearing fields are read directly so\n * they can be resolved against `baseDir` (config file's directory) instead\n * of `cwd` (deploy environment).\n */\nfunction envFromConfig(envName: string, configValue: string | number | boolean | undefined): void {\n if (configValue === undefined || configValue === null) return;\n if (process.env[envName] !== undefined && process.env[envName] !== \"\") return;\n process.env[envName] = String(configValue);\n}\n\nenvFromConfig(\"MCP_BROKER_PORT\", config.port);\nenvFromConfig(\"MCP_BROKER_HOST\", config.host);\nenvFromConfig(\"MCP_BROKER_PROTOCOL\", config.protocol);\nenvFromConfig(\"MCP_BROKER_LOCALE\", config.locale);\nenvFromConfig(\"MCP_BROKER_STDIO_PROVIDER\", config.stdioProvider);\nenvFromConfig(\"MCP_BROKER_PROVIDER_PATH\", config.paths?.provider);\nenvFromConfig(\"MCP_BROKER_PROVIDERS_PATH\", config.paths?.providers);\nenvFromConfig(\"MCP_BROKER_CLIENT_PATH\", config.paths?.client);\nenvFromConfig(\"MCP_BROKER_MCP_PATH\", config.paths?.mcp);\nenvFromConfig(\"MCP_BROKER_SSE_PATH\", config.paths?.sse);\nenvFromConfig(\"MCP_BROKER_MESSAGES_PATH\", config.paths?.messages);\nenvFromConfig(\"MCP_BROKER_PROVIDER_HEARTBEAT_MS\", config.providerHeartbeatIntervalMs);\nenvFromConfig(\"MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS\", config.providerRequestTimeoutMs);\nenvFromConfig(\"MCP_BROKER_MAX_SUBSCRIPTIONS_PER_CLIENT\", config.resourceSubscriptions?.maxSubscriptionsPerClient);\nenvFromConfig(\"MCP_BROKER_MAX_SUBSCRIPTIONS_PER_SLOT\", config.resourceSubscriptions?.maxSubscriptionsPerSlot);\nenvFromConfig(\"MCP_BROKER_MAX_RESOURCE_URI_LENGTH\", config.resourceSubscriptions?.maxResourceUriLength);\nenvFromConfig(\"MCP_BROKER_PROVIDER_TAKEOVER\", config.providerTakeover);\n// `www.open` is `boolean | string`: `true` means the root, a string is a path\n// or a same-origin URL. Both travel as the env string and are resolved once,\n// by `resolveOpenTarget`, so the file and the env var cannot diverge.\nenvFromConfig(\"MCP_BROKER_OPEN\", config.www?.open === true ? \"1\" : typeof config.www?.open === \"string\" ? config.www.open : undefined);\nenvFromConfig(\"MCP_BROKER_AUTH_ENABLED\", authConfig?.enabled === true ? \"1\" : undefined);\nenvFromConfig(\"MCP_BROKER_PUBLIC_BASE_URL\", authConfig?.publicBaseUrl);\nenvFromConfig(\"MCP_BROKER_JWKS\", authConfig?.jwks);\nenvFromConfig(\"MCP_BROKER_ISSUER\", authConfig?.issuer);\nenvFromConfig(\"MCP_BROKER_PROVIDER_SECRET\", authConfig?.providerSecret);\n\nconst stdioProvider = process.env[\"MCP_BROKER_STDIO_PROVIDER\"];\n\n// In stdio mode stdout is reserved for JSON-RPC, redirect all console output to stderr.\nif (stdioProvider) {\n const toStderr = (...args: unknown[]) => process.stderr.write(args.join(\" \") + \"\\n\");\n console.log = toStderr;\n console.info = toStderr;\n console.warn = toStderr;\n console.error = toStderr;\n}\n\nconst port = parseInt(process.env[\"MCP_BROKER_PORT\"] ?? \"3000\", 10);\nconst host = process.env[\"MCP_BROKER_HOST\"];\n// ── Endpoint paths ───────────────────────────────────────────────────────────\n// All six resolve the same way (env, then config file, then the default) and all\n// six are handed to the builder. Before this, `paths.providers` and\n// `paths.messages` were read nowhere and `paths.sse` reached only the banner, so\n// setting one advertised an endpoint the router did not serve.\nconst providerPath = process.env[\"MCP_BROKER_PROVIDER_PATH\"] ?? \"/provider\";\nconst providersPath = process.env[\"MCP_BROKER_PROVIDERS_PATH\"] ?? \"/providers\";\nconst clientPath = process.env[\"MCP_BROKER_CLIENT_PATH\"] ?? \"/\";\nconst mcpPath = process.env[\"MCP_BROKER_MCP_PATH\"] ?? \"/mcp\";\nconst ssePath = process.env[\"MCP_BROKER_SSE_PATH\"] ?? \"/sse\";\nconst messagesPath = process.env[\"MCP_BROKER_MESSAGES_PATH\"] ?? \"/messages\";\n\n// Provider telemetry uses the standard OpenTelemetry deployment variables so\n// the same collector configuration works in Docker, Kubernetes and Windows.\nconst otlpTracesEndpoint = process.env[\"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT\"] ?? config.telemetry?.otlpHttpEndpoint;\n\nfunction otlpHeadersFromEnv(raw: string | undefined): Record<string, string> {\n const headers: Record<string, string> = {};\n if (!raw) return headers;\n for (const field of raw.split(\",\")) {\n const separator = field.indexOf(\"=\");\n if (separator <= 0) {\n console.warn(`[mcp-broker] Ignoring malformed OTEL_EXPORTER_OTLP_HEADERS field: ${JSON.stringify(field)}.`);\n continue;\n }\n try {\n const key = decodeURIComponent(field.slice(0, separator).trim());\n const value = decodeURIComponent(field.slice(separator + 1).trim());\n if (key) headers[key] = value;\n } catch {\n console.warn(`[mcp-broker] Ignoring malformed percent encoding in OTEL_EXPORTER_OTLP_HEADERS field: ${JSON.stringify(field)}.`);\n }\n }\n return headers;\n}\n\nconst otlpHeaders = { ...(config.telemetry?.headers ?? {}), ...otlpHeadersFromEnv(process.env[\"OTEL_EXPORTER_OTLP_HEADERS\"]) };\n\n// ── TLS material ─────────────────────────────────────────────────────────────\n// Env var (relative to cwd) wins over config (relative to baseDir).\nconst tlsCertPath = process.env[\"MCP_BROKER_TLS_CERT\"] ? path.resolve(cwd, process.env[\"MCP_BROKER_TLS_CERT\"]) : config.tls?.cert ? path.resolve(baseDir, config.tls.cert) : null;\nconst tlsKeyPath = process.env[\"MCP_BROKER_TLS_KEY\"] ? path.resolve(cwd, process.env[\"MCP_BROKER_TLS_KEY\"]) : config.tls?.key ? path.resolve(baseDir, config.tls.key) : null;\n\nconst protocolOverride = process.env[\"MCP_BROKER_PROTOCOL\"]?.toLowerCase();\nif (protocolOverride !== undefined && protocolOverride !== \"http\" && protocolOverride !== \"https\") {\n console.error(`[mcp-broker] Invalid MCP_BROKER_PROTOCOL=\"${protocolOverride}\". Use \"http\" or \"https\".`);\n process.exit(1);\n}\nif (protocolOverride === \"https\" && (!tlsCertPath || !tlsKeyPath)) {\n console.error(\"[mcp-broker] MCP_BROKER_PROTOCOL=https requires TLS cert+key (via config.tls or MCP_BROKER_TLS_CERT/KEY).\");\n process.exit(1);\n}\n\nconst useTls = protocolOverride === \"http\" ? false : protocolOverride === \"https\" ? true : !!(tlsCertPath && tlsKeyPath);\n\n// ── Provider liveness knobs ──────────────────────────────────────────────────\n// The defaults are correct for nearly everyone; these exist for the operator who\n// has to tune them (long-running tools, a flaky link). An unusable value is\n// reported and ignored rather than silently coerced, so nobody believes a\n// setting is in force when it is not.\n\n/**\n * Parses a millisecond count from an env var. Returns `undefined` (keep the\n * built-in default) when unset or unusable, after saying why.\n */\nfunction millisFromEnv(envName: string): number | undefined {\n const raw = process.env[envName];\n if (raw === undefined || raw.trim() === \"\") return undefined;\n const value = Number(raw);\n if (!Number.isFinite(value) || value < 0) {\n console.warn(`[mcp-broker] Ignoring ${envName}=\"${raw}\": expected a whole number of milliseconds (0 disables). Using the default.`);\n return undefined;\n }\n return Math.floor(value);\n}\n\nconst providerHeartbeatIntervalMs = millisFromEnv(\"MCP_BROKER_PROVIDER_HEARTBEAT_MS\");\nconst providerRequestTimeoutMs = millisFromEnv(\"MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS\");\n\n/** Reads a positive whole number from the environment, or `undefined` (with a warning) for anything else. */\nfunction countFromEnv(envName: string): number | undefined {\n const raw = process.env[envName];\n if (raw === undefined || raw.trim() === \"\") return undefined;\n const value = Number(raw);\n if (!Number.isInteger(value) || value < 1) {\n console.warn(`[mcp-broker] Ignoring ${envName}=\"${raw}\": expected a whole number of at least 1. Using the default.`);\n return undefined;\n }\n return value;\n}\n\nconst resourceSubscriptionLimits = {\n maxSubscriptionsPerClient: countFromEnv(\"MCP_BROKER_MAX_SUBSCRIPTIONS_PER_CLIENT\"),\n maxSubscriptionsPerSlot: countFromEnv(\"MCP_BROKER_MAX_SUBSCRIPTIONS_PER_SLOT\"),\n maxResourceUriLength: countFromEnv(\"MCP_BROKER_MAX_RESOURCE_URI_LENGTH\"),\n};\n\nconst takeoverRaw = process.env[\"MCP_BROKER_PROVIDER_TAKEOVER\"]?.trim().toLowerCase();\nlet providerTakeover: ProviderTakeoverMode | undefined;\nif (takeoverRaw) {\n if (takeoverRaw === \"reject\" || takeoverRaw === \"liveness\" || takeoverRaw === \"always\") {\n providerTakeover = takeoverRaw;\n } else {\n console.warn(`[mcp-broker] Ignoring MCP_BROKER_PROVIDER_TAKEOVER=\"${takeoverRaw}\": expected \"reject\", \"liveness\" or \"always\". Using \"liveness\".`);\n }\n}\n\n// ── Allowed browser origins for the client HTTP surface ──────────────────────\n// Absent leaves the endpoint closed to browsers, which is the safe default.\n// The env var only carries the list form; a pattern needs the config file,\n// since a regular expression cannot survive comma-splitting.\nconst envOrigins = process.env[\"MCP_BROKER_ALLOWED_ORIGINS\"]\n ?.split(\",\")\n .map((origin) => origin.trim())\n .filter(Boolean);\n\nimport { compileRegex } from \"./authority/resource.limits\";\n\nlet allowedOrigins: readonly string[] | { readonly pattern: string; readonly flags?: string } | undefined;\nif (envOrigins?.length) {\n allowedOrigins = envOrigins;\n} else if (Array.isArray(config.allowedOrigins)) {\n allowedOrigins = config.allowedOrigins;\n} else if (config.allowedOrigins) {\n try {\n compileRegex(config.allowedOrigins.pattern, config.allowedOrigins.flags);\n allowedOrigins = config.allowedOrigins;\n } catch (err) {\n // Invalid security expressions refuse startup.\n console.error(`[mcp-broker] Cannot use allowedOrigins.pattern: ${(err as Error).message}`);\n process.exit(1);\n }\n}\n\n/**\n * One-line rendering of the resolved origin policy for the startup banner.\n *\n * Worth a banner row because the closed default is invisible otherwise: a page\n * served by this very broker still gets a 403 on `/<slot>/mcp` unless its origin\n * is listed, and nothing else says so until the request fails.\n */\nfunction describeAllowedOrigins(): string {\n if (allowedOrigins && \"pattern\" in allowedOrigins) return `pattern /${allowedOrigins.pattern}/${allowedOrigins.flags ?? \"\"} (RE2)`;\n if (allowedOrigins && allowedOrigins.length > 0) return allowedOrigins.join(\", \");\n return \"none (set MCP_BROKER_ALLOWED_ORIGINS, or allowedOrigins in the config file). Requests carrying no Origin header (Claude Desktop, MCP Inspector, any server-side SDK) still pass\";\n}\n\n// ── Env-var static mount shortcuts (relative to cwd) ─────────────────────────\nconst envWwwDir = process.env[\"MCP_BROKER_WWW_DIR\"] ? path.resolve(cwd, process.env[\"MCP_BROKER_WWW_DIR\"]) : null;\nconst envBundleDir = process.env[\"MCP_BROKER_BUNDLE_DIR\"] ? path.resolve(cwd, process.env[\"MCP_BROKER_BUNDLE_DIR\"]) : null;\n\n/**\n * Warns when the stdio bridge is pinned to a slot this broker does not host.\n *\n * The bridge gates every frame, `initialize` included, on the slot being\n * occupied, so an MCP host that starts before the slot is claimed fails the\n * handshake and gives up: with a browser-hosted provider, which cannot be\n * connected before the host launches, this fails every single time. Naming the\n * slots the broker does have turns that into a one-line fix.\n *\n * Deliberately a warning, not an exit: a WebSocket provider may legitimately\n * claim the slot later, and a broker that refuses to start would be worse.\n */\nfunction warnIfStdioTargetUnknown(target: string): void {\n const known = new Set<string>([\n BROKER_AGGREGATE_NAME,\n BROKER_PROVIDER_NAME,\n ...(config.stdioUpstreams ?? []).map((u) => u.name),\n ...(config.mcpServers ?? []).map((s) => s.name),\n ...(config.mcpbBundles ?? []).map((b) => b.name),\n ]);\n if (known.has(target)) return;\n\n const hosted = [...known].filter((name) => name !== BROKER_AGGREGATE_NAME && name !== BROKER_PROVIDER_NAME);\n console.warn(\n `[mcp-broker] MCP_BROKER_STDIO_PROVIDER=\"${target}\" names a slot this broker does not host. ` +\n `Nothing answers on it until a WebSocket provider connects to ${providerPath}/${target} (or announces \"${target}\" on ${providersPath}), ` +\n `and until then every request from the MCP host, including the initial handshake, fails with 'Provider \"${target}\" not connected'. ` +\n `An MCP host that starts before that provider is up, which is always the case for a provider hosted in a browser page, will therefore never connect. ` +\n `Prefer MCP_BROKER_STDIO_PROVIDER=\"${BROKER_AGGREGATE_NAME}\": it exists from startup, answers the handshake itself, unions every opted-in provider, ` +\n `and pushes notifications/tools/list_changed as providers join, so a page opened later appears live. ` +\n `Slots this broker hosts right now: ${hosted.length > 0 ? hosted.map((n) => `\"${n}\"`).join(\", \") + `, plus the reserved \"${BROKER_AGGREGATE_NAME}\" and \"${BROKER_PROVIDER_NAME}\"` : `the reserved \"${BROKER_AGGREGATE_NAME}\" and \"${BROKER_PROVIDER_NAME}\" only`}.`\n );\n}\n\n/** Keeps a collector outage from producing one CLI line per failed batch. */\nfunction telemetryExportLogger(intervalMs = 60_000): {\n readonly error: (error: unknown) => void;\n readonly success: (recordCount: number) => void;\n} {\n let failures = 0;\n let suppressed = 0;\n let lastReport = 0;\n return {\n error(error: unknown): void {\n failures++;\n const now = Date.now();\n if (lastReport === 0 || now - lastReport >= intervalMs) {\n const suffix = suppressed > 0 ? ` (${suppressed} repeated failure(s) suppressed)` : \"\";\n console.error(`[mcp-broker] telemetry export failed: ${(error as Error).message}${suffix}`);\n lastReport = now;\n suppressed = 0;\n } else {\n suppressed++;\n }\n },\n success(recordCount: number): void {\n if (failures === 0) return;\n console.info(\n `[mcp-broker] telemetry export recovered after ${failures} failed batch(es)` +\n `${suppressed > 0 ? `, ${suppressed} repeated log line(s) suppressed` : \"\"}; exported ${recordCount} span(s).`\n );\n failures = 0;\n suppressed = 0;\n lastReport = 0;\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Server bootstrap\n// ---------------------------------------------------------------------------\n\nasync function main(): Promise<void> {\n // Local grammar overrides: when `<baseDir>/grammars/` exists, the broker\n // server merges those JSON files on top of the packaged grammars.\n const localGrammarsDir = path.join(baseDir, \"grammars\");\n const hasLocalGrammars = fs.existsSync(localGrammarsDir);\n\n const builder = new WsTunnelBuilder()\n .withPort(port)\n .withProviderPath(providerPath)\n .withProvidersPath(providersPath)\n .withClientPath(clientPath)\n .withMcpPath(mcpPath)\n .withSsePath(ssePath)\n .withMessagesPath(messagesPath);\n\n if (host) {\n builder.withHost(host);\n }\n\n if (providerHeartbeatIntervalMs !== undefined) {\n builder.withProviderHeartbeat(providerHeartbeatIntervalMs);\n }\n if (providerRequestTimeoutMs !== undefined) {\n builder.withProviderRequestTimeout(providerRequestTimeoutMs);\n }\n // Only the limits actually set: an `undefined` field would override the default.\n const limits = Object.fromEntries(Object.entries(resourceSubscriptionLimits).filter(([, v]) => v !== undefined));\n if (Object.keys(limits).length > 0) {\n builder.withResourceSubscriptionLimits(limits);\n }\n if (providerTakeover) {\n builder.withProviderTakeover(providerTakeover);\n }\n if (otlpTracesEndpoint) {\n const exportLog = telemetryExportLogger();\n builder.withOtlpHttpTelemetry(\n {\n endpoint: otlpTracesEndpoint,\n headers: otlpHeaders,\n timeoutMs: config.telemetry?.timeoutMs,\n serviceNamespace: config.telemetry?.serviceNamespace,\n },\n {\n maxFrameBytes: config.telemetry?.maxFrameBytes,\n queueCapacity: config.telemetry?.queueCapacity,\n batchSize: config.telemetry?.batchSize,\n maxAttributes: config.telemetry?.maxAttributes,\n maxEvents: config.telemetry?.maxEvents,\n onExportError: exportLog.error,\n onExportSuccess: exportLog.success,\n }\n );\n }\n\n if (useTls) {\n // `withTlsFiles` reads both files synchronously and would otherwise\n // surface a bare ENOENT with no hint that TLS was on because a config\n // file mentioned it. That is the first wall a copied config template\n // hits, since the template ships `tls.cert`/`tls.key` but no `certs/`.\n try {\n builder.withTlsFiles(tlsCertPath!, tlsKeyPath!);\n } catch (err) {\n throw new Error(\n `TLS is enabled but the material could not be read: ${(err as Error).message}. ` +\n `Certificate: ${tlsCertPath}, key: ${tlsKeyPath}. ` +\n `Config-file paths are resolved against the config file's own directory, env-var paths against the current directory. ` +\n `Generate a development pair, point tls.cert/tls.key (or MCP_BROKER_TLS_CERT/MCP_BROKER_TLS_KEY) at real files, ` +\n `or run without TLS by removing the tls block and setting MCP_BROKER_PROTOCOL=http.`,\n { cause: err }\n );\n }\n }\n\n if (allowedOrigins) {\n builder.withAllowedOrigins(allowedOrigins);\n }\n\n // ── Static mounts ────────────────────────────────────────────────────────\n // First the env-var shortcuts (relative to cwd), then the config `www.mounts`\n // (relative to baseDir). Order matters: /bundle before / so the prefix router\n // can distinguish them. Subsequent registrations contribute additional mounts;\n // longest-prefix match wins at runtime.\n // Every prefix that actually got mounted, in registration order. The\n // browser-open check below tests the resolved path against this list rather\n // than against `config.www.mounts`, which would miss both env shortcuts and\n // would false-warn on a perfectly valid `open: \"/bundle/\"`.\n const mountedPrefixes: string[] = [];\n const mount = (urlPrefix: string, dir: string): void => {\n builder.withStaticMount(urlPrefix, dir);\n mountedPrefixes.push(urlPrefix);\n };\n\n if (envBundleDir && fs.existsSync(envBundleDir)) {\n mount(\"/bundle\", envBundleDir);\n }\n if (envWwwDir && fs.existsSync(envWwwDir)) {\n mount(\"/\", envWwwDir);\n }\n if (config.www?.mounts) {\n for (const entry of config.www.mounts) {\n const abs = path.resolve(baseDir, entry.dir);\n if (fs.existsSync(abs)) {\n mount(entry.urlPrefix, abs);\n } else {\n console.warn(`[mcp-broker] www.mounts entry \"${entry.urlPrefix}\" → ${abs} skipped (directory not found).`);\n }\n }\n }\n\n // ── Stdio upstreams ──────────────────────────────────────────────────────\n if (config.stdioUpstreams) {\n for (const u of config.stdioUpstreams) {\n builder.withStdioUpstream({\n name: u.name,\n command: u.command,\n args: u.args,\n env: u.env as NodeJS.ProcessEnv | undefined,\n aggregate: u.aggregate,\n });\n }\n }\n\n // ── Remote MCP server upstreams (reached by URL) ─────────────────────────\n // Config-discovered remote servers join the `_all` aggregate by default;\n // an explicit `aggregate: false` opts an entry out.\n if (config.mcpServers) {\n for (const s of config.mcpServers) {\n builder.withRemoteUpstream({ ...s, aggregate: s.aggregate ?? true });\n }\n }\n\n // ── Local `.mcpb` bundles ────────────────────────────────────────────────\n // Each bundle is signature-verified and unpacked before it is wired in as a\n // stdio upstream. A refused bundle is skipped (loadMcpbBundle logs why); it\n // never spawns a process. Bundles join `_all` by default (opt-out per entry).\n if (config.mcpbBundles) {\n for (const b of config.mcpbBundles) {\n const upstream = await loadMcpbBundle(b, baseDir);\n if (upstream) {\n builder.withStdioUpstream(upstream);\n }\n }\n }\n\n if (stdioProvider) {\n builder.withStdioClient(stdioProvider);\n warnIfStdioTargetUnknown(stdioProvider);\n }\n\n if (hasLocalGrammars) {\n builder.withBrokerLocalGrammarsDir(localGrammarsDir);\n }\n\n // ── Authorization (OAuth 2.1 resource server) ────────────────────────────\n // Opt-in: only wired when MCP_BROKER_AUTH_ENABLED / config.auth.enabled is on.\n // Env scalars win over config; array/object fields are read from config.\n const authEnabledEnv = process.env[\"MCP_BROKER_AUTH_ENABLED\"];\n const authEnabled = authEnabledEnv === \"1\" || authEnabledEnv === \"true\";\n if (authEnabled) {\n const publicBaseUrl = process.env[\"MCP_BROKER_PUBLIC_BASE_URL\"];\n const jwks = process.env[\"MCP_BROKER_JWKS\"];\n const issuer = process.env[\"MCP_BROKER_ISSUER\"];\n const authorizationServers = authConfig?.authorizationServers ?? (issuer ? [issuer] : []);\n if (!publicBaseUrl || !jwks || authorizationServers.length === 0) {\n console.error(\n \"[mcp-broker] auth.enabled requires publicBaseUrl, jwks, and at least one \" +\n \"authorizationServers entry (or issuer). Set them via config.auth or \" +\n \"MCP_BROKER_PUBLIC_BASE_URL / MCP_BROKER_JWKS / MCP_BROKER_ISSUER.\"\n );\n process.exit(1);\n }\n builder.withJwtAuth({\n publicBaseUrl,\n authorizationServers,\n jwksUri: jwks,\n issuer,\n scopesSupported: authConfig?.scopesSupported,\n requiredScopes: authConfig?.requiredScopes,\n perSlotScopes: authConfig?.perSlotScopes,\n providerScopes: authConfig?.providerScopes,\n subjectMapping: authConfig?.subjectMapping,\n roles: authConfig?.roles,\n assignments: authConfig?.assignments,\n denies: authConfig?.denies,\n slotResources: authConfig?.slotResources,\n toolCapabilities: authConfig?.toolCapabilities,\n providerToolCapabilities: authConfig?.providerToolCapabilities,\n audit: authConfig?.audit,\n });\n }\n\n // ── Development authorization (static tokens, loopback and named networks) ─\n // The security file's `auth.dev`: the same policy as OAuth, with tokens\n // read from the environment. Exclusive with OAuth; refused in config.json,\n // where the loader would not resolve or check it.\n const devCallers = security?.devCallers ?? [];\n if (config.auth?.dev !== undefined) {\n console.error('[mcp-broker] \"auth.dev\" belongs in the security file (securityFile / MCP_BROKER_SECURITY_FILE), not in the config file.');\n process.exit(1);\n }\n if (devCallers.length > 0) {\n if (authEnabled) {\n console.error(\"[mcp-broker] auth.dev and OAuth (MCP_BROKER_AUTH_ENABLED / auth.enabled) are exclusive: development tokens or OAuth, not both.\");\n process.exit(1);\n }\n builder.withDevAuth({\n callers: devCallers,\n networks: security?.devNetworks,\n publicBaseUrl: process.env[\"MCP_BROKER_PUBLIC_BASE_URL\"] ?? `${useTls ? \"https\" : \"http\"}://localhost:${port}`,\n scopesSupported: authConfig?.scopesSupported,\n requiredScopes: authConfig?.requiredScopes,\n perSlotScopes: authConfig?.perSlotScopes,\n roles: authConfig?.roles,\n assignments: authConfig?.assignments,\n denies: authConfig?.denies,\n slotResources: authConfig?.slotResources,\n toolCapabilities: authConfig?.toolCapabilities,\n providerToolCapabilities: authConfig?.providerToolCapabilities,\n audit: authConfig?.audit,\n });\n }\n\n // ── Provider authentication (independent of client OAuth) ────────────────\n // Requires every provider connecting to /provider/<slot> or /providers to\n // present the shared secret, closes off slot occupation by strangers.\n const providerSecret = process.env[\"MCP_BROKER_PROVIDER_SECRET\"];\n if (providerSecret) {\n builder.withProviderSecret(providerSecret);\n }\n\n // `withProviderPrincipals()` and `build()` refuse a security setup they\n // could not enforce (two providers sharing a secret, a protected slot with\n // no provider identities, ...): say so and stop, rather than crash.\n let tunnel: ReturnType<typeof builder.build>;\n try {\n // ── Security file: one identity per provider, protected slots ────────\n if (security) {\n if (security.credentials.length > 0) builder.withProviderPrincipals(security.credentials);\n const resourceLimits = security.security.authorization?.resourceLimits;\n if (resourceLimits) builder.withResourceLimits(resourceLimits);\n const protectedSlots = security.security.authorization?.protectedSlots;\n if (protectedSlots && Object.keys(protectedSlots).length > 0) builder.withProtectedSlots(protectedSlots);\n if (security.security.limits) builder.withLimits(security.security.limits);\n builder.withSecurityVersion(security.version);\n }\n tunnel = builder.build();\n } catch (error) {\n console.error(`[mcp-broker] ${(error as Error).message}`);\n process.exit(1);\n }\n await tunnel.start();\n\n // ── Startup banner ──────────────────────────────────────────────────────\n const httpScheme = useTls ? \"https\" : \"http\";\n const wsScheme = useTls ? \"wss\" : \"ws\";\n const hr = \"─\".repeat(64);\n const localhost = `${httpScheme}://localhost:${port}`;\n const mcpSuffix = mcpPath.replace(/^\\//, \"\");\n const sseSuffix = ssePath.replace(/^\\//, \"\");\n const messagesSuffix = messagesPath.replace(/^\\//, \"\");\n\n // The two provider rows are the point of the banner: the endpoint chosen\n // decides the framing, and connecting the wrong transport to either one is\n // the single most common way an integration fails. So each row names the\n // transport class it requires, right there.\n console.log();\n console.log(`⚙️ mcp-broker started${useTls ? \" (TLS)\" : \"\"}`);\n console.log(hr);\n console.log(`📡 Provider WS direct ${wsScheme}://localhost:${port}${providerPath}/<name>`);\n console.log(` one provider per socket, plain JSON-RPC frames: use DirectTransport`);\n console.log(`🧵 Provider WS multiplex ${wsScheme}://localhost:${port}${providersPath}`);\n console.log(` many providers per socket, envelope frames: use MultiplexTransport`);\n console.log(`🔌 MCP (Streamable HTTP) ${localhost}/<name>/${mcpSuffix}`);\n console.log(`📺 Legacy SSE ${localhost}/<name>/${sseSuffix} + POST ${localhost}/<name>/${messagesSuffix}`);\n // Raw WS clients are a real, configurable route. Left off the banner it read\n // as provider-only, and `clientPath` reached the router and nothing else,\n // which is the same defect `paths.sse` had.\n console.log(`🔗 Client WS (raw) ${wsScheme}://localhost:${port}${clientPath === \"/\" ? \"/\" : clientPath}<name>`);\n console.log(` bare JSON-RPC frames, no session layer; not origin-checked`);\n console.log(`🧩 Reserved slots ${BROKER_AGGREGATE_NAME}: live aggregate of every opted-in provider`);\n console.log(` ${BROKER_PROVIDER_NAME}: broker_info, providers_list, provider_status, broker_guide, broker_diagnose`);\n if (mountedPrefixes.length > 0) {\n console.log(`📁 Static mounts ${mountedPrefixes.map((prefix) => `${localhost}${prefix === \"/\" ? \"/\" : prefix + \"/\"}`).join(\" \")}`);\n }\n if (hasLocalGrammars) {\n console.log(`🌐 Local grammars ${localGrammarsDir}`);\n }\n console.log(\n `🔐 Authorization ${\n authEnabled\n ? \"OAuth 2.1 (Bearer required)\"\n : devCallers.length > 0\n ? `DEVELOPMENT static tokens (${devCallers.map((c) => c.id).join(\", \")}), clients from loopback${security?.devNetworks.length ? ` and ${security.devNetworks.join(\", \")}` : \" only\"}`\n : \"disabled (trusted network only)\"\n }`\n );\n const providerIdentities = security?.credentials.length ?? 0;\n console.log(\n `🛡️ Provider auth ${providerIdentities > 0 ? `${providerIdentities} provider identit${providerIdentities === 1 ? \"y\" : \"ies\"}${providerSecret ? \" + shared secret\" : \"\"}` : providerSecret ? \"shared secret required\" : \"disabled\"}`\n );\n if (security) {\n const protectedCount = Object.keys(security.security.authorization?.protectedSlots ?? {}).length;\n console.log(`🔏 Security file ${security.sourcePath} (version ${security.version}${protectedCount > 0 ? `, ${protectedCount} protected slot(s)` : \"\"})`);\n }\n console.log(`🌍 Browser origins ${describeAllowedOrigins()}`);\n console.log(` enforced on /<name>/${mcpSuffix}, /<name>/${sseSuffix} and /<name>/${messagesSuffix}`);\n if (!allowedOrigins) {\n console.log(` a page this broker serves is refused too, list its origin to admit it`);\n }\n console.log(`📈 Provider telemetry ${otlpTracesEndpoint ? `OTLP/HTTP to ${otlpTracesEndpoint}` : \"disabled\"}`);\n console.log(hr);\n console.log(` New here? Call broker_guide on the ${BROKER_PROVIDER_NAME} slot: ${localhost}/${BROKER_PROVIDER_NAME}/${mcpSuffix}`);\n console.log(` Press Ctrl+C to stop.`);\n console.log();\n\n // ── Auto-launch the browser ─────────────────────────────────────────────\n // Opt-in through MCP_BROKER_OPEN / www.open, and only onto a path a mount\n // actually serves: launching a browser at a 404 is worse than not launching.\n const openTarget = resolveOpenTarget(process.env[\"MCP_BROKER_OPEN\"], localhost);\n if (openTarget.error) {\n console.warn(`[mcp-broker] ${openTarget.error}`);\n } else if (openTarget.url && openTarget.path) {\n const openUrl = openTarget.url;\n const openPath = openTarget.path;\n const covered = mountedPrefixes.some((prefix) => prefix === \"/\" || openPath === prefix || openPath.startsWith(prefix.endsWith(\"/\") ? prefix : prefix + \"/\"));\n if (!covered) {\n console.warn(\n `[mcp-broker] Not opening ${openUrl}: no static mount serves \"${openPath}\", so it would 404. ` +\n (mountedPrefixes.length > 0\n ? `Mounted prefixes: ${mountedPrefixes.map((p) => `\"${p}\"`).join(\", \")}.`\n : `No directory is mounted. Add one with MCP_BROKER_WWW_DIR=<dir> (serves it at \"/\") or a www.mounts entry in the config file.`)\n );\n } else {\n console.log(`🚀 Opening browser: ${openUrl}`);\n console.log();\n try {\n await open(openUrl);\n } catch (err) {\n // The broker is listening and healthy; failing to spawn a browser\n // (a headless box, no default handler) must not take it down.\n console.warn(`[mcp-broker] Could not launch a browser for ${openUrl}: ${(err as Error).message}. The broker is running; open the URL yourself.`);\n }\n }\n }\n\n // ── Signal handlers ─────────────────────────────────────────────────────\n const shutdown = async (signal: string): Promise<void> => {\n console.log(`\\n⛔ ${signal} received, shutting down…`);\n await tunnel.stop();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => void shutdown(\"SIGINT\"));\n process.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\"));\n}\n\nmain().catch((err: unknown) => {\n // `tunnel.start()` now rejects on a listen failure instead of dying as an\n // uncaught EventEmitter error, so this path is live where it used to be\n // unreachable, and the rejection carries a fully worded diagnosis (for\n // EADDRINUSE: the address, the URL to attach to the broker that already\n // holds the port, and the MCP_BROKER_PORT escape). Print the sentence, not\n // the object: `console.error(\"…\", err)` renders a stack dump and buries it.\n //\n // In stdio mode console is redirected to stderr above, so this cannot\n // corrupt the JSON-RPC stream on stdout.\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[mcp-broker] Cannot start: ${message}`);\n\n // A wrapped cause keeps the original errno for anyone reading a host log.\n const cause = err instanceof Error ? err.cause : undefined;\n if (cause instanceof Error) {\n const code = (cause as NodeJS.ErrnoException).code;\n console.error(`[mcp-broker] Underlying error: ${code ? code + \" \" : \"\"}${cause.message}`);\n }\n\n // A bare Error with no diagnosis of its own is still worth grounding: say\n // where the broker was trying to listen, since that is the one fact the\n // reader needs to check next.\n if (!(err instanceof Error) || !message.includes(String(port))) {\n console.error(\n `[mcp-broker] Configuration in effect: host=${host ?? \"0.0.0.0\"} port=${port}. Set MCP_BROKER_PORT / MCP_BROKER_HOST, or \"port\" / \"host\" in .mcp-broker/config.json.`\n );\n }\n\n process.exit(1);\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/bin.ts"],"names":[],"mappings":"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","file":"bin.js","sourcesContent":["#!/usr/bin/env node\n/**\n * Standalone entry-point that starts the mcp-broker WebSocket server.\n *\n * ## Configuration sources (highest priority first)\n *\n * 1. Environment variables (`MCP_BROKER_*`).\n * 2. JSON config file. Resolved in order:\n * a. `MCP_BROKER_CONFIG` env var.\n * b. `./.mcp-broker/config.json` in the current working directory.\n * c. `./mcp-broker.config.json` (legacy, with a deprecation warning).\n * 3. Built-in defaults.\n *\n * ## Path resolution\n *\n * - Env-var paths (`MCP_BROKER_*_DIR`, `MCP_BROKER_TLS_*`) are resolved\n * against `process.cwd()`.\n * - Config-file paths (`tls.cert`, `www.mounts[*].dir`, `stdioUpstreams[*]`,\n * `mcpbBundles[*]`) are resolved against the **config file's directory**: so a config in\n * `./.mcp-broker/config.json` referring to `\"certs/cert.pem\"` points at\n * `./.mcp-broker/certs/cert.pem`. The folder is self-contained.\n *\n * ## Local grammar overrides\n *\n * When `.mcp-broker/grammars/<userAgent>/<locale>.json` files exist next to\n * the config file, they are merged **on top of** the packaged grammars. Lets\n * users customize tool/resource descriptions for their organization without\n * forking the package.\n *\n * ## Environment variables\n *\n * | Variable | Default | Notes |\n * |--------------------------------|----------|--------------------------------------------------------|\n * | MCP_BROKER_CONFIG | (none) | Path to a JSON config file (see above). |\n * | MCP_BROKER_PORT | 3000 | |\n * | MCP_BROKER_HOST | 0.0.0.0 | |\n * | MCP_BROKER_PROVIDER_PATH | /provider| Prefix for one-slot-per-socket providers (plain frames)|\n * | MCP_BROKER_PROVIDERS_PATH |/providers| Exact path for multiplexed providers (envelope frames) |\n * | MCP_BROKER_CLIENT_PATH | / | Prefix for raw WS clients |\n * | MCP_BROKER_MCP_PATH | /mcp | Suffix for Streamable HTTP transport |\n * | MCP_BROKER_SSE_PATH | /sse | Suffix for the legacy SSE stream (GET) |\n * | MCP_BROKER_MESSAGES_PATH | /messages| Suffix for legacy SSE JSON-RPC posts |\n * | MCP_BROKER_WWW_DIR | (none) | Ergonomic shortcut: mount this directory at \"/\" |\n * | MCP_BROKER_BUNDLE_DIR | (none) | Ergonomic shortcut: mount this directory at \"/bundle\" |\n * | MCP_BROKER_OPEN | (unset) | \"1\" for the root URL, or a path/same-origin URL to open|\n * | MCP_BROKER_TLS_CERT | (none) | Path to a PEM TLS certificate |\n * | MCP_BROKER_TLS_KEY | (none) | Path to a PEM private key |\n * | MCP_BROKER_PROTOCOL | auto | \"http\" to force plain, \"https\" to force TLS, otherwise |\n * | | | TLS is enabled iff cert+key are both set |\n * | MCP_BROKER_STDIO_PROVIDER | (none) | When set, stdin/stdout carry JSON-RPC for the named |\n * | | | provider (Claude Desktop bridge). |\n * | MCP_BROKER_LOCALE | en | Locale used for tool descriptions on the `_broker` |\n * | | | slot. ISO 639-1 base; regional variants accepted. |\n * | MCP_BROKER_ALLOWED_ORIGINS | (none) | Comma-separated browser origins allowed on the client |\n * | | | HTTP surface (`/<slot>/mcp`, `/sse`, `/messages`). |\n * | | | None means no browser origin passes. |\n * | MCP_BROKER_PROVIDER_HEARTBEAT_MS | 30000 | Provider ws ping interval; 0 disables liveness. |\n * | MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS| 60000 | Deadline for one provider answer; 0 disables. |\n * | MCP_BROKER_PROVIDER_TAKEOVER | liveness | \"reject\" | \"liveness\" | \"always\" on slot contention |\n * | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | (none) | Full OTLP/HTTP traces endpoint. |\n * | OTEL_EXPORTER_OTLP_HEADERS | (none) | Comma-separated percent-encoded key=value. |\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport open from \"open\";\nimport { WsTunnelBuilder, VERSION, PACKAGE_NAME, BROKER_PROVIDER_NAME, BROKER_AGGREGATE_NAME, type ProviderTakeoverMode } from \"./index\";\nimport { BrokerConfigError, loadBrokerConfig, loadSecurityConfig, resolveOpenTarget, type ILoadedBrokerConfig, type ILoadedSecurityConfig } from \"./config\";\nimport { loadMcpbBundle } from \"./mcpb/mcpb.loader\";\nimport { startBrokerPlugins } from \"./plugins\";\n\n// ---------------------------------------------------------------------------\n// CLI flags\n// ---------------------------------------------------------------------------\n// Handled before ANY other work (config load, logging, server start) so that\n// `--help` / `--version` never boot a server or bind a port.\n\nconst argv = process.argv.slice(2);\n\nif (argv.includes(\"--help\") || argv.includes(\"-h\")) {\n printHelp();\n process.exit(0);\n}\nif (argv.includes(\"--version\") || argv.includes(\"-v\")) {\n process.stdout.write(`${PACKAGE_NAME} ${VERSION}\\n`);\n process.exit(0);\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n `\\n${PACKAGE_NAME} ${VERSION}\\n` +\n `WebSocket-based Model Context Protocol broker.\\n\\n` +\n `USAGE\\n` +\n ` npx @cyanmycelium/mcp-broker Start the broker (foreground)\\n` +\n ` npx @cyanmycelium/mcp-broker --help Show this help\\n` +\n ` npx @cyanmycelium/mcp-broker --version Print the version\\n\\n` +\n `The broker takes no positional arguments. Configure it with a\\n` +\n `.mcp-broker/config.json file or MCP_BROKER_* environment variables\\n` +\n `(env vars win over the file).\\n\\n` +\n `COMMON ENVIRONMENT VARIABLES\\n` +\n ` MCP_BROKER_CONFIG Path to a JSON config file\\n` +\n ` MCP_BROKER_PORT TCP port (default 3000)\\n` +\n ` MCP_BROKER_HOST Bind interface (default 0.0.0.0)\\n` +\n ` MCP_BROKER_PROTOCOL \"http\" | \"https\" (default: auto from TLS)\\n` +\n ` MCP_BROKER_TLS_CERT PEM certificate path (enables HTTPS/WSS)\\n` +\n ` MCP_BROKER_TLS_KEY PEM private key path\\n` +\n ` MCP_BROKER_STDIO_PROVIDER Bridge stdin/stdout to this provider (\"_all\" is the safe target)\\n` +\n ` MCP_BROKER_LOCALE Locale for _broker tool descriptions (default en)\\n` +\n ` MCP_BROKER_ALLOWED_ORIGINS Comma-separated browser origins allowed on /<slot>/mcp,\\n` +\n ` /<slot>/sse and /<slot>/messages (default: none pass)\\n` +\n ` MCP_BROKER_PLUGINS_DIR Directory of installed plugin packages\\n` +\n ` MCP_BROKER_WWW_DIR Serve this directory at \"/\"\\n` +\n ` MCP_BROKER_BUNDLE_DIR Serve this directory at \"/bundle\"\\n` +\n ` MCP_BROKER_OPEN \"1\" to open the root in a browser, or a path such as /app/\\n\\n` +\n `ENDPOINT PATHS (change these only if every peer agrees)\\n` +\n ` MCP_BROKER_PROVIDER_PATH Prefix for one provider per socket (default /provider);\\n` +\n ` connect as <prefix>/<slot> with DirectTransport (plain frames)\\n` +\n ` MCP_BROKER_PROVIDERS_PATH Exact path for many providers on one socket (default /providers);\\n` +\n ` connect with MultiplexTransport (envelope frames)\\n` +\n ` MCP_BROKER_CLIENT_PATH Prefix for raw WebSocket clients (default /)\\n` +\n ` MCP_BROKER_MCP_PATH Streamable HTTP suffix (default /mcp)\\n` +\n ` MCP_BROKER_SSE_PATH Legacy SSE stream suffix (default /sse)\\n` +\n ` MCP_BROKER_MESSAGES_PATH Legacy SSE post suffix (default /messages)\\n\\n` +\n `PROVIDER LIVENESS\\n` +\n ` MCP_BROKER_PROVIDER_HEARTBEAT_MS Ping interval per provider socket (default 30000, 0 off)\\n` +\n ` MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS Deadline for one provider answer (default 60000, 0 off)\\n` +\n ` MCP_BROKER_PROVIDER_TAKEOVER reject | liveness | always (default liveness)\\n\\n` +\n `PROVIDER TELEMETRY (OpenTelemetry, opt-in)\\n` +\n ` OTEL_EXPORTER_OTLP_TRACES_ENDPOINT Full OTLP/HTTP endpoint, enables trace export\\n` +\n ` OTEL_EXPORTER_OTLP_HEADERS Comma-separated percent-encoded key=value headers\\n\\n` +\n `AUTHORIZATION (OAuth 2.1, opt-in)\\n` +\n ` MCP_BROKER_AUTH_ENABLED \"1\" to require bearer tokens on client endpoints\\n` +\n ` MCP_BROKER_PUBLIC_BASE_URL Public origin, e.g. https://mcp.example.com\\n` +\n ` MCP_BROKER_JWKS Authorization server JWKS URL\\n` +\n ` MCP_BROKER_ISSUER Expected token issuer\\n` +\n ` MCP_BROKER_PROVIDER_SECRET Shared secret required from providers\\n\\n` +\n `CONFIG-FILE ONLY (no env var; .mcp-broker/config.json)\\n` +\n ` stdioUpstreams[] Local MCP servers the broker spawns. NOT in _all unless \"aggregate\": true\\n` +\n ` mcpServers[] Remote MCP servers the broker dials out to. In _all unless \"aggregate\": false\\n` +\n ` mcpbBundles[] Signed .mcpb bundles, verified then spawned. In _all unless \"aggregate\": false\\n` +\n ` www.mounts[] Several urlPrefix -> dir mappings (the env vars above mount one each)\\n` +\n ` allowedOrigins Also accepts { \"pattern\": \"<regexp>\" }; the env var takes a list only\\n` +\n ` auth.* Roles, assignments, denies, per-slot and per-provider scopes\\n` +\n ` auth.dev Development tokens, security file only: { callers: [{ id, tokenEnv, user?, groups? }], networks?: [\"lan\"] }; loopback clients unless networks says more\\n` +\n ` brokerName Library-only: no builder setter exists, so the CLI cannot forward it\\n\\n` +\n `RESERVED SLOTS (always present, no provider needed)\\n` +\n ` _broker Introspection: broker_info, providers_list, provider_status,\\n` +\n ` broker_guide (integration docs), broker_diagnose (live problem report)\\n` +\n ` _all Live aggregate of every opted-in provider. The right target for\\n` +\n ` MCP_BROKER_STDIO_PROVIDER, since it exists before any provider connects.\\n\\n` +\n `Start here: call broker_guide on the _broker slot for the integration guide.\\n` +\n `Full reference: https://github.com/pandaGaume/mcp-broker/tree/main/node\\n\\n`\n );\n}\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\n/**\n * A config file that was designated or found but cannot be used stops the\n * process here, before anything listens. Carrying on with an empty config\n * would start a broker with no authentication and no authorization.\n */\nfunction loadConfigOrExit(): ILoadedBrokerConfig & { readonly security: ILoadedSecurityConfig | null } {\n try {\n const loaded = loadBrokerConfig();\n return { ...loaded, security: loadSecurityConfig(loaded) };\n } catch (error) {\n if (!(error instanceof BrokerConfigError)) throw error;\n process.stderr.write(`${error.message}\n`);\n process.exit(1);\n }\n}\n\nconst { config, baseDir, security } = loadConfigOrExit();\nconst cwd = process.cwd();\n\n/**\n * The `auth` block in force: the security file's when there is one (the\n * loader refused a `config.json` that carries one too), `config.json`'s\n * otherwise, for deployments that have not moved it yet.\n */\nconst authConfig = security?.security.auth ?? config.auth;\n\n/**\n * Fills an env var from the config file when the env var is not already set.\n * Used for **non-path** scalars, path-bearing fields are read directly so\n * they can be resolved against `baseDir` (config file's directory) instead\n * of `cwd` (deploy environment).\n */\nfunction envFromConfig(envName: string, configValue: string | number | boolean | undefined): void {\n if (configValue === undefined || configValue === null) return;\n if (process.env[envName] !== undefined && process.env[envName] !== \"\") return;\n process.env[envName] = String(configValue);\n}\n\nenvFromConfig(\"MCP_BROKER_PORT\", config.port);\nenvFromConfig(\"MCP_BROKER_HOST\", config.host);\nenvFromConfig(\"MCP_BROKER_PROTOCOL\", config.protocol);\nenvFromConfig(\"MCP_BROKER_LOCALE\", config.locale);\nenvFromConfig(\"MCP_BROKER_STDIO_PROVIDER\", config.stdioProvider);\nenvFromConfig(\"MCP_BROKER_PROVIDER_PATH\", config.paths?.provider);\nenvFromConfig(\"MCP_BROKER_PROVIDERS_PATH\", config.paths?.providers);\nenvFromConfig(\"MCP_BROKER_CLIENT_PATH\", config.paths?.client);\nenvFromConfig(\"MCP_BROKER_MCP_PATH\", config.paths?.mcp);\nenvFromConfig(\"MCP_BROKER_SSE_PATH\", config.paths?.sse);\nenvFromConfig(\"MCP_BROKER_MESSAGES_PATH\", config.paths?.messages);\nenvFromConfig(\"MCP_BROKER_PROVIDER_HEARTBEAT_MS\", config.providerHeartbeatIntervalMs);\nenvFromConfig(\"MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS\", config.providerRequestTimeoutMs);\nenvFromConfig(\"MCP_BROKER_MAX_SUBSCRIPTIONS_PER_CLIENT\", config.resourceSubscriptions?.maxSubscriptionsPerClient);\nenvFromConfig(\"MCP_BROKER_MAX_SUBSCRIPTIONS_PER_SLOT\", config.resourceSubscriptions?.maxSubscriptionsPerSlot);\nenvFromConfig(\"MCP_BROKER_MAX_RESOURCE_URI_LENGTH\", config.resourceSubscriptions?.maxResourceUriLength);\nenvFromConfig(\"MCP_BROKER_PROVIDER_TAKEOVER\", config.providerTakeover);\n// `www.open` is `boolean | string`: `true` means the root, a string is a path\n// or a same-origin URL. Both travel as the env string and are resolved once,\n// by `resolveOpenTarget`, so the file and the env var cannot diverge.\nenvFromConfig(\"MCP_BROKER_OPEN\", config.www?.open === true ? \"1\" : typeof config.www?.open === \"string\" ? config.www.open : undefined);\nenvFromConfig(\"MCP_BROKER_AUTH_ENABLED\", authConfig?.enabled === true ? \"1\" : undefined);\nenvFromConfig(\"MCP_BROKER_PUBLIC_BASE_URL\", authConfig?.publicBaseUrl);\nenvFromConfig(\"MCP_BROKER_JWKS\", authConfig?.jwks);\nenvFromConfig(\"MCP_BROKER_ISSUER\", authConfig?.issuer);\nenvFromConfig(\"MCP_BROKER_PROVIDER_SECRET\", authConfig?.providerSecret);\n\nconst stdioProvider = process.env[\"MCP_BROKER_STDIO_PROVIDER\"];\n\n// In stdio mode stdout is reserved for JSON-RPC, redirect all console output to stderr.\nif (stdioProvider) {\n const toStderr = (...args: unknown[]) => process.stderr.write(args.join(\" \") + \"\\n\");\n console.log = toStderr;\n console.info = toStderr;\n console.warn = toStderr;\n console.error = toStderr;\n}\n\nconst port = parseInt(process.env[\"MCP_BROKER_PORT\"] ?? \"3000\", 10);\nconst host = process.env[\"MCP_BROKER_HOST\"];\n// ── Endpoint paths ───────────────────────────────────────────────────────────\n// All six resolve the same way (env, then config file, then the default) and all\n// six are handed to the builder. Before this, `paths.providers` and\n// `paths.messages` were read nowhere and `paths.sse` reached only the banner, so\n// setting one advertised an endpoint the router did not serve.\nconst providerPath = process.env[\"MCP_BROKER_PROVIDER_PATH\"] ?? \"/provider\";\nconst providersPath = process.env[\"MCP_BROKER_PROVIDERS_PATH\"] ?? \"/providers\";\nconst clientPath = process.env[\"MCP_BROKER_CLIENT_PATH\"] ?? \"/\";\nconst mcpPath = process.env[\"MCP_BROKER_MCP_PATH\"] ?? \"/mcp\";\nconst ssePath = process.env[\"MCP_BROKER_SSE_PATH\"] ?? \"/sse\";\nconst messagesPath = process.env[\"MCP_BROKER_MESSAGES_PATH\"] ?? \"/messages\";\n\n// Provider telemetry uses the standard OpenTelemetry deployment variables so\n// the same collector configuration works in Docker, Kubernetes and Windows.\nconst otlpTracesEndpoint = process.env[\"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT\"] ?? config.telemetry?.otlpHttpEndpoint;\n\nfunction otlpHeadersFromEnv(raw: string | undefined): Record<string, string> {\n const headers: Record<string, string> = {};\n if (!raw) return headers;\n for (const field of raw.split(\",\")) {\n const separator = field.indexOf(\"=\");\n if (separator <= 0) {\n console.warn(`[mcp-broker] Ignoring malformed OTEL_EXPORTER_OTLP_HEADERS field: ${JSON.stringify(field)}.`);\n continue;\n }\n try {\n const key = decodeURIComponent(field.slice(0, separator).trim());\n const value = decodeURIComponent(field.slice(separator + 1).trim());\n if (key) headers[key] = value;\n } catch {\n console.warn(`[mcp-broker] Ignoring malformed percent encoding in OTEL_EXPORTER_OTLP_HEADERS field: ${JSON.stringify(field)}.`);\n }\n }\n return headers;\n}\n\nconst otlpHeaders = { ...(config.telemetry?.headers ?? {}), ...otlpHeadersFromEnv(process.env[\"OTEL_EXPORTER_OTLP_HEADERS\"]) };\n\n// ── TLS material ─────────────────────────────────────────────────────────────\n// Env var (relative to cwd) wins over config (relative to baseDir).\nconst tlsCertPath = process.env[\"MCP_BROKER_TLS_CERT\"] ? path.resolve(cwd, process.env[\"MCP_BROKER_TLS_CERT\"]) : config.tls?.cert ? path.resolve(baseDir, config.tls.cert) : null;\nconst tlsKeyPath = process.env[\"MCP_BROKER_TLS_KEY\"] ? path.resolve(cwd, process.env[\"MCP_BROKER_TLS_KEY\"]) : config.tls?.key ? path.resolve(baseDir, config.tls.key) : null;\n\nconst protocolOverride = process.env[\"MCP_BROKER_PROTOCOL\"]?.toLowerCase();\nif (protocolOverride !== undefined && protocolOverride !== \"http\" && protocolOverride !== \"https\") {\n console.error(`[mcp-broker] Invalid MCP_BROKER_PROTOCOL=\"${protocolOverride}\". Use \"http\" or \"https\".`);\n process.exit(1);\n}\nif (protocolOverride === \"https\" && (!tlsCertPath || !tlsKeyPath)) {\n console.error(\"[mcp-broker] MCP_BROKER_PROTOCOL=https requires TLS cert+key (via config.tls or MCP_BROKER_TLS_CERT/KEY).\");\n process.exit(1);\n}\n\nconst useTls = protocolOverride === \"http\" ? false : protocolOverride === \"https\" ? true : !!(tlsCertPath && tlsKeyPath);\n\n// ── Provider liveness knobs ──────────────────────────────────────────────────\n// The defaults are correct for nearly everyone; these exist for the operator who\n// has to tune them (long-running tools, a flaky link). An unusable value is\n// reported and ignored rather than silently coerced, so nobody believes a\n// setting is in force when it is not.\n\n/**\n * Parses a millisecond count from an env var. Returns `undefined` (keep the\n * built-in default) when unset or unusable, after saying why.\n */\nfunction millisFromEnv(envName: string): number | undefined {\n const raw = process.env[envName];\n if (raw === undefined || raw.trim() === \"\") return undefined;\n const value = Number(raw);\n if (!Number.isFinite(value) || value < 0) {\n console.warn(`[mcp-broker] Ignoring ${envName}=\"${raw}\": expected a whole number of milliseconds (0 disables). Using the default.`);\n return undefined;\n }\n return Math.floor(value);\n}\n\nconst providerHeartbeatIntervalMs = millisFromEnv(\"MCP_BROKER_PROVIDER_HEARTBEAT_MS\");\nconst providerRequestTimeoutMs = millisFromEnv(\"MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS\");\n\n/** Reads a positive whole number from the environment, or `undefined` (with a warning) for anything else. */\nfunction countFromEnv(envName: string): number | undefined {\n const raw = process.env[envName];\n if (raw === undefined || raw.trim() === \"\") return undefined;\n const value = Number(raw);\n if (!Number.isInteger(value) || value < 1) {\n console.warn(`[mcp-broker] Ignoring ${envName}=\"${raw}\": expected a whole number of at least 1. Using the default.`);\n return undefined;\n }\n return value;\n}\n\nconst resourceSubscriptionLimits = {\n maxSubscriptionsPerClient: countFromEnv(\"MCP_BROKER_MAX_SUBSCRIPTIONS_PER_CLIENT\"),\n maxSubscriptionsPerSlot: countFromEnv(\"MCP_BROKER_MAX_SUBSCRIPTIONS_PER_SLOT\"),\n maxResourceUriLength: countFromEnv(\"MCP_BROKER_MAX_RESOURCE_URI_LENGTH\"),\n};\n\nconst takeoverRaw = process.env[\"MCP_BROKER_PROVIDER_TAKEOVER\"]?.trim().toLowerCase();\nlet providerTakeover: ProviderTakeoverMode | undefined;\nif (takeoverRaw) {\n if (takeoverRaw === \"reject\" || takeoverRaw === \"liveness\" || takeoverRaw === \"always\") {\n providerTakeover = takeoverRaw;\n } else {\n console.warn(`[mcp-broker] Ignoring MCP_BROKER_PROVIDER_TAKEOVER=\"${takeoverRaw}\": expected \"reject\", \"liveness\" or \"always\". Using \"liveness\".`);\n }\n}\n\n// ── Allowed browser origins for the client HTTP surface ──────────────────────\n// Absent leaves the endpoint closed to browsers, which is the safe default.\n// The env var only carries the list form; a pattern needs the config file,\n// since a regular expression cannot survive comma-splitting.\nconst envOrigins = process.env[\"MCP_BROKER_ALLOWED_ORIGINS\"]\n ?.split(\",\")\n .map((origin) => origin.trim())\n .filter(Boolean);\n\nimport { compileRegex } from \"./authority/resource.limits\";\n\nlet allowedOrigins: readonly string[] | { readonly pattern: string; readonly flags?: string } | undefined;\nif (envOrigins?.length) {\n allowedOrigins = envOrigins;\n} else if (Array.isArray(config.allowedOrigins)) {\n allowedOrigins = config.allowedOrigins;\n} else if (config.allowedOrigins) {\n try {\n compileRegex(config.allowedOrigins.pattern, config.allowedOrigins.flags);\n allowedOrigins = config.allowedOrigins;\n } catch (err) {\n // Invalid security expressions refuse startup.\n console.error(`[mcp-broker] Cannot use allowedOrigins.pattern: ${(err as Error).message}`);\n process.exit(1);\n }\n}\n\n/**\n * One-line rendering of the resolved origin policy for the startup banner.\n *\n * Worth a banner row because the closed default is invisible otherwise: a page\n * served by this very broker still gets a 403 on `/<slot>/mcp` unless its origin\n * is listed, and nothing else says so until the request fails.\n */\nfunction describeAllowedOrigins(): string {\n if (allowedOrigins && \"pattern\" in allowedOrigins) return `pattern /${allowedOrigins.pattern}/${allowedOrigins.flags ?? \"\"} (RE2)`;\n if (allowedOrigins && allowedOrigins.length > 0) return allowedOrigins.join(\", \");\n return \"none (set MCP_BROKER_ALLOWED_ORIGINS, or allowedOrigins in the config file). Requests carrying no Origin header (Claude Desktop, MCP Inspector, any server-side SDK) still pass\";\n}\n\n// ── Env-var static mount shortcuts (relative to cwd) ─────────────────────────\nconst envWwwDir = process.env[\"MCP_BROKER_WWW_DIR\"] ? path.resolve(cwd, process.env[\"MCP_BROKER_WWW_DIR\"]) : null;\nconst envBundleDir = process.env[\"MCP_BROKER_BUNDLE_DIR\"] ? path.resolve(cwd, process.env[\"MCP_BROKER_BUNDLE_DIR\"]) : null;\n\n/**\n * Warns when the stdio bridge is pinned to a slot this broker does not host.\n *\n * The bridge gates every frame, `initialize` included, on the slot being\n * occupied, so an MCP host that starts before the slot is claimed fails the\n * handshake and gives up: with a browser-hosted provider, which cannot be\n * connected before the host launches, this fails every single time. Naming the\n * slots the broker does have turns that into a one-line fix.\n *\n * Deliberately a warning, not an exit: a WebSocket provider may legitimately\n * claim the slot later, and a broker that refuses to start would be worse.\n */\nfunction warnIfStdioTargetUnknown(target: string): void {\n const known = new Set<string>([\n BROKER_AGGREGATE_NAME,\n BROKER_PROVIDER_NAME,\n ...(config.stdioUpstreams ?? []).map((u) => u.name),\n ...(config.mcpServers ?? []).map((s) => s.name),\n ...(config.mcpbBundles ?? []).map((b) => b.name),\n ]);\n if (known.has(target)) return;\n\n const hosted = [...known].filter((name) => name !== BROKER_AGGREGATE_NAME && name !== BROKER_PROVIDER_NAME);\n console.warn(\n `[mcp-broker] MCP_BROKER_STDIO_PROVIDER=\"${target}\" names a slot this broker does not host. ` +\n `Nothing answers on it until a WebSocket provider connects to ${providerPath}/${target} (or announces \"${target}\" on ${providersPath}), ` +\n `and until then every request from the MCP host, including the initial handshake, fails with 'Provider \"${target}\" not connected'. ` +\n `An MCP host that starts before that provider is up, which is always the case for a provider hosted in a browser page, will therefore never connect. ` +\n `Prefer MCP_BROKER_STDIO_PROVIDER=\"${BROKER_AGGREGATE_NAME}\": it exists from startup, answers the handshake itself, unions every opted-in provider, ` +\n `and pushes notifications/tools/list_changed as providers join, so a page opened later appears live. ` +\n `Slots this broker hosts right now: ${hosted.length > 0 ? hosted.map((n) => `\"${n}\"`).join(\", \") + `, plus the reserved \"${BROKER_AGGREGATE_NAME}\" and \"${BROKER_PROVIDER_NAME}\"` : `the reserved \"${BROKER_AGGREGATE_NAME}\" and \"${BROKER_PROVIDER_NAME}\" only`}.`\n );\n}\n\n/** Keeps a collector outage from producing one CLI line per failed batch. */\nfunction telemetryExportLogger(intervalMs = 60_000): {\n readonly error: (error: unknown) => void;\n readonly success: (recordCount: number) => void;\n} {\n let failures = 0;\n let suppressed = 0;\n let lastReport = 0;\n return {\n error(error: unknown): void {\n failures++;\n const now = Date.now();\n if (lastReport === 0 || now - lastReport >= intervalMs) {\n const suffix = suppressed > 0 ? ` (${suppressed} repeated failure(s) suppressed)` : \"\";\n console.error(`[mcp-broker] telemetry export failed: ${(error as Error).message}${suffix}`);\n lastReport = now;\n suppressed = 0;\n } else {\n suppressed++;\n }\n },\n success(recordCount: number): void {\n if (failures === 0) return;\n console.info(\n `[mcp-broker] telemetry export recovered after ${failures} failed batch(es)` +\n `${suppressed > 0 ? `, ${suppressed} repeated log line(s) suppressed` : \"\"}; exported ${recordCount} span(s).`\n );\n failures = 0;\n suppressed = 0;\n lastReport = 0;\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Server bootstrap\n// ---------------------------------------------------------------------------\n\nasync function main(): Promise<void> {\n // Local grammar overrides: when `<baseDir>/grammars/` exists, the broker\n // server merges those JSON files on top of the packaged grammars.\n const localGrammarsDir = path.join(baseDir, \"grammars\");\n const hasLocalGrammars = fs.existsSync(localGrammarsDir);\n\n const builder = new WsTunnelBuilder()\n .withPort(port)\n .withProviderPath(providerPath)\n .withProvidersPath(providersPath)\n .withClientPath(clientPath)\n .withMcpPath(mcpPath)\n .withSsePath(ssePath)\n .withMessagesPath(messagesPath);\n\n if (host) {\n builder.withHost(host);\n }\n\n if (providerHeartbeatIntervalMs !== undefined) {\n builder.withProviderHeartbeat(providerHeartbeatIntervalMs);\n }\n if (providerRequestTimeoutMs !== undefined) {\n builder.withProviderRequestTimeout(providerRequestTimeoutMs);\n }\n // Only the limits actually set: an `undefined` field would override the default.\n const limits = Object.fromEntries(Object.entries(resourceSubscriptionLimits).filter(([, v]) => v !== undefined));\n if (Object.keys(limits).length > 0) {\n builder.withResourceSubscriptionLimits(limits);\n }\n if (providerTakeover) {\n builder.withProviderTakeover(providerTakeover);\n }\n if (otlpTracesEndpoint) {\n const exportLog = telemetryExportLogger();\n builder.withOtlpHttpTelemetry(\n {\n endpoint: otlpTracesEndpoint,\n headers: otlpHeaders,\n timeoutMs: config.telemetry?.timeoutMs,\n serviceNamespace: config.telemetry?.serviceNamespace,\n },\n {\n maxFrameBytes: config.telemetry?.maxFrameBytes,\n queueCapacity: config.telemetry?.queueCapacity,\n batchSize: config.telemetry?.batchSize,\n maxAttributes: config.telemetry?.maxAttributes,\n maxEvents: config.telemetry?.maxEvents,\n onExportError: exportLog.error,\n onExportSuccess: exportLog.success,\n }\n );\n }\n\n if (useTls) {\n // `withTlsFiles` reads both files synchronously and would otherwise\n // surface a bare ENOENT with no hint that TLS was on because a config\n // file mentioned it. That is the first wall a copied config template\n // hits, since the template ships `tls.cert`/`tls.key` but no `certs/`.\n try {\n builder.withTlsFiles(tlsCertPath!, tlsKeyPath!);\n } catch (err) {\n throw new Error(\n `TLS is enabled but the material could not be read: ${(err as Error).message}. ` +\n `Certificate: ${tlsCertPath}, key: ${tlsKeyPath}. ` +\n `Config-file paths are resolved against the config file's own directory, env-var paths against the current directory. ` +\n `Generate a development pair, point tls.cert/tls.key (or MCP_BROKER_TLS_CERT/MCP_BROKER_TLS_KEY) at real files, ` +\n `or run without TLS by removing the tls block and setting MCP_BROKER_PROTOCOL=http.`,\n { cause: err }\n );\n }\n }\n\n if (allowedOrigins) {\n builder.withAllowedOrigins(allowedOrigins);\n }\n\n // ── Static mounts ────────────────────────────────────────────────────────\n // First the env-var shortcuts (relative to cwd), then the config `www.mounts`\n // (relative to baseDir). Order matters: /bundle before / so the prefix router\n // can distinguish them. Subsequent registrations contribute additional mounts;\n // longest-prefix match wins at runtime.\n // Every prefix that actually got mounted, in registration order. The\n // browser-open check below tests the resolved path against this list rather\n // than against `config.www.mounts`, which would miss both env shortcuts and\n // would false-warn on a perfectly valid `open: \"/bundle/\"`.\n const mountedPrefixes: string[] = [];\n const mount = (urlPrefix: string, dir: string): void => {\n builder.withStaticMount(urlPrefix, dir);\n mountedPrefixes.push(urlPrefix);\n };\n\n if (envBundleDir && fs.existsSync(envBundleDir)) {\n mount(\"/bundle\", envBundleDir);\n }\n if (envWwwDir && fs.existsSync(envWwwDir)) {\n mount(\"/\", envWwwDir);\n }\n if (config.www?.mounts) {\n for (const entry of config.www.mounts) {\n const abs = path.resolve(baseDir, entry.dir);\n if (fs.existsSync(abs)) {\n mount(entry.urlPrefix, abs);\n } else {\n console.warn(`[mcp-broker] www.mounts entry \"${entry.urlPrefix}\" → ${abs} skipped (directory not found).`);\n }\n }\n }\n\n // ── Stdio upstreams ──────────────────────────────────────────────────────\n if (config.stdioUpstreams) {\n for (const u of config.stdioUpstreams) {\n builder.withStdioUpstream({\n name: u.name,\n command: u.command,\n args: u.args,\n env: u.env as NodeJS.ProcessEnv | undefined,\n aggregate: u.aggregate,\n });\n }\n }\n\n // ── Remote MCP server upstreams (reached by URL) ─────────────────────────\n // Config-discovered remote servers join the `_all` aggregate by default;\n // an explicit `aggregate: false` opts an entry out.\n if (config.mcpServers) {\n for (const s of config.mcpServers) {\n builder.withRemoteUpstream({ ...s, aggregate: s.aggregate ?? true });\n }\n }\n\n // ── Local `.mcpb` bundles ────────────────────────────────────────────────\n // Each bundle is signature-verified and unpacked before it is wired in as a\n // stdio upstream. A refused bundle is skipped (loadMcpbBundle logs why); it\n // never spawns a process. Bundles join `_all` by default (opt-out per entry).\n if (config.mcpbBundles) {\n for (const b of config.mcpbBundles) {\n const upstream = await loadMcpbBundle(b, baseDir);\n if (upstream) {\n builder.withStdioUpstream(upstream);\n }\n }\n }\n\n if (stdioProvider) {\n builder.withStdioClient(stdioProvider);\n warnIfStdioTargetUnknown(stdioProvider);\n }\n\n if (hasLocalGrammars) {\n builder.withBrokerLocalGrammarsDir(localGrammarsDir);\n }\n\n // ── Authorization (OAuth 2.1 resource server) ────────────────────────────\n // Opt-in: only wired when MCP_BROKER_AUTH_ENABLED / config.auth.enabled is on.\n // Env scalars win over config; array/object fields are read from config.\n const authEnabledEnv = process.env[\"MCP_BROKER_AUTH_ENABLED\"];\n const authEnabled = authEnabledEnv === \"1\" || authEnabledEnv === \"true\";\n if (authEnabled) {\n const publicBaseUrl = process.env[\"MCP_BROKER_PUBLIC_BASE_URL\"];\n const jwks = process.env[\"MCP_BROKER_JWKS\"];\n const issuer = process.env[\"MCP_BROKER_ISSUER\"];\n const authorizationServers = authConfig?.authorizationServers ?? (issuer ? [issuer] : []);\n if (!publicBaseUrl || !jwks || authorizationServers.length === 0) {\n console.error(\n \"[mcp-broker] auth.enabled requires publicBaseUrl, jwks, and at least one \" +\n \"authorizationServers entry (or issuer). Set them via config.auth or \" +\n \"MCP_BROKER_PUBLIC_BASE_URL / MCP_BROKER_JWKS / MCP_BROKER_ISSUER.\"\n );\n process.exit(1);\n }\n builder.withJwtAuth({\n publicBaseUrl,\n authorizationServers,\n jwksUri: jwks,\n issuer,\n scopesSupported: authConfig?.scopesSupported,\n requiredScopes: authConfig?.requiredScopes,\n perSlotScopes: authConfig?.perSlotScopes,\n providerScopes: authConfig?.providerScopes,\n subjectMapping: authConfig?.subjectMapping,\n roles: authConfig?.roles,\n assignments: authConfig?.assignments,\n denies: authConfig?.denies,\n slotResources: authConfig?.slotResources,\n toolCapabilities: authConfig?.toolCapabilities,\n providerToolCapabilities: authConfig?.providerToolCapabilities,\n audit: authConfig?.audit,\n });\n }\n\n // ── Development authorization (static tokens, loopback and named networks) ─\n // The security file's `auth.dev`: the same policy as OAuth, with tokens\n // read from the environment. Exclusive with OAuth; refused in config.json,\n // where the loader would not resolve or check it.\n const devCallers = security?.devCallers ?? [];\n if (config.auth?.dev !== undefined) {\n console.error('[mcp-broker] \"auth.dev\" belongs in the security file (securityFile / MCP_BROKER_SECURITY_FILE), not in the config file.');\n process.exit(1);\n }\n if (devCallers.length > 0) {\n if (authEnabled) {\n console.error(\"[mcp-broker] auth.dev and OAuth (MCP_BROKER_AUTH_ENABLED / auth.enabled) are exclusive: development tokens or OAuth, not both.\");\n process.exit(1);\n }\n builder.withDevAuth({\n callers: devCallers,\n networks: security?.devNetworks,\n publicBaseUrl: process.env[\"MCP_BROKER_PUBLIC_BASE_URL\"] ?? `${useTls ? \"https\" : \"http\"}://localhost:${port}`,\n scopesSupported: authConfig?.scopesSupported,\n requiredScopes: authConfig?.requiredScopes,\n perSlotScopes: authConfig?.perSlotScopes,\n roles: authConfig?.roles,\n assignments: authConfig?.assignments,\n denies: authConfig?.denies,\n slotResources: authConfig?.slotResources,\n toolCapabilities: authConfig?.toolCapabilities,\n providerToolCapabilities: authConfig?.providerToolCapabilities,\n audit: authConfig?.audit,\n });\n }\n\n // ── Provider authentication (independent of client OAuth) ────────────────\n // Requires every provider connecting to /provider/<slot> or /providers to\n // present the shared secret, closes off slot occupation by strangers.\n const providerSecret = process.env[\"MCP_BROKER_PROVIDER_SECRET\"];\n if (providerSecret) {\n builder.withProviderSecret(providerSecret);\n }\n\n // `withProviderPrincipals()` and `build()` refuse a security setup they\n // could not enforce (two providers sharing a secret, a protected slot with\n // no provider identities, ...): say so and stop, rather than crash.\n let tunnel: ReturnType<typeof builder.build>;\n try {\n // ── Security file: one identity per provider, protected slots ────────\n if (security) {\n if (security.credentials.length > 0) builder.withProviderPrincipals(security.credentials);\n const resourceLimits = security.security.authorization?.resourceLimits;\n if (resourceLimits) builder.withResourceLimits(resourceLimits);\n const protectedSlots = security.security.authorization?.protectedSlots;\n if (protectedSlots && Object.keys(protectedSlots).length > 0) builder.withProtectedSlots(protectedSlots);\n if (security.security.limits) builder.withLimits(security.security.limits);\n builder.withSecurityVersion(security.version);\n }\n tunnel = builder.build();\n } catch (error) {\n console.error(`[mcp-broker] ${(error as Error).message}`);\n process.exit(1);\n }\n await tunnel.start();\n let plugins: Awaited<ReturnType<typeof startBrokerPlugins>>;\n try {\n const pluginsDirectory = process.env[\"MCP_BROKER_PLUGINS_DIR\"] ?? config.pluginsDirectory;\n if (pluginsDirectory !== undefined && typeof pluginsDirectory !== \"string\") throw new Error(\"pluginsDirectory must be a string\");\n plugins = await startBrokerPlugins(config.plugins === undefined ? [] : config.plugins, {\n tunnel,\n baseDir,\n moduleDirectory: pluginsDirectory ? path.resolve(baseDir, pluginsDirectory) : baseDir,\n principals: new Map(\n (security?.credentials ?? []).map((credential) => [\n credential.id,\n {\n id: credential.id,\n subjects: credential.subjects,\n allowedResources: credential.allowedResources,\n },\n ])\n ),\n });\n } catch (error) {\n await tunnel.stop();\n throw error;\n }\n\n // ── Startup banner ──────────────────────────────────────────────────────\n const httpScheme = useTls ? \"https\" : \"http\";\n const wsScheme = useTls ? \"wss\" : \"ws\";\n const hr = \"─\".repeat(64);\n const localhost = `${httpScheme}://localhost:${port}`;\n const mcpSuffix = mcpPath.replace(/^\\//, \"\");\n const sseSuffix = ssePath.replace(/^\\//, \"\");\n const messagesSuffix = messagesPath.replace(/^\\//, \"\");\n\n // The two provider rows are the point of the banner: the endpoint chosen\n // decides the framing, and connecting the wrong transport to either one is\n // the single most common way an integration fails. So each row names the\n // transport class it requires, right there.\n console.log();\n console.log(`⚙️ mcp-broker started${useTls ? \" (TLS)\" : \"\"}`);\n console.log(hr);\n console.log(`📡 Provider WS direct ${wsScheme}://localhost:${port}${providerPath}/<name>`);\n console.log(` one provider per socket, plain JSON-RPC frames: use DirectTransport`);\n console.log(`🧵 Provider WS multiplex ${wsScheme}://localhost:${port}${providersPath}`);\n console.log(` many providers per socket, envelope frames: use MultiplexTransport`);\n console.log(`🔌 MCP (Streamable HTTP) ${localhost}/<name>/${mcpSuffix}`);\n console.log(`📺 Legacy SSE ${localhost}/<name>/${sseSuffix} + POST ${localhost}/<name>/${messagesSuffix}`);\n // Raw WS clients are a real, configurable route. Left off the banner it read\n // as provider-only, and `clientPath` reached the router and nothing else,\n // which is the same defect `paths.sse` had.\n console.log(`🔗 Client WS (raw) ${wsScheme}://localhost:${port}${clientPath === \"/\" ? \"/\" : clientPath}<name>`);\n console.log(` bare JSON-RPC frames, no session layer; not origin-checked`);\n console.log(`🧩 Reserved slots ${BROKER_AGGREGATE_NAME}: live aggregate of every opted-in provider`);\n console.log(` ${BROKER_PROVIDER_NAME}: broker_info, providers_list, provider_status, broker_guide, broker_diagnose`);\n if (mountedPrefixes.length > 0) {\n console.log(`📁 Static mounts ${mountedPrefixes.map((prefix) => `${localhost}${prefix === \"/\" ? \"/\" : prefix + \"/\"}`).join(\" \")}`);\n }\n if (hasLocalGrammars) {\n console.log(`🌐 Local grammars ${localGrammarsDir}`);\n }\n console.log(\n `🔐 Authorization ${\n authEnabled\n ? \"OAuth 2.1 (Bearer required)\"\n : devCallers.length > 0\n ? `DEVELOPMENT static tokens (${devCallers.map((c) => c.id).join(\", \")}), clients from loopback${security?.devNetworks.length ? ` and ${security.devNetworks.join(\", \")}` : \" only\"}`\n : \"disabled (trusted network only)\"\n }`\n );\n const providerIdentities = security?.credentials.length ?? 0;\n console.log(\n `🛡️ Provider auth ${providerIdentities > 0 ? `${providerIdentities} provider identit${providerIdentities === 1 ? \"y\" : \"ies\"}${providerSecret ? \" + shared secret\" : \"\"}` : providerSecret ? \"shared secret required\" : \"disabled\"}`\n );\n if (security) {\n const protectedCount = Object.keys(security.security.authorization?.protectedSlots ?? {}).length;\n console.log(`🔏 Security file ${security.sourcePath} (version ${security.version}${protectedCount > 0 ? `, ${protectedCount} protected slot(s)` : \"\"})`);\n }\n console.log(`🌍 Browser origins ${describeAllowedOrigins()}`);\n console.log(` enforced on /<name>/${mcpSuffix}, /<name>/${sseSuffix} and /<name>/${messagesSuffix}`);\n if (!allowedOrigins) {\n console.log(` a page this broker serves is refused too, list its origin to admit it`);\n }\n console.log(`📈 Provider telemetry ${otlpTracesEndpoint ? `OTLP/HTTP to ${otlpTracesEndpoint}` : \"disabled\"}`);\n console.log(hr);\n console.log(` New here? Call broker_guide on the ${BROKER_PROVIDER_NAME} slot: ${localhost}/${BROKER_PROVIDER_NAME}/${mcpSuffix}`);\n console.log(` Press Ctrl+C to stop.`);\n console.log();\n\n // ── Auto-launch the browser ─────────────────────────────────────────────\n // Opt-in through MCP_BROKER_OPEN / www.open, and only onto a path a mount\n // actually serves: launching a browser at a 404 is worse than not launching.\n const openTarget = resolveOpenTarget(process.env[\"MCP_BROKER_OPEN\"], localhost);\n if (openTarget.error) {\n console.warn(`[mcp-broker] ${openTarget.error}`);\n } else if (openTarget.url && openTarget.path) {\n const openUrl = openTarget.url;\n const openPath = openTarget.path;\n const covered = mountedPrefixes.some((prefix) => prefix === \"/\" || openPath === prefix || openPath.startsWith(prefix.endsWith(\"/\") ? prefix : prefix + \"/\"));\n if (!covered) {\n console.warn(\n `[mcp-broker] Not opening ${openUrl}: no static mount serves \"${openPath}\", so it would 404. ` +\n (mountedPrefixes.length > 0\n ? `Mounted prefixes: ${mountedPrefixes.map((p) => `\"${p}\"`).join(\", \")}.`\n : `No directory is mounted. Add one with MCP_BROKER_WWW_DIR=<dir> (serves it at \"/\") or a www.mounts entry in the config file.`)\n );\n } else {\n console.log(`🚀 Opening browser: ${openUrl}`);\n console.log();\n try {\n await open(openUrl);\n } catch (err) {\n // The broker is listening and healthy; failing to spawn a browser\n // (a headless box, no default handler) must not take it down.\n console.warn(`[mcp-broker] Could not launch a browser for ${openUrl}: ${(err as Error).message}. The broker is running; open the URL yourself.`);\n }\n }\n }\n\n // ── Signal handlers ─────────────────────────────────────────────────────\n let shuttingDown = false;\n const shutdown = async (signal: string): Promise<void> => {\n if (shuttingDown) return;\n shuttingDown = true;\n console.log(`\\n⛔ ${signal} received, shutting down…`);\n try {\n await plugins.stop();\n } finally {\n await tunnel.stop();\n }\n process.exit(0);\n };\n\n const shutdownFailed = (error: unknown): void => {\n console.error(`[mcp-broker] shutdown failed: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(1);\n };\n process.on(\"SIGINT\", () => void shutdown(\"SIGINT\").catch(shutdownFailed));\n process.on(\"SIGTERM\", () => void shutdown(\"SIGTERM\").catch(shutdownFailed));\n}\n\nmain().catch((err: unknown) => {\n // `tunnel.start()` now rejects on a listen failure instead of dying as an\n // uncaught EventEmitter error, so this path is live where it used to be\n // unreachable, and the rejection carries a fully worded diagnosis (for\n // EADDRINUSE: the address, the URL to attach to the broker that already\n // holds the port, and the MCP_BROKER_PORT escape). Print the sentence, not\n // the object: `console.error(\"…\", err)` renders a stack dump and buries it.\n //\n // In stdio mode console is redirected to stderr above, so this cannot\n // corrupt the JSON-RPC stream on stdout.\n const message = err instanceof Error ? err.message : String(err);\n console.error(`[mcp-broker] Cannot start: ${message}`);\n\n // A wrapped cause keeps the original errno for anyone reading a host log.\n const cause = err instanceof Error ? err.cause : undefined;\n if (cause instanceof Error) {\n const code = (cause as NodeJS.ErrnoException).code;\n console.error(`[mcp-broker] Underlying error: ${code ? code + \" \" : \"\"}${cause.message}`);\n }\n\n // A bare Error with no diagnosis of its own is still worth grounding: say\n // where the broker was trying to listen, since that is the one fact the\n // reader needs to check next.\n if (!(err instanceof Error) || !message.includes(String(port))) {\n console.error(\n `[mcp-broker] Configuration in effect: host=${host ?? \"0.0.0.0\"} port=${port}. Set MCP_BROKER_PORT / MCP_BROKER_HOST, or \"port\" / \"host\" in .mcp-broker/config.json.`\n );\n }\n\n process.exit(1);\n});\n"]}
|
|
@@ -3060,6 +3060,12 @@ var GUIDE_DEPLOY = [
|
|
|
3060
3060
|
"dials out over Streamable HTTP, SSE or WebSocket and republishes each as a slot.",
|
|
3061
3061
|
"Aggregated into `_all` by default.",
|
|
3062
3062
|
"",
|
|
3063
|
+
"**Deployment plugins.** `plugins[]` loads explicitly configured local ESM modules",
|
|
3064
|
+
"after the broker starts, using provider identities from the security file.",
|
|
3065
|
+
"Required plugin failures roll startup back; plugins stop before the tunnel.",
|
|
3066
|
+
"Plugin packages are supplied by the deployment, never downloaded at startup.",
|
|
3067
|
+
"They run as trusted code in the broker process. See docs/plugins.md.",
|
|
3068
|
+
"",
|
|
3063
3069
|
"**Broker running signed bundles.** `mcpbBundles[]` entries: each `.mcpb` is a ZIP",
|
|
3064
3070
|
"with a manifest, signature-verified against a trusted public key before being",
|
|
3065
3071
|
"unpacked and spawned as a stdio upstream. A bundle that fails verification is",
|
|
@@ -6564,6 +6570,7 @@ var WsTunnel = class {
|
|
|
6564
6570
|
this._loopbackPrincipals.delete(name);
|
|
6565
6571
|
this._failProviderDisconnected(state, name);
|
|
6566
6572
|
};
|
|
6573
|
+
if (options.aggregate) void this._aggregateServer?.addProvider(name);
|
|
6567
6574
|
const call = (method, params) => Promise.resolve(
|
|
6568
6575
|
this._loopbackProviders.get(name) === transport ? this._brokerMethod(state, name, method, params, principal) : { error: { code: -32e3, message: `Loopback provider "${name}" is no longer registered.` } }
|
|
6569
6576
|
);
|
|
@@ -9162,5 +9169,5 @@ var WsTunnelBuilder = class {
|
|
|
9162
9169
|
};
|
|
9163
9170
|
|
|
9164
9171
|
export { AuthError, BROKER_AGGREGATE_NAME, BROKER_AUDIT_RESULT_METHOD, BROKER_AUTHORIZE_METHOD, BROKER_DECLARE_METHOD, BROKER_GUIDES, BROKER_GUIDE_MIME_TYPE, BROKER_GUIDE_TOPICS, BROKER_GUIDE_URI_PREFIX, BROKER_GUIDE_URI_TEMPLATE, BROKER_METHOD_PREFIX, BROKER_PROVIDER_NAME, BROKER_RESERVED_SLOTS, BrokerAuthority, BrokerDiagnoseAdapter, BrokerDiagnoseBehavior, BrokerGuideAdapter, BrokerGuideBehavior, BrokerInfoBehavior, BrokerProvidersBehavior, CALLER_META_KEY, CORRELATION_ID_PATTERN, ConfigPolicyEngine, ConfiguredCapabilityClassifier, DEFAULT_AUTHORIZE_BATCH_LIMIT, DEFAULT_RESOURCE_SUBSCRIPTION_LIMITS, DEV_ISSUER, DEV_LAN_NETWORKS, DEV_TOKEN_MIN_LENGTH, DefaultSlotResourceResolver, DevClientFilter, DevTokenValidator, HttpAuthGuard, JwtSubjectMapper, JwtTokenValidator, LimitController, OtlpHttpTraceExporter, PACKAGE_NAME, ProviderTableAuthenticator, ProviderTelemetryDispatcher, RemoteUpstream, ResourcePath, ResourcePathPattern, ResourceSubscriptionRegistry, SUBSCRIPTION_LIMIT_ERROR_CODE, SharedSecretProviderAuthenticator, StdioUpstream, SubjectMappingError, TELEMETRY_NOTIFICATION_METHOD, TRACEPARENT_META_KEY, VERSION, WsTunnel, WsTunnelBuilder, authorizationWithEngine, brokerGrammarKey, brokerGuide, brokerGuideIndex, brokerGuideTopicFromUri, brokerGuideUri, buildDevAuth, buildJwtAuth, buildResourceMetadata, compileAuthorizationPolicy, compileProviderAllowedResources, compileRegex, compileResourceLimits, createTraceparent, diagnoseBroker, ensureTraceparent, formatTraceparent, hasAuthorizationPolicies, isLoopbackRequest, isReservedBrokerSlot, iterAvailableBrokerGrammars, iterBrokerGrammarsFrom, loadBrokerGrammar, normalizeProviderAuthentication, parseTraceparent, providerMayPublish, providerPublishDecision, resolveDevCallers, resolveDevNetworks, scopesOf, startBrokerServer, validateCapability, validateLimitsConfig };
|
|
9165
|
-
//# sourceMappingURL=chunk-
|
|
9166
|
-
//# sourceMappingURL=chunk-
|
|
9172
|
+
//# sourceMappingURL=chunk-PMEOLA5L.js.map
|
|
9173
|
+
//# sourceMappingURL=chunk-PMEOLA5L.js.map
|