[ Skip to content ](https://ai.pydantic.dev/toolsets/#toolsets)
**Join us at the inaugural PyAI Conf in San Francisco on March 10th![Learn More](https://pyai.events/?utm_source=pydantic-ai) **
[ ![logo](https://ai.pydantic.dev/img/logo-white.svg) ](https://ai.pydantic.dev/ "Pydantic AI")
Pydantic AI
Toolsets
Type to start searching
[ pydantic/pydantic-ai
  * v1.63.0
  * 15.1k
  * 1.7k

](https://github.com/pydantic/pydantic-ai "Go to repository")
[ ![logo](https://ai.pydantic.dev/img/logo-white.svg) ](https://ai.pydantic.dev/ "Pydantic AI") Pydantic AI
[ pydantic/pydantic-ai
  * v1.63.0
  * 15.1k
  * 1.7k

](https://github.com/pydantic/pydantic-ai "Go to repository")
  * [ Pydantic AI  ](https://ai.pydantic.dev/)
  * [ Installation  ](https://ai.pydantic.dev/install/)
  * [ Getting Help  ](https://ai.pydantic.dev/help/)
  * [ Troubleshooting  ](https://ai.pydantic.dev/troubleshooting/)
  * [ Pydantic AI Gateway  ](https://ai.pydantic.dev/gateway/)
  * Documentation
    * Core Concepts
      * [ Agents  ](https://ai.pydantic.dev/agent/)
      * [ Dependencies  ](https://ai.pydantic.dev/dependencies/)
      * [ Function Tools  ](https://ai.pydantic.dev/tools/)
      * [ Output  ](https://ai.pydantic.dev/output/)
      * [ Messages and chat history  ](https://ai.pydantic.dev/message-history/)
      * [ Direct Model Requests  ](https://ai.pydantic.dev/direct/)
    * Models & Providers
      * [ Overview  ](https://ai.pydantic.dev/models/overview/)
      * [ OpenAI  ](https://ai.pydantic.dev/models/openai/)
      * [ Anthropic  ](https://ai.pydantic.dev/models/anthropic/)
      * [ Google  ](https://ai.pydantic.dev/models/google/)
      * [ xAI  ](https://ai.pydantic.dev/models/xai/)
      * [ Bedrock  ](https://ai.pydantic.dev/models/bedrock/)
      * [ Cerebras  ](https://ai.pydantic.dev/models/cerebras/)
      * [ Cohere  ](https://ai.pydantic.dev/models/cohere/)
      * [ Groq  ](https://ai.pydantic.dev/models/groq/)
      * [ Hugging Face  ](https://ai.pydantic.dev/models/huggingface/)
      * [ Mistral  ](https://ai.pydantic.dev/models/mistral/)
      * [ OpenRouter  ](https://ai.pydantic.dev/models/openrouter/)
      * [ Outlines  ](https://ai.pydantic.dev/models/outlines/)
    * Tools & Toolsets
      * [ Function Tools  ](https://ai.pydantic.dev/tools/)
      * [ Advanced Tool Features  ](https://ai.pydantic.dev/tools-advanced/)
      * Toolsets  [ Toolsets  ](https://ai.pydantic.dev/toolsets/)
        * [ Function Toolset  ](https://ai.pydantic.dev/toolsets/#function-toolset)
        * [ Toolset Composition  ](https://ai.pydantic.dev/toolsets/#toolset-composition)
          * [ Combining Toolsets  ](https://ai.pydantic.dev/toolsets/#combining-toolsets)
          * [ Filtering Tools  ](https://ai.pydantic.dev/toolsets/#filtering-tools)
          * [ Prefixing Tool Names  ](https://ai.pydantic.dev/toolsets/#prefixing-tool-names)
          * [ Renaming Tools  ](https://ai.pydantic.dev/toolsets/#renaming-tools)
          * [ Dynamic Tool Definitions  ](https://ai.pydantic.dev/toolsets/#preparing-tool-definitions)
          * [ Requiring Tool Approval  ](https://ai.pydantic.dev/toolsets/#requiring-tool-approval)
          * [ Changing Tool Execution  ](https://ai.pydantic.dev/toolsets/#changing-tool-execution)
        * [ External Toolset  ](https://ai.pydantic.dev/toolsets/#external-toolset)
        * [ Dynamically Building a Toolset  ](https://ai.pydantic.dev/toolsets/#dynamically-building-a-toolset)
        * [ Building a Custom Toolset  ](https://ai.pydantic.dev/toolsets/#building-a-custom-toolset)
        * [ Third-Party Toolsets  ](https://ai.pydantic.dev/toolsets/#third-party-toolsets)
          * [ MCP Servers  ](https://ai.pydantic.dev/toolsets/#mcp-servers)
          * [ Agent Skills  ](https://ai.pydantic.dev/toolsets/#agent-skills)
          * [ Task Management  ](https://ai.pydantic.dev/toolsets/#task-management)
          * [ File Operations  ](https://ai.pydantic.dev/toolsets/#file-operations)
          * [ Code Execution  ](https://ai.pydantic.dev/toolsets/#code-execution)
          * [ LangChain Tools  ](https://ai.pydantic.dev/toolsets/#langchain-tools)
          * [ ACI.dev Tools  ](https://ai.pydantic.dev/toolsets/#aci-tools)
      * [ Deferred Tools  ](https://ai.pydantic.dev/deferred-tools/)
      * [ Built-in Tools  ](https://ai.pydantic.dev/builtin-tools/)
      * [ Common Tools  ](https://ai.pydantic.dev/common-tools/)
      * [ Third-Party Tools  ](https://ai.pydantic.dev/third-party-tools/)
    * Advanced Features
      * [ Image, Audio, Video & Document Input  ](https://ai.pydantic.dev/input/)
      * [ Thinking  ](https://ai.pydantic.dev/thinking/)
      * [ HTTP Request Retries  ](https://ai.pydantic.dev/retries/)
    * MCP
      * [ Overview  ](https://ai.pydantic.dev/mcp/overview/)
      * [ Client  ](https://ai.pydantic.dev/mcp/client/)
      * [ FastMCP Client  ](https://ai.pydantic.dev/mcp/fastmcp-client/)
      * [ Server  ](https://ai.pydantic.dev/mcp/server/)
    * [ Multi-Agent Patterns  ](https://ai.pydantic.dev/multi-agent-applications/)
    * [ Web Chat UI  ](https://ai.pydantic.dev/web/)
    * [ Embeddings  ](https://ai.pydantic.dev/embeddings/)
    * [ Testing  ](https://ai.pydantic.dev/testing/)
  * Pydantic Evals
    * [ Overview  ](https://ai.pydantic.dev/evals/)
    * Getting Started
      * [ Quick Start  ](https://ai.pydantic.dev/evals/quick-start/)
      * [ Core Concepts  ](https://ai.pydantic.dev/evals/core-concepts/)
    * Evaluators
      * [ Overview  ](https://ai.pydantic.dev/evals/evaluators/overview/)
      * [ Built-in Evaluators  ](https://ai.pydantic.dev/evals/evaluators/built-in/)
      * [ LLM Judge  ](https://ai.pydantic.dev/evals/evaluators/llm-judge/)
      * [ Custom Evaluators  ](https://ai.pydantic.dev/evals/evaluators/custom/)
      * [ Report Evaluators  ](https://ai.pydantic.dev/evals/evaluators/report-evaluators/)
      * [ Span-Based  ](https://ai.pydantic.dev/evals/evaluators/span-based/)
    * How-To Guides
      * [ Logfire Integration  ](https://ai.pydantic.dev/evals/how-to/logfire-integration/)
      * [ Dataset Management  ](https://ai.pydantic.dev/evals/how-to/dataset-management/)
      * [ Dataset Serialization  ](https://ai.pydantic.dev/evals/how-to/dataset-serialization/)
      * [ Concurrency & Performance  ](https://ai.pydantic.dev/evals/how-to/concurrency/)
      * [ Multi-Run Evaluation  ](https://ai.pydantic.dev/evals/how-to/multi-run/)
      * [ Retry Strategies  ](https://ai.pydantic.dev/evals/how-to/retry-strategies/)
      * [ Metrics & Attributes  ](https://ai.pydantic.dev/evals/how-to/metrics-attributes/)
    * Examples
      * [ Simple Validation  ](https://ai.pydantic.dev/evals/examples/simple-validation/)
  * Pydantic Graph
    * [ Overview  ](https://ai.pydantic.dev/graph/)
    * [ Beta API  ](https://ai.pydantic.dev/graph/beta/)
      * [ Steps  ](https://ai.pydantic.dev/graph/beta/steps/)
      * [ Joins & Reducers  ](https://ai.pydantic.dev/graph/beta/joins/)
      * [ Decisions  ](https://ai.pydantic.dev/graph/beta/decisions/)
      * [ Parallel Execution  ](https://ai.pydantic.dev/graph/beta/parallel/)
  * Integrations
    * [ Debugging & Monitoring with Pydantic Logfire  ](https://ai.pydantic.dev/logfire/)
    * Durable Execution
      * [ Overview  ](https://ai.pydantic.dev/durable_execution/overview/)
      * [ Temporal  ](https://ai.pydantic.dev/durable_execution/temporal/)
      * [ DBOS  ](https://ai.pydantic.dev/durable_execution/dbos/)
      * [ Prefect  ](https://ai.pydantic.dev/durable_execution/prefect/)
    * UI Event Streams
      * [ Overview  ](https://ai.pydantic.dev/ui/overview/)
      * [ AG-UI  ](https://ai.pydantic.dev/ui/ag-ui/)
      * [ Vercel AI  ](https://ai.pydantic.dev/ui/vercel-ai/)
    * [ Agent2Agent (A2A)  ](https://ai.pydantic.dev/a2a/)
  * Related Packages
    * [ clai  ](https://ai.pydantic.dev/cli/)
  * Examples
    * [ Setup  ](https://ai.pydantic.dev/examples/setup/)
    * Getting Started
      * [ Pydantic Model  ](https://ai.pydantic.dev/examples/pydantic-model/)
      * [ Weather agent  ](https://ai.pydantic.dev/examples/weather-agent/)
    * Conversational Agents
      * [ Chat App with FastAPI  ](https://ai.pydantic.dev/examples/chat-app/)
      * [ Bank support  ](https://ai.pydantic.dev/examples/bank-support/)
    * Data & Analytics
      * [ SQL Generation  ](https://ai.pydantic.dev/examples/sql-gen/)
      * [ Data Analyst  ](https://ai.pydantic.dev/examples/data-analyst/)
      * [ RAG  ](https://ai.pydantic.dev/examples/rag/)
    * Streaming
      * [ Stream markdown  ](https://ai.pydantic.dev/examples/stream-markdown/)
      * [ Stream whales  ](https://ai.pydantic.dev/examples/stream-whales/)
    * Complex Workflows
      * [ Flight booking  ](https://ai.pydantic.dev/examples/flight-booking/)
      * [ Question Graph  ](https://ai.pydantic.dev/examples/question-graph/)
    * Business Applications
      * [ Slack Lead Qualifier with Modal  ](https://ai.pydantic.dev/examples/slack-lead-qualifier/)
    * UI Examples
      * [ Agent User Interaction (AG-UI)  ](https://ai.pydantic.dev/examples/ag-ui/)
  * API Reference
    * pydantic_ai
      * [ pydantic_ai.ag_ui  ](https://ai.pydantic.dev/api/ag_ui/)
      * [ pydantic_ai.agent  ](https://ai.pydantic.dev/api/agent/)
      * [ pydantic_ai.builtin_tools  ](https://ai.pydantic.dev/api/builtin_tools/)
      * [ pydantic_ai.common_tools  ](https://ai.pydantic.dev/api/common_tools/)
      * [ pydantic_ai — Concurrency  ](https://ai.pydantic.dev/api/concurrency/)
      * [ pydantic_ai.direct  ](https://ai.pydantic.dev/api/direct/)
      * [ pydantic_ai.durable_exec  ](https://ai.pydantic.dev/api/durable_exec/)
      * [ pydantic_ai.embeddings  ](https://ai.pydantic.dev/api/embeddings/)
      * [ pydantic_ai.exceptions  ](https://ai.pydantic.dev/api/exceptions/)
      * [ pydantic_ai.ext  ](https://ai.pydantic.dev/api/ext/)
      * [ pydantic_ai.format_prompt  ](https://ai.pydantic.dev/api/format_prompt/)
      * [ pydantic_ai.mcp  ](https://ai.pydantic.dev/api/mcp/)
      * [ pydantic_ai.messages  ](https://ai.pydantic.dev/api/messages/)
      * [ pydantic_ai.models.anthropic  ](https://ai.pydantic.dev/api/models/anthropic/)
      * [ pydantic_ai.models  ](https://ai.pydantic.dev/api/models/base/)
      * [ pydantic_ai.models.bedrock  ](https://ai.pydantic.dev/api/models/bedrock/)
      * [ pydantic_ai.models.cerebras  ](https://ai.pydantic.dev/api/models/cerebras/)
      * [ pydantic_ai.models.cohere  ](https://ai.pydantic.dev/api/models/cohere/)
      * [ pydantic_ai.models.fallback  ](https://ai.pydantic.dev/api/models/fallback/)
      * [ pydantic_ai.models.function  ](https://ai.pydantic.dev/api/models/function/)
      * [ pydantic_ai.models.google  ](https://ai.pydantic.dev/api/models/google/)
      * [ pydantic_ai.models.xai  ](https://ai.pydantic.dev/api/models/xai/)
      * [ pydantic_ai.models.groq  ](https://ai.pydantic.dev/api/models/groq/)
      * [ pydantic_ai.models.huggingface  ](https://ai.pydantic.dev/api/models/huggingface/)
      * [ pydantic_ai.models.instrumented  ](https://ai.pydantic.dev/api/models/instrumented/)
      * [ pydantic_ai.models.mcp_sampling  ](https://ai.pydantic.dev/api/models/mcp-sampling/)
      * [ pydantic_ai.models.mistral  ](https://ai.pydantic.dev/api/models/mistral/)
      * [ pydantic_ai.models.openai  ](https://ai.pydantic.dev/api/models/openai/)
      * [ pydantic_ai.models.openrouter  ](https://ai.pydantic.dev/api/models/openrouter/)
      * [ pydantic_ai.models.outlines  ](https://ai.pydantic.dev/api/models/outlines/)
      * [ pydantic_ai.models.test  ](https://ai.pydantic.dev/api/models/test/)
      * [ pydantic_ai.models.wrapper  ](https://ai.pydantic.dev/api/models/wrapper/)
      * [ pydantic_ai.output  ](https://ai.pydantic.dev/api/output/)
      * [ pydantic_ai.profiles  ](https://ai.pydantic.dev/api/profiles/)
      * [ pydantic_ai.providers  ](https://ai.pydantic.dev/api/providers/)
      * [ pydantic_ai.result  ](https://ai.pydantic.dev/api/result/)
      * [ pydantic_ai.retries  ](https://ai.pydantic.dev/api/retries/)
      * [ pydantic_ai.run  ](https://ai.pydantic.dev/api/run/)
      * [ pydantic_ai.settings  ](https://ai.pydantic.dev/api/settings/)
      * [ pydantic_ai.tools  ](https://ai.pydantic.dev/api/tools/)
      * [ pydantic_ai.toolsets  ](https://ai.pydantic.dev/api/toolsets/)
      * [ pydantic_ai.ui.ag_ui  ](https://ai.pydantic.dev/api/ui/ag_ui/)
      * [ pydantic_ai.ui  ](https://ai.pydantic.dev/api/ui/base/)
      * [ pydantic_ai.ui.vercel_ai  ](https://ai.pydantic.dev/api/ui/vercel_ai/)
      * [ pydantic_ai.usage  ](https://ai.pydantic.dev/api/usage/)
    * pydantic_evals
      * [ pydantic_evals.dataset  ](https://ai.pydantic.dev/api/pydantic_evals/dataset/)
      * [ pydantic_evals.evaluators  ](https://ai.pydantic.dev/api/pydantic_evals/evaluators/)
      * [ pydantic_evals.reporting  ](https://ai.pydantic.dev/api/pydantic_evals/reporting/)
      * [ pydantic_evals.otel  ](https://ai.pydantic.dev/api/pydantic_evals/otel/)
      * [ pydantic_evals.generation  ](https://ai.pydantic.dev/api/pydantic_evals/generation/)
    * pydantic_graph
      * [ pydantic_graph  ](https://ai.pydantic.dev/api/pydantic_graph/graph/)
      * [ pydantic_graph.nodes  ](https://ai.pydantic.dev/api/pydantic_graph/nodes/)
      * [ pydantic_graph.persistence  ](https://ai.pydantic.dev/api/pydantic_graph/persistence/)
      * [ pydantic_graph.mermaid  ](https://ai.pydantic.dev/api/pydantic_graph/mermaid/)
      * [ pydantic_graph.exceptions  ](https://ai.pydantic.dev/api/pydantic_graph/exceptions/)
      * Beta API
        * [ pydantic_graph.beta  ](https://ai.pydantic.dev/api/pydantic_graph/beta/)
        * [ pydantic_graph.beta.graph  ](https://ai.pydantic.dev/api/pydantic_graph/beta_graph/)
        * [ pydantic_graph.beta.graph_builder  ](https://ai.pydantic.dev/api/pydantic_graph/beta_graph_builder/)
        * [ pydantic_graph.beta.step  ](https://ai.pydantic.dev/api/pydantic_graph/beta_step/)
        * [ pydantic_graph.beta.join  ](https://ai.pydantic.dev/api/pydantic_graph/beta_join/)
        * [ pydantic_graph.beta.decision  ](https://ai.pydantic.dev/api/pydantic_graph/beta_decision/)
        * [ pydantic_graph.beta.node  ](https://ai.pydantic.dev/api/pydantic_graph/beta_node/)
    * fasta2a
      * [ fasta2a  ](https://ai.pydantic.dev/api/fasta2a/)
  * Project
    * [ Contributing  ](https://ai.pydantic.dev/contributing/)
    * [ Upgrade Guide  ](https://ai.pydantic.dev/changelog/)
    * [ Version policy  ](https://ai.pydantic.dev/version-policy/)


  * [ Function Toolset  ](https://ai.pydantic.dev/toolsets/#function-toolset)
  * [ Toolset Composition  ](https://ai.pydantic.dev/toolsets/#toolset-composition)
    * [ Combining Toolsets  ](https://ai.pydantic.dev/toolsets/#combining-toolsets)
    * [ Filtering Tools  ](https://ai.pydantic.dev/toolsets/#filtering-tools)
    * [ Prefixing Tool Names  ](https://ai.pydantic.dev/toolsets/#prefixing-tool-names)
    * [ Renaming Tools  ](https://ai.pydantic.dev/toolsets/#renaming-tools)
    * [ Dynamic Tool Definitions  ](https://ai.pydantic.dev/toolsets/#preparing-tool-definitions)
    * [ Requiring Tool Approval  ](https://ai.pydantic.dev/toolsets/#requiring-tool-approval)
    * [ Changing Tool Execution  ](https://ai.pydantic.dev/toolsets/#changing-tool-execution)
  * [ External Toolset  ](https://ai.pydantic.dev/toolsets/#external-toolset)
  * [ Dynamically Building a Toolset  ](https://ai.pydantic.dev/toolsets/#dynamically-building-a-toolset)
  * [ Building a Custom Toolset  ](https://ai.pydantic.dev/toolsets/#building-a-custom-toolset)
  * [ Third-Party Toolsets  ](https://ai.pydantic.dev/toolsets/#third-party-toolsets)
    * [ MCP Servers  ](https://ai.pydantic.dev/toolsets/#mcp-servers)
    * [ Agent Skills  ](https://ai.pydantic.dev/toolsets/#agent-skills)
    * [ Task Management  ](https://ai.pydantic.dev/toolsets/#task-management)
    * [ File Operations  ](https://ai.pydantic.dev/toolsets/#file-operations)
    * [ Code Execution  ](https://ai.pydantic.dev/toolsets/#code-execution)
    * [ LangChain Tools  ](https://ai.pydantic.dev/toolsets/#langchain-tools)
    * [ ACI.dev Tools  ](https://ai.pydantic.dev/toolsets/#aci-tools)


  1. [ Pydantic AI  ](https://ai.pydantic.dev/)
  2. [ Documentation  ](https://ai.pydantic.dev/agent/)
  3. [ Tools & Toolsets  ](https://ai.pydantic.dev/tools/)


# Toolsets
A toolset represents a collection of [tools](https://ai.pydantic.dev/tools/) that can be registered with an agent in one go. They can be reused by different agents, swapped out at runtime or during testing, and composed in order to dynamically filter which tools are available, modify tool definitions, or change tool execution behavior. A toolset can contain locally defined functions, depend on an external service to provide them, or implement custom logic to list available tools and handle them being called.
Toolsets are used (among many other things) to define [MCP servers](https://ai.pydantic.dev/mcp/client/) available to an agent. Pydantic AI includes many kinds of toolsets which are described below, and you can define a [custom toolset](https://ai.pydantic.dev/toolsets/#building-a-custom-toolset) by inheriting from the [`AbstractToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset "AbstractToolset") class.
The toolsets that will be available during an agent run can be specified in four different ways:
  * at agent construction time, via the [`toolsets`](https://ai.pydantic.dev/api/agent/#pydantic_ai.agent.Agent.__init__ "__init__") keyword argument to `Agent`, which takes toolset instances as well as functions that generate toolsets [dynamically](https://ai.pydantic.dev/toolsets/#dynamically-building-a-toolset) based on the agent [run context](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.RunContext "RunContext



      dataclass
  ")
  * at agent run time, via the `toolsets` keyword argument to [`agent.run()`](https://ai.pydantic.dev/api/agent/#pydantic_ai.agent.AbstractAgent.run "run



      async
  "), [`agent.run_sync()`](https://ai.pydantic.dev/api/agent/#pydantic_ai.agent.AbstractAgent.run_sync "run_sync"), [`agent.run_stream()`](https://ai.pydantic.dev/api/agent/#pydantic_ai.agent.AbstractAgent.run_stream "run_stream



      async
  "), or [`agent.iter()`](https://ai.pydantic.dev/api/agent/#pydantic_ai.agent.Agent.iter "iter



      async
  "). These toolsets will be additional to those registered on the `Agent`
  * [dynamically](https://ai.pydantic.dev/toolsets/#dynamically-building-a-toolset), via the [`@agent.toolset`](https://ai.pydantic.dev/api/agent/#pydantic_ai.agent.Agent.toolset "toolset") decorator which lets you build a toolset based on the agent [run context](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.RunContext "RunContext



      dataclass
  ")
  * as a contextual override, via the `toolsets` keyword argument to the [`agent.override()`](https://ai.pydantic.dev/api/agent/#pydantic_ai.agent.Agent.iter "iter



      async
  ") context manager. These toolsets will replace those provided at agent construction or run time during the life of the context manager


toolsets.py```
from pydantic_ai import Agent, FunctionToolset
from pydantic_ai.models.test import TestModel


def agent_tool():
    return "I'm registered directly on the agent"


def extra_tool():
    return "I'm passed as an extra tool for a specific run"


def override_tool():
    return 'I override all other tools'


agent_toolset = FunctionToolset(tools=[agent_tool]) [](https://ai.pydantic.dev/toolsets/#__code_0_annotation_1)
extra_toolset = FunctionToolset(tools=[extra_tool])
override_toolset = FunctionToolset(tools=[override_tool])

test_model = TestModel() [](https://ai.pydantic.dev/toolsets/#__code_0_annotation_2)
agent = Agent(test_model, toolsets=[agent_toolset])

result = agent.run_sync('What tools are available?')
print([t.name for t in test_model.last_model_request_parameters.function_tools])
#> ['agent_tool']

result = agent.run_sync('What tools are available?', toolsets=[extra_toolset])
print([t.name for t in test_model.last_model_request_parameters.function_tools])
#> ['agent_tool', 'extra_tool']

with agent.override(toolsets=[override_toolset]):
    result = agent.run_sync('What tools are available?', toolsets=[extra_toolset]) [](https://ai.pydantic.dev/toolsets/#__code_0_annotation_3)
    print([t.name for t in test_model.last_model_request_parameters.function_tools])
    #> ['override_tool']

```

_(This example is complete, it can be run "as is")_
## Function Toolset
As the name suggests, a [`FunctionToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.FunctionToolset "FunctionToolset") makes locally defined functions available as tools.
Functions can be added as tools in three different ways:
  * via the [`@toolset.tool`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.FunctionToolset.tool "tool") decorator
  * via the [`tools`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.FunctionToolset.__init__ "__init__") keyword argument to the constructor which can take either plain functions, or instances of [`Tool`](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.Tool "Tool



      dataclass
  ")
  * via the [`toolset.add_function()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.FunctionToolset.add_function "add_function") and [`toolset.add_tool()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.FunctionToolset.add_tool "add_tool") methods which can take a plain function or an instance of [`Tool`](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.Tool "Tool



      dataclass
  ") respectively


Functions registered in any of these ways can define an initial `ctx: RunContext` argument in order to receive the agent [run context](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.RunContext "RunContext



      dataclass
  "). The `add_function()` and `add_tool()` methods can also be used from a tool function to dynamically register new tools during a run to be available in future run steps.
function_toolset.py```
from datetime import datetime

from pydantic_ai import Agent, FunctionToolset, RunContext
from pydantic_ai.models.test import TestModel


def temperature_celsius(city: str) -> float:
    return 21.0


def temperature_fahrenheit(city: str) -> float:
    return 69.8


weather_toolset = FunctionToolset(tools=[temperature_celsius, temperature_fahrenheit])


@weather_toolset.tool
def conditions(ctx: RunContext, city: str) -> str:
    if ctx.run_step % 2 == 0:
        return "It's sunny"
    else:
        return "It's raining"


datetime_toolset = FunctionToolset()
datetime_toolset.add_function(lambda: datetime.now(), name='now')

test_model = TestModel()  [](https://ai.pydantic.dev/toolsets/#__code_1_annotation_1)
agent = Agent(test_model)

result = agent.run_sync('What tools are available?', toolsets=[weather_toolset])
print([t.name for t in test_model.last_model_request_parameters.function_tools])
#> ['temperature_celsius', 'temperature_fahrenheit', 'conditions']

result = agent.run_sync('What tools are available?', toolsets=[datetime_toolset])
print([t.name for t in test_model.last_model_request_parameters.function_tools])
#> ['now']

```

_(This example is complete, it can be run "as is")_
## Toolset Composition
Toolsets can be composed to dynamically filter which tools are available, modify tool definitions, or change tool execution behavior. Multiple toolsets can also be combined into one.
### Combining Toolsets
[`CombinedToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.CombinedToolset "CombinedToolset



      dataclass
  ") takes a list of toolsets and lets them be used as one.
combined_toolset.py```
from pydantic_ai import Agent, CombinedToolset
from pydantic_ai.models.test import TestModel

from function_toolset import datetime_toolset, weather_toolset

combined_toolset = CombinedToolset([weather_toolset, datetime_toolset])

test_model = TestModel() [](https://ai.pydantic.dev/toolsets/#__code_2_annotation_1)
agent = Agent(test_model, toolsets=[combined_toolset])
result = agent.run_sync('What tools are available?')
print([t.name for t in test_model.last_model_request_parameters.function_tools])
#> ['temperature_celsius', 'temperature_fahrenheit', 'conditions', 'now']

```

_(This example is complete, it can be run "as is")_
### Filtering Tools
[`FilteredToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.FilteredToolset "FilteredToolset



      dataclass
  ") wraps a toolset and filters available tools ahead of each step of the run based on a user-defined function that is passed the agent [run context](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.RunContext "RunContext



      dataclass
  ") and each tool's [`ToolDefinition`](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.ToolDefinition "ToolDefinition



      dataclass
  ") and returns a boolean to indicate whether or not a given tool should be available.
To easily chain different modifications, you can also call [`filtered()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.filtered "filtered") on any toolset instead of directly constructing a `FilteredToolset`.
filtered_toolset.py```
from pydantic_ai import Agent
from pydantic_ai.models.test import TestModel

from combined_toolset import combined_toolset

filtered_toolset = combined_toolset.filtered(lambda ctx, tool_def: 'fahrenheit' not in tool_def.name)

test_model = TestModel() [](https://ai.pydantic.dev/toolsets/#__code_3_annotation_1)
agent = Agent(test_model, toolsets=[filtered_toolset])
result = agent.run_sync('What tools are available?')
print([t.name for t in test_model.last_model_request_parameters.function_tools])
#> ['weather_temperature_celsius', 'weather_conditions', 'datetime_now']

```

_(This example is complete, it can be run "as is")_
### Prefixing Tool Names
[`PrefixedToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.PrefixedToolset "PrefixedToolset



      dataclass
  ") wraps a toolset and adds a prefix to each tool name to prevent tool name conflicts between different toolsets.
To easily chain different modifications, you can also call [`prefixed()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.prefixed "prefixed") on any toolset instead of directly constructing a `PrefixedToolset`.
combined_toolset.py```
from pydantic_ai import Agent, CombinedToolset
from pydantic_ai.models.test import TestModel

from function_toolset import datetime_toolset, weather_toolset

combined_toolset = CombinedToolset(
    [
        weather_toolset.prefixed('weather'),
        datetime_toolset.prefixed('datetime')
    ]
)

test_model = TestModel() [](https://ai.pydantic.dev/toolsets/#__code_4_annotation_1)
agent = Agent(test_model, toolsets=[combined_toolset])
result = agent.run_sync('What tools are available?')
print([t.name for t in test_model.last_model_request_parameters.function_tools])
"""
[
    'weather_temperature_celsius',
    'weather_temperature_fahrenheit',
    'weather_conditions',
    'datetime_now',
]
"""

```

_(This example is complete, it can be run "as is")_
### Renaming Tools
[`RenamedToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.RenamedToolset "RenamedToolset



      dataclass
  ") wraps a toolset and lets you rename tools using a dictionary mapping new names to original names. This is useful when the names provided by a toolset are ambiguous or would conflict with tools defined by other toolsets, but [prefixing them](https://ai.pydantic.dev/toolsets/#prefixing-tool-names) creates a name that is unnecessarily long or could be confusing to the model.
To easily chain different modifications, you can also call [`renamed()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.renamed "renamed") on any toolset instead of directly constructing a `RenamedToolset`.
renamed_toolset.py```
from pydantic_ai import Agent
from pydantic_ai.models.test import TestModel

from combined_toolset import combined_toolset

renamed_toolset = combined_toolset.renamed(
    {
        'current_time': 'datetime_now',
        'temperature_celsius': 'weather_temperature_celsius',
        'temperature_fahrenheit': 'weather_temperature_fahrenheit'
    }
)

test_model = TestModel() [](https://ai.pydantic.dev/toolsets/#__code_5_annotation_1)
agent = Agent(test_model, toolsets=[renamed_toolset])
result = agent.run_sync('What tools are available?')
print([t.name for t in test_model.last_model_request_parameters.function_tools])
"""
['temperature_celsius', 'temperature_fahrenheit', 'weather_conditions', 'current_time']
"""

```

_(This example is complete, it can be run "as is")_
### Dynamic Tool Definitions
[`PreparedToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.PreparedToolset "PreparedToolset



      dataclass
  ") lets you modify the entire list of available tools ahead of each step of the agent run using a user-defined function that takes the agent [run context](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.RunContext "RunContext



      dataclass
  ") and a list of [`ToolDefinition`s](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.ToolDefinition "ToolDefinition



      dataclass
  ") and returns a list of modified `ToolDefinition`s.
This is the toolset-specific equivalent of the [`prepare_tools`](https://ai.pydantic.dev/tools-advanced/#prepare-tools) argument to `Agent` that prepares all tool definitions registered on an agent across toolsets.
Note that it is not possible to add or rename tools using `PreparedToolset`. Instead, you can use [`FunctionToolset.add_function()`](https://ai.pydantic.dev/toolsets/#function-toolset) or [`RenamedToolset`](https://ai.pydantic.dev/toolsets/#renaming-tools).
To easily chain different modifications, you can also call [`prepared()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.prepared "prepared") on any toolset instead of directly constructing a `PreparedToolset`.
prepared_toolset.py```
from dataclasses import replace

from pydantic_ai import Agent, RunContext, ToolDefinition
from pydantic_ai.models.test import TestModel

from renamed_toolset import renamed_toolset

descriptions = {
    'temperature_celsius': 'Get the temperature in degrees Celsius',
    'temperature_fahrenheit': 'Get the temperature in degrees Fahrenheit',
    'weather_conditions': 'Get the current weather conditions',
    'current_time': 'Get the current time',
}

async def add_descriptions(ctx: RunContext, tool_defs: list[ToolDefinition]) -> list[ToolDefinition] | None:
    return [
        replace(tool_def, description=description)
        if (description := descriptions.get(tool_def.name, None))
        else tool_def
        for tool_def
        in tool_defs
    ]

prepared_toolset = renamed_toolset.prepared(add_descriptions)

test_model = TestModel() [](https://ai.pydantic.dev/toolsets/#__code_6_annotation_1)
agent = Agent(test_model, toolsets=[prepared_toolset])
result = agent.run_sync('What tools are available?')
print(test_model.last_model_request_parameters.function_tools)
"""
[
    ToolDefinition(
        name='temperature_celsius',
        parameters_json_schema={
            'additionalProperties': False,
            'properties': {'city': {'type': 'string'}},
            'required': ['city'],
            'type': 'object',
        },
        description='Get the temperature in degrees Celsius',
    ),
    ToolDefinition(
        name='temperature_fahrenheit',
        parameters_json_schema={
            'additionalProperties': False,
            'properties': {'city': {'type': 'string'}},
            'required': ['city'],
            'type': 'object',
        },
        description='Get the temperature in degrees Fahrenheit',
    ),
    ToolDefinition(
        name='weather_conditions',
        parameters_json_schema={
            'additionalProperties': False,
            'properties': {'city': {'type': 'string'}},
            'required': ['city'],
            'type': 'object',
        },
        description='Get the current weather conditions',
    ),
    ToolDefinition(
        name='current_time',
        parameters_json_schema={
            'additionalProperties': False,
            'properties': {},
            'type': 'object',
        },
        description='Get the current time',
    ),
]
"""

```

### Requiring Tool Approval
[`ApprovalRequiredToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.ApprovalRequiredToolset "ApprovalRequiredToolset



      dataclass
  ") wraps a toolset and lets you dynamically [require approval](https://ai.pydantic.dev/deferred-tools/#human-in-the-loop-tool-approval) for a given tool call based on a user-defined function that is passed the agent [run context](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.RunContext "RunContext



      dataclass
  "), the tool's [`ToolDefinition`](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.ToolDefinition "ToolDefinition



      dataclass
  "), and the validated tool call arguments. If no function is provided, all tool calls will require approval.
To easily chain different modifications, you can also call [`approval_required()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.approval_required "approval_required") on any toolset instead of directly constructing a `ApprovalRequiredToolset`.
See the [Human-in-the-Loop Tool Approval](https://ai.pydantic.dev/deferred-tools/#human-in-the-loop-tool-approval) documentation for more information on how to handle agent runs that call tools that require approval and how to pass in the results.
approval_required_toolset.py```
from pydantic_ai import Agent, DeferredToolRequests, DeferredToolResults
from pydantic_ai.models.test import TestModel

from prepared_toolset import prepared_toolset

approval_required_toolset = prepared_toolset.approval_required(lambda ctx, tool_def, tool_args: tool_def.name.startswith('temperature'))

test_model = TestModel(call_tools=['temperature_celsius', 'temperature_fahrenheit']) [](https://ai.pydantic.dev/toolsets/#__code_7_annotation_1)
agent = Agent(
    test_model,
    toolsets=[approval_required_toolset],
    output_type=[str, DeferredToolRequests],
)
result = agent.run_sync('Call the temperature tools')
messages = result.all_messages()
print(result.output)
"""
DeferredToolRequests(
    calls=[],
    approvals=[
        ToolCallPart(
            tool_name='temperature_celsius',
            args={'city': 'a'},
            tool_call_id='pyd_ai_tool_call_id__temperature_celsius',
        ),
        ToolCallPart(
            tool_name='temperature_fahrenheit',
            args={'city': 'a'},
            tool_call_id='pyd_ai_tool_call_id__temperature_fahrenheit',
        ),
    ],
    metadata={},
)
"""

result = agent.run_sync(
    message_history=messages,
    deferred_tool_results=DeferredToolResults(
        approvals={
            'pyd_ai_tool_call_id__temperature_celsius': True,
            'pyd_ai_tool_call_id__temperature_fahrenheit': False,
        }
    )
)
print(result.output)
#> {"temperature_celsius":21.0,"temperature_fahrenheit":"The tool call was denied."}

```

_(This example is complete, it can be run "as is")_
### Changing Tool Execution
[`WrapperToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.WrapperToolset "WrapperToolset



      dataclass
  ") wraps another toolset and delegates all responsibility to it.
It is is a no-op by default, but you can subclass `WrapperToolset` to change the wrapped toolset's tool execution behavior by overriding the [`call_tool()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.call_tool "call_tool



      abstractmethod
      async
  ") method.
logging_toolset.py```
import asyncio

from typing_extensions import Any

from pydantic_ai import Agent, RunContext, ToolsetTool, WrapperToolset
from pydantic_ai.models.test import TestModel

from prepared_toolset import prepared_toolset

LOG = []

class LoggingToolset(WrapperToolset):
    async def call_tool(self, name: str, tool_args: dict[str, Any], ctx: RunContext, tool: ToolsetTool) -> Any:
        LOG.append(f'Calling tool {name!r} with args: {tool_args!r}')
        try:
            await asyncio.sleep(0.1 * len(LOG)) [](https://ai.pydantic.dev/toolsets/#__code_8_annotation_1)

            result = await super().call_tool(name, tool_args, ctx, tool)
            LOG.append(f'Finished calling tool {name!r} with result: {result!r}')
        except Exception as e:
            LOG.append(f'Error calling tool {name!r}: {e}')
            raise e
        else:
            return result


logging_toolset = LoggingToolset(prepared_toolset)

agent = Agent(TestModel(), toolsets=[logging_toolset]) [](https://ai.pydantic.dev/toolsets/#__code_8_annotation_2)
result = agent.run_sync('Call all the tools')
print(LOG)
"""
[
    "Calling tool 'temperature_celsius' with args: {'city': 'a'}",
    "Calling tool 'temperature_fahrenheit' with args: {'city': 'a'}",
    "Calling tool 'weather_conditions' with args: {'city': 'a'}",
    "Calling tool 'current_time' with args: {}",
    "Finished calling tool 'temperature_celsius' with result: 21.0",
    "Finished calling tool 'temperature_fahrenheit' with result: 69.8",
    'Finished calling tool \'weather_conditions\' with result: "It\'s raining"',
    "Finished calling tool 'current_time' with result: datetime.datetime(...)",
]
"""

```

_(This example is complete, it can be run "as is")_
## External Toolset
If your agent needs to be able to call [external tools](https://ai.pydantic.dev/deferred-tools/#external-tool-execution) that are provided and executed by an upstream service or frontend, you can build an [`ExternalToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.ExternalToolset "ExternalToolset") from a list of [`ToolDefinition`s](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.ToolDefinition "ToolDefinition



      dataclass
  ") containing the tool names, arguments JSON schemas, and descriptions.
When the model calls an external tool, the call is considered to be ["deferred"](https://ai.pydantic.dev/deferred-tools/#deferred-tools), and the agent run will end with a [`DeferredToolRequests`](https://ai.pydantic.dev/api/output/#pydantic_ai.output.DeferredToolRequests "DeferredToolRequests



      dataclass
  ") output object with a `calls` list holding [`ToolCallPart`s](https://ai.pydantic.dev/api/messages/#pydantic_ai.messages.ToolCallPart "ToolCallPart



      dataclass
  ") containing the tool name, validated arguments, and a unique tool call ID, which are expected to be passed to the upstream service or frontend that will produce the results.
When the tool call results are received from the upstream service or frontend, you can build a [`DeferredToolResults`](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.DeferredToolResults "DeferredToolResults



      dataclass
  ") object with a `calls` dictionary that maps each tool call ID to an arbitrary value to be returned to the model, a [`ToolReturn`](https://ai.pydantic.dev/tools-advanced/#advanced-tool-returns) object, or a [`ModelRetry`](https://ai.pydantic.dev/api/exceptions/#pydantic_ai.exceptions.ModelRetry "ModelRetry") exception in case the tool call failed and the model should [try again](https://ai.pydantic.dev/tools-advanced/#tool-retries). This `DeferredToolResults` object can then be provided to one of the agent run methods as `deferred_tool_results`, alongside the original run's [message history](https://ai.pydantic.dev/message-history/).
Note that you need to add `DeferredToolRequests` to the `Agent`'s or `agent.run()`'s [`output_type`](https://ai.pydantic.dev/output/#structured-output) so that the possible types of the agent run output are correctly inferred. For more information, see the [Deferred Tools](https://ai.pydantic.dev/deferred-tools/#deferred-tools) documentation.
To demonstrate, let us first define a simple agent _without_ deferred tools:
[With Pydantic AI Gateway](https://ai.pydantic.dev/toolsets/#__tabbed_1_1)[Directly to Provider API](https://ai.pydantic.dev/toolsets/#__tabbed_1_2)
[Learn about Gateway](https://ai.pydantic.dev/gateway) deferred_toolset_agent.py```
from pydantic import BaseModel

from pydantic_ai import Agent, FunctionToolset

toolset = FunctionToolset()


@toolset.tool
def get_default_language():
    return 'en-US'


@toolset.tool
def get_user_name():
    return 'David'


class PersonalizedGreeting(BaseModel):
    greeting: str
    language_code: str


agent = Agent('gateway/openai:gpt-5.2', toolsets=[toolset], output_type=PersonalizedGreeting)

result = agent.run_sync('Greet the user in a personalized way')
print(repr(result.output))
#> PersonalizedGreeting(greeting='Hello, David!', language_code='en-US')

```

deferred_toolset_agent.py```
from pydantic import BaseModel

from pydantic_ai import Agent, FunctionToolset

toolset = FunctionToolset()


@toolset.tool
def get_default_language():
    return 'en-US'


@toolset.tool
def get_user_name():
    return 'David'


class PersonalizedGreeting(BaseModel):
    greeting: str
    language_code: str


agent = Agent('openai:gpt-5.2', toolsets=[toolset], output_type=PersonalizedGreeting)

result = agent.run_sync('Greet the user in a personalized way')
print(repr(result.output))
#> PersonalizedGreeting(greeting='Hello, David!', language_code='en-US')

```

Next, let's define a function that represents a hypothetical "run agent" API endpoint that can be called by the frontend and takes a list of messages to send to the model, a list of frontend tool definitions, and optional deferred tool results. This is where `ExternalToolset`, `DeferredToolRequests`, and `DeferredToolResults` come in:
deferred_toolset_api.py```
from pydantic_ai import (
    DeferredToolRequests,
    DeferredToolResults,
    ExternalToolset,
    ModelMessage,
    ToolDefinition,
)

from deferred_toolset_agent import PersonalizedGreeting, agent


def run_agent(
    messages: list[ModelMessage] = [],
    frontend_tools: list[ToolDefinition] = {},
    deferred_tool_results: DeferredToolResults | None = None,
) -> tuple[PersonalizedGreeting | DeferredToolRequests, list[ModelMessage]]:
    deferred_toolset = ExternalToolset(frontend_tools)
    result = agent.run_sync(
        toolsets=[deferred_toolset], [](https://ai.pydantic.dev/toolsets/#__code_11_annotation_1)
        output_type=[agent.output_type, DeferredToolRequests], [](https://ai.pydantic.dev/toolsets/#__code_11_annotation_2)
        message_history=messages, [](https://ai.pydantic.dev/toolsets/#__code_11_annotation_3)
        deferred_tool_results=deferred_tool_results,
    )
    return result.output, result.new_messages()

```

Now, imagine that the code below is implemented on the frontend, and `run_agent` stands in for an API call to the backend that runs the agent. This is where we actually execute the deferred tool calls and start a new run with the new result included:
deferred_tools.py```
from pydantic_ai import (
    DeferredToolRequests,
    DeferredToolResults,
    ModelMessage,
    ModelRequest,
    ModelRetry,
    ToolDefinition,
    UserPromptPart,
)

from deferred_toolset_api import run_agent

frontend_tool_definitions = [
    ToolDefinition(
        name='get_preferred_language',
        parameters_json_schema={'type': 'object', 'properties': {'default_language': {'type': 'string'}}},
        description="Get the user's preferred language from their browser",
    )
]

def get_preferred_language(default_language: str) -> str:
    return 'es-MX' [](https://ai.pydantic.dev/toolsets/#__code_12_annotation_1)

frontend_tool_functions = {'get_preferred_language': get_preferred_language}

messages: list[ModelMessage] = [
    ModelRequest(
        parts=[
            UserPromptPart(content='Greet the user in a personalized way')
        ]
    )
]

deferred_tool_results: DeferredToolResults | None = None

final_output = None
while True:
    output, new_messages = run_agent(messages, frontend_tool_definitions, deferred_tool_results)
    messages += new_messages

    if not isinstance(output, DeferredToolRequests):
        final_output = output
        break

    print(output.calls)
    """
    [
        ToolCallPart(
            tool_name='get_preferred_language',
            args={'default_language': 'en-US'},
            tool_call_id='pyd_ai_tool_call_id',
        )
    ]
    """
    deferred_tool_results = DeferredToolResults()
    for tool_call in output.calls:
        if function := frontend_tool_functions.get(tool_call.tool_name):
            result = function(**tool_call.args_as_dict())
        else:
            result = ModelRetry(f'Unknown tool {tool_call.tool_name!r}')
        deferred_tool_results.calls[tool_call.tool_call_id] = result

print(repr(final_output))
"""
PersonalizedGreeting(greeting='Hola, David! Espero que tengas un gran día!', language_code='es-MX')
"""

```

_(This example is complete, it can be run "as is")_
## Dynamically Building a Toolset
Toolsets can be built dynamically ahead of each agent run or run step using a function that takes the agent [run context](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.RunContext "RunContext



      dataclass
  ") and returns a toolset or `None`. This is useful when a toolset (like an MCP server) depends on information specific to an agent run, like its [dependencies](https://ai.pydantic.dev/dependencies/).
To register a dynamic toolset, you can pass a function that takes [`RunContext`](https://ai.pydantic.dev/api/tools/#pydantic_ai.tools.RunContext "RunContext



      dataclass
  ") to the `toolsets` argument of the `Agent` constructor, or you can wrap a compliant function in the [`@agent.toolset`](https://ai.pydantic.dev/api/agent/#pydantic_ai.agent.Agent.toolset "toolset") decorator.
By default, the function will be called again ahead of each agent run step. If you are using the decorator, you can optionally provide a `per_run_step=False` argument to indicate that the toolset only needs to be built once for the entire run.
dynamic_toolset.py```
from dataclasses import dataclass
from typing import Literal

from pydantic_ai import Agent, RunContext
from pydantic_ai.models.test import TestModel

from function_toolset import datetime_toolset, weather_toolset


@dataclass
class ToggleableDeps:
    active: Literal['weather', 'datetime']

    def toggle(self):
        if self.active == 'weather':
            self.active = 'datetime'
        else:
            self.active = 'weather'

test_model = TestModel()  [](https://ai.pydantic.dev/toolsets/#__code_13_annotation_1)
agent = Agent(
    test_model,
    deps_type=ToggleableDeps  [](https://ai.pydantic.dev/toolsets/#__code_13_annotation_2)
)

@agent.toolset
def toggleable_toolset(ctx: RunContext[ToggleableDeps]):
    if ctx.deps.active == 'weather':
        return weather_toolset
    else:
        return datetime_toolset

@agent.tool
def toggle(ctx: RunContext[ToggleableDeps]):
    ctx.deps.toggle()

deps = ToggleableDeps('weather')

result = agent.run_sync('Toggle the toolset', deps=deps)
print([t.name for t in test_model.last_model_request_parameters.function_tools])  [](https://ai.pydantic.dev/toolsets/#__code_13_annotation_3)
#> ['toggle', 'now']

result = agent.run_sync('Toggle the toolset', deps=deps)
print([t.name for t in test_model.last_model_request_parameters.function_tools])
#> ['toggle', 'temperature_celsius', 'temperature_fahrenheit', 'conditions']

```

_(This example is complete, it can be run "as is")_
## Building a Custom Toolset
To define a fully custom toolset with its own logic to list available tools and handle them being called, you can subclass [`AbstractToolset`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset "AbstractToolset") and implement the [`get_tools()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.get_tools "get_tools



      abstractmethod
      async
  ") and [`call_tool()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.call_tool "call_tool



      abstractmethod
      async
  ") methods.
If you want to reuse a network connection or session across tool listings and calls during an agent run, you can implement [`__aenter__()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.__aenter__ "__aenter__



      async
  ") and [`__aexit__()`](https://ai.pydantic.dev/api/toolsets/#pydantic_ai.toolsets.AbstractToolset.__aexit__ "__aexit__



      async
  ").
## Third-Party Toolsets
### MCP Servers
Pydantic AI provides two toolsets that allow an agent to connect to and call tools on local and remote MCP Servers:
  1. `MCPServer`: the [MCP SDK-based Client](https://ai.pydantic.dev/mcp/client/) which offers more direct control by leveraging the MCP SDK directly
  2. `FastMCPToolset`: the [FastMCP-based Client](https://ai.pydantic.dev/mcp/fastmcp-client/) which offers additional capabilities like Tool Transformation, simpler OAuth configuration, and more.


### Agent Skills
Toolsets that implement [Agent Skills](https://agentskills.io) support so agents can efficiently discover and perform specific tasks:
  * [`pydantic-ai-skills`](https://github.com/DougTrajano/pydantic-ai-skills) - `SkillsToolset` implements Agent Skills support with progressive disclosure (load skills on-demand to reduce tokens). Supports filesystem and programmatic skills; compatible with [agentskills.io](https://agentskills.io).


### Task Management
Toolsets for task planning and progress tracking help agents organize complex work and provide visibility into agent progress:
  * [`pydantic-ai-todo`](https://github.com/vstorm-co/pydantic-ai-todo) - `TodoToolset` with `read_todos` and `write_todos` tools. Included in the third-party [`pydantic-deep`](https://github.com/vstorm-co/pydantic-deepagents) [deep agent](https://ai.pydantic.dev/multi-agent-applications/#deep-agents) framework.


### File Operations
Toolsets for file operations help agents read, write, and edit files:
  * [`pydantic-ai-filesystem-sandbox`](https://github.com/zby/pydantic-ai-filesystem-sandbox) - `FileSystemToolset` with a sandbox and LLM-friendly errors
  * [`pydantic-deep`](https://github.com/vstorm-co/pydantic-deepagents) — Deep agent framework that includes a `FilesystemToolset` with multiple backends (in-memory, real filesystem, Docker sandbox).


### Code Execution
Toolsets for sandboxed code execution help agents run code in a sandboxed environment:
  * [`mcp-run-python`](https://github.com/pydantic/mcp-run-python) - MCP server by the Pydantic team that runs Python code in a sandboxed environment. Can be used as `MCPServerStdio('uv', args=['run', 'mcp-run-python', 'stdio'])`.


### LangChain Tools
If you'd like to use tools or a [toolkit](https://python.langchain.com/docs/concepts/tools/#toolkits) from LangChain's [community tool library](https://python.langchain.com/docs/integrations/tools/) with Pydantic AI, you can use the [`LangChainToolset`](https://ai.pydantic.dev/api/ext/#pydantic_ai.ext.langchain.LangChainToolset "LangChainToolset") which takes a list of LangChain tools. Note that Pydantic AI will not validate the arguments in this case -- it's up to the model to provide arguments matching the schema specified by the LangChain tool, and up to the LangChain tool to raise an error if the arguments are invalid.
You will need to install the `langchain-community` package and any others required by the tools in question.
[With Pydantic AI Gateway](https://ai.pydantic.dev/toolsets/#__tabbed_2_1)[Directly to Provider API](https://ai.pydantic.dev/toolsets/#__tabbed_2_2)
[Learn about Gateway](https://ai.pydantic.dev/gateway)```
from langchain_community.agent_toolkits import SlackToolkit

from pydantic_ai import Agent
from pydantic_ai.ext.langchain import LangChainToolset

toolkit = SlackToolkit()
toolset = LangChainToolset(toolkit.get_tools())

agent = Agent('gateway/openai:gpt-5.2', toolsets=[toolset])
# ...

```

```
from langchain_community.agent_toolkits import SlackToolkit

from pydantic_ai import Agent
from pydantic_ai.ext.langchain import LangChainToolset

toolkit = SlackToolkit()
toolset = LangChainToolset(toolkit.get_tools())

agent = Agent('openai:gpt-5.2', toolsets=[toolset])
# ...

```

### ACI.dev Tools
If you'd like to use tools from the [ACI.dev tool library](https://www.aci.dev/tools) with Pydantic AI, you can use the [`ACIToolset`](https://ai.pydantic.dev/api/ext/#pydantic_ai.ext.aci.ACIToolset "ACIToolset") [toolset](https://ai.pydantic.dev/toolsets/) which takes a list of ACI tool names as well as the `linked_account_owner_id`. Note that Pydantic AI will not validate the arguments in this case -- it's up to the model to provide arguments matching the schema specified by the ACI tool, and up to the ACI tool to raise an error if the arguments are invalid.
You will need to install the `aci-sdk` package, set your ACI API key in the `ACI_API_KEY` environment variable, and pass your ACI "linked account owner ID" to the function.
[With Pydantic AI Gateway](https://ai.pydantic.dev/toolsets/#__tabbed_3_1)[Directly to Provider API](https://ai.pydantic.dev/toolsets/#__tabbed_3_2)
[Learn about Gateway](https://ai.pydantic.dev/gateway)```
import os

from pydantic_ai import Agent
from pydantic_ai.ext.aci import ACIToolset

toolset = ACIToolset(
    [
        'OPEN_WEATHER_MAP__CURRENT_WEATHER',
        'OPEN_WEATHER_MAP__FORECAST',
    ],
    linked_account_owner_id=os.getenv('LINKED_ACCOUNT_OWNER_ID'),
)

agent = Agent('gateway/openai:gpt-5.2', toolsets=[toolset])

```

```
import os

from pydantic_ai import Agent
from pydantic_ai.ext.aci import ACIToolset

toolset = ACIToolset(
    [
        'OPEN_WEATHER_MAP__CURRENT_WEATHER',
        'OPEN_WEATHER_MAP__FORECAST',
    ],
    linked_account_owner_id=os.getenv('LINKED_ACCOUNT_OWNER_ID'),
)

agent = Agent('openai:gpt-5.2', toolsets=[toolset])

```

© Pydantic Services Inc. 2024 to present
