Skip to content

python-billing-agent

A runnable Python example that defines three billing-related tools with @platools.tool(), hosts them in a tiny FastAPI process, and wires platools doctor + platools test into a make target.

What’s in the box

examples/python-billing-agent/
├── README.md
├── pyproject.toml
├── .env.example
├── .gitignore
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI host + platools.connect() bootstrap
│ ├── tools.py # @platools.tool() decorated functions
│ └── models.py # Pydantic models for inputs/outputs
├── platools-tests.yaml # batch test file for `platools test`
└── Makefile

The tools

app/tools.py registers three tools on a shared Platools() instance:

  • list_invoices(customer_id) -> list[Invoice] - read-only, auth="user", no role gate. Returns the most recent invoices for a customer.
  • create_invoice(customer_id, amount_cents, currency, description) -> Invoice - auth="admin", roles=["billing"]. Creates a draft invoice.
  • void_invoice(invoice_id, reason) -> VoidResult - auth="admin", roles=["billing"], annotations={"destructive": True}. Voids an existing invoice and logs the reason.

Every parameter has a type hint and a docstring description. Every return type is a BaseModel. platools doctor ships green out of the box.

Running it

Terminal window
cd examples/python-billing-agent
# One-time setup
cp .env.example .env # fill in PLATOS_URL + PLATOS_SECRET (or leave blank for doctor-only flows)
uv sync
# Static analysis - the ship gate
uv run platools doctor app.tools
# Local smoke tests (no platform required)
uv run platools test --module app.tools
# Run the FastAPI host + outbound WebSocket (requires PLATOS_URL + PLATOS_SECRET)
uv run uvicorn app.main:app --reload --port 8080

If you don’t have a Platos platform deployed yet, uv run platools serve --module app.tools gives you a local MCP endpoint you can point Claude Desktop at - see local-dev-loop for the walkthrough.

What to learn from it

  1. One Platools() per process. app/tools.py creates the instance; app/main.py imports it and calls asyncio.create_task(platools.connect()) during FastAPI startup.
  2. Pydantic models are first-class. Inputs and outputs both use BaseModel subclasses - the schema generator walks the annotations recursively.
  3. Doctor in CI. The Makefile’s check target runs uv run platools doctor app.tools and exits non-zero on any error finding.
  4. platools-tests.yaml is the smoke test. One happy-path case and one expected-error case per tool. Coverage target is 100% - if you add a tool, you add a case.
  5. Secrets stay out of the repo. .env.example is committed, .env is gitignored, and the app reads os.environ rather than hardcoding anything.

Source: examples/python-billing-agent/

Next steps