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:
- One verb per tool. "get_weather" is better than "weather_operations" with a sub-action parameter. The model picks the right tool faster when each name describes exactly one outcome.
- Be ruthless with descriptions. The schema description is your only chance to teach the model what the tool does and does not do. Spell out edge cases, units, formats, and prerequisites.
- Default everything you can. Optional parameters with sensible defaults let the model call your tool successfully even when it only knows half the context.
- Return small, focused payloads. A 50-line text response is fine; a 5,000-line dump bloats the context window and the bill. Summarise on the server side.
- Fail explicitly. Return a structured error message ("City not recognised. Did you mean Madrid or Madras?") instead of silently returning empty results.
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:
- Authentication: stdio inherits OS-level trust, but HTTP needs bearer tokens or mTLS. Use
OnHTTPAuthenticateto validate. - Rate limiting: a chatty model can fire tool calls in tight loops. Pair the MCP server with
TsgcWebSocketHTTPServer_RateLimiteror your own quota logic. - Logging: wire
OnLogto capture every tool call. You will need this when a user reports "Claude did something weird". - Schema discipline: the more accurate your JSON schemas (descriptions, enums, examples), the better the model picks the right tool with the right arguments.
- Idempotency: tools may be called twice if the model retries. Mark write operations idempotent or add a transaction ID parameter.
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:
- Stderr is your console. In stdio mode, stdout belongs to JSON-RPC. Anything you write to it breaks the protocol. Log to stderr (which Claude Desktop captures into its log files) or to a side log file with a process-id suffix.
- Use the MCP Inspector. Anthropic ships a free tool (
npx @modelcontextprotocol/inspector) that lets you point a UI at your server, click each tool, see the raw request/response, and validate schemas without involving an AI client at all. - Add a "diagnostic" tool. A simple
server_infotool that returns the build date, host, and version makes it trivial to confirm in the AI client that you are talking to the right server. We add this to every production MCP server.
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.