Table of Contents

Getting started with Attributes

This page covers the shortest path from a plain Delphi class to a running MCP server: which units to add, which methods RTTI can see, the one attribute that matters, and how to host the result. For the full attribute vocabulary, the schema rules, and target management, see the user guide.

Prerequisites

  • Delphi 11 Alexandria or newer (or C++Builder) with TMS AI Studio installed.
  • A VCL, FMX, console, or service application. The server is non-visual.
  • An MCP client to talk to. A desktop AI assistant that launches your executable over STDIO is the quickest way to see it work.

Add TMS.MCP.Attributes to the unit that declares your service class, and TMS.MCP.Attributes.Server to the unit that creates the server. You do not need TMS.MCP.Tools unless you also register tools by hand.

Write a plain class

Nothing about the class is special. It does not descend from a framework base class, it holds whatever state it likes, and its methods have ordinary Delphi signatures. The only requirement is that the methods you want to expose are visible to RTTI — that means public or published, because the default {$RTTI} setting emits method information for those two visibilities only. A private or protected method is invisible to the reflection pass no matter how you decorate it.

unit CatalogService;

interface

uses
  TMS.MCP.Attributes;

type
  TCatalogService = class
  public
    [TTMSMCPToolAttribute]
    [TTMSMCPNameAttribute('lookup_price')]
    [TTMSMCPDescriptionAttribute('Returns the current unit price of one catalog article')]
    [TTMSMCPReadOnlyAttribute]
    function LookupPrice(const ASku: string): Double;

    [TTMSMCPToolAttribute]
    [TTMSMCPNameAttribute('restock')]
    [TTMSMCPDescriptionAttribute('Adds units to the stock level of one catalog article')]
    procedure Restock(const ASku: string; AUnits: Integer);
  end;

implementation

uses
  System.SysUtils;

function TCatalogService.LookupPrice(const ASku: string): Double;
begin
  Result := CatalogDatabase.PriceOf(ASku);
end;

procedure TCatalogService.Restock(const ASku: string; AUnits: Integer);
begin
  if AUnits <= 0 then
    raise Exception.Create('AUnits must be greater than zero');

  CatalogDatabase.AddStock(ASku, AUnits);
end;

end.

[TTMSMCPToolAttribute] is the only attribute that is strictly required; every other one refines what the generated tool looks like. Without a [TTMSMCPNameAttribute] the tool is named <ClassName>_<MethodName>TCatalogService_LookupPrice in the example above — which works but reads poorly in a client, so name your tools explicitly.

Delphi lets you drop the Attribute suffix in an attribute reference, so [TTMSMCPNameAttribute('restock')] may also be written [TTMSMCPName('restock')]. These pages use the full class name throughout: it matches the API reference, and it keeps [TTMSMCPToolAttribute] distinct from the TTMSMCPTool class that the attribute generates.

Host it

TTMSMCPServerFactory creates a TTMSMCPAttributedServer and registers the target in one call. By the time it returns, the server already carries one tool per attributed method:

program CatalogServer;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  TMS.MCP.Attributes.Server,
  TMS.MCP.Transport.STDIO,
  CatalogService in 'CatalogService.pas';

var
  Service: TCatalogService;
  Server: TTMSMCPAttributedServer;
begin
  Service := TCatalogService.Create;
  try
    { CreateFromObject reflects over the class straight away, so the server
      already carries one tool per attributed method when it returns. }
    Server := TTMSMCPServerFactory.CreateFromObject(Service);
    try
      Server.ServerName := 'Catalog';
      Server.ServerVersion := '1.0.0';

      Server.Start;
      Server.Run;
    finally
      Server.Free;
    end;
  finally
    { The factory does not take ownership of the service instance. }
    Service.Free;
  end;
end.

With no Transport assigned the server uses STDIO, which is what a desktop assistant expects when it launches your executable. Run blocks and processes messages until the transport closes. See MCP Transports for the alternatives.

Ownership

The factory does not take ownership of the instance you pass it. The same is true of TTMSMCPAttributedServer.AddObject. To hand an instance over, set OwnsObject on the returned TTMSMCPTarget — the target then frees it when the server is destroyed. Decide once, per target, and do not free it yourself as well.

What the client sees

The client lists lookup_price and restock, each with the description you wrote, and an input schema derived from the method parameters: ASku as a string, AUnits as an integer, both required. lookup_price is advertised as read-only, so a client may call it without asking the user first. Nothing else is required to make the class reachable by an agent.

See also