FastAPI for safe AI tools.
Project description
ToolRampart
ToolRampart is a small Python framework for exposing normal Python functions as safe AI-agent tools and MCP-compatible tools.
Positioning:
- FastAPI for safe AI tools.
- Give agents tools without giving them unrestricted production access.
It is not an agent framework, chatbot framework, or LangChain replacement. It is a developer-first safety layer around tool execution.
Alpha Status
ToolRampart is currently alpha software. The public API is usable for evaluation, examples, and controlled internal pilots, but production deployments should follow the security notes, threat model, and production checklist in the docs.
Quick Start
Install:
pip install toolrampart
Documentation site:
pip install ".[docs]"
mkdocs serve
AI agents can start from llms.txt and AGENTS.md.
from toolrampart import tool, require_approval, redact, scope, rate_limit, side_effects
@tool
@scope("billing.refund")
@require_approval(over_amount=500)
@redact(["email", "phone", "api_key"])
@rate_limit("10/hour/user")
@side_effects(money_movement=True, writes_data=True)
def refund_user(user_id: str, amount: float, reason: str) -> dict:
return {
"status": "refund_started",
"user_id": user_id,
"amount": amount,
"reason": reason,
}
Run a tool module as an API:
toolrampart serve my_tools
Invoke a tool from Python:
from toolrampart import ToolContext, default_rampart
result = default_rampart.invoke(
"refund_user",
{"user_id": "u_123", "amount": 100, "reason": "duplicate charge"},
ToolContext(actor="support-agent", scopes=["billing.refund"]),
)
What It Does
ToolRampart runs every tool through the same safety pipeline:
- Validate inputs from the Python function signature with Pydantic v2.
- Check trusted actor scopes.
- Run custom policy functions.
- Create or verify approval requests for risky calls.
- Enforce local or Redis-backed rate limits.
- Deduplicate retries with idempotency keys.
- Execute with timeout, retry, and optional subprocess isolation controls.
- Validate structured return values when annotated.
- Emit optional OpenTelemetry spans and metrics.
- Write redacted audit logs.
REST API
from toolrampart.app import create_app
app = create_app()
Endpoints:
GET /healthGET /toolsGET /mcp/toolsPOST /tools/{tool_name}/invokeGET /approvalsPOST /approvals/{approval_id}/approvePOST /approvals/{approval_id}/rejectGET /auditGET /metricsGET /dashboard
CLI
toolrampart init
toolrampart list tools
toolrampart call refund_user --target tools --args '{"user_id":"u_1","amount":100,"reason":"duplicate"}' --scope billing.refund
toolrampart approvals list --target tools
toolrampart approvals approve APPROVAL_ID --target tools --actor alice
toolrampart call refund_user --target tools --idempotency-key refund-u_1-001 --args '{"user_id":"u_1","amount":100,"reason":"duplicate"}'
toolrampart serve tools
toolrampart mcp tools
On shells that make inline JSON awkward, write arguments to a file and use:
toolrampart call refund_user --target tools --args-file args.json --scope billing.refund
Python Client
from toolrampart import ToolRampartClient
client = ToolRampartClient("http://localhost:8000", api_key="dev-secret")
result = client.invoke(
"refund_user",
{"user_id": "u_1", "amount": 100, "reason": "duplicate"},
idempotency_key="refund-u_1-001",
)
if result.approval_required:
client.approve(result.approval_id, actor="alice")
Auth Boundary
REST auth is optional for local development and should be enabled for shared or production use.
ToolRampart supports:
- API keys through
Authorization: Bearer <key>orX-ToolRampart-Key - hashed API keys for production configuration and key rotation
- HS256 JWTs with signed actor and scopes
- JWKS-backed JWT verification for RS256/ES256 tokens
- trusted upstream headers:
X-ToolRampart-ActorandX-ToolRampart-Scopes
When auth is enabled, the REST request body cannot grant actor identity or scopes. Those come from the authenticator or trusted middleware.
Generate and hash API keys:
toolrampart auth generate-key
toolrampart auth hash-key trp_example_secret
Scopes support exact matches, *, and prefix wildcards such as billing.*.
Idempotency
Use idempotency keys for write tools so agent retries do not duplicate side effects:
from toolrampart import ToolContext, default_rampart
result = default_rampart.invoke(
"refund_user",
{"user_id": "u_1", "amount": 100, "reason": "duplicate"},
ToolContext(actor="agent", scopes=["billing.refund"], idempotency_key="refund-u_1-001"),
)
For REST, send Idempotency-Key or X-Idempotency-Key. ToolRampart replays the completed ToolResult when the same actor/tool/key/arguments are seen again, and returns idempotency_conflict if the key is reused with different arguments.
Subprocess Isolation
Use subprocess isolation for risky or long-running tools that need killable timeouts:
from toolrampart import isolated_process, timeout, tool
@tool
@isolated_process
@timeout(5)
def rebuild_index(index_name: str) -> dict:
return {"status": "rebuilt", "index_name": index_name}
The function and return value must be pickleable, and the function should be importable at module top level. Closures, local functions, live database clients, and open sockets do not cross a process boundary safely.
OpenTelemetry
Install:
pip install toolrampart[otel]
ToolRampart uses the OpenTelemetry API only. Host applications remain responsible for configuring SDK providers and exporters. When available, ToolRampart emits:
toolrampart.tool.invokespanstoolrampart.tool.invocationscountertoolrampart.tool.durationhistogram
MCP
Install MCP support:
pip install toolrampart[mcp]
Run a stdio MCP server:
TOOLRAMPART_MCP_SCOPES=billing.refund toolrampart mcp tools
ToolRampart uses the official MCP Python SDK as an optional dependency and keeps its own execution pipeline for validation, approvals, rate limits, and audit logging.
Storage
SQLite is the default storage backend and is intended for local development and single-node deployments.
Optional adapters:
toolrampart[postgres]for PostgreSQL audit and approval storagetoolrampart[redis]for Redis-backed rate limitingtoolrampart[otel]for OpenTelemetry API instrumentationtoolrampart[all]for MCP, OpenTelemetry, Postgres, and Redis extras
SQLite uses an explicit migration runner. Check local schema state with:
toolrampart migrations status
The CI workflow includes service-container integration tests for Postgres and Redis.
Docker Compose
Run ToolRampart with Postgres and Redis:
docker compose up --build
Then open http://localhost:8000/dashboard.
Current Boundaries
ToolRampart controls whether a tool function is called. It does not magically sandbox all side effects inside the function. For dangerous tools, combine ToolRampart with least-privilege service credentials, network controls, execution timeouts, and approval workflows.
Release Readiness
Before publishing an alpha, run the release checklist in docs/RELEASE.md. The short version is:
python -m pip install -e ".[dev,docs]"
python -m pytest
python -m mkdocs build --strict
python -m build
python -m twine check dist/*
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 toolrampart-0.2.0.tar.gz.
File metadata
- Download URL: toolrampart-0.2.0.tar.gz
- Upload date:
- Size: 64.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b82ddc650bb92dd53b46128b856cafbed9710fd80b64b2929407a8a0c9e5f00d
|
|
| MD5 |
076d476d6ca9378b2fea519f1f986d9f
|
|
| BLAKE2b-256 |
f4fb5f33967fb6bdc924108290be0fa4211b859a7e282fcd37fff8e029419eca
|
Provenance
The following attestation bundles were made for toolrampart-0.2.0.tar.gz:
Publisher:
release.yml on yuvrajraina/toolrampart
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
toolrampart-0.2.0.tar.gz -
Subject digest:
b82ddc650bb92dd53b46128b856cafbed9710fd80b64b2929407a8a0c9e5f00d - Sigstore transparency entry: 1674524686
- Sigstore integration time:
-
Permalink:
yuvrajraina/toolrampart@4625d14a73b7a01e7ff72a8331d792787b56b831 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/yuvrajraina
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4625d14a73b7a01e7ff72a8331d792787b56b831 -
Trigger Event:
push
-
Statement type:
File details
Details for the file toolrampart-0.2.0-py3-none-any.whl.
File metadata
- Download URL: toolrampart-0.2.0-py3-none-any.whl
- Upload date:
- Size: 39.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f932f0fb78f5918666b48d28c5f9c987c01602a5d62644d66eca30ae636e759
|
|
| MD5 |
d3600e43fd47d3dd600691ad7f00e799
|
|
| BLAKE2b-256 |
018c84516b2cc11cf262457a76eeb1a9cb0797efc49cbe093eec8d09f72c4042
|
Provenance
The following attestation bundles were made for toolrampart-0.2.0-py3-none-any.whl:
Publisher:
release.yml on yuvrajraina/toolrampart
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
toolrampart-0.2.0-py3-none-any.whl -
Subject digest:
8f932f0fb78f5918666b48d28c5f9c987c01602a5d62644d66eca30ae636e759 - Sigstore transparency entry: 1674524693
- Sigstore integration time:
-
Permalink:
yuvrajraina/toolrampart@4625d14a73b7a01e7ff72a8331d792787b56b831 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/yuvrajraina
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4625d14a73b7a01e7ff72a8331d792787b56b831 -
Trigger Event:
push
-
Statement type: