Tools reference¶
Tools implement Tool (or use @tool) and are shared across nodes. Agents
receive them through graph.run(state, tools=tools) or YAML tools:. A
library of built-in tools registers itself when teff.tool.builtin is
imported (the YAML helpers and examples do this for you). Marked tools need
pip install teff[tools].
Registry¶
from teff.tool.registry import default_tool_registry
print(default_tool_registry.list()) # all registered names
Built-in tools¶
| Name | Class | Deps | What it does |
|---|---|---|---|
calculator |
CalculatorTool |
— | AST-based safe math evaluation |
shell |
ShellTool |
— | Run shell commands behind a block/whitelist sandbox |
read_file |
ReadFileTool |
— | Read a file's contents |
write_file |
WriteFileTool |
— | Write content to a file |
edit_file |
EditFileTool |
— | Replace text in a file |
list_dir |
ListDirTool |
— | List files/dirs (optionally recursive) |
glob |
GlobTool |
— | Find files matching a glob pattern |
getenv |
GetEnvTool |
— | Read an env var (secret values masked) |
current_time |
CurrentTimeTool |
— | Current date/time in an IANA timezone |
json_parse |
JsonParseTool |
— | Parse and pretty-print JSON |
yaml_parse |
YamlParseTool |
— | Parse YAML, dump as JSON |
kv_store |
KVStoreTool |
— | Persistent JSON key-value store |
python_eval |
PythonEvalTool |
— | Safe AST-whitelist evaluation of Python expressions |
web_search |
WebSearchTool |
— | DuckDuckGo search, no API key |
fetch_url |
WebFetchTool |
beautifulsoup4 |
Fetch a URL and extract its text |
read_pdf |
PDFReadTool |
pypdf |
Extract text from a PDF, page by page |
pdf |
PDFTool |
pypdf |
RAG-pageable PDF text extraction (RAG) |
image |
ImageTool |
httpx |
OCR via an OpenAI-compatible vision model |
s3_list / s3_get / s3_put |
S3Tool/S3GetTool/S3PutTool |
boto3 |
S3 object ops |
slack_send |
SlackSendTool |
slack-sdk |
Send a message to a Slack channel |
sql_query |
SQLQueryTool |
sqlite3/psycopg |
Read-only SELECT (SQLite/PostgreSQL) |
sql_list_tables |
SQLListTablesTool |
sqlite3/psycopg |
List tables |
sql_describe |
SQLDescribeTool |
sqlite3/psycopg |
Describe a table's columns |
http_request |
HttpRequestTool |
httpx |
Arbitrary HTTP requests |
send_email |
SendEmailTool |
smtplib | Send email via SMTP |
send_telegram |
SendTelegramTool |
httpx |
Send a message via a Telegram bot |
csv_query |
CSVTool |
— | Query/aggregate a CSV |
git |
GitTool |
— | git status/log operations |
github_list_open_prs etc. |
GitHubTool family |
httpx |
GitHub PR list/changes/comment/approve |
gitlab_list_open_mrs etc. |
GitLabTool family |
httpx |
GitLab MR list/changes/comment/approve |
lock |
LockTool |
— | Distributed-ish lock acquire/release |
redis |
RedisTool |
redis |
Redis get/set/hash/list ops |
wait_for |
WaitForTool |
— | Poll until a condition holds |
rag |
RAGTool |
teff[stores-*] |
Retrieval over a vector store |
rag_ingest |
RAGIngestTool |
teff[stores-*] |
Add documents to a vector store (chunk+embed+store) |
memory |
MemoryTool |
teff[stores-*] |
Long-term memory (remember/recall/forget) |
mcp |
McpToolGroup |
teff[mcp] |
Expose every tool of an MCP server (streamable-http or stdio); ready-made presets for Google Drive / Gmail / Calendar |
Model Context Protocol (MCP) servers¶
type: mcp is a special tool: it doesn't map onto a registry class but onto
a lazily-opened MCP server, whose tools
become available to agents as Tool instances named <server_id>__<tool>.
tools:
- type: mcp
config:
id: drive # member tools become drive__ <tool>
command: [uvx, mcp-server-google-drive] # stdio server
- type: mcp
config:
id: git
url: http://localhost:8000/mcp # streamable-http server
Exactly one of command (stdio argv, resolved) or url (streamable HTTP
endpoint) must be given; optional env, cwd and client_info keys are
accepted too and map 1:1 onto McpToolGroup's constructor.
Instead of spelling out a server's launch command and env keys, use a
preset for a known server — the preset supplies the command (or url)
and its env-var keys, and your env: entries merge over the defaults
(same key overrides):
tools:
- type: mcp
config:
preset: google_drive
env: {GOOGLE_DRIVE_REFRESH_TOKEN: "${GDRIVE_TOKEN}"}
Known presets (MCP_PRESETS) split into two launchers:
npx(Node.js) — Google servers.google_drive,gmail,google_calendarrunnpx @google/mcp-server-*with the correspondingGOOGLE_DRIVE_*,GOOGLE_GMAIL_*,GOOGLE_CALENDAR_*env keys. Google distributes these only as npm packages, so Node.js must be installed.uvx(Python) — self-hosted servers.git,fetch,time,sqliterunuvx mcp-server-*(Python packages, no Node needed).
If the launcher (npx/uvx) is missing from PATH, opening the group
fails with a clear error instead of an opaque subprocess failure — see
graph.aclose()-adjacent errors, e.g.:
RuntimeError: cannot start MCP server 'google_drive': 'npx' is not
installed. ... Install Node.js ... or override the preset with a
python-based `command:`.
A preset plus explicit command:/url:/id:/cwd: overrides
fully replaces the preset's value for that key; id defaults to the preset
name. Validation accepts a config with either preset or one of
url/command.
The connection is opened lazily on first use and cached for the lifetime of
the graph, so daemon ticks and conversation turns reuse a single connection.
Close everything with graph.aclose() — or equivalently async with
graph::
graph, tools, state, reducers = load_workflow("app/flow.yaml")
async with graph:
result = await graph.run(state, tools=tools)
In plain Python, McpToolGroup and open_tools give the same primitives
without YAML:
from teff.tool import McpToolGroup, open_tools
async with open_tools(
[
McpToolGroup(id="git", command=["uvx", "mcp-server-git"]),
McpToolGroup.from_preset(
"google_drive",
env={"GOOGLE_DRIVE_REFRESH_TOKEN": os.environ["GDRIVE_TOKEN"]},
),
]
) as tools:
result = await graph.run(state, tools=tools)
Configuring tools¶
Tools are plain classes, so you can construct them directly with keyword
arguments — ShellTool(root_dir=..., allowed_commands=[...]),
WebSearchTool(provider="google"), SQLQueryTool({"db_type": "sqlite",
"path": "./v.db"}). The registry (used by YAML tools: blocks) maps a config
dict onto the constructor: a dict passed to constructors that take a config
dict, or keyword arguments for keyword constructors:
tools:
- type: sql_query
config: {db_type: sqlite, path: ./vectors.db}
- type: shell
config: {root_dir: /tmp, allowed_commands: [echo, ls]}
- type: s3_list
config: {bucket: my-bucket, region: eu-central-1, verify: false}
The same works in Python:
from teff.tool.registry import default_tool_registry
sql = default_tool_registry.create("sql_query", {"db_type": "sqlite", "path": "./v.db"})
shell = default_tool_registry.create(
"shell", {"root_dir": "/tmp", "allowed_commands": ["echo"]}
)
Writing a custom tool¶
from teff.tool.tool import Tool
from teff.tool.registry import tool
@tool("slugify", "Convert a string to a lowercase URL slug")
def slugify(text: str = "") -> str:
return "-".join(text.lower().split())
# or a subclass
class Search(Tool):
name = "search"
description = "Search a local index"
def run(self, query: str = "") -> str:
return f"results for {query}"
See Plugins for discovery of custom tools.
Security notes¶
shellenforces a blocklist of dangerous commands plus an optional whitelist. It executes viaexecve(no/bin/sh), so shell operators (&&,;, pipes, backticks,$(…)) are never interpreted — any token containing shell metacharacters is rejected outright.memoryfixes its namespace at construction time (namespace=(...)over thetools:config); an agent cannot switch namespaces mid-call, so one tool instance is scoped to one owner/tenant.getenvmasks values whose names hint at credentials (TOKEN,API_KEY,PASSWORD,DSN, …) unless configured withmask_secrets: false.sql_queryand the other SQL tools are read-only and rejectINSERT/UPDATE/DELETE/DDL.python_evalonly allows a whitelisted AST subset (math.*, builtins likelen/abs/sum, comparisons).