Building LLM Agents with Tool Use for DevOps Automation Using Claude API
How to build LLM agents that use tools to automate DevOps tasks — querying Kubernetes, running Terraform, checking AWS resources — using Claude API's tool use feature with real production patterns.
The difference between an LLM chatbot and an LLM agent is tool use. A chatbot answers questions. An agent takes actions — it can query your Kubernetes cluster, check AWS costs, run a Terraform plan, and combine results to give you a complete picture.
Claude's tool use (function calling) makes this straightforward to implement. Here is how to build DevOps agents that actually do things rather than just talk about them.
How Tool Use Works
You give Claude a list of tools with descriptions and schemas. When Claude needs information it does not have, it requests a tool call. You execute the tool and return the result. Claude continues reasoning with the new information.
import anthropic
import json
import subprocess
client = anthropic.Anthropic()
# Define the tools Claude can use
DEVOPS_TOOLS = [
{
"name": "kubectl_get",
"description": "Run kubectl get to list Kubernetes resources. Returns the output.",
"input_schema": {
"type": "object",
"properties": {
"resource_type": {
"type": "string",
"description": "Resource type: pods, deployments, services, nodes, etc."
},
"namespace": {
"type": "string",
"description": "Kubernetes namespace. Use 'all-namespaces' to query all."
},
"flags": {
"type": "string",
"description": "Additional kubectl flags like '-o wide' or '-l app=myapp'"
}
},
"required": ["resource_type"]
}
},
{
"name": "kubectl_describe",
"description": "Describe a specific Kubernetes resource to get detailed information and events.",
"input_schema": {
"type": "object",
"properties": {
"resource_type": {"type": "string"},
"name": {"type": "string"},
"namespace": {"type": "string", "default": "default"}
},
"required": ["resource_type", "name"]
}
},
{
"name": "kubectl_logs",
"description": "Get logs from a Kubernetes pod or deployment.",
"input_schema": {
"type": "object",
"properties": {
"pod_name": {"type": "string"},
"namespace": {"type": "string", "default": "default"},
"container": {"type": "string"},
"lines": {"type": "integer", "default": 50},
"previous": {"type": "boolean", "default": False}
},
"required": ["pod_name"]
}
},
{
"name": "aws_cli",
"description": "Run an AWS CLI command. Use for querying AWS resources like EC2, RDS, costs.",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "Full AWS CLI command starting with 'aws', e.g. 'aws ec2 describe-instances --query ...'"
}
},
"required": ["command"]
}
},
{
"name": "run_shell",
"description": "Run a safe, read-only shell command for diagnostics.",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "Shell command to run. Only read-only commands allowed."
}
},
"required": ["command"]
}
}
]Step 2: Tool Execution Functions
SAFE_SHELL_COMMANDS = {"curl", "dig", "nslookup", "ping", "nc", "telnet", "cat", "ls", "df", "ps", "top"}
BLOCKED_KUBECTL_VERBS = {"delete", "apply", "patch", "exec", "port-forward"}
def execute_tool(tool_name: str, tool_input: dict) -> str:
"""Execute a tool call and return the result as a string."""
try:
if tool_name == "kubectl_get":
return _kubectl_get(**tool_input)
elif tool_name == "kubectl_describe":
return _kubectl_describe(**tool_input)
elif tool_name == "kubectl_logs":
return _kubectl_logs(**tool_input)
elif tool_name == "aws_cli":
return _aws_cli(**tool_input)
elif tool_name == "run_shell":
return _run_shell(**tool_input)
else:
return f"Unknown tool: {tool_name}"
except Exception as e:
return f"Tool error: {str(e)}"
def _kubectl_get(resource_type: str, namespace: str = "default", flags: str = "") -> str:
cmd = ["kubectl", "get", resource_type]
if namespace == "all-namespaces":
cmd.append("--all-namespaces")
else:
cmd.extend(["-n", namespace])
if flags:
cmd.extend(flags.split())
cmd.append("--no-headers")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
output = result.stdout or result.stderr
return output[:3000] if output else "No output"
def _kubectl_describe(resource_type: str, name: str, namespace: str = "default") -> str:
cmd = ["kubectl", "describe", resource_type, name, "-n", namespace]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
output = result.stdout or result.stderr
return output[:4000] if output else "No output"
def _kubectl_logs(
pod_name: str,
namespace: str = "default",
container: str = None,
lines: int = 50,
previous: bool = False
) -> str:
cmd = ["kubectl", "logs", pod_name, "-n", namespace, f"--tail={lines}"]
if container:
cmd.extend(["-c", container])
if previous:
cmd.append("--previous")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
output = result.stdout or result.stderr
return output[-3000:] if output else "No logs found"
def _aws_cli(command: str) -> str:
# Safety: only allow read operations
blocked = ["delete", "terminate", "remove", "put", "create", "update", "modify"]
cmd_lower = command.lower()
for blocked_word in blocked:
if f" {blocked_word}" in cmd_lower:
return f"Blocked: '{blocked_word}' operations not allowed in read-only mode"
parts = command.split()
result = subprocess.run(parts, capture_output=True, text=True, timeout=60)
output = result.stdout or result.stderr
return output[:3000] if output else "No output"
def _run_shell(command: str) -> str:
first_word = command.split()[0]
if first_word not in SAFE_SHELL_COMMANDS:
return f"Blocked: '{first_word}' not in allowed commands list"
result = subprocess.run(
command, shell=True, capture_output=True, text=True, timeout=30
)
output = result.stdout or result.stderr
return output[:2000] if output else "No output"Step 3: The Agent Loop
def run_devops_agent(user_request: str, max_iterations: int = 10) -> str:
"""
Run an agentic loop that uses tools to answer DevOps questions.
"""
print(f"Agent starting: {user_request}")
messages = [{"role": "user", "content": user_request}]
system_prompt = """You are an expert DevOps engineer assistant with access to tools
to query Kubernetes clusters, AWS resources, and run diagnostics.
When investigating issues:
1. Start by gathering information — do not assume, verify with tools
2. Use specific queries rather than broad ones to avoid overwhelming output
3. Follow the evidence — if logs show an error, dig into that error
4. Provide a clear diagnosis and specific remediation steps at the end
You have read-only access — you cannot modify resources. Focus on diagnosis."""
for iteration in range(max_iterations):
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=4096,
system=system_prompt,
tools=DEVOPS_TOOLS,
messages=messages
)
# Add assistant response to history
messages.append({"role": "assistant", "content": response.content})
# Check stop reason
if response.stop_reason == "end_turn":
# Extract the final text response
for block in response.content:
if hasattr(block, "text"):
return block.text
return "Agent completed without text response"
if response.stop_reason != "tool_use":
break
# Process tool calls
tool_results = []
for block in response.content:
if block.type == "tool_use":
print(f" → Calling tool: {block.name}({json.dumps(block.input)[:100]})")
result = execute_tool(block.name, block.input)
print(f" ← Result: {result[:100]}...")
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result
})
# Add tool results to conversation
messages.append({"role": "user", "content": tool_results})
return "Agent reached iteration limit without completing"Using the Agent
if __name__ == "__main__":
# Example 1: Investigate a failing deployment
result = run_devops_agent(
"My payment-api deployment in the production namespace has been showing "
"increased error rates for the last 30 minutes. Can you investigate what's "
"happening and tell me the root cause?"
)
print(result)
# Example 2: Cost investigation
result = run_devops_agent(
"Our AWS bill spiked by $2000 this week. Can you check what EC2 instances "
"are running and if there are any unusual resources that might explain the increase?"
)
print(result)
# Example 3: Capacity planning
result = run_devops_agent(
"How many pods are running in each namespace in our cluster? "
"Which namespaces are using the most resources?"
)
print(result)Example Agent Trace
Agent starting: My payment-api deployment is in CrashLoopBackOff
→ Calling tool: kubectl_get(resource_type=deployments, namespace=production)
← Result: payment-api 0/3 3 0 5m
→ Calling tool: kubectl_get(resource_type=pods, namespace=production, flags=-l app=payment-api)
← Result: payment-api-7d9f-xk2p4 0/1 CrashLoopBackOff 5 5m
→ Calling tool: kubectl_describe(resource_type=pod, name=payment-api-7d9f-xk2p4, namespace=production)
← Result: ...Events: Back-off restarting failed container, Last State: Terminated (exit code 1)...
→ Calling tool: kubectl_logs(pod_name=payment-api-7d9f-xk2p4, namespace=production, previous=True)
← Result: Error: STRIPE_SECRET_KEY environment variable is not set...
**Diagnosis**: The payment-api deployment is crashing because the STRIPE_SECRET_KEY
environment variable is missing. The pod starts, immediately finds the missing env var,
and exits with code 1.
**Root cause**: A Secret reference is likely missing from the deployment's env configuration.
**Fix**:
1. Verify the secret exists: `kubectl get secret stripe-credentials -n production`
2. If missing, create it: `kubectl create secret generic stripe-credentials -n production --from-literal=STRIPE_SECRET_KEY=sk_prod_...`
3. Ensure the deployment references it: check that env.valueFrom.secretKeyRef is configured
4. Roll out: `kubectl rollout restart deployment/payment-api -n production`
The agent went from a vague "CrashLoopBackOff" report to an exact diagnosis and actionable fix, using three tool calls that a human would have run manually. That is the value of tool-using agents over simple Q&A.
More AI agent content? Read our build a DevOps AI agent with LangGraph and AI-powered incident response with LLM runbooks.
Today I Fixed
Short real fixes from production — posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Build an AI Deployment Health Checker with Claude API and Kubernetes
Step-by-step tutorial to build an AI-powered deployment health checker using Claude API and the Kubernetes Python client. Automatically diagnose failing pods, check resource limits, and get plain-English explanations of what's wrong.
Build an AI Kubernetes Deployment Readiness Checker with Claude API
Build a Python CLI tool using Claude API that analyzes Kubernetes YAML manifests before deployment — catches missing resource limits, root containers, and security issues with a go/no-go score.
Build an AI Kubernetes Runbook Generator with Claude API
Step-by-step tutorial to build a tool that automatically generates operational runbooks for any Kubernetes resource using Claude API — with real examples for Deployments, StatefulSets, and CronJobs.