DEV Community

Cover image for MCP Connect
Luca Minuti
Luca Minuti

Posted on

MCP Connect

Read this article in English

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

Architettura

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;

Enter fullscreen mode Exit fullscreen mode

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:

  1. 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.
  2. 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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)