Server MCP che espone Claude Code a Claude Desktop, per lavorare su un progetto senza copiare e incollare prompt tra le due applicazioni.
Si ragiona su un problema nella chat di Claude Desktop, e da lì si delega l'esecuzione a Claude Code sul progetto reale. I lavori lunghi girano in background: si avviano, si continua a fare altro, si recupera il risultato quando è pronto.
Questo server permette a un modello di eseguire Claude Code sulla tua macchina, con accesso al filesystem. È una superficie di rischio reale e va capita prima di usarlo.
Cosa fa e cosa non fa:
- Gira solo in locale. Il trasporto è stdio: Claude Desktop lancia il server come processo figlio e ci parla via stdin/stdout. Nessuna porta in ascolto, nessun endpoint pubblico, niente che entri dal firewall.
- Non limita cosa Claude Code può fare. Il server passa il prompt e basta. I veri freni sono i permessi in
.claude/settings.jsondel progetto: se lì è consentita la scrittura, l'agente scrive. - Non c'è allowlist di directory. Qualunque percorso esistente passato come
cwdviene accettato. Se ti serve un vincolo più stretto, aggiungi il controllo inavviaClaudeedeseguiClaude. - Ogni chiamata a strumento passa dalla conferma di Claude Desktop. Il client chiede l'approvazione prima di invocare il server — a meno che non l'abbia disattivata.
La raccomandazione: configura permessi restrittivi nei progetti su cui lo usi, in particolare per le operazioni irreversibili (migration, cancellazioni, deploy). Un agente che deve chiedere prima di fare danni è più utile di uno veloce.
- Windows, Linux o macOS — testato su Windows 10/11 e Debian/Kali
- Node.js 18+ — testato su 22
- Claude Code installato e autenticato
- Claude Desktop
git clone https://github.com/ITSWProj/claude-dispatch.git
cd claude-dispatch
npm installVerifica che parta:
node server.jsDeve restare appeso in silenzio, senza stampare nulla: sta aspettando input su stdin. È il comportamento corretto. Esci con Ctrl+C.
Il file di configurazione di Claude Desktop è:
| Sistema | Percorso |
|---|---|
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
Windows:
{
"mcpServers": {
"claude-dispatch": {
"command": "C:\\percorso\\a\\node.exe",
"args": ["C:\\percorso\\a\\claude-dispatch\\server.js"]
}
}
}macOS / Linux:
{
"mcpServers": {
"claude-dispatch": {
"command": "/percorso/assoluto/a/node",
"args": ["/percorso/assoluto/a/claude-dispatch/server.js"]
}
}
}Tre punti dove si sbaglia facilmente:
- Percorso assoluto all'eseguibile di Node, non la parola
node. Claude Desktop non eredita il PATH della shell. Se usi nvm, punta all'eseguibile della versione specifica (C:\nvm4w\v22.20.0\node.exesu Windows,~/.nvm/versions/node/v22.20.0/bin/nodesu Unix), non alla junction o al symlinkcurrent, che cambiano a ogninvm use. Per trovarlo:where nodesu Windows,which nodesu Unix. - Su Windows, backslash raddoppiati nel JSON, oppure slash normali (
C:/percorso/...), che Node accetta anche lì. - Percorso assoluto anche in
args: il processo viene avviato con una working directory indefinita, quindi./server.jsnon risolve. Su Unix nemmeno~viene espanso — quella è una funzione della shell, e qui la shell non c'è.
Poi chiudi Claude Desktop completamente — su Windows anche dall'area di notifica, su macOS con Cmd+Q e non con la X — e riavvialo. La configurazione si legge solo all'avvio.
Se non compare nulla, il sospetto numero uno è il JSON: una virgola di troppo disattiva silenziosamente tutti i server, senza messaggi. Passalo da un validatore.
Lo stderr del server (dove finiscono i console.error()) è nel file mcp-server-claude-dispatch.log, nella cartella logs/ accanto al file di configurazione. Se il processo muore all'avvio, il motivo è scritto lì.
| Strumento | Uso |
|---|---|
claude_run |
Esegue un prompt e attende la risposta. Per compiti brevi. |
claude_start |
Avvia un lavoro in background, ritorna subito un job_id. |
claude_wait |
Attende un job avviato; se non è pronto entro hold_ms (default 30s, massimo 60s) riporta "in corso". |
claude_kill |
Termina un job e tutti i suoi processi figli. |
Il discriminante è il tempo. Una domanda circoscritta — "leggi questi file e spiegami come funziona X" — sta bene in claude_run. Un lavoro vero — refactoring, implementazione, esplorazione di un'area sconosciuta — va con claude_start, poi claude_wait finché non è pronto.
claude_run ha un timeout (default 120s) perché una chiamata sincrona che non ritorna blocca la conversazione. claude_start non ne ha: il tetto sta sull'attesa, non sul lavoro.
Anche quell'attesa però è limitata: hold_ms viene tagliato a 60 secondi. Passare valori più alti non allunga l'attesa — semplicemente si richiama claude_wait con lo stesso job_id finché il lavoro non è concluso. È un vincolo voluto: hold_ms lo sceglie il modello, e senza tetto una singola chiamata potrebbe bloccare la conversazione per minuti.
Ogni risposta restituisce un session_id. Ripassarlo alla chiamata successiva fa riprendere la sessione con il contesto già caricato, invece di riesplorare il progetto da zero.
Non è un dettaglio: la prima invocazione su un progetto di medie dimensioni può costare qualche decina di centesimi. Riusare la sessione riduce il costo dei giri successivi di un ordine di grandezza.
- Lo stato vive in memoria. Riavviando Claude Desktop mentre un lavoro gira, il processo continua ma il
job_iddiventa irraggiungibile. Non riavviare durante i lavori lunghi. - I job in background sopravvivono alla chiusura del client. Su tutte le piattaforme: se hai chiuso Claude Desktop con lavori in corso, quei processi restano vivi e vanno terminati a mano (Gestione attività su Windows,
pkill -f "claude -p"su Unix). - Il timeout di
claude_runuccide l'intero albero di processi, agente incluso, e il lavoro parziale non è recuperabile. Se il compito potrebbe essere lungo, usaclaude_start: lì il tempo non è un vincolo. - La prima invocazione su un progetto è la più lenta. Sembra un blocco, non lo è.
- Nessun limite di spesa. Il server non impone tetti di costo o di turni. Se servono, si passano a Claude Code con
--max-turns.
Un server MCP stdio non è un servizio in ascolto: è un processo figlio. Claude Desktop lo lancia e ci scambia messaggi JSON-RPC via stdin/stdout.
Da qui discende la regola più importante per chi mette mano al codice: mai console.log(). Ogni byte su stdout finisce nel canale del protocollo e corrompe il messaggio. Per il debug si usa console.error(), che scrive su stderr.
L'asincronia è gestita con Promise.race tra la fine del lavoro e un timer: nessun polling, nessuna euristica sul "sembra fermo". I job vivono in una Map con pulizia opportunistica dei conclusi da oltre dieci minuti.
Il prompt viaggia su stdin, non come argomento della riga di comando: è testo lungo e pieno di caratteri speciali, e qualunque escaping per la shell sarebbe fragile.
Il file è unico: le differenze sono decise a runtime da process.platform e concentrate in tre costanti più un ramo in uccidiAlbero().
Il nodo è che il processo restituito da spawn() non è mai l'agente, ma il primo anello di una catena — cmd.exe → claude.cmd → node → agente su Windows, claude → node → agente su Unix. Terminare solo quello lascerebbe l'agente vivo a consumare token senza più essere raggiungibile.
Su Windows serve shell: true perché claude.cmd è uno script batch che solo cmd.exe sa interpretare, e da lì taskkill /T /F per abbattere l'albero. Su Unix la shell non serve — un guadagno anche di sicurezza — e si usa detached: true, che rende il figlio capostipite di un process group: da quel momento process.kill(-pid) raggiunge l'intero gruppo con un solo segnale. Le due opzioni vanno insieme: senza detached, il PID negativo colpirebbe il gruppo del server stesso.
Il codice è commentato in dettaglio, con il perché di ogni scelta non ovvia.
Entrambi i rami sono stati verificati: su Unix con ps -eo pid,pgid (il figlio deve avere PGID uguale al proprio PID, cioè essere capostipite del suo gruppo) e controllando che il kill abbatta l'albero senza toccare il server. macOS non è stato provato direttamente, ma usa lo stesso ramo.
MIT