Table of Contents

Getting started with Prompts

This page covers the shortest path to a working prompt: what a prompt is, the two ways to declare one, and how a client sees it. For the full feature set — arguments and defaults, message roles, titles and icons, runtime listing, and completion — 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.Prompts to your uses clause. System.Rtti is also needed, because a handler receives its arguments as an array of TValue.

Declare a prompt in one call

TTMSMCPPrompts.RegisterPrompt is the shortest route. It takes a name, a description, and the handler that builds the conversation:

procedure TForm1.RegisterSummarizePrompt;
var
  Arg: TTMSMCPPromptArgument;
begin
  { Server is a TTMSMCPServer dropped on the form. }
  Server.Prompts.RegisterPrompt('summarize_ticket',
    'Asks the model to summarize a support ticket in three sentences',
    function(const Args: array of TValue): TTMSMCPPromptMessages
    begin
      Result := TTMSMCPPromptMessages.Create(nil);
      Result.AddUserMessage(Format(
        'Summarize support ticket %s in three sentences.', [Args[0].AsString]));
    end);

  { RegisterPrompt declares no arguments, so declare them separately -
    the client needs them to build the prompts/get call. }
  Arg := Server.Prompts.FindByName('summarize_ticket').Arguments.Add;
  Arg.Name := 'ticket_id';
  Arg.Description := 'Identifier of the ticket to summarize';
  Arg.Required := True;
end;

The description matters more than it looks. A client shows it next to the prompt in its palette, and it is also returned with the messages, so write it for a reader who cannot see your code.

Note the second half of that snippet. RegisterPrompt declares no arguments, so the argument the handler reads as Args[0] has to be declared separately — the client has nothing to ask the user for otherwise.

Declare a prompt with the builder

For anything beyond one argument, the fluent builder keeps the whole declaration in one expression — the prompt, its handler, and its arguments:

procedure TForm1.RegisterReviewUnitPrompt;
var
  Prompt: TTMSMCPPrompt;
begin
  Prompt := TTMSMCPPrompt.CreateBuilder
    .Name('review_unit')
    .Title('Review a unit')
    .Description('Asks the model to review one source unit for a named concern')
    .Handler(
      function(const Args: array of TValue): TTMSMCPPromptMessages
      begin
        Result := TTMSMCPPromptMessages.Create(nil);
        Result.AddUserMessage(Format(
          'Review the unit %s. Focus on %s and list findings as a numbered list.',
          [Args[0].AsString, Args[1].AsString]));
      end)
    .AddArgument
      .Name('unit_name')
      .Description('Unit to review, for example TMS.MCP.Prompts.pas')
      .Required(True)
      .&End
    .AddArgument
      .Name('focus')
      .Description('What to concentrate on, for example memory management')
      .Required(False)
      .&End
    .Build;

  { AddPrompt copies the declaration into the server collection instead of
    reparenting the instance, so do not free the prompt you built. }
  Server.Prompts.AddPrompt(Prompt);
end;

TTMSMCPPrompt.CreateBuilder returns a builder class; every call returns the builder again, so the chain reads top to bottom. AddArgument switches to the argument builder, and .&End switches back. &End needs the ampersand because End is a reserved word.

Ownership

TTMSMCPPrompts.AddPrompt copies the declaration into a new item in the collection rather than reparenting the instance you pass. The prompt returned by Build is therefore never owned by the server's collection — do not free it after adding.

The collection the handler returns works the other way: create it with TTMSMCPPromptMessages.Create(nil), fill it, return it, and let the framework free it. Do not free it in the handler.

Run it

Start the server and connect any MCP client. The prompt appears in the client's prompt list with the name, title, and description you declared, and each argument you added becomes a field the user fills in. Selecting it calls prompts/get, and the messages your handler returns are inserted into the conversation in order.

See also