Securing LLM Tool Calls in Production
Tool calls are where your LLM agent actually changes things—and that's exactly where security breaks down in production. This guide shows you how to validate, authorize, and log every tool execution so your AI system can't be exploited by malicious prompts or user manipulation.
Why Tool Call Security Matters
When Claude has access to tools—database writes, API calls, file operations—you're giving it execution privileges. A crafted prompt or compromised user input can trick the model into running unauthorized operations. Unlike traditional applications where code paths are fixed, LLM agents can be steered toward unintended tool combinations by prompt injection.
Production systems need three layers: permission checking (does this user own this resource?), input validation (is the data safe to execute?), and audit trails (what actually ran and why?). Skipping any one creates exploitable gaps.
Permission Models for Tool Execution
Every tool call should check user context before executing. Store user permissions in your database (Supabase works well here) and enforce them at the tool handler level, not just the UI.
Your tool handlers should receive user_id and check ownership before acting. For example, a 'write_memo' tool shouldn't let user A modify user B's data just because the prompt says to.
export async function writeMemo(userId: string, memoId: string, content: string) {
const { data: memo } = await supabase
.from('memos')
.select('owner_id')
.eq('id', memoId)
.single();
if (memo.owner_id !== userId) throw new Error('Unauthorized');
return supabase
.from('memos')
.update({ content, updated_at: new Date() })
.eq('id', memoId);
}Input Validation at Tool Boundaries
Claude's tool use includes structured arguments, but the model can still generate invalid or malicious payloads. Validate every input: type check, bounds check, pattern match. Use Zod or similar schema validators before passing data to your backend.
Common exploits include oversized strings (DoS), negative numbers in quantity fields, or SQL-like patterns in text. Validate early, reject strictly.
Audit Logging Every Tool Call
Log what ran, who triggered it, what inputs it received, and what happened. Store these immutably in Supabase. This is your forensic record for debugging unexpected behavior and proving compliance.
Include: timestamp, user_id, tool_name, input_arguments, result status, and error messages (but not sensitive data from results). Query logs when a user reports something strange.
Rate Limiting and Quota Enforcement
Tool calls can be expensive—database mutations, third-party API calls, computational work. Without quotas, a malicious prompt loop can drain your budget or degrade service. Implement per-user rate limits on sensitive tools.
Track cumulative tool usage per user per period, and reject calls that exceed limits. Store these counters in Redis or a simple Supabase table with TTL logic.
Open-Source Implementation
The Pantheon project (github.com/lewisallena17/pantheon) provides a real-world reference for securing tool calls in Claude agents. It includes permission middleware, Zod schema validation, and Supabase audit logging built into Next.js route handlers.
Study how Pantheon structures tool handlers, enforces user context, and logs execution. You can adapt its patterns to your own agent system.
Open-source implementation
Everything in this article runs in pantheon — a production-ready Next.js + Supabase + Claude starter. Clone it, deploy to Vercel, run PM2. The dashboard auto-commits every agent edit and reverts itself if TypeScript breaks.
◈ Tools mentioned
- Supabase — open-source Firebase alt
- Vercel — zero-config Next.js hosting
- Claude — AI assistant by Anthropic
- Gumroad — sell digital products
Some links may pay us a referral if you sign up. Never affects the price you pay.
Get the full starter kit
Start with permission checks and input validation today—audit logging and rate limits follow. Get the full starter kit and secure your first production agent.