Summary
@beta_tool / @beta_async_tool on a function with a positional-only parameter (def f(a: int, /, b: str)) produces an input_schema of type: "array" and a tool whose .call() can never succeed. A function with *args gets the same type: "array" schema. Nothing raises at decoration time.
Reproduction
import json
from anthropic import beta_tool
@beta_tool
def lookup(user_id: int, /, field: str = "name") -> str:
"""Look up a field on a user record."""
return f"{user_id}:{field}"
print(json.dumps(lookup.input_schema))
lookup.call({"user_id": 7, "field": "email"})
Output on anthropic==1.3.0 (same code on current main, src/anthropic/lib/tools/_beta_functions.py):
{"maxItems": 2, "minItems": 1, "prefixItems": [{"title": "User Id", "type": "integer"}, {"default": "name", "title": "Field", "type": "string"}], "type": "array"}
ValueError: Invalid arguments for function lookup
(cause: 2 validation errors for lookup — missing_positional_only_argument / unexpected_keyword_argument)
def collect(*values: int) gives {"items": {"type": "integer"}, "type": "array"}.
Why this is wrong
InputSchema in this SDK is typed with type: Required[Literal["object"]] (src/anthropic/types/tool_param.py), and the API only accepts object schemas for tools[].input_schema. The generated type: array schema violates the SDK's own type and is sent to the API as-is.
- Even with a hand-written
input_schema=, BetaFunctionTool.call does self._func_with_validate(**input), so a positional-only parameter is always reported as unexpected_keyword_argument and the tool cannot be invoked by the tool runner.
- Positional-only plus keyword-only in the same signature already fails loudly (
PydanticInvalidForJsonSchema), so only the all-positional-only / *args cases fail silently.
Root cause
_create_schema_from_function delegates to pydantic's GenerateJsonSchema.arguments_schema, which renders the positional form (an array schema) whenever a signature has positional-only parameters or *args, and no keyword-only parameters. The docstring hook in kw_arguments_schema is never reached for those signatures. call() then forwards the input object purely by keyword.
Proposed fix
Tool inputs are JSON objects passed by name, so:
- treat positional-only parameters as named properties when generating the schema, and have
call() route those values back into positional slots (missing/extra values still go through validate_call);
- raise a
TypeError at decoration time for *args, which a JSON object cannot represent.
I have a small patch with tests for this and will open a PR.
Summary
@beta_tool/@beta_async_toolon a function with a positional-only parameter (def f(a: int, /, b: str)) produces aninput_schemaoftype: "array"and a tool whose.call()can never succeed. A function with*argsgets the sametype: "array"schema. Nothing raises at decoration time.Reproduction
Output on
anthropic==1.3.0(same code on currentmain,src/anthropic/lib/tools/_beta_functions.py):def collect(*values: int)gives{"items": {"type": "integer"}, "type": "array"}.Why this is wrong
InputSchemain this SDK is typed withtype: Required[Literal["object"]](src/anthropic/types/tool_param.py), and the API only accepts object schemas fortools[].input_schema. The generatedtype: arrayschema violates the SDK's own type and is sent to the API as-is.input_schema=,BetaFunctionTool.calldoesself._func_with_validate(**input), so a positional-only parameter is always reported asunexpected_keyword_argumentand the tool cannot be invoked by the tool runner.PydanticInvalidForJsonSchema), so only the all-positional-only /*argscases fail silently.Root cause
_create_schema_from_functiondelegates to pydantic'sGenerateJsonSchema.arguments_schema, which renders the positional form (an array schema) whenever a signature has positional-only parameters or*args, and no keyword-only parameters. The docstring hook inkw_arguments_schemais never reached for those signatures.call()then forwards the input object purely by keyword.Proposed fix
Tool inputs are JSON objects passed by name, so:
call()route those values back into positional slots (missing/extra values still go throughvalidate_call);TypeErrorat decoration time for*args, which a JSON object cannot represent.I have a small patch with tests for this and will open a PR.