Docs

Python

Agent toolkit

WhireToolkit serves the same 33 tools as the MCP server to any function-calling model, runs the calls through WhireClient, and turns every error into a dict the model can read instead of raising. Money-moving tools wait for a human.

Bash
pip install "whire>=0.2"

Give a model the tools#

from whire import WhireToolkit

toolkit = WhireToolkit(environment="sandbox")
tools = toolkit.get_tools("openai")  # Chat Completions
# toolkit.get_tools("openai-responses") for the Responses API

Put toolkit.system_prompt in the system message. Narrow the set when the agent needs less:

Python
tools = toolkit.get_tools(
    "anthropic",
    names={"get_capabilities", "authorize_payment", "list_payouts"},
    exclude_destructive=True,
)

Run the calls#

The loop is the same for every provider: send the tools, run each call the model makes through execute, and return the result as a string. hint() adds a sentence of guidance derived from the result, such as what to do next.

Python
import json

async def tool_result(name: str, arguments: dict) -> str:
    result = await toolkit.execute(name, arguments)      # never raises
    text = json.dumps(result)
    hint = toolkit.hint(name, arguments, result)
    return f"{text}\n\nHint: {hint}" if hint else text
Provider Read the call from Send the result as
OpenAI Chat Completions call.function.name, json.loads(call.function.arguments) {"role": "tool", "tool_call_id": call.id, "content": text}
OpenAI Responses item.name, json.loads(item.arguments) {"type": "function_call_output", "call_id": item.call_id, "output": text}
Anthropic Messages block.name, block.input {"type": "tool_result", "tool_use_id": block.id, "content": text}

Arguments are camelCase, as on the server; snake_case keys and numeric strings are normalised. A failed call comes back as the error dict, so the model can correct itself:

Python
error = await toolkit.execute("get_payout_status", {"payoutId": "nope"})
print(error["error_code"], error["is_input_error"], error["suggestion"])

Confirm before money moves#

execute_payout and pay_x402_resource move money. Without a decision from you they answer confirmation_required and do nothing. Pass a confirm callback, sync or async, and it receives a summary of what is about to happen: amount, currency, payee, mandate.

Python
def ask_human(name: str, arguments: dict, summary: dict) -> bool:
    print(f"{name}: send {summary['amount']} {summary['currency']}"
          f" to {summary['counterpartyName']}?")
    return input("y/N ").lower() == "y"

toolkit = WhireToolkit(environment="sandbox", confirm=ask_human)

Returning anything but True answers confirmation_declined and nothing moves. allow_destructive=True skips the gate; use it only when the host already asks the user, as MCP clients do. require_confirmation={"…"} adds further tools to the gate.

tip

The mandate bounds what the agent can do, and the confirmation puts a person in front of the last step. Keep both.

Talk to the MCP server#

client.mcp calls the hosted /mcp endpoint directly, over the same connection, key and retry policy as the rest of the client, without the mcp package.

Python
from whire import WhireClient

async with WhireClient(environment="sandbox") as client:
    tools = await client.mcp.list_tools()
    caps = await client.mcp.call_tool("get_capabilities", {})
    provider = await client.mcp.read_resource("config://provider")

Use it in Claude Desktop#

Clients with no URL field can run the SDK's stdio server, which serves the same tools and proxies resources and prompts to the deployment:

JSON
{
  "mcpServers": {
    "whire": {
      "command": "/absolute/path/to/.venv/bin/python",
      "args": ["-m", "whire.mcp_server"],
      "env": { "WHIRE_API_KEY": "your-api-key", "WHIRE_ENVIRONMENT": "sandbox" }
    }
  }
}

Use the full path of the interpreter you installed whire into; Claude Desktop does not inherit your shell's PATH. The server is configured by environment variables:

Variable Meaning
WHIRE_API_KEY The key. Required for production.
WHIRE_ENVIRONMENT sandbox (default) or production.
WHIRE_BASE_URL A self-hosted deployment. Wins over the environment.
WHIRE_ALLOW_DESTRUCTIVE Default true: the host asks the user for each call, so the server does not ask again.
WHIRE_TIMEOUT Read timeout in seconds. Default 30.
WHIRE_LOG_LEVEL Log level on stderr. Stdout carries only protocol messages.

Pay for a resource over x402#

An agent that meets a 402 Payment Required can read the price and pay it under a mandate:

Python
quote = await client.x402.quote(url="https://vendor.example/report")  # commits nothing
if not quote.free and quote.requirement is not None:
    price = quote.requirement
    print(price.amount, price.asset, "to", price.pay_to)
    paid = await client.x402.pay(
        url=quote.url, mandate_id=mandate.mandate_id, reason="Q3 report"
    )
    print(paid.paid, paid.status, paid.payout_id)

A seller verifies and settles with client.x402.verify and client.x402.settle. See x402 over SEPA.