Tools vs Skills vs ToolHost
Three terms that sound similar but mean different things. Getting them straight removes a lot of API confusion.
Tool
A Tool is a callable the agent can invoke. Implemented as a
Tool dataclass; usually constructed via the @tool decorator:
from loomflow import tool
@tool
async def get_weather(city: str) -> str:
"""Look up the current weather for a city."""
return await api.get_weather(city)What it carries:
- A name the model sees (
get_weather). - A description the model uses to decide when to call it (the docstring).
- A JSON schema for the arguments (derived from type hints).
- The callable the framework invokes when the model issues a tool call.
- A destructive flag (
@tool(destructive=True)) for the permissions layer.
ToolHost
A ToolHost is a registry of tools the agent dispatches through.
The default. What you get when you pass tools=[t1, t2, t3]. Is
InProcessToolHost, which holds a Python dict of {name: Tool} and
runs them in-process.
from loomflow import Agent
from loomflow.tools import InProcessToolHost
host = InProcessToolHost([t1, t2])
host.register(t3)
host.unregister("t1")
agent = Agent("...", model="...", tools=host)Other ToolHost implementations:
MCPRegistry. Tools come from MCP servers (stdio / HTTP).FilesystemSandbox/SubprocessSandbox. Wrap an inner host with isolation.- Any class implementing the
ToolHostprotocol. Three async methods (list_tools,call, etc.).
The agent loop talks to one ToolHost. Compose multiple sources
by wrapping them into one host (or by calling the framework’s
multi-host helpers).
Skill
A Skill is a packaged playbook the model can read on demand.
It’s a directory with SKILL.md (frontmatter + markdown body) and
optional bundled files. Anthropic’s Agent Skills format.
skills/git-discipline/
├── SKILL.md
└── reference.md---
name: git-discipline
description: Conventional Commits, signed-off-by, and the pre-commit checklist.
---
# Git discipline
Use Conventional Commits. Run `pre-commit run --all-files` before
every commit. Never `--no-verify`.The model sees only the name + description at startup (~50
tokens). When the user’s request matches a skill’s description, the
model calls a load_skill(name) tool and the full body lands in
context.
Skills can ship their own tools (Mode B / C. See Three skill modes)
which auto-register into the agent’s tool host with a skill_name__
prefix.
How they compose
Agent
│
▼
ToolHost ←── tools=[...] (the registry the loop dispatches through)
│
▼
Tool ←── @tool functions, MCP-discovered tools, sandbox-wrapped tools
│
▼
Skill ←── skills=[...] (playbooks; their tools auto-register into the host)A Skill (Mode B/C) injects new Tools into the agent’s ToolHost
when loaded. So skills use the tool surface. They’re not a parallel
universe.
When to use which
| You want… | Use |
|---|---|
| The agent can do X | @tool function |
| A pre-built tool surface (calendar, mail, git) | MCP server |
| The agent should follow a recipe when X happens | Skill (Mode A) |
| The agent can do X and there’s a recipe | Skill (Mode B/C) |
| Filesystem isolation | Wrap host in FilesystemSandbox |
| Crash isolation | Wrap host in SubprocessSandbox |
Why this matters
Frameworks that conflate “tool” and “playbook” force you to decide upfront whether your domain logic is a callable or a string of instructions. Splitting them means:
- A chunk of pure-prose domain knowledge (the company’s git policy, a writing style guide) doesn’t need to be packed into a tool’s description.
- A tool that’s used everywhere doesn’t have to live inside a skill to be discovered.
- Skills can layer (system → user → project) without affecting the base tool surface.
Tools tell the agent what; skills tell it how. Tools are verbs the model can invoke. Skills are recipes the model reads when relevant. Most agents need both.