@cyanmycelium/mcp-broker 0.4.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.mcp-broker.example/CONFIGURATION-EN.md +861 -0
  2. package/.mcp-broker.example/CONFIGURATION-FR.md +871 -0
  3. package/.mcp-broker.example/README.md +8 -3
  4. package/.mcp-broker.example/config.json +86 -0
  5. package/README.md +122 -3
  6. package/dist/bin.d.ts +0 -1
  7. package/dist/bin.js +180 -208
  8. package/dist/bin.js.map +1 -1
  9. package/dist/chunk-FTDKH2C4.js +3670 -0
  10. package/dist/chunk-FTDKH2C4.js.map +1 -0
  11. package/dist/{broker/grammars → grammars}/claude/en.json +1 -1
  12. package/dist/{broker/grammars → grammars}/claude/fr.json +1 -1
  13. package/dist/index.d.ts +1790 -18
  14. package/dist/index.js +2 -14
  15. package/dist/index.js.map +1 -1
  16. package/package.json +14 -8
  17. package/scripts/copy-assets.mjs +16 -10
  18. package/scripts/gen-cert.mjs +5 -5
  19. package/scripts/pack-mcpb.mjs +5 -5
  20. package/scripts/sign-bundle.mjs +6 -6
  21. package/src/auth/auth.config.ts +98 -0
  22. package/src/auth/auth.types.ts +120 -0
  23. package/src/auth/http.auth.ts +128 -0
  24. package/src/auth/index.ts +34 -0
  25. package/src/auth/jwt.validator.ts +63 -0
  26. package/src/auth/provider.auth.ts +114 -0
  27. package/src/auth/resource.metadata.ts +34 -0
  28. package/src/authorization/audit.ts +35 -0
  29. package/src/authorization/capability.classifier.ts +114 -0
  30. package/src/authorization/index.ts +37 -0
  31. package/src/authorization/policy.engine.ts +212 -0
  32. package/src/authorization/policy.types.ts +105 -0
  33. package/src/authorization/resource.path.ts +126 -0
  34. package/src/authorization/runtime.ts +56 -0
  35. package/src/authorization/slot.resource.ts +50 -0
  36. package/src/authorization/subject.mapper.ts +81 -0
  37. package/src/bin.ts +90 -7
  38. package/src/broker/adapters/broker.adapter.info.ts +3 -3
  39. package/src/broker/adapters/broker.adapter.providers.ts +3 -3
  40. package/src/broker/aggregate/aggregate.catalog.ts +49 -21
  41. package/src/broker/aggregate/aggregate.server.ts +149 -19
  42. package/src/broker/aggregate/provider.client.session.ts +22 -22
  43. package/src/broker/behaviors/broker.behavior.info.ts +4 -4
  44. package/src/broker/behaviors/broker.behavior.providers.ts +6 -6
  45. package/src/broker/broker.context.ts +10 -4
  46. package/src/broker/broker.grammars.ts +16 -13
  47. package/src/broker/broker.server.ts +12 -9
  48. package/src/broker/grammars/claude/en.json +1 -1
  49. package/src/broker/grammars/claude/fr.json +1 -1
  50. package/src/broker/index.ts +9 -9
  51. package/src/config.ts +81 -8
  52. package/src/index.ts +121 -20
  53. package/src/{mcpb.loader.ts → mcpb/mcpb.loader.ts} +24 -21
  54. package/src/{mcpb.unzip.ts → mcpb/mcpb.unzip.ts} +2 -2
  55. package/src/remote.transports.ts +11 -8
  56. package/src/remote.upstream.ts +11 -8
  57. package/src/stdio.upstream.ts +9 -6
  58. package/src/upstream.ts +6 -3
  59. package/src/ws/ws.interfaces.ts +357 -0
  60. package/src/{ws.tunnel.builder.ts → ws/ws.tunnel.builder.ts} +112 -9
  61. package/src/{ws.tunnel.ts → ws/ws.tunnel.ts} +646 -457
  62. package/web/README.md +94 -0
  63. package/web/assets/logo.png +0 -0
  64. package/web/broker-self-mcp.html +338 -0
  65. package/web/css/styles.css +580 -0
  66. package/web/demos/DemoPlaceholder.html +258 -0
  67. package/web/demos/broker-explorer/css/app.css +393 -0
  68. package/web/demos/broker-explorer/index.html +94 -0
  69. package/web/demos/broker-explorer/js/app.js +271 -0
  70. package/web/demos/broker-explorer/js/mcp-ws-client.js +132 -0
  71. package/web/demos/oauth-lab/README.md +94 -0
  72. package/web/demos/oauth-lab/config.json +117 -0
  73. package/web/demos/oauth-lab/css/app.css +1097 -0
  74. package/web/demos/oauth-lab/index.html +323 -0
  75. package/web/demos/oauth-lab/js/app.js +654 -0
  76. package/web/demos/oauth-lab/server/auth-server.mjs +426 -0
  77. package/web/demos/oauth-lab/server/factory-provider.mjs +269 -0
  78. package/web/demos/oauth-lab/server/smoke-test.mjs +308 -0
  79. package/web/demos/oauth-lab/server/start.mjs +106 -0
  80. package/web/demos/provider-tunnel/css/app.css +384 -0
  81. package/web/demos/provider-tunnel/index.html +99 -0
  82. package/web/demos/provider-tunnel/js/app.js +226 -0
  83. package/web/demos/provider-tunnel/js/toolbox-server.js +186 -0
  84. package/web/index.html +558 -0
  85. package/web/js/lib/broker-tunnel.js +173 -0
  86. package/dist/broker/adapters/broker.adapter.info.d.ts +0 -16
  87. package/dist/broker/adapters/broker.adapter.info.js +0 -43
  88. package/dist/broker/adapters/broker.adapter.info.js.map +0 -1
  89. package/dist/broker/adapters/broker.adapter.providers.d.ts +0 -18
  90. package/dist/broker/adapters/broker.adapter.providers.js +0 -61
  91. package/dist/broker/adapters/broker.adapter.providers.js.map +0 -1
  92. package/dist/broker/aggregate/aggregate.catalog.d.ts +0 -54
  93. package/dist/broker/aggregate/aggregate.catalog.js +0 -105
  94. package/dist/broker/aggregate/aggregate.catalog.js.map +0 -1
  95. package/dist/broker/aggregate/aggregate.server.d.ts +0 -47
  96. package/dist/broker/aggregate/aggregate.server.js +0 -151
  97. package/dist/broker/aggregate/aggregate.server.js.map +0 -1
  98. package/dist/broker/aggregate/provider.client.session.d.ts +0 -52
  99. package/dist/broker/aggregate/provider.client.session.js +0 -140
  100. package/dist/broker/aggregate/provider.client.session.js.map +0 -1
  101. package/dist/broker/behaviors/broker.behavior.info.d.ts +0 -15
  102. package/dist/broker/behaviors/broker.behavior.info.js +0 -41
  103. package/dist/broker/behaviors/broker.behavior.info.js.map +0 -1
  104. package/dist/broker/behaviors/broker.behavior.providers.d.ts +0 -19
  105. package/dist/broker/behaviors/broker.behavior.providers.js +0 -69
  106. package/dist/broker/behaviors/broker.behavior.providers.js.map +0 -1
  107. package/dist/broker/broker.context.d.ts +0 -59
  108. package/dist/broker/broker.context.js +0 -2
  109. package/dist/broker/broker.context.js.map +0 -1
  110. package/dist/broker/broker.grammars.d.ts +0 -130
  111. package/dist/broker/broker.grammars.js +0 -229
  112. package/dist/broker/broker.grammars.js.map +0 -1
  113. package/dist/broker/broker.server.d.ts +0 -66
  114. package/dist/broker/broker.server.js +0 -73
  115. package/dist/broker/broker.server.js.map +0 -1
  116. package/dist/broker/index.d.ts +0 -9
  117. package/dist/broker/index.js +0 -7
  118. package/dist/broker/index.js.map +0 -1
  119. package/dist/config.d.ts +0 -136
  120. package/dist/config.js +0 -61
  121. package/dist/config.js.map +0 -1
  122. package/dist/mcpb.loader.d.ts +0 -24
  123. package/dist/mcpb.loader.js +0 -161
  124. package/dist/mcpb.loader.js.map +0 -1
  125. package/dist/mcpb.unzip.d.ts +0 -6
  126. package/dist/mcpb.unzip.js +0 -95
  127. package/dist/mcpb.unzip.js.map +0 -1
  128. package/dist/remote.transports.d.ts +0 -16
  129. package/dist/remote.transports.js +0 -297
  130. package/dist/remote.transports.js.map +0 -1
  131. package/dist/remote.upstream.d.ts +0 -36
  132. package/dist/remote.upstream.js +0 -52
  133. package/dist/remote.upstream.js.map +0 -1
  134. package/dist/stdio.upstream.d.ts +0 -45
  135. package/dist/stdio.upstream.js +0 -85
  136. package/dist/stdio.upstream.js.map +0 -1
  137. package/dist/upstream.d.ts +0 -33
  138. package/dist/upstream.js +0 -2
  139. package/dist/upstream.js.map +0 -1
  140. package/dist/version.d.ts +0 -2
  141. package/dist/version.js +0 -9
  142. package/dist/version.js.map +0 -1
  143. package/dist/ws.tunnel.builder.d.ts +0 -139
  144. package/dist/ws.tunnel.builder.js +0 -205
  145. package/dist/ws.tunnel.builder.js.map +0 -1
  146. package/dist/ws.tunnel.d.ts +0 -373
  147. package/dist/ws.tunnel.js +0 -1090
  148. package/dist/ws.tunnel.js.map +0 -1
  149. /package/dist/{broker/grammars → grammars}/default/en.json +0 -0
  150. /package/dist/{broker/grammars → grammars}/default/fr.json +0 -0
  151. /package/dist/{broker/grammars → grammars}/default/zh.json +0 -0
@@ -7,6 +7,11 @@ broker, then adapt to your needs:
7
7
  cp -r .mcp-broker.example .mcp-broker
8
8
  ```
9
9
 
10
+ Property-by-property educational guides:
11
+
12
+ - [English](CONFIGURATION-EN.md)
13
+ - [Français](CONFIGURATION-FR.md)
14
+
10
15
  ## Layout
11
16
 
12
17
  ```
@@ -26,7 +31,7 @@ cp -r .mcp-broker.example .mcp-broker
26
31
  └── index.html
27
32
  ```
28
33
 
29
- 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`
30
35
  mount at it (`"dir": "../web"`) to serve it. See [`node/web/README.md`](../web/README.md).
31
36
 
32
37
  ## Path resolution
@@ -36,14 +41,14 @@ config file** (i.e. `.mcp-broker/`). So `"certs/cert.pem"` in the config
36
41
  points at `.mcp-broker/certs/cert.pem`. The folder is self-contained.
37
42
 
38
43
  Env vars (`MCP_BROKER_TLS_CERT`, `MCP_BROKER_WWW_DIR`, ...) are still
39
- resolved against `process.cwd()` — they are the deploy-time override
44
+ resolved against `process.cwd()`: they are the deploy-time override
40
45
  mechanism and not tied to the config file's location.
41
46
 
42
47
  ## `.mcpb` bundles
43
48
 
44
49
  `mcpbBundles` entries load local `.mcpb` bundles as stdio provider slots.
45
50
  Each bundle is verified against a **detached signature** before it is
46
- unpacked and run — the broker never spawns an unverified bundle.
51
+ unpacked and run: the broker never spawns an unverified bundle.
47
52
 
48
53
  1. Generate a signing key pair (once):
49
54
  `node ../scripts/sign-bundle.mjs keygen bundles`
@@ -22,6 +22,92 @@
22
22
  ]
23
23
  },
24
24
 
25
+ "auth": {
26
+ "enabled": true,
27
+ "publicBaseUrl": "https://mcp.factory.local",
28
+ "authorizationServers": [
29
+ "https://identity.factory.local"
30
+ ],
31
+ "jwks": "https://identity.factory.local/.well-known/jwks.json",
32
+ "requiredScopes": ["mcp:call"],
33
+ "perSlotScopes": {
34
+ "_broker": ["broker:admin"]
35
+ },
36
+ "subjectMapping": {
37
+ "userClaim": "sub",
38
+ "groupClaims": ["groups"],
39
+ "clientClaim": "client_id"
40
+ },
41
+ "roles": {
42
+ "viewer": {
43
+ "capabilities": [
44
+ "mcp.resources.read",
45
+ "mcp.tools.list",
46
+ "mcp.prompts.read"
47
+ ]
48
+ },
49
+ "maintenance": {
50
+ "inherits": ["viewer"],
51
+ "capabilities": [
52
+ "mcp.tools.call",
53
+ "mcp.tools.diagnose",
54
+ "mcp.tools.configure-analysis"
55
+ ]
56
+ },
57
+ "operator": {
58
+ "inherits": ["viewer"],
59
+ "capabilities": ["mcp.tools.operate"]
60
+ },
61
+ "administrator": {
62
+ "capabilities": ["*"]
63
+ }
64
+ },
65
+ "assignments": [
66
+ {
67
+ "id": "maintenance-area-a",
68
+ "subject": "group:maintenance-area-a",
69
+ "role": "maintenance",
70
+ "resource": "/enterprise-a/site-paris/area-a/**"
71
+ },
72
+ {
73
+ "id": "energy-team",
74
+ "subject": "group:energy-team",
75
+ "role": "viewer",
76
+ "resource": "/enterprise-a/site-paris/**"
77
+ }
78
+ ],
79
+ "denies": [
80
+ {
81
+ "id": "protect-critical-furnace",
82
+ "subject": "group:maintenance-area-a",
83
+ "capabilities": [
84
+ "mcp.tools.configure-analysis",
85
+ "mcp.tools.operate"
86
+ ],
87
+ "resource": "/enterprise-a/site-paris/area-a/line-2/cell-4/critical-furnace"
88
+ }
89
+ ],
90
+ "slotResources": {
91
+ "spoony-00452": "/enterprise-a/site-paris/area-a/line-3/cell-2/motor-7",
92
+ "site-energy": "/enterprise-a/site-paris"
93
+ },
94
+ "toolCapabilities": {
95
+ "get_electrical_state": "mcp.resources.read",
96
+ "diagnose_motor": "mcp.tools.diagnose",
97
+ "reset_baseline": "mcp.tools.configure-analysis",
98
+ "start_motor": "mcp.tools.operate"
99
+ },
100
+ "providerToolCapabilities": {
101
+ "/enterprise-a/site-paris/area-a/**": {
102
+ "start_motor": "mcp.tools.operate"
103
+ }
104
+ },
105
+ "audit": {
106
+ "logAllowed": false
107
+ },
108
+ "providerSecret": "change-me"
109
+ },
110
+
25
111
  "stdioUpstreams": [
26
112
  {
27
113
  "name": "fs",
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
  |---|---|---|
@@ -96,6 +96,85 @@ Full reference (every field, defaults, recipes, grammar overrides): **[docs/conf
96
96
  | `MCP_BROKER_TLS_KEY` | (unset) | Path to a PEM private key. Enables HTTPS/WSS |
97
97
  | `MCP_BROKER_PROTOCOL` | auto | `http` forces plain, `https` forces TLS, unset auto-detects from cert+key |
98
98
  | `MCP_BROKER_STDIO_PROVIDER` | (unset) | When set, bridge stdin/stdout JSON-RPC to this provider (Claude Desktop integration) |
99
+ | `MCP_BROKER_AUTH_ENABLED` | (unset) | `1` to turn on the OAuth 2.1 resource server (requires the three below) |
100
+ | `MCP_BROKER_PUBLIC_BASE_URL` | (unset) | Public origin used to build canonical resource URIs, e.g. `https://mcp.example.com` |
101
+ | `MCP_BROKER_JWKS` | (unset) | Authorization server's JWKS URL, used to verify token signatures |
102
+ | `MCP_BROKER_ISSUER` | (unset) | Expected token issuer (defaults to the sole authorization server) |
103
+ | `MCP_BROKER_PROVIDER_SECRET` | (unset) | Shared secret every provider must present to occupy a slot |
104
+
105
+ ## Authorization (OAuth 2.1)
106
+
107
+ By default the broker performs **no** authentication. That is fine behind a
108
+ trusted network boundary, but do not expose it publicly as-is. Turn on the OAuth 2.1
109
+ resource server to require a bearer token on every client request, authenticate
110
+ providers, and filter the `_all` aggregate per caller.
111
+
112
+ Enabled via the `auth` config block (or env vars). Minimal `.mcp-broker/config.json`:
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
+
118
+ ```json
119
+ {
120
+ "auth": {
121
+ "enabled": true,
122
+ "publicBaseUrl": "https://mcp.example.com",
123
+ "authorizationServers": ["https://auth.example.com"],
124
+ "jwks": "https://auth.example.com/.well-known/jwks.json",
125
+ "requiredScopes": ["mcp:call"],
126
+ "perSlotScopes": { "_broker": ["broker:admin"] },
127
+ "subjectMapping": {
128
+ "userClaim": "sub",
129
+ "groupClaims": ["groups"],
130
+ "clientClaim": "client_id"
131
+ },
132
+ "roles": {
133
+ "viewer": {
134
+ "capabilities": ["mcp.tools.list", "mcp.resources.read"]
135
+ },
136
+ "maintenance": {
137
+ "inherits": ["viewer"],
138
+ "capabilities": ["mcp.tools.diagnose"]
139
+ }
140
+ },
141
+ "assignments": [
142
+ {
143
+ "subject": "group:maintenance-site-a",
144
+ "role": "maintenance",
145
+ "resource": "/enterprise/site-a/**"
146
+ }
147
+ ],
148
+ "slotResources": {
149
+ "motor-7": "/enterprise/site-a/area-a/line-3/cell-2/motor-7"
150
+ },
151
+ "toolCapabilities": {
152
+ "diagnose_motor": "mcp.tools.diagnose"
153
+ },
154
+ "providerSecret": "change-me"
155
+ }
156
+ }
157
+ ```
158
+
159
+ With this on:
160
+
161
+ - `POST /<slot>/mcp` (and `/sse`, `/messages`, `ws://…/<slot>`) require
162
+ `Authorization: Bearer <token>`; the token's audience must be
163
+ `https://mcp.example.com/<slot>/mcp`.
164
+ - Clients discover the authorization server via
165
+ `GET /.well-known/oauth-protected-resource/<slot>/mcp` (RFC 9728), advertised
166
+ in the `401` challenge.
167
+ - Providers must present `providerSecret` (via `X-Provider-Token` or
168
+ `Authorization: Bearer`) to connect to `/provider/<slot>` or `/providers`.
169
+ - `_all` only shows providers allowed by the caller's hierarchical policy.
170
+ - Explicit denies override inherited role grants.
171
+ - Structured provider principals can publish only inside their allowed resource
172
+ namespace.
173
+
174
+ Full model, flows, scopes, and endpoints: **[../docs/authorization.md](../docs/authorization.md)**.
175
+ Roles, resource paths, denies, and migration:
176
+ **[../docs/hierarchical-authorization.md](../docs/hierarchical-authorization.md)**.
177
+ Config field reference: **[docs/config.md](docs/config.md#auth-oauth-21-authorization)**.
99
178
 
100
179
  ## Programmatic API
101
180
 
@@ -111,12 +190,52 @@ const broker = new WsTunnelBuilder()
111
190
  .withStdioUpstream("my-server", "node", ["./my-server.js"])
112
191
  // Optional: serve a dev harness at /
113
192
  .withStaticMount("/", "/abs/path/to/www")
193
+ // Optional: enable the OAuth 2.1 resource server + provider auth
194
+ .withJwtAuth({
195
+ publicBaseUrl: "https://mcp.example.com",
196
+ authorizationServers: ["https://auth.example.com"],
197
+ jwksUri: "https://auth.example.com/.well-known/jwks.json",
198
+ requiredScopes: ["mcp:call"],
199
+ roles: {
200
+ viewer: { capabilities: ["mcp.tools.list"] },
201
+ },
202
+ assignments: [
203
+ {
204
+ subject: "group:operators",
205
+ role: "viewer",
206
+ resource: "/enterprise/site-a/**",
207
+ },
208
+ ],
209
+ subjectMapping: { groupClaims: ["groups"] },
210
+ })
211
+ .withProviderSecret(process.env.PROVIDER_SECRET!)
114
212
  .build();
115
213
 
116
214
  await broker.start();
117
215
  ```
118
216
 
119
- All builder methods are documented inline. The full options interface is `WsTunnelOptions`, also exported.
217
+ All builder methods are documented inline. The full options interface is
218
+ `IWsTunnelOptions`, also exported. Use `withAuthorizationPolicy` for a
219
+ standalone policy configuration, `withPolicyEngine` for a custom engine, and
220
+ `withSlotResourceResolver` for a custom namespace mapping. For a custom token
221
+ validator (for example RFC 7662 introspection) or provider authenticator, use
222
+ `withAuth(resolvedAuth)` or `withProviderAuth(authenticator)` with your own
223
+ `ITokenValidator` or `IProviderAuthenticator`.
224
+
225
+ ## Interactive OAuth demo
226
+
227
+ The bundled [OAuth Policy Lab](web/demos/oauth-lab/) runs a complete local
228
+ Authorization Code and PKCE flow with signed JWTs, JWKS validation,
229
+ audience-bound tokens, hierarchical policies, explicit denies, and a live
230
+ audit:
231
+
232
+ ```sh
233
+ npm run demo:oauth
234
+ ```
235
+
236
+ The page opens at `http://127.0.0.1:3001/demos/oauth-lab/`. See the
237
+ [demo README](web/demos/oauth-lab/README.md) for its identities, policy matrix,
238
+ and automated smoke test.
120
239
 
121
240
  ## TLS for local development
122
241
 
package/dist/bin.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  #!/usr/bin/env node
2
- export {};