Tools and MCP¶
Learn: Give the agent tools. Concepts: Tool calls. Guides: Tools on objects and from data, Use MCP servers.
tool
¶
tool(fn: Callable[..., Any]) -> FunctionTool
tool(*, name: str | None = None, description: str | None = None, parallel: bool = True, exception_handler: Callable[[Any], str] | None = None, hints_for: Callable[[dict[str, Any]], Hints | None] | None = None, read_only: bool | None = None, destructive: bool | None = None, idempotent: bool | None = None, open_world: bool | None = None) -> Callable[[Callable[..., Any]], FunctionTool]
tool(fn: Callable[..., Any] | None = None, /, *, name: str | None = None, description: str | None = None, parallel: bool = True, exception_handler: Callable[[Any], str] | None = None, hints_for: Callable[[dict[str, Any]], Hints | None] | None = None, read_only: bool | None = None, destructive: bool | None = None, idempotent: bool | None = None, open_world: bool | None = None) -> FunctionTool | Callable[[Callable[..., Any]], FunctionTool]
Turns a function or method into a tool the model can call.
Type hints become the input schema and the docstring becomes the description. The Args: section of the
docstring describes each parameter. A parameter typed State is hidden from the model and receives the
current State. The return value is sent to the model: a str as is, None as (done), an Image
(or a list of text and Images, such as ["Page loaded", Image(png)]) as an image, anything else as JSON.
In such a list a str is text and anything else that is not an Image is sent as JSON text. A value that
cannot become JSON raises TypeError, which stops the run like any exception from a tool.
Every parameter needs a type hint. Supported: str, int, float, bool, Literal[...],
Enum, list[T], dict[str, T], T | None, dataclasses, TypedDict (on Python 3.11 import it
from typing_extensions) and Pydantic models. Parameters with defaults are optional. A mistake in the
function raises TypeError when @tool runs, before any model request. tool.spec shows exactly what
the model sees.
Use it bare (@tool) or with options (@tool(name=..., description=..., parallel=...,
exception_handler=..., hints_for=..., read_only=..., destructive=..., idempotent=..., open_world=...)).
An exception from the tool stops the run, because it is usually a bug. For a failure the model should see and
handle, raise ToolError("message"), or name the exceptions in exception_handler.
read_only, destructive, idempotent and open_world describe what the tool does, with the meaning of
MCP tool annotations. The model does not see them: they are for your code, such as a loop that asks before calls
that are not read-only, and it reads them through agent.tool_map. A hint left out assumes the worst: not
read-only, destructive, not idempotent, open world. @tool(read_only=True, open_world=False) fits a tool that
reads local files. These hints are the worst case for any call; hints_for= tells what one call does.
Example
@tool
def read_file(path: str) -> str:
"""Read a file's contents
Args:
path: Path relative to the repository root
"""
return Path(path).read_text()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Any] | None
|
The function or method. Every parameter needs a type hint. |
None
|
name
|
str | None
|
The tool name. Must match |
None
|
description
|
str | None
|
Defaults to the docstring's first paragraph. |
None
|
parallel
|
bool
|
|
True
|
exception_handler
|
Callable[[Any], str] | None
|
A function that takes one exception and returns the message the model is told. The
type hint of its parameter names the exceptions it takes ( |
None
|
hints_for
|
Callable[[dict[str, Any]], Hints | None] | None
|
A function that takes the arguments of one call and returns its |
None
|
read_only
|
bool | None
|
The tool does not change its environment. Sets |
None
|
destructive
|
bool | None
|
The tool may delete or overwrite something ( |
None
|
idempotent
|
bool | None
|
Calling it again with the same arguments has no further effect. |
None
|
open_world
|
bool | None
|
The tool reaches outside its own domain, such as the web, a shell or another system. |
None
|
Returns:
| Type | Description |
|---|---|
FunctionTool | Callable[[Callable[..., Any]], FunctionTool]
|
A |
FunctionTool | Callable[[Callable[..., Any]], FunctionTool]
|
function, subclass |
Raises:
| Type | Description |
|---|---|
TypeError
|
|
ValueError
|
|
Tool
¶
Tool(*, name: str, description: str = '', input_schema: Mapping[str, Any] | None = None, parallel: bool = True, read_only: bool | None = None, destructive: bool | None = None, idempotent: bool | None = None, open_world: bool | None = None)
A tool the model can call. @tool makes one from a function; subclass it for a tool that is not a Python
function, such as one built from a JSON Schema in a database or an OpenAPI spec.
A subclass calls super().__init__(...) with the name, description and input schema, and implements run.
run gets the model's arguments as a dict and returns the result, which the model gets as @tool results
are: a str as is, None as (done), an Image (or a list with one) as an image, anything else as
JSON. It signals with exceptions:
ToolInputError("..."): the arguments are wrong. The model gets(input error: ...).ToolError("..."): a failure the model should handle. The model gets it as an error result.- Any other exception stops the run.
A subclass that does not call super().__init__, or does not define run, raises TypeError when the
Agent is created, and so does passing the class instead of an object. run gets the model's arguments as
your own copy: changing it does not change the recorded call, whose args are read-only. A Tool and a
FunctionTool (made by @tool) are both Tool objects, and so is an MCP server's MCPTool, so every
value of agent.tool_map is a Tool.
Arguments that are not valid JSON, or that were cut off by the output token limit, never reach run: the model
gets an input error. run can be async def; agent.arun then awaits it on the event loop, and a regular
run runs on a worker thread.
Example
class Webhook(Tool):
def __init__(self, row):
super().__init__(
name=row.name,
description=row.description,
input_schema=row.schema, # {"type": "object", "properties": {...}}
open_world=True,
)
self.url = row.url
def run(self, args: dict, state: State) -> dict:
if "title" not in args:
raise ToolInputError("title is required")
response = httpx.post(self.url, json=args)
if response.status_code >= 400:
raise ToolError(f"HTTP {response.status_code}")
return response.json()
Sets what the model sees and what the tool does. A subclass calls it from its own __init__.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name the model calls. Must match |
required |
description
|
str
|
What the model reads about the tool. |
''
|
input_schema
|
Mapping[str, Any] | None
|
The JSON Schema of the arguments, an object schema such as
|
None
|
parallel
|
bool
|
|
True
|
read_only
|
bool | None
|
The tool does not change its environment. Sets |
None
|
destructive
|
bool | None
|
The tool may delete or overwrite something. |
None
|
idempotent
|
bool | None
|
Calling it again with the same arguments has no further effect. |
None
|
open_world
|
bool | None
|
The tool reaches outside its own domain, such as the web, a shell or another system. |
None
|
Raises:
| Type | Description |
|---|---|
TypeError
|
The name is invalid, |
ValueError
|
|
name
instance-attribute
¶
name: str = _check_tool_name(name, explicit=True)
The name the model calls.
description
instance-attribute
¶
description: str = description
What the model reads about the tool.
input_schema
instance-attribute
¶
input_schema: dict[str, Any] = _check_input_schema(self.name, input_schema)
The JSON Schema object of the arguments, shown to the model.
parallel
instance-attribute
¶
parallel: bool = parallel
True runs the tool together with the turn's other calls. False runs it alone after they finish.
read_only
instance-attribute
¶
read_only: bool
The tool does not change its environment. False unless given. The model does not see the hints; they are
for your own code, such as a loop that asks before calls that are not read-only. These four are the worst case
for the tool; hints_for(args) gives the hints of one call.
destructive
instance-attribute
¶
destructive: bool
The tool may delete or overwrite something. True unless given, and False for a read-only tool.
idempotent
instance-attribute
¶
idempotent: bool
Calling it again with the same arguments has no further effect. False unless given, and True for a
read-only tool.
open_world
instance-attribute
¶
open_world: bool
The tool reaches outside its own domain, such as the web, a shell or another system. True unless given.
spec
property
¶
spec: ToolSpec
The tool as the model sees it: ToolSpec(name, description, input_schema).
run
¶
run(args: dict[str, Any], state: State) -> Any
Runs one call. A subclass implements it, as def or async def.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
dict[str, Any]
|
The model's arguments, parsed from JSON. Not checked against |
required |
state
|
State
|
The State of the run. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The result: a |
Any
|
they are, anything else as JSON. |
Raises:
| Type | Description |
|---|---|
ToolInputError
|
The arguments are wrong. The model gets |
ToolError
|
A failure the model should handle. The model gets it as an error result. |
hints_for
¶
hints_for(args: Mapping[str, Any]) -> Hints
The hints of one call: what running the tool with these arguments does. By default the tool's own hints
(read_only and the others), which are the worst case for any call.
A subclass can override it to tell more about one call, such as a shell tool whose ls changes nothing.
Return the tool's own hints (super().hints_for(args)) when unsure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
Mapping[str, Any]
|
The model's arguments, parsed from JSON but not yet checked against |
required |
Returns:
| Type | Description |
|---|---|
Hints
|
A |
FunctionTool
¶
FunctionTool(fn: Callable[..., Any], *, name: str | None = None, description: str | None = None, parallel: bool = True, exception_handler: Callable[[Any], str] | None = None, hints_for: Callable[[dict[str, Any]], Hints | None] | None = None, read_only: bool | None = None, destructive: bool | None = None, idempotent: bool | None = None, open_world: bool | None = None)
Bases: Tool
A tool made by @tool from a function or method. Calling it runs the original function as is, without
validation.
A @tool method in a class becomes a bound tool when accessed on an object (fs.read_file).
The attributes do not change after creation.
Usually created with @tool. A bad tool raises here, before any model call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Any]
|
The function or method. Every parameter needs a type hint. |
required |
name
|
str | None
|
The tool name. Must match |
None
|
description
|
str | None
|
Defaults to the docstring's first paragraph. |
None
|
parallel
|
bool
|
|
True
|
exception_handler
|
Callable[[Any], str] | None
|
See |
None
|
hints_for
|
Callable[[dict[str, Any]], Hints | None] | None
|
See |
None
|
read_only
|
bool | None
|
See |
None
|
destructive
|
bool | None
|
See |
None
|
idempotent
|
bool | None
|
See |
None
|
open_world
|
bool | None
|
See |
None
|
Raises:
| Type | Description |
|---|---|
TypeError
|
The name is invalid, a parameter has no type hint or an unsupported type, the function
takes |
ValueError
|
|
input_schema
instance-attribute
¶
input_schema: dict[str, Any]
The JSON Schema object shown to the model. State parameters and self are left out, Args:
descriptions become property descriptions, and parameters with defaults are optional.
exception_handler
instance-attribute
¶
exception_handler: Callable[[Any], str] | None = exception_handler
Turns the exceptions named in its parameter's type hint into error results the model sees. None if not
given.
run
¶
run(args: dict[str, Any], state: State) -> Any
Checks the arguments against the type hints (ToolInputError if they do not fit), then calls the
function with them and the State. Returns a coroutine for an async def function.
hints_for
¶
hints_for(args: Mapping[str, Any]) -> Hints
The hints of one call: what the hints_for= function returns for args, or the tool's own hints
when it returns None or was not given.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
Mapping[str, Any]
|
The model's arguments, parsed from JSON but not yet checked against the type hints. |
required |
Returns:
| Type | Description |
|---|---|
Hints
|
A |
Raises:
| Type | Description |
|---|---|
TypeError
|
The |
copy
¶
copy(**changes: Any) -> FunctionTool
Returns a new tool with some options changed. The original does not change, and a bound tool stays bound.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**changes
|
Any
|
Any |
{}
|
Returns:
| Type | Description |
|---|---|
FunctionTool
|
The new tool. |
Raises:
| Type | Description |
|---|---|
TypeError
|
A name in |
ValueError
|
The new hints contradict each other, as in |
Example
strict_fetch = fetch_url.copy(exception_handler=None) # every exception stops the run
Hints
dataclass
¶
Hints(*, read_only: bool = None, destructive: bool = None, idempotent: bool = None, open_world: bool = None)
The four hints of one call: what running the tool with these arguments does. tool.hints_for(args)
returns them.
Made like the hints of @tool: Hints(read_only=True, open_world=False). A hint left out assumes the
worst (not read-only, destructive, not idempotent, open world), and read_only=True sets
destructive=False and idempotent=True. After construction every hint is a bool.
A subclass can carry more facts about the call. Give each new field a default:
@dataclass(frozen=True)
class FileHints(Hints):
paths: tuple[str, ...] = ()
Raises:
| Type | Description |
|---|---|
TypeError
|
A hint is not a bool. |
ValueError
|
|
read_only
class-attribute
instance-attribute
¶
read_only: bool = None
The call does not change its environment.
destructive
class-attribute
instance-attribute
¶
destructive: bool = None
The call may delete or overwrite something.
idempotent
class-attribute
instance-attribute
¶
idempotent: bool = None
Making the call again with the same arguments has no further effect.
open_world
class-attribute
instance-attribute
¶
open_world: bool = None
The call reaches outside the tool's own domain, such as the web, a shell or another system.
MCP
¶
MCP(command: str | None = None, *, name: str | None = None, url: str | None = None, server: Any = None, env: Mapping[str, str] | None = None, headers: Mapping[str, str] | None = None, cwd: str | None = None, timeout: float | None = None)
An MCP server, put in Agent(tools=[...]) like any other tool. Needs pip install "alpineagents[mcp]".
The model sees the server's tools as {name}__{tool}, e.g. github__create_issue. To give the Agent
only one tool, pass gh.search_code, or gh["search-code"] for a name that is not a Python identifier.
agent.run connects the servers and disconnects them when it ends. with agent: (or
async with agent:) keeps them connected across runs. Agents and concurrent runs that share one MCP
object share one connection.
Example
github = MCP("npx -y @modelcontextprotocol/server-github", name="github", env={"GITHUB_TOKEN": token})
linear = MCP(url="https://mcp.linear.app/mcp", name="linear", headers={"Authorization": f"Bearer {key}"})
agent = Agent(model="claude-sonnet-5", tools=[github, linear.list_issues])
Takes exactly one of command, url or server. Nothing connects until the Agent uses it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
command
|
str | None
|
The command that starts a stdio server. |
None
|
name
|
str | None
|
Required. Letters, digits, |
None
|
url
|
str | None
|
The URL of a Streamable HTTP server. |
None
|
server
|
Any
|
Anything |
None
|
env
|
Mapping[str, str] | None
|
Extra environment variables for |
None
|
headers
|
Mapping[str, str] | None
|
HTTP headers for |
None
|
cwd
|
str | None
|
The working directory for |
None
|
timeout
|
float | None
|
Seconds to wait for connecting and for each call. |
None
|
Raises:
| Type | Description |
|---|---|
TypeError
|
No server or more than one is given, |
ValueError
|
|
env
instance-attribute
¶
env = dict(env) if env is not None else None
Extra environment variables for command, or None.
headers
instance-attribute
¶
headers = dict(headers) if headers is not None else None
HTTP headers for url, or None.
timeout
instance-attribute
¶
timeout = timeout
Seconds to wait for connecting and for each call, or None for no limit.
MCPTool
¶
MCPTool(server: MCP, remote: Any)
Bases: Tool
A tool of a connected MCP server, as found in agent.tool_map under {server}__{tool}.
It has the same name, description, input_schema and hints as any Tool. The hints come from the
server's tool annotations (readOnlyHint and so on), with the same defaults as @tool for the ones it
leaves out. They are what the server says about itself, not a guarantee: do not trust them more than the server.
hints_for(args) returns the same hints for every call, and AllowByReadOnly() ignores them unless it has
trust_mcp=True.
remote_name
instance-attribute
¶
remote_name: str = remote.name
The tool's name on the server (name has the {server}__ prefix).
destructive
instance-attribute
¶
destructive: bool
From destructiveHint. True if the server does not say, and False for a read-only tool.
idempotent
instance-attribute
¶
idempotent: bool
From idempotentHint. False if the server does not say, and True for a read-only tool.
open_world
instance-attribute
¶
open_world: bool
From openWorldHint. True if the server does not say.
run
async
¶
run(args: dict[str, Any], state: Any) -> str | list[str | Image]
Calls the tool on the server. Missing required arguments are an input error; the server checks the
rest. An error the server reports (isError, or an error response) becomes an error result; a lost
connection raises MCPConnectionError.
Text blocks are joined by newlines, image blocks become Images (the result is then a list of text and
images), other blocks become short placeholders, structured_content is sent as JSON when there are no
blocks, and (done) when there is nothing.