Using both features together
JSON outputs and strict tool use solve different problems and work together:
- JSON outputs control Claude's response format (what Claude says)
- Strict tool use validates tool parameters (how Claude calls your functions)
When combined, Claude can call tools with guaranteed-valid parameters AND return structured JSON responses. This is useful for agentic workflows where you need both reliable tool calls and structured final outputs.
bash
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "Help me plan a trip to Paris departing May 15, 2026"
}
],
"output_config": {
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"next_steps": {"type": "array", "items": {"type": "string"}}
},
"required": ["summary", "next_steps"],
"additionalProperties": false
}
}
},
"tools": [
{
"name": "search_flights",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"destination": {"type": "string"},
"date": {"type": "string", "format": "date"}
},
"required": ["destination", "date"],
"additionalProperties": false
}
}
]
}'bash
ant messages create <<'YAML'
model: claude-opus-5
max_tokens: 1024
messages:
- role: user
content: Help me plan a trip to Paris departing May 15, 2026
# JSON outputs: structured response format
output_config:
format:
type: json_schema
schema:
type: object
properties:
summary:
type: string
next_steps:
type: array
items:
type: string
required: [summary, next_steps]
additionalProperties: false
# Strict tool use: guaranteed tool parameters
tools:
- name: search_flights
strict: true
input_schema:
type: object
properties:
destination:
type: string
date:
type: string
format: date
required: [destination, date]
additionalProperties: false
YAMLpython
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Help me plan a trip to Paris departing May 15, 2026",
}
],
# JSON outputs: structured response format
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"next_steps": {"type": "array", "items": {"type": "string"}},
},
"required": ["summary", "next_steps"],
"additionalProperties": False,
},
}
},
# Strict tool use: guaranteed tool parameters
tools=[
{
"name": "search_flights",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"destination": {"type": "string"},
"date": {"type": "string", "format": "date"},
},
"required": ["destination", "date"],
"additionalProperties": False,
},
}
],
)
print(response)typescript
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Help me plan a trip to Paris departing May 15, 2026" }],
// JSON outputs: structured response format
output_config: {
format: {
type: "json_schema",
schema: {
type: "object",
properties: {
summary: { type: "string" },
next_steps: { type: "array", items: { type: "string" } }
},
required: ["summary", "next_steps"],
additionalProperties: false
}
}
},
// Strict tool use: guaranteed tool parameters
tools: [
{
name: "search_flights",
description: "Search for available flights to a destination on a specific date",
strict: true,
input_schema: {
type: "object",
properties: {
destination: { type: "string" },
date: { type: "string", format: "date" }
},
required: ["destination", "date"],
additionalProperties: false
}
}
]
});
// Claude may call the tool first (tool_use) or respond with JSON (text)
console.log("Stop reason:", response.stop_reason);
for (const block of response.content) {
if (block.type === "tool_use") {
console.log(`Tool call: ${block.name}(${JSON.stringify(block.input)})`);
} else if (block.type === "text") {
console.log("Response:", block.text);
}
}csharp
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Messages = [new() { Role = Role.User, Content = "Help me plan a trip to Paris departing May 15, 2026" }],
// JSON outputs: structured response format
OutputConfig = new OutputConfig
{
Format = new JsonOutputFormat
{
Schema = new Dictionary<string, JsonElement>
{
["type"] = JsonSerializer.SerializeToElement("object"),
["properties"] = JsonSerializer.SerializeToElement(new
{
summary = new { type = "string" },
next_steps = new { type = "array", items = new { type = "string" } },
}),
["required"] = JsonSerializer.SerializeToElement(new[] { "summary", "next_steps" }),
["additionalProperties"] = JsonSerializer.SerializeToElement(false),
},
},
},
// Strict tool use: guaranteed tool parameters
Tools =
[
new Tool
{
Name = "search_flights",
Strict = true,
InputSchema = new InputSchema(new Dictionary<string, JsonElement>
{
["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object>
{
["destination"] = new { type = "string" },
["date"] = new { type = "string", format = "date" },
}),
["required"] = JsonSerializer.SerializeToElement(new[] { "destination", "date" }),
["additionalProperties"] = JsonSerializer.SerializeToElement(false),
}),
}
],
};
var message = await client.Messages.Create(parameters);
Console.WriteLine(message);go
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Help me plan a trip to Paris departing May 15, 2026")),
},
// JSON outputs: structured response format
OutputConfig: anthropic.OutputConfigParam{
Format: anthropic.JSONOutputFormatParam{
Schema: map[string]any{
"type": "object",
"additionalProperties": false,
"properties": map[string]any{
"summary": map[string]any{"type": "string"},
"next_steps": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
},
"required": []string{"summary", "next_steps"},
},
},
},
// Strict tool use: guaranteed tool parameters
Tools: []anthropic.ToolUnionParam{
{OfTool: &anthropic.ToolParam{
Name: "search_flights",
Strict: anthropic.Bool(true),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"destination": map[string]any{"type": "string"},
"date": map[string]any{"type": "string", "format": "date"},
},
Required: []string{"destination", "date"},
ExtraFields: map[string]any{
"additionalProperties": false,
},
}}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Content)java
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// JSON outputs: structured response format
JsonOutputFormat.Schema outputSchema = JsonOutputFormat.Schema.builder()
.putAdditionalProperty("type", JsonValue.from("object"))
.putAdditionalProperty("properties", JsonValue.from(Map.of(
"summary", Map.of("type", "string"),
"next_steps", Map.of("type", "array", "items", Map.of("type", "string"))
)))
.putAdditionalProperty("required", JsonValue.from(List.of("summary", "next_steps")))
.putAdditionalProperty("additionalProperties", JsonValue.from(false))
.build();
// Strict tool use: guaranteed tool parameters
InputSchema toolSchema = InputSchema.builder()
.properties(JsonValue.from(Map.of(
"destination", Map.of("type", "string"),
"date", Map.of("type", "string", "format", "date")
)))
.putAdditionalProperty("required", JsonValue.from(List.of("destination", "date")))
.putAdditionalProperty("additionalProperties", JsonValue.from(false))
.build();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024L)
.addUserMessage("Help me plan a trip to Paris departing May 15, 2026")
.outputConfig(OutputConfig.builder()
.format(JsonOutputFormat.builder().schema(outputSchema).build())
.build())
.addTool(Tool.builder()
.name("search_flights")
.description("Search for available flights to a destination on a specific date")
.strict(true)
.inputSchema(toolSchema)
.build())
.build();
Message response = client.messages().create(params);
IO.println(response);php
use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
use Anthropic\Lib\Contracts\StructuredOutputModel;
use Anthropic\Messages\ToolUseBlock;
$client = new Client();
class TripPlan implements StructuredOutputModel
{
use StructuredOutputModelTrait;
public string $summary;
public array $next_steps;
}
$message = $client->messages->create(
maxTokens: 1024,
messages: [
['role' => 'user', 'content' => 'Help me plan a trip to Paris departing May 15, 2026']
],
model: 'claude-opus-5',
// JSON outputs: structured response format
outputConfig: ['format' => TripPlan::class],
// Strict tool use: guaranteed tool parameters
tools: [
[
'name' => 'search_flights',
'strict' => true,
'input_schema' => [
'type' => 'object',
'properties' => [
'destination' => ['type' => 'string'],
'date' => ['type' => 'string', 'format' => 'date']
],
'required' => ['destination', 'date'],
'additionalProperties' => false
]
]
],
);
// Claude may call the tool first (tool_use) or respond with JSON (text)
$plan = $message->parsedOutput();
if ($plan instanceof TripPlan) {
echo $plan->summary, "\n";
} elseif ($toolUse = array_find($message->content, fn($block) => $block instanceof ToolUseBlock)) {
echo "Tool call: {$toolUse->name}(", json_encode($toolUse->input), ")\n";
}ruby
client = Anthropic::Client.new
message = client.messages.create(
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{role: "user", content: "Help me plan a trip to Paris departing May 15, 2026"}
],
# JSON outputs: structured response format
output_config: {
format: {
type: :json_schema,
schema: {
type: "object",
properties: {
summary: {type: "string"},
next_steps: {type: "array", items: {type: "string"}}
},
required: ["summary", "next_steps"],
additionalProperties: false
}
}
},
# Strict tool use: guaranteed tool parameters
tools: [
{
name: "search_flights",
strict: true,
input_schema: {
type: "object",
properties: {
destination: {type: "string"},
date: {type: "string", format: "date"}
},
required: ["destination", "date"],
additionalProperties: false
}
}
]
)
puts message