Architecture Guide
How to Build an AI Agent With Multiple Tools
Connecting an AI agent to one tool takes an afternoon. Connecting it to fifty tools is a distributed systems problem. This guide walks through the architecture — tool discovery, authentication, routing, error handling, and billing — and shows how a gateway collapses that complexity into a single endpoint.
One Tool Is Easy. Fifty Tools Is Infrastructure.
Every tutorial on building AI agents makes it look simple. Import a library, define a tool, call it from the LLM. And for a single tool, it is simple. The agent searches the web, summarizes the results, and you ship it.
Then the requirements grow. The agent needs to search the web and send emails and query a database and generate images and check DNS records and scan code for vulnerabilities. Each tool has its own API key, its own authentication scheme, its own rate limits, its own error format, and its own SDK. You are no longer building an agent — you are building a tool management platform.
This is the gap between a demo and a production agent. The intelligence is in the LLM. The hard engineering is in the tool infrastructure.
The Five Problems of Multi-Tool Agents
Before looking at solutions, name the problems. Every multi-tool agent hits these five:
- Tool discovery. The agent needs to know what tools exist, what they do, and what inputs they accept. Hardcoding tool definitions does not scale past a handful.
- Authentication. Each tool provider issues its own API key. Managing 50 keys — rotation, scoping, secure storage — becomes a security project.
- Routing. Different tools speak different protocols: REST, GraphQL, MCP, gRPC. The agent needs a uniform way to call any of them.
- Error handling. Tavily returns errors differently than Resend, which returns errors differently than Stripe. Without normalization, every tool needs its own error parser.
- Billing. Some tools are free, some charge per call, some charge monthly. If the agent runs autonomously, you need spend controls or it will drain every account it touches.
The Architecture: How a Gateway Solves All Five
An MCP gateway sits between the agent and the tool providers. It handles discovery, auth, routing, error normalization, and billing so the agent code stays clean. Here is how to build on top of one, step by step.
Step 1: Tool Discovery — Let the Agent Browse the Catalog
Instead of hardcoding tool definitions, query the catalog at runtime. The agent learns what is available, what each tool costs, and what inputs it needs.
# List all 87 tools across 14 categories
curl https://toolroute.ai/api/v1/tools
# Filter by category
curl "https://toolroute.ai/api/v1/tools?category=security"
# Get tool definitions in OpenAI function-calling format
curl "https://toolroute.ai/api/v1/tools?format=openai"
# Search for a specific capability
curl "https://toolroute.ai/api/v1/tools?q=dns+records"In Python, you can fetch the catalog and pass it directly to your LLM as function definitions:
import requests, os
TOOLROUTE_KEY = os.environ["TOOLROUTE_KEY"]
BASE = "https://toolroute.ai/api/v1"
# Fetch every tool the agent can use
catalog = requests.get(f"{BASE}/tools?format=openai").json()
print(f"Agent has access to {len(catalog)} tools")
# Agent has access to 87 tools
# Pass to any OpenAI-compatible LLM
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Find DNS records for example.com"}],
tools=catalog
)The agent never needs a hardcoded tool list. When new tools are added to the registry, every agent that queries the catalog picks them up automatically.
Step 2: Authentication — One Key for Everything
Instead of managing 50 API keys, generate one ToolRoute key. It authenticates against all 87 tools through 47 adapters. The gateway holds the upstream credentials.
export TOOLROUTE_KEY="tr_live_abc123..."
# This one key works for Tavily, Resend, Stripe, GoDaddy,
# Semgrep, Playwright, and 81 other tools.
# No per-provider key management.If you already have an API key with a specific provider — say you are on a Tavily enterprise plan — register it via BYOK and the gateway routes calls through your key instead:
# Register your own Tavily key — calls use your key, not credits
curl -X POST https://toolroute.ai/api/v1/byok \
-H "Authorization: Bearer $TOOLROUTE_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "tavily",
"api_key": "tvly-your-enterprise-key..."
}'Step 3: Execution — One Endpoint, Any Tool
Every tool call goes through POST /api/v1/execute. The gateway routes to the correct provider across all 47 adapters and normalizes the response into a consistent format.
# Search the web
curl -X POST https://toolroute.ai/api/v1/execute \
-H "Authorization: Bearer $TOOLROUTE_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "tavily",
"operation": "search",
"input": { "query": "multi-tool AI agent architecture 2026" }
}'
# Scan code for vulnerabilities
curl -X POST https://toolroute.ai/api/v1/execute \
-H "Authorization: Bearer $TOOLROUTE_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "semgrep",
"operation": "scan",
"input": { "code": "eval(user_input)", "language": "python" }
}'
# Send an email
curl -X POST https://toolroute.ai/api/v1/execute \
-H "Authorization: Bearer $TOOLROUTE_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "resend",
"operation": "send_email",
"input": {
"to": "team@company.com",
"subject": "Scan complete",
"html": "<p>No critical vulnerabilities found.</p>"
}
}'Same endpoint, same auth header, same response shape. Whether the tool is a free open-source scanner or a paid third-party API, the calling code is identical.
Step 4: Chaining Tools in a Workflow
Real agents do not call one tool. They chain multiple tools into a workflow: research a topic, analyze the results, generate a report, email it to a stakeholder. Here is a Python example that chains four tools:
import requests, os
KEY = os.environ["TOOLROUTE_KEY"]
BASE = "https://toolroute.ai/api/v1"
HEADERS = {
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json"
}
def execute(tool: str, operation: str, input_data: dict) -> dict:
"""Execute any tool through the gateway."""
resp = requests.post(f"{BASE}/execute", headers=HEADERS, json={
"tool": tool,
"operation": operation,
"input": input_data
})
resp.raise_for_status()
return resp.json()
# Step 1: Research — search for competitor pricing
research = execute("tavily", "search", {
"query": "AI security scanning pricing 2026"
})
# Step 2: Analyze — extract structured data from search results
analysis = execute("openai", "chat", {
"model": "gpt-4o-mini",
"messages": [{
"role": "user",
"content": f"Extract competitor names and prices: {research['result']}"
}]
})
# Step 3: Store — save to a database
execute("supabase", "insert", {
"table": "competitor_intel",
"data": {"analysis": analysis["result"], "source": "tavily"}
})
# Step 4: Notify — email the team
execute("resend", "send_email", {
"to": "strategy@company.com",
"subject": "Competitor pricing update",
"html": f"<pre>{analysis['result']}</pre>"
})
print("4-tool workflow complete")Four different providers — Tavily, OpenAI, Supabase, Resend — all through the same execute() function. No SDK imports, no per-provider error handling, no credential juggling.
Step 5: Billing and Spend Controls
Autonomous agents need guardrails. Without spend controls, a loop that calls a paid tool 10,000 times will drain your account before you notice.
Context7, Playwright, Semgrep, GitHub MCP, and 30+ others cost nothing. No credits needed.
Each paid tool call deducts from your balance. When credits hit zero, calls stop. No surprise bills.
Register your own API key for any provider. Calls route through your key at the provider's rates.
Check your balance and usage programmatically to build spend alerts into your agent:
# Check balance before expensive operations
key_info = requests.get(
f"{BASE}/key",
headers={"Authorization": f"Bearer {KEY}"}
).json()
if key_info["credits_remaining"] < 100:
print("Low credits — pausing paid tool calls")
# Fall back to free tools onlyStep 6: Error Handling Across 47 Adapters
Every provider returns errors differently. The gateway normalizes them into a consistent format so your agent needs exactly one error handler:
def safe_execute(tool: str, operation: str, input_data: dict) -> dict:
"""Execute with standardized error handling."""
try:
resp = requests.post(f"{BASE}/execute", headers=HEADERS, json={
"tool": tool,
"operation": operation,
"input": input_data
})
resp.raise_for_status()
return {"ok": True, "result": resp.json()}
except requests.HTTPError as e:
body = e.response.json()
# Every adapter returns the same error shape:
# { "error": { "code": "rate_limited", "message": "...", "retry_after": 30 } }
error = body.get("error", {})
if error.get("code") == "rate_limited":
time.sleep(error.get("retry_after", 10))
return safe_execute(tool, operation, input_data) # retry once
return {"ok": False, "error": error}
# Works identically whether the tool is Tavily, Resend, or Stripe
result = safe_execute("tavily", "search", {"query": "MCP tools"})Architecture Diagram: What This Looks Like in Production
Your Agent (Python / Node / Any Language)
|
| Single API key
v
ToolRoute Gateway (/api/v1/execute)
|
+-- Tool Discovery (87 tools, 14 categories)
+-- Auth Router (47 adapters, BYOK support)
+-- Response Normalizer
+-- Credit Tracker + Rate Limiter
|
+----+------+------+------+------+
| | | | | |
Tavily Resend Stripe Semgrep ... +81 moreThe agent talks to one endpoint. The gateway fans out to whichever provider the tool maps to. The agent code never changes when tools are added, updated, or swapped out underneath.
Connecting via MCP Protocol (for Claude Code and MCP Clients)
If your agent speaks MCP natively, you can skip the REST layer entirely. Add the gateway as an MCP server and every tool appears as a native MCP tool call:
// .mcp.json — one entry gives your agent 87 tools
{
"toolroute": {
"type": "http",
"url": "https://toolroute.ai/mcp",
"headers": {
"Authorization": "Bearer tr_live_abc123..."
}
}
}No individual server processes. No dependency management. No port conflicts. The gateway handles all of it, and your MCP client sees a flat list of tools it can call.
When to Build Your Own vs. Use a Gateway
| Scenario | Build Your Own | Use a Gateway |
|---|---|---|
| 1-3 tools | Simple, full control | Overkill |
| 10+ tools | Key management headache | One key, one endpoint |
| Autonomous agents | Build your own billing | Spend controls built in |
| Sub-10ms latency | Direct connection | +50-100ms per hop |
| Custom tool logic | Modify anything | Standard operations only |
| Compliance (data residency) | Self-hosted infra | Depends on gateway provider |
Most production agents land in the 10-50 tool range. That is exactly where the infrastructure tax becomes the bottleneck and a gateway pays for itself in engineering time saved.
The Bottom Line
Building an AI agent that uses multiple tools is not an intelligence problem — it is an infrastructure problem. The LLM already knows how to decide which tool to call. What it needs is a clean interface to discover, authenticate, execute, and handle errors across dozens of providers without your codebase turning into a credential management system.
A gateway gives you that interface. One API key. One endpoint. One error format. Eighty-seven tools. The agent code stays focused on what it should be focused on: the task, not the plumbing.
ToolRoute provides 87 tools across 14 categories through one endpoint. Browse the catalog, read the docs, try the playground, or see pricing.