@cyanmycelium/mcp-broker 1.0.1 → 1.2.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.
Files changed (188) hide show
  1. package/.mcp-broker.example/README.md +3 -3
  2. package/README.md +6 -2
  3. package/dist/bin.d.ts +0 -1
  4. package/dist/bin.js +224 -253
  5. package/dist/bin.js.map +1 -1
  6. package/dist/chunk-FTDKH2C4.js +3670 -0
  7. package/dist/chunk-FTDKH2C4.js.map +1 -0
  8. package/dist/{broker/grammars → grammars}/claude/en.json +1 -1
  9. package/dist/{broker/grammars → grammars}/claude/fr.json +1 -1
  10. package/dist/index.d.ts +1790 -22
  11. package/dist/index.js +2 -18
  12. package/dist/index.js.map +1 -1
  13. package/package.json +9 -7
  14. package/scripts/copy-assets.mjs +16 -10
  15. package/scripts/gen-cert.mjs +5 -5
  16. package/scripts/pack-mcpb.mjs +5 -5
  17. package/scripts/sign-bundle.mjs +6 -6
  18. package/src/auth/auth.config.ts +3 -3
  19. package/src/auth/auth.types.ts +34 -59
  20. package/src/auth/http.auth.ts +18 -22
  21. package/src/auth/index.ts +12 -12
  22. package/src/auth/jwt.validator.ts +3 -3
  23. package/src/auth/provider.auth.ts +1 -1
  24. package/src/auth/resource.metadata.ts +14 -21
  25. package/src/authorization/audit.ts +2 -2
  26. package/src/authorization/capability.classifier.ts +1 -1
  27. package/src/authorization/index.ts +13 -13
  28. package/src/authorization/policy.engine.ts +3 -3
  29. package/src/authorization/policy.types.ts +1 -1
  30. package/src/authorization/runtime.ts +5 -5
  31. package/src/authorization/slot.resource.ts +1 -1
  32. package/src/authorization/subject.mapper.ts +1 -1
  33. package/src/bin.ts +86 -8
  34. package/src/broker/adapters/broker.adapter.info.ts +1 -1
  35. package/src/broker/adapters/broker.adapter.providers.ts +1 -1
  36. package/src/broker/aggregate/aggregate.catalog.ts +1 -1
  37. package/src/broker/aggregate/aggregate.server.ts +7 -7
  38. package/src/broker/aggregate/provider.client.session.ts +2 -2
  39. package/src/broker/behaviors/broker.behavior.info.ts +3 -3
  40. package/src/broker/behaviors/broker.behavior.providers.ts +5 -5
  41. package/src/broker/broker.grammars.ts +9 -9
  42. package/src/broker/broker.server.ts +6 -6
  43. package/src/broker/grammars/claude/en.json +1 -1
  44. package/src/broker/grammars/claude/fr.json +1 -1
  45. package/src/broker/index.ts +9 -9
  46. package/src/config.ts +25 -3
  47. package/src/index.ts +24 -24
  48. package/src/{mcpb.loader.ts → mcpb/mcpb.loader.ts} +11 -11
  49. package/src/{mcpb.unzip.ts → mcpb/mcpb.unzip.ts} +2 -2
  50. package/src/remote.transports.ts +3 -3
  51. package/src/remote.upstream.ts +3 -3
  52. package/src/stdio.upstream.ts +2 -2
  53. package/src/upstream.ts +2 -2
  54. package/src/ws/ws.interfaces.ts +357 -0
  55. package/src/{ws.tunnel.builder.ts → ws/ws.tunnel.builder.ts} +28 -5
  56. package/src/{ws.tunnel.ts → ws/ws.tunnel.ts} +231 -467
  57. package/web/README.md +3 -3
  58. package/web/broker-self-mcp.html +9 -9
  59. package/web/css/styles.css +1 -1
  60. package/web/demos/DemoPlaceholder.html +6 -6
  61. package/web/demos/broker-explorer/css/app.css +1 -1
  62. package/web/demos/broker-explorer/index.html +2 -2
  63. package/web/demos/broker-explorer/js/app.js +4 -4
  64. package/web/demos/broker-explorer/js/mcp-ws-client.js +2 -2
  65. package/web/demos/oauth-lab/README.md +26 -0
  66. package/web/demos/oauth-lab/config.json +1 -0
  67. package/web/demos/oauth-lab/js/app.js +40 -16
  68. package/web/demos/oauth-lab/server/smoke-test.mjs +57 -10
  69. package/web/demos/provider-tunnel/css/app.css +1 -1
  70. package/web/demos/provider-tunnel/index.html +3 -3
  71. package/web/demos/provider-tunnel/js/app.js +10 -10
  72. package/web/demos/provider-tunnel/js/toolbox-server.js +5 -5
  73. package/web/index.html +11 -11
  74. package/web/js/lib/broker-tunnel.js +12 -12
  75. package/dist/auth/auth.config.d.ts +0 -40
  76. package/dist/auth/auth.config.js +0 -62
  77. package/dist/auth/auth.config.js.map +0 -1
  78. package/dist/auth/auth.types.d.ts +0 -119
  79. package/dist/auth/auth.types.js +0 -38
  80. package/dist/auth/auth.types.js.map +0 -1
  81. package/dist/auth/http.auth.d.ts +0 -51
  82. package/dist/auth/http.auth.js +0 -121
  83. package/dist/auth/http.auth.js.map +0 -1
  84. package/dist/auth/index.d.ts +0 -16
  85. package/dist/auth/index.js +0 -12
  86. package/dist/auth/index.js.map +0 -1
  87. package/dist/auth/jwt.validator.d.ts +0 -35
  88. package/dist/auth/jwt.validator.js +0 -46
  89. package/dist/auth/jwt.validator.js.map +0 -1
  90. package/dist/auth/provider.auth.d.ts +0 -51
  91. package/dist/auth/provider.auth.js +0 -80
  92. package/dist/auth/provider.auth.js.map +0 -1
  93. package/dist/auth/resource.metadata.d.ts +0 -32
  94. package/dist/auth/resource.metadata.js +0 -26
  95. package/dist/auth/resource.metadata.js.map +0 -1
  96. package/dist/authorization/audit.d.ts +0 -14
  97. package/dist/authorization/audit.js +0 -20
  98. package/dist/authorization/audit.js.map +0 -1
  99. package/dist/authorization/capability.classifier.d.ts +0 -30
  100. package/dist/authorization/capability.classifier.js +0 -85
  101. package/dist/authorization/capability.classifier.js.map +0 -1
  102. package/dist/authorization/index.d.ts +0 -13
  103. package/dist/authorization/index.js +0 -8
  104. package/dist/authorization/index.js.map +0 -1
  105. package/dist/authorization/policy.engine.d.ts +0 -11
  106. package/dist/authorization/policy.engine.js +0 -192
  107. package/dist/authorization/policy.engine.js.map +0 -1
  108. package/dist/authorization/policy.types.d.ts +0 -92
  109. package/dist/authorization/policy.types.js +0 -2
  110. package/dist/authorization/policy.types.js.map +0 -1
  111. package/dist/authorization/resource.path.d.ts +0 -27
  112. package/dist/authorization/resource.path.js +0 -122
  113. package/dist/authorization/resource.path.js.map +0 -1
  114. package/dist/authorization/runtime.d.ts +0 -22
  115. package/dist/authorization/runtime.js +0 -34
  116. package/dist/authorization/runtime.js.map +0 -1
  117. package/dist/authorization/slot.resource.d.ts +0 -12
  118. package/dist/authorization/slot.resource.js +0 -41
  119. package/dist/authorization/slot.resource.js.map +0 -1
  120. package/dist/authorization/subject.mapper.d.ts +0 -15
  121. package/dist/authorization/subject.mapper.js +0 -67
  122. package/dist/authorization/subject.mapper.js.map +0 -1
  123. package/dist/broker/adapters/broker.adapter.info.d.ts +0 -16
  124. package/dist/broker/adapters/broker.adapter.info.js +0 -43
  125. package/dist/broker/adapters/broker.adapter.info.js.map +0 -1
  126. package/dist/broker/adapters/broker.adapter.providers.d.ts +0 -18
  127. package/dist/broker/adapters/broker.adapter.providers.js +0 -61
  128. package/dist/broker/adapters/broker.adapter.providers.js.map +0 -1
  129. package/dist/broker/aggregate/aggregate.catalog.d.ts +0 -66
  130. package/dist/broker/aggregate/aggregate.catalog.js +0 -119
  131. package/dist/broker/aggregate/aggregate.catalog.js.map +0 -1
  132. package/dist/broker/aggregate/aggregate.server.d.ts +0 -66
  133. package/dist/broker/aggregate/aggregate.server.js +0 -257
  134. package/dist/broker/aggregate/aggregate.server.js.map +0 -1
  135. package/dist/broker/aggregate/provider.client.session.d.ts +0 -52
  136. package/dist/broker/aggregate/provider.client.session.js +0 -140
  137. package/dist/broker/aggregate/provider.client.session.js.map +0 -1
  138. package/dist/broker/behaviors/broker.behavior.info.d.ts +0 -15
  139. package/dist/broker/behaviors/broker.behavior.info.js +0 -41
  140. package/dist/broker/behaviors/broker.behavior.info.js.map +0 -1
  141. package/dist/broker/behaviors/broker.behavior.providers.d.ts +0 -19
  142. package/dist/broker/behaviors/broker.behavior.providers.js +0 -69
  143. package/dist/broker/behaviors/broker.behavior.providers.js.map +0 -1
  144. package/dist/broker/broker.context.d.ts +0 -63
  145. package/dist/broker/broker.context.js +0 -2
  146. package/dist/broker/broker.context.js.map +0 -1
  147. package/dist/broker/broker.grammars.d.ts +0 -132
  148. package/dist/broker/broker.grammars.js +0 -229
  149. package/dist/broker/broker.grammars.js.map +0 -1
  150. package/dist/broker/broker.server.d.ts +0 -68
  151. package/dist/broker/broker.server.js +0 -73
  152. package/dist/broker/broker.server.js.map +0 -1
  153. package/dist/broker/index.d.ts +0 -9
  154. package/dist/broker/index.js +0 -7
  155. package/dist/broker/index.js.map +0 -1
  156. package/dist/config.d.ts +0 -182
  157. package/dist/config.js +0 -61
  158. package/dist/config.js.map +0 -1
  159. package/dist/mcpb.loader.d.ts +0 -26
  160. package/dist/mcpb.loader.js +0 -161
  161. package/dist/mcpb.loader.js.map +0 -1
  162. package/dist/mcpb.unzip.d.ts +0 -6
  163. package/dist/mcpb.unzip.js +0 -95
  164. package/dist/mcpb.unzip.js.map +0 -1
  165. package/dist/remote.transports.d.ts +0 -18
  166. package/dist/remote.transports.js +0 -297
  167. package/dist/remote.transports.js.map +0 -1
  168. package/dist/remote.upstream.d.ts +0 -38
  169. package/dist/remote.upstream.js +0 -52
  170. package/dist/remote.upstream.js.map +0 -1
  171. package/dist/stdio.upstream.d.ts +0 -47
  172. package/dist/stdio.upstream.js +0 -85
  173. package/dist/stdio.upstream.js.map +0 -1
  174. package/dist/upstream.d.ts +0 -35
  175. package/dist/upstream.js +0 -2
  176. package/dist/upstream.js.map +0 -1
  177. package/dist/version.d.ts +0 -2
  178. package/dist/version.js +0 -9
  179. package/dist/version.js.map +0 -1
  180. package/dist/ws.tunnel.builder.d.ts +0 -176
  181. package/dist/ws.tunnel.builder.js +0 -271
  182. package/dist/ws.tunnel.builder.js.map +0 -1
  183. package/dist/ws.tunnel.d.ts +0 -455
  184. package/dist/ws.tunnel.js +0 -1425
  185. package/dist/ws.tunnel.js.map +0 -1
  186. /package/dist/{broker/grammars → grammars}/default/en.json +0 -0
  187. /package/dist/{broker/grammars → grammars}/default/fr.json +0 -0
  188. /package/dist/{broker/grammars → grammars}/default/zh.json +0 -0
@@ -31,7 +31,7 @@ Property-by-property educational guides:
31
31
  └── index.html
32
32
  ```
33
33
 
34
- A ready-made instance UI lives at [`node/web/`](../web/) — point a `www`
34
+ A ready-made instance UI lives at [`node/web/`](../web/), point a `www`
35
35
  mount at it (`"dir": "../web"`) to serve it. See [`node/web/README.md`](../web/README.md).
36
36
 
37
37
  ## Path resolution
@@ -41,14 +41,14 @@ config file** (i.e. `.mcp-broker/`). So `"certs/cert.pem"` in the config
41
41
  points at `.mcp-broker/certs/cert.pem`. The folder is self-contained.
42
42
 
43
43
  Env vars (`MCP_BROKER_TLS_CERT`, `MCP_BROKER_WWW_DIR`, ...) are still
44
- resolved against `process.cwd()` — they are the deploy-time override
44
+ resolved against `process.cwd()`: they are the deploy-time override
45
45
  mechanism and not tied to the config file's location.
46
46
 
47
47
  ## `.mcpb` bundles
48
48
 
49
49
  `mcpbBundles` entries load local `.mcpb` bundles as stdio provider slots.
50
50
  Each bundle is verified against a **detached signature** before it is
51
- unpacked and run — the broker never spawns an unverified bundle.
51
+ unpacked and run: the broker never spawns an unverified bundle.
52
52
 
53
53
  1. Generate a signing key pair (once):
54
54
  `node ../scripts/sign-bundle.mjs keygen bundles`
package/README.md CHANGED
@@ -32,7 +32,7 @@ The broker starts on `http://localhost:3000` by default.
32
32
 
33
33
  Two sources, env vars **always win** over the file. The file is the static baseline you ship with the broker; env vars are deploy-specific overrides.
34
34
 
35
- ### Option A — `.mcp-broker/` folder (recommended)
35
+ ### Option A: `.mcp-broker/` folder (recommended)
36
36
 
37
37
  Drop a `.mcp-broker/` folder next to where you launch the broker. Paths
38
38
  inside `config.json` are resolved against this folder, so it stays
@@ -79,7 +79,7 @@ cp -r node_modules/@cyanmycelium/mcp-broker/.mcp-broker.example .mcp-broker
79
79
 
80
80
  Full reference (every field, defaults, recipes, grammar overrides): **[docs/config.md](docs/config.md)**.
81
81
 
82
- ### Option B — Environment variables
82
+ ### Option B: Environment variables
83
83
 
84
84
  | Variable | Default | Notes |
85
85
  |---|---|---|
@@ -111,6 +111,10 @@ providers, and filter the `_all` aggregate per caller.
111
111
 
112
112
  Enabled via the `auth` config block (or env vars). Minimal `.mcp-broker/config.json`:
113
113
 
114
+ The resource paths below use a compact ISA-95 / IEC 62264-aligned industrial
115
+ profile. The engine remains domain-neutral, does not claim full ISA-95
116
+ compliance, and can map UMD-style namespaces through `slotResources`.
117
+
114
118
  ```json
115
119
  {
116
120
  "auth": {
package/dist/bin.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  #!/usr/bin/env node
2
- export {};
package/dist/bin.js CHANGED
@@ -1,77 +1,63 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Standalone entry-point that starts the mcp-broker WebSocket server.
4
- *
5
- * ## Configuration sources (highest priority first)
6
- *
7
- * 1. Environment variables (`MCP_BROKER_*`).
8
- * 2. JSON config file. Resolved in order:
9
- * a. `MCP_BROKER_CONFIG` env var.
10
- * b. `./.mcp-broker/config.json` in the current working directory.
11
- * c. `./mcp-broker.config.json` (legacy, with a deprecation warning).
12
- * 3. Built-in defaults.
13
- *
14
- * ## Path resolution
15
- *
16
- * - Env-var paths (`MCP_BROKER_*_DIR`, `MCP_BROKER_TLS_*`) are resolved
17
- * against `process.cwd()`.
18
- * - Config-file paths (`tls.cert`, `www.mounts[*].dir`, `stdioUpstreams[*]`,
19
- * `mcpbBundles[*]`) are resolved against the **config file's directory** — so a config in
20
- * `./.mcp-broker/config.json` referring to `"certs/cert.pem"` points at
21
- * `./.mcp-broker/certs/cert.pem`. The folder is self-contained.
22
- *
23
- * ## Local grammar overrides
24
- *
25
- * When `.mcp-broker/grammars/<userAgent>/<locale>.json` files exist next to
26
- * the config file, they are merged **on top of** the packaged grammars. Lets
27
- * users customize tool/resource descriptions for their organization without
28
- * forking the package.
29
- *
30
- * ## Environment variables
31
- *
32
- * | Variable | Default | Notes |
33
- * |--------------------------------|----------|--------------------------------------------------------|
34
- * | MCP_BROKER_CONFIG | (none) | Path to a JSON config file (see above). |
35
- * | MCP_BROKER_PORT | 3000 | |
36
- * | MCP_BROKER_HOST | 0.0.0.0 | |
37
- * | MCP_BROKER_PROVIDER_PATH | /provider| Prefix for provider WS |
38
- * | MCP_BROKER_CLIENT_PATH | / | Prefix for raw WS clients |
39
- * | MCP_BROKER_MCP_PATH | /mcp | Suffix for Streamable HTTP transport |
40
- * | MCP_BROKER_WWW_DIR | (none) | Ergonomic shortcut: mount this directory at "/" |
41
- * | MCP_BROKER_BUNDLE_DIR | (none) | Ergonomic shortcut: mount this directory at "/bundle" |
42
- * | MCP_BROKER_OPEN | (unset) | "1" to auto-launch the default browser at the root URL |
43
- * | MCP_BROKER_TLS_CERT | (none) | Path to a PEM TLS certificate |
44
- * | MCP_BROKER_TLS_KEY | (none) | Path to a PEM private key |
45
- * | MCP_BROKER_PROTOCOL | auto | "http" to force plain, "https" to force TLS, otherwise |
46
- * | | | TLS is enabled iff cert+key are both set |
47
- * | MCP_BROKER_STDIO_PROVIDER | (none) | When set, stdin/stdout carry JSON-RPC for the named |
48
- * | | | provider (Claude Desktop bridge). |
49
- * | MCP_BROKER_LOCALE | en | Locale used for tool descriptions on the `_broker` |
50
- * | | | slot. ISO 639-1 base; regional variants accepted. |
51
- */
52
- import * as fs from "fs";
53
- import * as path from "path";
54
- import open from "open";
55
- import { WsTunnelBuilder } from "./index.js";
56
- import { loadBrokerConfig } from "./config.js";
57
- import { loadMcpbBundle } from "./mcpb.loader.js";
58
- // ---------------------------------------------------------------------------
59
- // Configuration
60
- // ---------------------------------------------------------------------------
61
- const { config, baseDir } = loadBrokerConfig();
62
- const cwd = process.cwd();
63
- /**
64
- * Fills an env var from the config file when the env var is not already set.
65
- * Used for **non-path** scalars — path-bearing fields are read directly so
66
- * they can be resolved against `baseDir` (config file's directory) instead
67
- * of `cwd` (deploy environment).
68
- */
2
+ import { PACKAGE_NAME, VERSION, loadBrokerConfig, WsTunnelBuilder, loadMcpbBundle } from './chunk-FTDKH2C4.js';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import open from 'open';
6
+
7
+ var argv = process.argv.slice(2);
8
+ if (argv.includes("--help") || argv.includes("-h")) {
9
+ printHelp();
10
+ process.exit(0);
11
+ }
12
+ if (argv.includes("--version") || argv.includes("-v")) {
13
+ process.stdout.write(`${PACKAGE_NAME} ${VERSION}
14
+ `);
15
+ process.exit(0);
16
+ }
17
+ function printHelp() {
18
+ process.stdout.write(
19
+ `
20
+ ${PACKAGE_NAME} ${VERSION}
21
+ WebSocket-based Model Context Protocol broker.
22
+
23
+ USAGE
24
+ npx @cyanmycelium/mcp-broker Start the broker (foreground)
25
+ npx @cyanmycelium/mcp-broker --help Show this help
26
+ npx @cyanmycelium/mcp-broker --version Print the version
27
+
28
+ The broker takes no positional arguments. Configure it with a
29
+ .mcp-broker/config.json file or MCP_BROKER_* environment variables
30
+ (env vars win over the file).
31
+
32
+ COMMON ENVIRONMENT VARIABLES
33
+ MCP_BROKER_CONFIG Path to a JSON config file
34
+ MCP_BROKER_PORT TCP port (default 3000)
35
+ MCP_BROKER_HOST Bind interface (default 0.0.0.0)
36
+ MCP_BROKER_PROTOCOL "http" | "https" (default: auto from TLS)
37
+ MCP_BROKER_TLS_CERT PEM certificate path (enables HTTPS/WSS)
38
+ MCP_BROKER_TLS_KEY PEM private key path
39
+ MCP_BROKER_STDIO_PROVIDER Bridge stdin/stdout to this provider (Claude Desktop)
40
+ MCP_BROKER_LOCALE Locale for _broker tool descriptions (default en)
41
+ MCP_BROKER_ALLOWED_ORIGINS Comma-separated browser origins allowed on /<slot>/mcp
42
+
43
+ AUTHORIZATION (OAuth 2.1, opt-in)
44
+ MCP_BROKER_AUTH_ENABLED "1" to require bearer tokens on client endpoints
45
+ MCP_BROKER_PUBLIC_BASE_URL Public origin, e.g. https://mcp.example.com
46
+ MCP_BROKER_JWKS Authorization server JWKS URL
47
+ MCP_BROKER_ISSUER Expected token issuer
48
+ MCP_BROKER_PROVIDER_SECRET Shared secret required from providers
49
+
50
+ Full reference: https://github.com/pandaGaume/mcp-broker/tree/main/node
51
+
52
+ `
53
+ );
54
+ }
55
+ var { config, baseDir } = loadBrokerConfig();
56
+ var cwd = process.cwd();
69
57
  function envFromConfig(envName, configValue) {
70
- if (configValue === undefined || configValue === null)
71
- return;
72
- if (process.env[envName] !== undefined && process.env[envName] !== "")
73
- return;
74
- process.env[envName] = String(configValue);
58
+ if (configValue === void 0 || configValue === null) return;
59
+ if (process.env[envName] !== void 0 && process.env[envName] !== "") return;
60
+ process.env[envName] = String(configValue);
75
61
  }
76
62
  envFromConfig("MCP_BROKER_PORT", config.port);
77
63
  envFromConfig("MCP_BROKER_HOST", config.host);
@@ -81,204 +67,189 @@ envFromConfig("MCP_BROKER_STDIO_PROVIDER", config.stdioProvider);
81
67
  envFromConfig("MCP_BROKER_PROVIDER_PATH", config.paths?.provider);
82
68
  envFromConfig("MCP_BROKER_CLIENT_PATH", config.paths?.client);
83
69
  envFromConfig("MCP_BROKER_MCP_PATH", config.paths?.mcp);
84
- envFromConfig("MCP_BROKER_OPEN", config.www?.open === true ? "1" : undefined);
85
- envFromConfig("MCP_BROKER_AUTH_ENABLED", config.auth?.enabled === true ? "1" : undefined);
70
+ envFromConfig("MCP_BROKER_OPEN", config.www?.open === true ? "1" : void 0);
71
+ envFromConfig("MCP_BROKER_AUTH_ENABLED", config.auth?.enabled === true ? "1" : void 0);
86
72
  envFromConfig("MCP_BROKER_PUBLIC_BASE_URL", config.auth?.publicBaseUrl);
87
73
  envFromConfig("MCP_BROKER_JWKS", config.auth?.jwks);
88
74
  envFromConfig("MCP_BROKER_ISSUER", config.auth?.issuer);
89
75
  envFromConfig("MCP_BROKER_PROVIDER_SECRET", config.auth?.providerSecret);
90
- const stdioProvider = process.env["MCP_BROKER_STDIO_PROVIDER"];
91
- // In stdio mode stdout is reserved for JSON-RPC — redirect all console output to stderr.
76
+ var stdioProvider = process.env["MCP_BROKER_STDIO_PROVIDER"];
92
77
  if (stdioProvider) {
93
- const toStderr = (...args) => process.stderr.write(args.join(" ") + "\n");
94
- console.log = toStderr;
95
- console.info = toStderr;
96
- console.warn = toStderr;
97
- console.error = toStderr;
78
+ const toStderr = (...args) => process.stderr.write(args.join(" ") + "\n");
79
+ console.log = toStderr;
80
+ console.info = toStderr;
81
+ console.warn = toStderr;
82
+ console.error = toStderr;
98
83
  }
99
- const port = parseInt(process.env["MCP_BROKER_PORT"] ?? "3000", 10);
100
- const host = process.env["MCP_BROKER_HOST"];
101
- const providerPath = process.env["MCP_BROKER_PROVIDER_PATH"] ?? "/provider";
102
- const clientPath = process.env["MCP_BROKER_CLIENT_PATH"] ?? "/";
103
- const mcpPath = process.env["MCP_BROKER_MCP_PATH"] ?? "/mcp";
104
- const ssePath = config.paths?.sse ?? "/sse";
105
- // ── TLS material ─────────────────────────────────────────────────────────────
106
- // Env var (relative to cwd) wins over config (relative to baseDir).
107
- const 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;
108
- const 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;
109
- const protocolOverride = process.env["MCP_BROKER_PROTOCOL"]?.toLowerCase();
110
- if (protocolOverride !== undefined && protocolOverride !== "http" && protocolOverride !== "https") {
111
- console.error(`[mcp-broker] Invalid MCP_BROKER_PROTOCOL="${protocolOverride}". Use "http" or "https".`);
112
- process.exit(1);
84
+ var port = parseInt(process.env["MCP_BROKER_PORT"] ?? "3000", 10);
85
+ var host = process.env["MCP_BROKER_HOST"];
86
+ var providerPath = process.env["MCP_BROKER_PROVIDER_PATH"] ?? "/provider";
87
+ var clientPath = process.env["MCP_BROKER_CLIENT_PATH"] ?? "/";
88
+ var mcpPath = process.env["MCP_BROKER_MCP_PATH"] ?? "/mcp";
89
+ var ssePath = config.paths?.sse ?? "/sse";
90
+ var 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;
91
+ var 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;
92
+ var protocolOverride = process.env["MCP_BROKER_PROTOCOL"]?.toLowerCase();
93
+ if (protocolOverride !== void 0 && protocolOverride !== "http" && protocolOverride !== "https") {
94
+ console.error(`[mcp-broker] Invalid MCP_BROKER_PROTOCOL="${protocolOverride}". Use "http" or "https".`);
95
+ process.exit(1);
113
96
  }
114
97
  if (protocolOverride === "https" && (!tlsCertPath || !tlsKeyPath)) {
115
- console.error("[mcp-broker] MCP_BROKER_PROTOCOL=https requires TLS cert+key (via config.tls or MCP_BROKER_TLS_CERT/KEY).");
116
- process.exit(1);
98
+ console.error("[mcp-broker] MCP_BROKER_PROTOCOL=https requires TLS cert+key (via config.tls or MCP_BROKER_TLS_CERT/KEY).");
99
+ process.exit(1);
100
+ }
101
+ var useTls = protocolOverride === "http" ? false : protocolOverride === "https" ? true : !!(tlsCertPath && tlsKeyPath);
102
+ var envOrigins = process.env["MCP_BROKER_ALLOWED_ORIGINS"]?.split(",").map((origin) => origin.trim()).filter(Boolean);
103
+ var allowedOrigins;
104
+ if (envOrigins?.length) {
105
+ allowedOrigins = envOrigins;
106
+ } else if (Array.isArray(config.allowedOrigins)) {
107
+ allowedOrigins = config.allowedOrigins;
108
+ } else if (config.allowedOrigins) {
109
+ try {
110
+ allowedOrigins = new RegExp(config.allowedOrigins.pattern, config.allowedOrigins.flags);
111
+ } catch (err) {
112
+ console.error(`[mcp-broker] Ignoring allowedOrigins.pattern: ${err.message}`);
113
+ }
117
114
  }
118
- const useTls = protocolOverride === "http" ? false : protocolOverride === "https" ? true : !!(tlsCertPath && tlsKeyPath);
119
- // ── Env-var static mount shortcuts (relative to cwd) ─────────────────────────
120
- const envWwwDir = process.env["MCP_BROKER_WWW_DIR"] ? path.resolve(cwd, process.env["MCP_BROKER_WWW_DIR"]) : null;
121
- const envBundleDir = process.env["MCP_BROKER_BUNDLE_DIR"] ? path.resolve(cwd, process.env["MCP_BROKER_BUNDLE_DIR"]) : null;
122
- // ---------------------------------------------------------------------------
123
- // Server bootstrap
124
- // ---------------------------------------------------------------------------
115
+ var envWwwDir = process.env["MCP_BROKER_WWW_DIR"] ? path.resolve(cwd, process.env["MCP_BROKER_WWW_DIR"]) : null;
116
+ var envBundleDir = process.env["MCP_BROKER_BUNDLE_DIR"] ? path.resolve(cwd, process.env["MCP_BROKER_BUNDLE_DIR"]) : null;
125
117
  async function main() {
126
- // Local grammar overrides: when `<baseDir>/grammars/` exists, the broker
127
- // server merges those JSON files on top of the packaged grammars.
128
- const localGrammarsDir = path.join(baseDir, "grammars");
129
- const hasLocalGrammars = fs.existsSync(localGrammarsDir);
130
- const builder = new WsTunnelBuilder().withPort(port).withProviderPath(providerPath).withClientPath(clientPath).withMcpPath(mcpPath);
131
- if (host) {
132
- builder.withHost(host);
133
- }
134
- if (useTls) {
135
- builder.withTlsFiles(tlsCertPath, tlsKeyPath);
136
- }
137
- // ── Static mounts ────────────────────────────────────────────────────────
138
- // First the env-var shortcuts (relative to cwd), then the config `www.mounts`
139
- // (relative to baseDir). Order matters: /bundle before / so the prefix router
140
- // can distinguish them. Subsequent registrations contribute additional mounts;
141
- // longest-prefix match wins at runtime.
142
- if (envBundleDir && fs.existsSync(envBundleDir)) {
143
- builder.withStaticMount("/bundle", envBundleDir);
144
- }
145
- if (envWwwDir && fs.existsSync(envWwwDir)) {
146
- builder.withStaticMount("/", envWwwDir);
118
+ const localGrammarsDir = path.join(baseDir, "grammars");
119
+ const hasLocalGrammars = fs.existsSync(localGrammarsDir);
120
+ const builder = new WsTunnelBuilder().withPort(port).withProviderPath(providerPath).withClientPath(clientPath).withMcpPath(mcpPath);
121
+ if (host) {
122
+ builder.withHost(host);
123
+ }
124
+ if (useTls) {
125
+ builder.withTlsFiles(tlsCertPath, tlsKeyPath);
126
+ }
127
+ if (allowedOrigins) {
128
+ builder.withAllowedOrigins(allowedOrigins);
129
+ }
130
+ if (envBundleDir && fs.existsSync(envBundleDir)) {
131
+ builder.withStaticMount("/bundle", envBundleDir);
132
+ }
133
+ if (envWwwDir && fs.existsSync(envWwwDir)) {
134
+ builder.withStaticMount("/", envWwwDir);
135
+ }
136
+ if (config.www?.mounts) {
137
+ for (const mount of config.www.mounts) {
138
+ const abs = path.resolve(baseDir, mount.dir);
139
+ if (fs.existsSync(abs)) {
140
+ builder.withStaticMount(mount.urlPrefix, abs);
141
+ } else {
142
+ console.warn(`[mcp-broker] www.mounts entry "${mount.urlPrefix}" \u2192 ${abs} skipped (directory not found).`);
143
+ }
147
144
  }
148
- if (config.www?.mounts) {
149
- for (const mount of config.www.mounts) {
150
- const abs = path.resolve(baseDir, mount.dir);
151
- if (fs.existsSync(abs)) {
152
- builder.withStaticMount(mount.urlPrefix, abs);
153
- }
154
- else {
155
- console.warn(`[mcp-broker] www.mounts entry "${mount.urlPrefix}" → ${abs} skipped (directory not found).`);
156
- }
157
- }
145
+ }
146
+ if (config.stdioUpstreams) {
147
+ for (const u of config.stdioUpstreams) {
148
+ builder.withStdioUpstream({
149
+ name: u.name,
150
+ command: u.command,
151
+ args: u.args,
152
+ env: u.env,
153
+ aggregate: u.aggregate
154
+ });
158
155
  }
159
- // ── Stdio upstreams ──────────────────────────────────────────────────────
160
- if (config.stdioUpstreams) {
161
- for (const u of config.stdioUpstreams) {
162
- builder.withStdioUpstream({
163
- name: u.name,
164
- command: u.command,
165
- args: u.args,
166
- env: u.env,
167
- aggregate: u.aggregate,
168
- });
169
- }
156
+ }
157
+ if (config.mcpServers) {
158
+ for (const s of config.mcpServers) {
159
+ builder.withRemoteUpstream({ ...s, aggregate: s.aggregate ?? true });
170
160
  }
171
- // ── Remote MCP server upstreams (reached by URL) ─────────────────────────
172
- // Config-discovered remote servers join the `_all` aggregate by default;
173
- // an explicit `aggregate: false` opts an entry out.
174
- if (config.mcpServers) {
175
- for (const s of config.mcpServers) {
176
- builder.withRemoteUpstream({ ...s, aggregate: s.aggregate ?? true });
177
- }
161
+ }
162
+ if (config.mcpbBundles) {
163
+ for (const b of config.mcpbBundles) {
164
+ const upstream = await loadMcpbBundle(b, baseDir);
165
+ if (upstream) {
166
+ builder.withStdioUpstream(upstream);
167
+ }
178
168
  }
179
- // ── Local `.mcpb` bundles ────────────────────────────────────────────────
180
- // Each bundle is signature-verified and unpacked before it is wired in as a
181
- // stdio upstream. A refused bundle is skipped (loadMcpbBundle logs why); it
182
- // never spawns a process. Bundles join `_all` by default (opt-out per entry).
183
- if (config.mcpbBundles) {
184
- for (const b of config.mcpbBundles) {
185
- const upstream = await loadMcpbBundle(b, baseDir);
186
- if (upstream) {
187
- builder.withStdioUpstream(upstream);
188
- }
189
- }
169
+ }
170
+ if (stdioProvider) {
171
+ builder.withStdioClient(stdioProvider);
172
+ }
173
+ if (hasLocalGrammars) {
174
+ builder.withBrokerLocalGrammarsDir(localGrammarsDir);
175
+ }
176
+ const authEnabledEnv = process.env["MCP_BROKER_AUTH_ENABLED"];
177
+ const authEnabled = authEnabledEnv === "1" || authEnabledEnv === "true";
178
+ if (authEnabled) {
179
+ const publicBaseUrl = process.env["MCP_BROKER_PUBLIC_BASE_URL"];
180
+ const jwks = process.env["MCP_BROKER_JWKS"];
181
+ const issuer = process.env["MCP_BROKER_ISSUER"];
182
+ const authorizationServers = config.auth?.authorizationServers ?? (issuer ? [issuer] : []);
183
+ if (!publicBaseUrl || !jwks || authorizationServers.length === 0) {
184
+ console.error(
185
+ "[mcp-broker] auth.enabled requires publicBaseUrl, jwks, and at least one authorizationServers entry (or issuer). Set them via config.auth or MCP_BROKER_PUBLIC_BASE_URL / MCP_BROKER_JWKS / MCP_BROKER_ISSUER."
186
+ );
187
+ process.exit(1);
190
188
  }
191
- if (stdioProvider) {
192
- builder.withStdioClient(stdioProvider);
193
- }
194
- if (hasLocalGrammars) {
195
- builder.withBrokerLocalGrammarsDir(localGrammarsDir);
196
- }
197
- // ── Authorization (OAuth 2.1 resource server) ────────────────────────────
198
- // Opt-in: only wired when MCP_BROKER_AUTH_ENABLED / config.auth.enabled is on.
199
- // Env scalars win over config; array/object fields are read from config.
200
- const authEnabledEnv = process.env["MCP_BROKER_AUTH_ENABLED"];
201
- const authEnabled = authEnabledEnv === "1" || authEnabledEnv === "true";
202
- if (authEnabled) {
203
- const publicBaseUrl = process.env["MCP_BROKER_PUBLIC_BASE_URL"];
204
- const jwks = process.env["MCP_BROKER_JWKS"];
205
- const issuer = process.env["MCP_BROKER_ISSUER"];
206
- const authorizationServers = config.auth?.authorizationServers ?? (issuer ? [issuer] : []);
207
- if (!publicBaseUrl || !jwks || authorizationServers.length === 0) {
208
- console.error("[mcp-broker] auth.enabled requires publicBaseUrl, jwks, and at least one " +
209
- "authorizationServers entry (or issuer). Set them via config.auth or " +
210
- "MCP_BROKER_PUBLIC_BASE_URL / MCP_BROKER_JWKS / MCP_BROKER_ISSUER.");
211
- process.exit(1);
212
- }
213
- builder.withJwtAuth({
214
- publicBaseUrl,
215
- authorizationServers,
216
- jwksUri: jwks,
217
- issuer,
218
- scopesSupported: config.auth?.scopesSupported,
219
- requiredScopes: config.auth?.requiredScopes,
220
- perSlotScopes: config.auth?.perSlotScopes,
221
- providerScopes: config.auth?.providerScopes,
222
- subjectMapping: config.auth?.subjectMapping,
223
- roles: config.auth?.roles,
224
- assignments: config.auth?.assignments,
225
- denies: config.auth?.denies,
226
- slotResources: config.auth?.slotResources,
227
- toolCapabilities: config.auth?.toolCapabilities,
228
- providerToolCapabilities: config.auth?.providerToolCapabilities,
229
- audit: config.auth?.audit,
230
- });
231
- }
232
- // ── Provider authentication (independent of client OAuth) ────────────────
233
- // Requires every provider connecting to /provider/<slot> or /providers to
234
- // present the shared secret — closes off slot occupation by strangers.
235
- const providerSecret = process.env["MCP_BROKER_PROVIDER_SECRET"];
236
- if (providerSecret) {
237
- builder.withProviderSecret(providerSecret);
238
- }
239
- const tunnel = builder.build();
240
- await tunnel.start();
241
- // ── Startup banner ──────────────────────────────────────────────────────
242
- const httpScheme = useTls ? "https" : "http";
243
- const wsScheme = useTls ? "wss" : "ws";
244
- const hr = "─".repeat(64);
245
- const localhost = `${httpScheme}://localhost:${port}`;
246
- const mcpSuffix = mcpPath.replace(/^\//, "");
247
- const sseSuffix = ssePath.replace(/^\//, "");
248
- const hasWwwRoot = !!(envWwwDir && fs.existsSync(envWwwDir)) || (config.www?.mounts ?? []).some((m) => m.urlPrefix === "/");
189
+ builder.withJwtAuth({
190
+ publicBaseUrl,
191
+ authorizationServers,
192
+ jwksUri: jwks,
193
+ issuer,
194
+ scopesSupported: config.auth?.scopesSupported,
195
+ requiredScopes: config.auth?.requiredScopes,
196
+ perSlotScopes: config.auth?.perSlotScopes,
197
+ providerScopes: config.auth?.providerScopes,
198
+ subjectMapping: config.auth?.subjectMapping,
199
+ roles: config.auth?.roles,
200
+ assignments: config.auth?.assignments,
201
+ denies: config.auth?.denies,
202
+ slotResources: config.auth?.slotResources,
203
+ toolCapabilities: config.auth?.toolCapabilities,
204
+ providerToolCapabilities: config.auth?.providerToolCapabilities,
205
+ audit: config.auth?.audit
206
+ });
207
+ }
208
+ const providerSecret = process.env["MCP_BROKER_PROVIDER_SECRET"];
209
+ if (providerSecret) {
210
+ builder.withProviderSecret(providerSecret);
211
+ }
212
+ const tunnel = builder.build();
213
+ await tunnel.start();
214
+ const httpScheme = useTls ? "https" : "http";
215
+ const wsScheme = useTls ? "wss" : "ws";
216
+ const hr = "\u2500".repeat(64);
217
+ const localhost = `${httpScheme}://localhost:${port}`;
218
+ const mcpSuffix = mcpPath.replace(/^\//, "");
219
+ const sseSuffix = ssePath.replace(/^\//, "");
220
+ const hasWwwRoot = !!(envWwwDir && fs.existsSync(envWwwDir)) || (config.www?.mounts ?? []).some((m) => m.urlPrefix === "/");
221
+ console.log();
222
+ console.log(`\u2699\uFE0F mcp-broker started${useTls ? " (TLS)" : ""}`);
223
+ console.log(hr);
224
+ console.log(`\u{1F4E1} Provider WebSocket ${wsScheme}://localhost:${port}${providerPath}/<name>`);
225
+ console.log(`\u{1F50C} MCP (Streamable HTTP) ${localhost}/<name>/${mcpSuffix}`);
226
+ console.log(`\u{1F4FA} Legacy SSE ${localhost}/<name>/${sseSuffix}`);
227
+ if (hasLocalGrammars) {
228
+ console.log(`\u{1F310} Local grammars ${localGrammarsDir}`);
229
+ }
230
+ console.log(`\u{1F510} Authorization ${authEnabled ? "OAuth 2.1 (Bearer required)" : "disabled (trusted network only)"}`);
231
+ console.log(`\u{1F6E1}\uFE0F Provider auth ${providerSecret ? "shared secret required" : "disabled"}`);
232
+ console.log(hr);
233
+ console.log(` Press Ctrl+C to stop.`);
234
+ console.log();
235
+ if (hasWwwRoot && process.env["MCP_BROKER_OPEN"] === "1") {
236
+ const url = `${localhost}/`;
237
+ console.log(`\u{1F680} Opening browser: ${url}`);
249
238
  console.log();
250
- console.log(`⚙️ mcp-broker started${useTls ? " (TLS)" : ""}`);
251
- console.log(hr);
252
- console.log(`📡 Provider WebSocket ${wsScheme}://localhost:${port}${providerPath}/<name>`);
253
- console.log(`🔌 MCP (Streamable HTTP) ${localhost}/<name>/${mcpSuffix}`);
254
- console.log(`📺 Legacy SSE ${localhost}/<name>/${sseSuffix}`);
255
- if (hasLocalGrammars) {
256
- console.log(`🌐 Local grammars ${localGrammarsDir}`);
257
- }
258
- console.log(`🔐 Authorization ${authEnabled ? "OAuth 2.1 (Bearer required)" : "disabled (trusted network only)"}`);
259
- console.log(`🛡️ Provider auth ${providerSecret ? "shared secret required" : "disabled"}`);
260
- console.log(hr);
261
- console.log(` Press Ctrl+C to stop.`);
262
- console.log();
263
- // Auto-launch browser only if explicitly opted in via MCP_BROKER_OPEN=1
264
- // AND a mount at "/" is configured.
265
- if (hasWwwRoot && process.env["MCP_BROKER_OPEN"] === "1") {
266
- const url = `${localhost}/`;
267
- console.log(`🚀 Opening browser: ${url}`);
268
- console.log();
269
- await open(url);
270
- }
271
- // ── Signal handlers ─────────────────────────────────────────────────────
272
- const shutdown = async (signal) => {
273
- console.log(`\n⛔ ${signal} received — shutting down…`);
274
- await tunnel.stop();
275
- process.exit(0);
276
- };
277
- process.on("SIGINT", () => void shutdown("SIGINT"));
278
- process.on("SIGTERM", () => void shutdown("SIGTERM"));
239
+ await open(url);
240
+ }
241
+ const shutdown = async (signal) => {
242
+ console.log(`
243
+ \u26D4 ${signal} received, shutting down\u2026`);
244
+ await tunnel.stop();
245
+ process.exit(0);
246
+ };
247
+ process.on("SIGINT", () => void shutdown("SIGINT"));
248
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
279
249
  }
280
250
  main().catch((err) => {
281
- console.error("[mcp-broker] Fatal error:", err);
282
- process.exit(1);
251
+ console.error("[mcp-broker] Fatal error:", err);
252
+ process.exit(1);
283
253
  });
254
+ //# sourceMappingURL=bin.js.map
284
255
  //# sourceMappingURL=bin.js.map