@imenam/mcp-http-gateway 2.0.8 → 2.0.9
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/package.json +3 -2
- package/startup-exposition.js +89 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imenam/mcp-http-gateway",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "HTTP gateway that exposes MCP servers as REST routes — bridge between MCP and any HTTP-capable agent",
|
|
6
6
|
"main": "server.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"mcp-endpoint.js",
|
|
14
14
|
"auth.js",
|
|
15
15
|
"README.md",
|
|
16
|
-
"embeddings/"
|
|
16
|
+
"embeddings/",
|
|
17
|
+
"startup-exposition.js"
|
|
17
18
|
],
|
|
18
19
|
"scripts": {
|
|
19
20
|
"start": "node server.js",
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Pré-exposition des MCP au démarrage de l'agent (EXPOSE_AT_AGENT_STARTUP).
|
|
2
|
+
//
|
|
3
|
+
// Par défaut, un agent connecté au gateway ne reçoit que les trois outils de découverte
|
|
4
|
+
// (list_mcps / list_tools / call_tool) et doit interroger le gateway pour découvrir les
|
|
5
|
+
// MCP et leurs outils. Pour les MCP fréquemment utilisés, cette découverte coûte cher :
|
|
6
|
+
// `list_tools` reverse l'intégralité des schémas dans la conversation, où ils restent
|
|
7
|
+
// jusqu'à la fin de la session.
|
|
8
|
+
//
|
|
9
|
+
// Un MCP pré-exposé voit à la place ses outils publiés comme de VRAIS outils MCP dans la
|
|
10
|
+
// réponse `tools/list`, nommés `<serveur>__<outil>`, avec description et inputSchema
|
|
11
|
+
// complets. L'agent les appelle directement, sans passer par call_tool.
|
|
12
|
+
//
|
|
13
|
+
// L'option se déclare PAR MCP, dans le bloc `env` de config/mcp-servers.json — au même
|
|
14
|
+
// endroit que MCP_SERVER_ROUTE, qui est également une directive lue par le gateway :
|
|
15
|
+
//
|
|
16
|
+
// "documentor": {
|
|
17
|
+
// "command": "node",
|
|
18
|
+
// "args": ["…"],
|
|
19
|
+
// "description": "…",
|
|
20
|
+
// "env": {
|
|
21
|
+
// "MCP_SERVER_ROUTE": "/documentor",
|
|
22
|
+
// "EXPOSE_AT_AGENT_STARTUP": "true"
|
|
23
|
+
// }
|
|
24
|
+
// }
|
|
25
|
+
//
|
|
26
|
+
// Sans cette clé (ou à "false"), le MCP n'est pas pré-exposé et reste accessible via
|
|
27
|
+
// list_mcps / list_tools / call_tool.
|
|
28
|
+
//
|
|
29
|
+
// Dans tous les cas le MCP enfant reste hébergé côté gateway : seules ses définitions
|
|
30
|
+
// sont relayées, les appels sont proxifiés.
|
|
31
|
+
|
|
32
|
+
const TRUTHY = new Set(['true', '1', 'yes', 'on']);
|
|
33
|
+
const FALSY = new Set(['false', '0', 'no', 'off', '']);
|
|
34
|
+
|
|
35
|
+
// Séparateur entre le nom du MCP et celui de l'outil dans les outils proxy.
|
|
36
|
+
const PROXY_SEPARATOR = '__';
|
|
37
|
+
|
|
38
|
+
// Le client ajoute son propre préfixe (ex. `mcp__<serveur>__`) et de nombreux clients
|
|
39
|
+
// plafonnent les noms d'outils à 64 caractères : on garde notre part courte.
|
|
40
|
+
const MAX_PROXY_NAME_LENGTH = 40;
|
|
41
|
+
|
|
42
|
+
// Interprète la valeur déclarée. Retourne { value, valid } pour permettre à l'appelant
|
|
43
|
+
// d'avertir sur une valeur non reconnue plutôt que de l'ignorer en silence.
|
|
44
|
+
export function parseExposeFlag(raw) {
|
|
45
|
+
if (raw === undefined || raw === null) return { value: false, valid: true };
|
|
46
|
+
if (typeof raw === 'boolean') return { value: raw, valid: true };
|
|
47
|
+
|
|
48
|
+
const normalized = String(raw).trim().toLowerCase();
|
|
49
|
+
if (TRUTHY.has(normalized)) return { value: true, valid: true };
|
|
50
|
+
if (FALSY.has(normalized)) return { value: false, valid: true };
|
|
51
|
+
return { value: false, valid: false };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Les noms d'outils MCP doivent rester dans [a-zA-Z0-9_-].
|
|
55
|
+
function sanitizeSegment(value) {
|
|
56
|
+
return String(value).replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Construit les outils proxy à partir du registry. Chaque entrée porte `route` +
|
|
60
|
+
// `toolName` pour le routage — à retirer avant publication via toolDefinition().
|
|
61
|
+
export function buildProxyTools(registry) {
|
|
62
|
+
const proxyTools = [];
|
|
63
|
+
|
|
64
|
+
for (const entry of registry ?? []) {
|
|
65
|
+
if (!entry.exposeAtStartup) continue;
|
|
66
|
+
|
|
67
|
+
for (const tool of entry.tools ?? []) {
|
|
68
|
+
proxyTools.push({
|
|
69
|
+
name: `${sanitizeSegment(entry.name)}${PROXY_SEPARATOR}${sanitizeSegment(tool.name)}`,
|
|
70
|
+
description: tool.description ?? '',
|
|
71
|
+
inputSchema: tool.inputSchema ?? { type: 'object', properties: {} },
|
|
72
|
+
route: entry.route,
|
|
73
|
+
toolName: tool.name,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return proxyTools;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Vue « définition MCP » d'un outil proxy : sans les champs de routage internes.
|
|
82
|
+
export function toolDefinition({ name, description, inputSchema }) {
|
|
83
|
+
return { name, description, inputSchema };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Noms proxy trop longs pour survivre au préfixe ajouté par le client.
|
|
87
|
+
export function overlongProxyNames(proxyTools) {
|
|
88
|
+
return (proxyTools ?? []).filter(t => t.name.length > MAX_PROXY_NAME_LENGTH).map(t => t.name);
|
|
89
|
+
}
|