@cyanmycelium/mcp-broker 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/bin.js +3 -2
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-BTWLF6KI.js → chunk-2GDTE7BA.js} +13 -5
- package/dist/chunk-2GDTE7BA.js.map +1 -0
- package/dist/{chunk-K2EQP4RQ.js → chunk-JVXOJUWT.js} +1128 -483
- package/dist/chunk-JVXOJUWT.js.map +1 -0
- package/dist/grammars/default/en.json +7 -0
- package/dist/grammars/default/fr.json +7 -0
- package/dist/grammars/default/zh.json +7 -0
- package/dist/index.d.ts +5 -4
- 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-BYCkbtA7.d.ts → ws.tunnel.builder-CpWTI0-0.d.ts} +191 -2
- package/package.json +3 -3
- package/src/authority/broker.authority.ts +82 -4
- package/src/authority/declaration.ts +7 -1
- package/src/authorization/capability.classifier.ts +2 -0
- package/src/bin.ts +1 -0
- package/src/broker/adapters/broker.adapter.limits.ts +52 -0
- package/src/broker/behaviors/broker.behavior.limits.ts +37 -0
- package/src/broker/broker.context.ts +6 -0
- package/src/broker/broker.diagnostics.ts +45 -1
- package/src/broker/broker.guides.ts +4 -0
- package/src/broker/broker.server.ts +3 -0
- package/src/broker/grammars/default/en.json +7 -0
- package/src/broker/grammars/default/fr.json +7 -0
- package/src/broker/grammars/default/zh.json +7 -0
- package/src/config.ts +12 -2
- package/src/index.ts +11 -0
- package/src/limits/controller.ts +580 -0
- package/src/ws/ws.interfaces.ts +4 -0
- package/src/ws/ws.tunnel.builder.ts +8 -0
- package/src/ws/ws.tunnel.ts +130 -4
- package/dist/chunk-BTWLF6KI.js.map +0 -1
- package/dist/chunk-K2EQP4RQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -928,8 +928,9 @@ The package is published to npm by [`.github/workflows/release-node.yml`](https:
|
|
|
928
928
|
|
|
929
929
|
```sh
|
|
930
930
|
# from the node/ directory:
|
|
931
|
-
npm
|
|
932
|
-
git push
|
|
931
|
+
npm run bump:minor # updates the broker, commits, and creates node-v<version>
|
|
932
|
+
git push # pushes the release commit
|
|
933
|
+
git push origin node-v1.6.0 # pushes the tag explicitly and starts the workflow
|
|
933
934
|
```
|
|
934
935
|
|
|
935
936
|
The workflow runs lint, build, test, then `npm publish --access public --provenance` and creates a GitHub Release with auto-generated notes.
|
|
@@ -937,3 +938,7 @@ The workflow runs lint, build, test, then `npm publish --access public --provena
|
|
|
937
938
|
## License
|
|
938
939
|
|
|
939
940
|
Apache-2.0. See [LICENSE](LICENSE).
|
|
941
|
+
|
|
942
|
+
## Execution limits
|
|
943
|
+
|
|
944
|
+
Operator-owned call quotas and provider operation budgets: [configuration, SDK and recovery](docs/execution-limits.md).
|
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, WsTunnelBuilder, BROKER_AGGREGATE_NAME, BROKER_PROVIDER_NAME } from './chunk-
|
|
2
|
+
import { loadBrokerConfig, loadSecurityConfig, BrokerConfigError, loadMcpbBundle, resolveOpenTarget } from './chunk-2GDTE7BA.js';
|
|
3
|
+
import { PACKAGE_NAME, VERSION, WsTunnelBuilder, BROKER_AGGREGATE_NAME, BROKER_PROVIDER_NAME } from './chunk-JVXOJUWT.js';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import open from 'open';
|
|
@@ -428,6 +428,7 @@ async function main() {
|
|
|
428
428
|
if (security.credentials.length > 0) builder.withProviderPrincipals(security.credentials);
|
|
429
429
|
const protectedSlots = security.security.authorization?.protectedSlots;
|
|
430
430
|
if (protectedSlots && Object.keys(protectedSlots).length > 0) builder.withProtectedSlots(protectedSlots);
|
|
431
|
+
if (security.security.limits) builder.withLimits(security.security.limits);
|
|
431
432
|
builder.withSecurityVersion(security.version);
|
|
432
433
|
}
|
|
433
434
|
tunnel = builder.build();
|
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 ` 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\nlet allowedOrigins: readonly string[] | RegExp | 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 allowedOrigins = new RegExp(config.allowedOrigins.pattern, config.allowedOrigins.flags);\n } catch (err) {\n // Falling back to the closed default rather than starting with a rule\n // the operator believes is in force but which never compiled.\n console.error(`[mcp-broker] Ignoring allowedOrigins.pattern: ${(err as Error).message}`);\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 instanceof RegExp) return `pattern ${String(allowedOrigins)}`;\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 // ── 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 protectedSlots = security.security.authorization?.protectedSlots;\n if (protectedSlots && Object.keys(protectedSlots).length > 0) builder.withProtectedSlots(protectedSlots);\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(`🔐 Authorization ${authEnabled ? \"OAuth 2.1 (Bearer required)\" : \"disabled (trusted network only)\"}`);\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":";;;;;;;AA2EA,IAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA;AAEjC,IAAI,KAAK,QAAA,CAAS,QAAQ,KAAK,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA,EAAG;AAChD,EAAA,SAAA,EAAU;AACV,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAClB;AACA,IAAI,KAAK,QAAA,CAAS,WAAW,KAAK,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA,EAAG;AACnD,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,YAAY,IAAI,OAAO;AAAA,CAAI,CAAA;AACnD,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAClB;AAEA,SAAS,SAAA,GAAkB;AACvB,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,IACX;AAAA,EAAK,YAAY,IAAI,OAAO;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA,GA4DhC;AACJ;AAWA,SAAS,gBAAA,GAA8F;AACnG,EAAA,IAAI;AACA,IAAA,MAAM,SAAS,gBAAA,EAAiB;AAChC,IAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,QAAA,EAAU,kBAAA,CAAmB,MAAM,CAAA,EAAE;AAAA,EAC7D,SAAS,KAAA,EAAO;AACZ,IAAA,IAAI,EAAE,KAAA,YAAiB,iBAAA,CAAA,EAAoB,MAAM,KAAA;AACjD,IAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,KAAA,CAAM,OAAO;AAAA,CAC5C,CAAA;AACO,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAClB;AACJ;AAEA,IAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,KAAa,gBAAA,EAAiB;AACvD,IAAM,GAAA,GAAM,QAAQ,GAAA,EAAI;AAOxB,IAAM,UAAA,GAAa,QAAA,EAAU,QAAA,CAAS,IAAA,IAAQ,MAAA,CAAO,IAAA;AAQrD,SAAS,aAAA,CAAc,SAAiB,WAAA,EAA0D;AAC9F,EAAA,IAAI,WAAA,KAAgB,MAAA,IAAa,WAAA,KAAgB,IAAA,EAAM;AACvD,EAAA,IAAI,OAAA,CAAQ,IAAI,OAAO,CAAA,KAAM,UAAa,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA,KAAM,EAAA,EAAI;AACvE,EAAA,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA,GAAI,MAAA,CAAO,WAAW,CAAA;AAC7C;AAEA,aAAA,CAAc,iBAAA,EAAmB,OAAO,IAAI,CAAA;AAC5C,aAAA,CAAc,iBAAA,EAAmB,OAAO,IAAI,CAAA;AAC5C,aAAA,CAAc,qBAAA,EAAuB,OAAO,QAAQ,CAAA;AACpD,aAAA,CAAc,mBAAA,EAAqB,OAAO,MAAM,CAAA;AAChD,aAAA,CAAc,2BAAA,EAA6B,OAAO,aAAa,CAAA;AAC/D,aAAA,CAAc,0BAAA,EAA4B,MAAA,CAAO,KAAA,EAAO,QAAQ,CAAA;AAChE,aAAA,CAAc,2BAAA,EAA6B,MAAA,CAAO,KAAA,EAAO,SAAS,CAAA;AAClE,aAAA,CAAc,wBAAA,EAA0B,MAAA,CAAO,KAAA,EAAO,MAAM,CAAA;AAC5D,aAAA,CAAc,qBAAA,EAAuB,MAAA,CAAO,KAAA,EAAO,GAAG,CAAA;AACtD,aAAA,CAAc,qBAAA,EAAuB,MAAA,CAAO,KAAA,EAAO,GAAG,CAAA;AACtD,aAAA,CAAc,0BAAA,EAA4B,MAAA,CAAO,KAAA,EAAO,QAAQ,CAAA;AAChE,aAAA,CAAc,kCAAA,EAAoC,OAAO,2BAA2B,CAAA;AACpF,aAAA,CAAc,wCAAA,EAA0C,OAAO,wBAAwB,CAAA;AACvF,aAAA,CAAc,yCAAA,EAA2C,MAAA,CAAO,qBAAA,EAAuB,yBAAyB,CAAA;AAChH,aAAA,CAAc,uCAAA,EAAyC,MAAA,CAAO,qBAAA,EAAuB,uBAAuB,CAAA;AAC5G,aAAA,CAAc,oCAAA,EAAsC,MAAA,CAAO,qBAAA,EAAuB,oBAAoB,CAAA;AACtG,aAAA,CAAc,8BAAA,EAAgC,OAAO,gBAAgB,CAAA;AAIrE,aAAA,CAAc,iBAAA,EAAmB,MAAA,CAAO,GAAA,EAAK,IAAA,KAAS,OAAO,GAAA,GAAM,OAAO,MAAA,CAAO,GAAA,EAAK,IAAA,KAAS,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,OAAO,MAAS,CAAA;AACrI,aAAA,CAAc,yBAAA,EAA2B,UAAA,EAAY,OAAA,KAAY,IAAA,GAAO,MAAM,MAAS,CAAA;AACvF,aAAA,CAAc,4BAAA,EAA8B,YAAY,aAAa,CAAA;AACrE,aAAA,CAAc,iBAAA,EAAmB,YAAY,IAAI,CAAA;AACjD,aAAA,CAAc,mBAAA,EAAqB,YAAY,MAAM,CAAA;AACrD,aAAA,CAAc,4BAAA,EAA8B,YAAY,cAAc,CAAA;AAEtE,IAAM,aAAA,GAAgB,OAAA,CAAQ,GAAA,CAAI,2BAA2B,CAAA;AAG7D,IAAI,aAAA,EAAe;AACf,EAAA,MAAM,QAAA,GAAW,CAAA,GAAI,IAAA,KAAoB,OAAA,CAAQ,MAAA,CAAO,MAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA,GAAI,IAAI,CAAA;AACnF,EAAA,OAAA,CAAQ,GAAA,GAAM,QAAA;AACd,EAAA,OAAA,CAAQ,IAAA,GAAO,QAAA;AACf,EAAA,OAAA,CAAQ,IAAA,GAAO,QAAA;AACf,EAAA,OAAA,CAAQ,KAAA,GAAQ,QAAA;AACpB;AAEA,IAAM,OAAO,QAAA,CAAS,OAAA,CAAQ,IAAI,iBAAiB,CAAA,IAAK,QAAQ,EAAE,CAAA;AAClE,IAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA;AAM1C,IAAM,YAAA,GAAe,OAAA,CAAQ,GAAA,CAAI,0BAA0B,CAAA,IAAK,WAAA;AAChE,IAAM,aAAA,GAAgB,OAAA,CAAQ,GAAA,CAAI,2BAA2B,CAAA,IAAK,YAAA;AAClE,IAAM,UAAA,GAAa,OAAA,CAAQ,GAAA,CAAI,wBAAwB,CAAA,IAAK,GAAA;AAC5D,IAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,qBAAqB,CAAA,IAAK,MAAA;AACtD,IAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,qBAAqB,CAAA,IAAK,MAAA;AACtD,IAAM,YAAA,GAAe,OAAA,CAAQ,GAAA,CAAI,0BAA0B,CAAA,IAAK,WAAA;AAIhE,IAAM,qBAAqB,OAAA,CAAQ,GAAA,CAAI,oCAAoC,CAAA,IAAK,OAAO,SAAA,EAAW,gBAAA;AAElG,SAAS,mBAAmB,GAAA,EAAiD;AACzE,EAAA,MAAM,UAAkC,EAAC;AACzC,EAAA,IAAI,CAAC,KAAK,OAAO,OAAA;AACjB,EAAA,KAAA,MAAW,KAAA,IAAS,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA,EAAG;AAChC,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AACnC,IAAA,IAAI,aAAa,CAAA,EAAG;AAChB,MAAA,OAAA,CAAQ,KAAK,CAAA,kEAAA,EAAqE,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA,CAAA,CAAG,CAAA;AAC1G,MAAA;AAAA,IACJ;AACA,IAAA,IAAI;AACA,MAAA,MAAM,GAAA,GAAM,mBAAmB,KAAA,CAAM,KAAA,CAAM,GAAG,SAAS,CAAA,CAAE,MAAM,CAAA;AAC/D,MAAA,MAAM,KAAA,GAAQ,mBAAmB,KAAA,CAAM,KAAA,CAAM,YAAY,CAAC,CAAA,CAAE,MAAM,CAAA;AAClE,MAAA,IAAI,GAAA,EAAK,OAAA,CAAQ,GAAG,CAAA,GAAI,KAAA;AAAA,IAC5B,CAAA,CAAA,MAAQ;AACJ,MAAA,OAAA,CAAQ,KAAK,CAAA,sFAAA,EAAyF,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,IAClI;AAAA,EACJ;AACA,EAAA,OAAO,OAAA;AACX;AAEA,IAAM,WAAA,GAAc,EAAE,GAAI,MAAA,CAAO,WAAW,OAAA,IAAW,EAAC,EAAI,GAAG,kBAAA,CAAmB,OAAA,CAAQ,GAAA,CAAI,4BAA4B,CAAC,CAAA,EAAE;AAI7H,IAAM,WAAA,GAAc,QAAQ,GAAA,CAAI,qBAAqB,IAAS,IAAA,CAAA,OAAA,CAAQ,GAAA,EAAK,QAAQ,GAAA,CAAI,qBAAqB,CAAC,CAAA,GAAI,MAAA,CAAO,KAAK,IAAA,GAAY,IAAA,CAAA,OAAA,CAAQ,SAAS,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA,GAAI,IAAA;AAC7K,IAAM,UAAA,GAAa,QAAQ,GAAA,CAAI,oBAAoB,IAAS,IAAA,CAAA,OAAA,CAAQ,GAAA,EAAK,QAAQ,GAAA,CAAI,oBAAoB,CAAC,CAAA,GAAI,MAAA,CAAO,KAAK,GAAA,GAAW,IAAA,CAAA,OAAA,CAAQ,SAAS,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA,GAAI,IAAA;AAExK,IAAM,gBAAA,GAAmB,OAAA,CAAQ,GAAA,CAAI,qBAAqB,GAAG,WAAA,EAAY;AACzE,IAAI,gBAAA,KAAqB,MAAA,IAAa,gBAAA,KAAqB,MAAA,IAAU,qBAAqB,OAAA,EAAS;AAC/F,EAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,0CAAA,EAA6C,gBAAgB,CAAA,yBAAA,CAA2B,CAAA;AACtG,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAClB;AACA,IAAI,gBAAA,KAAqB,OAAA,KAAY,CAAC,WAAA,IAAe,CAAC,UAAA,CAAA,EAAa;AAC/D,EAAA,OAAA,CAAQ,MAAM,2GAA2G,CAAA;AACzH,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAClB;AAEA,IAAM,MAAA,GAAS,qBAAqB,MAAA,GAAS,KAAA,GAAQ,qBAAqB,OAAA,GAAU,IAAA,GAAO,CAAC,EAAE,WAAA,IAAe,UAAA,CAAA;AAY7G,SAAS,cAAc,OAAA,EAAqC;AACxD,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA;AAC/B,EAAA,IAAI,QAAQ,MAAA,IAAa,GAAA,CAAI,IAAA,EAAK,KAAM,IAAI,OAAO,MAAA;AACnD,EAAA,MAAM,KAAA,GAAQ,OAAO,GAAG,CAAA;AACxB,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,IAAK,QAAQ,CAAA,EAAG;AACtC,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,sBAAA,EAAyB,OAAO,CAAA,EAAA,EAAK,GAAG,CAAA,2EAAA,CAA6E,CAAA;AAClI,IAAA,OAAO,MAAA;AAAA,EACX;AACA,EAAA,OAAO,IAAA,CAAK,MAAM,KAAK,CAAA;AAC3B;AAEA,IAAM,2BAAA,GAA8B,cAAc,kCAAkC,CAAA;AACpF,IAAM,wBAAA,GAA2B,cAAc,wCAAwC,CAAA;AAGvF,SAAS,aAAa,OAAA,EAAqC;AACvD,EAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAA;AAC/B,EAAA,IAAI,QAAQ,MAAA,IAAa,GAAA,CAAI,IAAA,EAAK,KAAM,IAAI,OAAO,MAAA;AACnD,EAAA,MAAM,KAAA,GAAQ,OAAO,GAAG,CAAA;AACxB,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,KAAK,CAAA,IAAK,QAAQ,CAAA,EAAG;AACvC,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,sBAAA,EAAyB,OAAO,CAAA,EAAA,EAAK,GAAG,CAAA,4DAAA,CAA8D,CAAA;AACnH,IAAA,OAAO,MAAA;AAAA,EACX;AACA,EAAA,OAAO,KAAA;AACX;AAEA,IAAM,0BAAA,GAA6B;AAAA,EAC/B,yBAAA,EAA2B,aAAa,yCAAyC,CAAA;AAAA,EACjF,uBAAA,EAAyB,aAAa,uCAAuC,CAAA;AAAA,EAC7E,oBAAA,EAAsB,aAAa,oCAAoC;AAC3E,CAAA;AAEA,IAAM,cAAc,OAAA,CAAQ,GAAA,CAAI,8BAA8B,CAAA,EAAG,IAAA,GAAO,WAAA,EAAY;AACpF,IAAI,gBAAA;AACJ,IAAI,WAAA,EAAa;AACb,EAAA,IAAI,WAAA,KAAgB,QAAA,IAAY,WAAA,KAAgB,UAAA,IAAc,gBAAgB,QAAA,EAAU;AACpF,IAAA,gBAAA,GAAmB,WAAA;AAAA,EACvB,CAAA,MAAO;AACH,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,oDAAA,EAAuD,WAAW,CAAA,+DAAA,CAAiE,CAAA;AAAA,EACpJ;AACJ;AAMA,IAAM,aAAa,OAAA,CAAQ,GAAA,CAAI,4BAA4B,CAAA,EACrD,MAAM,GAAG,CAAA,CACV,GAAA,CAAI,CAAC,WAAW,MAAA,CAAO,IAAA,EAAM,CAAA,CAC7B,OAAO,OAAO,CAAA;AAEnB,IAAI,cAAA;AACJ,IAAI,YAAY,MAAA,EAAQ;AACpB,EAAA,cAAA,GAAiB,UAAA;AACrB,CAAA,MAAA,IAAW,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,cAAc,CAAA,EAAG;AAC7C,EAAA,cAAA,GAAiB,MAAA,CAAO,cAAA;AAC5B,CAAA,MAAA,IAAW,OAAO,cAAA,EAAgB;AAC9B,EAAA,IAAI;AACA,IAAA,cAAA,GAAiB,IAAI,MAAA,CAAO,MAAA,CAAO,eAAe,OAAA,EAAS,MAAA,CAAO,eAAe,KAAK,CAAA;AAAA,EAC1F,SAAS,GAAA,EAAK;AAGV,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,8CAAA,EAAkD,GAAA,CAAc,OAAO,CAAA,CAAE,CAAA;AAAA,EAC3F;AACJ;AASA,SAAS,sBAAA,GAAiC;AACtC,EAAA,IAAI,0BAA0B,MAAA,EAAQ,OAAO,CAAA,QAAA,EAAW,MAAA,CAAO,cAAc,CAAC,CAAA,CAAA;AAC9E,EAAA,IAAI,kBAAkB,cAAA,CAAe,MAAA,GAAS,GAAG,OAAO,cAAA,CAAe,KAAK,IAAI,CAAA;AAChF,EAAA,OAAO,iLAAA;AACX;AAGA,IAAM,SAAA,GAAY,OAAA,CAAQ,GAAA,CAAI,oBAAoB,CAAA,GAAS,IAAA,CAAA,OAAA,CAAQ,GAAA,EAAK,OAAA,CAAQ,GAAA,CAAI,oBAAoB,CAAC,CAAA,GAAI,IAAA;AAC7G,IAAM,YAAA,GAAe,OAAA,CAAQ,GAAA,CAAI,uBAAuB,CAAA,GAAS,IAAA,CAAA,OAAA,CAAQ,GAAA,EAAK,OAAA,CAAQ,GAAA,CAAI,uBAAuB,CAAC,CAAA,GAAI,IAAA;AActH,SAAS,yBAAyB,MAAA,EAAsB;AACpD,EAAA,MAAM,KAAA,uBAAY,GAAA,CAAY;AAAA,IAC1B,qBAAA;AAAA,IACA,oBAAA;AAAA,IACA,GAAA,CAAI,OAAO,cAAA,IAAkB,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA;AAAA,IAClD,GAAA,CAAI,OAAO,UAAA,IAAc,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA;AAAA,IAC9C,GAAA,CAAI,OAAO,WAAA,IAAe,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI;AAAA,GAClD,CAAA;AACD,EAAA,IAAI,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA,EAAG;AAEvB,EAAA,MAAM,MAAA,GAAS,CAAC,GAAG,KAAK,CAAA,CAAE,MAAA,CAAO,CAAC,IAAA,KAAS,IAAA,KAAS,qBAAA,IAAyB,IAAA,KAAS,oBAAoB,CAAA;AAC1G,EAAA,OAAA,CAAQ,IAAA;AAAA,IACJ,2CAA2C,MAAM,CAAA,uGAAA,EACmB,YAAY,CAAA,CAAA,EAAI,MAAM,CAAA,gBAAA,EAAmB,MAAM,CAAA,KAAA,EAAQ,aAAa,6GAC1B,MAAM,CAAA,wMAAA,EAE3E,qBAAqB,CAAA,gOAAA,EAEpB,MAAA,CAAO,SAAS,CAAA,GAAI,MAAA,CAAO,GAAA,CAAI,CAAC,MAAM,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAAE,KAAK,IAAI,CAAA,GAAI,CAAA,qBAAA,EAAwB,qBAAqB,UAAU,oBAAoB,CAAA,CAAA,CAAA,GAAM,iBAAiB,qBAAqB,CAAA,OAAA,EAAU,oBAAoB,CAAA,MAAA,CAAQ,CAAA,CAAA;AAAA,GACxQ;AACJ;AAGA,SAAS,qBAAA,CAAsB,aAAa,GAAA,EAG1C;AACE,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,OAAO;AAAA,IACH,MAAM,KAAA,EAAsB;AACxB,MAAA,QAAA,EAAA;AACA,MAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,MAAA,IAAI,UAAA,KAAe,CAAA,IAAK,GAAA,GAAM,UAAA,IAAc,UAAA,EAAY;AACpD,QAAA,MAAM,MAAA,GAAS,UAAA,GAAa,CAAA,GAAI,CAAA,EAAA,EAAK,UAAU,CAAA,gCAAA,CAAA,GAAqC,EAAA;AACpF,QAAA,OAAA,CAAQ,MAAM,CAAA,sCAAA,EAA0C,KAAA,CAAgB,OAAO,CAAA,EAAG,MAAM,CAAA,CAAE,CAAA;AAC1F,QAAA,UAAA,GAAa,GAAA;AACb,QAAA,UAAA,GAAa,CAAA;AAAA,MACjB,CAAA,MAAO;AACH,QAAA,UAAA,EAAA;AAAA,MACJ;AAAA,IACJ,CAAA;AAAA,IACA,QAAQ,WAAA,EAA2B;AAC/B,MAAA,IAAI,aAAa,CAAA,EAAG;AACpB,MAAA,OAAA,CAAQ,IAAA;AAAA,QACJ,CAAA,8CAAA,EAAiD,QAAQ,CAAA,iBAAA,EAClD,UAAA,GAAa,CAAA,GAAI,KAAK,UAAU,CAAA,gCAAA,CAAA,GAAqC,EAAE,CAAA,WAAA,EAAc,WAAW,CAAA,SAAA;AAAA,OAC3G;AACA,MAAA,QAAA,GAAW,CAAA;AACX,MAAA,UAAA,GAAa,CAAA;AACb,MAAA,UAAA,GAAa,CAAA;AAAA,IACjB;AAAA,GACJ;AACJ;AAMA,eAAe,IAAA,GAAsB;AAGjC,EAAA,MAAM,gBAAA,GAAwB,IAAA,CAAA,IAAA,CAAK,OAAA,EAAS,UAAU,CAAA;AACtD,EAAA,MAAM,gBAAA,GAAsB,cAAW,gBAAgB,CAAA;AAEvD,EAAA,MAAM,OAAA,GAAU,IAAI,eAAA,EAAgB,CAC/B,SAAS,IAAI,CAAA,CACb,gBAAA,CAAiB,YAAY,CAAA,CAC7B,iBAAA,CAAkB,aAAa,CAAA,CAC/B,cAAA,CAAe,UAAU,CAAA,CACzB,WAAA,CAAY,OAAO,EACnB,WAAA,CAAY,OAAO,CAAA,CACnB,gBAAA,CAAiB,YAAY,CAAA;AAElC,EAAA,IAAI,IAAA,EAAM;AACN,IAAA,OAAA,CAAQ,SAAS,IAAI,CAAA;AAAA,EACzB;AAEA,EAAA,IAAI,gCAAgC,MAAA,EAAW;AAC3C,IAAA,OAAA,CAAQ,sBAAsB,2BAA2B,CAAA;AAAA,EAC7D;AACA,EAAA,IAAI,6BAA6B,MAAA,EAAW;AACxC,IAAA,OAAA,CAAQ,2BAA2B,wBAAwB,CAAA;AAAA,EAC/D;AAEA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,QAAQ,0BAA0B,CAAA,CAAE,MAAA,CAAO,CAAC,GAAG,CAAC,CAAA,KAAM,CAAA,KAAM,MAAS,CAAC,CAAA;AAC/G,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,SAAS,CAAA,EAAG;AAChC,IAAA,OAAA,CAAQ,+BAA+B,MAAM,CAAA;AAAA,EACjD;AACA,EAAA,IAAI,gBAAA,EAAkB;AAClB,IAAA,OAAA,CAAQ,qBAAqB,gBAAgB,CAAA;AAAA,EACjD;AACA,EAAA,IAAI,kBAAA,EAAoB;AACpB,IAAA,MAAM,YAAY,qBAAA,EAAsB;AACxC,IAAA,OAAA,CAAQ,qBAAA;AAAA,MACJ;AAAA,QACI,QAAA,EAAU,kBAAA;AAAA,QACV,OAAA,EAAS,WAAA;AAAA,QACT,SAAA,EAAW,OAAO,SAAA,EAAW,SAAA;AAAA,QAC7B,gBAAA,EAAkB,OAAO,SAAA,EAAW;AAAA,OACxC;AAAA,MACA;AAAA,QACI,aAAA,EAAe,OAAO,SAAA,EAAW,aAAA;AAAA,QACjC,aAAA,EAAe,OAAO,SAAA,EAAW,aAAA;AAAA,QACjC,SAAA,EAAW,OAAO,SAAA,EAAW,SAAA;AAAA,QAC7B,aAAA,EAAe,OAAO,SAAA,EAAW,aAAA;AAAA,QACjC,SAAA,EAAW,OAAO,SAAA,EAAW,SAAA;AAAA,QAC7B,eAAe,SAAA,CAAU,KAAA;AAAA,QACzB,iBAAiB,SAAA,CAAU;AAAA;AAC/B,KACJ;AAAA,EACJ;AAEA,EAAA,IAAI,MAAA,EAAQ;AAKR,IAAA,IAAI;AACA,MAAA,OAAA,CAAQ,YAAA,CAAa,aAAc,UAAW,CAAA;AAAA,IAClD,SAAS,GAAA,EAAK;AACV,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,sDAAuD,GAAA,CAAc,OAAO,CAAA,eAAA,EACxD,WAAW,UAAU,UAAU,CAAA,wTAAA,CAAA;AAAA,QAInD,EAAE,OAAO,GAAA;AAAI,OACjB;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,IAAI,cAAA,EAAgB;AAChB,IAAA,OAAA,CAAQ,mBAAmB,cAAc,CAAA;AAAA,EAC7C;AAWA,EAAA,MAAM,kBAA4B,EAAC;AACnC,EAAA,MAAM,KAAA,GAAQ,CAAC,SAAA,EAAmB,GAAA,KAAsB;AACpD,IAAA,OAAA,CAAQ,eAAA,CAAgB,WAAW,GAAG,CAAA;AACtC,IAAA,eAAA,CAAgB,KAAK,SAAS,CAAA;AAAA,EAClC,CAAA;AAEA,EAAA,IAAI,YAAA,IAAmB,EAAA,CAAA,UAAA,CAAW,YAAY,CAAA,EAAG;AAC7C,IAAA,KAAA,CAAM,WAAW,YAAY,CAAA;AAAA,EACjC;AACA,EAAA,IAAI,SAAA,IAAgB,EAAA,CAAA,UAAA,CAAW,SAAS,CAAA,EAAG;AACvC,IAAA,KAAA,CAAM,KAAK,SAAS,CAAA;AAAA,EACxB;AACA,EAAA,IAAI,MAAA,CAAO,KAAK,MAAA,EAAQ;AACpB,IAAA,KAAA,MAAW,KAAA,IAAS,MAAA,CAAO,GAAA,CAAI,MAAA,EAAQ;AACnC,MAAA,MAAM,GAAA,GAAW,IAAA,CAAA,OAAA,CAAQ,OAAA,EAAS,KAAA,CAAM,GAAG,CAAA;AAC3C,MAAA,IAAO,EAAA,CAAA,UAAA,CAAW,GAAG,CAAA,EAAG;AACpB,QAAA,KAAA,CAAM,KAAA,CAAM,WAAW,GAAG,CAAA;AAAA,MAC9B,CAAA,MAAO;AACH,QAAA,OAAA,CAAQ,KAAK,CAAA,+BAAA,EAAkC,KAAA,CAAM,SAAS,CAAA,SAAA,EAAO,GAAG,CAAA,+BAAA,CAAiC,CAAA;AAAA,MAC7G;AAAA,IACJ;AAAA,EACJ;AAGA,EAAA,IAAI,OAAO,cAAA,EAAgB;AACvB,IAAA,KAAA,MAAW,CAAA,IAAK,OAAO,cAAA,EAAgB;AACnC,MAAA,OAAA,CAAQ,iBAAA,CAAkB;AAAA,QACtB,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,SAAS,CAAA,CAAE,OAAA;AAAA,QACX,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,KAAK,CAAA,CAAE,GAAA;AAAA,QACP,WAAW,CAAA,CAAE;AAAA,OAChB,CAAA;AAAA,IACL;AAAA,EACJ;AAKA,EAAA,IAAI,OAAO,UAAA,EAAY;AACnB,IAAA,KAAA,MAAW,CAAA,IAAK,OAAO,UAAA,EAAY;AAC/B,MAAA,OAAA,CAAQ,kBAAA,CAAmB,EAAE,GAAG,CAAA,EAAG,WAAW,CAAA,CAAE,SAAA,IAAa,MAAM,CAAA;AAAA,IACvE;AAAA,EACJ;AAMA,EAAA,IAAI,OAAO,WAAA,EAAa;AACpB,IAAA,KAAA,MAAW,CAAA,IAAK,OAAO,WAAA,EAAa;AAChC,MAAA,MAAM,QAAA,GAAW,MAAM,cAAA,CAAe,CAAA,EAAG,OAAO,CAAA;AAChD,MAAA,IAAI,QAAA,EAAU;AACV,QAAA,OAAA,CAAQ,kBAAkB,QAAQ,CAAA;AAAA,MACtC;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,IAAI,aAAA,EAAe;AACf,IAAA,OAAA,CAAQ,gBAAgB,aAAa,CAAA;AACrC,IAAA,wBAAA,CAAyB,aAAa,CAAA;AAAA,EAC1C;AAEA,EAAA,IAAI,gBAAA,EAAkB;AAClB,IAAA,OAAA,CAAQ,2BAA2B,gBAAgB,CAAA;AAAA,EACvD;AAKA,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,GAAA,CAAI,yBAAyB,CAAA;AAC5D,EAAA,MAAM,WAAA,GAAc,cAAA,KAAmB,GAAA,IAAO,cAAA,KAAmB,MAAA;AACjE,EAAA,IAAI,WAAA,EAAa;AACb,IAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,GAAA,CAAI,4BAA4B,CAAA;AAC9D,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA;AAC1C,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,mBAAmB,CAAA;AAC9C,IAAA,MAAM,uBAAuB,UAAA,EAAY,oBAAA,KAAyB,SAAS,CAAC,MAAM,IAAI,EAAC,CAAA;AACvF,IAAA,IAAI,CAAC,aAAA,IAAiB,CAAC,IAAA,IAAQ,oBAAA,CAAqB,WAAW,CAAA,EAAG;AAC9D,MAAA,OAAA,CAAQ,KAAA;AAAA,QACJ;AAAA,OAGJ;AACA,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,IAClB;AACA,IAAA,OAAA,CAAQ,WAAA,CAAY;AAAA,MAChB,aAAA;AAAA,MACA,oBAAA;AAAA,MACA,OAAA,EAAS,IAAA;AAAA,MACT,MAAA;AAAA,MACA,iBAAiB,UAAA,EAAY,eAAA;AAAA,MAC7B,gBAAgB,UAAA,EAAY,cAAA;AAAA,MAC5B,eAAe,UAAA,EAAY,aAAA;AAAA,MAC3B,gBAAgB,UAAA,EAAY,cAAA;AAAA,MAC5B,gBAAgB,UAAA,EAAY,cAAA;AAAA,MAC5B,OAAO,UAAA,EAAY,KAAA;AAAA,MACnB,aAAa,UAAA,EAAY,WAAA;AAAA,MACzB,QAAQ,UAAA,EAAY,MAAA;AAAA,MACpB,eAAe,UAAA,EAAY,aAAA;AAAA,MAC3B,kBAAkB,UAAA,EAAY,gBAAA;AAAA,MAC9B,0BAA0B,UAAA,EAAY,wBAAA;AAAA,MACtC,OAAO,UAAA,EAAY;AAAA,KACtB,CAAA;AAAA,EACL;AAKA,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,GAAA,CAAI,4BAA4B,CAAA;AAC/D,EAAA,IAAI,cAAA,EAAgB;AAChB,IAAA,OAAA,CAAQ,mBAAmB,cAAc,CAAA;AAAA,EAC7C;AAKA,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AAEA,IAAA,IAAI,QAAA,EAAU;AACV,MAAA,IAAI,SAAS,WAAA,CAAY,MAAA,GAAS,GAAG,OAAA,CAAQ,sBAAA,CAAuB,SAAS,WAAW,CAAA;AACxF,MAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,QAAA,CAAS,aAAA,EAAe,cAAA;AACxD,MAAA,IAAI,cAAA,IAAkB,OAAO,IAAA,CAAK,cAAc,EAAE,MAAA,GAAS,CAAA,EAAG,OAAA,CAAQ,kBAAA,CAAmB,cAAc,CAAA;AACvG,MAAA,IAAI,SAAS,QAAA,CAAS,MAAA,UAAgB,UAAA,CAAW,QAAA,CAAS,SAAS,MAAM,CAAA;AACzE,MAAA,OAAA,CAAQ,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAAA,IAChD;AACA,IAAA,MAAA,GAAS,QAAQ,KAAA,EAAM;AAAA,EAC3B,SAAS,KAAA,EAAO;AACZ,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,aAAA,EAAiB,KAAA,CAAgB,OAAO,CAAA,CAAE,CAAA;AACxD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAClB;AACA,EAAA,MAAM,OAAO,KAAA,EAAM;AAGnB,EAAA,MAAM,UAAA,GAAa,SAAS,OAAA,GAAU,MAAA;AACtC,EAAA,MAAM,QAAA,GAAW,SAAS,KAAA,GAAQ,IAAA;AAClC,EAAA,MAAM,EAAA,GAAK,QAAA,CAAI,MAAA,CAAO,EAAE,CAAA;AACxB,EAAA,MAAM,SAAA,GAAY,CAAA,EAAG,UAAU,CAAA,aAAA,EAAgB,IAAI,CAAA,CAAA;AACnD,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAC3C,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAC3C,EAAA,MAAM,cAAA,GAAiB,YAAA,CAAa,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAMrD,EAAA,OAAA,CAAQ,GAAA,EAAI;AACZ,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,gCAAA,EAAyB,MAAA,GAAS,QAAA,GAAW,EAAE,CAAA,CAAE,CAAA;AAC7D,EAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,EAAA,OAAA,CAAQ,IAAI,CAAA,iCAAA,EAA6B,QAAQ,gBAAgB,IAAI,CAAA,EAAG,YAAY,CAAA,OAAA,CAAS,CAAA;AAC7F,EAAA,OAAA,CAAQ,IAAI,CAAA,6FAAA,CAA+F,CAAA;AAC3G,EAAA,OAAA,CAAQ,IAAI,CAAA,iCAAA,EAA6B,QAAQ,gBAAgB,IAAI,CAAA,EAAG,aAAa,CAAA,CAAE,CAAA;AACvF,EAAA,OAAA,CAAQ,IAAI,CAAA,4FAAA,CAA8F,CAAA;AAC1G,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAA6B,SAAS,CAAA,QAAA,EAAW,SAAS,CAAA,CAAE,CAAA;AACxE,EAAA,OAAA,CAAQ,GAAA,CAAI,oCAA6B,SAAS,CAAA,QAAA,EAAW,SAAS,CAAA,UAAA,EAAa,SAAS,CAAA,QAAA,EAAW,cAAc,CAAA,CAAE,CAAA;AAIvH,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAA6B,QAAQ,CAAA,aAAA,EAAgB,IAAI,GAAG,UAAA,KAAe,GAAA,GAAM,GAAA,GAAM,UAAU,CAAA,MAAA,CAAQ,CAAA;AACrH,EAAA,OAAA,CAAQ,IAAI,CAAA,oFAAA,CAAsF,CAAA;AAClG,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAA6B,qBAAqB,CAAA,2CAAA,CAA6C,CAAA;AAC3G,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,0BAAA,EAA6B,oBAAoB,CAAA,6EAAA,CAA+E,CAAA;AAC5I,EAAA,IAAI,eAAA,CAAgB,SAAS,CAAA,EAAG;AAC5B,IAAA,OAAA,CAAQ,IAAI,CAAA,iCAAA,EAA6B,eAAA,CAAgB,IAAI,CAAC,MAAA,KAAW,GAAG,SAAS,CAAA,EAAG,WAAW,GAAA,GAAM,GAAA,GAAM,SAAS,GAAG,CAAA,CAAE,EAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,EAC/I;AACA,EAAA,IAAI,gBAAA,EAAkB;AAClB,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAA6B,gBAAgB,CAAA,CAAE,CAAA;AAAA,EAC/D;AACA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAA6B,WAAA,GAAc,6BAAA,GAAgC,iCAAiC,CAAA,CAAE,CAAA;AAC1H,EAAA,MAAM,kBAAA,GAAqB,QAAA,EAAU,WAAA,CAAY,MAAA,IAAU,CAAA;AAC3D,EAAA,OAAA,CAAQ,GAAA;AAAA,IACJ,2CAA+B,kBAAA,GAAqB,CAAA,GAAI,CAAA,EAAG,kBAAkB,oBAAoB,kBAAA,KAAuB,CAAA,GAAI,GAAA,GAAM,KAAK,GAAG,cAAA,GAAiB,kBAAA,GAAqB,EAAE,CAAA,CAAA,GAAK,cAAA,GAAiB,2BAA2B,UAAU,CAAA;AAAA,GACjP;AACA,EAAA,IAAI,QAAA,EAAU;AACV,IAAA,MAAM,cAAA,GAAiB,OAAO,IAAA,CAAK,QAAA,CAAS,SAAS,aAAA,EAAe,cAAA,IAAkB,EAAE,CAAA,CAAE,MAAA;AAC1F,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAA6B,QAAA,CAAS,UAAU,aAAa,QAAA,CAAS,OAAO,CAAA,EAAG,cAAA,GAAiB,CAAA,GAAI,CAAA,EAAA,EAAK,cAAc,CAAA,kBAAA,CAAA,GAAuB,EAAE,CAAA,CAAA,CAAG,CAAA;AAAA,EACpK;AACA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAA6B,sBAAA,EAAwB,CAAA,CAAE,CAAA;AACnE,EAAA,OAAA,CAAQ,IAAI,CAAA,8CAAA,EAAiD,SAAS,aAAa,SAAS,CAAA,aAAA,EAAgB,cAAc,CAAA,CAAE,CAAA;AAC5H,EAAA,IAAI,CAAC,cAAA,EAAgB;AACjB,IAAA,OAAA,CAAQ,IAAI,CAAA,+FAAA,CAAiG,CAAA;AAAA,EACjH;AACA,EAAA,OAAA,CAAQ,IAAI,CAAA,iCAAA,EAA6B,kBAAA,GAAqB,gBAAgB,kBAAkB,CAAA,CAAA,GAAK,UAAU,CAAA,CAAE,CAAA;AACjH,EAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,EAAA,OAAA,CAAQ,GAAA,CAAI,yCAAyC,oBAAoB,CAAA,OAAA,EAAU,SAAS,CAAA,CAAA,EAAI,oBAAoB,CAAA,CAAA,EAAI,SAAS,CAAA,CAAE,CAAA;AACnI,EAAA,OAAA,CAAQ,IAAI,CAAA,wBAAA,CAA0B,CAAA;AACtC,EAAA,OAAA,CAAQ,GAAA,EAAI;AAKZ,EAAA,MAAM,aAAa,iBAAA,CAAkB,OAAA,CAAQ,GAAA,CAAI,iBAAiB,GAAG,SAAS,CAAA;AAC9E,EAAA,IAAI,WAAW,KAAA,EAAO;AAClB,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,aAAA,EAAgB,UAAA,CAAW,KAAK,CAAA,CAAE,CAAA;AAAA,EACnD,CAAA,MAAA,IAAW,UAAA,CAAW,GAAA,IAAO,UAAA,CAAW,IAAA,EAAM;AAC1C,IAAA,MAAM,UAAU,UAAA,CAAW,GAAA;AAC3B,IAAA,MAAM,WAAW,UAAA,CAAW,IAAA;AAC5B,IAAA,MAAM,UAAU,eAAA,CAAgB,IAAA,CAAK,CAAC,MAAA,KAAW,MAAA,KAAW,OAAO,QAAA,KAAa,MAAA,IAAU,QAAA,CAAS,UAAA,CAAW,OAAO,QAAA,CAAS,GAAG,IAAI,MAAA,GAAS,MAAA,GAAS,GAAG,CAAC,CAAA;AAC3J,IAAA,IAAI,CAAC,OAAA,EAAS;AACV,MAAA,OAAA,CAAQ,IAAA;AAAA,QACJ,CAAA,yBAAA,EAA4B,OAAO,CAAA,0BAAA,EAA6B,QAAQ,0BACnE,eAAA,CAAgB,MAAA,GAAS,IACpB,CAAA,kBAAA,EAAqB,eAAA,CAAgB,IAAI,CAAC,CAAA,KAAM,IAAI,CAAC,CAAA,CAAA,CAAG,EAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA,GACpE,CAAA,2HAAA,CAAA;AAAA,OACd;AAAA,IACJ,CAAA,MAAO;AACH,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,4BAAA,EAAwB,OAAO,CAAA,CAAE,CAAA;AAC7C,MAAA,OAAA,CAAQ,GAAA,EAAI;AACZ,MAAA,IAAI;AACA,QAAA,MAAM,KAAK,OAAO,CAAA;AAAA,MACtB,SAAS,GAAA,EAAK;AAGV,QAAA,OAAA,CAAQ,KAAK,CAAA,4CAAA,EAA+C,OAAO,CAAA,EAAA,EAAM,GAAA,CAAc,OAAO,CAAA,+CAAA,CAAiD,CAAA;AAAA,MACnJ;AAAA,IACJ;AAAA,EACJ;AAGA,EAAA,MAAM,QAAA,GAAW,OAAO,MAAA,KAAkC;AACtD,IAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,QAAA,EAAQ,MAAM,CAAA,8BAAA,CAA2B,CAAA;AACrD,IAAA,MAAM,OAAO,IAAA,EAAK;AAClB,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAClB,CAAA;AAEA,EAAA,OAAA,CAAQ,GAAG,QAAA,EAAU,MAAM,KAAK,QAAA,CAAS,QAAQ,CAAC,CAAA;AAClD,EAAA,OAAA,CAAQ,GAAG,SAAA,EAAW,MAAM,KAAK,QAAA,CAAS,SAAS,CAAC,CAAA;AACxD;AAEA,IAAA,EAAK,CAAE,KAAA,CAAM,CAAC,GAAA,KAAiB;AAU3B,EAAA,MAAM,UAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC/D,EAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,2BAAA,EAA8B,OAAO,CAAA,CAAE,CAAA;AAGrD,EAAA,MAAM,KAAA,GAAQ,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,KAAA,GAAQ,MAAA;AACjD,EAAA,IAAI,iBAAiB,KAAA,EAAO;AACxB,IAAA,MAAM,OAAQ,KAAA,CAAgC,IAAA;AAC9C,IAAA,OAAA,CAAQ,KAAA,CAAM,kCAAkC,IAAA,GAAO,IAAA,GAAO,MAAM,EAAE,CAAA,EAAG,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,EAC5F;AAKA,EAAA,IAAI,EAAE,eAAe,KAAA,CAAA,IAAU,CAAC,QAAQ,QAAA,CAAS,MAAA,CAAO,IAAI,CAAC,CAAA,EAAG;AAC5D,IAAA,OAAA,CAAQ,KAAA;AAAA,MACJ,CAAA,2CAAA,EAA8C,IAAA,IAAQ,SAAS,CAAA,MAAA,EAAS,IAAI,CAAA,uFAAA;AAAA,KAChF;AAAA,EACJ;AAEA,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAClB,CAAC,CAAA","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 ` 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\nlet allowedOrigins: readonly string[] | RegExp | 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 allowedOrigins = new RegExp(config.allowedOrigins.pattern, config.allowedOrigins.flags);\n } catch (err) {\n // Falling back to the closed default rather than starting with a rule\n // the operator believes is in force but which never compiled.\n console.error(`[mcp-broker] Ignoring allowedOrigins.pattern: ${(err as Error).message}`);\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 instanceof RegExp) return `pattern ${String(allowedOrigins)}`;\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 // ── 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 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(`🔐 Authorization ${authEnabled ? \"OAuth 2.1 (Bearer required)\" : \"disabled (trusted network only)\"}`);\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,10 +1,10 @@
|
|
|
1
|
+
import { validateLimitsConfig } from './chunk-JVXOJUWT.js';
|
|
1
2
|
import { createHash, X509Certificate, createPublicKey, verify } from 'crypto';
|
|
2
3
|
import { readFileSync, mkdirSync, writeFileSync, existsSync, rmSync } from 'fs';
|
|
3
4
|
import { homedir } from 'os';
|
|
4
5
|
import { resolve, relative, isAbsolute, dirname, join, sep } from 'path';
|
|
5
6
|
import { inflateRawSync } from 'zlib';
|
|
6
7
|
|
|
7
|
-
// src/mcpb/mcpb.loader.ts
|
|
8
8
|
var EOCD_SIGNATURE = 101010256;
|
|
9
9
|
var CENTRAL_HEADER_SIGNATURE = 33639248;
|
|
10
10
|
var LOCAL_HEADER_SIGNATURE = 67324752;
|
|
@@ -181,9 +181,9 @@ async function loadMcpbBundle(cfg, baseDir) {
|
|
|
181
181
|
var DEFAULT_CONFIG_DIR = ".mcp-broker";
|
|
182
182
|
var DEFAULT_CONFIG_FILENAME = "config.json";
|
|
183
183
|
var LEGACY_CONFIG_FILENAME = "mcp-broker.config.json";
|
|
184
|
-
var SECURITY_KEYS = /* @__PURE__ */ new Set(["$schema", "description", "auth", "providers", "authorization"]);
|
|
184
|
+
var SECURITY_KEYS = /* @__PURE__ */ new Set(["$schema", "description", "auth", "providers", "authorization", "limits"]);
|
|
185
185
|
function loadSecurityConfig(loaded, env = process.env) {
|
|
186
|
-
const misplaced = ["providers", "authorization"].filter((key) => Object.prototype.hasOwnProperty.call(loaded.config, key));
|
|
186
|
+
const misplaced = ["providers", "authorization", "limits"].filter((key) => Object.prototype.hasOwnProperty.call(loaded.config, key));
|
|
187
187
|
if (misplaced.length > 0 && loaded.sourcePath) {
|
|
188
188
|
throw new BrokerConfigError(
|
|
189
189
|
loaded.sourcePath,
|
|
@@ -256,6 +256,14 @@ function loadSecurityConfig(loaded, env = process.env) {
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
|
+
if (security.limits !== void 0) {
|
|
260
|
+
try {
|
|
261
|
+
validateLimitsConfig(security.limits);
|
|
262
|
+
if (security.limits.storeFile) security.limits = { ...security.limits, storeFile: resolve(dirname(sourcePath), security.limits.storeFile) };
|
|
263
|
+
} catch (error) {
|
|
264
|
+
problems.push(error.message);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
259
267
|
const authorization = security.authorization;
|
|
260
268
|
if (authorization !== void 0) {
|
|
261
269
|
if (typeof authorization !== "object" || authorization === null || Array.isArray(authorization)) problems.push('"authorization" must be an object');
|
|
@@ -374,5 +382,5 @@ function resolveOpenTarget(raw, baseUrl) {
|
|
|
374
382
|
}
|
|
375
383
|
|
|
376
384
|
export { BrokerConfigError, DEFAULT_CONFIG_FILENAME, loadBrokerConfig, loadMcpbBundle, loadSecurityConfig, resolveOpenTarget, unzipMcpb };
|
|
377
|
-
//# sourceMappingURL=chunk-
|
|
378
|
-
//# sourceMappingURL=chunk-
|
|
385
|
+
//# sourceMappingURL=chunk-2GDTE7BA.js.map
|
|
386
|
+
//# sourceMappingURL=chunk-2GDTE7BA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcpb/mcpb.unzip.ts","../src/mcpb/mcpb.loader.ts","../src/config.ts"],"names":["dirname","resolve","readFileSync","existsSync"],"mappings":";;;;;;;AAiBA,IAAM,cAAA,GAAiB,SAAA;AACvB,IAAM,wBAAA,GAA2B,QAAA;AACjC,IAAM,sBAAA,GAAyB,QAAA;AAG/B,SAAS,SAAS,GAAA,EAAqB;AAEnC,EAAA,MAAM,SAAS,IAAA,CAAK,GAAA,CAAI,GAAG,GAAA,CAAI,MAAA,GAAS,KAAK,KAAM,CAAA;AACnD,EAAA,KAAA,IAAS,MAAM,GAAA,CAAI,MAAA,GAAS,EAAA,EAAI,GAAA,IAAO,QAAQ,GAAA,EAAA,EAAO;AAClD,IAAA,IAAI,GAAA,CAAI,YAAA,CAAa,GAAG,CAAA,KAAM,gBAAgB,OAAO,GAAA;AAAA,EACzD;AACA,EAAA,MAAM,IAAI,MAAM,8DAA8D,CAAA;AAClF;AAOO,SAAS,SAAA,CAAU,UAAkB,OAAA,EAAuB;AAC/D,EAAA,MAAM,GAAA,GAAM,aAAa,QAAQ,CAAA;AACjC,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AAEzB,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,YAAA,CAAa,IAAA,GAAO,EAAE,CAAA;AAC/C,EAAA,MAAM,gBAAA,GAAmB,GAAA,CAAI,YAAA,CAAa,IAAA,GAAO,EAAE,CAAA;AACnD,EAAA,IAAI,gBAAA,KAAqB,UAAA,IAAc,YAAA,KAAiB,KAAA,EAAQ;AAC5D,IAAA,MAAM,IAAI,MAAM,kCAAkC,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,OAAA,GAAU,QAAQ,OAAO,CAAA;AAC/B,EAAA,SAAA,CAAU,OAAA,EAAS,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAEtC,EAAA,IAAI,GAAA,GAAM,gBAAA;AACV,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,YAAA,EAAc,CAAA,EAAA,EAAK;AACnC,IAAA,IAAI,GAAA,CAAI,YAAA,CAAa,GAAG,CAAA,KAAM,wBAAA,EAA0B;AACpD,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oDAAA,EAAuD,GAAG,CAAA,CAAE,CAAA;AAAA,IAChF;AACA,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,YAAA,CAAa,GAAA,GAAM,EAAE,CAAA;AACxC,IAAA,MAAM,cAAA,GAAiB,GAAA,CAAI,YAAA,CAAa,GAAA,GAAM,EAAE,CAAA;AAChD,IAAA,MAAM,gBAAA,GAAmB,GAAA,CAAI,YAAA,CAAa,GAAA,GAAM,EAAE,CAAA;AAClD,IAAA,MAAM,OAAA,GAAU,GAAA,CAAI,YAAA,CAAa,GAAA,GAAM,EAAE,CAAA;AACzC,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,YAAA,CAAa,GAAA,GAAM,EAAE,CAAA;AAC1C,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,YAAA,CAAa,GAAA,GAAM,EAAE,CAAA;AAC5C,IAAA,MAAM,iBAAA,GAAoB,GAAA,CAAI,YAAA,CAAa,GAAA,GAAM,EAAE,CAAA;AACnD,IAAA,MAAM,IAAA,GAAO,IAAI,QAAA,CAAS,MAAA,EAAQ,MAAM,EAAA,EAAI,GAAA,GAAM,KAAK,OAAO,CAAA;AAC9D,IAAA,GAAA,IAAO,EAAA,GAAK,UAAU,QAAA,GAAW,UAAA;AAEjC,IAAA,IAAI,cAAA,KAAmB,UAAA,IAAc,gBAAA,KAAqB,UAAA,IAAc,sBAAsB,UAAA,EAAY;AACtG,MAAA,MAAM,IAAI,MAAM,kCAAkC,CAAA;AAAA,IACtD;AAGA,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,EAAS,IAAI,CAAA;AACpC,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,OAAA,EAAS,MAAM,CAAA;AACpC,IAAA,IAAI,IAAI,UAAA,CAAW,IAAI,CAAA,IAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AACzC,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,IAAI,CAAA,CAAE,CAAA;AAAA,IAC/D;AAGA,IAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AACpB,MAAA,SAAA,CAAU,MAAA,EAAQ,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AACrC,MAAA;AAAA,IACJ;AAGA,IAAA,IAAI,GAAA,CAAI,YAAA,CAAa,iBAAiB,CAAA,KAAM,sBAAA,EAAwB;AAChE,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,IACjE;AACA,IAAA,MAAM,YAAA,GAAe,GAAA,CAAI,YAAA,CAAa,iBAAA,GAAoB,EAAE,CAAA;AAC5D,IAAA,MAAM,aAAA,GAAgB,GAAA,CAAI,YAAA,CAAa,iBAAA,GAAoB,EAAE,CAAA;AAC7D,IAAA,MAAM,SAAA,GAAY,iBAAA,GAAoB,EAAA,GAAK,YAAA,GAAe,aAAA;AAC1D,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,QAAA,CAAS,SAAA,EAAW,YAAY,cAAc,CAAA;AAErE,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,WAAW,CAAA,EAAG;AACd,MAAA,IAAA,GAAO,UAAA;AAAA,IACX,CAAA,MAAA,IAAW,WAAW,CAAA,EAAG;AACrB,MAAA,IAAA,GAAO,eAAe,UAAU,CAAA;AAAA,IACpC,CAAA,MAAO;AACH,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,MAAM,CAAA,MAAA,EAAS,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,IAChF;AAEA,IAAA,SAAA,CAAU,QAAQ,MAAM,CAAA,EAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAC9C,IAAA,aAAA,CAAc,QAAQ,IAAI,CAAA;AAAA,EAC9B;AACJ;;;ACnDA,SAAS,cAAc,GAAA,EAAwB;AAC3C,EAAA,IAAI,GAAA,CAAI,QAAA,CAAS,mBAAmB,CAAA,EAAG;AACnC,IAAA,OAAO,IAAI,eAAA,CAAgB,GAAG,CAAA,CAAE,SAAA;AAAA,EACpC;AACA,EAAA,OAAO,gBAAgB,GAAG,CAAA;AAC9B;AAGA,SAAS,uBAAA,CAAwB,IAAA,EAAc,SAAA,EAAmB,SAAA,EAA+B;AAE7F,EAAA,MAAM,UAAU,SAAA,CAAU,iBAAA;AAC1B,EAAA,MAAM,SAAA,GAAY,OAAA,KAAY,SAAA,IAAa,OAAA,KAAY,UAAU,IAAA,GAAO,QAAA;AACxE,EAAA,OAAO,MAAA,CAAO,SAAA,EAAW,IAAA,EAAM,SAAA,EAAW,SAAS,CAAA;AACvD;AAGA,SAAS,kBAAA,CAAmB,GAAA,EAAaA,QAAAA,EAAiB,UAAA,EAA6G;AACnK,EAAA,IAAI,GAAA,KAAQ,aAAa,OAAOA,QAAAA;AAChC,EAAA,IAAI,GAAA,KAAQ,MAAA,EAAQ,OAAO,OAAA,EAAQ;AACnC,EAAA,IAAI,QAAQ,SAAA,EAAW,OAAO,IAAA,CAAK,OAAA,IAAW,SAAS,CAAA;AACvD,EAAA,IAAI,QAAQ,WAAA,EAAa,OAAO,IAAA,CAAK,OAAA,IAAW,WAAW,CAAA;AAC3D,EAAA,IAAI,QAAQ,WAAA,EAAa,OAAO,IAAA,CAAK,OAAA,IAAW,WAAW,CAAA;AAC3D,EAAA,IAAI,GAAA,KAAQ,eAAA,IAAmB,GAAA,KAAQ,GAAA,EAAK,OAAO,GAAA;AACnD,EAAA,IAAI,GAAA,CAAI,UAAA,CAAW,cAAc,CAAA,EAAG;AAChC,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,KAAA,CAAM,cAAA,CAAe,MAAM,CAAA;AAC5C,IAAA,MAAM,KAAA,GAAQ,aAAa,IAAI,CAAA;AAC/B,IAAA,IAAI,UAAU,MAAA,EAAW,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,IAAI,CAAA,CAAA,CAAG,CAAA;AAC/E,IAAA,OAAO,KAAA;AAAA,EACX;AACA,EAAA,OAAO,MAAA;AACX;AAGA,SAAS,YAAA,CAAa,KAAA,EAAeA,QAAAA,EAAiB,UAAA,EAAqD;AACvG,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,gBAAA,EAAkB,CAAC,OAAO,GAAA,KAAgB;AAC3D,IAAA,MAAM,KAAA,GAAQ,kBAAA,CAAmB,GAAA,EAAKA,QAAAA,EAAS,UAAU,CAAA;AACzD,IAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAChC,IAAA,IAAI,KAAA,CAAM,QAAQ,KAAK,CAAA,QAAS,IAAI,KAAA,CAAM,CAAA,gBAAA,EAAmB,GAAG,CAAA,0CAAA,CAA4C,CAAA;AAC5G,IAAA,OAAO,OAAO,KAAK,CAAA;AAAA,EACvB,CAAC,CAAA;AACL;AAGA,SAAS,SAAA,CAAU,GAAA,EAAaA,QAAAA,EAAiB,UAAA,EAAuD;AACpG,EAAA,MAAM,UAAA,GAAa,8BAAA,CAA+B,IAAA,CAAK,GAAG,CAAA;AAC1D,EAAA,IAAI,UAAA,EAAY;AACZ,IAAA,MAAM,QAAQ,kBAAA,CAAmB,UAAA,CAAW,CAAC,CAAA,EAAGA,UAAS,UAAU,CAAA;AACnE,IAAA,IAAI,MAAM,OAAA,CAAQ,KAAK,GAAG,OAAO,KAAA,CAAM,IAAI,MAAM,CAAA;AACjD,IAAA,OAAO,CAAC,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,EACzB;AACA,EAAA,OAAO,CAAC,YAAA,CAAa,GAAA,EAAKA,QAAAA,EAAS,UAAU,CAAC,CAAA;AAClD;AASA,eAAsB,cAAA,CAAe,KAAwB,OAAA,EAAuD;AAChH,EAAA,MAAM,GAAA,GAAM,CAAA,0BAAA,EAA6B,GAAA,CAAI,IAAI,CAAA,CAAA,CAAA;AACjD,EAAA,IAAI;AACA,IAAA,MAAM,QAAA,GAAWC,OAAAA,CAAQ,OAAA,EAAS,GAAA,CAAI,IAAI,CAAA;AAC1C,IAAA,MAAM,aAAA,GAAgBA,OAAAA,CAAQ,OAAA,EAAS,GAAA,CAAI,SAAS,CAAA;AACpD,IAAA,MAAM,aAAA,GAAgB,IAAI,SAAA,GAAYA,OAAAA,CAAQ,SAAS,GAAA,CAAI,SAAS,CAAA,GAAI,CAAA,EAAG,QAAQ,CAAA,IAAA,CAAA;AAEnF,IAAA,KAAA,MAAW,CAAC,KAAA,EAAO,IAAI,CAAA,IAAK;AAAA,MACxB,CAAC,UAAU,QAAQ,CAAA;AAAA,MACnB,CAAC,cAAc,aAAa,CAAA;AAAA,MAC5B,CAAC,aAAa,aAAa;AAAA,KAC/B,EAAY;AACR,MAAA,IAAI,CAAC,UAAA,CAAW,IAAI,CAAA,EAAG;AACnB,QAAA,OAAA,CAAQ,MAAM,CAAA,EAAG,GAAG,KAAK,KAAK,CAAA,mBAAA,EAAsB,IAAI,CAAA,iBAAA,CAAmB,CAAA;AAC3E,QAAA,OAAO,IAAA;AAAA,MACX;AAAA,IACJ;AAGA,IAAA,MAAM,WAAA,GAAcC,aAAa,QAAQ,CAAA;AACzC,IAAA,MAAM,cAAA,GAAiBA,aAAa,aAAa,CAAA;AACjD,IAAA,MAAM,SAAA,GAAY,aAAA,CAAcA,YAAAA,CAAa,aAAA,EAAe,MAAM,CAAC,CAAA;AACnE,IAAA,IAAI,CAAC,uBAAA,CAAwB,WAAA,EAAa,cAAA,EAAgB,SAAS,CAAA,EAAG;AAClE,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,8EAAA,CAAgF,CAAA;AACpG,MAAA,OAAO,IAAA;AAAA,IACX;AAGA,IAAA,MAAM,YAAYD,OAAAA,CAAQ,OAAA,EAAS,QAAA,EAAU,MAAA,EAAQ,IAAI,IAAI,CAAA;AAC7D,IAAA,MAAA,CAAO,WAAW,EAAE,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AAClD,IAAA,SAAA,CAAU,UAAU,SAAS,CAAA;AAG7B,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,SAAA,EAAW,eAAe,CAAA;AACpD,IAAA,IAAI,CAAC,UAAA,CAAW,YAAY,CAAA,EAAG;AAC3B,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,0DAAA,CAA4D,CAAA;AAChF,MAAA,OAAO,IAAA;AAAA,IACX;AACA,IAAA,MAAM,WAAW,IAAA,CAAK,KAAA,CAAMC,YAAAA,CAAa,YAAA,EAAc,MAAM,CAAC,CAAA;AAC9D,IAAA,MAAM,SAAA,GAAY,SAAS,MAAA,EAAQ,UAAA;AACnC,IAAA,IAAI,CAAC,SAAA,EAAW;AACZ,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,oDAAA,CAAsD,CAAA;AAC1E,MAAA,OAAO,IAAA;AAAA,IACX;AAGA,IAAA,MAAM,QAAA,GAAW,SAAA,CAAU,kBAAA,GAAqB,OAAA,CAAQ,QAAQ,CAAA;AAChE,IAAA,MAAM,OAAA,GAAU,QAAA,EAAU,OAAA,IAAW,SAAA,CAAU,OAAA;AAC/C,IAAA,MAAM,OAAA,GAAU,QAAA,EAAU,IAAA,IAAQ,SAAA,CAAU,QAAQ,EAAC;AACrD,IAAA,MAAM,SAAS,EAAE,GAAG,UAAU,GAAA,EAAK,GAAG,UAAU,GAAA,EAAI;AACpD,IAAA,IAAI,CAAC,OAAA,EAAS;AACV,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,qDAAA,CAAuD,CAAA;AAC3E,MAAA,OAAO,IAAA;AAAA,IACX;AAGA,IAAA,MAAM,eAAA,GAAkB,YAAA,CAAa,OAAA,EAAS,SAAA,EAAW,IAAI,UAAU,CAAA;AACvE,IAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,OAAA,CAAQ,CAAC,GAAA,KAAQ,UAAU,GAAA,EAAK,SAAA,EAAW,GAAA,CAAI,UAAU,CAAC,CAAA;AACvF,IAAA,MAAM,cAAsC,EAAC;AAC7C,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC/C,MAAA,WAAA,CAAY,GAAG,CAAA,GAAI,YAAA,CAAa,KAAA,EAAO,SAAA,EAAW,IAAI,UAAU,CAAA;AAAA,IACpE;AAEA,IAAA,OAAO;AAAA,MACH,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,OAAA,EAAS,eAAA;AAAA,MACT,IAAA,EAAM,YAAA;AAAA,MACN,KAAK,MAAA,CAAO,IAAA,CAAK,WAAW,CAAA,CAAE,MAAA,GAAS,IAAI,WAAA,GAAc,KAAA,CAAA;AAAA,MACzD,SAAA,EAAW,IAAI,SAAA,IAAa;AAAA,KAChC;AAAA,EACJ,SAAS,GAAA,EAAK;AACV,IAAA,OAAA,CAAQ,MAAM,CAAA,EAAG,GAAG,CAAA,EAAA,EAAM,GAAA,CAAc,OAAO,CAAA,iBAAA,CAAmB,CAAA;AAClE,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;ACmIO,IAAM,kBAAA,GAAqB,aAAA;AAG3B,IAAM,uBAAA,GAA0B;AAGhC,IAAM,sBAAA,GAAyB,wBAAA;AAyCtC,IAAM,aAAA,mBAAgB,IAAI,GAAA,CAAI,CAAC,SAAA,EAAW,eAAe,MAAA,EAAQ,WAAA,EAAa,eAAA,EAAiB,QAAQ,CAAC,CAAA;AAajG,SAAS,kBAAA,CAAmB,MAAA,EAA6B,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAmC;AAIhI,EAAA,MAAM,YAAY,CAAC,WAAA,EAAa,eAAA,EAAiB,QAAQ,EAAE,MAAA,CAAO,CAAC,GAAA,KAAQ,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,GAAG,CAAC,CAAA;AACnI,EAAA,IAAI,SAAA,CAAU,MAAA,GAAS,CAAA,IAAK,MAAA,CAAO,UAAA,EAAY;AAC3C,IAAA,MAAM,IAAI,iBAAA;AAAA,MACN,MAAA,CAAO,UAAA;AAAA,MACP,CAAA,EAAG,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAAE,IAAA,CAAK,OAAO,CAAC,CAAA,8FAAA;AAAA,KACnD;AAAA,EACJ;AAEA,EAAA,MAAM,OAAA,GAAU,IAAI,0BAA0B,CAAA;AAC9C,EAAA,MAAM,aAAa,OAAA,GAAUD,OAAAA,CAAQ,OAAA,CAAQ,GAAA,IAAO,OAAO,CAAA,GAAI,MAAA,CAAO,MAAA,CAAO,eAAeA,OAAAA,CAAQ,MAAA,CAAO,SAAS,MAAA,CAAO,MAAA,CAAO,YAAY,CAAA,GAAI,IAAA;AAClJ,EAAA,IAAI,CAAC,YAAY,OAAO,IAAA;AAExB,EAAA,IAAI,CAACE,UAAAA,CAAW,UAAU,CAAA,EAAG;AACzB,IAAA,MAAM,IAAI,iBAAA,CAAkB,UAAA,EAAY,mCAAmC,OAAA,GAAU,0BAAA,GAA6B,iCAAiC,CAAA,eAAA,CAAiB,CAAA;AAAA,EACxK;AACA,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACA,IAAA,GAAA,GAAMD,aAAa,UAAU,CAAA;AAC7B,IAAA,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,QAAA,CAAS,OAAO,CAAC,CAAA;AAAA,EAC7C,SAAS,GAAA,EAAK;AACV,IAAA,MAAM,IAAI,iBAAA,CAAkB,UAAA,EAAa,GAAA,CAAc,OAAO,CAAA;AAAA,EAClE;AACA,EAAA,IAAI,OAAO,WAAW,QAAA,IAAY,MAAA,KAAW,QAAQ,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AACxE,IAAA,MAAM,IAAI,iBAAA,CAAkB,UAAA,EAAY,2CAA2C,CAAA;AAAA,EACvF;AACA,EAAA,MAAM,QAAA,GAAW,MAAA;AAEjB,EAAA,MAAM,WAAqB,EAAC;AAC5B,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,QAAQ,GAAG,IAAI,CAAC,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA,EAAG,QAAA,CAAS,IAAA,CAAK,CAAA,aAAA,EAAgB,GAAG,CAAA,CAAA,CAAG,CAAA;AAC1G,EAAA,IAAI,MAAA,CAAO,MAAA,CAAO,IAAA,KAAS,MAAA,EAAW;AAClC,IAAA,QAAA,CAAS,IAAA;AAAA,MACL,kBAAkB,MAAA,CAAO,UAAA,GAAa,KAAK,MAAA,CAAO,UAAU,MAAM,EAAE,CAAA,iGAAA;AAAA,KACxE;AAAA,EACJ;AACA,EAAA,IAAI,QAAA,CAAS,IAAA,EAAM,cAAA,KAAmB,MAAA,EAAW;AAC7C,IAAA,QAAA,CAAS,KAAK,sJAAsJ,CAAA;AAAA,EACxK;AAEA,EAAA,MAAM,cAAqC,EAAC;AAC5C,EAAA,IAAI,QAAA,CAAS,cAAc,MAAA,EAAW;AAClC,IAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,QAAA,CAAS,SAAS,CAAA,EAAG,QAAA,CAAS,KAAK,8BAA8B,CAAA;AAAA,SAC/E;AACD,MAAA,KAAA,MAAW,CAAC,KAAA,EAAO,KAAK,KAAK,QAAA,CAAS,SAAA,CAAU,SAAQ,EAAG;AACvD,QAAA,MAAM,KAAA,GAAQ,aAAa,KAAK,CAAA,CAAA,CAAA;AAChC,QAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,EAAM;AAC7C,UAAA,QAAA,CAAS,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,kBAAA,CAAoB,CAAA;AAC1C,UAAA;AAAA,QACJ;AACA,QAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,EAAG;AAClC,UAAA,IAAI,QAAQ,QAAA,EAAU,QAAA,CAAS,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,8GAAA,CAAgH,CAAA;AAAA,eAAA,IACnJ,CAAC,CAAC,IAAA,EAAM,WAAA,EAAa,UAAA,EAAY,kBAAkB,CAAA,CAAE,QAAA,CAAS,GAAG,CAAA,WAAY,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,eAAA,EAAkB,GAAG,CAAA,CAAA,CAAG,CAAA;AAAA,QAC/H;AACA,QAAA,IAAI,OAAO,KAAA,CAAM,EAAA,KAAO,QAAA,IAAY,KAAA,CAAM,EAAA,CAAG,MAAA,KAAW,CAAA,EAAG,QAAA,CAAS,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,iCAAA,CAAmC,CAAA;AACpH,QAAA,IAAI,MAAM,QAAA,KAAa,MAAA,KAAc,CAAC,KAAA,CAAM,QAAQ,KAAA,CAAM,QAAQ,CAAA,IAAK,KAAA,CAAM,SAAS,IAAA,CAAK,CAAC,CAAA,KAAM,OAAO,MAAM,QAAQ,CAAA,CAAA;AACnH,UAAA,QAAA,CAAS,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,wCAAA,CAA0C,CAAA;AACpE,QAAA,IAAI,MAAM,gBAAA,KAAqB,MAAA,KAAc,CAAC,KAAA,CAAM,QAAQ,KAAA,CAAM,gBAAgB,CAAA,IAAK,KAAA,CAAM,iBAAiB,IAAA,CAAK,CAAC,MAAM,OAAO,CAAA,KAAM,QAAQ,CAAA,CAAA,EAAI;AAC/I,UAAA,QAAA,CAAS,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,gDAAA,CAAkD,CAAA;AAAA,QAC5E;AACA,QAAA,IAAI,OAAO,KAAA,CAAM,SAAA,KAAc,YAAY,KAAA,CAAM,SAAA,CAAU,WAAW,CAAA,EAAG;AACrE,UAAA,QAAA,CAAS,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,+EAAA,CAAiF,CAAA;AACvG,UAAA;AAAA,QACJ;AACA,QAAA,MAAM,MAAA,GAAS,GAAA,CAAI,KAAA,CAAM,SAAS,CAAA;AAClC,QAAA,IAAI,CAAC,MAAA,EAAQ;AACT,UAAA,QAAA,CAAS,IAAA,CAAK,CAAA,EAAG,KAAK,CAAA,GAAA,EAAM,MAAA,CAAO,KAAA,CAAM,EAAE,CAAC,CAAA,6BAAA,EAAgC,KAAA,CAAM,SAAS,CAAA,WAAA,CAAa,CAAA;AACxG,UAAA;AAAA,QACJ;AACA,QAAA,WAAA,CAAY,IAAA,CAAK;AAAA,UACb,IAAI,KAAA,CAAM,EAAA;AAAA,UACV,MAAA;AAAA,UACA,GAAI,MAAM,QAAA,KAAa,MAAA,GAAY,EAAE,QAAA,EAAU,KAAA,CAAM,QAAA,EAAS,GAAI,EAAC;AAAA,UACnE,GAAI,MAAM,gBAAA,KAAqB,MAAA,GAAY,EAAE,gBAAA,EAAkB,KAAA,CAAM,gBAAA,EAAiB,GAAI;AAAC,SAC9F,CAAA;AAAA,MACL;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,IAAI,QAAA,CAAS,WAAW,MAAA,EAAW;AAC/B,IAAA,IAAI;AACA,MAAA,oBAAA,CAAqB,SAAS,MAAM,CAAA;AACpC,MAAA,IAAI,SAAS,MAAA,CAAO,SAAA,EAAW,QAAA,CAAS,MAAA,GAAS,EAAE,GAAG,QAAA,CAAS,MAAA,EAAQ,SAAA,EAAWD,QAAQD,OAAAA,CAAQ,UAAU,GAAG,QAAA,CAAS,MAAA,CAAO,SAAS,CAAA,EAAE;AAAA,IAC9I,SAAS,KAAA,EAAO;AACZ,MAAA,QAAA,CAAS,IAAA,CAAM,MAAgB,OAAO,CAAA;AAAA,IAC1C;AAAA,EACJ;AACA,EAAA,MAAM,gBAAgB,QAAA,CAAS,aAAA;AAC/B,EAAA,IAAI,kBAAkB,MAAA,EAAW;AAC7B,IAAA,IAAI,OAAO,aAAA,KAAkB,QAAA,IAAY,aAAA,KAAkB,IAAA,IAAQ,KAAA,CAAM,OAAA,CAAQ,aAAa,CAAA,EAAG,QAAA,CAAS,IAAA,CAAK,mCAAmC,CAAA;AAAA,SAC7I;AACD,MAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,aAAa,CAAA,EAAG,IAAI,GAAA,KAAQ,gBAAA,EAAkB,QAAA,CAAS,IAAA,CAAK,CAAA,4BAAA,EAA+B,GAAG,CAAA,CAAA,CAAG,CAAA;AAC/H,MAAA,MAAM,QAAQ,aAAA,CAAc,cAAA;AAC5B,MAAA,IAAI,KAAA,KAAU,WAAc,OAAO,KAAA,KAAU,YAAY,KAAA,KAAU,IAAA,IAAQ,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,CAAA;AAC1F,QAAA,QAAA,CAAS,KAAK,qEAAqE,CAAA;AAAA,WAAA,IAC9E,KAAA,EAAO;AACZ,QAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC/C,UAAA,IAAI,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACrE,YAAA,QAAA,CAAS,IAAA,CAAK,CAAA,8BAAA,EAAiC,IAAI,CAAA,0DAAA,CAA4D,CAAA;AAC/G,YAAA;AAAA,UACJ;AACA,UAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AAC/B,YAAA,IAAI,GAAA,KAAQ,YAAA,IAAgB,GAAA,KAAQ,aAAA,EAAe,QAAA,CAAS,KAAK,CAAA,8BAAA,EAAiC,IAAI,CAAA,iBAAA,EAAoB,GAAG,CAAA,CAAA,CAAG,CAAA;AAAA,QACxI;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,IAAI,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG,MAAM,IAAI,kBAAkB,UAAA,EAAY,QAAA,CAAS,IAAA,CAAK,IAAI,CAAC,CAAA;AAEpF,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA,EAAS,UAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAO,GAAI,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAAA,IACpE;AAAA,GACJ;AACJ;AAQO,IAAM,iBAAA,GAAN,cAAgC,KAAA,CAAM;AAAA,EACzC,WAAA,CACoB,YACA,MAAA,EAClB;AACE,IAAA,KAAA;AAAA,MACI,CAAA,2CAAA,EAA8C,UAAU,CAAA,EAAA,EAAK,MAAM,CAAA,wJAAA;AAAA,KAGvE;AAPgB,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAOhB,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EAChB;AAAA,EAToB,UAAA;AAAA,EACA,MAAA;AASxB;AAyBO,SAAS,iBAAiB,IAAA,EAAoC;AACjE,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,EAAI;AACxB,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,GAAA,CAAI,mBAAmB,CAAA;AAE/C,EAAA,IAAI,UAAA,GAA4B,IAAA;AAEhC,EAAA,IAAI,IAAA,EAAM;AACN,IAAA,UAAA,GAAaC,OAAAA,CAAQ,KAAK,IAAI,CAAA;AAAA,EAClC,WAAW,OAAA,EAAS;AAChB,IAAA,UAAA,GAAaA,OAAAA,CAAQ,KAAK,OAAO,CAAA;AAAA,EACrC,CAAA,MAAO;AACH,IAAA,MAAM,MAAA,GAASA,OAAAA,CAAQ,GAAA,EAAK,kBAAA,EAAoB,uBAAuB,CAAA;AACvE,IAAA,MAAM,MAAA,GAASA,OAAAA,CAAQ,GAAA,EAAK,sBAAsB,CAAA;AAClD,IAAA,IAAIE,UAAAA,CAAW,MAAM,CAAA,EAAG;AACpB,MAAA,UAAA,GAAa,MAAA;AAAA,IACjB,CAAA,MAAA,IAAWA,UAAAA,CAAW,MAAM,CAAA,EAAG;AAC3B,MAAA,UAAA,GAAa,MAAA;AACb,MAAA,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,QACX,uCAAuC,MAAM,CAAA,aAAA,EAAqBF,QAAQ,GAAA,EAAK,kBAAA,EAAoB,uBAAuB,CAAC,CAAA;AAAA;AAAA,OAC/H;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,IAAI,CAAC,UAAA,EAAY;AACb,IAAA,OAAO,EAAE,MAAA,EAAQ,IAAI,OAAA,EAAS,GAAA,EAAK,YAAY,IAAA,EAAK;AAAA,EACxD;AACA,EAAA,IAAI,CAACE,UAAAA,CAAW,UAAU,CAAA,EAAG;AACzB,IAAA,MAAM,MAAA,GAAS,OAAO,sCAAA,GAAyC,mBAAA;AAC/D,IAAA,MAAM,IAAI,iBAAA,CAAkB,UAAA,EAAY,CAAA,uBAAA,EAA0B,MAAM,CAAA,eAAA,CAAiB,CAAA;AAAA,EAC7F;AAEA,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACA,IAAA,MAAA,GAAS,IAAA,CAAK,KAAA,CAAMD,YAAAA,CAAa,UAAA,EAAY,OAAO,CAAC,CAAA;AAAA,EACzD,SAAS,GAAA,EAAK;AACV,IAAA,MAAM,IAAI,iBAAA,CAAkB,UAAA,EAAa,GAAA,CAAc,OAAO,CAAA;AAAA,EAClE;AACA,EAAA,IAAI,OAAO,WAAW,QAAA,IAAY,MAAA,KAAW,QAAQ,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AACxE,IAAA,MAAM,IAAI,iBAAA,CAAkB,UAAA,EAAY,2CAA2C,CAAA;AAAA,EACvF;AACA,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAyB,SAASF,OAAAA,CAAQ,UAAU,GAAG,UAAA,EAAW;AACvF;AAsDO,SAAS,iBAAA,CAAkB,KAA0C,OAAA,EAAwC;AAChH,EAAA,MAAM,OAAA,GAAiC,EAAE,GAAA,EAAK,IAAA,EAAM,MAAM,IAAA,EAAK;AAE/D,EAAA,IAAI,QAAQ,MAAA,IAAa,GAAA,KAAQ,IAAA,IAAQ,GAAA,KAAQ,OAAO,OAAO,OAAA;AAC/D,EAAA,IAAI,GAAA,KAAQ,IAAA,EAAM,OAAO,iBAAA,CAAkB,KAAK,OAAO,CAAA;AAEvD,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,EAAK;AAC/B,EAAA,IAAI,UAAU,EAAA,IAAM,KAAA,KAAU,GAAA,IAAO,KAAA,KAAU,SAAS,OAAO,OAAA;AAC/D,EAAA,IAAI,UAAU,GAAA,IAAO,KAAA,KAAU,QAAQ,OAAO,iBAAA,CAAkB,KAAK,OAAO,CAAA;AAE5E,EAAA,MAAM,MAAA,GAAS,CAAC,GAAA,MAAwC;AAAA,IACpD,GAAA,EAAK,IAAA;AAAA,IACL,IAAA,EAAM,IAAA;AAAA,IACN,KAAA,EACI,CAAA,EAAG,GAAG,CAAA,2HAAA,EACsB,OAAO,CAAA,+BAAA;AAAA,GAC3C,CAAA;AAGA,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,IAAI,CAAA,EAAG;AACxB,IAAA,OAAO,MAAA,CAAO,CAAA,kBAAA,EAAqB,KAAK,CAAA,wGAAA,CAA0G,CAAA;AAAA,EACtJ;AAIA,EAAA,MAAM,cAAA,GAAiB,KAAA,CAAM,UAAA,CAAW,GAAG,CAAA;AAC3C,EAAA,MAAM,SAAA,GAAY,2BAAA,CAA4B,IAAA,CAAK,KAAK,CAAA;AACxD,EAAA,IAAI,CAAC,cAAA,IAAkB,CAAC,SAAA,EAAW;AAC/B,IAAA,OAAO,MAAA,CAAO,CAAA,kBAAA,EAAqB,KAAK,CAAA,sEAAA,CAAwE,CAAA;AAAA,EACpH;AAEA,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI;AACA,IAAA,QAAA,GAAW,IAAI,GAAA,CAAI,KAAA,EAAO,OAAO,CAAA;AAAA,EACrC,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,MAAA,CAAO,CAAA,kBAAA,EAAqB,KAAK,CAAA,iCAAA,CAAmC,CAAA;AAAA,EAC/E;AAEA,EAAA,IAAI,SAAA,EAAW;AAGX,IAAA,IAAI,QAAA,CAAS,QAAA,KAAa,OAAA,IAAW,QAAA,CAAS,aAAa,QAAA,EAAU;AACjE,MAAA,OAAO,OAAO,CAAA,kBAAA,EAAqB,KAAK,CAAA,6CAAA,EAAgD,QAAA,CAAS,QAAQ,CAAA,aAAA,CAAe,CAAA;AAAA,IAC5H;AACA,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,OAAO,CAAA;AAC5B,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,IAAA,CAAK,MAAA,EAAQ;AACjC,MAAA,OAAO,MAAA,CAAO,qBAAqB,KAAK,CAAA,gBAAA,EAAmB,SAAS,MAAM,CAAA,4BAAA,EAA+B,IAAA,CAAK,MAAM,CAAA,EAAA,CAAI,CAAA;AAAA,IAC5H;AAAA,EACJ;AAEA,EAAA,OAAO,EAAE,GAAA,EAAK,QAAA,CAAS,UAAS,EAAG,IAAA,EAAM,SAAS,QAAA,EAAS;AAC/D","file":"chunk-2GDTE7BA.js","sourcesContent":["/**\n * Minimal ZIP extractor for `.mcpb` bundles, built on `node:zlib` only.\n *\n * A `.mcpb` bundle is an ordinary ZIP archive. The broker deliberately avoids\n * the `@anthropic-ai/mcpb` package so it stays compatible with the format\n * without being coupled to Anthropic's tooling, hence this small reader.\n *\n * Supports the two compression methods used in practice: stored (0) and\n * deflate (8). ZIP64 archives are rejected with a clear error (`.mcpb` bundles\n * are small and never need it). Any trailing bytes after the End Of Central\n * Directory record (e.g. a native PKCS#7 signature block) are ignored, since\n * extraction is driven entirely by the central directory.\n */\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, relative, resolve } from \"node:path\";\nimport { inflateRawSync } from \"node:zlib\";\n\nconst EOCD_SIGNATURE = 0x06054b50;\nconst CENTRAL_HEADER_SIGNATURE = 0x02014b50;\nconst LOCAL_HEADER_SIGNATURE = 0x04034b50;\n\n/** Locates the End Of Central Directory record by scanning backwards. */\nfunction findEocd(buf: Buffer): number {\n // The EOCD is 22 bytes plus a comment of up to 65535 bytes.\n const minPos = Math.max(0, buf.length - 22 - 0xffff);\n for (let pos = buf.length - 22; pos >= minPos; pos--) {\n if (buf.readUInt32LE(pos) === EOCD_SIGNATURE) return pos;\n }\n throw new Error(\"not a ZIP archive (no End Of Central Directory record found)\");\n}\n\n/**\n * Extracts every entry of the `.mcpb` archive at `mcpbPath` into `destDir`.\n * `destDir` is created if missing. Entries whose resolved path would escape\n * `destDir` (zip-slip) are rejected.\n */\nexport function unzipMcpb(mcpbPath: string, destDir: string): void {\n const buf = readFileSync(mcpbPath);\n const eocd = findEocd(buf);\n\n const totalEntries = buf.readUInt16LE(eocd + 10);\n const centralDirOffset = buf.readUInt32LE(eocd + 16);\n if (centralDirOffset === 0xffffffff || totalEntries === 0xffff) {\n throw new Error(\"ZIP64 archives are not supported\");\n }\n\n const absDest = resolve(destDir);\n mkdirSync(absDest, { recursive: true });\n\n let pos = centralDirOffset;\n for (let i = 0; i < totalEntries; i++) {\n if (buf.readUInt32LE(pos) !== CENTRAL_HEADER_SIGNATURE) {\n throw new Error(`corrupt ZIP: bad central directory header at offset ${pos}`);\n }\n const method = buf.readUInt16LE(pos + 10);\n const compressedSize = buf.readUInt32LE(pos + 20);\n const uncompressedSize = buf.readUInt32LE(pos + 24);\n const nameLen = buf.readUInt16LE(pos + 28);\n const extraLen = buf.readUInt16LE(pos + 30);\n const commentLen = buf.readUInt16LE(pos + 32);\n const localHeaderOffset = buf.readUInt32LE(pos + 42);\n const name = buf.toString(\"utf8\", pos + 46, pos + 46 + nameLen);\n pos += 46 + nameLen + extraLen + commentLen;\n\n if (compressedSize === 0xffffffff || uncompressedSize === 0xffffffff || localHeaderOffset === 0xffffffff) {\n throw new Error(\"ZIP64 archives are not supported\");\n }\n\n // Resolve the destination path and reject zip-slip escapes.\n const target = resolve(absDest, name);\n const rel = relative(absDest, target);\n if (rel.startsWith(\"..\") || isAbsolute(rel)) {\n throw new Error(`unsafe ZIP entry path (zip-slip): ${name}`);\n }\n\n // Directory entry.\n if (name.endsWith(\"/\")) {\n mkdirSync(target, { recursive: true });\n continue;\n }\n\n // Parse the local header to locate the entry's data.\n if (buf.readUInt32LE(localHeaderOffset) !== LOCAL_HEADER_SIGNATURE) {\n throw new Error(`corrupt ZIP: bad local header for \"${name}\"`);\n }\n const localNameLen = buf.readUInt16LE(localHeaderOffset + 26);\n const localExtraLen = buf.readUInt16LE(localHeaderOffset + 28);\n const dataStart = localHeaderOffset + 30 + localNameLen + localExtraLen;\n const compressed = buf.subarray(dataStart, dataStart + compressedSize);\n\n let data: Buffer;\n if (method === 0) {\n data = compressed;\n } else if (method === 8) {\n data = inflateRawSync(compressed);\n } else {\n throw new Error(`unsupported ZIP compression method ${method} for \"${name}\"`);\n }\n\n mkdirSync(dirname(target), { recursive: true });\n writeFileSync(target, data);\n }\n}\n","/**\n * Loads a local `.mcpb` bundle into an {@link IStdioUpstreamConfig}.\n *\n * A `.mcpb` bundle is a ZIP holding a `manifest.json` whose `server.mcp_config`\n * describes a stdio MCP server process. The broker:\n *\n * 1. verifies a **detached signature** of the `.mcpb` file against a trusted\n * public key (PEM), integrity *and* provenance, using `node:crypto` only;\n * 2. unpacks the archive;\n * 3. reads the manifest, expands the `mcp_config` placeholders, and produces a\n * `IStdioUpstreamConfig` that the existing upstream wiring spawns.\n *\n * The broker stays compatible with the `.mcpb` format without depending on the\n * `@anthropic-ai/mcpb` package: the bundle's *own* (native PKCS#7) signature is\n * never parsed: the detached signature layer is the broker's trust anchor.\n *\n * Any failure (missing files, bad signature, malformed manifest, missing\n * `user_config` value) is logged and yields `null`: the bundle is refused and\n * no process is ever spawned. Never auto-runs an unverified bundle.\n */\nimport { createPublicKey, verify, X509Certificate, type KeyObject } from \"node:crypto\";\nimport { existsSync, readFileSync, rmSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve, sep } from \"node:path\";\nimport { unzipMcpb } from \"./mcpb.unzip\";\nimport type { IStdioUpstreamConfig } from \"../stdio.upstream\";\n\n/** A `.mcpb` bundle entry from the broker config file. */\nexport interface IMcpbBundleConfig {\n /** Provider slot name the bundle is bound to. */\n name: string;\n /** Path to the `.mcpb` file. */\n path: string;\n /** Path to the trusted public key (PEM) verifying the detached signature. */\n publicKey: string;\n /** Path to the detached signature file. Defaults to `<path>.sig`. */\n signature?: string;\n /** Values substituted into the manifest's `${user_config.*}` placeholders. */\n userConfig?: Record<string, string | number | boolean | Array<string | number>>;\n /** When `false`, the bundle stays out of the `_all` aggregate slot. Defaults to `true`. */\n aggregate?: boolean;\n}\n\ninterface IMcpConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n platform_overrides?: Record<string, { command?: string; args?: string[]; env?: Record<string, string> }>;\n}\n\n/** Loads a PEM that holds either an X.509 certificate or a bare public key. */\nfunction loadPublicKey(pem: string): KeyObject {\n if (pem.includes(\"BEGIN CERTIFICATE\")) {\n return new X509Certificate(pem).publicKey;\n }\n return createPublicKey(pem);\n}\n\n/** Verifies the detached `signature` of `data` against `publicKey`. */\nfunction verifyDetachedSignature(data: Buffer, signature: Buffer, publicKey: KeyObject): boolean {\n // Ed25519/Ed448 are used without a separate hash algorithm; RSA/EC need one.\n const keyType = publicKey.asymmetricKeyType;\n const algorithm = keyType === \"ed25519\" || keyType === \"ed448\" ? null : \"sha256\";\n return verify(algorithm, data, publicKey, signature);\n}\n\n/** Resolves a single `${...}` placeholder key, or `undefined` when unknown. */\nfunction resolvePlaceholder(key: string, dirname: string, userConfig: IMcpbBundleConfig[\"userConfig\"]): string | number | boolean | Array<string | number> | undefined {\n if (key === \"__dirname\") return dirname;\n if (key === \"HOME\") return homedir();\n if (key === \"DESKTOP\") return join(homedir(), \"Desktop\");\n if (key === \"DOCUMENTS\") return join(homedir(), \"Documents\");\n if (key === \"DOWNLOADS\") return join(homedir(), \"Downloads\");\n if (key === \"pathSeparator\" || key === \"/\") return sep;\n if (key.startsWith(\"user_config.\")) {\n const name = key.slice(\"user_config.\".length);\n const value = userConfig?.[name];\n if (value === undefined) throw new Error(`missing user_config value: \"${name}\"`);\n return value;\n }\n return undefined;\n}\n\n/** Expands placeholders in a scalar string (command, env value). */\nfunction expandScalar(input: string, dirname: string, userConfig: IMcpbBundleConfig[\"userConfig\"]): string {\n return input.replace(/\\$\\{([^}]+)\\}/g, (match, key: string) => {\n const value = resolvePlaceholder(key, dirname, userConfig);\n if (value === undefined) return match; // unknown placeholder, leave verbatim\n if (Array.isArray(value)) throw new Error(`placeholder \"\\${${key}}\" is multi-valued and cannot be used here`);\n return String(value);\n });\n}\n\n/** Expands one manifest argument; a standalone multi-valued placeholder spreads. */\nfunction expandArg(arg: string, dirname: string, userConfig: IMcpbBundleConfig[\"userConfig\"]): string[] {\n const standalone = /^\\$\\{(user_config\\.[^}]+)\\}$/.exec(arg);\n if (standalone) {\n const value = resolvePlaceholder(standalone[1], dirname, userConfig);\n if (Array.isArray(value)) return value.map(String);\n return [String(value)];\n }\n return [expandScalar(arg, dirname, userConfig)];\n}\n\n/**\n * Verifies, unpacks and resolves a `.mcpb` bundle into an `IStdioUpstreamConfig`.\n *\n * @param cfg The bundle entry from the broker config.\n * @param baseDir Directory the bundle paths are resolved against.\n * @returns A ready upstream config, or `null` when the bundle is refused.\n */\nexport async function loadMcpbBundle(cfg: IMcpbBundleConfig, baseDir: string): Promise<IStdioUpstreamConfig | null> {\n const tag = `[mcp-broker] mcpb bundle \"${cfg.name}\"`;\n try {\n const mcpbPath = resolve(baseDir, cfg.path);\n const publicKeyPath = resolve(baseDir, cfg.publicKey);\n const signaturePath = cfg.signature ? resolve(baseDir, cfg.signature) : `${mcpbPath}.sig`;\n\n for (const [label, file] of [\n [\"bundle\", mcpbPath],\n [\"public key\", publicKeyPath],\n [\"signature\", signaturePath],\n ] as const) {\n if (!existsSync(file)) {\n console.error(`${tag}: ${label} file not found at ${file}, bundle refused.`);\n return null;\n }\n }\n\n // ── Signature verification (mandatory) ──────────────────────────────\n const bundleBytes = readFileSync(mcpbPath);\n const signatureBytes = readFileSync(signaturePath);\n const publicKey = loadPublicKey(readFileSync(publicKeyPath, \"utf8\"));\n if (!verifyDetachedSignature(bundleBytes, signatureBytes, publicKey)) {\n console.error(`${tag}: detached signature is invalid for the configured public key, bundle refused.`);\n return null;\n }\n\n // ── Unpack ──────────────────────────────────────────────────────────\n const outputDir = resolve(baseDir, \".cache\", \"mcpb\", cfg.name);\n rmSync(outputDir, { recursive: true, force: true });\n unzipMcpb(mcpbPath, outputDir);\n\n // ── Manifest → mcp_config ───────────────────────────────────────────\n const manifestPath = join(outputDir, \"manifest.json\");\n if (!existsSync(manifestPath)) {\n console.error(`${tag}: manifest.json missing inside the bundle, bundle refused.`);\n return null;\n }\n const manifest = JSON.parse(readFileSync(manifestPath, \"utf8\")) as { server?: { mcp_config?: IMcpConfig } };\n const mcpConfig = manifest.server?.mcp_config;\n if (!mcpConfig) {\n console.error(`${tag}: manifest has no server.mcp_config, bundle refused.`);\n return null;\n }\n\n // Platform-specific overrides replace the base fields when present.\n const override = mcpConfig.platform_overrides?.[process.platform];\n const command = override?.command ?? mcpConfig.command;\n const rawArgs = override?.args ?? mcpConfig.args ?? [];\n const rawEnv = { ...mcpConfig.env, ...override?.env };\n if (!command) {\n console.error(`${tag}: manifest mcp_config has no command, bundle refused.`);\n return null;\n }\n\n // ── Placeholder expansion ───────────────────────────────────────────\n const expandedCommand = expandScalar(command, outputDir, cfg.userConfig);\n const expandedArgs = rawArgs.flatMap((arg) => expandArg(arg, outputDir, cfg.userConfig));\n const expandedEnv: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawEnv)) {\n expandedEnv[key] = expandScalar(value, outputDir, cfg.userConfig);\n }\n\n return {\n name: cfg.name,\n command: expandedCommand,\n args: expandedArgs,\n env: Object.keys(expandedEnv).length > 0 ? expandedEnv : undefined,\n aggregate: cfg.aggregate ?? true,\n };\n } catch (err) {\n console.error(`${tag}: ${(err as Error).message}, bundle refused.`);\n return null;\n }\n}\n\n/** @deprecated Use {@link IMcpbBundleConfig}. */\nexport type McpbBundleConfig = IMcpbBundleConfig;\n","import { validateLimitsConfig, type ILimitsConfig } from \"./limits/controller\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport type { IAuthorizationPolicyConfig } from \"./authorization/index\";\nimport type { IProviderCredential } from \"./auth/provider.auth\";\nimport type { IProtectedSlot } from \"./authority/declaration\";\n\nexport interface IBrokerAuthConfig extends IAuthorizationPolicyConfig {\n /** Master switch. Absent/`false` keeps the broker unauthenticated. */\n enabled?: boolean;\n /** Public origin the broker is reached at (e.g. `https://mcp.example.com`). */\n publicBaseUrl?: string;\n /** Authorization server issuer URL(s) advertised in the metadata. */\n authorizationServers?: string[];\n /** URL of the authorization server's JWKS document. */\n jwks?: string;\n /** Expected token issuer. Defaults to the sole `authorizationServers` entry. */\n issuer?: string;\n /** Scopes advertised in the metadata `scopes_supported`. */\n scopesSupported?: string[];\n /** Baseline scope(s) required to reach any slot. */\n requiredScopes?: string[];\n /** Per-slot required-scope overrides (e.g. an admin scope for `_broker`). */\n perSlotScopes?: Record<string, string[]>;\n /**\n * Per-provider scope requirements for the `_all` aggregate. Deprecated in\n * favor of hierarchical policy assignments.\n */\n providerScopes?: Record<string, string[]>;\n /**\n * Shared secret every provider must present to occupy a slot. Independent\n * of client authorization.\n */\n providerSecret?: string;\n}\n\n/**\n * Shape of the optional JSON config file consumed by `bin.ts` at startup.\n * Every field is optional. Environment variables (`MCP_BROKER_*`) always win\n * over file values, and file values win over the built-in defaults.\n *\n * @example\n * ```json\n * {\n * \"port\": 3001,\n * \"locale\": \"fr\",\n * \"tls\": { \"cert\": \"certs/cert.pem\", \"key\": \"certs/key.pem\" },\n * \"stdioUpstreams\": [\n * { \"name\": \"fs\", \"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/data\"] }\n * ]\n * }\n * ```\n */\nexport interface IBrokerConfig {\n /** TCP port. Maps to `MCP_BROKER_PORT`. */\n port?: number;\n\n /** Bind host. Maps to `MCP_BROKER_HOST`. */\n host?: string;\n\n /** Force protocol (`http`/`https`) regardless of cert presence. Maps to `MCP_BROKER_PROTOCOL`. */\n protocol?: \"http\" | \"https\";\n\n /** Locale fed to the broker grammar resolver. Maps to `MCP_BROKER_LOCALE`. */\n locale?: string;\n\n /** Bridge stdin/stdout for a Claude-Desktop-style client. Maps to `MCP_BROKER_STDIO_PROVIDER`. */\n stdioProvider?: string;\n\n /**\n * Logical broker name reported by `broker_info`.\n *\n * **Library-only today.** `WsTunnel` honors it\n * (`IWsTunnelOptions.brokerName`), but `WsTunnelBuilder` has no\n * `withBrokerName()`, so the CLI cannot forward it and setting it in\n * `config.json` has no effect. Set it through the programmatic API until\n * the setter exists.\n */\n brokerName?: string;\n\n /**\n * How often (ms) the broker pings each connected provider socket to check\n * it is still there. `0` disables the heartbeat. Maps to\n * `MCP_BROKER_PROVIDER_HEARTBEAT_MS`.\n *\n * A provider that misses a full interval is terminated and its slot freed,\n * which is what stops a half-open socket (a killed browser tab, a slept\n * laptop, a dropped VPN) from holding a slot for the ~2 hours the OS takes\n * to give up on the TCP connection, refusing every reconnect meanwhile.\n *\n * @default 30000\n */\n providerHeartbeatIntervalMs?: number;\n\n /**\n * How long (ms) the broker waits for a provider to answer one request\n * before failing it with a JSON-RPC error naming the slot. `0` disables the\n * deadline. Maps to `MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS`.\n *\n * Raise it if you host genuinely long-running tools; without it a provider\n * that stays connected and never answers (a throttled background browser\n * tab is the ordinary case) leaves the caller waiting forever.\n *\n * @default 60000\n */\n providerRequestTimeoutMs?: number;\n\n /**\n * Bounds on `resources/subscribe` bookkeeping. Each field maps to an env\n * var: `MCP_BROKER_MAX_SUBSCRIPTIONS_PER_CLIENT` (default 64),\n * `MCP_BROKER_MAX_SUBSCRIPTIONS_PER_SLOT` (default 1024),\n * `MCP_BROKER_MAX_RESOURCE_URI_LENGTH` (default 2048).\n */\n resourceSubscriptions?: {\n maxSubscriptionsPerClient?: number;\n maxSubscriptionsPerSlot?: number;\n maxResourceUriLength?: number;\n };\n\n /** Optional OTLP/HTTP trace export for provider telemetry notifications. */\n telemetry?: {\n /** Full traces endpoint, usually http://collector:4318/v1/traces. */\n otlpHttpEndpoint: string;\n headers?: Record<string, string>;\n timeoutMs?: number;\n serviceNamespace?: string;\n maxFrameBytes?: number;\n queueCapacity?: number;\n batchSize?: number;\n maxAttributes?: number;\n maxEvents?: number;\n };\n\n /**\n * What happens when a provider connects to a slot another socket already\n * holds. Maps to `MCP_BROKER_PROVIDER_TAKEOVER`.\n *\n * - `\"reject\"`: the incumbent always keeps the slot.\n * - `\"liveness\"` (default): the incumbent keeps it only while it answers\n * the heartbeat.\n * - `\"always\"`: the newcomer wins, but only when provider authentication is\n * configured and it authenticated as the same principal as the incumbent.\n * Without provider auth the broker falls back to `\"liveness\"` and says so,\n * because unconditional takeover would let anyone who can reach the URL\n * evict the real provider.\n *\n * @default \"liveness\"\n */\n providerTakeover?: \"reject\" | \"liveness\" | \"always\";\n\n /**\n * Browser origins allowed to reach `/<slot>/mcp`.\n *\n * Absent means no browser origin is accepted: a request carrying an\n * `Origin` header gets `403`, one carrying none (Claude Desktop, MCP\n * Inspector, any server-side SDK) always passes. The MCP specification asks\n * for this check, since without it any web page loaded in a browser that\n * can reach the broker could drive it.\n *\n * An array lists origins matched **exactly**. An object supplies a regular\n * expression tested against the whole `Origin` header. Also maps to\n * `MCP_BROKER_ALLOWED_ORIGINS` (comma-separated list form only; a pattern\n * needs the config file).\n *\n * @example\n * ```json\n * { \"allowedOrigins\": [\"https://app.example.com\", \"http://localhost:5173\"] }\n * { \"allowedOrigins\": { \"pattern\": \"^https://[a-z0-9-]+\\\\.example\\\\.com$\" } }\n * ```\n */\n allowedOrigins?: string[] | { pattern: string; flags?: string };\n\n /**\n * OAuth 2.1 resource-server authorization. When `enabled` is `true`, every\n * HTTP client request to a slot must carry a valid `Authorization: Bearer`\n * token issued for that slot, and the broker publishes Protected Resource\n * Metadata (RFC 9728). Absent/`false` ⇒ no authentication (trusted-network\n * mode, the historical behavior).\n *\n * Scalars also map to env vars (which win): `MCP_BROKER_AUTH_ENABLED`,\n * `MCP_BROKER_PUBLIC_BASE_URL`, `MCP_BROKER_JWKS`, `MCP_BROKER_ISSUER`.\n *\n * Deprecated here: put it in the security file ({@link securityFile}).\n * The broker refuses to start when both carry one.\n */\n auth?: IBrokerAuthConfig;\n\n /**\n * Path to the security file, resolved against this file's directory. It\n * holds `auth`, the `providers` table and `authorization.protectedSlots`,\n * so topology and security are written, read and protected separately.\n * Maps to `MCP_BROKER_SECURITY_FILE` (resolved against the cwd), which wins.\n */\n securityFile?: string;\n\n /**\n * URL paths (override the defaults). Every key is also settable through an\n * environment variable, which wins.\n *\n * Changing one moves an endpoint for **every** peer: a provider SDK, a\n * client, and the startup banner all have to agree. The two provider paths\n * are not interchangeable, they carry different framing:\n * `provider` is a prefix (`<provider>/<slot>`) speaking plain JSON-RPC\n * frames (`DirectTransport`), `providers` is matched exactly and speaks\n * multiplex envelopes (`MultiplexTransport`).\n */\n paths?: {\n /** Prefix for one-slot-per-socket provider connections. Maps to `MCP_BROKER_PROVIDER_PATH`. @default \"/provider\" */\n provider?: string;\n /** Exact path for multiplexed provider connections. Maps to `MCP_BROKER_PROVIDERS_PATH`. @default \"/providers\" */\n providers?: string;\n /** Prefix raw-WebSocket clients connect to. Maps to `MCP_BROKER_CLIENT_PATH`. @default \"/\" */\n client?: string;\n /** Per-slot suffix for the Streamable HTTP transport. Maps to `MCP_BROKER_MCP_PATH`. @default \"/mcp\" */\n mcp?: string;\n /** Per-slot suffix for the legacy SSE stream (GET). Maps to `MCP_BROKER_SSE_PATH`. @default \"/sse\" */\n sse?: string;\n /** Per-slot suffix for legacy SSE JSON-RPC posts. Maps to `MCP_BROKER_MESSAGES_PATH`. @default \"/messages\" */\n messages?: string;\n };\n\n /** TLS material as paths on disk. Resolved against the config file's directory. */\n tls?: {\n cert: string;\n key: string;\n };\n\n /**\n * Static-file serving alongside the JSON-RPC endpoints. JSON-RPC routes\n * always take precedence.\n */\n www?: {\n /**\n * Auto-launch the default browser on startup. Maps to `MCP_BROKER_OPEN`.\n *\n * - `false` / absent / `\"\"` / `\"0\"`: do not open anything.\n * - `true` / `\"1\"`: open the broker root, `<scheme>://localhost:<port>/`.\n * - a path (`\"/app/index.html\"`): open that page on this broker.\n * - an absolute URL on this broker's own origin: opened as given.\n *\n * A URL on any other origin is refused with a message, and so is any\n * other string. See {@link resolveOpenTarget} for why.\n *\n * The browser opens only when a static mount actually covers the\n * resolved path; otherwise the broker says which mounts exist instead of\n * launching a browser onto a 404.\n */\n open?: boolean | string;\n /** URL-prefix → directory mappings. Longest-prefix match wins. */\n mounts?: Array<{\n urlPrefix: string;\n dir: string;\n }>;\n };\n\n /** Stdio upstream providers spawned by the broker at startup. */\n stdioUpstreams?: Array<{\n name: string;\n command: string;\n args?: string[];\n env?: Record<string, string>;\n /** When `true`, the upstream joins the `_all` aggregate slot once connected. */\n aggregate?: boolean;\n }>;\n\n /**\n * Remote MCP servers the broker connects out to and exposes as provider\n * slots. Each entry is reached by URL (Streamable HTTP / SSE / WebSocket);\n * local servers should be shipped as `.mcpb` bundles instead.\n */\n mcpServers?: Array<{\n name: string;\n url: string;\n transport?: \"streamable-http\" | \"sse\" | \"websocket\";\n headers?: Record<string, string>;\n /** Defaults to `true`; set to `false` to exclude this upstream from the `_all` aggregate slot. */\n aggregate?: boolean;\n }>;\n\n /**\n * Local `.mcpb` bundles the broker loads at startup and runs as stdio\n * provider slots. A bundle is a ZIP with a `manifest.json`; the broker\n * verifies a detached signature against a trusted public key before\n * unpacking and spawning it.\n */\n mcpbBundles?: Array<{\n /** Provider slot name the bundle is bound to. */\n name: string;\n /** Path to the `.mcpb` file (resolved against the config file's directory). */\n path: string;\n /** Path to the trusted public key (PEM) used to verify the detached signature. */\n publicKey: string;\n /** Path to the detached signature file. Defaults to `<path>.sig`. */\n signature?: string;\n /** Values substituted into the manifest's `${user_config.*}` placeholders. */\n userConfig?: Record<string, string | number | boolean | Array<string | number>>;\n /** Defaults to `true`; set to `false` to exclude this bundle from the `_all` aggregate slot. */\n aggregate?: boolean;\n }>;\n}\n\n/**\n * Returned by {@link loadBrokerConfig}. The {@link config} is the parsed JSON;\n * {@link baseDir} is the directory used to resolve relative paths inside it\n * (the directory containing the config file when one was found, otherwise\n * `process.cwd()`).\n */\nexport interface ILoadedBrokerConfig {\n config: IBrokerConfig;\n baseDir: string;\n /** Absolute path of the config file that was loaded, or `null` if none. */\n sourcePath: string | null;\n}\n\n/** Default folder name (relative to `process.cwd()`) holding broker-local files. */\nexport const DEFAULT_CONFIG_DIR = \".mcp-broker\";\n\n/** Default config filename inside {@link DEFAULT_CONFIG_DIR}. */\nexport const DEFAULT_CONFIG_FILENAME = \"config.json\";\n\n/** Legacy flat config filename at the cwd root (pre-`.mcp-broker/` layout). */\nexport const LEGACY_CONFIG_FILENAME = \"mcp-broker.config.json\";\n\n/** One entry of the security file's `providers` table: the secret is named, never written. */\nexport interface ISecurityProviderEntry {\n /** Provider principal id. */\n id: string;\n /** Name of the environment variable holding this provider's secret. */\n secretEnv: string;\n /** Subjects this provider holds when it calls the broker as a client. */\n subjects?: string[];\n /** Resource patterns this provider may publish into; absent means all. */\n allowedResources?: string[];\n}\n\n/**\n * Shape of the security file: everything that decides who may do what, kept\n * apart from the topology in `config.json`.\n */\nexport interface ISecurityConfig {\n limits?: ILimitsConfig;\n /** The same block as `config.json`'s `auth`, minus `providerSecret` (use `MCP_BROKER_PROVIDER_SECRET`). */\n auth?: IBrokerAuthConfig;\n /** One identity per provider. */\n providers?: ISecurityProviderEntry[];\n authorization?: {\n /** Slots only one provider may call and one may publish, keyed by slot name. */\n protectedSlots?: Record<string, IProtectedSlot>;\n };\n}\n\n/** A loaded security file, with the secrets it names resolved from the environment. */\nexport interface ILoadedSecurityConfig {\n readonly security: ISecurityConfig;\n /** Absolute path of the file. */\n readonly sourcePath: string;\n /** First 12 hex digits of the file's SHA-256: what `policyVersion` starts with. */\n readonly version: string;\n /** The `providers` table, secrets resolved. */\n readonly credentials: readonly IProviderCredential[];\n}\n\nconst SECURITY_KEYS = new Set([\"$schema\", \"description\", \"auth\", \"providers\", \"authorization\", \"limits\"]);\n\n/**\n * Loads the security file named by `MCP_BROKER_SECURITY_FILE` or by\n * `config.securityFile`, or returns `null` when neither names one.\n *\n * Fails closed, like {@link loadBrokerConfig}: a designated file that is\n * missing, unreadable, not a JSON object, or inconsistent throws a\n * {@link BrokerConfigError}, and so does a provider whose secret variable is\n * unset. So does a `config.json` that still carries `auth` next to a security\n * file: picking one of the two silently would leave the operator guessing\n * which rules are in force.\n */\nexport function loadSecurityConfig(loaded: ILoadedBrokerConfig, env: NodeJS.ProcessEnv = process.env): ILoadedSecurityConfig | null {\n // Keys that only the security file may carry, refused in config.json\n // whether or not a security file exists: left there, they would be\n // ignored, and a protection the operator wrote would silently not exist.\n const misplaced = [\"providers\", \"authorization\", \"limits\"].filter((key) => Object.prototype.hasOwnProperty.call(loaded.config, key));\n if (misplaced.length > 0 && loaded.sourcePath) {\n throw new BrokerConfigError(\n loaded.sourcePath,\n `${misplaced.map((k) => `\"${k}\"`).join(\" and \")} belong in the security file (securityFile / MCP_BROKER_SECURITY_FILE), not in the config file`\n );\n }\n\n const envPath = env[\"MCP_BROKER_SECURITY_FILE\"];\n const sourcePath = envPath ? resolve(process.cwd(), envPath) : loaded.config.securityFile ? resolve(loaded.baseDir, loaded.config.securityFile) : null;\n if (!sourcePath) return null;\n\n if (!existsSync(sourcePath)) {\n throw new BrokerConfigError(sourcePath, `the security file designated by ${envPath ? \"MCP_BROKER_SECURITY_FILE\" : \"securityFile in the config file\"} does not exist`);\n }\n let raw: Buffer;\n let parsed: unknown;\n try {\n raw = readFileSync(sourcePath);\n parsed = JSON.parse(raw.toString(\"utf-8\")) as unknown;\n } catch (err) {\n throw new BrokerConfigError(sourcePath, (err as Error).message);\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new BrokerConfigError(sourcePath, \"the top-level value must be a JSON object\");\n }\n const security = parsed as ISecurityConfig;\n\n const problems: string[] = [];\n for (const key of Object.keys(security)) if (!SECURITY_KEYS.has(key)) problems.push(`unknown key \"${key}\"`);\n if (loaded.config.auth !== undefined) {\n problems.push(\n `the config file${loaded.sourcePath ? ` (${loaded.sourcePath})` : \"\"} also carries \"auth\"; move it into the security file, so there is one source of security settings`\n );\n }\n if (security.auth?.providerSecret !== undefined) {\n problems.push('\"auth.providerSecret\" would put a secret in clear in the file; set MCP_BROKER_PROVIDER_SECRET instead, or give each provider an entry in \"providers\"');\n }\n\n const credentials: IProviderCredential[] = [];\n if (security.providers !== undefined) {\n if (!Array.isArray(security.providers)) problems.push('\"providers\" must be an array');\n else {\n for (const [index, entry] of security.providers.entries()) {\n const label = `providers[${index}]`;\n if (typeof entry !== \"object\" || entry === null) {\n problems.push(`${label} must be an object`);\n continue;\n }\n for (const key of Object.keys(entry)) {\n if (key === \"secret\") problems.push(`${label}: \"secret\" would put a secret in clear in the file; name the environment variable that holds it in \"secretEnv\"`);\n else if (![\"id\", \"secretEnv\", \"subjects\", \"allowedResources\"].includes(key)) problems.push(`${label}: unknown key \"${key}\"`);\n }\n if (typeof entry.id !== \"string\" || entry.id.length === 0) problems.push(`${label}: \"id\" must be a non-empty string`);\n if (entry.subjects !== undefined && (!Array.isArray(entry.subjects) || entry.subjects.some((v) => typeof v !== \"string\")))\n problems.push(`${label}: \"subjects\" must be an array of strings`);\n if (entry.allowedResources !== undefined && (!Array.isArray(entry.allowedResources) || entry.allowedResources.some((v) => typeof v !== \"string\"))) {\n problems.push(`${label}: \"allowedResources\" must be an array of strings`);\n }\n if (typeof entry.secretEnv !== \"string\" || entry.secretEnv.length === 0) {\n problems.push(`${label}: \"secretEnv\" must name the environment variable holding this provider's secret`);\n continue;\n }\n const secret = env[entry.secretEnv];\n if (!secret) {\n problems.push(`${label} (\"${String(entry.id)}\"): the environment variable ${entry.secretEnv} is not set`);\n continue;\n }\n credentials.push({\n id: entry.id,\n secret,\n ...(entry.subjects !== undefined ? { subjects: entry.subjects } : {}),\n ...(entry.allowedResources !== undefined ? { allowedResources: entry.allowedResources } : {}),\n });\n }\n }\n }\n\n if (security.limits !== undefined) {\n try {\n validateLimitsConfig(security.limits);\n if (security.limits.storeFile) security.limits = { ...security.limits, storeFile: resolve(dirname(sourcePath), security.limits.storeFile) };\n } catch (error) {\n problems.push((error as Error).message);\n }\n }\n const authorization = security.authorization;\n if (authorization !== undefined) {\n if (typeof authorization !== \"object\" || authorization === null || Array.isArray(authorization)) problems.push('\"authorization\" must be an object');\n else {\n for (const key of Object.keys(authorization)) if (key !== \"protectedSlots\") problems.push(`authorization: unknown key \"${key}\"`);\n const slots = authorization.protectedSlots;\n if (slots !== undefined && (typeof slots !== \"object\" || slots === null || Array.isArray(slots)))\n problems.push('\"authorization.protectedSlots\" must be an object keyed by slot name');\n else if (slots) {\n for (const [slot, entry] of Object.entries(slots)) {\n if (typeof entry !== \"object\" || entry === null || Array.isArray(entry)) {\n problems.push(`authorization.protectedSlots[\"${slot}\"] must be { \"declaredBy\": \"<id>\", \"publishedBy\": \"<id>\" }`);\n continue;\n }\n for (const key of Object.keys(entry))\n if (key !== \"declaredBy\" && key !== \"publishedBy\") problems.push(`authorization.protectedSlots[\"${slot}\"]: unknown key \"${key}\"`);\n }\n }\n }\n }\n\n if (problems.length > 0) throw new BrokerConfigError(sourcePath, problems.join(\"; \"));\n\n return {\n security,\n sourcePath,\n version: createHash(\"sha256\").update(raw!).digest(\"hex\").slice(0, 12),\n credentials,\n };\n}\n\n/**\n * A config file was designated or found, and could not be used.\n *\n * Thrown rather than logged: starting with an empty config instead would start\n * a broker with no authentication and no authorization at all.\n */\nexport class BrokerConfigError extends Error {\n constructor(\n public readonly sourcePath: string,\n public readonly detail: string\n ) {\n super(\n `[mcp-broker] Cannot use the config file at ${sourcePath}: ${detail}. ` +\n `The broker refuses to start rather than run without the security settings that file may hold. ` +\n `Fix the file, or point MCP_BROKER_CONFIG at a valid one.`\n );\n this.name = \"BrokerConfigError\";\n }\n}\n\n/**\n * Loads the broker config from a JSON file.\n *\n * Discovery order:\n * 1. The `path` argument when provided (explicit override).\n * 2. The `MCP_BROKER_CONFIG` env var.\n * 3. `./.mcp-broker/config.json` relative to `process.cwd()`.\n * 4. `./mcp-broker.config.json` relative to `process.cwd()` (legacy layout ,\n * a deprecation warning is written to stderr).\n *\n * When no file is found by discovery (steps 3 and 4), returns the built-in\n * empty config with `baseDir = process.cwd()`.\n *\n * Fails closed otherwise: throws a {@link BrokerConfigError} when a file was\n * designated (steps 1 and 2) but does not exist, or when the file that was\n * found cannot be read, is not valid JSON, or is not a JSON object. This used\n * to log a warning and return the empty config, and the empty config is a\n * broker with no authentication and no authorization: one stray comma in a\n * secured deployment's config opened every slot to everyone.\n *\n * Paths inside the config file are intended to be resolved against\n * {@link ILoadedBrokerConfig.baseDir} by the consumer.\n */\nexport function loadBrokerConfig(path?: string): ILoadedBrokerConfig {\n const cwd = process.cwd();\n const envPath = process.env[\"MCP_BROKER_CONFIG\"];\n\n let sourcePath: string | null = null;\n\n if (path) {\n sourcePath = resolve(cwd, path);\n } else if (envPath) {\n sourcePath = resolve(cwd, envPath);\n } else {\n const modern = resolve(cwd, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME);\n const legacy = resolve(cwd, LEGACY_CONFIG_FILENAME);\n if (existsSync(modern)) {\n sourcePath = modern;\n } else if (existsSync(legacy)) {\n sourcePath = legacy;\n process.stderr.write(\n `[mcp-broker] Using legacy config at ${legacy}. ` + `Move it to ${resolve(cwd, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME)} ` + `to silence this warning.\\n`\n );\n }\n }\n\n if (!sourcePath) {\n return { config: {}, baseDir: cwd, sourcePath: null };\n }\n if (!existsSync(sourcePath)) {\n const origin = path ? \"the path given to loadBrokerConfig()\" : \"MCP_BROKER_CONFIG\";\n throw new BrokerConfigError(sourcePath, `the file designated by ${origin} does not exist`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(sourcePath, \"utf-8\")) as unknown;\n } catch (err) {\n throw new BrokerConfigError(sourcePath, (err as Error).message);\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new BrokerConfigError(sourcePath, \"the top-level value must be a JSON object\");\n }\n return { config: parsed as IBrokerConfig, baseDir: dirname(sourcePath), sourcePath };\n}\n\n/**\n * Outcome of {@link resolveOpenTarget}. Exactly one of the three states holds:\n *\n * - `{ url: null, path: null }` and no `error`: nothing should be opened.\n * - `{ url, path }`: open `url`; `path` is what a static mount has to cover.\n * - `{ url: null, path: null, error }`: the value was refused, `error` is a\n * sentence to print verbatim that names both the fault and the fix.\n */\nexport interface IOpenTargetResolution {\n /** Absolute URL to hand to the platform opener, or `null` to open nothing. */\n url: string | null;\n /** Path portion of {@link url} (always starts with `/`), or `null`. */\n path: string | null;\n /** Set when the raw value was refused. Human-readable, names the fix. */\n error?: string;\n}\n\n/**\n * Resolves `www.open` / `MCP_BROKER_OPEN` into an absolute URL to launch.\n *\n * Lives here rather than in `bin.ts` because `bin.ts` starts a server the\n * moment it is imported, so nothing in it can be unit-tested.\n *\n * Accepted forms (leading/trailing whitespace is trimmed):\n *\n * | `raw` | result |\n * |----------------------------|-------------------------------------------|\n * | `undefined`, `false`, `\"\"`, `\"0\"`, `\"false\"` | open nothing |\n * | `true`, `\"1\"`, `\"true\"` | `<baseUrl>/` |\n * | `\"/app/\"`, `\"/index.html\"` | resolved against `baseUrl` |\n * | `\"http://localhost:3000/x\"`| passed through when the origin is `baseUrl`'s |\n * | anything else | refused, with `error` explaining why |\n *\n * Two refusals are security-load-bearing rather than pedantic:\n *\n * - **A foreign origin is refused.** Auto-opening is a startup convenience, and\n * a config file (or an env var inherited from a parent process) that can make\n * the broker launch a browser at an arbitrary site is a phishing primitive\n * with no upside: nothing about starting a broker requires visiting another\n * host. Open it yourself, or point `open` at a page this broker serves.\n * - **`//host/path` is refused** even though it starts with `/`: it is a\n * protocol-relative URL, so `new URL(\"//evil.example/x\", base)` resolves to\n * `evil.example` and a naive \"starts with a slash so it is local\" test lets\n * it through.\n *\n * Anything that is neither is refused rather than forwarded, because the\n * platform opener performs no validation of its own: a typo'd relative path is\n * handed to the shell and can launch a local file or a registered application.\n *\n * @param raw The configured value (`config.www.open`) or the raw env string.\n * @param baseUrl Origin this broker is reachable at, e.g. `http://localhost:3000`.\n */\nexport function resolveOpenTarget(raw: boolean | string | undefined | null, baseUrl: string): IOpenTargetResolution {\n const nothing: IOpenTargetResolution = { url: null, path: null };\n\n if (raw === undefined || raw === null || raw === false) return nothing;\n if (raw === true) return resolveOpenTarget(\"/\", baseUrl);\n\n const value = String(raw).trim();\n if (value === \"\" || value === \"0\" || value === \"false\") return nothing;\n if (value === \"1\" || value === \"true\") return resolveOpenTarget(\"/\", baseUrl);\n\n const refuse = (why: string): IOpenTargetResolution => ({\n url: null,\n path: null,\n error:\n `${why} Set it to true (or \"1\") for the broker root, to a path on this broker such as \"/app/index.html\", ` +\n `or to an absolute URL on ${baseUrl}. Leave it out to open nothing.`,\n });\n\n // Protocol-relative: starts with a slash but resolves off-origin.\n if (value.startsWith(\"//\")) {\n return refuse(`Refusing to open \"${value}\": a value starting with \"//\" is a protocol-relative URL and points at another host, not at this broker.`);\n }\n\n // Only two spellings are accepted, so that a bare word (almost always a\n // typo) is reported rather than silently resolved onto the broker root.\n const isAbsolutePath = value.startsWith(\"/\");\n const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value);\n if (!isAbsolutePath && !hasScheme) {\n return refuse(`Refusing to open \"${value}\": it is neither a path starting with \"/\" nor an absolute http(s) URL.`);\n }\n\n let resolved: URL;\n try {\n resolved = new URL(value, baseUrl);\n } catch {\n return refuse(`Refusing to open \"${value}\": it is not a valid path or URL.`);\n }\n\n if (hasScheme) {\n // Decide on the parsed form, not on the raw string: `localhost:3000/x`\n // looks like a host but parses with the scheme `localhost:`.\n if (resolved.protocol !== \"http:\" && resolved.protocol !== \"https:\") {\n return refuse(`Refusing to open \"${value}\": only http and https URLs are opened, and \"${resolved.protocol}\" is neither.`);\n }\n const base = new URL(baseUrl);\n if (resolved.origin !== base.origin) {\n return refuse(`Refusing to open \"${value}\": it points at ${resolved.origin}, which is not this broker (${base.origin}).`);\n }\n }\n\n return { url: resolved.toString(), path: resolved.pathname };\n}\n\n/** @deprecated Use {@link IBrokerAuthConfig}. */\nexport type BrokerAuthConfig = IBrokerAuthConfig;\n\n/** @deprecated Use {@link IBrokerConfig}. */\nexport type BrokerConfig = IBrokerConfig;\n\n/** @deprecated Use {@link ILoadedBrokerConfig}. */\nexport type LoadedBrokerConfig = ILoadedBrokerConfig;\n"]}
|