Skip to main content
Version: 0.21.0

Tool labels

A tool call is shown as a short sentence instead of its technical name: Reading knowledge/index.md instead of Read {"file_path": "…"}. The chats, the progress view and the logs use a formatAction(toolName, input) function; by default createFriendlyToolLabel().

The kit's labels​

ToolLabel
Read, Write, EditReading <file>, Writing to <file>, Editing <file>
GlobSearching for files matching "<pattern>"
GrepSearching file contents for "<pattern>"
BashRunning "<command>"
AgentDelegating to "<subagent>": <description>
WebFetchFetching <url>
WebSearchSearching the web for "<query>"
SkillApplying the "<skill>" skill
request_human_approvalAsking for human approval: <summary>
request_manual_loginWaiting for a human to intervene manually
ask_humanAsking: <question>
present_planPresenting the plan
current_time, date_mathChecking the date and time, Calculating dates
TodoWriteUpdating the task list (not shown in the chats, which draw the list)

Labels follow the kit's language. Long paths keep their end (…/course/knowledge/index.md), long text its start.

Any other tool gets its name with underscores as spaces. An MCP tool from a server that isn't local is prefixed with the server: [inventory] find product.

An extension's labels​

An extension brings its tools' labels with what it contributes (toolLabels), so the chat shows them without the agent doing anything. The kit's own:

ExtensionToolLabel
sourceslist_sourcesListing the sources
sourcesextract_textReading <source>
sourcessave_to_sourcesSaving <destination> to the sources
sourcesdownload_to_sourcesDownloading <url> to the sources
sourcesrequest_fileAsking for a file: <description>
sourcesretire_sourceRetiring the source <source>
knowledgeknowledge_indexReading the knowledge base's index
knowledgeknowledge_searchSearching the knowledge base for "<query>"
knowledgeknowledge_readReading <page>
knowledgeknowledge_createCreating <type>/<slug>
knowledgeknowledge_editEditing <page>
knowledgeknowledge_rewriteRewriting <page>
knowledgeknowledge_supersedeMarking <page> superseded
knowledgeknowledge_retireRetiring <page>
knowledgeknowledge_logUpdating the knowledge base's log
knowledgeknowledge_checkChecking the knowledge base
memoryrecallLooking through what it remembers of you, or Recalling "<entry>" with a name
memoryrememberRemembering "<entry>"
memoryforgetForgetting "<entry>"

An installed extension gives them in its manifest (labels, per tool and language). One written in your agent's code gives each tool, by its full name, a label from its input and, optionally, the phrase it counts with in a folded summary, in the kit's language (getLanguage(), already chosen when contribute() runs):

import { getLanguage, type Extension, type Language, type ToolPhrase } from "@falkenslab/agent-kit";

const LABELS: Record<Language, { label: (sides: string) => string; phrase: ToolPhrase }> = {
en: { label: (sides) => `Rolling a ${sides}-sided die`, phrase: ["rolled once", "rolled {n} times"] },
es: { label: (sides) => `Tirando un dado de ${sides} caras`, phrase: ["tiró una vez", "tiró {n} veces"] },
fr: { label: (sides) => `Lance un dé à ${sides} faces`, phrase: ["a lancé une fois", "a lancé {n} fois"] },
de: { label: (sides) => `Würfelt mit ${sides} Seiten`, phrase: ["einmal gewürfelt", "{n}-mal gewürfelt"] },
};

export const diceExtension: Extension = {
// …
async contribute() {
const { label, phrase } = LABELS[getLanguage()];
return {
// …
toolLabels: { mcp__dice__roll: { label: (input) => label(String(input.sides)), phrase } },
};
},
};

buildSessionOptions() hands back the active extensions' labels as toolLabels. runChatInk() and runChatTui() take them from a session opener (one that returns its whole result), and they come before formatAction and toolPhrase. With plain Options, pass them yourself (toolLabels: built.toolLabels); for the progress view or a host of your own, withToolLabels() and withToolPhrases() put them in front of a formatAction and a toolPhrase:

import { buildSessionOptions, createProgressView, withToolLabels, withToolPhrases } from "@falkenslab/agent-kit";

const built = await buildSessionOptions(config, runDir, spec);
const view = createProgressView({
formatAction: withToolLabels(() => built.toolLabels),
toolPhrase: withToolPhrases(() => built.toolLabels),
});

Labels for your tools​

import { createFriendlyToolLabel, truncate, truncatePath } from "@falkenslab/agent-kit";

const formatAction = createFriendlyToolLabel({
// Called with the tool's short name (without "mcp__<server>__") and its input.
describe: (tool, input) => {
switch (tool) {
case "browser_navigate":
return `Opening ${truncate(String(input.url), 70)}`;
case "browser_click":
return `Clicking "${truncate(String(input.element), 50)}"`;
case "save_report":
return `Saving the report to ${truncatePath(String(input.path), 60)}`;
default:
return undefined; // the kit's label, or the tool's name
}
},
// Servers whose tools are yours: no "[server]" prefix.
extraLocalServers: ["playwright", "reports"],
});

await runChatInk(opener, { runsDir, formatAction });

truncate(text, max = 60) cuts the end with …; truncatePath(path, max = 60) cuts the start, keeping the file name.

Folded summaries​

With Ctrl+O, a group of consecutive tool calls folds into one line: "Read 2 files, searched the web, ran 1 subagent". The kit has phrases for the built-in tools; any other counts as "used N tools". Give your tools their own with toolPhrase:

import type { ToolPhrase } from "@falkenslab/agent-kit";

const toolPhrase = (tool: string): ToolPhrase | undefined => {
if (tool === "mcp__playwright__browser_navigate") return ["opened {n} page", "opened {n} pages"];
if (tool === "mcp__playwright__browser_click") return ["clicked {n} time", "clicked {n} times"];
return undefined;
};

await runChatInk(opener, { runsDir, toolPhrase });

A ToolPhrase is [one, many], with {n} for the count. Calls with the same phrase are counted together, in the order they first appear, and the line starts with a capital.

How much shows​

toolDetail chooses how much of the tool calls the chat (and the progress view) shows until Ctrl+O:

LevelWhat shows
"full" (default)Every call with its result line.
"calls"Every call without its result. A failed call shows one short line in the kit's language ("Couldn't complete it"), not the tool's error.
"summary"One summary line per group, as above.
"full" "calls" "summary"
● Fetching https://example.com ● Fetching https://example.com Fetched 1 page, ran 1 shell command
⎿ ### Ran Playwright code (+12 lines) ● Running "npm test"
● Running "npm test" ⎿ Couldn't complete it
⎿ npm ERR! missing script: test

Ctrl+O unfolds any level into the full view, and folds it back. Only the screen changes: session.log and transcript.jsonl are the same whatever the level. "full" suits whoever develops the agent; "calls" or "summary" an audience that doesn't need the tools' output, which is written for the model.

Results for your tools​

The result line is the first line of the tool's output, often written for the model (Playwright's ### Ran Playwright code, the CLI's Web search results for query: …). formatResult gives your own, as formatAction does for labels: a string replaces the line, null hides it, undefined keeps the kit's.

import type { ResultFormatter } from "@falkenslab/agent-kit";

const formatResult: ResultFormatter = (tool, result) => {
if (!tool.startsWith("mcp__playwright__")) return undefined; // the kit's line
if (result.isError) return "The browser couldn't do it";
return tool === "mcp__playwright__browser_snapshot" ? null : "Done"; // a snapshot needs no line
};

await runChatInk(opener, { runsDir, formatResult });

The string is plain text, drawn in the theme's toolResult role, or error for a failed call. formatResult applies wherever results show: in "full", and in any level unfolded with Ctrl+O.

Coloring labels​

Labels are drawn in the theme's toolLabel role (dimmed by default). An agent can still color inside its own labels (with ui.* or picocolors); see Themes.