Table of Contents

Getting started with Resources

This page covers the shortest path to a readable resource: the two kinds of resource, how to declare one, and what the reader has to return. For the full feature set — template matching, binary content, runtime listing, subscriptions, and change notifications — see the user guide.

Prerequisites

  • Delphi 11 Alexandria or newer (or C++Builder) with TMS AI Studio installed.
  • A VCL or FMX application with a TTMSMCPServer on a form, or created in code.
  • A transport assigned to the server so a client can reach it. See MCP Transports; STDIO is the simplest for a first test.

Add TMS.MCP.Resources to your uses clause. Add System.NetEncoding too when a resource returns binary content, since a blob travels as base64.

Declare a resource at a fixed URI

TTMSMCPResources.RegisterDirectResource is the shortest route. It takes a name, the URI, a description, a MIME type, and the reader that produces the content:

procedure TForm1.RegisterSettingsResource;
begin
  { Server is a TTMSMCPServer dropped on the form. }
  Server.Resources.RegisterDirectResource(
    'app_settings',
    'config://application/settings',
    'Current application settings',
    'application/json',
    function(const URI: string): TTMSMCPResourceContent
    begin
      { Create a fresh content object per read. The framework serializes
        it and frees it - do not free it here and do not cache it. }
      Result := TTMSMCPResourceContent.FromText(URI, 'application/json',
        SettingsAsJSON);
    end);
end;

The URI scheme is yours to choose — config://, crm://, doc://. MCP does not require an HTTP URL, and a scheme that names your own domain is clearer about what the content is.

Declare a family of URIs with a template

When the same shape of content exists for many keys, declare one template instead of many resources. Placeholders go in curly braces, and the reader receives the concrete URI the client asked for, so the key is read back out of it:

procedure TForm1.RegisterCustomerResource;
begin
  Server.Resources.RegisterTemplateResource(
    'customer_profile',
    'crm://customers/{id}/profile',
    'Profile of one customer, by customer id',
    'application/json',
    function(const URI: string): TTMSMCPResourceContent
    var
      Parts: TArray<string>;
      CustomerId: string;
    begin
      { The reader receives the URI the client actually asked for, not the
        template, so the placeholder value is read back from it.
        crm://customers/42/profile splits into
        ['crm:', '', 'customers', '42', 'profile']. }
      Parts := URI.Split(['/']);
      if Length(Parts) < 2 then
        raise Exception.CreateFmt('Malformed customer URI: %s', [URI]);

      CustomerId := Parts[High(Parts) - 1];

      Result := TTMSMCPResourceContent.FromText(URI, 'application/json',
        LoadCustomerJSON(CustomerId));
    end);
end;

Matching is segment-based: the template and the requested URI must split into the same number of /-separated parts, so a placeholder never spans a /.

What a reader returns

A reader returns a TTMSMCPResourceContent built with FromText or FromBlob. Three rules govern it:

  • Build a fresh object on every read. The framework serializes it and frees it, so a cached instance is freed out from under you.
  • Do not free it in the reader. Ownership passes to the framework when the function returns.
  • Raise on failure rather than returning nil. The exception message reaches the client; a nil result only produces a generic error.

Ownership when you use the builder

TTMSMCPResource.CreateBuilder.Build hands the instance to you, and TTMSMCPResources.AddResource copies the declaration into the collection. So free the resource you built after adding it — the convenience registrations do exactly this internally, which is why they leave nothing to clean up.

Run it

Start the server and connect any MCP client. Direct resources appear in resources/list and templates in resources/templates/list, each with the name, title, description, and MIME type you declared. Reading a URI calls your reader and returns its content.

See also