The four shipped tool sets
TMS AI Studio ships four tool sets: logging, file system access, dataset access, and email. Between them they cover the four things an application is most often asked to let a model do — say something, touch the disk, read the data, and send a message — and each one is a single component with no declarations to write. What they do not do is decide policy: none of them restricts a path, a table, or a recipient, and none asks for confirmation before it writes. This guide lists every tool of every set with its exact arguments and what it returns to the model, the component properties each set reads while its tools run, and the ordering constraints that decide whether a conversation succeeds or fails halfway through.
Logging with the logger set
Reach for TTMSMCPCloudAILogger
when the model should record or surface a result rather than only return it —
an audit trail of what an assistant decided, or a message the user sees
immediately. It declares three tools:
| Tool | Arguments | Does |
|---|---|---|
ShowDialog |
result (string, required) |
Shows the text in a message box. |
ShowInConsole |
result (string, required) |
Writes the text to the framework log. |
LogToFile |
result (string, required), logfile (string, optional) |
Appends the text to a file, prefixed with a yyyy-mm-dd hh:nn:ss timestamp. |
LogFileName on the component is the fallback LogToFile uses when the model
omits the optional logfile argument. With neither supplied the tool raises
No log file specified, so set the property even when you expect the model to
name a file:
procedure TForm1.AttachLogger;
begin
FLogger := TTMSMCPCloudAILogger.Create(Self);
{ LogToFile falls back to this name whenever the model omits its optional
logfile argument. Leave it empty and a call without that argument
raises 'No log file specified'. }
FLogger.LogFileName := TPath.Combine(TPath.GetDocumentsPath, 'assistant.log');
FLogger.AI := AI;
end;
procedure TForm1.btnReviewClick(Sender: TObject);
begin
if AI.Busy then
Exit;
{ ShowDialog puts the text in a message box, ShowInConsole writes it to
the framework log, LogToFile appends it with a timestamp. Say which one
you want - the model picks otherwise. }
AI.SystemRole.Text :=
'Review the text for factual errors. Report every finding with the ' +
'LogToFile tool, one call per finding, and show a short summary with ' +
'the ShowDialog tool when you are done.';
AI.Context.Text := memoInput.Lines.Text;
AI.Execute;
end;
ShowDialog is modal, which matters more than it looks: it blocks inside a
tool call, in the middle of a request the model is waiting on. It is useful for
a desktop assistant and unsuitable for anything unattended — a service, a batch
run, or a session the user has walked away from. ShowDialog and
ShowInConsole are compiled in for VCL and FMX applications; in a
console-style build without a UI framework, ShowDialog does nothing while
ShowInConsole and LogToFile keep working.
File system access
TTMSMCPCloudAIFileSystem is
the set to attach when the model has to find something — read a report,
inspect a folder of exports, reorganise files by their contents. It needs no
configuration at all: every tool takes the paths it works on as arguments.
| Tool | Arguments | Returns |
|---|---|---|
GetFiles |
Folder (string, required), Mask (string, optional), Details (boolean, optional) |
The files in one folder. With Details, each entry also carries its size and last-modified date. |
GetFolders |
Folder (string, required), Mask (string, optional) |
The subfolders of one folder. |
ReadTextFile |
Filename (string, required) |
The whole file as text. |
WriteTextFile |
Filename (string, required), Content (string, required) |
Replaces the file's contents. |
AppendTextFile |
Filename (string, required), Content (string, required) |
Adds to the end of an existing file. |
CreateFolder |
Folder (string, required), Foldername (string, required) |
Creates a subfolder inside Folder. |
CopyFile |
Source, Target (strings, required), Overwrite (boolean, optional) |
Copies a file. |
MoveFile |
Source, Target (strings, required), Overwrite (boolean, optional) |
Moves a file. |
Both listing tools search the named folder only — they do not recurse — and an
empty Folder argument resolves to the process's current directory, which is
rarely what anyone meant. The mask is an ordinary file mask (*.log), and the
default is every file.
procedure TForm1.AttachFileSystem;
begin
{ The file system set has no properties to configure: its eight tools
take the folder and file names they work on as arguments. }
FFiles := TTMSMCPCloudAIFileSystem.Create(Self);
FFiles.AI := AI;
end;
procedure TForm1.btnTidyClick(Sender: TObject);
begin
if AI.Busy then
Exit;
{ Nothing in the tool set restricts the paths the model may name, so the
boundary has to come from the prompt - or from disabling the writing
tools outright. }
AI.SystemRole.Text :=
'You may only work inside ' + FWorkRoot + ' and its subfolders. ' +
'Use GetFiles and GetFolders to look around, ReadTextFile to inspect ' +
'a file, and CreateFolder plus MoveFile to reorganise. Never delete ' +
'anything and never leave that folder.';
AI.Context.Text :=
'Group the invoices in this folder into one subfolder per year, ' +
'based on the date inside each file.';
AI.Execute;
end;
WriteTextFile replaces rather than appends, and neither it nor MoveFile
asks before overwriting the target when the model passes Overwrite. When the
model only needs to read, clear Enabled on the five writing tools as shown
in Attaching tool sets —
that is a real boundary, whereas an instruction in the system role is only a
strong suggestion.
Dataset access
TTMSMCPCloudAIDataSet reaches an
open dataset through the standard DataSource property, so it works with any
TDataSet descendant the application already has on a form. Use it when the
answer lives in the data rather than in a document, and when you would
otherwise be writing one hand-declared tool per query.
| Tool | Arguments | Does |
|---|---|---|
GetFields |
— | Returns every field with its name, size, type, and kind. |
GetTableName |
— | Returns the dataset's table name. |
GetRecords |
Fields (string, optional) |
Returns every record. Fields is a comma-separated list that narrows the columns. |
GetRecord |
Fields (string, optional) |
The same, for the current record only. |
First, Last, Next, Previous |
— | Move the active record. |
Move |
Number (number, required) |
Moves the active record forward or backward by that many rows. |
AddRecord |
FieldValues (array of {Name, Value}, required) |
Inserts a record with those field values. |
ModifyRecord |
FieldValues (array of {Name, Value}, required) |
Updates the current record. |
LocateRecord |
FieldValues (array of {Name, Value}, required) |
Positions the dataset on the first matching record, case-insensitively. |
procedure TForm1.AttachDataSetTools;
begin
{ The tools reach the dataset through a TDataSource, not directly. Every
one of them refuses to run while the dataset is closed. }
FDQuery.Open;
FDataTools := TTMSMCPCloudAIDataSet.Create(Self);
FDataTools.DataSource := dsCustomers;
FDataTools.AI := AI;
end;
procedure TForm1.btnAnalyseClick(Sender: TObject);
begin
if AI.Busy then
Exit;
{ GetFields and GetTableName let the model learn the shape of the data
before it asks for rows, so it is worth naming them explicitly. }
AI.SystemRole.Text :=
'Call GetFields first so you know the column names, then use ' +
'GetRecords with a Fields list to fetch only the columns you need. ' +
'Ask before changing any record.';
AI.Context.Text :=
'Which customers in this table have no country set, and what would ' +
'you suggest filling in for each?';
AI.Execute;
end;
procedure TForm1.btnCorrectClick(Sender: TObject);
begin
if AI.Busy then
Exit;
{ LocateRecord positions the dataset, ModifyRecord edits the record that
is current after it. Ordering matters: ask for both in one instruction
so the model does not modify whichever row happened to be active. }
AI.SystemRole.Text :=
'To correct a row, call LocateRecord with the field values that ' +
'identify it, then ModifyRecord with the corrected field values.';
AI.Context.Text := memoInstruction.Lines.Text;
AI.Execute;
end;
Four details decide whether a run behaves. Every tool checks the dataset first
and answers No dataset assigned or dataset not active when DataSource, its
DataSet, or the dataset's Active state is missing — so open the dataset
before the request, not in response to a failed one. GetRecords walks the
whole dataset and returns each field's display text, so formatted values
(dates, currency, boolean fields) arrive the way they would appear on screen
rather than as raw values. It also restores the original record position
afterwards, so it is safe to call between navigation steps. And ModifyRecord
acts on wherever the dataset currently is, which is why it is only meaningful
immediately after LocateRecord — say so in the system role, or the model will
happily modify whichever row was already current.
Every value in a FieldValues entry is a string on the wire. Numeric and date
fields are converted on assignment, so a value the model phrases loosely ("next
Friday") fails at the field rather than being interpreted.
TTMSMCPCloudAIEmail sends over SMTP
and reads over POP3, using Indy clients it owns internally. Attach it when the
model should triage a mailbox or send what it has produced.
| Tool | Arguments | Does |
|---|---|---|
SendEmail |
EmailTo, EmailSubject, EmailBody (strings, required) |
Connects to the SMTP host and sends one message. |
GetCountEmail |
— | Returns the number of messages waiting, opening the POP3 connection if needed. |
RetrieveEmail |
Index (number, required) |
Returns the sender, subject, and body of one message as JSON. |
The set reads its server settings from published properties. Note the two
spellings inherited from the original API — SMPTPort and SMPTUserName:
| Property | Used for |
|---|---|
SMTPHost, SMPTPort |
Outgoing server and port. |
SMPTUserName, SMTPPassword |
Outgoing credentials. |
SMTPSendFrom |
The From address on every message the model sends. |
PopHost, PopPort |
Incoming server and port. |
PopUserName, PopPassword |
Incoming credentials. |
PopUseSSL |
Switches the POP3 connection to explicit TLS. |
procedure TForm1.AttachEmailTools;
begin
FMail := TTMSMCPCloudAIEmail.Create(Self);
{ Outgoing. The credentials come from the application configuration -
never from a literal in the source. }
FMail.SMTPHost := Config.ReadString('mail', 'smtp_host', '');
FMail.SMPTPort := Config.ReadInteger('mail', 'smtp_port', 587);
FMail.SMPTUserName := Config.ReadString('mail', 'smtp_user', '');
FMail.SMTPPassword := Config.ReadString('mail', 'smtp_password', '');
FMail.SMTPSendFrom := Config.ReadString('mail', 'send_from', '');
{ Incoming. }
FMail.PopHost := Config.ReadString('mail', 'pop_host', '');
FMail.PopPort := Config.ReadInteger('mail', 'pop_port', 995);
FMail.PopUserName := Config.ReadString('mail', 'pop_user', '');
FMail.PopPassword := Config.ReadString('mail', 'pop_password', '');
FMail.PopUseSSL := True;
FMail.AI := AI;
end;
procedure TForm1.btnTriageClick(Sender: TObject);
begin
if AI.Busy then
Exit;
{ GetCountEmail opens the POP3 connection if it is not open yet, and
RetrieveEmail needs that connection, so the count has to come first.
RetrieveEmail counts from 1, not from 0. }
AI.SystemRole.Text :=
'Call GetCountEmail first, then RetrieveEmail for each message from ' +
'index 1 upwards. Summarise what arrived, and only send a reply with ' +
'SendEmail when the user asks you to.';
AI.Context.Text := 'What came in today and what needs an answer?';
AI.Execute;
end;
procedure TForm1.CloseMailConnection;
begin
{ The POP3 connection stays open between requests. Close it when the
conversation ends rather than leaving the session on the server. }
FMail.DisconnectFromServer;
end;
Two ordering rules are worth stating in the system role. RetrieveEmail
requires an open POP3 connection and refuses when there is none, while
GetCountEmail opens one as a side effect — so the count has to come first in
any conversation that reads mail. And POP3 indexes start at 1: asking for
index 0, or for one past the count, answers with an error string rather than a
message.
Beyond the tools, the component exposes the same operations to your own code:
ConnectToServer, DisconnectFromServer, GetEmailCount, RetrieveEmail
returning a TEmailInfo record, and RetrieveEmailMessage returning a full
Indy message you own and must free. The connection is kept open between
requests, so call DisconnectFromServer when the conversation ends instead of
leaving a session on the server.
Combining the dataset, email, and logger sets
Several sets can serve one component, and that is where tool sets earn their keep: a request that reads the data, acts on it, and records what it did needs no tool declaration at all, only three components and one clear instruction.
procedure TForm1.RunOverdueRun;
begin
{ 1. The dataset set supplies the data. It needs an open dataset. }
if not FDQuery.Active then
FDQuery.Open;
if not Assigned(FDataTools) then
begin
FDataTools := TTMSMCPCloudAIDataSet.Create(Self);
FDataTools.DataSource := dsInvoices;
FDataTools.AI := AI;
end;
{ 2. The email set sends the reminders. }
if not Assigned(FMail) then
begin
FMail := TTMSMCPCloudAIEmail.Create(Self);
FMail.SMTPHost := Config.ReadString('mail', 'smtp_host', '');
FMail.SMPTPort := Config.ReadInteger('mail', 'smtp_port', 587);
FMail.SMPTUserName := Config.ReadString('mail', 'smtp_user', '');
FMail.SMTPPassword := Config.ReadString('mail', 'smtp_password', '');
FMail.SMTPSendFrom := Config.ReadString('mail', 'send_from', '');
FMail.AI := AI;
end;
{ 3. The logger set records every reminder that went out. }
if not Assigned(FLogger) then
begin
FLogger := TTMSMCPCloudAILogger.Create(Self);
FLogger.LogFileName := TPath.Combine(FWorkRoot, 'reminders.log');
FLogger.AI := AI;
end;
{ All three sets are merged into one tool list on Execute, so the model
sees the dataset, mail, and logging tools side by side. Spell out the
order you expect - the tools themselves enforce none. }
AI.SystemRole.Text :=
'Call GetFields, then GetRecords to find the invoices whose due date ' +
'has passed. Send one short reminder per customer with SendEmail, and ' +
'record each one with LogToFile. Send nothing to a customer twice.';
AI.Context.Text := 'Run the overdue reminders for today.';
AI.OnExecuted := AIExecuted;
AI.Execute;
end;
The tools of all three sets are merged into a single list on Execute, so the
model sees GetRecords, SendEmail, and LogToFile side by side and has to
be told the order you expect. Nothing in the sets sequences them, and nothing
prevents the model from sending before it has finished reading.
Common mistakes
- Leaving
LogFileNameempty.LogToFileraises No log file specified when neither the property nor the model's optionallogfileargument is present. Set the property as a fallback. - Using
ShowDialogin an unattended process. It is a modal message box raised inside a tool call, so the request blocks until someone dismisses it. - Calling a dataset tool against a closed dataset. Every one of the twelve answers No dataset assigned or dataset not active. Open the dataset before the request.
- Expecting
ModifyRecordto find its own row. It edits the current record. Pair it withLocateRecordin the same instruction, or the model edits whichever record happened to be active. - Reading raw values out of
GetRecords. It returns each field's display text, so a currency or date field arrives formatted. Say so in the prompt when the model has to compare values. - Retrieving mail before counting it.
RetrieveEmailneeds an open POP3 connection;GetCountEmailis what opens it. Ask for the count first. - Treating the mail index as zero-based. POP3 messages are numbered from 1.
- Spelling the mail properties the way they read. They are
SMPTPortandSMPTUserName, notSMTPPortandSMTPUserName. - Expecting the sets to enforce a boundary. No shipped tool limits a path,
a table, or a recipient. Withhold tools with
Enabledand state the limits in the system role.
See also
- Attaching tool sets — registration,
Enabled, lifetime, and extending a set - Cloud AI — the component that issues the request
- Function calling — the tool and parameter model
TTMSMCPCloudAILogger,TTMSMCPCloudAIFileSystem,TTMSMCPCloudAIDataSet,TTMSMCPCloudAIEmail