MAj MCP Serveur
This commit is contained in:
@@ -179,6 +179,136 @@ export default function Aide() {
|
||||
</p>
|
||||
</FaqItem>
|
||||
|
||||
<FaqItem question="Comment configurer le serveur MCP en production (sans passer par le développement local) ?">
|
||||
<p style={{ marginTop: 0 }}>
|
||||
Le serveur MCP tourne déjà en continu en production (service Docker <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending-mcp</code>,
|
||||
exposé via Traefik) — contrairement au développement local, vous n'avez rien à démarrer ni
|
||||
à laisser tourner sur votre machine. Il suffit de connecter Claude Desktop à l'URL publique.
|
||||
</p>
|
||||
|
||||
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Prérequis</h4>
|
||||
<ul style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 1.8 }}>
|
||||
<li>Le service <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending-mcp</code> du
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}> docker-compose.yml</code> doit être déployé, avec un enregistrement
|
||||
DNS pour <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>mcp.<votre domaine></code> pointant vers la même IP que l'app
|
||||
(certificat TLS automatique via Traefik).</li>
|
||||
<li>Une clé API <strong style={{ color: 'var(--text)' }}>dédiée à la production</strong>, distincte de celle utilisée en développement
|
||||
local si vous en avez une — cela permet de révoquer l'une sans affecter l'autre.</li>
|
||||
</ul>
|
||||
|
||||
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Étapes</h4>
|
||||
<ol style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 1.8 }}>
|
||||
<li>Générez une clé API dédiée : <strong style={{ color: 'var(--text)' }}>Mon compte → Clés API → Nouvelle clé</strong> (par exemple nommée « MCP Prod »).</li>
|
||||
<li>Allez dans <strong style={{ color: 'var(--text)' }}>Mon compte → Serveur MCP</strong> et renseignez l'URL publique
|
||||
(<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>https://mcp.<votre domaine>/mcp</code>) ainsi que la clé générée. L'environnement
|
||||
se détecte automatiquement sur « PROD » dès que l'URL ne contient ni <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>localhost</code> ni
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}> dev</code> — cochez « Forcer manuellement » si votre domaine de test prête à confusion.</li>
|
||||
<li>Copiez la configuration générée dans <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>claude_desktop_config.json</code> (Réglages
|
||||
→ Développeur → Serveurs MCP locaux → Modifier la config), exactement comme en développement — seule l'URL change,
|
||||
le mécanisme <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>mcp-remote</code> (et le wrapper <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>cmd /c</code> sous
|
||||
Windows) reste identique.</li>
|
||||
<li>Redémarrez complètement Claude Desktop.</li>
|
||||
</ol>
|
||||
<p>
|
||||
Vous pouvez connecter dev et prod <strong style={{ color: 'var(--text)' }}>simultanément</strong> : répétez ces étapes une seconde fois
|
||||
avec l'URL locale et une clé distincte, les deux entrées de config (<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending-dev</code> /
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}> crowdlending-prod</code>) coexistent sans collision.
|
||||
</p>
|
||||
|
||||
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Sécurité</h4>
|
||||
<ul style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 1.8 }}>
|
||||
<li>Le point d'entrée public n'a <strong style={{ color: 'var(--text)' }}>volontairement aucune restriction d'IP</strong> (accessible
|
||||
depuis n'importe où, pour un usage nomade) : la clé API est donc la seule barrière d'accès. Traitez-la comme un
|
||||
mot de passe, et révoquez-la immédiatement en cas de doute (Mon compte → Clés API).</li>
|
||||
<li>Limite de 60 requêtes/minute par IP (au-delà, erreur 429) et fermeture automatique des sessions inactives
|
||||
depuis plus de 30 minutes — aucune donnée de session n'est conservée entre deux connexions.</li>
|
||||
<li>L'outil <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_fetch_url</code> (lecture d'une page web arbitraire) reste
|
||||
<strong style={{ color: 'var(--text)' }}> désactivé en production</strong>, même s'il est activé chez vous en développement local.</li>
|
||||
</ul>
|
||||
<p style={{ marginBottom: 0 }}>
|
||||
Si la connexion reste bloquée sans erreur visible, la cause est presque toujours la même qu'en développement local
|
||||
(<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>npx</code> qui échoue silencieusement à joindre le registre npm) — voir la
|
||||
section Dépannage de <strong style={{ color: 'var(--text)' }}>Mon compte → Serveur MCP</strong>.
|
||||
</p>
|
||||
</FaqItem>
|
||||
|
||||
<FaqItem question="Comment utiliser le serveur MCP au quotidien ? (fonctions disponibles et exemples)">
|
||||
<p style={{ marginTop: 0 }}>
|
||||
Une fois connecté, Claude (Desktop ou tout autre client MCP) peut consulter votre portefeuille en langage
|
||||
naturel — il choisit lui-même le bon outil selon votre question. Le serveur est <strong style={{ color: 'var(--text)' }}>strictement
|
||||
en lecture seule</strong> : aucune donnée n'est jamais créée, modifiée ou supprimée depuis une conversation. Toute
|
||||
saisie (nouvel investissement, remboursement…) reste manuelle dans l'application.
|
||||
</p>
|
||||
|
||||
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Fonctions disponibles</h4>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 'var(--fs-sm)', margin: '0 0 14px' }}>
|
||||
<tbody>
|
||||
<tr style={{ borderBottom: '1px solid var(--border)' }}>
|
||||
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_get_investisseur</code>
|
||||
</td>
|
||||
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Profil de l'investisseur lié à la clé API (nom, type famille/entreprise, régime fiscal).</td>
|
||||
</tr>
|
||||
<tr style={{ borderBottom: '1px solid var(--border)' }}>
|
||||
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_get_dashboard</code>
|
||||
</td>
|
||||
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>KPIs du portefeuille : capital investi, capital en risque, montant remboursé, intérêts bruts/nets, dépôts/retraits — filtrable par année.</td>
|
||||
</tr>
|
||||
<tr style={{ borderBottom: '1px solid var(--border)' }}>
|
||||
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_list_investissements</code>
|
||||
</td>
|
||||
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Liste des investissements (projet, émetteur, plateforme, montant, taux, durée, statut), filtrable par statut.</td>
|
||||
</tr>
|
||||
<tr style={{ borderBottom: '1px solid var(--border)' }}>
|
||||
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_get_investissement</code>
|
||||
</td>
|
||||
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Détail complet d'un investissement (par id), y compris la liste de ses remboursements réels perçus.</td>
|
||||
</tr>
|
||||
<tr style={{ borderBottom: '1px solid var(--border)' }}>
|
||||
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_list_remboursements</code>
|
||||
</td>
|
||||
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Historique des remboursements perçus (toutes plateformes), filtrable par période.</td>
|
||||
</tr>
|
||||
<tr style={{ borderBottom: '1px solid var(--border)' }}>
|
||||
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_list_depots_retraits</code>
|
||||
</td>
|
||||
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>Historique des mouvements de cash (dépôts et retraits), du plus récent au plus ancien.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={{ padding: '8px 10px 8px 0', verticalAlign: 'top', whiteSpace: 'nowrap' }}>
|
||||
<code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_fetch_url</code>
|
||||
</td>
|
||||
<td style={{ padding: '8px 0', verticalAlign: 'top' }}>
|
||||
<em>Développement local uniquement</em>, désactivé par défaut. Lit une page web (ex. annonce de projet sur
|
||||
une plateforme) et en extrait le texte propre — c'est à vous d'en reprendre les informations utiles pour
|
||||
créer l'investissement manuellement, l'outil ne saisit rien lui-même.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4 style={{ margin: '16px 0 8px', color: 'var(--text)' }}>Exemples de questions</h4>
|
||||
<ul style={{ margin: '0 0 12px 16px', paddingLeft: 0, lineHeight: 2 }}>
|
||||
<li>« Quel est mon encours de crowdlending actuellement ? »</li>
|
||||
<li>« Quel a été mon rendement (intérêts nets) en 2026 ? »</li>
|
||||
<li>« Liste-moi les investissements en retard ou en procédure. »</li>
|
||||
<li>« Donne-moi le détail de l'investissement 42, avec ses remboursements. »</li>
|
||||
<li>« Quels remboursements ai-je reçus entre le 1er et le 30 juin 2026 ? »</li>
|
||||
<li>« Quels ont été mes derniers dépôts et retraits ? »</li>
|
||||
<li>« Regarde cette annonce de projet et propose-moi les infos pour créer l'investissement : [URL] »
|
||||
(développement local, avec <code style={{ background: 'var(--surface-2)', padding: '1px 5px', borderRadius: 4 }}>crowdlending_fetch_url</code> activé)</li>
|
||||
</ul>
|
||||
<p style={{ marginBottom: 0 }}>
|
||||
Ces exemples fonctionnent aussi bien en dev qu'en prod dès lors que le serveur correspondant est connecté
|
||||
(voir les FAQ de configuration ci-dessus) — les six premiers outils sont identiques dans les deux environnements.
|
||||
</p>
|
||||
</FaqItem>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1152,6 +1152,7 @@ function McpServerSection({ goToApiKeys }) {
|
||||
const [mcpUrl, setMcpUrl] = useState(guessMcpUrl());
|
||||
const [apiKey, setApiKey] = useState('');
|
||||
const [manualLabel, setManualLabel] = useState(null); // null = auto-détecté depuis mcpUrl, sinon override manuel
|
||||
const [debugFlag, setDebugFlag] = useState(false); // ajoute --debug : génère un fichier mcp-server-<nom>.log dédié (voir Dépannage)
|
||||
|
||||
const detectedLabel = detectLabelFromUrl(mcpUrl);
|
||||
const label = manualLabel ?? detectedLabel;
|
||||
@@ -1164,14 +1165,23 @@ function McpServerSection({ goToApiKeys }) {
|
||||
// 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 mcpRemoteArgs = [
|
||||
'-y', 'mcp-remote', mcpUrl, '--header', `X-API-Key:\${${envVarName}}`,
|
||||
...(debugFlag ? ['--debug'] : []),
|
||||
];
|
||||
|
||||
// Sur Windows, npx est en réalité npx.cmd (un script) : child_process.spawn,
|
||||
// utilisé par Claude Desktop, ne sait pas l'exécuter directement sans passer
|
||||
// par l'interpréteur de commandes — le serveur reste bloqué sur "running"
|
||||
// sans jamais répondre. Il faut donc l'appeler via cmd /c. Sans risque sur
|
||||
// macOS/Linux, qui n'ont pas ce problème (npx s'exécute nativement).
|
||||
const isWindows = typeof navigator !== 'undefined' && /win/i.test(navigator.platform || navigator.userAgent || '');
|
||||
|
||||
const configJson = JSON.stringify({
|
||||
mcpServers: {
|
||||
[serverKey]: {
|
||||
command: 'npx',
|
||||
args: ['-y', 'mcp-remote', mcpUrl, '--header', `X-API-Key:\${${envVarName}}`],
|
||||
env: { [envVarName]: apiKey || '<VOTRE_CLE_API>' },
|
||||
},
|
||||
[serverKey]: isWindows
|
||||
? { command: 'cmd', args: ['/c', 'npx', ...mcpRemoteArgs], env: { [envVarName]: apiKey || '<VOTRE_CLE_API>' } }
|
||||
: { command: 'npx', args: mcpRemoteArgs, env: { [envVarName]: apiKey || '<VOTRE_CLE_API>' } },
|
||||
},
|
||||
}, null, 2);
|
||||
|
||||
@@ -1261,18 +1271,34 @@ function McpServerSection({ goToApiKeys }) {
|
||||
<h4 style={{ margin: '0 0 6px', fontSize: 'var(--fs-sm)' }}>3. Copier la configuration</h4>
|
||||
<p style={{ margin: '0 0 10px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
|
||||
Dans Claude Desktop : Réglages → Développeur → Serveurs MCP locaux → <strong>Modifier la config</strong>.
|
||||
Ce bouton ouvre le bon fichier quelle que soit votre installation — le chemin diffère en effet
|
||||
selon que Claude Desktop vient de claude.ai (<code>%APPDATA%\Claude\claude_desktop_config.json</code>)
|
||||
ou du Microsoft Store (dossier virtualisé sous <code>...\Packages\Claude_*\LocalCache\Roaming\Claude\</code>).
|
||||
Si le fichier contient déjà une clé <code>"mcpServers"</code>, ajoutez-y seulement l'entrée
|
||||
<code>"{serverKey}"</code> ci-dessous sans écraser le reste ; sinon collez le bloc entier.
|
||||
Selon l'installation (même téléchargée directement depuis anthropic.com — l'origine ne garantit
|
||||
rien), Claude Desktop peut être packagé en MSIX et virtualiser ce fichier : le bouton ouvre parfois
|
||||
une copie sous <code>...\Packages\Claude_*\LocalCache\Roaming\Claude\claude_desktop_config.json</code>
|
||||
alors que l'app tourne réellement avec <code>%APPDATA%\Claude\claude_desktop_config.json</code> (ou
|
||||
l'inverse). Si vos outils <code>crowdlending_*</code> n'apparaissent jamais après configuration,
|
||||
vérifiez les <strong>deux emplacements</strong> et éditez celui qui correspond au dossier où
|
||||
<code>logs\mcp.log</code> se met réellement à jour quand vous relancez l'app (voir Dépannage
|
||||
ci-dessous). Si le fichier contient déjà une clé <code>"mcpServers"</code>, ajoutez-y seulement
|
||||
l'entrée <code>"{serverKey}"</code> sans écraser le reste ; sinon collez le bloc entier.
|
||||
</p>
|
||||
{isWindows && (
|
||||
<p style={{ margin: '0 0 10px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
|
||||
La config ci-dessous passe par <code>cmd /c npx</code> plutôt que <code>npx</code> directement :
|
||||
nécessaire sous Windows, où Claude Desktop ne sait pas lancer <code>npx</code> (script
|
||||
<code>.cmd</code>) sans passer par l'interpréteur de commandes — sinon le serveur reste bloqué
|
||||
sur « running » sans jamais répondre.
|
||||
</p>
|
||||
)}
|
||||
<img
|
||||
src="/mcp/claude-desktop-developer-settings.png"
|
||||
alt="Claude Desktop — Réglages → Développeur → Serveurs MCP locaux → Modifier la config"
|
||||
style={{ width: '100%', maxWidth: 520, borderRadius: 8, border: '1px solid var(--border)', display: 'block', margin: '0 auto 16px' }}
|
||||
onError={(e) => { e.currentTarget.style.display = 'none'; }}
|
||||
/>
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: 6, margin: '0 0 10px', fontWeight: 400, fontSize: 'var(--fs-sm)', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={debugFlag} onChange={e => setDebugFlag(e.target.checked)} style={{ width: 'auto' }} />
|
||||
Ajouter <code>--debug</code> (recommandé en cas de problème — voir Dépannage ci-dessous)
|
||||
</label>
|
||||
<CopyBlock text={configJson} />
|
||||
|
||||
<h4 style={{ margin: '20px 0 6px', fontSize: 'var(--fs-sm)' }}>4. Redémarrer Claude Desktop</h4>
|
||||
@@ -1281,12 +1307,47 @@ function McpServerSection({ goToApiKeys }) {
|
||||
</p>
|
||||
|
||||
<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 0 20px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
|
||||
Dans la liste des outils MCP de Claude Desktop, les outils <code>crowdlending_*</code> doivent
|
||||
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>
|
||||
|
||||
<h4 style={{ margin: '0 0 6px', fontSize: 'var(--fs-sm)' }}>6. Dépannage</h4>
|
||||
|
||||
<p style={{ margin: '0 0 4px', fontSize: 'var(--fs-sm)', fontWeight: 600 }}>
|
||||
Le serveur reste sur « running » indéfiniment, aucun outil n'apparaît, aucune erreur visible
|
||||
</p>
|
||||
<p style={{ margin: '0 0 14px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
|
||||
Cause la plus fréquente sous Windows, même avec <code>cmd /c</code> déjà en place : <code>npx</code>
|
||||
recontacte le registre npm (<code>registry.npmjs.org</code>) à chaque lancement pour vérifier la
|
||||
version, et un antivirus ou un proxy avec inspection HTTPS (Avast, Kaspersky, ESET, proxy
|
||||
d'entreprise…) fait échouer cette requête avec une erreur de certificat — invisible depuis Claude
|
||||
Desktop, qui attend simplement une réponse jamais reçue jusqu'à expirer au bout d'une minute.
|
||||
Confirmez en cherchant <code>UNABLE_TO_VERIFY_LEAF_SIGNATURE</code> dans les logs (voir plus bas).
|
||||
Solution : installez <code>mcp-remote</code> une bonne fois pour toutes (<code>npm install -g
|
||||
mcp-remote</code>) puis redémarrez Claude Desktop — <code>npx</code> utilisera alors le binaire déjà
|
||||
installé sans repasser par le registre à chaque fois.
|
||||
</p>
|
||||
|
||||
<p style={{ margin: '0 0 4px', fontSize: 'var(--fs-sm)', fontWeight: 600 }}>Où trouver les logs</p>
|
||||
<p style={{ margin: '0 0 14px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
|
||||
<code>logs\mcp.log</code> trace les échanges entre Claude Desktop et le process local (tous serveurs
|
||||
confondus) ; <code>logs\mcp-server-{serverKey}.log</code> contient la sortie détaillée de ce serveur
|
||||
précis, uniquement si <code>--debug</code> est activé ci-dessus (case à cocher, étape 3). Le bouton
|
||||
« Afficher les journaux » de Claude Desktop peut ne pas s'ouvrir sous Windows (bug connu) — allez
|
||||
chercher directement dans le dossier <code>logs</code>, à l'un des deux emplacements mentionnés à
|
||||
l'étape 3 (essayez l'autre si l'un des deux est vide ou ne se met pas à jour).
|
||||
</p>
|
||||
|
||||
<p style={{ margin: '0 0 4px', fontSize: 'var(--fs-sm)', fontWeight: 600 }}>Vérifier côté serveur</p>
|
||||
<p style={{ margin: 0, fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
|
||||
La console du serveur (le terminal où tourne <code>npm run dev</code>) affiche désormais une ligne
|
||||
par requête reçue — session, outil appelé, statut, durée. Si rien n'y apparaît alors qu'un appel a
|
||||
été fait depuis Claude Desktop, la requête n'arrive jamais jusqu'ici : le problème est côté
|
||||
<code>npx</code>/<code>mcp-remote</code> (voir ci-dessus), pas dans <code>server.js</code>.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+42
-1
@@ -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
@@ -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}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user