Function tools in the Anthropic SDK are registered via @beta_tool and @beta_async_tool decorators, which automatically derive the tool schema from Python type annotations and docstrings, apply runtime validation via Pydantic, and support rich error handling through ToolError. Tools decorated with @beta_tool can declare cleanup logic via BaseFunctionTool.close, which is invoked by session-aware runners but not by the Messages API tool runner—stateful tools must use Sessions APIs to avoid resource leaks.
The beta_tool (sync) and beta_async_tool (async) decorators are both exported from the top-level anthropic package and register plain Python functions as tools whose schema is derived from their docstrings and type annotations.[1]
BaseFunctionTool requires Pydantic v2 and raises RuntimeError at instantiation time if Pydantic v1 is detected.[2] At construction, BaseFunctionTool.__init__ wraps the user's function with pydantic.validate_call, so type coercion and required-field checks are applied automatically on every tool call.[2] BaseFunctionTool accepts an input_schema that may be either a raw InputSchema dict or a Pydantic BaseModel subclass; when a BaseModel is provided, model_json_schema() is called automatically.[2] When no explicit description is supplied, BaseFunctionTool derives one from the function's docstring (short and long description via docstring_parser).[2] The JSON input schema is derived from the function's type annotations via pydantic.TypeAdapter, with per-parameter descriptions injected from the docstring's Args section.[2] BaseFunctionTool.to_dict produces a BetaToolParam dict and conditionally includes optional fields — defer_loading, cache_control, allowed_callers, eager_input_streaming, input_examples, and strict — only when they were explicitly set.[2]
BetaFunctionTool.call raises RuntimeError if the wrapped function is a coroutine function, directing callers to use @async_tool instead.[2] BetaAsyncFunctionTool.call raises RuntimeError if the wrapped function is NOT a coroutine function, directing callers to use the synchronous @tool decorator instead.[2] BetaFunctionTool.call raises ValueError (wrapping pydantic.ValidationError) when the tool's input arguments fail validation.[2]
ToolError allows a tool to return structured error content with is_error: True; when the tool runner catches it, the exception's content property is used as the tool result rather than repr(exc).[2] ToolError accepts either a plain string or an iterable of content blocks (BetaContent) as its content argument, supporting rich error payloads including images.[2]
BetaFunctionToolResultType is the return type annotation for @beta_tool-decorated functions and is exported from anthropic.lib.tools.[1] BetaBuiltinFunctionTool.name returns mcp_server_name when that key is present in the tool dict (MCP tools), otherwise falls back to the name key.[2]
BaseFunctionTool.close is an optional cleanup hook for tools that own resources; it is called by SessionToolRunner and EnvironmentWorker at run end, but NOT by the Messages BetaToolRunner / BetaAsyncToolRunner. Stateful tools (e.g. a bash subprocess) handed to the Messages tool runner therefore leak their resource — see Tool runner and Sessions resource for the alternatives.[2]
Sources