This commit is contained in:
2026-07-15 23:08:36 +02:00
parent 56dd1f89bd
commit 6e58731a20
3 changed files with 101 additions and 56 deletions
+1
View File
@@ -67,6 +67,7 @@ services:
PORT: 4100 PORT: 4100
CROWDLENDING_API_URL: http://crowdlending-backend:4000/api/v1 CROWDLENDING_API_URL: http://crowdlending-backend:4000/api/v1
MCP_ALLOWED_HOSTS: mcp.crowdlending.croguennec.net,localhost MCP_ALLOWED_HOSTS: mcp.crowdlending.croguennec.net,localhost
MCP_LABEL: prod
# Explicitement désactivé : ce serveur sert des utilisateurs distants # Explicitement désactivé : ce serveur sert des utilisateurs distants
# non maîtrisés, l'outil de lecture d'URL arbitraire (SSRF) reste # non maîtrisés, l'outil de lecture d'URL arbitraire (SSRF) reste
# réservé au développement local. Ne pas passer à true ici. # réservé au développement local. Ne pas passer à true ici.
+60 -44
View File
@@ -1091,27 +1091,30 @@ function ApiKeysSection() {
} }
/* ── Serveur MCP ────────────────────────────────────────────── /* ── Serveur MCP ──────────────────────────────────────────────
Guide pas-à-pas pour connecter Claude Desktop au serveur MCP local Guide pas-à-pas pour connecter Claude Desktop au serveur MCP
(mcp-server/ à la racine du projet). Le JSON de config est généré (mcp-server/ à la racine du projet, un seul modèle url+headers en HTTP,
côté client à partir des champs ci-dessous ; la clé API saisie ici que ce soit en développement local ou déployé à distance en prod — voir
reste uniquement en mémoire du navigateur, elle n'est jamais envoyée mcp-server/README.md). Le JSON de config est généré côté client à partir
au backend — seulement utilisée pour composer l'aperçu à copier. ── */ des champs ci-dessous ; la clé API saisie ici reste uniquement en mémoire
du navigateur, elle n'est jamais envoyée au backend — seulement utilisée
pour composer l'aperçu à copier. ── */
/** Devine une URL d'API raisonnable selon l'environnement courant : /** Devine une URL de serveur MCP raisonnable selon l'environnement courant :
* en dev (Vite sur :5173), le process Node du serveur MCP ne passe pas * en dev (Vite sur :5173 ou localhost), le serveur MCP tourne en local sur
* par le proxy Vite, il faut donc viser directement le port du backend (4000). * son port par défaut (npm run dev, :4100). En prod, il s'agit du
* En prod (nginx sert front + /api sur la même origine), l'origine courante convient. */ * sous-domaine dédié mcp.<domaine de l'app>, déjà déployé en continu. */
function guessMcpApiUrl() { function guessMcpUrl() {
if (typeof window === 'undefined') return 'http://localhost:4000/api/v1'; if (typeof window === 'undefined') return 'http://localhost:4100/mcp';
const { hostname, port, origin } = window.location; const { hostname, port } = window.location;
if (port === '5173') return `http://${hostname}:4000/api/v1`; if (port === '5173' || hostname === 'localhost' || hostname === '127.0.0.1') {
return `${origin}/api/v1`; return 'http://localhost:4100/mcp';
}
return `https://mcp.${hostname}/mcp`;
} }
/** Détecte automatiquement l'environnement ('dev' ou 'prod') à partir de /** Détecte automatiquement l'environnement ('dev' ou 'prod') à partir de
* l'URL de l'API : localhost/IP locale ou nom d'hôte contenant "dev" → * l'URL du serveur MCP : localhost/IP locale → dev, tout le reste → prod.
* dev, tout le reste → prod. Best-effort — reste modifiable manuellement * Best-effort — reste modifiable manuellement pour les cas particuliers. */
* pour les cas particuliers (domaine de test qui ne contient pas "dev"...). */
function detectLabelFromUrl(url) { function detectLabelFromUrl(url) {
if (!url) return 'prod'; if (!url) return 'prod';
let hostname; let hostname;
@@ -1146,27 +1149,28 @@ function CopyBlock({ text }) {
} }
function McpServerSection({ goToApiKeys }) { function McpServerSection({ goToApiKeys }) {
const [mcpPath, setMcpPath] = useState('C:\\dev\\crowdlending-app\\mcp-server\\index.js'); const [mcpUrl, setMcpUrl] = useState(guessMcpUrl());
const [apiUrl, setApiUrl] = useState(guessMcpApiUrl());
const [apiKey, setApiKey] = useState(''); const [apiKey, setApiKey] = useState('');
const [manualLabel, setManualLabel] = useState(null); // null = auto-détecté depuis apiUrl, sinon override manuel const [manualLabel, setManualLabel] = useState(null); // null = auto-détecté depuis mcpUrl, sinon override manuel
const detectedLabel = detectLabelFromUrl(apiUrl); const detectedLabel = detectLabelFromUrl(mcpUrl);
const label = manualLabel ?? detectedLabel; const label = manualLabel ?? detectedLabel;
const isLocal = label === 'dev';
const serverKey = `crowdlending-${label}`; const serverKey = `crowdlending-${label}`;
const env = { // claude_desktop_config.json n'a pas de champ url/headers natif : on passe
CROWDLENDING_API_KEY: apiKey || '<VOTRE_CLE_API>', // par mcp-remote (https://github.com/geelen/mcp-remote), un pont stdio↔HTTP
CROWDLENDING_API_URL: apiUrl, // que Claude Desktop lance comme n'importe quel serveur "command". La clé
CROWDLENDING_LABEL: label, // API passe en variable d'environnement plutôt que directement dans args
}; // (bug connu de Claude Desktop Windows qui tronque les valeurs à espaces).
const envVarName = `${label.toUpperCase()}_API_KEY`;
const configJson = JSON.stringify({ const configJson = JSON.stringify({
mcpServers: { mcpServers: {
[serverKey]: { [serverKey]: {
command: 'node', command: 'npx',
args: [mcpPath], args: ['-y', 'mcp-remote', mcpUrl, '--header', `X-API-Key:\${${envVarName}}`],
env, env: { [envVarName]: apiKey || '<VOTRE_CLE_API>' },
}, },
}, },
}, null, 2); }, null, 2);
@@ -1176,8 +1180,8 @@ function McpServerSection({ goToApiKeys }) {
<h3 style={{ margin: '0 0 4px' }}>Serveur MCP</h3> <h3 style={{ margin: '0 0 4px' }}>Serveur MCP</h3>
<p className="text-muted" style={{ margin: '0 0 20px', fontSize: 'var(--fs-sm)' }}> <p className="text-muted" style={{ margin: '0 0 20px', fontSize: 'var(--fs-sm)' }}>
Permet à Claude Desktop (ou tout client MCP) de consulter votre portefeuille en lecture seule. Permet à Claude Desktop (ou tout client MCP) de consulter votre portefeuille en lecture seule.
Le serveur (dossier <code>mcp-server/</code> du projet) doit être installé sur cette machine Un seul modèle de connexion, en développement local comme en production : une URL de serveur MCP
(<code>npm install</code>) voir <code>mcp-server/README.md</code> pour le détail. et votre clé API personnelle voir <code>mcp-server/README.md</code> pour le détail.
</p> </p>
<h4 style={{ margin: '0 0 6px', fontSize: 'var(--fs-sm)' }}>1. Créer une clé API dédiée</h4> <h4 style={{ margin: '0 0 6px', fontSize: 'var(--fs-sm)' }}>1. Créer une clé API dédiée</h4>
@@ -1194,12 +1198,8 @@ function McpServerSection({ goToApiKeys }) {
<h4 style={{ margin: '0 0 10px', fontSize: 'var(--fs-sm)' }}>2. Renseigner les paramètres</h4> <h4 style={{ margin: '0 0 10px', fontSize: 'var(--fs-sm)' }}>2. Renseigner les paramètres</h4>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginBottom: 20, maxWidth: 520 }}> <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginBottom: 20, maxWidth: 520 }}>
<div> <div>
<label>Chemin vers mcp-server/index.js</label> <label>URL du serveur MCP</label>
<input value={mcpPath} onChange={e => setMcpPath(e.target.value)} /> <input value={mcpUrl} onChange={e => setMcpUrl(e.target.value)} />
</div>
<div>
<label>URL de l'API</label>
<input value={apiUrl} onChange={e => setApiUrl(e.target.value)} />
</div> </div>
<div> <div>
<label>Clé API</label> <label>Clé API</label>
@@ -1218,7 +1218,7 @@ function McpServerSection({ goToApiKeys }) {
</span> </span>
<span style={{ fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}> <span style={{ fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
{manualLabel === null {manualLabel === null
? <>déduit de l'URL de l'API ci-dessus (localhost / « dev » → dev, sinon prod)</> ? <>déduit de l'URL ci-dessus (localhost → dev, sinon prod)</>
: <>forcé manuellement</>} : <>forcé manuellement</>}
</span> </span>
</div> </div>
@@ -1236,12 +1236,26 @@ function McpServerSection({ goToApiKeys }) {
)} )}
</div> </div>
</div> </div>
{isLocal ? (
<p style={{ margin: '-8px 0 20px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}> <p style={{ margin: '-8px 0 20px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
En développement, ce serveur doit tourner sur votre machine pour que l'URL ci-dessus réponde :
<code style={{ display: 'block', margin: '6px 0', padding: '6px 8px', borderRadius: 6, background: 'var(--surface-2, #f9fafb)' }}>
cd mcp-server &amp;&amp; npm install &amp;&amp; npm run dev
</code>
comme pour le backend et le frontend. Laissez ce terminal ouvert tant que vous utilisez Claude Desktop.
</p>
) : (
<p style={{ margin: '-8px 0 20px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
En production, le serveur tourne déjà en continu (service <code>crowdlending-mcp</code>) aucune
action nécessaire au-delà de renseigner l'URL et votre clé API.
</p>
)}
<p style={{ margin: '0 0 20px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
Claude Desktop peut se connecter à plusieurs serveurs MCP en même temps : pour avoir dev et Claude Desktop peut se connecter à plusieurs serveurs MCP en même temps : pour avoir dev et
prod accessibles simultanément, répétez ces étapes une deuxième fois avec une URL d'API pointant prod accessibles simultanément, répétez ces étapes une deuxième fois avec l'autre URL (et une clé
vers l'autre environnement (et une clé API distincte) — l'étiquette et la clé de config API distincte) la clé de config (<code>{serverKey}</code> ci-dessous) s'ajuste automatiquement,
(<code>{serverKey}</code> ci-dessous) s'ajustent automatiquement. Elle apparaît dans le titre et Claude Desktop ne confondra jamais les deux portefeuilles.
la description de chaque outil, pour que l'agent ne confonde jamais les deux portefeuilles.
</p> </p>
<h4 style={{ margin: '0 0 6px', fontSize: 'var(--fs-sm)' }}>3. Copier la configuration</h4> <h4 style={{ margin: '0 0 6px', fontSize: 'var(--fs-sm)' }}>3. Copier la configuration</h4>
@@ -1268,8 +1282,10 @@ function McpServerSection({ goToApiKeys }) {
<h4 style={{ margin: '0 0 6px', fontSize: 'var(--fs-sm)' }}>5. Vérifier</h4> <h4 style={{ margin: '0 0 6px', fontSize: 'var(--fs-sm)' }}>5. Vérifier</h4>
<p style={{ margin: 0, fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}> <p style={{ margin: 0, fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
Dans la liste des outils MCP de Claude Desktop, les 7 outils <code>crowdlending_*</code> doivent Dans la liste des outils MCP de Claude Desktop, les outils <code>crowdlending_*</code> doivent
apparaître. Testez avec une question du type « Quel est mon encours de crowdlending actuellement ? ». apparaître (6 en production, 7 en développement local si <code>crowdlending_fetch_url</code> est
activé — voir <code>mcp-server/README.md</code>). Testez avec une question du type « Quel est mon
encours de crowdlending actuellement ? ».
</p> </p>
</div> </div>
); );
+39 -11
View File
@@ -44,15 +44,37 @@ locaux → **Modifier la config**, puis ajoutez dans `mcpServers` :
{ {
"mcpServers": { "mcpServers": {
"crowdlending-dev": { "crowdlending-dev": {
"url": "http://localhost:4100/mcp", "command": "npx",
"headers": { "args": [
"X-API-Key": "clk_live_..." "-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
celui-ci, on passe donc par
[`mcp-remote`](https://github.com/geelen/mcp-remote), un petit pont
stdio↔HTTP officiel : Claude Desktop lance `npx mcp-remote` comme d'habitude,
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
(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`.
Redémarrez Claude Desktop. Les outils `crowdlending_*` doivent apparaître Redémarrez Claude Desktop. Les outils `crowdlending_*` doivent apparaître
(voir la liste plus bas — `crowdlending_fetch_url` en plus si activé, voir (voir la liste plus bas — `crowdlending_fetch_url` en plus si activé, voir
Configuration). Configuration).
@@ -72,8 +94,8 @@ distant réel (en local, les valeurs par défaut conviennent) :
Aucune clé API fixe n'est configurée côté serveur, contrairement à une Aucune clé API fixe n'est configurée côté serveur, contrairement à une
ancienne version qui utilisait le transport stdio : chaque session MCP lit ancienne version qui utilisait le transport stdio : chaque session MCP lit
sa propre clé dans l'en-tête `X-API-Key` de la requête qui l'initialise (le sa propre clé dans l'en-tête `X-API-Key` de la requête qui l'initialise
bloc `headers` de la config Claude Desktop ci-dessus). Un même process peut (transmis via `mcp-remote --header`, voir ci-dessus). Un même process peut
donc servir plusieurs utilisateurs/sessions en parallèle sans jamais mélanger donc servir plusieurs utilisateurs/sessions en parallèle sans jamais mélanger
leurs données — voir le déploiement distant plus bas. leurs données — voir le déploiement distant plus bas.
@@ -88,12 +110,14 @@ possible, même si les noms d'outils sont identiques des deux côtés.
{ {
"mcpServers": { "mcpServers": {
"crowdlending-dev": { "crowdlending-dev": {
"url": "http://localhost:4100/mcp", "command": "npx",
"headers": { "X-API-Key": "clk_live_..." } "args": ["-y", "mcp-remote", "http://localhost:4100/mcp", "--header", "X-API-Key:${DEV_API_KEY}"],
"env": { "DEV_API_KEY": "clk_live_..." }
}, },
"crowdlending-prod": { "crowdlending-prod": {
"url": "https://mcp.crowdlending.croguennec.net/mcp", "command": "npx",
"headers": { "X-API-Key": "clk_live_..." } "args": ["-y", "mcp-remote", "https://mcp.crowdlending.croguennec.net/mcp", "--header", "X-API-Key:${PROD_API_KEY}"],
"env": { "PROD_API_KEY": "clk_live_..." }
} }
} }
} }
@@ -186,8 +210,12 @@ Garde-fous :
## Dépannage ## Dépannage
- **`En-tête X-API-Key manquant`** — vérifiez la section `headers` de votre - **`En-tête X-API-Key manquant`** — vérifiez l'argument `--header` et la
config Claude Desktop. variable d'environnement correspondante dans votre config Claude Desktop.
- **« Certains serveurs MCP n'ont pas pu être chargés » / entrée ignorée** —
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`.
- **`Clé API invalide ou révoquée`** — régénérez une clé dans Mon compte → - **`Clé API invalide ou révoquée`** — régénérez une clé dans Mon compte →
Clés API et mettez à jour la config. Clés API et mettez à jour la config.
- **`Impossible de joindre l'API`** — le backend n'est pas démarré, ou - **`Impossible de joindre l'API`** — le backend n'est pas démarré, ou