|
|
6 days ago | |
|---|---|---|
| .github | 6 days ago | |
| .vscode | 6 days ago | |
| assets | 6 days ago | |
| src/gnome_vte_mcp | 6 days ago | |
| .gitignore | 7 days ago | |
| README.md | 6 days ago | |
| TEST_REPORT.md | 6 days ago | |
| pyproject.toml | 6 days ago | |
README.md
mcp-hal9002
mcp-hal9002 e un terminal workspace GTK4 + VTE controllabile via MCP, con GUI locale, control plane Unix socket e strumenti per screenshot/debug UI.
Il nome
Il nome e un omaggio a HAL 9000, il computer di bordo dell'astronave Discovery One in 2001: Odissea nello Spazio (regia di Stanley Kubrick, sceneggiatura di Arthur C. Clarke, 1968). HAL e un'intelligenza artificiale che governa e controlla in modo autonomo tutti i sistemi della nave — esattamente come mcp-hal9002 e un agente che controlla il terminale tramite MCP. Il numero e aggiornato da 9000 a 9002, come a indicare una versione successiva, piu obbediente e (si spera) priva di derive omicide. Il logo riprende l'iconico occhio rosso di HAL.
Obiettivo
Il progetto evita di dipendere da API non ufficiali di GNOME Terminal. Invece espone direttamente una GUI propria, che supporta:
- apertura tab
- elenco tab
- focus tab
- invio comandi a una shell esistente
- lettura dello scrollback della tab
- screenshot della finestra, del contenuto VTE della tab, o del container della tab per debug UI/UX
- chiusura tab
Architettura
src/gnome_vte_mcp/gui.pyapplicazione GTK4/VTE con un controllo locale via socket Unixsrc/gnome_vte_mcp/server.pyMCP server basato suFastMCPche parla con la GUIsrc/gnome_vte_mcp/control.pyprotocollo client per il socket locale
Il server MCP lancia automaticamente la GUI se non e gia in esecuzione.
La GUI e considerata una finestra gestita dal server MCP: il flusso previsto e aprirla, portarla in primo piano e chiuderla tramite tool MCP, non tramite una CLI separata.
Uso con uvx
Entrypoint principali:
mcp-hal9002avvia il server MCP
Esempi locali dal checkout:
uvx --from . mcp-hal9002
Esempio da repository Git:
uvx --from git+https://git.enne2.net/enne2/mcp-hal9002.git mcp-hal9002
Uso diretto in VS Code con Copilot
Configurazione a livello utente (raccomandata, tramite uvx)
Per abilitare mcp-hal9002 in tutti i workspace VS Code senza dover aprire questo repository, aggiungilo al file di configurazione MCP utente:
Linux/macOS: ~/.config/Code/User/mcp.json
Windows: %APPDATA%\Code\User\mcp.json
Da repository Git (non richiede installazione locale):
{
"servers": {
"mcpHal9002": {
"type": "stdio",
"command": "uvx",
"args": [
"--from",
"git+https://git.enne2.net/enne2/mcp-hal9002.git",
"mcp-hal9002"
]
}
}
}
Se il pacchetto viene pubblicato su PyPI, la versione semplificata sara:
{
"servers": {
"mcpHal9002": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-hal9002"]
}
}
}
Dopo aver salvato il file, esegui MCP: List Servers in VS Code per rilevare la nuova configurazione, oppure attiva chat.mcp.autostart in settings per il riavvio automatico.
Configurazione di workspace (sviluppo locale)
Il workspace include una configurazione .vscode/mcp.json che registra mcp-hal9002 come server MCP stdio per GitHub Copilot usando il Python del virtualenv locale e PYTHONPATH=${workspaceFolder}/src.
Nel workspace e presente anche .vscode/settings.json con chat.mcp.autostart=true, cosi VS Code puo riavviare automaticamente il server quando la configurazione cambia.
Per usarlo in Copilot:
- apri questo workspace in VS Code
- assicurati che
.venvesista e contenga le dipendenze del progetto - apri Chat e conferma la trust prompt del server MCP quando VS Code la mostra
- se i tool non compaiono subito, esegui
MCP: List ServersoppureMCP: Reset Cached Tools
Requisiti
Nel sistema devono essere disponibili:
- Python 3.12+
- package Python
pycairo - PyGObject (
gi) - GTK4
- VTE con binding GI (
Vte 3.91) - package Python
mcp
In questa macchina il runtime necessario risulta disponibile.
Debug locale della GUI
La GUI continua ad avere un modulo eseguibile per debug e sviluppo, ma il percorso normale e pilotarla dal server MCP con open_gui() e chiuderla con close_gui().
PYTHONPATH=src python3 -m gnome_vte_mcp.gui
Per usare un socket custom:
PYTHONPATH=src python3 -m gnome_vte_mcp.gui --socket /tmp/mcp-hal9002-demo.sock
Avvio del server MCP
PYTHONPATH=src python3 -m gnome_vte_mcp.server
Variabile opzionale:
export MCP_HAL9002_SOCKET=/tmp/mcp-hal9002-demo.sock
La variabile legacy GNOME_VTE_MCP_SOCKET resta accettata per compatibilita.
Tool MCP disponibili
gui_status()open_gui()open_tab(title=None, cwd=None, command=None)list_tabs()focus_tab(tab_id)exec_command(tab_id, command, newline=True, auto_submit=False, poll_interval=0.1)read_tab(tab_id, last_n_lines=200)read_last_command_result(tab_id)wait_for_command(delay_seconds)wait_for_running_command(tab_id, timeout=None, poll_interval=0.1)wait_for_command_result(tab_id, after_sequence=None, timeout=None, poll_interval=0.1)wait_for_prompt(tab_id, timeout=None, poll_interval=0.1, idle_seconds=0.4, prompt_pattern=None)capture_screenshot(target="window", tab_id=None, path=None, diagnostic_overlay=False)close_tab(tab_id)close_gui()
Lifecycle GUI:
open_gui()avvia la GUI se manca e porta in primo piano la finestra condivisagui_status()restituisce lo stato senza creare nuove istanzeclose_gui()chiude la finestra condivisa se e in esecuzione- tutti gli altri tool che richiedono la GUI continuano a riusare la stessa istanza attraverso il socket locale
Per la lettura testuale dei comandi:
read_tab(...)restituisce lo scrollback recente, utile per debugging grezzoexec_command(...)scrive il testo nel terminale e, di default, resta bloccato indefinitamente finche l'utente non premeEntermanualmente nella GUI; il parametronewlineresta ignorato in questa modalita per compatibilitaexec_command(..., auto_submit=True)invia ancheEnterda solo, ma e volutamente ristretto a piccoli comandi read-only e senza shell syntax complessa- dopo eventuali modifiche manuali nella GUI, premi
Entertu per sbloccare davveroexec_command(...)e inviare il comando alla shell - per comandi che aprono una sessione interattiva delegata come
ssh,arcao una subshell, la tab non viene trattata come bloccata: puoi continuare a scrivere nuovi comandi nella stessa sessione senza aspettare il ritorno della shell locale wait_for_command(delay_seconds)non osserva il terminale e non prova a capire se il comando e finito: e solo una pausa sincrona esplicita, utile quando il loop eread_tab(...)-> attesa arbitraria ->read_tab(...)wait_for_running_command(...)aspetta il completamento del comando che e gia in esecuzione nella tab quando il terminale e occupato e non puoi ancora inviare un nuovo comando- se la tab e dentro una sessione interattiva delegata,
wait_for_running_command(...)fallisce esplicitamente perche il completamento tracciato tornera disponibile solo quando esci da quella sessione wait_for_command_result(...)aspetta in modo bloccante il completamento del comando dopo l'after_sequencerestituito daexec_command(...)e restituiscecommand,cwd,cwd_after,started_at,finished_at,duration_seconds,exit_codeetext; setimeoute omesso o<= 0, l'attesa e indefinitawait_for_prompt(...)serve per sessioni interattive delegate comessh: aspetta che il transcript smetta di crescere per un breve intervallo e che l'ultima riga assomigli a un promptread_last_command_result(tab_id)restituisce l'ultimo comando completato con gli stessi metadati temporali e di path
Per capture_screenshot:
target="window"prova a catturare l'intera finestra, inclusa la titlebar GTK; se la piattaforma non lo consente ripiega sul contenuto renderizzatotarget="tab"cattura solo il widgetVte.Terminaldella tab attiva o della tab indicata contab_id, escludendo header bar e tab bartarget="tab-container"cattura il container GTK della tab, utile se serve includere il bordo o eventuali scrollbarpathpermette di scegliere il file PNG di destinazionediagnostic_overlay=Trueannota lo screenshot con una griglia e i bounds del target perwindow,tabetab-container
Il tool restituisce un payload JSON serializzabile con path, dimensioni e metadati del capture, cosi il bridge MCP non dipende da tipi immagine custom.
Per ogni screenshot viene salvato anche un file JSON sidecar con metadati di debug UI, tra cui:
- widget target effettivo
- allocazione del widget rispetto alla finestra
- renderer GSK usato per la cattura
- dettagli di superficie e scala
- stato dell'overlay diagnostico richiesto/applicato
Primo avvio e onboarding
La prima tab mostra un piccolo pannello di onboarding centrato sopra il terminale, con un riepilogo delle azioni MCP disponibili e del workspace corrente.
Il pannello sparisce automaticamente:
- alla prima pressione di tasto dentro il terminale
- al primo
exec_command(...)inviato via MCP oppure al primo comando eseguito manualmente
Titoli tab dinamici
I titoli delle tab sono derivati in modo compatto da:
titleesplicito, se fornito e non generico- comando iniziale o ultimo comando eseguito
cwd, quando non c'e un comando significativo
Questo rende il StackSwitcher piu compatto e piu utile quando ci sono piu tab aperte.
Limiti attuali
- non salva ancora layout o sessioni persistenti
- non gestisce ancora rename tab automatico da processo o cwd
- il canale di controllo locale non ha ancora autenticazione aggiuntiva oltre ai permessi del socket Unix
- il recupero testo usa un transcript locale della shell, ma non implementa ancora stream incrementale o eventi
- la cattura screenshot dipende dal fatto che il widget sia già renderizzato nella sessione grafica corrente
- se una GUI gia avviata resta in esecuzione, i test del control plane continueranno a usare quel processo: dopo modifiche a screenshot o titlebar conviene riavviare la GUI prima di ritestare
Prossimi passi naturali
- aggiungere eventi di output incrementale e subscription
- introdurre persistenza dello stato tab/sessioni
- migliorare il modello UI con veri tab drag-and-drop e split view
- aggiungere policy locali per limitare i comandi consentiti
- usare gli screenshot e i sidecar JSON per confronti UI before/after automatizzabili
Autore
Matteo Benedetto — Enne2
Computer engineer, systems designer and architect, eclectic. Working in aerospace industry (e-geos S.p.A.), Italy.
- GitHub: github.com/Enne2
- Web: enne2.net
© 2025–2026 Matteo Benedetto