agent-toolbox
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@agent-toolboxRun a Python snippet to analyze the recent signups trend."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
agent-toolbox
An MCP server that gives an agent hands: tools to query data, work with files and run Python for analysis — and a sandbox where those tools are callable as plain functions. New tools are added over time; every client discovers the current set over MCP.
It is a standard MCP server: the homelab agent uses it, and so can any MCP client.
Using the toolbox — run it, connect a client, know what each tool does. Working on the toolbox — how it is built and how to add a tool.
┌──────────────────────────────────────────────┐
agent / MCP client ───►│ agent-toolbox /mcp │
(X-Request-ID) │ │
│ data tools ───────► databases, files, … │
│ python_executor ─► sandbox ─┬─► working dir │
│ └─► data tools │
└──────────────────────────────────────────────┘Using the toolbox
Run it
Requirements: Python 3.14 and uv.
uv sync --group dev
cp .env.example .env # fill in the paths and the URL clients will use
make runEndpoint | |
MCP (streamable HTTP) |
|
Health |
|
Version |
|
/health/readiness answers 503 until the server has tools registered.
With Docker: make docker_build builds the image.
The tools
The server is the reference for what it offers: an MCP client lists the tools (tools/list) with their descriptions and full input and output schemas — the agent does it at boot. The tools available today:
Tool | What it does | Inputs |
| Read-only SQL on the service database (users dashboard, payment data). Anything that writes or changes the schema is refused before it reaches the database |
|
| Returns a file's contents — parsed for |
|
| Writes (replaces) a file in one of the same formats |
|
| Runs Python for analysis or computation; the value of the variable |
|
A call with invalid arguments gets an error naming the bad fields. Every tool added later follows the same rules: published with its schema, callable directly and from the sandbox.
The working directory. When WORKING_DIRECTORY is set, every file a tool reads or writes lives there, and relative paths name the same file in a tool call and inside python_executor code. Access outside it is refused.
The Python sandbox runs each piece of code in its own process:
allowed modules: pandas, numpy, matplotlib, datetime and the rest of pycraftcore's allowlist (listed in the tool's description);
256 MB of memory, at most 8 runs at a time, and a hard timeout;
with
SANDBOX_TOOL_ACCESS=true, the other tools are available inside the code as functions that return exactly what the tool returns:
import pandas as pd
rows = users_tables(query="SELECT country, COUNT(*) AS users FROM users GROUP BY country")
df = pd.DataFrame(rows["rows"])
file_writer(file_path="users_by_country.csv", data=df.to_csv(index=False))
result = df.sort_values("users", ascending=False).head(3).to_dict("records")Connect a client
Point the client's MCP connector at http://<host>:<port>/mcp with the streamable_http transport. The homelab agent's connector:
toolbox:
name: toolbox
type: mcp
base_url: ${oc.env:TOOLBOX_URL}
timeout: 30
transport: streamable_httpTo try the tools by hand, the MCP Inspector works against the same URL:
npx @modelcontextprotocol/inspectorAllowed hosts. Only requests whose Host matches TOOLBOX_URL are accepted (protection against DNS rebinding); any other host gets 421 Invalid Host header. When clients reach the toolbox under another name — a LAN hostname, a Tailscale name, a tool like Postman — list it in TOOLBOX_EXTRA_ALLOWED_HOSTS rather than changing TOOLBOX_URL.
Configure it
Variable | Meaning |
| Which |
|
|
| Where |
| Label shown by |
| The URL clients use, e.g. |
| Other |
| The folder file tools and the sandbox are confined to; unset = no working directory |
|
|
| OpenTelemetry collector (gRPC); an empty host turns telemetry off |
Tools that reach a backend add their own settings, read through a connector in config/<APP_ENV>/connector/. Today the SQL tool reads its SQLite database from USER_DB_HOST (folder) and USER_DB_NAME (file). .env.example is the reference for the full list.
Configuration files are in config/: root.yml sets the log level and lists what exists, and config/<APP_ENV>/connector/ defines the MCP server itself, telemetry, and each backend a tool uses. root.yml keeps server: {}: the MCP endpoint is not one of the web servers server/*.yml describes.
Logs
One format for every line the service writes; the third column is the request id sent by the client in X-Request-ID. Tool calls made from sandboxed code keep the id of the request that started them.
2026-09-27 10:47:47.785 | INFO | my-conversation | invoke_tool_usecase:invoke:23 - tool 'file_reader' invokedWith OTEL_HOST set, the same lines also go to the OpenTelemetry collector. Granian's own start and stop lines ([INFO] Starting granian…) keep Granian's format; access-log lines carry no request id.
Related MCP server: postgres-mcp-server
Working on the toolbox
Architecture
Hexagonal, like the agent: the use case and the tools know nothing about MCP. tests/architecture/test_boundaries.py enforces it (no agent, LangChain or LangGraph imports; no framework in the domain; no transport in the application).
MCP request ──► ToolBinder ──────────► InvokeToolUseCase ──► Tool.run
(inbound) one MCP tool per Tool validates arguments │
schemas from models logs, maps failures ├─► a data tool ──► its backend
│ (database, files, API, …)
sandboxed code ─► SandboxToolBridge ──► InvokeToolUseCase └─► ExecutePython ──► PythonSandbox
(python_executor) same use case, same tools, same request id (pycraftcore)Direct calls and calls from sandboxed code go through the same InvokeToolUseCase, so they return the same thing — tests/contract/test_tool_parity.py checks it.
Project layout
Path | What it holds |
|
|
|
|
|
|
|
|
| The tools, one folder per area ( |
| Settings, |
| Unit, architecture and contract tests |
Adding a tool
Define its input and output as pydantic models (
adapter/outbound/<area>/model/). Field descriptions become the MCP schema the agent reads.Subclass
Tool[Input, Output]with aname, adescriptionwritten for the model, andasync run(arguments). RaiseToolFailurewith a clear message when the call can't be served.If it needs a backend (a database, an API, …), add a connector in
config/<APP_ENV>/connector/, list it inconfig/root.yml, put its variables in.env.example, and build the client inToolboxDIfrom that connector.Register it in
ToolboxDI._invoke_tool_use_case. It is then published over MCP and, whenSANDBOX_TOOL_ACCESSis on, callable frompython_executorcode — no change needed in the agent, which discovers it at its next start.Add unit tests next to the others, and its row to The tools.
class CountRows(Tool[CountRowsInput, CountRowsOutput]):
name = "count_rows"
description = "Count the rows of a table in the service database."
input_model = CountRowsInput
output_model = CountRowsOutput
async def run(self, arguments: CountRowsInput) -> CountRowsOutput: ...Tests and checks
make check # ruff + ty + pytest
make test
make lint / make format / make typecheckPre-commit hooks:
uv run pre-commit install --hook-type pre-commit --hook-type pre-pushDeploying
make docker_build builds agent-toolbox:local. It listens on port 8001; set the environment variables above on the container, with TOOLBOX_URL matching the address clients use.
The image is served by Granian, configured with GRANIAN_* variables in the Dockerfile (ASGI interface, factory, host, port, access log). Set any other Granian option the same way in your deployment, without rebuilding: for example GRANIAN_WORKERS=2 for two worker processes (each one builds its own server and tools), or GRANIAN_WORKERS_MAX_RSS=1024 to restart a worker that grows above 1 GiB — useful here, since python_executor runs user code.
This server cannot be deployed
Maintenance
Related MCP Connectors
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Paid remote MCP for governed database query review, SQL simulation, approvals, and audits.
Read-only MCP access to sessions, funnels, campaigns, errors, live visitors, and anomalies.
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables SQL query execution and database structure browsing via MCP tools and resources.44 PyPIMIT
- AlicenseNot gradedqualityDmaintenanceEnables inspecting database schemas and executing read-only SQL queries on a PostgreSQL database via MCP tools.2 npmMIT
- FlicenseNot gradedqualityDmaintenanceEnables secure execution of Python code, SQL queries, and metric fetching through MCP with ephemeral Docker sandboxing.-
- AlicenseNot gradedqualityDmaintenanceEnables AI agents and workflows to safely explore and query data in ephemeral sandboxed databases via MCP, with guardrails and snapshot capabilities.21MIT