MCP support lands in MARS-Curiosity
If you have been watching the AI agent space, you have probably come across MCP — the Model Context Protocol, the open standard that lets AI agents like Claude, ChatGPT and locally-run models discover and call tools exposed by a server. Hundreds of MCP servers already exist for filesystems, GitHub, databases, SaaS products of every kind.
As of today, you can add “any Delphi application built with MARS” to that list.
I have just landed native MCP support in MARS-Curiosity (on the develop and master branches): a set of MARS.MCP.* units that turn a MARS resource into a fully compliant MCP server over the Streamable HTTP transport — JSON-RPC 2.0, protocol version negotiation, tool discovery, the whole thing. And in true MARS spirit, you write none of the protocol code.
Tools are just methods
Here is a complete MCP server:
type
TCalculationResult = record
operation: string;
value: Double;
end;
[Path('mcp')
, MCPServerInfo('My MCP Server', '1.0.0'
, 'Instructions the AI agent reads on connection.')]
TMyMCPResource = class(TMCPResource)
public
[MCPTool('say_hello', 'Returns a friendly greeting for the given name')]
function SayHello(
[MCPParam('name', 'Name of the person to greet')] const AName: string): string;
[MCPTool('add_numbers', 'Adds two numbers and returns a structured result')]
function AddNumbers(
[MCPParam('a', 'First operand')] const A: Double;
[MCPParam('b', 'Second operand')] const B: Double): TCalculationResult;
end;
That is it. Register the resource as usual and point an MCP client at http://localhost:8080/rest/default/mcp. The tool list — including the JSON Schema describing every parameter — is generated from RTTI: strings, numbers, booleans, enumerations, TDateTime, dynamic arrays and nested records all map to the right schema types. Return a record and the agent receives it as structuredContent; raise an exception and it becomes a proper tool error the model can reason about.
Ask Claude to “use add_numbers to sum 39.5 and 2.5” and your Delphi method runs. (The answer is 42, of course.)
The part your customers actually care about: the database
Exposing arithmetic is cute; exposing your application’s data is the point. Derive from TMCPDataResource and your tools can return a TFDQuery directly, with the injected TMARSFireDAC you already know:
[MCPTool('find_employees', 'Finds employees whose name contains the given text')]
function FindEmployees(
[MCPParam('nameContains', 'Text to search for')] const AText: string): TFDQuery;
function TMyDBMCPResource.FindEmployees(const AText: string): TFDQuery;
begin
Result := FD.Query(
'select ID, NAME, ROLE, SALARY from EMPLOYEES where upper(NAME) like upper(:TXT)'
, nil, True
, procedure (AQuery: TFDQuery)
begin
AQuery.ParamByName('TXT').AsString := '%' + AText + '%';
end);
end;
Rows come back to the agent as { rowCount, rows: [...] }. Suddenly “ask your ERP a question in plain language” is an afternoon of work, not a migration project.
Security: from roles to a real OAuth login window
This is where I think MARS has something genuinely nice to offer.
Per-tool authorization. Put the MARS attributes you already use on individual tool methods: a tool marked [RolesAllowed('admin')] is invisible to agents whose token lacks the role — it does not appear in tools/list, and calling it answers as if it never existed. Different users, different tool sets, zero extra code.
Bearer tokens. A MARS MCP endpoint accepts the standard MARS JWT, issued by the token resource you already have. Perfect for scripts, testing, server-to-server.
OAuth 2.1, self-contained. Consumer clients — Claude connectors, ChatGPT, Open WebUI — expect the full MCP authorization flow: automatic discovery, dynamic client registration, a browser login window, silent token refresh. The new MARS.MCP.OAuth unit implements a complete OAuth 2.1 authorization server (authorization code + PKCE, refresh token rotation, RFC 7591/8414/9728) in pure Delphi, no external dependencies. You override one method — Authenticate — and your users get the same “sign in and authorize” experience they know from connecting Gmail or Notion. The access token it issues is a MARS JWT, so both authentication modes coexist on the same endpoint and per-tool role filtering applies to everyone.
Tested for real
I did not want this to be a “works in curl” feature. The implementation has been verified end-to-end with the official MCP Inspector, with Claude Code, and — my favourite setup — with Open WebUI and a local qwen3 model on Ollama calling FireDAC tools on a MARS server running in a Windows VM, including the full OAuth dance with the login page served by MARS itself. The core suite gained about 60 new DUnitX tests along the way, covering the dispatcher, schema generation, authorization filtering and the complete OAuth flow.
Where to find everything
- Units:
Source/MARS.MCP.pas,MARS.MCP.Attributes.pas,MARS.MCP.Resource.pas,MARS.MCP.Data.pas,MARS.MCP.OAuth.pas(branchdevelop) - Demo:
Demos/MCPServer— public tools, authenticated FireDAC tools on SQLite, token resource and OAuth server, ready to run - Docs: the new MCP Servers page in the MARS documentation
- Agent Skill: the MARS repo doubles as a Claude plugin marketplace and now includes a
mars-mcp-serverskill, so Claude Code can guide you (or do it for you) when building your own MCP server with MARS. Not a Claude Code user?npx skills add andrea-magni/MARSinstalls the same skills into any SKILL.md-compatible tool.
On the roadmap: MCP resources/* and prompts/*, server-initiated SSE streams, sessions, and JWKS validation for external identity providers.
If you try it, I would love to hear what you connect it to — the Delphi-Praxis forum or the GitHub issues are the right places. Delphi applications hold an enormous amount of valuable business logic and data; MCP is how AI agents get to use it, and now it is a first-class citizen in MARS.
Stay tuned for more features coming to MARS 🙂
Andrea