Tools

Make Claude return schema-valid JSON: use tool use instead of “please return JSON”

TL;DR

Declare a tool with an input_schema (JSON Schema), set tool_choice to force that tool, then read the input of the tool_use block. Validate server-side and retry on failure. The Claude API also has an official structured outputs feature — check the docs to pick the right approach.

Example: ticket extraction

tools = [{
  "name": "save_ticket",
  "description": "Save a classified ticket",
  "input_schema": {
    "type": "object",
    "properties": {
      "category": {"type": "string", "enum": ["bug", "billing", "feature", "other"]},
      "priority": {"type": "string", "enum": ["P1", "P2", "P3"]},
      "summary":  {"type": "string"}
    },
    "required": ["category", "priority", "summary"]
  }
}]

msg = client.messages.create(
  model="claude-sonnet-5-5", max_tokens=500, tools=tools,
  tool_choice={"type": "tool", "name": "save_ticket"},
  messages=[{"role": "user", "content": ticket_text}])

data = next(b.input for b in msg.content if b.type == "tool_use")

Why it beats prompting

  • enum constrains values — no more “High”, “high”, “urgent” mixed together.
  • required enforces every field.
  • No “Here is your JSON:” preamble breaking your parser.

Production checklist

  • Re-validate with Pydantic / Zod on the server.
  • Retry 1–2 times with the validation error.
  • Log inputs/outputs for debugging.
  • Use a small model for simple extraction, plus the Batch API for bulk runs.