Skip to content

Add tools, commands and skills ​

A new harness has no tools. This page tells you how to give the agent what your product needs. You can do it in two ways:

  • In code, with arguments to running_harness.
  • In a plugin: a directory that you list in the configuration file. A plugin can hold tools, skills, prompts, hooks and commands.
You want the agent to …Add aWhere
Call your API or read your datatool, from an MCP servera plugin's mcp.json
Follow a playbook for one kind of requestskilla plugin's skills/
Know about your product and rulesprompta plugin's prompt files
Answer a /name message without the modelcommandcode, or a plugin's commands.json
Check or change what the agent does at a stephooka plugin's hooks.json

Add a command in code ​

A command is a message such as /echo hi. Your function answers it. The model does not see it, so a command is fast and costs nothing.

python
from tiny_harness.harness.commands import CommandInvokedIn, CommandResult, CommandSpec
from tiny_harness.service import running_harness

echo_spec = CommandSpec(name="echo", description="Repeat the text", argument_hint="<text>")


def echo(ctx: CommandInvokedIn) -> CommandResult:
    return CommandResult(reply=ctx.arguments)


async with running_harness(settings, commands=[(echo_spec, echo)]) as harness:
    await harness.wait()
  • ctx.arguments is the text after the command name. ctx.sender is the sender's name. ctx.task describes the task.
  • The function can be async.
  • Clients see the command in the agent card, so the TUI and the web renderer show it when the user types /.
  • To remove the built-in commands (/model, /effort, /sub-task, /status), add builtin_commands=False.

Make a plugin ​

A plugin is a directory. Only plugin.json is necessary.

text
my-plugin/
├── plugin.json                                 the name and version (necessary)
├── mcp.json                                    MCP servers: the tools
├── skills/
│   └── refunds/SKILL.md                        a skill
└── io.github.madarauchiha-314.tiny-harness/    parts only tiny-harness reads
    ├── prompts/rules.md                        add text to the system prompt
    ├── systemprompt.md                         replace the system prompt
    ├── hooks.json                              hooks
    └── commands.json                           commands

plugin.json:

json
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "my-plugin",
  "version": "0.1.0",
  "description": "Tools and rules for the Acme support desk."
}

Load the plugin in one of two ways:

  • In the configuration file: plugins = ["path/to/my-plugin"].
  • In code: running_harness(settings, plugins=[Path("path/to/my-plugin")]).

Plugins from code load after the plugins in the configuration file. The format follows Agent Plugins 1.0. If plugin.json is not valid, tiny-harness does not load the plugin. If one part is not valid, tiny-harness skips that part and loads the rest.

Add tools with an MCP server ​

A tool is a function the agent can call. tiny-harness gets tools from MCP servers. MCP is an open protocol for tools. Write a small MCP server, or use an existing one.

mcp.json:

json
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
  "mcpServers": {
    "orders": {
      "type": "stdio",
      "command": "python",
      "args": ["orders_server.py"],
      "cwd": "${PLUGIN_ROOT}"
    }
  }
}
  • The agent sees each tool as <plugin>/<server>.<tool>, for example my-plugin/orders.get_order.
  • "type": "stdio" starts the server as a child process. For a server that runs elsewhere, use "type": "streamable-http" and a url.
  • ${PLUGIN_ROOT} is the plugin directory. ${PLUGIN_DATA} is a private data directory for the plugin.
  • The server process gets only the env you list, plus HOME, PATH, USER, SHELL, TERM and LOGNAME. It never gets your API keys.
  • tiny-harness retries a failed tool call only when the MCP server marks the tool as idempotent or read-only. Mark a tool that changes data (for example, "send a refund") as neither, so it never runs twice.

Add a skill ​

A skill is a playbook in Markdown. The agent sees the name and description of every skill. It reads the full text only when it needs it.

skills/refunds/SKILL.md:

markdown
---
name: refunds
description: How to decide and issue a refund for an order, within policy.
---

# Refunds

1. Call `my-plugin/orders.get_order` with the order id.
2. ...

The directory name must be the same as name. A skill can also hold scripts/, references/ and assets/ directories, and its own mcp.json. The format is Agent Skills.

Change the system prompt ​

The system prompt has seven sections: role, task, participants, tools, skills, agents and rules.

  • To add text to one section, write a Markdown file in io.github.madarauchiha-314.tiny-harness/prompts/. Name the section in its front matter:

    markdown
    ---
    extends: rules
    ---
    
    You are the support agent of Acme. Ask before any refund.
  • To replace the whole prompt, write io.github.madarauchiha-314.tiny-harness/systemprompt.md, with one ## heading for each section.

Add a command in a plugin ​

commands.json lists the commands. Each command names the hook that answers it.

json
{
  "commands": [
    { "name": "deploy", "description": "Deploy to an environment", "argument_hint": "<env>", "hook": "deploy-body" }
  ]
}

hooks.json defines that hook. Its points must be exactly ["command.invoked.in"]:

json
{
  "hooks": [
    { "name": "deploy-body", "points": ["command.invoked.in"], "url": "https://hooks.example.com/rpc" }
  ]
}

The schema is in docs/plugins/commands.json.

Add a hook ​

A hook runs your code before, during or after a step of the agent. For example, a hook on tool.invoked.pre can refuse a tool call or change its arguments.

Each entry in hooks.json has a name, an optional priority (lower runs first, default 500), the points it handles, and one of these:

KeyYour code runs
import_pathIn the harness process. "my_package.hooks:audit" names a Python object that has name, priority, points and async handle(point, ctx)
urlOn your HTTP server, as JSON-RPC 2.0
mcpOn an MCP server, as the tool hooks.<operation>.<phase>

A hook that fails stops the step. tiny-harness then retries the step or fails the task. It never ignores the failure.

The hooks reference lists every hook point.

Connect other agents ​

The agent can give work to other A2A agents. List them in the configuration file. See Plugins and other agents.

Next steps ​