@cyanmycelium/mcp-broker 1.2.1 → 1.3.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 +300 -44
- package/.mcp-broker.example/CONFIGURATION-FR.md +313 -44
- package/.mcp-broker.example/README.md +73 -2
- package/.mcp-broker.example/config.json +18 -9
- package/.mcp-broker.example/config.stdio-bridge.json +16 -0
- package/README.md +407 -27
- package/dist/bin.js +170 -23
- package/dist/bin.js.map +1 -1
- package/dist/chunk-BZUZYXVA.js +5955 -0
- package/dist/chunk-BZUZYXVA.js.map +1 -0
- package/dist/grammars/claude/en.json +12 -0
- package/dist/grammars/claude/fr.json +12 -0
- package/dist/grammars/default/en.json +40 -0
- package/dist/grammars/default/fr.json +40 -0
- package/dist/grammars/default/zh.json +40 -0
- package/dist/index.d.ts +991 -25
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/auth/index.ts +3 -1
- package/src/auth/provider.auth.ts +126 -8
- package/src/authorization/policy.engine.ts +11 -2
- package/src/authorization/policy.types.ts +25 -1
- package/src/bin.ts +280 -31
- package/src/broker/adapters/broker.adapter.diagnose.ts +45 -0
- package/src/broker/adapters/broker.adapter.guide.ts +108 -0
- package/src/broker/aggregate/aggregate.server.ts +82 -15
- package/src/broker/aggregate/provider.client.session.ts +85 -11
- package/src/broker/behaviors/broker.behavior.diagnose.ts +47 -0
- package/src/broker/behaviors/broker.behavior.guide.ts +79 -0
- package/src/broker/broker.context.ts +65 -0
- package/src/broker/broker.diagnostics.ts +495 -0
- package/src/broker/broker.guides.ts +1029 -0
- package/src/broker/broker.server.ts +23 -7
- package/src/broker/broker.slots.ts +36 -0
- package/src/broker/grammars/claude/en.json +12 -0
- package/src/broker/grammars/claude/fr.json +12 -0
- package/src/broker/grammars/default/en.json +40 -0
- package/src/broker/grammars/default/fr.json +40 -0
- package/src/broker/grammars/default/zh.json +40 -0
- package/src/config.ts +191 -4
- package/src/index.ts +38 -3
- package/src/remote.transports.ts +127 -10
- package/src/remote.upstream.ts +4 -1
- package/src/ws/ws.interfaces.ts +148 -3
- package/src/ws/ws.tunnel.builder.ts +63 -1
- package/src/ws/ws.tunnel.ts +1150 -173
- package/web/README.md +31 -4
- package/dist/chunk-FTDKH2C4.js +0 -3670
- package/dist/chunk-FTDKH2C4.js.map +0 -1
|
@@ -24,15 +24,27 @@ Quelques règles de lecture :
|
|
|
24
24
|
- Les chemins de fichiers relatifs sont résolus depuis le dossier
|
|
25
25
|
`.mcp-broker/`.
|
|
26
26
|
|
|
27
|
+
Chaque section ci-dessous porte le nom de la clé qu'elle explique, et non un
|
|
28
|
+
numéro de ligne, pour que le guide reste juste quand le fichier d'exemple
|
|
29
|
+
grossit.
|
|
30
|
+
|
|
31
|
+
Une chose à savoir avant tout le reste : **le modèle est livré avec
|
|
32
|
+
`auth.enabled: false`**. Une copie fraîche démarre et répond à tous les clients,
|
|
33
|
+
ce qui est exactement ce qu'il faut le temps de prendre ses marques. Tout le
|
|
34
|
+
bloc `auth` reste présent, comme référence pour le jour où vous l'activerez.
|
|
35
|
+
Lisez [`docs/authorization.md`](../../../../docs/authorization.md) avant de le
|
|
36
|
+
faire.
|
|
37
|
+
|
|
27
38
|
## La carte mentale
|
|
28
39
|
|
|
29
|
-
Le fichier répond à
|
|
40
|
+
Le fichier répond à six questions :
|
|
30
41
|
|
|
31
42
|
1. Où le broker écoute-t-il ?
|
|
32
43
|
2. Comment chiffre-t-il les connexions ?
|
|
33
|
-
3.
|
|
34
|
-
4.
|
|
35
|
-
5.
|
|
44
|
+
3. Quelles pages web, s'il y en a, ont le droit de l'appeler ?
|
|
45
|
+
4. Comment reconnaît-il les clients et les fournisseurs ?
|
|
46
|
+
5. Que peut faire chaque client, et sur quelles ressources ?
|
|
47
|
+
6. Quels serveurs MCP locaux ou empaquetés doit-il charger ?
|
|
36
48
|
|
|
37
49
|
Le bloc `auth` est le plus important pour la sécurité. Il se lit ainsi :
|
|
38
50
|
|
|
@@ -86,7 +98,7 @@ Cette séparation est essentielle :
|
|
|
86
98
|
- les ressources décrivent où cela est permis ;
|
|
87
99
|
- les sujets décrivent à qui cela est permis.
|
|
88
100
|
|
|
89
|
-
##
|
|
101
|
+
## Paramètres généraux
|
|
90
102
|
|
|
91
103
|
```json
|
|
92
104
|
{
|
|
@@ -127,17 +139,71 @@ Nom logique affiché par les outils d'introspection du broker.
|
|
|
127
139
|
|
|
128
140
|
- Il aide à distinguer plusieurs brokers.
|
|
129
141
|
- Il n'a aucun effet sur l'autorisation.
|
|
142
|
+
- **Utilisable seulement par l'API programmatique aujourd'hui.** Le tunnel
|
|
143
|
+
l'honore, mais le broker en ligne de commande n'a pas encore de moyen de le
|
|
144
|
+
transmettre : le renseigner ici ne change donc rien. Passez par
|
|
145
|
+
`IWsTunnelOptions.brokerName` si vous en avez besoin dès maintenant.
|
|
146
|
+
|
|
147
|
+
## Origines navigateur autorisées
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
"allowedOrigins": ["https://app.factory.local", "https://mcp.factory.local"]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
La liste des origines de pages web autorisées à appeler ce broker en HTTP. Elle
|
|
154
|
+
est vérifiée sur `/<slot>/mcp`, `/<slot>/sse` et `/<slot>/messages`.
|
|
155
|
+
|
|
156
|
+
Trois règles à retenir, parce que chacune surprend quelqu'un :
|
|
157
|
+
|
|
158
|
+
1. **Absent veut dire fermé.** Sans `allowedOrigins`, toute requête portant un
|
|
159
|
+
en-tête `Origin` est refusée avec un `403`. C'est volontaire : sans ce
|
|
160
|
+
contrôle, n'importe quelle page ouverte dans le navigateur de l'utilisateur
|
|
161
|
+
pourrait piloter votre broker.
|
|
162
|
+
2. **Une requête sans en-tête `Origin` passe toujours.** Claude Desktop,
|
|
163
|
+
l'Inspector MCP et tous les SDK côté serveur n'en envoient pas : c'est
|
|
164
|
+
pourquoi tout semble parfait jusqu'au premier navigateur.
|
|
165
|
+
3. **Être servie par ce broker n'exempte de rien.** Une page chargée depuis le
|
|
166
|
+
montage `www` reste une origine navigateur et doit figurer dans la liste.
|
|
167
|
+
|
|
168
|
+
La comparaison est littérale : le schéma et le port font partie de la valeur.
|
|
169
|
+
`https://app.factory.local` ne correspond ni à `http://app.factory.local` ni à
|
|
170
|
+
`https://app.factory.local:8443`, et une barre oblique finale ne correspond
|
|
171
|
+
jamais. Ce modèle définit `tls.cert`/`tls.key`, le broker parle donc HTTPS et les
|
|
172
|
+
entrées utilisent `https://`. Retirez le bloc TLS et elles doivent devenir
|
|
173
|
+
`http://...:3001`.
|
|
130
174
|
|
|
131
|
-
|
|
175
|
+
Une expression régulière remplace la liste quand les origines ne sont pas
|
|
176
|
+
connues à l'avance :
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
"allowedOrigins": { "pattern": "^https://[a-z0-9-]+\\.factory\\.local$" }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`MCP_BROKER_ALLOWED_ORIGINS` remplace le fichier par une liste séparée par des
|
|
183
|
+
virgules. Cette variable ne peut pas porter la forme `pattern` : une expression
|
|
184
|
+
régulière ne survit pas à un découpage sur les virgules.
|
|
185
|
+
|
|
186
|
+
## Chemins HTTP et WebSocket
|
|
132
187
|
|
|
133
188
|
```json
|
|
134
189
|
"paths": {
|
|
135
|
-
"provider":
|
|
136
|
-
"
|
|
137
|
-
"
|
|
190
|
+
"provider": "/provider",
|
|
191
|
+
"providers": "/providers",
|
|
192
|
+
"client": "/",
|
|
193
|
+
"mcp": "/mcp",
|
|
194
|
+
"sse": "/sse",
|
|
195
|
+
"messages": "/messages"
|
|
138
196
|
}
|
|
139
197
|
```
|
|
140
198
|
|
|
199
|
+
Les six clés sont honorées, et chacune est aussi réglable par une variable
|
|
200
|
+
d'environnement, qui l'emporte : `MCP_BROKER_PROVIDER_PATH`,
|
|
201
|
+
`MCP_BROKER_PROVIDERS_PATH`, `MCP_BROKER_CLIENT_PATH`, `MCP_BROKER_MCP_PATH`,
|
|
202
|
+
`MCP_BROKER_SSE_PATH`, `MCP_BROKER_MESSAGES_PATH`.
|
|
203
|
+
|
|
204
|
+
En changer un déplace le point d'entrée pour tout le monde : le SDK fournisseur,
|
|
205
|
+
les clients, et les URL affichées au démarrage. N'y touchez pas sans raison.
|
|
206
|
+
|
|
141
207
|
### `paths.provider`
|
|
142
208
|
|
|
143
209
|
Préfixe WebSocket utilisé par un fournisseur qui se connecte au broker.
|
|
@@ -148,7 +214,35 @@ Exemple :
|
|
|
148
214
|
wss://mcp.factory.local/provider/spoony-00452
|
|
149
215
|
```
|
|
150
216
|
|
|
151
|
-
Le fournisseur demande ici le slot `spoony-00452`.
|
|
217
|
+
Le fournisseur demande ici le slot `spoony-00452`. La socket transporte des
|
|
218
|
+
trames JSON-RPC nues, un fournisseur par socket. Dans
|
|
219
|
+
`@cyanmycelium/mcp-broker-provider`, c'est `DirectTransport`.
|
|
220
|
+
|
|
221
|
+
### `paths.providers`
|
|
222
|
+
|
|
223
|
+
Chemin WebSocket exact (aucun nom de slot n'y est ajouté) utilisé par un
|
|
224
|
+
fournisseur qui porte plusieurs slots sur une seule socket, en enveloppant
|
|
225
|
+
chaque trame dans une enveloppe qui nomme le slot. Dans
|
|
226
|
+
`@cyanmycelium/mcp-broker-provider`, c'est `MultiplexTransport`.
|
|
227
|
+
|
|
228
|
+
```text
|
|
229
|
+
wss://mcp.factory.local/providers
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`paths.provider` et `paths.providers` diffèrent d'une lettre et ne sont **pas
|
|
233
|
+
interchangeables** : ce qui les sépare est le format des trames, pas seulement
|
|
234
|
+
l'URL. Brancher un `MultiplexTransport` sur `/provider/<nom>`, ou un
|
|
235
|
+
`DirectTransport` sur `/providers`, est l'erreur d'intégration la plus fréquente.
|
|
236
|
+
Le broker nomme désormais l'incohérence et refuse la socket au lieu de rester
|
|
237
|
+
muet, mais autant faire juste du premier coup :
|
|
238
|
+
|
|
239
|
+
| Point d'entrée | Trames | Transport |
|
|
240
|
+
|---------------------|--------------------|----------------------|
|
|
241
|
+
| `/provider/<nom>` | JSON-RPC nu | `DirectTransport` |
|
|
242
|
+
| `/providers` | enveloppes | `MultiplexTransport` |
|
|
243
|
+
|
|
244
|
+
`/providers/<nom>` n'est ni l'un ni l'autre : c'est compris comme une connexion
|
|
245
|
+
*cliente* sur un slot littéralement nommé `providers/<nom>`.
|
|
152
246
|
|
|
153
247
|
### `paths.client`
|
|
154
248
|
|
|
@@ -169,10 +263,84 @@ Avec le slot `spoony-00452`, l'URL devient :
|
|
|
169
263
|
https://mcp.factory.local/spoony-00452/mcp
|
|
170
264
|
```
|
|
171
265
|
|
|
172
|
-
|
|
173
|
-
|
|
266
|
+
C'est le transport à privilégier. Les deux suivants sont l'ancien couple.
|
|
267
|
+
|
|
268
|
+
### `paths.sse`
|
|
269
|
+
|
|
270
|
+
Suffixe du flux SSE historique, ouvert en `GET`. Le broker y répond par un
|
|
271
|
+
événement `endpoint` qui porte l'URL où poster.
|
|
272
|
+
|
|
273
|
+
```text
|
|
274
|
+
https://mcp.factory.local/spoony-00452/sse
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### `paths.messages`
|
|
278
|
+
|
|
279
|
+
Suffixe où le client SSE historique `POST`e ses requêtes JSON-RPC, en paire avec
|
|
280
|
+
le flux ci-dessus.
|
|
281
|
+
|
|
282
|
+
```text
|
|
283
|
+
https://mcp.factory.local/spoony-00452/messages
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Ces deux points d'entrée historiques sont soumis au même contrôle
|
|
287
|
+
`allowedOrigins` que `/<slot>/mcp`.
|
|
288
|
+
|
|
289
|
+
## Surveillance des fournisseurs
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
"providerHeartbeatIntervalMs": 30000,
|
|
293
|
+
"providerRequestTimeoutMs": 60000,
|
|
294
|
+
"providerTakeover": "liveness"
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Trois clés facultatives. Les valeurs montrées sont celles par défaut : vous
|
|
298
|
+
pouvez supprimer le bloc entier. Elles sont explicitées ici parce que, quand un
|
|
299
|
+
fournisseur se comporte mal, ce sont ces trois-là qu'on règle.
|
|
300
|
+
|
|
301
|
+
### `providerHeartbeatIntervalMs`
|
|
302
|
+
|
|
303
|
+
Intervalle entre deux pings envoyés à chaque socket fournisseur. Un fournisseur
|
|
304
|
+
qui rate un intervalle complet est déconnecté et son slot libéré. `0` désactive
|
|
305
|
+
le mécanisme. Aussi `MCP_BROKER_PROVIDER_HEARTBEAT_MS`.
|
|
306
|
+
|
|
307
|
+
Sans lui, une socket dont le pair a disparu sans fermer proprement (onglet tué,
|
|
308
|
+
portable mis en veille, VPN coupé) reste ouverte pour le système pendant environ
|
|
309
|
+
deux heures, durant lesquelles le broker annonce le slot comme connecté et
|
|
310
|
+
refuse toutes les tentatives de reconnexion.
|
|
311
|
+
|
|
312
|
+
Soyons honnêtes sur ce que cela prouve : un pong est renvoyé par la pile réseau
|
|
313
|
+
du pair, pas par le JavaScript de la page. Cela détecte un processus, une machine
|
|
314
|
+
ou un chemin réseau mort, pas un fournisseur connecté qui ne répond simplement
|
|
315
|
+
pas. Pour cela, voir la clé suivante.
|
|
316
|
+
|
|
317
|
+
### `providerRequestTimeoutMs`
|
|
318
|
+
|
|
319
|
+
Délai au bout duquel le broker abandonne l'attente d'une réponse du fournisseur
|
|
320
|
+
et renvoie une erreur JSON-RPC nommant le slot. `0` désactive le délai. Aussi
|
|
321
|
+
`MCP_BROKER_PROVIDER_REQUEST_TIMEOUT_MS`.
|
|
322
|
+
|
|
323
|
+
Augmentez-le si vous hébergez des outils réellement longs. Diminuez-le si vous
|
|
324
|
+
préférez une erreur à un client qui attend indéfiniment, car c'est l'alternative :
|
|
325
|
+
un onglet mis en veille par le navigateur est un fournisseur connecté qui ne
|
|
326
|
+
répond à rien.
|
|
174
327
|
|
|
175
|
-
|
|
328
|
+
### `providerTakeover`
|
|
329
|
+
|
|
330
|
+
Ce qui se passe quand un fournisseur se connecte à un slot déjà tenu par une
|
|
331
|
+
autre socket.
|
|
332
|
+
|
|
333
|
+
- `"reject"` : le tenant garde toujours le slot.
|
|
334
|
+
- `"liveness"` (défaut) : le tenant ne le garde que tant qu'il répond aux pings.
|
|
335
|
+
- `"always"` : le nouveau venu l'emporte, mais uniquement si l'authentification
|
|
336
|
+
des fournisseurs est configurée et qu'il s'est authentifié sous le même
|
|
337
|
+
principal que le tenant. Sans authentification des fournisseurs, le broker
|
|
338
|
+
retombe sur `"liveness"` et le dit, car une reprise inconditionnelle
|
|
339
|
+
permettrait à quiconque atteint l'URL d'évincer le vrai fournisseur.
|
|
340
|
+
|
|
341
|
+
Aussi `MCP_BROKER_PROVIDER_TAKEOVER`.
|
|
342
|
+
|
|
343
|
+
## TLS
|
|
176
344
|
|
|
177
345
|
```json
|
|
178
346
|
"tls": {
|
|
@@ -202,7 +370,7 @@ Cette clé est secrète. Elle ne doit jamais être ajoutée au dépôt Git.
|
|
|
202
370
|
Le broker doit pouvoir lire les deux fichiers. Une paire certificat et clé
|
|
203
371
|
incorrecte empêche le démarrage en HTTPS.
|
|
204
372
|
|
|
205
|
-
##
|
|
373
|
+
## Fichiers web statiques
|
|
206
374
|
|
|
207
375
|
```json
|
|
208
376
|
"www": {
|
|
@@ -215,10 +383,25 @@ incorrecte empêche le démarrage en HTTPS.
|
|
|
215
383
|
|
|
216
384
|
### `www.open`
|
|
217
385
|
|
|
218
|
-
Indique si le broker doit ouvrir automatiquement le navigateur.
|
|
386
|
+
Indique si le broker doit ouvrir automatiquement le navigateur au démarrage.
|
|
387
|
+
|
|
388
|
+
- `false` (ou absent) convient aux serveurs, conteneurs et environnements
|
|
389
|
+
headless.
|
|
390
|
+
- `true` ouvre la racine du broker, `https://localhost:3001/`.
|
|
391
|
+
- Une chaîne ouvre une page précise : `"/app/index.html"`, ou une URL absolue
|
|
392
|
+
sur l'origine de ce broker.
|
|
219
393
|
|
|
220
|
-
|
|
221
|
-
|
|
394
|
+
Une URL sur une autre origine est refusée avec un message sur la sortie
|
|
395
|
+
d'erreur, comme toute autre chaîne : une valeur transmise telle quelle à la
|
|
396
|
+
commande « ouvrir ceci » du système peut lancer un fichier local ou une
|
|
397
|
+
application enregistrée, et rien dans le démarrage d'un broker n'exige de visiter
|
|
398
|
+
un autre hôte. Ouvrez-la vous-même.
|
|
399
|
+
|
|
400
|
+
Le navigateur ne s'ouvre que si une entrée `www.mounts` couvre réellement le
|
|
401
|
+
chemin résolu. Sinon, le broker indique quels préfixes sont montés au lieu de
|
|
402
|
+
lancer un navigateur sur un `404`.
|
|
403
|
+
|
|
404
|
+
`MCP_BROKER_OPEN` accepte les mêmes valeurs (`"1"` pour la racine).
|
|
222
405
|
|
|
223
406
|
### `www.mounts`
|
|
224
407
|
|
|
@@ -240,11 +423,11 @@ Ce bloc ne protège pas automatiquement une interface web. Les routes MCP sont
|
|
|
240
423
|
protégées par `auth`, mais une application web statique doit aussi être conçue
|
|
241
424
|
pour ne pas exposer de secret.
|
|
242
425
|
|
|
243
|
-
##
|
|
426
|
+
## Activation OAuth
|
|
244
427
|
|
|
245
428
|
```json
|
|
246
429
|
"auth": {
|
|
247
|
-
"enabled":
|
|
430
|
+
"enabled": false,
|
|
248
431
|
"publicBaseUrl": "https://mcp.factory.local",
|
|
249
432
|
"authorizationServers": [
|
|
250
433
|
"https://identity.factory.local"
|
|
@@ -259,13 +442,23 @@ pour ne pas exposer de secret.
|
|
|
259
442
|
|
|
260
443
|
### `auth.enabled`
|
|
261
444
|
|
|
262
|
-
Active l'authentification OAuth des clients.
|
|
445
|
+
Active l'authentification OAuth des clients. **Ce modèle la livre désactivée.**
|
|
263
446
|
|
|
264
|
-
- `
|
|
265
|
-
|
|
447
|
+
- `false` (la valeur livrée) conserve le mode historique sans authentification :
|
|
448
|
+
tous les clients atteignent tous les slots. À utiliser sur un réseau de
|
|
449
|
+
confiance, et le temps de faire fonctionner le reste.
|
|
450
|
+
- `true` exige un bearer token valide sur chaque requête cliente. Le reste du
|
|
451
|
+
bloc doit alors décrire un vrai serveur d'autorisation : avec `enabled: true`
|
|
452
|
+
et les valeurs d'exemple `identity.factory.local` encore en place, le broker
|
|
453
|
+
répond `401` à tous les clients avec un défi pointant vers un hôte qui
|
|
454
|
+
n'existe pas, ce qui est une façon déroutante d'occuper un après-midi.
|
|
266
455
|
- Une politique détaillée n'est utile que si les clients possèdent une
|
|
267
456
|
identité authentifiée.
|
|
268
457
|
|
|
458
|
+
Tout ce qui suit (`roles`, `assignments`, `denies`, `slotResources`,
|
|
459
|
+
`toolCapabilities`) est inerte tant que `enabled` vaut `false`. C'est conservé
|
|
460
|
+
dans le modèle comme exemple travaillé, pas parce que cela agit.
|
|
461
|
+
|
|
269
462
|
### `auth.publicBaseUrl`
|
|
270
463
|
|
|
271
464
|
Adresse publique utilisée par les clients pour joindre le broker.
|
|
@@ -352,7 +545,7 @@ Pour accorder cet accès, ajoutez par exemple :
|
|
|
352
545
|
Le JWT devra alors posséder à la fois le scope `broker:admin` et le groupe
|
|
353
546
|
`broker-administrators`.
|
|
354
547
|
|
|
355
|
-
##
|
|
548
|
+
## Claims JWT transformés en sujets
|
|
356
549
|
|
|
357
550
|
```json
|
|
358
551
|
"subjectMapping": {
|
|
@@ -419,7 +612,7 @@ client:local-ai-assistant
|
|
|
419
612
|
Un même appel peut donc posséder plusieurs identités en même temps, par exemple
|
|
420
613
|
un utilisateur, deux groupes et une application cliente.
|
|
421
614
|
|
|
422
|
-
##
|
|
615
|
+
## Rôles et capacités
|
|
423
616
|
|
|
424
617
|
Un rôle répond uniquement à la question « que peut-on faire ? ». Il ne contient
|
|
425
618
|
jamais de chemin de ressource.
|
|
@@ -493,7 +686,7 @@ Déclarer un rôle ne l'accorde à personne. Dans le fichier d'exemple, aucune
|
|
|
493
686
|
affectation n'utilise `administrator`. Personne n'est donc administrateur par
|
|
494
687
|
ce seul bloc.
|
|
495
688
|
|
|
496
|
-
##
|
|
689
|
+
## Affectations
|
|
497
690
|
|
|
498
691
|
Une affectation répond à la phrase :
|
|
499
692
|
|
|
@@ -561,7 +754,7 @@ site Paris, sans pouvoir appeler les outils.
|
|
|
561
754
|
|
|
562
755
|
Les expressions régulières ne sont pas acceptées.
|
|
563
756
|
|
|
564
|
-
##
|
|
757
|
+
## Interdiction explicite
|
|
565
758
|
|
|
566
759
|
```json
|
|
567
760
|
"denies": [
|
|
@@ -591,7 +784,7 @@ quelle que soit la position des règles dans le fichier.
|
|
|
591
784
|
Utilisez `"capabilities": ["*"]` pour interdire toute capacité sur une
|
|
592
785
|
ressource précise.
|
|
593
786
|
|
|
594
|
-
##
|
|
787
|
+
## Noms techniques et ressources stables
|
|
595
788
|
|
|
596
789
|
```json
|
|
597
790
|
"slotResources": {
|
|
@@ -629,7 +822,7 @@ Un slot non déclaré est normalement converti en `/<nom-du-slot>`. Pour un
|
|
|
629
822
|
environnement industriel, il est préférable de déclarer explicitement les
|
|
630
823
|
mappings afin de conserver des identités stables.
|
|
631
824
|
|
|
632
|
-
##
|
|
825
|
+
## Classification globale des outils
|
|
633
826
|
|
|
634
827
|
```json
|
|
635
828
|
"toolCapabilities": {
|
|
@@ -656,7 +849,7 @@ générique `mcp.tools.call`.
|
|
|
656
849
|
Cette valeur par défaut explique pourquoi le rôle `maintenance` contient aussi
|
|
657
850
|
`mcp.tools.call`.
|
|
658
851
|
|
|
659
|
-
##
|
|
852
|
+
## Classification spécifique à une zone
|
|
660
853
|
|
|
661
854
|
```json
|
|
662
855
|
"providerToolCapabilities": {
|
|
@@ -680,7 +873,7 @@ valeur globale. Cette redondance est volontairement pédagogique. Dans un vrai
|
|
|
680
873
|
déploiement, ce bloc est surtout utile si le même nom d'outil n'a pas le même
|
|
681
874
|
niveau de risque selon le fournisseur ou la zone.
|
|
682
875
|
|
|
683
|
-
##
|
|
876
|
+
## Audit
|
|
684
877
|
|
|
685
878
|
```json
|
|
686
879
|
"audit": {
|
|
@@ -698,16 +891,31 @@ Passez temporairement à `true` pour comprendre une politique ou diagnostiquer
|
|
|
698
891
|
un problème. Les journaux contiennent la décision et les identifiants de
|
|
699
892
|
politique, jamais le bearer token ni le secret fournisseur.
|
|
700
893
|
|
|
701
|
-
##
|
|
894
|
+
## Secret partagé des fournisseurs
|
|
702
895
|
|
|
703
896
|
```json
|
|
704
897
|
"providerSecret": "change-me"
|
|
705
898
|
```
|
|
706
899
|
|
|
707
|
-
|
|
708
|
-
|
|
900
|
+
**Volontairement absent du modèle livré.** Ajoutez la clé dans `auth` pour
|
|
901
|
+
activer l'authentification des fournisseurs.
|
|
709
902
|
|
|
710
|
-
|
|
903
|
+
Ce secret authentifie les serveurs MCP qui se connectent à `/provider/<slot>`
|
|
904
|
+
ou `/providers`. Chaque fournisseur doit alors le présenter dans
|
|
905
|
+
`X-Provider-Token` ou `Authorization: Bearer`, faute de quoi il est refusé dès la
|
|
906
|
+
poignée de main WebSocket.
|
|
907
|
+
|
|
908
|
+
Il est indépendant des bearer tokens des clients, et surtout il n'est **pas**
|
|
909
|
+
gouverné par `auth.enabled` : dès qu'il est défini, l'authentification des
|
|
910
|
+
fournisseurs est active, même avec OAuth désactivé. C'est pourquoi le modèle ne
|
|
911
|
+
le livre pas. Laissé en place avec sa valeur d'exemple, il refuserait tous les
|
|
912
|
+
fournisseurs sur un broker que le lecteur croit grand ouvert.
|
|
913
|
+
|
|
914
|
+
Une conséquence à anticiper : le constructeur `WebSocket` du navigateur ne peut
|
|
915
|
+
pas poser d'en-têtes de requête, donc un fournisseur hébergé dans une page web ne
|
|
916
|
+
peut pas présenter ce secret du tout. Avec `providerSecret` défini, les
|
|
917
|
+
fournisseurs navigateur sont exclus ; il leur faut l'authentification
|
|
918
|
+
fournisseur désactivée, ou un reverse proxy authentifiant en amont.
|
|
711
919
|
|
|
712
920
|
La valeur `change-me` est uniquement un placeholder. En production :
|
|
713
921
|
|
|
@@ -722,14 +930,15 @@ chemins de ressources. Pour limiter chaque appareil à son propre sous-arbre,
|
|
|
722
930
|
utilisez un `IProviderAuthenticator` personnalisé qui renvoie un
|
|
723
931
|
`IProviderPrincipal.allowedResources`.
|
|
724
932
|
|
|
725
|
-
##
|
|
933
|
+
## Serveur MCP local lancé par le broker
|
|
726
934
|
|
|
727
935
|
```json
|
|
728
936
|
"stdioUpstreams": [
|
|
729
937
|
{
|
|
730
|
-
"name":
|
|
731
|
-
"command":
|
|
732
|
-
"args":
|
|
938
|
+
"name": "fs",
|
|
939
|
+
"command": "npx",
|
|
940
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/data"],
|
|
941
|
+
"aggregate": true
|
|
733
942
|
}
|
|
734
943
|
]
|
|
735
944
|
```
|
|
@@ -757,10 +966,58 @@ Arguments transmis au programme :
|
|
|
757
966
|
Accorder un accès au filesystem est sensible. Limitez `/data` au strict
|
|
758
967
|
nécessaire.
|
|
759
968
|
|
|
760
|
-
|
|
761
|
-
|
|
969
|
+
### `aggregate`
|
|
970
|
+
|
|
971
|
+
`true` fait entrer ce fournisseur dans le slot réservé `_all`, en plus de son
|
|
972
|
+
propre `/fs/mcp`. Sans cette propriété, cet upstream stdio reste accessible par
|
|
973
|
+
son slot direct uniquement.
|
|
974
|
+
|
|
975
|
+
Notez l'asymétrie : les entrées `stdioUpstreams` ne rejoignent **pas** `_all` par
|
|
976
|
+
défaut, alors que les entrées `mcpServers` et `mcpbBundles` le font. Renseignez
|
|
977
|
+
la clé explicitement dans les deux cas et vous n'aurez jamais à vous en
|
|
978
|
+
souvenir.
|
|
979
|
+
|
|
980
|
+
## Passerelle stdio vers un hôte MCP
|
|
981
|
+
|
|
982
|
+
Absent de `config.json`, mais c'est la raison pour laquelle on règle `aggregate`
|
|
983
|
+
en premier lieu. Un second fichier de ce dossier,
|
|
984
|
+
`config.stdio-bridge.json`, ajoute une clé :
|
|
985
|
+
|
|
986
|
+
```json
|
|
987
|
+
"stdioProvider": "_all"
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
Avec elle, le broker se comporte aussi comme un serveur MCP stdio : il lit du
|
|
991
|
+
JSON-RPC sur son entrée standard et écrit les réponses sur sa sortie standard,
|
|
992
|
+
reliant un hôte comme Claude Desktop à un slot. Pointez la configuration de
|
|
993
|
+
l'hôte sur ce fichier :
|
|
994
|
+
|
|
995
|
+
```json
|
|
996
|
+
{
|
|
997
|
+
"command": "npx",
|
|
998
|
+
"args": ["-y", "@cyanmycelium/mcp-broker"],
|
|
999
|
+
"env": { "MCP_BROKER_CONFIG": "/chemin/absolu/.mcp-broker/config.stdio-bridge.json" }
|
|
1000
|
+
}
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
Deux points à ne pas manquer.
|
|
1004
|
+
|
|
1005
|
+
- **Visez `_all`, pas un vrai slot.** `_all` existe dès le démarrage et répond
|
|
1006
|
+
lui-même à la poignée de main, donc l'hôte se connecte même si aucun
|
|
1007
|
+
fournisseur n'est encore arrivé, et il annonce les nouveaux outils au fur et à
|
|
1008
|
+
mesure. Pointé sur un vrai slot, l'hôte démarre avant le fournisseur, reçoit
|
|
1009
|
+
« non connecté » dès son premier message et abandonne. Pour un fournisseur
|
|
1010
|
+
hébergé dans une page web, c'est garanti : la page ne peut pas être ouverte
|
|
1011
|
+
avant le lancement de l'hôte. `_broker` répond toujours lui aussi, mais
|
|
1012
|
+
n'offrira jamais que les cinq outils d'introspection. Le broker prévient au
|
|
1013
|
+
démarrage quand `stdioProvider` nomme un slot qu'il n'héberge pas.
|
|
1014
|
+
- **Gardez cela dans un fichier séparé.** Avec `stdioProvider`, la sortie
|
|
1015
|
+
standard appartient au flux JSON-RPC et toutes les traces passent sur la sortie
|
|
1016
|
+
d'erreur : un broker lancé ainsi dans un terminal a l'air de ne rien faire.
|
|
1017
|
+
|
|
1018
|
+
`MCP_BROKER_STDIO_PROVIDER` règle la même chose depuis l'environnement.
|
|
762
1019
|
|
|
763
|
-
##
|
|
1020
|
+
## Bundle MCP local signé
|
|
764
1021
|
|
|
765
1022
|
```json
|
|
766
1023
|
"mcpbBundles": [
|
|
@@ -849,17 +1106,23 @@ Si Alice tente `start_motor` sur le four critique :
|
|
|
849
1106
|
|
|
850
1107
|
## Checklist avant un déploiement
|
|
851
1108
|
|
|
1109
|
+
- Passer `auth.enabled` à `true`. Le modèle le livre désactivé pour qu'une copie
|
|
1110
|
+
fraîche démarre ; le laisser ainsi en production signifie que tous les clients
|
|
1111
|
+
atteignent tous les slots.
|
|
852
1112
|
- Remplacer tous les domaines `.local` par les adresses réelles.
|
|
853
1113
|
- Vérifier que `publicBaseUrl` est exactement l'adresse publique du broker.
|
|
854
1114
|
- Vérifier que les JWT utilisent cette ressource dans leur audience.
|
|
855
1115
|
- Vérifier l'URL JWKS et l'émetteur attendu.
|
|
856
|
-
-
|
|
1116
|
+
- Si vous ajoutez `providerSecret`, ne jamais conserver `change-me`.
|
|
857
1117
|
- Ne jamais publier la clé TLS privée.
|
|
858
1118
|
- Ne jamais publier les clés API de `userConfig`.
|
|
859
1119
|
- Utiliser `127.0.0.1` au lieu de `0.0.0.0` si aucun accès réseau n'est requis.
|
|
860
1120
|
- Tester chaque rôle avec un compte représentatif.
|
|
861
1121
|
- Tester les denies sur les actifs critiques.
|
|
862
1122
|
- Vérifier que `_all` ne révèle pas les providers non autorisés.
|
|
1123
|
+
- Lister dans `allowedOrigins` exactement les origines navigateur qui doivent
|
|
1124
|
+
accéder au broker, avec le bon schéma et le bon port, et aucune autre. Une page
|
|
1125
|
+
servie par ce broker compte aussi comme une origine navigateur.
|
|
863
1126
|
- Laisser `audit.logAllowed` à `false` après le diagnostic.
|
|
864
1127
|
- Redémarrer le broker après toute modification, car les politiques sont
|
|
865
1128
|
chargées une seule fois au démarrage.
|
|
@@ -867,5 +1130,11 @@ Si Alice tente `start_motor` sur le four critique :
|
|
|
867
1130
|
## Pour aller plus loin
|
|
868
1131
|
|
|
869
1132
|
- [Référence complète de configuration](../docs/config.md)
|
|
870
|
-
- [Guide OAuth du broker](
|
|
871
|
-
- [Autorisation hiérarchique](
|
|
1133
|
+
- [Guide OAuth du broker](../../../../docs/authorization.md)
|
|
1134
|
+
- [Autorisation hiérarchique](../../../../docs/hierarchical-authorization.md)
|
|
1135
|
+
- [Points d'entrée et transports](../../../../docs/endpoints.md)
|
|
1136
|
+
|
|
1137
|
+
Ou demandez au broker lui-même : le slot réservé `_broker` expose un outil
|
|
1138
|
+
`broker_guide` (guides d'intégration, écrits depuis le code source) et un outil
|
|
1139
|
+
`broker_diagnose` (état en direct et problèmes prouvés, chacun avec sa
|
|
1140
|
+
correction).
|
|
@@ -7,16 +7,79 @@ broker, then adapt to your needs:
|
|
|
7
7
|
cp -r .mcp-broker.example .mcp-broker
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
+
**Read this before you copy.** `config.json` is a *production reference*: it
|
|
11
|
+
shows every key the broker understands, filled in with values from an imagined
|
|
12
|
+
factory deployment. Two consequences.
|
|
13
|
+
|
|
14
|
+
1. **Authorization ships off** (`auth.enabled: false`), so a fresh copy starts
|
|
15
|
+
and answers. The whole `auth` block is still there as the reference for when
|
|
16
|
+
you turn it on: flip `enabled` to `true`, then replace
|
|
17
|
+
`identity.factory.local` with your own authorization server and
|
|
18
|
+
`providerSecret: "change-me"` with a real secret. See
|
|
19
|
+
[`docs/authorization.md`](../../../../docs/authorization.md).
|
|
20
|
+
2. **Other values point at hosts and files that do not exist here**: the TLS
|
|
21
|
+
material under `certs/`, the `www/` directory, the `/data` filesystem
|
|
22
|
+
upstream, the `.mcpb` bundle under `bundles/`. Delete the sections you do not
|
|
23
|
+
use. A missing directory is skipped with a warning; a missing TLS file stops
|
|
24
|
+
startup, with a message naming the two files and the three ways out.
|
|
25
|
+
|
|
26
|
+
So a straight `cp -r` does not run yet. Do one of these first:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
# either: run without TLS, which ignores the tls block entirely
|
|
30
|
+
MCP_BROKER_PROTOCOL=http npx @cyanmycelium/mcp-broker
|
|
31
|
+
|
|
32
|
+
# or: put a self-signed pair where the config says
|
|
33
|
+
mkdir -p .mcp-broker/certs
|
|
34
|
+
openssl req -x509 -newkey rsa:2048 -nodes -days 365 -subj "/CN=localhost" \
|
|
35
|
+
-keyout .mcp-broker/certs/key.pem -out .mcp-broker/certs/cert.pem
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
(Working inside this repository, `npm run gen-cert -w @cyanmycelium/mcp-broker`
|
|
39
|
+
does the same thing without openssl. That script is not usable from an installed
|
|
40
|
+
package: it needs a development dependency the published package does not
|
|
41
|
+
carry.)
|
|
42
|
+
|
|
43
|
+
If you take the second route, remember the `allowedOrigins` entries below become
|
|
44
|
+
`http://`, since the scheme is part of the comparison.
|
|
45
|
+
|
|
10
46
|
Property-by-property educational guides:
|
|
11
47
|
|
|
12
48
|
- [English](CONFIGURATION-EN.md)
|
|
13
49
|
- [Français](CONFIGURATION-FR.md)
|
|
14
50
|
|
|
51
|
+
## Bridging an MCP host over stdio
|
|
52
|
+
|
|
53
|
+
`config.stdio-bridge.json` is the second, minimal file in this folder: it turns
|
|
54
|
+
the broker into a stdio MCP server for a host such as Claude Desktop, bridged to
|
|
55
|
+
the reserved `_all` slot.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"command": "npx",
|
|
60
|
+
"args": ["-y", "@cyanmycelium/mcp-broker"],
|
|
61
|
+
"env": { "MCP_BROKER_CONFIG": "/abs/path/to/.mcp-broker/config.stdio-bridge.json" }
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Pin the bridge to `_all`, not to a real slot. `_all` exists from startup and
|
|
66
|
+
answers the handshake itself, so the host connects even though no provider has
|
|
67
|
+
arrived yet, and it pushes `notifications/tools/list_changed` as providers join,
|
|
68
|
+
so a browser page opened later shows up live. Pinned to a real slot, the host
|
|
69
|
+
starts before the provider does and fails the handshake every time. Providers
|
|
70
|
+
join `_all` by opting in: `aggregate: true` on an upstream, or
|
|
71
|
+
`{ aggregate: true }` on `DirectTransport` / `MultiplexTransport.create`.
|
|
72
|
+
|
|
73
|
+
Keep that file separate from `config.json`: with `stdioProvider` set, stdout
|
|
74
|
+
carries JSON-RPC and every log line moves to stderr, so a broker started that
|
|
75
|
+
way in a terminal looks like it is doing nothing.
|
|
76
|
+
|
|
15
77
|
## Layout
|
|
16
78
|
|
|
17
79
|
```
|
|
18
80
|
.mcp-broker/
|
|
19
81
|
├── config.json ← broker configuration (port, locale, TLS, mounts, ...)
|
|
82
|
+
├── config.stdio-bridge.json ← minimal config for an MCP host over stdio (optional)
|
|
20
83
|
├── certs/ ← TLS material (optional, gitignore this)
|
|
21
84
|
│ ├── cert.pem
|
|
22
85
|
│ └── key.pem
|
|
@@ -31,8 +94,16 @@ Property-by-property educational guides:
|
|
|
31
94
|
└── index.html
|
|
32
95
|
```
|
|
33
96
|
|
|
34
|
-
A ready-made instance UI lives at
|
|
35
|
-
|
|
97
|
+
A ready-made instance UI lives at
|
|
98
|
+
[`node/packages/broker/web/`](../web/), point a `www` mount at it
|
|
99
|
+
(`"dir": "../web"`) to serve it. See [its README](../web/README.md).
|
|
100
|
+
|
|
101
|
+
The page is served by the broker but is still a *browser* origin, so it has to
|
|
102
|
+
be listed in `allowedOrigins` before it may call `/<slot>/mcp`, `/<slot>/sse` or
|
|
103
|
+
`/<slot>/messages`. Being served by the same broker exempts nothing. With
|
|
104
|
+
`port: 3001` and no TLS, that is `"http://localhost:3001"`; the template ships
|
|
105
|
+
`https://` entries because it also sets `tls.cert`/`tls.key`, which makes the
|
|
106
|
+
broker speak HTTPS.
|
|
36
107
|
|
|
37
108
|
## Path resolution
|
|
38
109
|
|