Delphi MCP Connect (MCPConnect) è un framework leggero e robusto progettato per semplificare drasticamente la creazione di Server Model Context Protocol (MCP) utilizzando Embarcadero Delphi. Sfruttando gli attributi, il framework consente di riutilizzare la logica esistente e le classi Delphi standard, trasformandole in componenti server consapevoli del protocollo. MCPConnect gestisce la serializzazione, il routing e la gestione del contesto richiesti per l'implementazione lato server del protocollo MCP.
Introduzione a MCP
Il Model Context Protocol è un framework sviluppato da Anthropic nel 2024. Il suo scopo è standardizzare il modo in cui i modelli linguistici di grandi dimensioni (LLM) comunicano e interagiscono con le fonti esterne. Questo perché in molti casi le possibilità degli LLM sono vincolate dal fatto di poter produrre del testo (token) senza però poter agire in qualche modo sul mondo esterno o produrre risposte basate su qualcosa che non provenga dai loro dati di addestramento.
In realtà nel 2023 OpenAI aveva già provato ad espandere le possibilità dei propri LLM con un approccio simile, tramite una tecnologia denominata "function-calling". Il problema è che questa tecnologia, copiata dalla maggior parte degli altri produttori era legata alle API dei singoli prodotti. Per questo chi avesse voluto usarla era obbligato a scrivere diverse integrazioni.
Alcuni casi d'uso tipici di MCP possono essere:
Accesso a database locali - Permettere di interrogare direttamente database PostgreSQL, MySQL o SQLite per analisi dati e query complesse
Integrazione con filesystem - Ottenere la capacità di leggere, cercare e analizzare file nel tuo sistema locale per supporto allo sviluppo o analisi di progetti
Automazione browser - Controllare un browser per web scraping, testing automatizzato o interazione con applicazioni web
API di terze parti - Integrare servizi esterni come Slack, GitHub, Linear o Jira per gestire task e comunicazioni
Tool personalizzati aziendali - Esporre API e strumenti interni all'organizzazione per permettere all'LLM di interagire con sistemi proprietari
Manipolazione dati complessi - Processare file Excel, CSV o altri formati strutturati con logica personalizzata non disponibile nei tool standard
L'architettura
Per ottenere queste possibilità l'architettura di MCP è divisa in tre blocchi:
- MCP Host: L'applicazione AI che userà MCP, per esempio Claude Desktop
- MCP Client: Il componente che si connette con il server MCP e invia all'host il nuovo contesto (dati ricavati dal server)
- MCP Server: Il programma che fornisce il contesto. In pratica risponde alle chiamate fornendo le informazioni richieste dall'host tramite il client
Il Server
Il server è il cuore del protocollo MCP, la parte che concretamente esegue i compiti necessari. Le funzioni che può eseguire sono molteplici: accedere ai documenti del file system, eseguire delle query su un database, inviare messaggi tramite mail o sistemi di messaggistica, sincronizzarsi con un calendario, ecc.
Per eseguire questi compiti il protocollo prevede tre tipi di blocchi:
- Tools: funzioni che l'LLM può decidere di chiamare. I tool possono interagire con un database, chiamare API esterne, modificare file o eseguire un qualsiasi tipo di logica.
- Risorse: sono dei dataset read-only che possono essere richiesti dal LLM per inserire informazioni nel contesto. Possono essere documenti, file o qualsiasi altro tipo di dato strutturato. (attualmente non supportato da MCPConnect)
- Prompts: sono dei template che possono essere richiamati dall'utente (di solito scrivendo il nome del template preceduto da uno slash) per generare un prompt specifico per una particolare operazione. (attualmente non supportato da MCPConnect)
Supponendo di avere una classe TDocumentService che implementa alcuni tool è possibile configurare il server di MCPConnect in questo modo:
uses
MCPConnect.JRPC.Server,
MCPConnect.MCP.Server.Api, // This register the standard MCP API
MCPConnect.Configuration.MCP,
Demo.DocumentService; // Unit with your MCP classes
// Create the JSON-RPC Server
FJRPCServer := TJRPCServer.Create(Self);
FJRPCServer
.Plugin.Configure<IMCPConfig>
.SetServerName('delphi-mcp-server')
.SetServerVersion('2.0.0')
.RegisterToolClass(TDocumentService) // Register your tool class here
.ApplyConfig;
Il trasporto
Il protocollo MCP usa JSON-RPC per codificare i messaggi che passano tra il client e il server mentre per quanto riguarda il trasporto vero e proprio supporta due tipo di canali:
- Stdio: lo standard input e output in maniera analoga ai vecchi CGI: in pratica l'applicazione sarà di tipo consolle con le richieste che arrivano interamente sullo standard input e le risposte inviate sulla standard output.
- Streamable HTTP: una comunicazione HTTP con la possibilità di mantenere aperto il canale per permettere al server di inviare direttamente notifiche al client senza bisogno di fare polling.
MCP Connect supporta entrambe le modalità di funzionamento e la scelta dipende dal tipo di progetto e anche dalla fase di sviluppo. Per esempio in genere il debug è molto più semplice in una applicazione HTTP rispetto ad una che fa uso di stdio. La stessa applicazione potrebbe anche funzionare in entrambe le modalità a seconda della configurazione o un parametro passato all'avvio.
| Aspetto | stdio | Streamable HTTP |
|---|---|---|
| Ambito | Locale | Locale + remoto |
| Complessità | ⭐ Bassa | ⭐⭐⭐ Alta |
| Scalabilità | ❌ No | ✅ Sì |
| Sicurezza | Implicita | Da configurare |
| Streaming | Limitato | Avanzato |
| Debug | Complesso | Semplice |
| Uso tipico | Dev / tool locali | Produzione / cloud |
Riprendendo l'esempio precedente con MCPConnect è possibile impostare HTTP come meccanismo di trasporto specificando la tecnologia (Indy o WebBroker) da usare. Per esempio volendo usare WebBroker la configurazione è la seguente:
uses
MCPConnect.JRPC.Server,
MCPConnect.MCP.Server.Api, // This register the standard MCP API
MCPConnect.Transport.WebBroker,
MCPConnect.Configuration.MCP,
Demo.DocumentService; // Unit with your MCP classes
// Create the JSON-RPC Server
FJRPCServer := TJRPCServer.Create(Self);
FJRPCServer
.Plugin.Configure<IMCPConfig>
....
// Create and configure the Dispatcher
FJRPCDispatcher := TJRPCDispatcher.Create(Self); // Self should be the TWebModule
FJRPCDispatcher.PathInfo := '/mcp'; // Set the endpoint path
FJRPCDispatcher.Server := FJRPCServer; // Connect to the server
Con il codice precedente FJRPCDispatcher si farà carico di rispondere a tutte le chiamate in arrivo al path /mcp tramite il server JRPC configurato in precedenza.
Tools con MCP Connect
L'aspetto più interessante di MCPConnect è il fatto che permette di scrivere dei server MCP senza doversi preoccupare dei dettagli dell'architettura del protocollo. Supponiamo per esempio di voler fornire all'LLM delle informazioni in base a una categoria. In Delphi potremmo scrivere una classe di questo genere:
TDocumentService = class
public
function ListDocument(const ACategory: string): string;
end;
Le informazioni finiranno nel contesto di un LLM quindi, anche se MCPConnect gestisce anche dei valori di ritorno più complessi (es. TArray<TDocument>, TStringList, TImage, ecc.), molto spesso restituire una stringa è più che sufficiente.
Ritornando al nostro esempio non è importante come il metodo ListDocument restituisca i dati MCPConnect si occuperà di descriverlo all'LLM perché possa decidere come e quando chiamarlo.
Per poterlo fare però è necessario "spiegare" all'LLM a cosa serve la classe e in particolare il metodo in questione. Questo con MCPConnect avviene principalmente attraverso degli attributi. In questo modo la dichiarazione della classe verrà arricchita come segue:
TDocumentService = class
public
// This method is published as an MCP tool
[McpTool('doclist', 'List all the available documents')]
function ListDocument(
[McpParam('category', 'Document Category')] const ACategory: string
): string;
// This method is NOT exposed because it lacks the [McpTool] attribute
procedure InternalStuff;
end;
Gli attributi McpTool e McpParam prevedono due parametri: il nome del metodo e la descrizione. La descrizione è essenziale, infatti è proprio in base a questa che LLM capisce a cosa serve l'elemento in questione, se deve usarlo e come usarlo. Quindi è fondamentale essere il più possibile chiari nella formulazione del testo.
Il risultato dei tool
Nell'esempio che abbiamo visto il tool restituisce una stringa, molto spesso questo può essere sufficiente, ma ovviamente dipende dallo scopo del tool. Possiamo dividere i tool in due macro gruppi:
- Operativi: il cui scopo principale è compiere un'azione concreta. Possono scrivere dei file, modificare lo stato di un database, inviare una mail, ecc. In questo caso l'output potrebbe anche essere semplicemente un indicazione dell'esito dell'operazione, ad esempio un booleano, oppure una stringa che indichi il problema rilevato in caso di errore.
- Informativi: in questo caso il tool si occupa di cercare dei dati e di iniettarli nel contesto. A seconda della natura del dato potrebbe essere sufficiente restituirlo sotto forma di stringa o in formato strutturato.
MCP Connect si occupa di questo in maniera trasparente quindi in caso la funzione restituisca un oggetto complesso automaticamente lo convertirà nel formato appropriato.
C'è però un caso particolare che vale la pena accennare ed è la classe TContentList. Questa classe mappa abbastanza a basso livello l'output previsto da MCP. In questo caso la risposta può essere composta da varie sezioni, ognuna di tipo diverso (text, image, audio, link, blob). MCPConnect permette di semplificare l'utilizzo di questa classe tramite TToolResultBuilder come è possibile vedere nell'esempio seguente:
function TDelphiDayTool.BuyTicket(AId, AQuantity: Integer): TContentList;
begin
var LTicketStream := FCart.BuyTicket(AId, AQuantity);
FGC.Add(LTicketStream); // Add the stream to the garbage collector
var LResultBuilder := TToolResultBuilder.CreateInstance;
LResultBuilder.AddText('Purchase completed successfully.');
LResultBuilder.AddImage('image/png', LTicketStream);
Result := LResultBuilder.Build;
end;
Gestione della sessione
Una funzione essenziale di MCP è la gestione delle sessioni, che sono implicite con il trasporto stdio e esplicite se si usa Streamable HTTP. MCP Connect gestisce tutto questo in maniera trasparente e in qualsiasi tool sarà possibile andare a recuperare i dati dalla sessione corrente. A seconda della configurazione la sessione potrà essere una istanza dell'oggetto TSessionData (che concretamente salva le informazioni in un JSON) o da un qualsiasi oggetto che derivi da TSessionBase.
Il seguente codice mostra come configurare un'applicazione per usare una sessione custom (TShoppingSession) per salvare le informazioni necessarie durante il processo di creazione di un ordine.
uses
...
MCPConnect.Configuration.Session;
begin
...
FJRPCServer
.Plugin.Configure<ISessionConfig>
.SetLocation(TSessionIdLocation.Header) // default
.SetHeaderName('Mcp-Session-Id') // default
.SetTimeout(30) // 30 minutes timeout (default)
.SetSessionClass(TShoppingSession) // Use custom typed session
.ApplyConfig
Conclusione
MCP Connect è una libreria giovane ma già piuttosto vasta. In questo articolo ho trattato in maniera rapida alcuni degli argomenti principali senza entrare troppo nel dettaglio. Una documentazione più esaustiva può essere trovata nella pagina GitHub. Inoltre i demo presenti nei sorgenti presentano numerosi casi da cui è possibile imparare alcune tecniche per creare semplici server MCP.
Altre caratteristiche di MCP Connect di cui non ho parlato ma penso di scrivere qualcosa quanto prima sono:
- Context injection
- Fluent-configuration
- Garbage collection
- Tools namespacing
- JSON-RPC only server (no MCP)
- VCL/Firemokey support

Top comments (0)