The agent-native CLI framework for Python
Project description
Tooli
The agent-native CLI framework for Python.
Tooli turns typed Python functions into command-line tools that work for both humans and AI agents: rich output in a terminal, strict structured output in automation, and self-describing schemas for tool calling and orchestration.
The name comes from "tool" + "CLI" = "tooli".
Why Tooli?
AI agents invoke lots of local commands, but typical CLIs are optimized for humans:
- Huge, unstructured stdout that burns context windows
- Opaque errors that don't suggest a fix
- Fragile pipelines that mix logs with machine output
- Undocumented flags that agents hallucinate
Tooli is built to be machine-consumable by default while still feeling great for humans.
Install
pip install tooli
Optional extras:
pip install "tooli[mcp]" # MCP server support
pip install "tooli[api]" # HTTP API + OpenAPI export (experimental)
Quick Start
Create file_tools.py:
from __future__ import annotations
from pathlib import Path
from typing import Annotated
from tooli import Argument, Option, Tooli
from tooli.annotations import Idempotent, ReadOnly
app = Tooli(name="file-tools", description="File utilities", version="1.1.0")
@app.command(annotations=ReadOnly | Idempotent, paginated=True, list_processing=True)
def find_files(
pattern: Annotated[str, Argument(help="Glob to match")],
root: Annotated[Path, Option(help="Root directory")] = Path("."),
) -> list[dict[str, str]]:
return [{"path": str(p)} for p in root.rglob(pattern)]
if __name__ == "__main__":
app()
Run it:
python file_tools.py find-files "*.py" --root .
python file_tools.py find-files "*.py" --root . --output json
python file_tools.py find-files --schema
Structured Output (JSON / JSONL)
Tooli supports dual-mode output:
- Human mode: pretty output when attached to a TTY
- Agent mode: strict envelopes when using
--output jsonor--output jsonl
JSON envelope shape:
{
"ok": true,
"result": {"...": "..."},
"meta": {
"tool": "file-tools.find-files",
"version": "1.1.0",
"duration_ms": 12,
"annotations": {"readOnlyHint": true, "idempotentHint": true}
}
}
Structured Errors With Recovery Hints
When a command fails, Tooli emits a structured error with an actionable suggestion:
{
"ok": false,
"error": {
"code": "E1004",
"category": "input",
"message": "Exact search string was not found in source.",
"suggestion": {
"action": "adjust search text",
"fix": "Double-check exact spacing/newlines. Did you mean: \"...\"?"
},
"is_retryable": true
}
}
Schemas, Docs, and Orchestration
Tooli can generate tool schemas and agent-facing docs directly from type hints and metadata:
python file_tools.py find-files --schema
python file_tools.py generate-skill > SKILL.md
python file_tools.py docs llms
python file_tools.py docs man
Run as an MCP server (one tool per command):
python file_tools.py mcp serve --transport stdio
python file_tools.py mcp serve --transport http --host 127.0.0.1 --port 8080
python file_tools.py mcp serve --transport sse --host 127.0.0.1 --port 8080
Universal Input (files / URLs / stdin)
Use StdinOr[T] to accept a file path, a URL, or piped stdin with one parameter.
from pathlib import Path
from typing import Annotated
from tooli import Argument, StdinOr, Tooli
app = Tooli(name="log-tools")
@app.command()
def head(
source: Annotated[StdinOr[str], Argument(help="Path, URL, or '-' for stdin")],
) -> dict[str, int]:
return {"bytes": len(source)} # `source` resolves to the content
Built-In Guardrails
Tooli provides primitives for safer automation:
ReadOnly,Idempotent,Destructive,OpenWorldannotations on commands--dry-runplanning support via@dry_run_support+record_dry_action(...)SecretInput[T]with automatic redaction in outputs and errors- Cursor pagination (
--limit,--cursor,--fields,--filter) for list-shaped results
Example Apps (agent pain points)
The GitHub repo includes sample apps under examples/ that target common agent failure modes:
code-lens: token-efficient symbol outlines from Python ASTs (avoid dumping whole files)safe-patch: self-healing file edits with dry-run plans and recovery hintslog-sift: pipeline-friendly log extraction with strict JSON/JSONL outputsqlite-probe: read-only SQLite exploration with pagination and guardrails
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tooli-1.1.0.tar.gz.
File metadata
- Download URL: tooli-1.1.0.tar.gz
- Upload date:
- Size: 67.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8cd0a5537e827b1d6c5d73750beeeca8c027e138ee6b62dec5c7c37dafe31c03
|
|
| MD5 |
f7788bc2153002fc08132ddddceb7743
|
|
| BLAKE2b-256 |
9d3cd213a0901250554ff6356ff2b9df8d7946931f57e8d60f21612f8d932cf1
|
Provenance
The following attestation bundles were made for tooli-1.1.0.tar.gz:
Publisher:
publish.yml on weisberg/tooli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tooli-1.1.0.tar.gz -
Subject digest:
8cd0a5537e827b1d6c5d73750beeeca8c027e138ee6b62dec5c7c37dafe31c03 - Sigstore transparency entry: 955553574
- Sigstore integration time:
-
Permalink:
weisberg/tooli@945010fdd5ab1d24a8a19873716cd0debfae916d -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/weisberg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@945010fdd5ab1d24a8a19873716cd0debfae916d -
Trigger Event:
release
-
Statement type:
File details
Details for the file tooli-1.1.0-py3-none-any.whl.
File metadata
- Download URL: tooli-1.1.0-py3-none-any.whl
- Upload date:
- Size: 53.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
095fdf29925f30b24da67d4854cd756d61c5db736e5b782c9b223d9b438f32e6
|
|
| MD5 |
2c6f88165328223e9a095f280be68c42
|
|
| BLAKE2b-256 |
9fe073f50c0914afd39a31b6e1f9b81740d23786e6c36f5459bab54d2540497e
|
Provenance
The following attestation bundles were made for tooli-1.1.0-py3-none-any.whl:
Publisher:
publish.yml on weisberg/tooli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tooli-1.1.0-py3-none-any.whl -
Subject digest:
095fdf29925f30b24da67d4854cd756d61c5db736e5b782c9b223d9b438f32e6 - Sigstore transparency entry: 955553576
- Sigstore integration time:
-
Permalink:
weisberg/tooli@945010fdd5ab1d24a8a19873716cd0debfae916d -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/weisberg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@945010fdd5ab1d24a8a19873716cd0debfae916d -
Trigger Event:
release
-
Statement type: