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.
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 APIfrom whire import WhireToolkit
toolkit = WhireToolkit(environment="sandbox")
tools = toolkit.get_tools("anthropic")from whire import WhireToolkit
toolkit = WhireToolkit(environment="sandbox")
tools = toolkit.get_tools("mcp") # the server's definitions, verbatimPut toolkit.system_prompt in the system message. Narrow the set when the agent needs less:
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.
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:
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.
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.
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.
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:
{
"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:
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.