Examples
The toolcue packages hold the client and the framework integrations; these examples use them. For the framework setups, see Integrations.
Install#
pip install toolcue # Python 3.10+, no dependencies
npm install toolcue # Node 18+, no dependencies| Python extra | Module | Guide |
|---|---|---|
toolcue[langchain] | toolcue.langchain | LangChain, LangGraph |
toolcue[pydantic-ai] | toolcue.pydantic_ai | Pydantic AI |
toolcue[openai-agents] | toolcue.openai_agents | OpenAI Agents SDK |
For the Vercel AI SDK, npm install toolcue ai zod and import from toolcue/ai-sdk (guide).
Run the quickstart#
With your access, set three environment variables and run either quickstart. It publishes the sample catalog to an agent called acme-billing and asks for the tools of a few turns.
export TOOLCUE_URL=... # the base URL you were given
export TOOLCUE_TOKEN=... # read token: per-turn calls
export TOOLCUE_ADMIN_TOKEN=... # admin token: publishing
python3 python/quickstart.py
node typescript/quickstart.ts # Node 23.6+; older Node: npx tsx typescript/quickstart.tsKeep catalog.json one folder above the scripts, as in the download paths below.
The sample catalog#
A billing assistant with 9 tools: lookups that declare the ids they return (produces), an admin-only tool (requiredRoles) and an always-on "ask the user" (core).
{
"tools": [
{"name": "find_customer", "displayName": "Find customer",
"description": "Look up a customer by name or email and return its customer_id.",
"parameters": [{"name": "query", "description": "Customer name or email", "dataType": "string", "required": true}],
"produces": ["customer_id"]},
{"name": "get_customer_invoices", "displayName": "List a customer's invoices",
"description": "List the invoices of one customer, newest first, with status and amount due.",
"parameters": [{"name": "customer_id", "description": "The customer's id", "dataType": "string", "required": true}]},
{"name": "refund_invoice", "displayName": "Refund an invoice",
"description": "Refund a paid invoice fully or partially.",
"parameters": [{"name": "invoice_id", "description": "The invoice id, e.g. INV-1042", "dataType": "string", "required": true},
{"name": "amount", "description": "Amount to refund; omit for a full refund", "dataType": "number", "required": false}],
"examples": ["give them their money back", "refund INV-1042"]},
{"name": "create_invoice", "displayName": "Create an invoice",
"description": "Create and send a new invoice to a customer.",
"parameters": [{"name": "customer_id", "description": "The customer's id", "dataType": "string", "required": true},
{"name": "amount", "description": "Total amount", "dataType": "number", "required": true}]},
{"name": "change_plan", "displayName": "Change subscription plan",
"description": "Upgrade or downgrade a customer's subscription plan.",
"parameters": [{"name": "customer_id", "description": "The customer's id", "dataType": "string", "required": true},
{"name": "plan", "description": "Target plan: starter, growth or scale", "dataType": "string", "required": true}]},
{"name": "cancel_subscription", "displayName": "Cancel subscription",
"description": "Cancel a customer's subscription at the end of the billing period.",
"parameters": [{"name": "customer_id", "description": "The customer's id", "dataType": "string", "required": true}]},
{"name": "export_report", "displayName": "Export revenue report",
"description": "Export monthly revenue, MRR and churn as a CSV file.",
"parameters": [{"name": "month", "description": "Month as YYYY-MM", "dataType": "string", "required": true}]},
{"name": "update_tax_settings", "displayName": "Update tax settings",
"description": "Change the default tax rate or VAT number on invoices.",
"parameters": [{"name": "tax_rate", "description": "Tax rate in percent", "dataType": "number", "required": false}],
"requiredRoles": ["admin"]},
{"name": "ask_user", "displayName": "Ask the user",
"description": "Ask the user a clarifying question with optional choices.",
"parameters": [{"name": "question", "description": "The question", "dataType": "string", "required": true}],
"core": true}
]
}Python#
"""Publish the sample catalog and select tools for a few turns.
pip install toolcue
export TOOLCUE_URL=... TOOLCUE_TOKEN=... TOOLCUE_ADMIN_TOKEN=...
python3 quickstart.py
"""
import json
import os
from toolcue import ToolcueClient
AGENT = "acme-billing"
catalog = json.load(open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "catalog.json")))
toolcue = ToolcueClient()
summary = toolcue.publish_catalog(AGENT, catalog["tools"])
print(f"published {summary['tools']} tools, {summary['edges']} dependency links")
turns = [
("Refund the last invoice for Globex", [], {"roles": ["agent"]}),
("thanks!", [], {"roles": ["agent"]}),
("ok, move them to the scale plan", [
{"role": "user", "content": "what plan is Globex on?"},
{"role": "assistant", "content": "Globex (cus_8812) is on growth.", "toolCalls": [
{"name": "find_customer", "arguments": {"query": "Globex"},
"result": "{\"customer_id\": \"cus_8812\", \"plan\": \"growth\"}"}]}], {"roles": ["agent"]}),
]
for message, history, user in turns:
answer = toolcue.select(AGENT, message, history, user)
print(f"\n> {message}\n turn type: {answer['turnType']}, {answer['timings']['totalMs']:.0f} ms")
for t in answer["tools"]:
print(f" - {t['name']:<24} {t['reason']:<10} {t['detail']}")
if answer["resolvedParams"]:
print(f" values found in the conversation: {answer['resolvedParams']}")
found = toolcue.find_tools(AGENT, "set the VAT rate to 20%", user={"roles": ["admin"]})
print("\nfind_tools('set the VAT rate to 20%') as an admin:", [t["name"] for t in found["tools"]])TypeScript#
// Publish the sample catalog and select tools for one turn.
// npm install toolcue
// TOOLCUE_URL=... TOOLCUE_TOKEN=... TOOLCUE_ADMIN_TOKEN=... node quickstart.ts (Node 23.6+; older: npx tsx quickstart.ts)
import { readFileSync } from "node:fs";
import { ToolcueClient } from "toolcue";
const catalog = JSON.parse(readFileSync(new URL("../catalog.json", import.meta.url), "utf8"));
const toolcue = new ToolcueClient();
const summary = await toolcue.publishCatalog("acme-billing", catalog.tools);
console.log(`published ${summary.tools} tools, ${summary.edges} dependency links`);
const answer = await toolcue.select("acme-billing", "Refund the last invoice for Globex", [], { roles: ["agent"] });
console.log(answer.turnType, `${answer.timings.totalMs.toFixed(0)} ms`);
for (const t of answer.tools) console.log(`- ${t.name.padEnd(24)} ${t.reason.padEnd(10)} ${t.detail}`);A real LangChain agent#
Three turns with a real model (Claude or an OpenAI model), printing the tools the model got on every call: pip install "toolcue[langchain]" langchain-anthropic, set ANTHROPIC_API_KEY, and run it next to billing_tools.py.
"""A real LangChain agent with Toolcue: three turns with a real model, showing which tools the model got each time.
pip install -r requirements.txt # toolcue[langchain] and a model provider
export TOOLCUE_URL=... TOOLCUE_TOKEN=... TOOLCUE_ADMIN_TOKEN=...
export ANTHROPIC_API_KEY=... # or OPENAI_API_KEY=... and OPENAI_MODEL=...
python agent.py
"""
import os
import sys
from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.memory import InMemorySaver
from toolcue.langchain import ToolcueMiddleware, publish_tools
from billing_tools import EXTRAS, TOOLS
AGENT = "acme-billing"
@dataclass
class Session:
roles: list[str]
class ShowTools(AgentMiddleware):
"""Prints the tools each model call receives (after Toolcue filtered them)."""
def wrap_model_call(self, request, handler):
print(f" model gets {len(request.tools)}/{len(TOOLS) + 1} tools:",
", ".join(sorted(t.name for t in request.tools if hasattr(t, "name"))))
return handler(request)
def model_name() -> str:
if os.environ.get("ANTHROPIC_API_KEY"):
return "anthropic:claude-opus-5-5"
if os.environ.get("OPENAI_API_KEY") and os.environ.get("OPENAI_MODEL"):
return "openai:" + os.environ["OPENAI_MODEL"]
sys.exit("Set ANTHROPIC_API_KEY, or OPENAI_API_KEY and OPENAI_MODEL.")
def main():
summary = publish_tools(AGENT, TOOLS, EXTRAS)
print(f"published {summary['tools']} tools, {summary['edges']} dependency links")
agent = create_agent(
model_name(),
tools=TOOLS,
system_prompt="You are the billing assistant of Acme. Use the tools; never invent ids.",
middleware=[ToolcueMiddleware(AGENT, user=lambda session: {"roles": session.roles}), ShowTools()],
context_schema=Session,
checkpointer=InMemorySaver(),
)
thread = {"configurable": {"thread_id": "demo"}}
for text in ["Refund the last invoice for Globex", "thanks! what plan are they on now?",
"ok, move them to the scale plan"]:
print(f"\n> {text}")
result = agent.invoke({"messages": [HumanMessage(text)]}, thread, context=Session(roles=["agent"]))
calls = [c["name"] for m in result["messages"] for c in getattr(m, "tool_calls", [])]
print(" tool calls so far:", ", ".join(calls) or "none")
print(" answer:", result["messages"][-1].text)
if __name__ == "__main__":
main()