Delphi MCP 全栈工具包

MCP for Delphi 是一个面向 Delphi 与 DMVCFramework 的 MCP 全栈工具包,集服务端、客户端、智能体及 REST→MCP 桥接于一体,使开发者能够向 AI 助手暴露 Delphi 工具、从 Delphi 代码调用任意 MCP 服务,并借助 OpenAI 兼容模型构建 AI 驱动的后台与本地智能体应用。

  • by danieleteti
  • Pascal
  • 本地部署
  • 31 星标
  • 更新于
在 GitHub 查看 danieleteti/mcp-server-delphi

使用说明

MCP for Delphi

Server · Client · Agent — a full-stack MCP toolkit for DMVCFramework

A production-ready Model Context Protocol (MCP) toolkit for Delphi. It is not just a server library — it gives Delphi the complete MCP triangle in one package.

📖 Full documentation & guide → www.danieleteti.it/mcp-server-delphi

This README is just a hook. Every detail — getting started, API reference, the client, the agent, the REST→MCP bridge, configuration and testing — lives in the official documentation.


What is this?

Most "MCP for X" libraries only let you build a server the AI calls into. This one gives Delphi all three corners of the MCP world:

Role What it does
🟢 Server Expose your Delphi tools, resources, and prompts to AI assistants (Claude, Gemini, ChatGPT, …) over HTTP or stdio — attribute-driven, zero boilerplate.
🔵 Client Consume any spec-compliant MCP server from your own Delphi code with TMCPClient (Streamable HTTP) or TMCPStdioClient (spawns a server as a child process and talks over pipes).
🟣 Agent Drive an MCP server from an LLM with TMCPOpenAIAgent — a complete agent loop (tool discovery → LLM round-trips → tool dispatch → token accounting) that works with any OpenAI-compatible model: OpenAI, OpenRouter, Anthropic-compat, Together, Groq, Ollama, vLLM, llama.cpp --api.
🌉 REST → MCP Bridge Auto-expose an existing DMVCFramework REST API as MCP tools by scanning the engine's routes via RTTI. Make your current backend AI-ready without writing a single tool by hand.

In short: build AI-powered Delphi applications where Delphi is the server, the client, and the agent.

🚀 MCP with DMVCFramework? Sure — even for on-premises AI engines.

Expose your ERP and let a Delphi-side agent chain your tools. Ask: "Reorder customer C-1024's best-selling product from last March — but only if they're within their credit limit." The agent finds March's top product, reads the customer's balance, decides, and drafts the invoice — several tool calls, planned on the fly, all against your data. 💡

Features at a glance

  • MCP Protocol 2026-07-28 (current) and 2025-03-26, served together on the same endpoint and on stdio: the request decides the era (see Protocol conformance)
  • Server, client AND agent in one library — plus a REST→MCP bridge
  • Attribute-driven tool/resource/prompt registration using RTTI — no manual wiring
  • Dual transport, server and client side: Streamable HTTP and stdio
  • Type-safe parameter binding with automatic JSON Schema generation
  • Rich content types: Text, Image, Audio, Embedded Resources — with a fluent multi-content API
  • URI resource templates (RFC 6570 Level 1)
  • Session management with automatic cleanup
  • DMVCFramework integration via the idiomatic PublishObject pattern
  • Apache 2.0 — free for commercial and personal use

A 30-second taste

Expose a tool (server):

[MCPTool('reverse_string', 'Reverses a string')]
function ReverseString(
  [MCPParam('The string to reverse')] const Value: string
): TMCPToolResult;

Call a server (client):

LClient := TMCPClient.Create('http://localhost:8080/mcp');
LClient.Initialize;
WriteLn(LClient.CallTool('reverse_string',
  TJSONObject.Create.AddPair('Value', 'hello')));

Drive a server from an LLM (agent):

LAgent := TMCPOpenAIAgent.Create('http://localhost:8080/mcp', LApiKey, 'gpt-4o-mini');
LAgent.SystemPrompt := 'You are a helpful Delphi-powered assistant.';
LResult := LAgent.Run(LUserMessages);   // tool discovery + LLM loop + dispatch
WriteLn(LResult.Content);

👉 Full, runnable examples and the complete API are in the documentation.

Quick Start

Copy a ready-to-run Quick Start project and customize it — all share the same provider units in quickstart/shared/:

Project Role Transport Use when
quickstart/quickstart/ Server HTTP + stdio You want a network server AI clients connect to via HTTP
quickstart/quickstart_stdio/ Server stdio only You want the AI client (e.g. Claude Desktop) to launch the server locally
quickstart/quickstart_stdio_agent/ Agent (host+client) stdio You want a Delphi-side AI agent that spawns and consumes a stdio MCP server, driven by an LLM

Open the .dproj in Delphi, add DMVCFramework and this repo's sources/ to the search path, build, run. Step-by-step instructions, IDE wiring, and how to connect Claude Desktop / Gemini CLI / Claude Code / Continue are all in the documentation.

Protocol conformance

The library is dual-era, as the specification allows (Versioning and Compatibility, "Dual-era"):

Era Spec How a client opens Sessions
Modern 2026-07-28 (current) Any request whose params._meta carries io.modelcontextprotocol/protocolVersion. server/discover is optional. None. Mcp-Session-Id is ignored and never sent
Legacy 2025-03-26 initialize, then notifications/initialized Mcp-Session-Id, as before

Existing clients and servers keep working with no change: a request with no version in _meta is a legacy request, and TMCPClient, TMCPStdioClient and TMCPOpenAIAgent speak the legacy era unless told otherwise.

Implemented from 2026-07-28: per-request _meta (version, client info, client capabilities); server/discover; resultType and the server info in every result; ttlMs and cacheScope on the lists and on server/discover (ListCacheTTLms, 60 s by default; private for an authenticated caller); tools listed in a deterministic order; the MCP-Protocol-Version, Mcp-Method and Mcp-Name headers validated against the body (400 and -32020, =?base64?...?= decoded); UnsupportedProtocolVersionError (400 and -32022, with supported and requested); -32602 for a missing capabilities field, an unknown tool and a resource not found; 404 and -32601 for an unknown method. Authentication does not change: the bearer-token filter (static token or JWT validator) stands in front of both eras and the tools see the same Caller.

Implemented from 2025-06-18 and later: tool title and annotations ([MCPToolTitle], [MCPReadOnly], [MCPNonDestructive], [MCPIdempotent], [MCPClosedWorld]).

Client: Era := mpeModern or mpeAuto on TMCPClient, TMCPStdioClient and TMCPOpenAIAgent.MCPEra. mpeAuto tries the modern era and falls back to the handshake when the server does not know it. TMCPClient.BearerToken (agent: MCPBearerToken) sends the identity of the user.

Not implemented (none is needed for a server made of tools, resources and prompts):

  • subscriptions/listen, resources/subscribe and the list-changed notifications: the registry is fixed once the server serves.
  • Multi round-trip requests (input_required), and with them elicitation, sampling and roots. Sampling, roots and logging are deprecated by the specification.
  • The tasks extension, structuredContent and outputSchema, pagination (nextCursor), completion/complete, icons, x-mcp-header.
  • The OAuth 2.1 authorization flow and the protected-resource metadata. Use the DMVC bearer-token filter (static token, or a JWT validator).
  • The HTTP+SSE transport of 2024-11-05 (deprecated).

Protecting the endpoint

Two layers.

The endpoint's own rules, always on. A request that carries an Origin not listed in TMCPServer.Instance.AllowedOrigins gets 403 (the protocol asks the server to validate it; the default list is empty, and native clients send no Origin). Only a POST is executed: the JSON-RPC publisher answers any other verb itself (a GET /mcp gets 200 with an "Invalid Request" error and runs nothing), and the endpoint's hook refuses a non-POST with 405 as a backstop. For a clean 405 on those routes mount AllowedMethodsFilter. A refusal is logged as a warning that names the refused Origin (or verb), never as an error.

Authentication and the rest, optional. They are the DMVC request guards of MVCFramework.Filters, registered with UseHTTPFilter before PublishObject and before any classic middleware, all given the same path:

uses MVCFramework.Filters, MVCFramework.MCP.Types;

TMCPServer.Instance.AllowedOrigins := ['http://localhost:5173'];   // only if a browser page must call it
AEngine.UseHTTPFilter(BearerAuthFilter(StaticBearerTokens([TPair<string, string>.Create('cli', LToken)]), MCP_ENDPOINT));
AEngine.UseHTTPFilter(AllowedMethodsFilter([httpPOST, httpDELETE], MCP_ENDPOINT));

A private server on localhost with no token can run AllowedHostFilter([], MCP_ENDPOINT) instead of the bearer filter (403 unless the Host is localhost, 127.0.0.1 or [::1]: it stops DNS rebinding), or no filter at all. AllowedMethodsFilter limits the publisher's own GET <path>, /describe and /proxy, which never reach the endpoint. For a JWT, give BearerAuthFilter a validator that checks the token (see JWTBearerValidator in tests/testproject/TestAuthHandlerU.pas) and keep the JWT middleware only for /login.

Engine path prefix. The path given to every filter is the full routed path: under the prefix /api, /api/mcp, not /mcp. Use one constant everywhere. A filter given another path matches nothing and fails silently open: POST /api/mcp, the session DELETE and the catalogue at GET /api/mcp/describe are then served without a token. Nothing backs the filters up.

Providers read the request through Context (the TWebContext: LoggedUser, Session, headers), HasContext and Transport; on stdio there is no context and reading Context raises EMCPContextNotAvailable.

Testing ✅

Tested by six compliance suites: a Python HTTP suite (200 cases), a Python stdio suite (160 cases), a Python suite for the 2026-07-28 era (69 cases, over HTTP and stdio), a Python security suite (218 cases: the REST-to-MCP bridge filters, the endpoint's own rules and the DMVC request guards, the caller identity and a JWT middleware in front of /mcp; it starts the test server itself, once per configuration), a TMCPClient suite (21 Delphi cases, run over HTTP and stdio, in the legacy, modern and auto eras), and a TMCPOpenAIAgent suite (32 Delphi cases driving the agent loop, and the SSE stream reassembly, against an embedded fake LLM). The stdio suite also runs against any other stdio MCP server with --server-independent (70 cases against the test server; it skips the cases that name a tool, resource or prompt of the library's own test server). See the documentation for how to build and run them.

Requirements

  • Delphi 11+ (Alexandria) or later
  • DMVCFramework 3.5.x

License

Apache License 2.0 — see LICENSE.

Links


Built with ❤️ by Daniele Teti