MAj MCP Serveur

This commit is contained in:
2026-07-20 17:30:08 +02:00
parent 6e58731a20
commit 9eb19efd92
4 changed files with 268 additions and 19 deletions
+42 -1
View File
@@ -41,6 +41,7 @@ Connectez ensuite Claude Desktop : Réglages → Développeur → Serveurs MCP
locaux → **Modifier la config**, puis ajoutez dans `mcpServers` :
```json
// macOS / Linux
{
"mcpServers": {
"crowdlending-dev": {
@@ -60,6 +61,28 @@ locaux → **Modifier la config**, puis ajoutez dans `mcpServers` :
}
```
```json
// Windows
{
"mcpServers": {
"crowdlending-dev": {
"command": "cmd",
"args": [
"/c", "npx",
"-y",
"mcp-remote",
"http://localhost:4100/mcp",
"--header",
"X-API-Key:${CROWDLENDING_API_KEY}"
],
"env": {
"CROWDLENDING_API_KEY": "clk_live_..."
}
}
}
}
```
**Important :** `claude_desktop_config.json` n'accepte que des entrées
`command`/`args` — il n'existe pas de champ `url`/`headers` natif dans ce
fichier (contrairement à d'autres clients MCP). Pour un serveur HTTP comme
@@ -70,7 +93,16 @@ qui se charge de parler HTTP à notre serveur en coulisses, en-tête
`X-API-Key` inclus. Aucune installation manuelle requise, `npx` le télécharge
à la volée.
Notez l'absence d'espace autour du `:` dans `--header` : Claude Desktop
**Sous Windows, le wrapper `cmd /c` est obligatoire** (voir le second bloc
ci-dessus) : `npx` y est en réalité `npx.cmd` (un script), et la façon dont
Claude Desktop lance les process (`spawn` sans interpréteur de commandes) ne
sait pas l'exécuter directement — sans ce wrapper, le serveur reste affiché
comme « running » dans Claude Desktop mais ne répond jamais (blocage
silencieux, pas d'erreur explicite). C'est un problème Node.js/Windows
connu, pas spécifique à ce serveur — la config générée automatiquement dans
Mon compte → Serveur MCP l'applique déjà si elle détecte Windows.
Notez aussi l'absence d'espace autour du `:` dans `--header` : Claude Desktop
(Windows) a un bug connu qui tronque les arguments contenant un espace — on
passe donc la valeur réelle (avec l'espace éventuel) via une variable
d'environnement dans `env` plutôt que directement dans `args`.
@@ -123,6 +155,9 @@ possible, même si les noms d'outils sont identiques des deux côtés.
}
```
Sous Windows, remplacez `"command": "npx"` par `"command": "cmd", "args": ["/c", "npx", ...]`
sur chacune des deux entrées (voir Développement local plus haut).
Utilisez deux clés API différentes (une par instance) : ça permet de
révoquer l'une sans affecter l'autre. Réglez `MCP_LABEL=dev` (côté serveur
local, dans votre `.env` ou variable d'environnement au lancement) pour que
@@ -216,6 +251,12 @@ Garde-fous :
votre config utilise un bloc `url`/`headers` directement, non supporté par
`claude_desktop_config.json` (voir Développement local plus haut) : il faut
passer par `command: "npx"` + `mcp-remote`.
- **Le serveur reste sur « running » indéfiniment, « Preparing session… » puis
« Could not attach »** — sous Windows, il manque le wrapper `cmd /c` (voir
Développement local). C'est un problème de fond très courant : `npx` y est
un script `.cmd` que `spawn()` ne sait pas exécuter directement, donc le
process est lancé mais ne communique jamais réellement. Symptôme
caractéristique : aucune erreur explicite, juste un blocage silencieux.
- **`Clé API invalide ou révoquée`** — régénérez une clé dans Mon compte →
Clés API et mettez à jour la config.
- **`Impossible de joindre l'API`** — le backend n'est pas démarré, ou
+24 -7
View File
@@ -17,10 +17,11 @@
* des outils de CETTE session uniquement (voir createSession). Deux sessions
* ne partagent jamais d'état ni de données.
*
* Connexion depuis Claude Desktop (dev comme prod) :
* { "url": "http://localhost:4100/mcp", "headers": { "X-API-Key": "..." } }
* (remplacer l'URL par https://mcp.crowdlending.croguennec.net/mcp pour la
* prod). Voir README.md pour le détail.
* Connexion depuis Claude Desktop (dev comme prod) : ce fichier expose du
* HTTP pur, mais claude_desktop_config.json n'accepte que des entrées
* command/args — la connexion passe donc par le pont mcp-remote
* (https://github.com/geelen/mcp-remote), voir README.md pour la config
* exacte (et le wrapper cmd /c obligatoire sous Windows).
*/
import { randomUUID } from 'node:crypto';
@@ -63,6 +64,17 @@ const SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1000; // 30 min
console.error(`[crowdlending-mcp${LABEL ? `-${LABEL}` : ''}] démarrage — API cible : ${API_BASE}, hôtes autorisés : ${ALLOWED_HOSTS.join(', ')}, fetch_url : ${ENABLE_FETCH_URL ? 'activé' : 'désactivé'}`);
/** Préfixe commun à toutes les lignes de log applicatif. */
const LOG_PREFIX = `[crowdlending-mcp${LABEL ? `-${LABEL}` : ''}]`;
/** Résumé lisible d'une requête JSON-RPC entrante (nom d'outil pour un
* tools/call, méthode sinon), pour le log applicatif — jamais la clé API. */
function describeRequest(body) {
if (!body || typeof body !== 'object') return 'requête inconnue';
if (body.method === 'tools/call') return `tools/call ${body.params?.name || '?'}`;
return body.method || 'requête inconnue';
}
/** Ajoute le libellé d'environnement au titre d'un outil (ex. "[PROD]"). */
const withLabel = (title) => LABEL ? `${title} [${LABEL.toUpperCase()}]` : title;
@@ -118,7 +130,7 @@ setInterval(() => {
const now = Date.now();
for (const [sessionId, s] of sessions.entries()) {
if (now - s.lastActivity > SESSION_IDLE_TIMEOUT_MS) {
console.error(`[crowdlending-mcp${LABEL ? `-${LABEL}` : ''}] session ${sessionId} inactive depuis plus de 30 min, fermeture.`);
console.error(`${LOG_PREFIX} session ${sessionId.slice(0, 8)} inactive depuis plus de 30 min, fermeture.`);
s.transport.close();
sessions.delete(sessionId);
}
@@ -179,7 +191,10 @@ app.post('/mcp', async (req, res) => {
return;
}
touchSession(sessionId);
const label = describeRequest(req.body);
const startedAt = Date.now();
await session.transport.handleRequest(req, res, req.body);
console.error(`${LOG_PREFIX} session ${sessionId.slice(0, 8)}${label}${res.statusCode} (${Date.now() - startedAt}ms)`);
return;
}
@@ -195,9 +210,11 @@ app.post('/mcp', async (req, res) => {
}
const transport = await createSession(apiKey);
const startedAt = Date.now();
await transport.handleRequest(req, res, req.body);
console.error(`${LOG_PREFIX} nouvelle session ${transport.sessionId ? transport.sessionId.slice(0, 8) : '?'} — initialize — ${res.statusCode} (${Date.now() - startedAt}ms)`);
} catch (e) {
console.error(`[crowdlending-mcp${LABEL ? `-${LABEL}` : ''}] erreur /mcp POST :`, e);
console.error(`${LOG_PREFIX} erreur /mcp POST :`, e);
if (!res.headersSent) res.status(500).json({ jsonrpc: '2.0', error: { code: -32603, message: e.message }, id: null });
}
});
@@ -220,5 +237,5 @@ app.get('/mcp', handleExistingSession);
app.delete('/mcp', handleExistingSession);
app.listen(PORT, '0.0.0.0', () => {
console.error(`[crowdlending-mcp${LABEL ? `-${LABEL}` : ''}] à l'écoute sur le port ${PORT}`);
console.error(`${LOG_PREFIX} à l'écoute sur le port ${PORT}`);
});