Skip to content
Operations
Skill

/tool-use-structured-output

Use Bedrock tool_use to guarantee structured JSON outputs from Claude models. Eliminates JSON parsing failures by forcing responses through typed tool schemas.

BOOST
From plugin
guidance-for-claude-code-with-amazon-bedrock
4094 skills25 agents23 commands
Install
$ npx -y skills add aws-solutions-library-samples/guidance-for-claude-code-with-amazon-bedrock --skill tool-use-structured-output --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/tool-use-structured-output

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use Bedrock tool_use to guarantee structured JSON outputs from Claude models. Eliminates JSON parsing failures by forcing responses through typed tool schemas.

SKILL.md

tool-use-structured-output.SKILL.md
name: tool-use-structured-output
description: Use Bedrock tool_use to guarantee structured JSON outputs from Claude models. Eliminates JSON parsing failures by forcing responses through typed tool schemas.

This skill demonstrates using Amazon Bedrock's tool_use feature to get highly reliable structured JSON outputs from Claude models. Instead of hoping the model returns valid JSON, define a tool schema that forces the exact structure you need.

The Problem

Asking Claude to "return JSON" fails 10-30% of the time:

  • Markdown code fences around JSON
  • Truncated responses mid-object
  • Extra text before/after JSON
  • Invalid escaping in strings

The Solution

Define a tool with your exact output schema. Claude will call the tool with arguments that should match the schema structure. While tool use strongly steers the model toward valid structured output, you should still validate the response and have fallback handling for edge cases.

Implementation Pattern

1. Define Tool Schema

tools = [
    {
        "name": "emit_extraction_result",
        "description": "Return the extracted course information",
        "input_schema": {
            "type": "object",
            "required": ["courses", "confidence", "page_number"],
            "properties": {
                "courses": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "required": ["course_code", "title"],
                        "properties": {
                            "course_code": {
                                "type": "string",
                                "description": "Course identifier (e.g., CS 101)"
                            },
                            "title": {
                                "type": "string",
                                "description": "Course title"
                            },
                            "credits": {
                                "type": "number",
                                "description": "Credit hours"
                            },
                            "description": {
                                "type": "string",
                                "description": "Course description"
                            }
                        }
                    }
                },
                "confidence": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1,
                    "description": "Extraction confidence score"
                },
                "page_number": {
                    "type": "integer",
                    "description": "Source page number"
                }
            }
        }
    }
]

2. Invoke Bedrock with Tool

import boto3
import json

bedrock = boto3.client('bedrock-runtime', region_name='us-east-1')

response = bedrock.invoke_model(
    modelId='anthropic.claude-3-5-sonnet-20241022-v2:0',
    body=json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 4096,
        "tools": tools,
        "tool_choice": {"type": "tool", "name": "emit_extraction_result"},  # Force tool use
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": {
                            "type": "base64",
                            "media_type": "image/png",
                            "data": base64_image_data
                        }
                    },
                    {
                        "type": "text",
                        "text": "Extract all courses from this catalog page."
                    }
                ]
            }
        ]
    })
)

3. Parse Response

result = json.loads(response['body'].read())

# Find tool use in response
for content_block in result.get('content', []):
    if content_block.get('type') == 'tool_use':
        tool_name = content_block.get('name')
        tool_input = content_block.get('input')

        # tool_input should match your schema structure
        # Always validate before using in production
        courses = tool_input['courses']
        confidence = tool_input['confidence']
        page_number = tool_input['page_number']

        # No JSON parsing needed - it's already a dict!
        break

Advanced Patterns

Enum Constraints

Force specific values:

"status": {
    "type": "string",
    "enum": ["approved", "rejected", "needs_review"]
}

Numeric Ranges

Constrain numbers:

"accuracy": {
    "type": "number",
    "minimum": 0,
    "maximum": 1
},
"score": {
    "type": "integer",
    "enum": [0, 1, 2, 3, 4, 5]  # Discrete scale
}

Nested Objects

Complex structures:

"evaluation": {
    "type": "object",
    "properties": {
        "field_scores": {
            "type": "object",
            "properties": {
                "course_code": {"type": "number", "enum": [0, 0.5, 1]},
                "title": {"type": "number", "enum": [0, 0.5, 1]},
                "credits": {"type": "number", "enum": [0, 0.5, 1]}
            }
        },
        "overall_accuracy": {"type": "number"},
        "reasoning": {"type": "string"}
    }
}

Lambda Integration

import boto3
import json
import logging
from typing import TypedDict, List, NotRequired

logger = logging.getLogger(__name__)

class Course(TypedDict):
    course_code: str
    title: str
    credits: NotRequired[float | None]
    description: NotRequired[str]

class ExtractionResult(TypedDict):
    courses: List[Course]
    confidence: float
    page_number: int

def extract_courses(image_base64: str) -> ExtractionResult:
    bedrock = boto3.client('bedrock-runtime')

    response = bedrock.invoke_model(
        modelId='anthropic.claude-3-5-sonnet-20241022-v2:0',
        body=json.dumps({
Read more
Ships withguidance-for-claude-code-with-amazon-bedrock

This guidance enables enterprise deployment of Claude Code and Claude Cowork on Amazon Bedrock across command-line (CLI) and desktop surfaces — with secure single sign-on (SSO), usage monitoring, and cost controls.

Get the whole plugin