@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
package/web/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # `node/web/`: broker instance UI
2
+
3
+ Static web UI served by the broker. No build step: plain ES modules and CSS,
4
+ served as-is over the broker's static mount.
5
+
6
+ ## Layout
7
+
8
+ ```
9
+ web/
10
+ ├── index.html ← launcher: shown when the broker starts
11
+ ├── broker-self-mcp.html ← explainer: the _broker slot, _all aggregate, .mcpb bundles
12
+ ├── css/
13
+ │ └── styles.css ← common stylesheet (shared by all pages)
14
+ ├── js/
15
+ │ └── lib/
16
+ │ └── broker-tunnel.js ← common reusable lib (shared by demos)
17
+ ├── assets/
18
+ │ └── logo.png
19
+ └── demos/
20
+ ├── DemoPlaceholder.html ← stand-in for demos not bundled yet
21
+ ├── provider-tunnel/ ← one self-contained folder per demo
22
+ │ ├── index.html
23
+ │ ├── css/
24
+ │ │ └── app.css
25
+ │ └── js/
26
+ │ ├── app.js
27
+ │ └── toolbox-server.js
28
+ ├── broker-explorer/ ← MCP client connecting to a broker slot
29
+ │ ├── index.html
30
+ │ ├── css/
31
+ │ │ └── app.css
32
+ │ └── js/
33
+ │ ├── app.js
34
+ │ └── mcp-ws-client.js
35
+ └── oauth-lab/ <- OAuth 2.1 and policy demonstration
36
+ ├── index.html
37
+ ├── config.json
38
+ ├── css/app.css
39
+ ├── js/app.js
40
+ └── server/
41
+ ```
42
+
43
+ ## Conventions
44
+
45
+ - **`index.html`** is the launcher. The broker opens it on start (`www.open`).
46
+ It links the bundled demos and the broker's own endpoints.
47
+ - **`broker-self-mcp.html`** is a root-level explainer page (not a demo): the
48
+ `_broker` introspection slot, the `_all` aggregate, and loading signed
49
+ `.mcpb` bundles. Linked from the launcher's "Broker self-MCP" card.
50
+ - **`css/` and `js/` at the root hold only common, shared assets.**
51
+ `css/styles.css` is the sitewide stylesheet; `js/lib/broker-tunnel.js` is
52
+ the reusable, zero-dependency broker connection module.
53
+ - **Each demo lives in its own folder under `demos/`** with its own
54
+ `index.html`, `css/`, and `js/`. A demo may import shared code from the root
55
+ `js/` (e.g. `../../../js/lib/broker-tunnel.js`).
56
+ - **`demos/DemoPlaceholder.html`** is the stand-in linked from the launcher
57
+ for demos that are not packaged yet. It reads `?slot=<name>` to label itself.
58
+
59
+ ## Bundled demos
60
+
61
+ - **`demos/provider-tunnel/`**, hosts an MCP server (official
62
+ `@modelcontextprotocol/sdk`) and tunnels it to the broker over a WebSocket.
63
+ Proves the broker is implementation-agnostic: a server built with the
64
+ reference SDK tunnels through unchanged. The only broker-specific code is
65
+ `js/lib/broker-tunnel.js`.
66
+ - **`demos/broker-explorer/`**: the client side: connects a browser MCP
67
+ client to a broker slot (`_broker`, `_all`, or any provider), lists its
68
+ tools and calls them live. The MCP-over-WebSocket client is isolated in
69
+ `js/mcp-ws-client.js`. Paired with the `broker-self-mcp.html` explainer.
70
+ - **`demos/oauth-lab/`** is a complete local OAuth 2.1 environment with
71
+ Authorization Code and PKCE, JWT and JWKS validation, audience-bound access
72
+ tokens, hierarchical roles, explicit denies, protected MCP calls, and a live
73
+ authorization audit. Run it with `npm run demo:oauth` from `node/`.
74
+
75
+ ## Serving it
76
+
77
+ Point a `www` mount at this folder from a broker config:
78
+
79
+ ```json
80
+ {
81
+ "www": {
82
+ "open": true,
83
+ "mounts": [{ "urlPrefix": "/", "dir": "../web" }]
84
+ }
85
+ }
86
+ ```
87
+
88
+ (`../web` is resolved against the config file's directory, e.g. `.mcp-broker/`.)
89
+
90
+ Or with an environment variable, from the `node/` directory:
91
+
92
+ ```sh
93
+ MCP_BROKER_WWW_DIR=web npm start
94
+ ```
Binary file
@@ -0,0 +1,338 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Broker self-MCP · @cyanmycelium/mcp-broker</title>
7
+ <meta name="description" content="The broker is itself an MCP server: the _broker introspection slot, the _all aggregate, and loading signed .mcpb bundles." />
8
+ <link rel="icon" type="image/png" href="assets/logo.png" />
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap">
12
+ <link rel="stylesheet" href="css/styles.css" />
13
+ <style>
14
+ /* The hero here is an explainer header, a touch shorter than the docs hero. */
15
+ .hero { padding: 72px 0 56px; }
16
+ .hero-mark { margin-bottom: 20px; }
17
+ .hero-mark img { width: 72px; height: 72px; }
18
+
19
+ /* Endpoints, quick reference grid, shared look with the launcher page. */
20
+ .endpoints {
21
+ display: grid;
22
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
23
+ gap: 12px;
24
+ margin-top: 20px;
25
+ }
26
+ .endpoint {
27
+ display: flex;
28
+ flex-direction: column;
29
+ gap: 6px;
30
+ padding: 14px 16px;
31
+ background: var(--bg-elev-1);
32
+ border: 1px solid var(--rule);
33
+ border-radius: 6px;
34
+ }
35
+ .endpoint .label {
36
+ font-family: var(--font-mono);
37
+ font-size: 10px;
38
+ letter-spacing: 0.16em;
39
+ color: var(--blue);
40
+ text-transform: uppercase;
41
+ }
42
+ .endpoint .url {
43
+ font-family: var(--font-mono);
44
+ font-size: 13px;
45
+ color: var(--fg);
46
+ word-break: break-all;
47
+ }
48
+
49
+ /* Tool list: the broker's three built-in introspection tools. */
50
+ .tools {
51
+ display: grid;
52
+ gap: 10px;
53
+ margin: 24px 0 8px;
54
+ }
55
+ .tool {
56
+ display: grid;
57
+ grid-template-columns: minmax(0, 220px) 1fr;
58
+ gap: 16px;
59
+ padding: 14px 16px;
60
+ background: var(--bg-elev-1);
61
+ border: 1px solid var(--rule);
62
+ border-radius: 6px;
63
+ align-items: baseline;
64
+ }
65
+ .tool .name {
66
+ font-family: var(--font-mono);
67
+ font-size: 13px;
68
+ color: var(--blue);
69
+ word-break: break-all;
70
+ }
71
+ .tool .desc { color: var(--fg-muted); font-size: 14px; line-height: 1.5; }
72
+ @media (max-width: 640px) {
73
+ .tool { grid-template-columns: 1fr; gap: 4px; }
74
+ }
75
+ </style>
76
+ </head>
77
+ <body>
78
+
79
+ <!-- ============================== TOPBAR ============================== -->
80
+ <header class="topbar">
81
+ <div class="shell topbar-inner">
82
+ <a class="brand" href="/">
83
+ <img class="brand-mark" src="assets/logo.png" alt="" aria-hidden="true" />
84
+ <span>@cyanmycelium/mcp-broker</span>
85
+ </a>
86
+ <nav class="topbar-links">
87
+ <a href="/">← Back to launcher</a>
88
+ <a href="#all">The _all slot</a>
89
+ <a href="#bundles">.mcpb bundles</a>
90
+ <a href="demos/broker-explorer/">Explorer demo</a>
91
+ <a href="https://github.com/pandaGaume/mcp-broker" target="_blank" rel="noopener">GitHub →</a>
92
+ </nav>
93
+ </div>
94
+ </header>
95
+
96
+ <main>
97
+
98
+ <!-- ================================ HERO =============================== -->
99
+ <section class="hero">
100
+ <div class="shell">
101
+ <div class="hero-mark"><img src="assets/logo.png" alt="" /></div>
102
+
103
+ <div class="eyebrow"><span class="dot"></span>broker · self-mcp</div>
104
+
105
+ <h1 class="hero-title">The broker is its own MCP server.</h1>
106
+
107
+ <p class="hero-sub">
108
+ Beyond tunnelling other servers, the broker exposes a reserved slot,
109
+ <code class="inline">_broker</code>, that answers MCP itself: and an
110
+ aggregate slot, <code class="inline">_all</code>, that unions every
111
+ connected provider into one server.
112
+ </p>
113
+
114
+ <div class="hero-cta">
115
+ <a class="btn btn-primary" href="demos/broker-explorer/">Open the explorer demo →</a>
116
+ <a class="btn btn-secondary" href="https://github.com/pandaGaume/mcp-broker/tree/main/docs" target="_blank" rel="noopener">Docs</a>
117
+ </div>
118
+ </div>
119
+ </section>
120
+
121
+ <!-- ============================ _broker SLOT =========================== -->
122
+ <section id="broker">
123
+ <div class="shell">
124
+ <div class="section-num">01 - The _broker slot</div>
125
+ <h2>Introspect the broker over MCP</h2>
126
+
127
+ <p class="intro">
128
+ The reserved <code class="inline">_broker</code> slot is an in-process MCP
129
+ server. Point any MCP client at it (MCP Inspector, Claude, your own) to
130
+ inspect the broker's identity and the providers currently tunnelled in.
131
+ </p>
132
+
133
+ <div class="tools">
134
+ <div class="tool">
135
+ <span class="name">broker_info</span>
136
+ <span class="desc">Reports the broker's identity, version and configuration.</span>
137
+ </div>
138
+ <div class="tool">
139
+ <span class="name">providers_list</span>
140
+ <span class="desc">Lists the provider slots currently registered with the broker.</span>
141
+ </div>
142
+ <div class="tool">
143
+ <span class="name">provider_status</span>
144
+ <span class="desc">Reports the connection status of a given provider slot.</span>
145
+ </div>
146
+ </div>
147
+
148
+ <p class="caption">Reachable on every client transport, see the endpoints below.</p>
149
+ </div>
150
+ </section>
151
+
152
+ <!-- ============================== _all SLOT ============================ -->
153
+ <section id="all">
154
+ <div class="shell">
155
+ <div class="section-num">02 - The _all aggregate</div>
156
+ <h2>One slot, every provider</h2>
157
+
158
+ <p class="intro">
159
+ The reserved <code class="inline">_all</code> slot presents the union of
160
+ every opted-in provider's tools and prompts as a single MCP server.
161
+ Aggregated names are provider-prefixed with a <code class="inline">-</code>
162
+ separator, so a <code class="inline">forecast</code> tool from a
163
+ <code class="inline">weather</code> provider appears as
164
+ <code class="inline">weather-forecast</code>.
165
+ </p>
166
+
167
+ <p>
168
+ The broker's own introspection tools are aggregated in too: under
169
+ <code class="inline">_all</code> they appear as
170
+ <code class="inline">_broker-broker_info</code>,
171
+ <code class="inline">_broker-providers_list</code> and
172
+ <code class="inline">_broker-provider_status</code>. A client pinned to
173
+ <code class="inline">_all</code> therefore sees the whole broker, its
174
+ meta tools plus every tunnelled provider, through one connection.
175
+ </p>
176
+
177
+ <div class="conclusion">
178
+ Config-discovered upstreams (<code class="inline">mcpServers</code> and
179
+ <code class="inline">mcpbBundles</code>) join <code class="inline">_all</code>
180
+ automatically. Set <code class="inline">"aggregate": false</code> on an
181
+ entry to keep it on its own slot only.
182
+ </div>
183
+ </div>
184
+ </section>
185
+
186
+ <!-- ============================ .mcpb BUNDLES =========================== -->
187
+ <section id="bundles">
188
+ <div class="shell">
189
+ <div class="section-num">03 - Loading .mcpb bundles</div>
190
+ <h2>Run a packaged server as a provider</h2>
191
+
192
+ <p class="intro">
193
+ The broker loads local <code class="inline">.mcpb</code> bundles listed in
194
+ its config and runs them as stdio provider slots. Before a bundle is
195
+ unpacked or spawned, the broker verifies a <strong>detached signature</strong>
196
+ against a trusted public key: an unverified bundle is refused, never run.
197
+ </p>
198
+
199
+ <div class="code-wrap">
200
+ <div class="code-head">
201
+ <span class="lang">JSON · .mcp-broker/config.json</span>
202
+ <button class="copy-btn" data-copy-block>Copy</button>
203
+ </div>
204
+ <pre><code><span class="tk-pu">{</span>
205
+ <span class="tk-p">"mcpbBundles"</span><span class="tk-pu">: [{</span>
206
+ <span class="tk-p">"name"</span><span class="tk-pu">:</span> <span class="tk-s">"weather"</span><span class="tk-pu">,</span>
207
+ <span class="tk-p">"path"</span><span class="tk-pu">:</span> <span class="tk-s">"bundles/weather.mcpb"</span><span class="tk-pu">,</span>
208
+ <span class="tk-p">"publicKey"</span><span class="tk-pu">:</span> <span class="tk-s">"bundles/mcpb-signing.pub.pem"</span><span class="tk-pu">,</span>
209
+ <span class="tk-p">"signature"</span><span class="tk-pu">:</span> <span class="tk-s">"bundles/weather.mcpb.sig"</span><span class="tk-pu">,</span>
210
+ <span class="tk-p">"aggregate"</span><span class="tk-pu">:</span> <span class="tk-k">true</span> <span class="tk-c">// default; false keeps it off _all</span>
211
+ <span class="tk-pu">}]</span>
212
+ <span class="tk-pu">}</span>
213
+ </code></pre>
214
+ </div>
215
+
216
+ <p>
217
+ Sign bundles with the bundled helper, Node built-ins only, no OpenSSL
218
+ and no dependency on Anthropic's <code class="inline">mcpb</code> CLI:
219
+ </p>
220
+
221
+ <div class="code-wrap">
222
+ <div class="code-head">
223
+ <span class="lang">Bash · from node/</span>
224
+ <button class="copy-btn" data-copy-block>Copy</button>
225
+ </div>
226
+ <pre><code><span class="tk-c"># generate an Ed25519 signing key pair</span>
227
+ node scripts/sign-bundle.mjs keygen bundles
228
+
229
+ <span class="tk-c"># produce the detached signature next to the bundle</span>
230
+ node scripts/sign-bundle.mjs sign bundles/weather.mcpb bundles/mcpb-signing.key.pem
231
+ </code></pre>
232
+ </div>
233
+
234
+ <p class="caption">
235
+ Keep the private key out of version control. The broker only ever needs
236
+ the public key referenced by <code class="inline">publicKey</code>.
237
+ </p>
238
+ </div>
239
+ </section>
240
+
241
+ <!-- ============================ ENDPOINTS ============================== -->
242
+ <section id="endpoints">
243
+ <div class="shell">
244
+ <div class="section-num">04 - Endpoints</div>
245
+ <h2>Where to connect</h2>
246
+
247
+ <p class="intro">
248
+ Both reserved slots are reachable on every client transport. Swap
249
+ <code class="inline">_broker</code> for <code class="inline">_all</code>
250
+ (or any provider slot name) in the paths below.
251
+ </p>
252
+
253
+ <div class="endpoints">
254
+ <div class="endpoint">
255
+ <span class="label">Broker · Streamable HTTP</span>
256
+ <span class="url">http://localhost:3000/_broker/mcp</span>
257
+ </div>
258
+ <div class="endpoint">
259
+ <span class="label">Aggregate · Streamable HTTP</span>
260
+ <span class="url">http://localhost:3000/_all/mcp</span>
261
+ </div>
262
+ <div class="endpoint">
263
+ <span class="label">Broker · Raw WebSocket</span>
264
+ <span class="url">ws://localhost:3000/_broker</span>
265
+ </div>
266
+ <div class="endpoint">
267
+ <span class="label">Aggregate · Raw WebSocket</span>
268
+ <span class="url">ws://localhost:3000/_all</span>
269
+ </div>
270
+ <div class="endpoint">
271
+ <span class="label">Broker · Legacy SSE</span>
272
+ <span class="url">http://localhost:3000/_broker/sse</span>
273
+ </div>
274
+ <div class="endpoint">
275
+ <span class="label">Aggregate · Legacy SSE</span>
276
+ <span class="url">http://localhost:3000/_all/sse</span>
277
+ </div>
278
+ </div>
279
+ </div>
280
+ </section>
281
+
282
+ </main>
283
+
284
+ <!-- ================================ FOOTER ============================= -->
285
+ <footer>
286
+ <div class="shell footer-inner">
287
+ <div class="footer-links">
288
+ <a href="https://github.com/pandaGaume/mcp-broker" target="_blank" rel="noopener">github.com/pandaGaume/mcp-broker</a>
289
+ <a href="https://npmjs.com/package/@cyanmycelium/mcp-broker" target="_blank" rel="noopener">npmjs.com/package/@cyanmycelium/mcp-broker</a>
290
+ <a href="https://github.com/pandaGaume/mcp-broker/tree/main/docs" target="_blank" rel="noopener">Docs</a>
291
+ </div>
292
+ <div>© CyanMycelium · Apache-2.0</div>
293
+ </div>
294
+ </footer>
295
+
296
+ <div class="toast" id="toast">Copied to clipboard</div>
297
+
298
+ <script>
299
+ /* ---- toast + copy plumbing (mirrors the launcher page) ----------- */
300
+ const toast = document.getElementById('toast');
301
+ let toastTimer;
302
+ function showToast(msg) {
303
+ toast.textContent = msg || 'Copied to clipboard';
304
+ toast.classList.add('show');
305
+ clearTimeout(toastTimer);
306
+ toastTimer = setTimeout(() => toast.classList.remove('show'), 1600);
307
+ }
308
+
309
+ document.querySelectorAll('[data-copy-block]').forEach((btn) => {
310
+ btn.addEventListener('click', async () => {
311
+ const wrap = btn.closest('.code-wrap');
312
+ const pre = wrap.querySelector('pre');
313
+ try {
314
+ await navigator.clipboard.writeText(pre.innerText);
315
+ btn.classList.add('copied');
316
+ const orig = btn.textContent;
317
+ btn.textContent = '✓ Copied';
318
+ setTimeout(() => { btn.textContent = orig; btn.classList.remove('copied'); }, 1400);
319
+ } catch (e) {
320
+ showToast('Copy failed');
321
+ }
322
+ });
323
+ });
324
+
325
+ document.querySelectorAll('a[href^="#"]').forEach((a) => {
326
+ a.addEventListener('click', (e) => {
327
+ const id = a.getAttribute('href').slice(1);
328
+ const el = document.getElementById(id);
329
+ if (!el) return;
330
+ e.preventDefault();
331
+ window.scrollTo({ top: el.offsetTop - 70, behavior: 'smooth' });
332
+ history.replaceState(null, '', '#' + id);
333
+ });
334
+ });
335
+ </script>
336
+
337
+ </body>
338
+ </html>