Building a Model Context Protocol (MCP) Server in Delphi

· Components

What Is the Model Context Protocol?

The Model Context Protocol (MCP) is Anthropic's open standard for exposing tools, data, and prompt templates to AI models. Think of it as USB for AI: any MCP server can be plugged into any MCP client (Claude Desktop, Cursor, Continue, your own Delphi app, OpenAI's clients), and the model immediately discovers what tools are available and how to call them.

Before MCP, every AI integration meant wiring tool definitions, JSON schemas, and call dispatchers into each individual app. MCP collapses that into a single protocol: write the server once, every model can use it. For Delphi shops that already have a stack of business APIs — ERP queries, CRM lookups, file generators, billing functions — MCP is the cheapest path to making those APIs AI-accessible.

This tutorial walks through building an MCP server in Delphi using the TsgcAI_MCP_Server component, exposing a working get_weather tool, and connecting it to Claude Desktop over both stdio and HTTP transports.

A bit of history puts MCP in context. Anthropic published the spec in late 2024 as an answer to the proliferation of incompatible tool-calling formats — OpenAI's function-calling JSON, Google's Gemini grounding tools, every vendor's bespoke schema. By the time you read this, MCP support is shipping in Claude Desktop, Cursor, Continue, Zed, and most modern AI IDEs; OpenAI announced compatibility in early 2026. In other words: building an MCP server today is a one-time investment that pays out across every client your customers might use, both today and tomorrow.

MCP Building Blocks

An MCP server can expose three kinds of capabilities:

Tools

Callable functions with typed inputs. The model decides when to invoke them. Example: get_weather(city), create_invoice(customer, lines).
Resources

Read-only data the model can fetch. Each resource has a URI. Example: file:///docs/handbook.md, db://customers/12345.
Prompts

Reusable prompt templates the user (not the model) selects. Example: a "Translate to formal Spanish" or "Summarise meeting notes" template.

The protocol uses JSON-RPC 2.0 over one of two transports: stdio (the server is a child process; messages flow over stdin/stdout) or HTTP with Server-Sent Events for the server-to-client stream. Stdio is the default for desktop integrations like Claude Desktop; HTTP is the right choice when the server runs on a different machine or serves multiple clients.

The component hides both transports behind a single API. You write your tool handler once, then pick the transport at startup. Same code runs as a Claude Desktop helper, a network-reachable HTTP service, or both at the same time inside one process. We have shops running 30+ MCP servers on a single box, multiplexed over different ports and one or two stdio binaries, all serving the same Delphi codebase.

Project Setup

Add sgcAI_MCP_Server to your uses clause, drop a TsgcAI_MCP_Server on the form (or create it in code for a console app), wire the events, and start it. The component does the JSON-RPC framing, message routing, and capability advertisement for you.

uses
  sgcAI_MCP_Server, sgcAI_MCP_Classes;

procedure TForm1.FormCreate(Sender: TObject);
begin
  oMCP := TsgcAI_MCP_Server.Create(Self);
  oMCP.ServerInfo.Name    := 'weather-mcp';
  oMCP.ServerInfo.Version := '1.0.0';

  // Wire handlers
  oMCP.OnListTools := DoListTools;
  oMCP.OnCallTool  := DoCallTool;

  // Stdio transport for Claude Desktop integration
  oMCP.Transport := mtStdio;
  oMCP.Active    := True;
end;

Implementing a Tool: get_weather

A tool needs two handlers. OnListTools tells the client which tools exist and what arguments they accept. OnCallTool actually runs the call. Both handlers are plain Delphi events — no manual JSON construction.

procedure TForm1.DoListTools(Sender: TObject;
  const aTools: TsgcAI_MCP_Tools);
var
  oTool: TsgcAI_MCP_Tool;
begin
  oTool := aTools.Add;
  oTool.Name        := 'get_weather';
  oTool.Description := 'Return the current weather for a city.';
  oTool.InputSchema :=
    '{"type":"object",' +
     '"properties":{' +
       '"city":{"type":"string","description":"City name, e.g. Madrid"},' +
       '"units":{"type":"string","enum":["metric","imperial"],"default":"metric"}' +
     '},' +
     '"required":["city"]}';
end;

procedure TForm1.DoCallTool(Sender: TObject;
  const aRequest : TsgcAI_MCP_ToolCall;
  const aResult  : TsgcAI_MCP_ToolResult);
var
  vCity, vUnits, vReport: string;
begin
  if aRequest.Name = 'get_weather' then
  begin
    vCity  := aRequest.Arguments.S['city'];
    vUnits := aRequest.Arguments.S['units'];
    if vUnits = '' then vUnits := 'metric';

    // Call your own weather backend
    vReport := MyWeatherService.Fetch(vCity, vUnits);
    aResult.AddText(vReport);
  end
  else
    aResult.Error('Unknown tool: ' + aRequest.Name);
end;

That is the full implementation. The component takes care of input validation against the schema, error formatting, and the underlying JSON-RPC envelope. You focus on the business logic.

A few principles for designing good tools, learned the hard way:

Exposing Resources

Tools are for actions; resources are for data the model can read. A resource handler returns the content for a given URI. A documentation server might expose every Markdown file under docs/ as a resource.

oMCP.OnListResources := DoListResources;
oMCP.OnReadResource  := DoReadResource;

procedure TForm1.DoListResources(Sender: TObject;
  const aResources: TsgcAI_MCP_Resources);
var
  vFiles: TStringDynArray;
  i     : Integer;
  oRes  : TsgcAI_MCP_Resource;
begin
  vFiles := TDirectory.GetFiles('C:\docs', '*.md');
  for i := 0 to High(vFiles) do
  begin
    oRes := aResources.Add;
    oRes.URI         := 'file:///' + StringReplace(vFiles[i], '\', '/', [rfReplaceAll]);
    oRes.Name        := ExtractFileName(vFiles[i]);
    oRes.MimeType    := 'text/markdown';
    oRes.Description := 'Documentation page';
  end;
end;

procedure TForm1.DoReadResource(Sender: TObject;
  const aURI: string; const aResult: TsgcAI_MCP_ResourceResult);
var
  vPath: string;
begin
  vPath := StringReplace(Copy(aURI, 9, MaxInt), '/', '\', [rfReplaceAll]);
  if FileExists(vPath) then
    aResult.AddText(TFile.ReadAllText(vPath))
  else
    aResult.Error('Resource not found');
end;

Stdio vs HTTP Transport

Choose the transport based on where the server runs and who uses it.

Transport Use case Pros Cons
Stdio Local desktop tools (Claude Desktop, Cursor) Zero config, no ports, OS handles process lifecycle One client per process, no remote access
HTTP / SSE Shared corporate servers, web clients, multi-tenant Many clients, network reachable, TLS, auth Requires hosting, port management, auth design

Switching transport is a one-liner:

// Stdio (default for desktop)
oMCP.Transport := mtStdio;

// HTTP listener on port 8080
oMCP.Transport       := mtHTTP;
oMCP.HTTPOptions.Host := '0.0.0.0';
oMCP.HTTPOptions.Port := 8080;
oMCP.HTTPOptions.TLS.Enabled  := True;
oMCP.HTTPOptions.TLS.CertFile := 'cert.pem';
oMCP.HTTPOptions.TLS.KeyFile  := 'key.pem';
oMCP.Active := True;

Connecting from Claude Desktop

For stdio, edit claude_desktop_config.json and add an entry pointing to your compiled .exe. Claude Desktop will spawn the process on demand.

{
  "mcpServers": {
    "weather": {
      "command": "C:\\Tools\\weather-mcp.exe",
      "args": []
    }
  }
}

Restart Claude Desktop and you will see a small tools icon in the prompt area. Ask "What is the weather in Tokyo?" and Claude will call your Delphi tool.

For HTTP, point any MCP-capable client at https://your-host:8080/mcp. The same Delphi server happily handles a Claude Desktop instance, a Cursor session, and a custom Delphi MCP client at the same time.

Prompts: The Underused Third Capability

Tools and resources get all the attention; prompts are the quiet workhorse. An MCP prompt is a reusable template — "summarise this meeting", "extract action items", "translate to formal Spanish" — that the user invokes from a UI menu in their MCP client. The model then runs that prompt against whatever context the user supplies.

For internal tooling this is gold. Your support team gets a curated list of company-blessed prompts inside Claude Desktop. Your sales team gets a "draft a follow-up email" prompt that already knows your tone of voice. None of them have to learn prompt engineering — they just pick the template.

oMCP.OnListPrompts := DoListPrompts;
oMCP.OnGetPrompt   := DoGetPrompt;

procedure TForm1.DoListPrompts(Sender: TObject;
  const aPrompts: TsgcAI_MCP_Prompts);
var
  oPrompt: TsgcAI_MCP_Prompt;
begin
  oPrompt := aPrompts.Add;
  oPrompt.Name        := 'summarise_meeting';
  oPrompt.Description := 'Turn a meeting transcript into bullet decisions and actions.';
  oPrompt.AddArgument('transcript', 'Plain-text transcript', True);
end;

procedure TForm1.DoGetPrompt(Sender: TObject;
  const aRequest: TsgcAI_MCP_PromptRequest;
  const aResult : TsgcAI_MCP_PromptResult);
begin
  if aRequest.Name = 'summarise_meeting' then
    aResult.AddMessage('user',
      'You are a meeting note taker. Read the transcript and produce: ' +
      '(1) a 3-sentence summary, (2) decisions taken, (3) action items ' +
      'with owners and due dates.' + sLineBreak + sLineBreak +
      aRequest.Arguments.S['transcript']);
end;

Production Considerations

A few things to budget for before you ship an MCP server to production:

Debugging Tips

MCP runs invisibly inside the AI client, which makes debugging tricky the first time something goes wrong. Three habits will save you hours:

Wrap-up

MCP turns an internal Delphi API into something every modern AI client can use, with no per-client wiring. The TsgcAI_MCP_Server component handles the JSON-RPC framing, transport, and capability advertisement, leaving you to write the actual tool body. Start with one read-only tool, see how the model uses it, then expand to writes, resources, and prompts.

Our biggest takeaway from rolling out MCP internally: the value is not "Claude can now use our APIs". The value is "anyone in the company can use our APIs through natural language, without learning the API". That changes who can do what, and that is where the actual productivity gain comes from.

Want the matching client? See TsgcAI_MCP_Client for building Delphi apps that consume any MCP server. And if you are new to the AI components, the Getting Started hub walks you through installation in five minutes.