MAj MCP Serveur
This commit is contained in:
@@ -179,6 +179,136 @@ export default function Aide() {
|
|||||||
</p>
|
</p>
|
||||||
</FaqItem>
|
</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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1152,6 +1152,7 @@ function McpServerSection({ goToApiKeys }) {
|
|||||||
const [mcpUrl, setMcpUrl] = useState(guessMcpUrl());
|
const [mcpUrl, setMcpUrl] = useState(guessMcpUrl());
|
||||||
const [apiKey, setApiKey] = useState('');
|
const [apiKey, setApiKey] = useState('');
|
||||||
const [manualLabel, setManualLabel] = useState(null); // null = auto-détecté depuis mcpUrl, sinon override manuel
|
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 detectedLabel = detectLabelFromUrl(mcpUrl);
|
||||||
const label = manualLabel ?? detectedLabel;
|
const label = manualLabel ?? detectedLabel;
|
||||||
@@ -1164,14 +1165,23 @@ function McpServerSection({ goToApiKeys }) {
|
|||||||
// API passe en variable d'environnement plutôt que directement dans args
|
// API passe en variable d'environnement plutôt que directement dans args
|
||||||
// (bug connu de Claude Desktop Windows qui tronque les valeurs à espaces).
|
// (bug connu de Claude Desktop Windows qui tronque les valeurs à espaces).
|
||||||
const envVarName = `${label.toUpperCase()}_API_KEY`;
|
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({
|
const configJson = JSON.stringify({
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
[serverKey]: {
|
[serverKey]: isWindows
|
||||||
command: 'npx',
|
? { command: 'cmd', args: ['/c', 'npx', ...mcpRemoteArgs], env: { [envVarName]: apiKey || '<VOTRE_CLE_API>' } }
|
||||||
args: ['-y', 'mcp-remote', mcpUrl, '--header', `X-API-Key:\${${envVarName}}`],
|
: { command: 'npx', args: mcpRemoteArgs, env: { [envVarName]: apiKey || '<VOTRE_CLE_API>' } },
|
||||||
env: { [envVarName]: apiKey || '<VOTRE_CLE_API>' },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}, null, 2);
|
}, null, 2);
|
||||||
|
|
||||||
@@ -1261,18 +1271,34 @@ function McpServerSection({ goToApiKeys }) {
|
|||||||
<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>
|
||||||
<p style={{ margin: '0 0 10px', fontSize: 'var(--fs-sm)', color: 'var(--text-muted)' }}>
|
<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>.
|
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 l'installation (même téléchargée directement depuis anthropic.com — l'origine ne garantit
|
||||||
selon que Claude Desktop vient de claude.ai (<code>%APPDATA%\Claude\claude_desktop_config.json</code>)
|
rien), Claude Desktop peut être packagé en MSIX et virtualiser ce fichier : le bouton ouvre parfois
|
||||||
ou du Microsoft Store (dossier virtualisé sous <code>...\Packages\Claude_*\LocalCache\Roaming\Claude\</code>).
|
une copie sous <code>...\Packages\Claude_*\LocalCache\Roaming\Claude\claude_desktop_config.json</code>
|
||||||
Si le fichier contient déjà une clé <code>"mcpServers"</code>, ajoutez-y seulement l'entrée
|
alors que l'app tourne réellement avec <code>%APPDATA%\Claude\claude_desktop_config.json</code> (ou
|
||||||
<code>"{serverKey}"</code> ci-dessous sans écraser le reste ; sinon collez le bloc entier.
|
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>
|
</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
|
<img
|
||||||
src="/mcp/claude-desktop-developer-settings.png"
|
src="/mcp/claude-desktop-developer-settings.png"
|
||||||
alt="Claude Desktop — Réglages → Développeur → Serveurs MCP locaux → Modifier la config"
|
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' }}
|
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'; }}
|
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} />
|
<CopyBlock text={configJson} />
|
||||||
|
|
||||||
<h4 style={{ margin: '20px 0 6px', fontSize: 'var(--fs-sm)' }}>4. Redémarrer Claude Desktop</h4>
|
<h4 style={{ margin: '20px 0 6px', fontSize: 'var(--fs-sm)' }}>4. Redémarrer Claude Desktop</h4>
|
||||||
@@ -1281,12 +1307,47 @@ function McpServerSection({ goToApiKeys }) {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<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 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
|
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
|
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
|
activé — voir <code>mcp-server/README.md</code>). Testez avec une question du type « Quel est mon
|
||||||
encours de crowdlending actuellement ? ».
|
encours de crowdlending actuellement ? ».
|
||||||
</p>
|
</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>
|
</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` :
|
locaux → **Modifier la config**, puis ajoutez dans `mcpServers` :
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
// macOS / Linux
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"crowdlending-dev": {
|
"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
|
**Important :** `claude_desktop_config.json` n'accepte que des entrées
|
||||||
`command`/`args` — il n'existe pas de champ `url`/`headers` natif dans ce
|
`command`/`args` — il n'existe pas de champ `url`/`headers` natif dans ce
|
||||||
fichier (contrairement à d'autres clients MCP). Pour un serveur HTTP comme
|
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
|
`X-API-Key` inclus. Aucune installation manuelle requise, `npx` le télécharge
|
||||||
à la volée.
|
à 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
|
(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
|
passe donc la valeur réelle (avec l'espace éventuel) via une variable
|
||||||
d'environnement dans `env` plutôt que directement dans `args`.
|
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
|
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
|
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
|
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
|
votre config utilise un bloc `url`/`headers` directement, non supporté par
|
||||||
`claude_desktop_config.json` (voir Développement local plus haut) : il faut
|
`claude_desktop_config.json` (voir Développement local plus haut) : il faut
|
||||||
passer par `command: "npx"` + `mcp-remote`.
|
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é 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
|
||||||
|
|||||||
+24
-7
@@ -17,10 +17,11 @@
|
|||||||
* des outils de CETTE session uniquement (voir createSession). Deux sessions
|
* des outils de CETTE session uniquement (voir createSession). Deux sessions
|
||||||
* ne partagent jamais d'état ni de données.
|
* ne partagent jamais d'état ni de données.
|
||||||
*
|
*
|
||||||
* Connexion depuis Claude Desktop (dev comme prod) :
|
* Connexion depuis Claude Desktop (dev comme prod) : ce fichier expose du
|
||||||
* { "url": "http://localhost:4100/mcp", "headers": { "X-API-Key": "..." } }
|
* HTTP pur, mais claude_desktop_config.json n'accepte que des entrées
|
||||||
* (remplacer l'URL par https://mcp.crowdlending.croguennec.net/mcp pour la
|
* command/args — la connexion passe donc par le pont mcp-remote
|
||||||
* prod). Voir README.md pour le détail.
|
* (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';
|
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é'}`);
|
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]"). */
|
/** Ajoute le libellé d'environnement au titre d'un outil (ex. "[PROD]"). */
|
||||||
const withLabel = (title) => LABEL ? `${title} [${LABEL.toUpperCase()}]` : title;
|
const withLabel = (title) => LABEL ? `${title} [${LABEL.toUpperCase()}]` : title;
|
||||||
|
|
||||||
@@ -118,7 +130,7 @@ setInterval(() => {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
for (const [sessionId, s] of sessions.entries()) {
|
for (const [sessionId, s] of sessions.entries()) {
|
||||||
if (now - s.lastActivity > SESSION_IDLE_TIMEOUT_MS) {
|
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();
|
s.transport.close();
|
||||||
sessions.delete(sessionId);
|
sessions.delete(sessionId);
|
||||||
}
|
}
|
||||||
@@ -179,7 +191,10 @@ app.post('/mcp', async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
touchSession(sessionId);
|
touchSession(sessionId);
|
||||||
|
const label = describeRequest(req.body);
|
||||||
|
const startedAt = Date.now();
|
||||||
await session.transport.handleRequest(req, res, req.body);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,9 +210,11 @@ app.post('/mcp', async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const transport = await createSession(apiKey);
|
const transport = await createSession(apiKey);
|
||||||
|
const startedAt = Date.now();
|
||||||
await transport.handleRequest(req, res, req.body);
|
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) {
|
} 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 });
|
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.delete('/mcp', handleExistingSession);
|
||||||
|
|
||||||
app.listen(PORT, '0.0.0.0', () => {
|
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