@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.
- package/.mcp-broker.example/CONFIGURATION-EN.md +861 -0
- package/.mcp-broker.example/CONFIGURATION-FR.md +871 -0
- package/.mcp-broker.example/README.md +8 -3
- package/.mcp-broker.example/config.json +86 -0
- package/README.md +122 -3
- package/dist/bin.d.ts +0 -1
- package/dist/bin.js +180 -208
- package/dist/bin.js.map +1 -1
- package/dist/chunk-FTDKH2C4.js +3670 -0
- package/dist/chunk-FTDKH2C4.js.map +1 -0
- package/dist/{broker/grammars → grammars}/claude/en.json +1 -1
- package/dist/{broker/grammars → grammars}/claude/fr.json +1 -1
- package/dist/index.d.ts +1790 -18
- package/dist/index.js +2 -14
- package/dist/index.js.map +1 -1
- package/package.json +14 -8
- package/scripts/copy-assets.mjs +16 -10
- package/scripts/gen-cert.mjs +5 -5
- package/scripts/pack-mcpb.mjs +5 -5
- package/scripts/sign-bundle.mjs +6 -6
- package/src/auth/auth.config.ts +98 -0
- package/src/auth/auth.types.ts +120 -0
- package/src/auth/http.auth.ts +128 -0
- package/src/auth/index.ts +34 -0
- package/src/auth/jwt.validator.ts +63 -0
- package/src/auth/provider.auth.ts +114 -0
- package/src/auth/resource.metadata.ts +34 -0
- package/src/authorization/audit.ts +35 -0
- package/src/authorization/capability.classifier.ts +114 -0
- package/src/authorization/index.ts +37 -0
- package/src/authorization/policy.engine.ts +212 -0
- package/src/authorization/policy.types.ts +105 -0
- package/src/authorization/resource.path.ts +126 -0
- package/src/authorization/runtime.ts +56 -0
- package/src/authorization/slot.resource.ts +50 -0
- package/src/authorization/subject.mapper.ts +81 -0
- package/src/bin.ts +90 -7
- package/src/broker/adapters/broker.adapter.info.ts +3 -3
- package/src/broker/adapters/broker.adapter.providers.ts +3 -3
- package/src/broker/aggregate/aggregate.catalog.ts +49 -21
- package/src/broker/aggregate/aggregate.server.ts +149 -19
- package/src/broker/aggregate/provider.client.session.ts +22 -22
- package/src/broker/behaviors/broker.behavior.info.ts +4 -4
- package/src/broker/behaviors/broker.behavior.providers.ts +6 -6
- package/src/broker/broker.context.ts +10 -4
- package/src/broker/broker.grammars.ts +16 -13
- package/src/broker/broker.server.ts +12 -9
- package/src/broker/grammars/claude/en.json +1 -1
- package/src/broker/grammars/claude/fr.json +1 -1
- package/src/broker/index.ts +9 -9
- package/src/config.ts +81 -8
- package/src/index.ts +121 -20
- package/src/{mcpb.loader.ts → mcpb/mcpb.loader.ts} +24 -21
- package/src/{mcpb.unzip.ts → mcpb/mcpb.unzip.ts} +2 -2
- package/src/remote.transports.ts +11 -8
- package/src/remote.upstream.ts +11 -8
- package/src/stdio.upstream.ts +9 -6
- package/src/upstream.ts +6 -3
- package/src/ws/ws.interfaces.ts +357 -0
- package/src/{ws.tunnel.builder.ts → ws/ws.tunnel.builder.ts} +112 -9
- package/src/{ws.tunnel.ts → ws/ws.tunnel.ts} +646 -457
- package/web/README.md +94 -0
- package/web/assets/logo.png +0 -0
- package/web/broker-self-mcp.html +338 -0
- package/web/css/styles.css +580 -0
- package/web/demos/DemoPlaceholder.html +258 -0
- package/web/demos/broker-explorer/css/app.css +393 -0
- package/web/demos/broker-explorer/index.html +94 -0
- package/web/demos/broker-explorer/js/app.js +271 -0
- package/web/demos/broker-explorer/js/mcp-ws-client.js +132 -0
- package/web/demos/oauth-lab/README.md +94 -0
- package/web/demos/oauth-lab/config.json +117 -0
- package/web/demos/oauth-lab/css/app.css +1097 -0
- package/web/demos/oauth-lab/index.html +323 -0
- package/web/demos/oauth-lab/js/app.js +654 -0
- package/web/demos/oauth-lab/server/auth-server.mjs +426 -0
- package/web/demos/oauth-lab/server/factory-provider.mjs +269 -0
- package/web/demos/oauth-lab/server/smoke-test.mjs +308 -0
- package/web/demos/oauth-lab/server/start.mjs +106 -0
- package/web/demos/provider-tunnel/css/app.css +384 -0
- package/web/demos/provider-tunnel/index.html +99 -0
- package/web/demos/provider-tunnel/js/app.js +226 -0
- package/web/demos/provider-tunnel/js/toolbox-server.js +186 -0
- package/web/index.html +558 -0
- package/web/js/lib/broker-tunnel.js +173 -0
- package/dist/broker/adapters/broker.adapter.info.d.ts +0 -16
- package/dist/broker/adapters/broker.adapter.info.js +0 -43
- package/dist/broker/adapters/broker.adapter.info.js.map +0 -1
- package/dist/broker/adapters/broker.adapter.providers.d.ts +0 -18
- package/dist/broker/adapters/broker.adapter.providers.js +0 -61
- package/dist/broker/adapters/broker.adapter.providers.js.map +0 -1
- package/dist/broker/aggregate/aggregate.catalog.d.ts +0 -54
- package/dist/broker/aggregate/aggregate.catalog.js +0 -105
- package/dist/broker/aggregate/aggregate.catalog.js.map +0 -1
- package/dist/broker/aggregate/aggregate.server.d.ts +0 -47
- package/dist/broker/aggregate/aggregate.server.js +0 -151
- package/dist/broker/aggregate/aggregate.server.js.map +0 -1
- package/dist/broker/aggregate/provider.client.session.d.ts +0 -52
- package/dist/broker/aggregate/provider.client.session.js +0 -140
- package/dist/broker/aggregate/provider.client.session.js.map +0 -1
- package/dist/broker/behaviors/broker.behavior.info.d.ts +0 -15
- package/dist/broker/behaviors/broker.behavior.info.js +0 -41
- package/dist/broker/behaviors/broker.behavior.info.js.map +0 -1
- package/dist/broker/behaviors/broker.behavior.providers.d.ts +0 -19
- package/dist/broker/behaviors/broker.behavior.providers.js +0 -69
- package/dist/broker/behaviors/broker.behavior.providers.js.map +0 -1
- package/dist/broker/broker.context.d.ts +0 -59
- package/dist/broker/broker.context.js +0 -2
- package/dist/broker/broker.context.js.map +0 -1
- package/dist/broker/broker.grammars.d.ts +0 -130
- package/dist/broker/broker.grammars.js +0 -229
- package/dist/broker/broker.grammars.js.map +0 -1
- package/dist/broker/broker.server.d.ts +0 -66
- package/dist/broker/broker.server.js +0 -73
- package/dist/broker/broker.server.js.map +0 -1
- package/dist/broker/index.d.ts +0 -9
- package/dist/broker/index.js +0 -7
- package/dist/broker/index.js.map +0 -1
- package/dist/config.d.ts +0 -136
- package/dist/config.js +0 -61
- package/dist/config.js.map +0 -1
- package/dist/mcpb.loader.d.ts +0 -24
- package/dist/mcpb.loader.js +0 -161
- package/dist/mcpb.loader.js.map +0 -1
- package/dist/mcpb.unzip.d.ts +0 -6
- package/dist/mcpb.unzip.js +0 -95
- package/dist/mcpb.unzip.js.map +0 -1
- package/dist/remote.transports.d.ts +0 -16
- package/dist/remote.transports.js +0 -297
- package/dist/remote.transports.js.map +0 -1
- package/dist/remote.upstream.d.ts +0 -36
- package/dist/remote.upstream.js +0 -52
- package/dist/remote.upstream.js.map +0 -1
- package/dist/stdio.upstream.d.ts +0 -45
- package/dist/stdio.upstream.js +0 -85
- package/dist/stdio.upstream.js.map +0 -1
- package/dist/upstream.d.ts +0 -33
- package/dist/upstream.js +0 -2
- package/dist/upstream.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.js +0 -9
- package/dist/version.js.map +0 -1
- package/dist/ws.tunnel.builder.d.ts +0 -139
- package/dist/ws.tunnel.builder.js +0 -205
- package/dist/ws.tunnel.builder.js.map +0 -1
- package/dist/ws.tunnel.d.ts +0 -373
- package/dist/ws.tunnel.js +0 -1090
- package/dist/ws.tunnel.js.map +0 -1
- /package/dist/{broker/grammars → grammars}/default/en.json +0 -0
- /package/dist/{broker/grammars → grammars}/default/fr.json +0 -0
- /package/dist/{broker/grammars → grammars}/default/zh.json +0 -0
package/web/index.html
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
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>@cyanmycelium/mcp-broker - running on localhost:3000</title>
|
|
7
|
+
<meta name="description" content="The MCP broker is running. It is safe to close this page." />
|
|
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
|
+
/* --- Landing-specific overrides ----------------------------------- */
|
|
15
|
+
|
|
16
|
+
/* The hero is shorter on this page: it's a status banner, not a marketing pitch */
|
|
17
|
+
.hero { padding: 80px 0 56px; }
|
|
18
|
+
.hero-mark { margin-bottom: 20px; }
|
|
19
|
+
|
|
20
|
+
/* "Running" status dot, pulsing green to read as "live" at a glance */
|
|
21
|
+
.status-row {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: 10px;
|
|
25
|
+
font-family: var(--font-mono);
|
|
26
|
+
font-size: 12px;
|
|
27
|
+
color: var(--lime);
|
|
28
|
+
letter-spacing: 0.04em;
|
|
29
|
+
text-transform: uppercase;
|
|
30
|
+
margin-bottom: 18px;
|
|
31
|
+
}
|
|
32
|
+
.status-row .pulse {
|
|
33
|
+
position: relative;
|
|
34
|
+
width: 8px; height: 8px;
|
|
35
|
+
border-radius: 50%;
|
|
36
|
+
background: var(--lime);
|
|
37
|
+
box-shadow: 0 0 10px var(--lime);
|
|
38
|
+
}
|
|
39
|
+
.status-row .pulse::after {
|
|
40
|
+
content: "";
|
|
41
|
+
position: absolute;
|
|
42
|
+
inset: -4px;
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
border: 1px solid var(--lime);
|
|
45
|
+
opacity: 0.6;
|
|
46
|
+
animation: pulse 1.6s ease-out infinite;
|
|
47
|
+
}
|
|
48
|
+
@keyframes pulse {
|
|
49
|
+
0% { transform: scale(0.6); opacity: 0.7; }
|
|
50
|
+
100% { transform: scale(2.4); opacity: 0; }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Address line, copy-on-click, same vibe as the hero CTA from the docs site */
|
|
54
|
+
.address {
|
|
55
|
+
display: inline-flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 14px;
|
|
58
|
+
margin-top: 8px;
|
|
59
|
+
padding: 14px 18px;
|
|
60
|
+
background: var(--bg-elev-1);
|
|
61
|
+
border: 1px solid var(--rule-strong);
|
|
62
|
+
border-radius: 8px;
|
|
63
|
+
font-family: var(--font-mono);
|
|
64
|
+
font-size: 15px;
|
|
65
|
+
color: var(--fg);
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
transition: border-color 120ms, background 120ms;
|
|
68
|
+
max-width: 100%;
|
|
69
|
+
}
|
|
70
|
+
.address:hover { border-color: var(--blue-dim); background: var(--bg-elev-2); }
|
|
71
|
+
.address .scheme { color: var(--fg-dim); }
|
|
72
|
+
.address .host { color: var(--blue); }
|
|
73
|
+
.address .copy-hint {
|
|
74
|
+
margin-left: auto;
|
|
75
|
+
font-size: 10px;
|
|
76
|
+
color: var(--fg-dim);
|
|
77
|
+
letter-spacing: 0.16em;
|
|
78
|
+
text-transform: uppercase;
|
|
79
|
+
padding-left: 12px;
|
|
80
|
+
border-left: 1px solid var(--rule);
|
|
81
|
+
}
|
|
82
|
+
.address.copied .copy-hint { color: var(--blue); }
|
|
83
|
+
|
|
84
|
+
/* "Safe to close" reassurance, green-tinted version of the .conclusion block */
|
|
85
|
+
.safe {
|
|
86
|
+
margin: 28px 0 0;
|
|
87
|
+
padding: 14px 18px;
|
|
88
|
+
background: rgba(184, 224, 96, 0.06);
|
|
89
|
+
border-left: 2px solid var(--lime);
|
|
90
|
+
border-radius: 0 6px 6px 0;
|
|
91
|
+
color: var(--fg-muted);
|
|
92
|
+
font-size: 14px;
|
|
93
|
+
line-height: 1.55;
|
|
94
|
+
}
|
|
95
|
+
.safe b { color: var(--fg); font-weight: 500; }
|
|
96
|
+
.safe svg { vertical-align: -3px; margin-right: 6px; color: var(--lime); }
|
|
97
|
+
|
|
98
|
+
/* Endpoints: a quick reference, two columns on wide screens */
|
|
99
|
+
.endpoints {
|
|
100
|
+
display: grid;
|
|
101
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
102
|
+
gap: 12px;
|
|
103
|
+
margin-top: 24px;
|
|
104
|
+
}
|
|
105
|
+
.endpoint {
|
|
106
|
+
display: flex;
|
|
107
|
+
flex-direction: column;
|
|
108
|
+
gap: 6px;
|
|
109
|
+
padding: 14px 16px;
|
|
110
|
+
background: var(--bg-elev-1);
|
|
111
|
+
border: 1px solid var(--rule);
|
|
112
|
+
border-radius: 6px;
|
|
113
|
+
}
|
|
114
|
+
.endpoint .label {
|
|
115
|
+
font-family: var(--font-mono);
|
|
116
|
+
font-size: 10px;
|
|
117
|
+
letter-spacing: 0.16em;
|
|
118
|
+
color: var(--blue);
|
|
119
|
+
text-transform: uppercase;
|
|
120
|
+
}
|
|
121
|
+
.endpoint .url {
|
|
122
|
+
font-family: var(--font-mono);
|
|
123
|
+
font-size: 13px;
|
|
124
|
+
color: var(--fg);
|
|
125
|
+
word-break: break-all;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Example cards grid, links to other pages mounted under www/ */
|
|
129
|
+
.examples-grid {
|
|
130
|
+
display: grid;
|
|
131
|
+
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
132
|
+
gap: 14px;
|
|
133
|
+
margin-top: 8px;
|
|
134
|
+
}
|
|
135
|
+
a.example {
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
gap: 8px;
|
|
139
|
+
padding: 18px 20px;
|
|
140
|
+
background: var(--bg-elev-1);
|
|
141
|
+
border: 1px solid var(--rule-strong);
|
|
142
|
+
border-radius: 8px;
|
|
143
|
+
color: var(--fg);
|
|
144
|
+
text-decoration: none;
|
|
145
|
+
transition: border-color 160ms, background 160ms, transform 160ms;
|
|
146
|
+
position: relative;
|
|
147
|
+
}
|
|
148
|
+
a.example:hover {
|
|
149
|
+
border-color: var(--blue-dim);
|
|
150
|
+
background: var(--bg-elev-2);
|
|
151
|
+
text-decoration: none;
|
|
152
|
+
transform: translateY(-1px);
|
|
153
|
+
}
|
|
154
|
+
a.example .ex-head {
|
|
155
|
+
display: flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
justify-content: space-between;
|
|
158
|
+
gap: 12px;
|
|
159
|
+
}
|
|
160
|
+
a.example .ex-title {
|
|
161
|
+
font-family: var(--font-sans);
|
|
162
|
+
font-size: 15px;
|
|
163
|
+
font-weight: 600;
|
|
164
|
+
color: var(--fg);
|
|
165
|
+
letter-spacing: -0.01em;
|
|
166
|
+
}
|
|
167
|
+
a.example .ex-tag {
|
|
168
|
+
font-family: var(--font-mono);
|
|
169
|
+
font-size: 10px;
|
|
170
|
+
letter-spacing: 0.12em;
|
|
171
|
+
text-transform: uppercase;
|
|
172
|
+
color: var(--fg-dim);
|
|
173
|
+
padding: 2px 7px;
|
|
174
|
+
border: 1px solid var(--rule-strong);
|
|
175
|
+
border-radius: 3px;
|
|
176
|
+
}
|
|
177
|
+
a.example .ex-tag.live { color: var(--lime); border-color: rgba(184, 224, 96, 0.35); }
|
|
178
|
+
a.example .ex-tag.tool { color: var(--blue); border-color: rgba(59, 130, 246, 0.35); }
|
|
179
|
+
a.example .ex-desc {
|
|
180
|
+
color: var(--fg-muted);
|
|
181
|
+
font-size: 13.5px;
|
|
182
|
+
line-height: 1.5;
|
|
183
|
+
margin: 0;
|
|
184
|
+
}
|
|
185
|
+
a.example .ex-path {
|
|
186
|
+
font-family: var(--font-mono);
|
|
187
|
+
font-size: 11px;
|
|
188
|
+
color: var(--fg-dim);
|
|
189
|
+
margin-top: 4px;
|
|
190
|
+
letter-spacing: 0.01em;
|
|
191
|
+
}
|
|
192
|
+
a.example .ex-path .arrow {
|
|
193
|
+
color: var(--blue);
|
|
194
|
+
transition: transform 160ms;
|
|
195
|
+
display: inline-block;
|
|
196
|
+
}
|
|
197
|
+
a.example:hover .ex-path .arrow { transform: translateX(3px); }
|
|
198
|
+
|
|
199
|
+
/* Placeholder cards, visibly inactive, still clickable to the explainer.
|
|
200
|
+
Uses the same dashed/striped pattern as the placeholder page's mark
|
|
201
|
+
so the two visually agree. */
|
|
202
|
+
a.example.placeholder {
|
|
203
|
+
background:
|
|
204
|
+
repeating-linear-gradient(
|
|
205
|
+
45deg,
|
|
206
|
+
transparent 0 6px,
|
|
207
|
+
rgba(255, 255, 255, 0.012) 6px 7px
|
|
208
|
+
),
|
|
209
|
+
var(--bg-elev-1);
|
|
210
|
+
border-style: dashed;
|
|
211
|
+
border-color: var(--rule);
|
|
212
|
+
opacity: 0.72;
|
|
213
|
+
}
|
|
214
|
+
a.example.placeholder:hover {
|
|
215
|
+
opacity: 1;
|
|
216
|
+
transform: none;
|
|
217
|
+
border-color: var(--rule-strong);
|
|
218
|
+
background:
|
|
219
|
+
repeating-linear-gradient(
|
|
220
|
+
45deg,
|
|
221
|
+
transparent 0 6px,
|
|
222
|
+
rgba(255, 255, 255, 0.02) 6px 7px
|
|
223
|
+
),
|
|
224
|
+
var(--bg-elev-2);
|
|
225
|
+
}
|
|
226
|
+
a.example.placeholder .ex-title { color: var(--fg-muted); }
|
|
227
|
+
a.example.placeholder .ex-desc { color: var(--fg-dim); }
|
|
228
|
+
a.example.placeholder .ex-path { color: var(--fg-dim); }
|
|
229
|
+
a.example.placeholder .ex-path .arrow { color: var(--fg-dim); }
|
|
230
|
+
a.example.placeholder .ex-tag {
|
|
231
|
+
color: var(--fg-dim);
|
|
232
|
+
border-color: var(--rule);
|
|
233
|
+
background: transparent;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* Hide-this-page section: a quieter, more compact look */
|
|
237
|
+
#disable .code-wrap { margin-top: 14px; }
|
|
238
|
+
.hide-options {
|
|
239
|
+
display: grid;
|
|
240
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
241
|
+
gap: 14px;
|
|
242
|
+
margin-top: 18px;
|
|
243
|
+
}
|
|
244
|
+
.hide-option {
|
|
245
|
+
padding: 14px 16px;
|
|
246
|
+
background: var(--bg-elev-1);
|
|
247
|
+
border: 1px solid var(--rule);
|
|
248
|
+
border-radius: 6px;
|
|
249
|
+
}
|
|
250
|
+
.hide-option .h {
|
|
251
|
+
font-family: var(--font-mono);
|
|
252
|
+
font-size: 11px;
|
|
253
|
+
color: var(--blue);
|
|
254
|
+
letter-spacing: 0.12em;
|
|
255
|
+
text-transform: uppercase;
|
|
256
|
+
margin-bottom: 6px;
|
|
257
|
+
}
|
|
258
|
+
.hide-option p {
|
|
259
|
+
margin: 0;
|
|
260
|
+
font-size: 13.5px;
|
|
261
|
+
line-height: 1.55;
|
|
262
|
+
}
|
|
263
|
+
.hide-option code {
|
|
264
|
+
font-family: var(--font-mono);
|
|
265
|
+
font-size: 12px;
|
|
266
|
+
color: var(--blue);
|
|
267
|
+
background: var(--bg-inline);
|
|
268
|
+
border: 1px solid var(--rule);
|
|
269
|
+
padding: 1px 5px;
|
|
270
|
+
border-radius: 3px;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* Section heads on this page don't need such big padding: it's a single screen */
|
|
274
|
+
section { padding: 56px 0; }
|
|
275
|
+
section:first-of-type { padding-top: 0; } /* hero handles its own top */
|
|
276
|
+
</style>
|
|
277
|
+
</head>
|
|
278
|
+
<body>
|
|
279
|
+
|
|
280
|
+
<!-- ============================== TOPBAR ============================== -->
|
|
281
|
+
<header class="topbar">
|
|
282
|
+
<div class="shell topbar-inner">
|
|
283
|
+
<a class="brand" href="/">
|
|
284
|
+
<img class="brand-mark" src="assets/logo.png" alt="" aria-hidden="true" />
|
|
285
|
+
<span>@cyanmycelium/mcp-broker</span>
|
|
286
|
+
</a>
|
|
287
|
+
<nav class="topbar-links">
|
|
288
|
+
<a href="#endpoints">Endpoints</a>
|
|
289
|
+
<a href="#examples">Examples</a>
|
|
290
|
+
<a href="#disable">Hide this page</a>
|
|
291
|
+
<a href="https://github.com/pandaGaume/mcp-broker" target="_blank" rel="noopener">GitHub →</a>
|
|
292
|
+
</nav>
|
|
293
|
+
</div>
|
|
294
|
+
</header>
|
|
295
|
+
|
|
296
|
+
<main>
|
|
297
|
+
|
|
298
|
+
<!-- ================================ HERO =============================== -->
|
|
299
|
+
<section class="hero">
|
|
300
|
+
<div class="shell">
|
|
301
|
+
<div class="hero-mark"><img src="assets/logo.png" alt="" /></div>
|
|
302
|
+
|
|
303
|
+
<div class="status-row">
|
|
304
|
+
<span class="pulse" aria-hidden="true"></span>
|
|
305
|
+
<span>broker running · uptime 00:00:12</span>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<h1 class="hero-title">
|
|
309
|
+
Your broker is up.
|
|
310
|
+
</h1>
|
|
311
|
+
|
|
312
|
+
<p class="hero-sub">
|
|
313
|
+
<code class="inline" style="font-size: 0.95em;">@cyanmycelium/mcp-broker</code>
|
|
314
|
+
is listening on the address below. Point your MCP clients at it,
|
|
315
|
+
or open one of the bundled example pages.
|
|
316
|
+
</p>
|
|
317
|
+
|
|
318
|
+
<button class="address" type="button" data-copy="http://localhost:3000" id="address-btn">
|
|
319
|
+
<span class="scheme">http://</span><span class="host">localhost:3000</span>
|
|
320
|
+
<span class="copy-hint" data-copy-state>copy</span>
|
|
321
|
+
</button>
|
|
322
|
+
|
|
323
|
+
<div class="safe">
|
|
324
|
+
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5"/></svg>
|
|
325
|
+
<b>Safe to close this page.</b>
|
|
326
|
+
The broker runs in the terminal that started it, closing the tab will not stop it.
|
|
327
|
+
Use <code class="inline">Ctrl + C</code> in that terminal when you want to shut it down.
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</section>
|
|
331
|
+
|
|
332
|
+
<!-- ============================ ENDPOINTS ============================== -->
|
|
333
|
+
<section id="endpoints">
|
|
334
|
+
<div class="shell">
|
|
335
|
+
<div class="section-num">01 - Endpoints</div>
|
|
336
|
+
<h2>Where to connect from</h2>
|
|
337
|
+
|
|
338
|
+
<p class="intro">Four transports are mounted on the same host. Replace <code class="inline"><name></code> with one of your provider slot names: or use <code class="inline">_broker</code> to talk to the broker itself, or <code class="inline">_all</code> for the aggregate of every provider.</p>
|
|
339
|
+
|
|
340
|
+
<div class="endpoints">
|
|
341
|
+
<div class="endpoint">
|
|
342
|
+
<span class="label">Provider · WebSocket</span>
|
|
343
|
+
<span class="url">ws://localhost:3000/provider/<name></span>
|
|
344
|
+
</div>
|
|
345
|
+
<div class="endpoint">
|
|
346
|
+
<span class="label">Client · Streamable HTTP</span>
|
|
347
|
+
<span class="url">http://localhost:3000/<name>/mcp</span>
|
|
348
|
+
</div>
|
|
349
|
+
<div class="endpoint">
|
|
350
|
+
<span class="label">Client · Raw WebSocket</span>
|
|
351
|
+
<span class="url">ws://localhost:3000/<name></span>
|
|
352
|
+
</div>
|
|
353
|
+
<div class="endpoint">
|
|
354
|
+
<span class="label">Client · Legacy SSE</span>
|
|
355
|
+
<span class="url">http://localhost:3000/<name>/sse</span>
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
</section>
|
|
360
|
+
|
|
361
|
+
<!-- ============================ EXAMPLES =============================== -->
|
|
362
|
+
<section id="examples">
|
|
363
|
+
<div class="shell">
|
|
364
|
+
<div class="section-num">02 - Bundled examples</div>
|
|
365
|
+
<h2>Try the broker without writing any code</h2>
|
|
366
|
+
|
|
367
|
+
<p class="intro">Each card is a small page served by the broker's static mount. They register themselves as MCP providers on connect, so any client pointed at the matching slot can call their tools immediately.</p>
|
|
368
|
+
|
|
369
|
+
<div class="examples-grid">
|
|
370
|
+
|
|
371
|
+
<a class="example" href="demos/provider-tunnel/">
|
|
372
|
+
<div class="ex-head">
|
|
373
|
+
<span class="ex-title">Provider Tunnel</span>
|
|
374
|
+
<span class="ex-tag live">live</span>
|
|
375
|
+
</div>
|
|
376
|
+
<p class="ex-desc">An MCP server running in the browser, tunnelled to the broker over WebSocket using the official <code class="inline">@modelcontextprotocol/sdk</code>. Exposes 6 tools and 2 resources.</p>
|
|
377
|
+
<span class="ex-path">demos/provider-tunnel/ <span class="arrow">→</span></span>
|
|
378
|
+
</a>
|
|
379
|
+
|
|
380
|
+
<a class="example placeholder" href="demos/DemoPlaceholder.html?slot=babylon-scene">
|
|
381
|
+
<div class="ex-head">
|
|
382
|
+
<span class="ex-title">Babylon scene</span>
|
|
383
|
+
<span class="ex-tag">soon</span>
|
|
384
|
+
</div>
|
|
385
|
+
<p class="ex-desc">A 3D scene that exposes <code class="inline">move</code>, <code class="inline">rotate</code> and <code class="inline">camera</code> tools over a WebSocket up to the broker.</p>
|
|
386
|
+
<span class="ex-path">demos/babylon-scene/ <span class="arrow">→</span></span>
|
|
387
|
+
</a>
|
|
388
|
+
|
|
389
|
+
<a class="example" href="broker-self-mcp.html">
|
|
390
|
+
<div class="ex-head">
|
|
391
|
+
<span class="ex-title">Broker self-MCP</span>
|
|
392
|
+
<span class="ex-tag tool">explainer</span>
|
|
393
|
+
</div>
|
|
394
|
+
<p class="ex-desc">The broker is itself an MCP server. Read about the <code class="inline">_broker</code> introspection slot, the <code class="inline">_all</code> aggregate, and loading signed <code class="inline">.mcpb</code> bundles.</p>
|
|
395
|
+
<span class="ex-path">broker-self-mcp.html <span class="arrow">→</span></span>
|
|
396
|
+
</a>
|
|
397
|
+
|
|
398
|
+
<a class="example" href="demos/broker-explorer/">
|
|
399
|
+
<div class="ex-head">
|
|
400
|
+
<span class="ex-title">Broker Explorer</span>
|
|
401
|
+
<span class="ex-tag live">live</span>
|
|
402
|
+
</div>
|
|
403
|
+
<p class="ex-desc">Connect a browser MCP client to the broker's <code class="inline">_broker</code> or <code class="inline">_all</code> slot, list its tools and call them live.</p>
|
|
404
|
+
<span class="ex-path">demos/broker-explorer/ <span class="arrow">→</span></span>
|
|
405
|
+
</a>
|
|
406
|
+
|
|
407
|
+
<a class="example" href="demos/oauth-lab/">
|
|
408
|
+
<div class="ex-head">
|
|
409
|
+
<span class="ex-title">OAuth Policy Lab</span>
|
|
410
|
+
<span class="ex-tag tool">npm demo</span>
|
|
411
|
+
</div>
|
|
412
|
+
<p class="ex-desc">Start with <code class="inline">npm run demo:oauth</code>, then run PKCE, inspect an audience-bound JWT, exercise hierarchical policies, and follow the live audit.</p>
|
|
413
|
+
<span class="ex-path">demos/oauth-lab/ <span class="arrow">-></span></span>
|
|
414
|
+
</a>
|
|
415
|
+
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
</section>
|
|
419
|
+
|
|
420
|
+
<!-- ========================== DISABLE THIS PAGE ======================== -->
|
|
421
|
+
<section id="disable">
|
|
422
|
+
<div class="shell">
|
|
423
|
+
<div class="section-num">03 - Hide this page on next launch</div>
|
|
424
|
+
<h2>Don't want a browser tab every time?</h2>
|
|
425
|
+
|
|
426
|
+
<p class="intro">This landing only opens because <code class="inline">www.open</code> is <code class="inline">true</code> (the default for fresh installs). Flip it off and the broker will keep serving everything silently in the background.</p>
|
|
427
|
+
|
|
428
|
+
<div class="hide-options">
|
|
429
|
+
<div class="hide-option">
|
|
430
|
+
<div class="h">Option A · config file</div>
|
|
431
|
+
<p>Set <code>"open": false</code> under <code>www</code> in <code>.mcp-broker/config.json</code>.</p>
|
|
432
|
+
</div>
|
|
433
|
+
<div class="hide-option">
|
|
434
|
+
<div class="h">Option B · env var</div>
|
|
435
|
+
<p>Unset (or remove) <code>MCP_BROKER_OPEN</code> before launching the broker.</p>
|
|
436
|
+
</div>
|
|
437
|
+
<div class="hide-option">
|
|
438
|
+
<div class="h">Option C · CLI flag</div>
|
|
439
|
+
<p>Pass <code>--no-open</code> on the command line, wins over both.</p>
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
|
|
443
|
+
<div class="code-wrap">
|
|
444
|
+
<div class="code-head">
|
|
445
|
+
<span class="lang">JSON · .mcp-broker/config.json</span>
|
|
446
|
+
<button class="copy-btn" data-copy-block>Copy</button>
|
|
447
|
+
</div>
|
|
448
|
+
<pre><code><span class="tk-pu">{</span>
|
|
449
|
+
<span class="tk-p">"port"</span><span class="tk-pu">:</span> <span class="tk-n">3000</span><span class="tk-pu">,</span>
|
|
450
|
+
<span class="tk-p">"www"</span><span class="tk-pu">: {</span>
|
|
451
|
+
<span class="tk-p">"open"</span><span class="tk-pu">:</span> <span class="tk-k">false</span><span class="tk-pu">,</span> <span class="tk-c">// ← was true; flip to false</span>
|
|
452
|
+
<span class="tk-p">"mounts"</span><span class="tk-pu">: [{</span> <span class="tk-p">"urlPrefix"</span><span class="tk-pu">:</span> <span class="tk-s">"/"</span><span class="tk-pu">,</span> <span class="tk-p">"dir"</span><span class="tk-pu">:</span> <span class="tk-s">"www"</span> <span class="tk-pu">}]</span>
|
|
453
|
+
<span class="tk-pu">}</span>
|
|
454
|
+
<span class="tk-pu">}</span>
|
|
455
|
+
</code></pre>
|
|
456
|
+
</div>
|
|
457
|
+
|
|
458
|
+
<p class="caption">Need the page back for a one-off? <code class="inline">npx @cyanmycelium/mcp-broker --open</code> overrides the config for a single launch.</p>
|
|
459
|
+
</div>
|
|
460
|
+
</section>
|
|
461
|
+
|
|
462
|
+
</main>
|
|
463
|
+
|
|
464
|
+
<!-- ================================ FOOTER ============================= -->
|
|
465
|
+
<footer>
|
|
466
|
+
<div class="shell footer-inner">
|
|
467
|
+
<div class="footer-links">
|
|
468
|
+
<a href="https://github.com/pandaGaume/mcp-broker" target="_blank" rel="noopener">github.com/pandaGaume/mcp-broker</a>
|
|
469
|
+
<a href="https://npmjs.com/package/@cyanmycelium/mcp-broker" target="_blank" rel="noopener">npmjs.com/package/@cyanmycelium/mcp-broker</a>
|
|
470
|
+
<a href="https://github.com/pandaGaume/mcp-broker/tree/main/docs" target="_blank" rel="noopener">Docs</a>
|
|
471
|
+
</div>
|
|
472
|
+
<div>© CyanMycelium · Apache-2.0</div>
|
|
473
|
+
</div>
|
|
474
|
+
</footer>
|
|
475
|
+
|
|
476
|
+
<div class="toast" id="toast">Copied to clipboard</div>
|
|
477
|
+
|
|
478
|
+
<script>
|
|
479
|
+
/* ---- live uptime counter ----------------------------------------- */
|
|
480
|
+
(function () {
|
|
481
|
+
const start = Date.now();
|
|
482
|
+
const el = document.querySelector('.status-row span:last-child');
|
|
483
|
+
if (!el) return;
|
|
484
|
+
function pad(n) { return String(n).padStart(2, '0'); }
|
|
485
|
+
function tick() {
|
|
486
|
+
const sec = Math.floor((Date.now() - start) / 1000);
|
|
487
|
+
const h = Math.floor(sec / 3600);
|
|
488
|
+
const m = Math.floor((sec % 3600) / 60);
|
|
489
|
+
const s = sec % 60;
|
|
490
|
+
el.textContent = 'broker running · uptime ' + pad(h) + ':' + pad(m) + ':' + pad(s);
|
|
491
|
+
}
|
|
492
|
+
tick();
|
|
493
|
+
setInterval(tick, 1000);
|
|
494
|
+
})();
|
|
495
|
+
|
|
496
|
+
/* ---- toast + copy plumbing (mirrors the docs site) --------------- */
|
|
497
|
+
const toast = document.getElementById('toast');
|
|
498
|
+
let toastTimer;
|
|
499
|
+
function showToast(msg) {
|
|
500
|
+
toast.textContent = msg || 'Copied to clipboard';
|
|
501
|
+
toast.classList.add('show');
|
|
502
|
+
clearTimeout(toastTimer);
|
|
503
|
+
toastTimer = setTimeout(() => toast.classList.remove('show'), 1600);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
document.querySelectorAll('[data-copy]').forEach((btn) => {
|
|
507
|
+
btn.addEventListener('click', async () => {
|
|
508
|
+
const text = btn.getAttribute('data-copy');
|
|
509
|
+
try {
|
|
510
|
+
await navigator.clipboard.writeText(text);
|
|
511
|
+
btn.classList.add('copied');
|
|
512
|
+
const stateEl = btn.querySelector('[data-copy-state]');
|
|
513
|
+
if (stateEl) {
|
|
514
|
+
const original = stateEl.textContent;
|
|
515
|
+
stateEl.textContent = '✓ copied';
|
|
516
|
+
setTimeout(() => {
|
|
517
|
+
stateEl.textContent = original;
|
|
518
|
+
btn.classList.remove('copied');
|
|
519
|
+
}, 1400);
|
|
520
|
+
}
|
|
521
|
+
showToast(text);
|
|
522
|
+
} catch (e) {
|
|
523
|
+
showToast('Copy failed');
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
document.querySelectorAll('[data-copy-block]').forEach((btn) => {
|
|
529
|
+
btn.addEventListener('click', async () => {
|
|
530
|
+
const wrap = btn.closest('.code-wrap');
|
|
531
|
+
const pre = wrap.querySelector('pre');
|
|
532
|
+
const text = pre.innerText;
|
|
533
|
+
try {
|
|
534
|
+
await navigator.clipboard.writeText(text);
|
|
535
|
+
btn.classList.add('copied');
|
|
536
|
+
const orig = btn.textContent;
|
|
537
|
+
btn.textContent = '✓ Copied';
|
|
538
|
+
setTimeout(() => { btn.textContent = orig; btn.classList.remove('copied'); }, 1400);
|
|
539
|
+
} catch (e) {
|
|
540
|
+
showToast('Copy failed');
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
document.querySelectorAll('a[href^="#"]').forEach((a) => {
|
|
546
|
+
a.addEventListener('click', (e) => {
|
|
547
|
+
const id = a.getAttribute('href').slice(1);
|
|
548
|
+
const el = document.getElementById(id);
|
|
549
|
+
if (!el) return;
|
|
550
|
+
e.preventDefault();
|
|
551
|
+
window.scrollTo({ top: el.offsetTop - 70, behavior: 'smooth' });
|
|
552
|
+
history.replaceState(null, '', '#' + id);
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
</script>
|
|
556
|
+
|
|
557
|
+
</body>
|
|
558
|
+
</html>
|