How to declare a custom tool
This guide shows how to turn a script or a program into a tool the model sees on every turn, with typed parameters, by declaring it in the agent file. It assumes you have an agent YAML file and a command you already run by hand.
Use it for a frequent, precise action — run the tests of a package, prepare a release, query a service. For a procedure in several steps, or know-how the model should read before acting, write a skill instead.
Steps
-
Make sure the command works from the directory mm runs in:
./scripts/release.sh 1.4.2 beta -
Add a
tools:block to the agent file, with one entry per command. Name the tool after the action, describe what it does and when to use it, declare one parameter per argument, and write the command as a list — the program first, then one entry per argument, with{{parameter}}where a value goes:tools: - name: release description: Prepare a release of the project for a given version and channel. parameters: version: { type: string, description: "e.g. 1.4.2" } channel: { type: string, enum: [stable, beta, nightly] } command: ["./scripts/release.sh", "{{version}}", "{{channel}}"] -
Start mm and check that the banner lists the tool among the others (
releasehere). A mistake in the block stops mm with a[config error: …]naming the entry; the configuration reference lists them. -
Ask for the action in your own words:
mm -p "Prepare release 1.4.2 on the beta channel."The terminal shows the call and its output:
🧰 release: ./scripts/release.sh 1.4.2 beta │ release done: 1.4.2 beta
Variants
-
A number, an integer or a flag. Give the parameter a
type:number,integerorboolean. A boolean usually goes inside an entry:"--dry-run={{dryRun}}". -
An optional argument. Add
required: false. Left out by the model, an entry that is only"{{p}}"disappears from the command; give adefaultto pass a value anyway:retries: { type: integer, required: false, default: 3 } -
A command longer than
bashTimeout. Addtimeout: 2mto the entry. -
Ask before each call. Set
yolo: false: the declared tools then ask likebashdoes. Withyolo: truethey run without asking. -
A shell feature (pipe, redirection,
&&). The command runs without a shell, so write it in a script and declare the script. Passing["bash", "-c", "…{{p}}…"]would put the model's value back into a shell line. -
The same action as an MCP tool. A declared tool cannot take the name of an MCP tool: it is skipped with a start-up warning. Rename one of them.
-
Restrict what runs before or after.
preToolCallandpostToolCallhooks apply to declared tools; use the tool's name asmatcher.
See also
- A complete example with four tools and every parameter type:
demos/declared-tools/ - Every key, type and error: configuration reference and declared tools
- Why typed tools beside skills, and what was measured: Declared tools
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|