anthropics / anthropics/claude-agent-sdk-python

create_sdk_mcp_server tools cannot opt into strict tool use: tools/list reconstruction drops the field

Aberta
#1,243 4 comentários 0 reações 0 responsáveis Ver no GitHub
enhancement
Linguagem predominante
Python
Estrelas
8.1k
Forks
1.3k
Métricas de merge de PRs
Métricas de PR pendentes

Descrição

## Summary

Tools registered through `create_sdk_mcp_server` cannot opt into [strict tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use). The SDK rebuilds the `tools/list` response by hand and keeps only `name` / `description` / `inputSchema` / `annotations` / `_meta`, so a `strict` field set by the tool author is silently dropped before it reaches the CLI. There is no other route: the CLI has no `_meta` key for it either, and no hook fires for the failure strict would prevent.

For agents whose custom tools take array parameters this matters in production: we measure the model emitting unparseable JSON for an array argument in 17 of 18 turns on one tool shape (details below), the exact failure class `strict: true` exists to eliminate.

## Where the field is dropped

`claude_agent_sdk/_internal/query.py`, `_handle_sdk_mcp_request`, `tools/list` branch (v0.2.131, lines ~653-672):

```python
tool_data: dict[str, Any] = {
"name": tool.name,
"description": tool.description,
"inputSchema": (...),
}
if tool.annotations:
tool_data["annotations"] = tool.annotations.model_dump(exclude_none=True)
if tool.meta:
tool_data["_meta"] = tool.meta
tools_data.append(tool_data)
```

`mcp.types.Tool` is `extra="allow"`, so `Tool.model_validate({..., "strict": True})` succeeds — but the reconstruction above discards the extra field instead of forwarding it.

## What we verified before filing (SDK 0.2.131, CLI 2.1.252, macOS arm64)

1. **The CLI honors `strict` when its internal tool object carries it.** The bundled binary contains the pipeline: `if(e.strict===!0){...fe.strict=!0, fe.input_schema=} else `, and the API tool definition builder spreads `...fe.strict&&{strict:!0}`. So the gap is transport, not capability.
2. **Top-level `strict` on the MCP `Tool` never crosses the wire** — dropped by the reconstruction above (confirmed by invoking `_handle_sdk_mcp_request` directly with a synthetic `tools/list` message: the emitted dict has keys `name`/`description`/`inputSchema` only).
3. **`_meta: {"anthropic/strict": true}` crosses the wire but nothing reads it** — a byte search of the CLI binary finds zero occurrences of `anthropic/strict`; the closed set of `anthropic/*` `_meta` keys it knows is `alwaysLoad, claude, configs, devicePassthrough, dirSync, hearth, id, maxResultSizeChars, models, permissionDisplay, remoteToolExecution, requiresUserInteraction, searchHint, v`.
4. **Live confirmation:** with both injection variants applied, 5 of 5 turns still produced a tool call whose input the CLI replaced with `{"__unparsedToolInput": ...}`, and the CLI's strict-incompatible-schema warning never appeared on stderr.
5. **No hook can compensate:** for a tool call whose input JSON cannot be parsed, no hook fires at all — `PreToolUse`, `PostToolUse`, `PostToolUseFailure` were all instrumented; 20/20 successful calls in the same turns logged hooks, the malformed call logged none.

## Why this matters (measured impact)

Production agent (claude-sonnet-5, effort=high, adaptive thinking), five in-process MCP retrieval tools. One tool has an array-of-strings parameter next to a required enum parameter. The model emits the array value as bare unquoted words — `{"jurisdiction": "se", "terms": token machine payout percentage}` — in **17 of 18 turns** on that tool shape (1 of 9 on the same tool without the enum). The call never runs; the CLI substitutes `__unparsedToolInput` and returns its own `InputValidationError`. Description engineering reduced it to 4/18; it cannot reach zero, and the model sometimes abandons the search after the rejection instead of re-sending it.

Independent report of the same regression class on this model family, where `strict: true` eliminated it via the raw API: https://lucumr.pocoo.org/2026/7/4/better-models-worse-tools/

## Ask

Let an `SdkMcpTool` (and/or the MCP `Tool` listing) declare `strict: true` and forward it to the CLI's tool-definition pipeline, which already knows how to normalize the schema or warn. The same forwarding gap presumably applies to `input_examples`.

## Environment

- claude-agent-sdk 0.2.131 (Python 3.12), mcp 1.29.1
- Claude Code CLI 2.1.252, macOS arm64
- Model: claude-sonnet-5, effort high, adaptive thinking

Guia de contribuição

Nenhum guia de contribuição indexado para este repositório

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.