anthropics / anthropics/claude-agent-sdk-python

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

Abierto
#1,243 4 comentarios 0 reacciones 0 asignados Ver en GitHub
enhancement
Lenguaje dominante
Python
Estrellas
8.1k
Forks
1.3k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

## 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

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.