# https://github.com/strands-agents/sdk-python Project Manual

Generated at: 2026-05-28 09:12:46 UTC

## Table of Contents

- [Getting Started with Strands Agents](#getting-started)
- [System Architecture](#architecture-overview)
- [Agent System](#agent-system)
- [Model Providers](#model-providers)
- [Tool System](#tool-system)
- [Multi-Agent Patterns](#multi-agent-patterns)
- [Bidirectional Streaming (Experimental)](#bidirectional-streaming)
- [Session & Conversation Management](#session-conversation-mgmt)
- [MCP Integration](#mcp-integration)
- [Plugin System](#plugins-system)

<a id='getting-started'></a>

## Getting Started with Strands Agents

### Related Pages

Related topics: [System Architecture](#architecture-overview), [Agent System](#agent-system)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/__init__.py)
- [strands-py/src/strands/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)
- [strands-py/src/strands/tools/mcp/mcp_client.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)
- [strands-py/src/strands/tools/mcp/mcp_tasks.py](https://github://strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)
- [strands-py/src/strands/tools/structured_output/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/structured_output/__init__.py)
- [strands-py/src/strands/telemetry/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/__init__.py)
- [strands-py/src/strands/telemetry/config.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/config.py)
- [strands-py/src/strands/telemetry/metrics_constants.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/metrics_constants.py)
- [strands-py/src/strands/tools/executors/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/__init__.py)
- [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)
</details>

# Getting Started with Strands Agents

Strands Agents is a model-driven SDK for building and running AI agents in Python. The framework provides a simple yet powerful abstraction that handles the agent loop, tool execution, and model interactions, allowing developers to focus on application logic rather than infrastructure.

## Overview

Strands Agents is designed to be lightweight, flexible, and model-agnostic. It supports multiple model providers including Amazon Bedrock, Anthropic, Google Gemini, LiteLLM, Llama, Ollama, OpenAI, and Writer. The SDK natively integrates with the Model Context Protocol (MCP), enabling access to thousands of pre-built tools and services.

**Key capabilities include:**
- Python-based tool creation using decorators
- MCP server integration for external tools
- Multi-agent orchestration support
- Structured output using Pydantic models
- Built-in telemetry and metrics
- Streaming support
- Conversation management with context window handling

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## Installation

### Requirements

- Python 3.10 or higher
- pip or another Python package manager

### Package Installation

```bash
# Install the core Strands Agents SDK
pip install strands-agents

# Install the tools package (includes built-in tools)
pip install strands-agents-tools
```

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

### Setting Up a Virtual Environment

```bash
# Create and activate virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows use: .venv\Scripts\activate

# Install Strands packages
pip install strands-agents strands-agents-tools
```

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## Quick Start

### Your First Agent

The simplest way to create an agent is to use the built-in `@tool` decorator and pass tools to the Agent constructor:

```python
from strands import Agent, tool

@tool
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression.
    
    Args:
        expression: A mathematical expression to evaluate (e.g., "2 + 2")
    """
    return str(eval(expression))

# Create an agent with the calculator tool
agent = Agent(tools=[calculator])

# Run the agent
response = agent("What is 15 * 23?")
print(response)
```

Source: [strands-py/src/strands/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)

### Using Built-in Tools

The `strands-agents-tools` package provides pre-built tools that can be imported directly:

```python
from strands import Agent
from strands_tools import calculator

agent = Agent(tools=[calculator])
response = agent("Calculate the square root of 1764")
```

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## Core Concepts

### The Agent

The `Agent` is the central component of the Strands SDK. It orchestrates the interaction between the user, the model, and tools.

```python
from strands import Agent

agent = Agent(
    model=None,           # Uses default BedrockModel if None
    tools=[],             # List of available tools
    system_prompt=None,   # Optional system instructions
    **kwargs              # Additional configuration
)
```

Source: [strands-py/src/strands/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)

### Tool Execution

Tools in Strands are Python functions decorated with `@tool`. The docstring is used by the LLM to understand the tool's purpose and generate appropriate calls.

```python
from strands import Agent, tool

@tool
def word_count(text: str) -> int:
    """Count words in text.
    
    This docstring is used by the LLM to understand the tool's purpose.
    """
    return len(text.split())

agent = Agent(tools=[word_count])
```

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

### Hot Reloading Tools from Directory

For development, you can enable automatic tool loading from a directory:

```python
from strands import Agent

# Agent will watch ./tools/ directory for changes
agent = Agent(load_tools_from_directory=True)
response = agent("Use any tools you find in the tools directory")
```

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## Model Configuration

### Default Model (Amazon Bedrock)

By default, Strands uses the Amazon Bedrock model provider. For this to work, you need:
- AWS credentials configured
- Model access enabled (e.g., Claude 4 Sonnet in us-west-2)

### Using Amazon Bedrock Explicitly

```python
from strands import Agent
from strands.models import BedrockModel

bedrock_model = BedrockModel(
    model_id="us.amazon.nova-pro-v1:0",
    temperature=0.3,
    streaming=True,  # Enable/disable streaming
)
agent = Agent(model=bedrock_model)
agent("Tell me about Agentic AI")
```

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

### Using Google Gemini

```python
from strands import Agent
from strands.models.gemini import GeminiModel

gemini_model = GeminiModel(
    client_args={
        "api_key": "your_gemini_api_key",
    },
    model_id="gemini-2.5-flash",
    params={"temperature": 0.7}
)
agent = Agent(model=gemini_model)
agent("Tell me about Agentic AI")
```

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

### Using Ollama

```python
from strands import Agent
from strands.models.ollama import OllamaModel

ollama_model = OllamaModel(
    model_id="llama3.2",
    base_url="http://localhost:11434"
)
agent = Agent(model=ollama_model)
```

### Available Model Providers

| Provider | Class | Description |
|----------|-------|-------------|
| Amazon Bedrock | `BedrockModel` | Default provider with AWS integration |
| Google Gemini | `GeminiModel` | Google's Gemini models |
| Ollama | `OllamaModel` | Local model serving |
| LlamaAPI | `LlamaAPIModel` | LlamaCloud API |
| LiteLLM | `LiteLLMModel` | Unified API for multiple providers |
| LlamaCpp | `LlamaCppModel` | Local GGUF model execution |

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## Model Context Protocol (MCP) Integration

Strands provides native support for MCP servers, enabling integration with thousands of external tools and services.

### Basic MCP Client Usage

```python
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp import stdio_client, StdioServerParameters

# Create an MCP client for the AWS Documentation MCP server
aws_docs_client = MCPClient(
    lambda: stdio_client(
        StdioServerParameters(
            command="uvx",
            args=["awslabs.aws-documentation-mcp-server@latest"]
        )
    )
)

with aws_docs_client:
    agent = Agent(tools=aws_docs_client.list_tools_sync())
    response = agent("Tell me about Amazon Bedrock")
```

Source: [strands-py/src/strands/tools/mcp/mcp_client.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

### MCP Task Configuration

For long-running tool calls, you can configure task-based execution with polling and TTL settings:

```python
from strands.tools.mcp import MCPClient
from strands.tools.mcp.mcp_tasks import TasksConfig, DEFAULT_TASK_CONFIG

# Configure task settings
tasks_config: TasksConfig = {
    "ttl": timedelta(minutes=2),        # Task time-to-live
    "poll_timeout": timedelta(minutes=10)  # Polling timeout
}

mcp_client = MCPClient(
    server_factory=...,
    tasks_config=tasks_config
)
```

Source: [strands-py/src/strands/tools/mcp/mcp_tasks.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)

| Config Option | Default | Description |
|---------------|---------|-------------|
| `ttl` | 1 minute | Task time-to-live before expiration |
| `poll_timeout` | 5 minutes | Maximum time to wait for task completion |

Source: [strands-py/src/strands/tools/mcp/mcp_tasks.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)

## Structured Output

Strands supports structured output using Pydantic models, ensuring responses conform to a specific schema.

```python
from strands import Agent
from pydantic import BaseModel

class WeatherResponse(BaseModel):
    temperature: float
    unit: str
    condition: str

agent = Agent(
    model=...,
    tools=[...],
    structured_output_model=WeatherResponse
)

result = agent("What's the weather like in Seattle?")
# result will be a WeatherResponse instance
```

Source: [strands-py/src/strands/tools/structured_output/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/structured_output/__init__.py)

## Tool Executors

Strands provides different execution strategies for tools:

### Concurrent Execution

Tools are executed in parallel when possible:

```python
from strands.tools.executors import ConcurrentToolExecutor

agent = Agent(
    tools=[tool1, tool2],
    tool_executor=ConcurrentToolExecutor()
)
```

Source: [strands-py/src/strands/tools/executors/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/__init__.py)

### Sequential Execution

Tools are executed one at a time:

```python
from strands.tools.executors import SequentialToolExecutor

agent = Agent(
    tools=[tool1, tool2],
    tool_executor=SequentialToolExecutor()
)
```

Source: [strands-py/src/strands/tools/executors/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/__init__.py)

## Telemetry and Observability

Strands includes built-in telemetry support using OpenTelemetry for tracing and metrics.

### Configuration

```python
from strands.telemetry import StrandsTelemetry

# Quick setup with console exporter
StrandsTelemetry().setup_console_exporter()

# Setup with OTLP exporter
StrandsTelemetry().setup_otlp_exporter()

# Chain multiple exporters
StrandsTelemetry().setup_console_exporter().setup_otlp_exporter()

# Setup meter provider
StrandsTelemetry().setup_meter(enable_console_exporter=True, enable_otlp_exporter=True)
```

Source: [strands-py/src/strands/telemetry/config.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/config.py)

### Available Metrics

| Metric | Type | Description |
|--------|------|-------------|
| `strands.event_loop.cycle_count` | Counter | Total number of agent loop cycles |
| `strands.event_loop.latency` | Histogram | Agent loop cycle latency |
| `strands.tool.call_count` | Counter | Total tool calls made |
| `strands.tool.duration` | Histogram | Tool execution duration |
| `strands.model.time_to_first_token` | Histogram | Time to first token in model responses |

Source: [strands-py/src/strands/telemetry/metrics_constants.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/metrics_constants.py)

### Environment Variables

| Variable | Description |
|----------|-------------|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP endpoint URL for trace/metric export |
| `OTEL_EXPORTER_OTLP_HEADERS` | Headers for OTLP requests |
| `OTEL_SERVICE_NAME` | Override for the service name in telemetry |

Source: [strands-py/src/strands/telemetry/config.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/config.py)

## Agent Loop Architecture

The agent operates in a continuous loop that processes user input, calls the model, executes tools as needed, and returns results.

```mermaid
graph TD
    A[User Input] --> B[Agent Loop]
    B --> C[Model Inference]
    C --> D{Stop Reason?}
    D -->|tool_use| E[Execute Tool]
    D -->|end_turn| F[Return Result]
    E --> B
    F --> G[Response]
    
    H[Context Window] --> B
    B --> I[Conversation Manager]
    I --> H
```

### Stop Reasons

| Reason | Description |
|--------|-------------|
| `end_turn` | Agent has completed the request and returned a response |
| `tool_use` | Agent wants to use a tool to gather more information |
| `interrupt` | Agent is waiting for user input to continue |
| `max_tokens` | Maximum token limit reached |

Source: [strands-py/src/strands/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)

## Skills Integration

Strands supports the AgentSkills.io integration for progressive disclosure of instructions:

```python
from strands import Agent
from strands.vended_plugins.skills import Skill, AgentSkills

# Load skill from filesystem
skill = Skill.from_file("./skills/pdf-processing")

# Load all skills from a directory
skills = Skill.from_directory("./skills/")

# Create plugin and attach to agent
plugin = AgentSkills(skills=["./skills/pdf-processing"])
agent = Agent(plugins=[plugin])
```

Source: [strands-py/src/strands/vended_plugins/skills/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/skills/__init__.py)

## Known Limitations and Issues

### Thread Safety

The `MetricsClient` singleton is not thread-safe in version 1.41.0. If using multi-threaded applications, be aware of potential race conditions when accessing metrics. See [Issue #2342](https://github.com/strands-agents/sdk-python/issues/2342).

### Gemini Model Safety Settings

`GeminiModel` may crash with a `TypeError` when safety settings block content and `usage_metadata` is `None`. See [Issue #2347](https://github.com/strands-agents/sdk-python/issues/2347).

### MCP Progress Updates

Strands does not currently support MCP progress updates for long-running tasks, which is available in other MCP implementations like FastMCP. See [Feature Request #1812](https://github.com/strands-agents/sdk-python/issues/1812).

### Python Wheel Package

When publishing wheels, ensure that `README.md` and `LICENSE` files are properly included. The current setup uses a relative path that may not work with all Python build backends. See [Issue #2351](https://github.com/strands-agents/sdk-python/issues/2351).

## Development Setup

For contributing to the Strands SDK:

```bash
# Navigate to the strands-py subdirectory
cd strands-py

# Install hatch for development
pip install hatch

# Run unit tests
hatch test

# Format and lint code
hatch fmt
```

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## See Also

- [Agent Loop Documentation](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/)
- [Structured Output Guide](https://strandsagents.com/docs/user-guide/concepts/agents/structured-output/)
- [MCP Integration Guide](https://strandsagents.com/docs/user-guide/tools/mcp/)
- [Telemetry and Observability](https://strandsagents.com/docs/user-guide/telemetry/)
- [Production Deployment Guide](https://strandsagents.com/docs/user-guide/deploy/operating-agents-in-production/)
- [API Reference](https://strandsagents.com/docs/api/python/strands.agent.agent/)
- [Contributing Guide](https://github.com/strands-agents/sdk-python/blob/main/CONTRIBUTING.md)

---

<a id='architecture-overview'></a>

## System Architecture

### Related Pages

Related topics: [Agent System](#agent-system), [Model Providers](#model-providers), [Tool System](#tool-system)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)
- [strands-py/src/strands/agent/base.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/base.py)
- [strands-py/src/strands/models/model.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/model.py)
- [strands-py/src/strands/tools/tools.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/tools.py)
- [strands-py/src/strands/session/session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_manager.py)
- [strands-py/src/strands/tools/mcp/mcp_client.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)
- [strands-py/src/strands/tools/executors/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/__init__.py)
- [strands-py/src/strands/event_loop/event_loop.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/event_loop/event_loop.py)
- [strands-py/src/strands/telemetry/metrics_constants.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/metrics_constants.py)
- [strands-py/src/strands/multiagent/a2a/_converters.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/_converters.py)
- [strands-py/src/strands/vended_plugins/skills/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/skills/__init__.py)
- [strands-py/src/strands/tools/structured_output/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/structured_output/__init__.py)
- [strands-py/src/strands/tools/mcp/mcp_tasks.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)
- [designs/0001-plugins.md](https://github.com/strands-agents/sdk-python/blob/main/designs/0001-plugins.md)
- [designs/0003-context-management.md](https://github.com/strands-agents/sdk-python/blob/main/designs/0003-context-management.md)
- [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)
</details>

# System Architecture

The Strands Agents SDK is a model-driven framework for building and running AI agents. The architecture follows a simple yet powerful agent loop design that scales from basic conversational assistants to complex autonomous multi-agent workflows. This document provides a comprehensive overview of the system's architectural components, their interactions, and design patterns.

## Overview

The SDK is organized as a monorepo (consolidating from multiple repositories as noted in [Issue #2286](https://github.com/strands-agents/sdk-python/issues/2286)) with the core implementation located in the `strands-py/` subdirectory. The architecture emphasizes modularity, allowing users to customize model providers, tool execution strategies, conversation management, and agent behavior through a plugin system. Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## High-Level Architecture

```mermaid
graph TD
    subgraph "User Application Layer"
        A[User Code] --> B[Agent]
    end

    subgraph "Agent Core"
        B --> C[Agent Loop]
        C --> D[Model Client]
        C --> E[Tool Executor]
        C --> F[Session Manager]
        C --> G[Event Loop Handler]
    end

    subgraph "Model Providers"
        D --> H[BedrockModel]
        D --> I[GeminiModel]
        D --> J[OllamaModel]
        D --> K[OpenAIModel]
        D --> L[Custom Provider]
    end

    subgraph "Tool System"
        E --> M[Built-in Tools]
        E --> N[MCP Client]
        N --> O[MCP Servers]
        E --> P[Plugin Tools]
    end

    subgraph "Session & Context"
        F --> Q[Conversation History]
        G --> R[Structured Output]
        G --> S[Telemetry/Metrics]
    end

    subgraph "Multi-Agent"
        B --> T[MultiAgentPlugin]
        T --> U[Swarm Orchestrator]
        T --> V[Graph Orchestrator]
        T --> W[A2A Communication]
    end
```

## Core Components

### Agent

The `Agent` class is the central entry point for building AI agents. It orchestrates the interaction between model providers, tools, and session management through an event-driven loop. Source: [strands-py/src/strands/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)

The Agent class provides:
- **Model integration**: Configurable model providers (Bedrock, Gemini, OpenAI, Ollama, and more)
- **Tool management**: Dynamic loading and execution of tools
- **Session persistence**: Conversation history management across interactions
- **Plugin architecture**: Extensible system for adding custom behaviors
- **Streaming support**: Real-time response streaming capabilities

```python
from strands import Agent
from strands_tools import calculator

agent = Agent(tools=[calculator])
response = agent("What is the square root of 1764?")
```

### Model Abstraction Layer

The model layer provides a unified interface for interacting with various LLM providers. This abstraction allows the SDK to be model-agnostic while supporting provider-specific features. Source: [strands-py/src/strands/models/model.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/model.py)

| Model Provider | Class | Key Features |
|----------------|-------|--------------|
| Amazon Bedrock | `BedrockModel` | Service tiers (Priority, Standard, Flex), caching, system prompt caching |
| Google Gemini | `GeminiModel` | Safety settings, API key authentication |
| Ollama | `OllamaModel` | Local model execution |
| OpenAI | `OpenAIModel` | Standard OpenAI API compatibility |
| Llama API | `LlamaAPIModel` | Custom provider integration |
| LiteLLM | `LiteLLMModel` | Unified interface for multiple providers |

### Tool System Architecture

The tool system enables agents to interact with external capabilities. Tools are registered with the agent and invoked through a configurable executor. Source: [strands-py/src/strands/tools/tools.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/tools.py)

```mermaid
graph LR
    A[Agent Loop] --> B[Tool Executor]
    B --> C[ConcurrentToolExecutor]
    B --> D[SequentialToolExecutor]
    C --> E[Thread Pool]
    D --> F[Single Thread]
    E --> G[Tool 1]
    E --> H[Tool 2]
    E --> I[Tool N]
```

#### Tool Executors

The SDK provides built-in tool execution strategies. Source: [strands-py/src/strands/tools/executors/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/__init__.py)

| Executor | Description | Use Case |
|----------|-------------|----------|
| `SequentialToolExecutor` | Executes tools one at a time | Debugging, ordered operations |
| `ConcurrentToolExecutor` | Executes multiple tools in parallel | Independent tool calls, performance |

### Model Context Protocol (MCP) Integration

The MCP client provides native support for the Model Context Protocol, enabling agents to connect to MCP servers and access pre-built tools. Source: [strands-py/src/strands/tools/mcp/mcp_client.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

```mermaid
sequenceDiagram
    participant Agent
    participant MCPClient
    participant MCPSession
    participant MCPServer

    Agent->>MCPClient: list_tools_sync()
    MCPClient->>MCPSession: list_tools()
    MCPSession->>MCPServer: ListToolsRequest
    MCPServer-->>MCPSession: ListToolsResult
    MCPSession-->>MCPClient: tools[]
    MCPClient-->>Agent: MCPAgentTool[]

    Agent->>MCPClient: call_tool(name, args)
    MCPClient->>MCPSession: call_tool()
    MCPSession->>MCPServer: CallToolRequest
    MCPServer-->>MCPSession: CallToolResult
    MCPSession-->>MCPClient: MCPToolResult
    MCPClient-->>Agent: Tool Result
```

#### MCP Task-Augmented Execution

For long-running tool operations, the SDK supports task-augmented execution as defined in the MCP specification (2025-11-25). This feature uses a three-phase approach: create task, poll for completion, and retrieve results. Source: [strands-py/src/strands/tools/mcp/mcp_tasks.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)

| Configuration | Type | Default | Description |
|---------------|------|---------|-------------|
| `ttl` | `timedelta` | 1 minute | Task time-to-live |
| `poll_timeout` | `timedelta` | 5 minutes | Timeout for polling task completion |

> **Note**: This is an experimental feature. The MCP specification and Strands implementation are subject to change. Source: [strands-py/src/strands/tools/mcp/mcp_tasks.py:14-24](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)

```mermaid
graph TD
    A[Tool Call Request] --> B{Server Supports Tasks?}
    B -->|Yes| C[call_tool_as_task]
    B -->|No| D[call_tool]
    C --> E[Create Task]
    E --> F[Poll for Status]
    F --> G{Terminal Status?}
    G -->|No| F
    G -->|Yes| H[get_task_result]
    H --> I[Return Result]
    D --> J[Execute Directly]
    J --> I
```

## Agent Loop Architecture

The agent loop is the core execution engine that orchestrates model interactions, tool calls, and session management. Source: [strands-py/src/strands/event_loop/event_loop.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/event_loop/event_loop.py)

```mermaid
graph TD
    A[User Input] --> B[Start Cycle]
    B --> C[Call Model API]
    C --> D{Response Type?}
    D -->|Tool Use| E[Handle Tool Execution]
    E --> F[Execute Tools]
    F --> G[Collect Results]
    G --> H[Append to History]
    H --> B
    D -->|End Turn| I[Return Response]
    D -->|Max Tokens| J[Raise MaxTokensReachedException]
```

### Event Loop Lifecycle

The event loop manages the cycle of:
1. **Start cycle**: Initialize metrics and tracing for the cycle
2. **Model invocation**: Call the configured model with current conversation history
3. **Handle response**: Process the model's response (tool call, text response, or error)
4. **Tool execution**: Execute requested tools and collect results
5. **End cycle**: Update metrics and append results to conversation history

Source: [strands-py/src/strands/telemetry/metrics_constants.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/metrics_constants.py)

### Structured Output

The SDK supports structured output through a tool-based mechanism. When enabled, the model is forced to invoke a structured output tool to extract typed responses. Source: [strands-py/src/strands/tools/structured_output/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/structured_output/__init__.py)

## Session Management

The session manager handles conversation history, message persistence, and context management. It provides strategies for managing context windows as conversations grow. Source: [strands-py/src/strands/session/session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_manager.py)

### Conversation Managers

Recent releases (v1.40.0+) introduced proactive context compression for conversation managers. This feature helps manage context window limits by intelligently compressing conversation history when needed. Source: [strands-py/src/strands/session/session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_manager.py)

| Manager Type | Description |
|--------------|-------------|
| `SlidingWindowConversationManager` | Maintains recent messages within a window limit |
| `FullConversationManager` | Stores all messages (subject to context limits) |

## Plugin Architecture

The plugin system provides a powerful mechanism for extending agent capabilities. Plugins can contribute tools, modify agent behavior, and integrate with external systems. Source: [designs/0001-plugins.md](https://github.com/strands-agents/sdk-python/blob/main/designs/0001-plugins.md)

```mermaid
graph TD
    A[Agent] --> B[Plugin System]
    B --> C[MultiAgentPlugin]
    B --> D[Custom Plugins]
    C --> E[Swarm Orchestrator]
    C --> F[Graph Orchestrator]
    D --> G[Tool Plugins]
    D --> H[Hook Plugins]
```

### Available Plugins

#### AgentSkills Plugin

The AgentSkills plugin integrates with AgentSkills.io, enabling progressive disclosure of instructions. Metadata is injected into the system prompt upfront, and full instructions are loaded on demand via tools. Source: [strands-py/src/strands/vended_plugins/skills/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/skills/__init__.py)

#### MultiAgentPlugin

Added in v1.41.0, the MultiAgentPlugin supports Swarm and Graph orchestrators for coordinating multiple agents. Source: [strands-py/src/strands/multiagent/a2a/_converters.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/_converters.py)

## Multi-Agent Systems

The SDK supports building multi-agent systems through the Agent-to-Agent (A2A) protocol. This enables complex workflows where specialized agents collaborate to complete tasks. Source: [strands-py/src/strands/multiagent/a2a/_converters.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/_converters.py)

```mermaid
graph LR
    A[Orchestrator Agent] -->|A2A| B[Specialist Agent 1]
    A -->|A2A| C[Specialist Agent 2]
    A -->|A2A| D[Specialist Agent N]
    
    B -->|Task Complete| A
    C -->|Task Complete| A
    D -->|Task Complete| A
```

### Task State Mapping

The SDK maps A2A task lifecycle states to appropriate Strands stop reasons:

| A2A TaskState | Strands Stop Reason | Behavior |
|---------------|---------------------|----------|
| `completed` | `end_turn` | Normal completion |
| `failed` | `end_turn` | Error in content |
| `canceled` | `end_turn` | Cancellation info |
| `rejected` | `end_turn` | Rejection info |
| `input_required` | `interrupt` | Agent needs user input |
| `auth_required` | `interrupt` | Agent needs authentication |

Source: [strands-py/src/strands/multiagent/a2a/_converters.py:72-82](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/_converters.py)

## Telemetry and Metrics

The SDK emits structured metrics for observability, including cycle counts, tool execution metrics, token usage, and latency measurements. Source: [strands-py/src/strands/telemetry/metrics_constants.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/metrics_constants.py)

| Metric | Type | Description |
|--------|------|-------------|
| `strands.event_loop.cycle_count` | Counter | Number of agent loop cycles |
| `strands.event_loop.latency` | Histogram | End-to-end cycle latency |
| `strands.tool.call_count` | Counter | Total tool calls |
| `strands.tool.duration` | Histogram | Tool execution duration |
| `strands.event_loop.input.tokens` | Histogram | Input token count |
| `strands.event_loop.output.tokens` | Histogram | Output token count |
| `strands.model.time_to_first_token` | Histogram | Time to first token |

> **Known Issue**: The MetricsClient singleton is not thread-safe (see [Issue #2342](https://github.com/strands-agents/sdk-python/issues/2342)). This is a known limitation in v1.41.0.

## Context Management

Context management strategies define how conversation history is maintained and when compression occurs. The SDK provides configurable strategies for different use cases. Source: [designs/0003-context-management.md](https://github.com/strands-agents/sdk-python/blob/main/designs/0003-context-management.md)

### Proactive Context Compression

Introduced in v1.40.0, proactive context compression helps prevent context window exhaustion by compressing conversation history before limits are reached. The system includes a fallback trim point for tool-heavy conversations. Source: [strands-py/src/strands/session/session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_manager.py)

## Common Architecture Patterns

### Tool Invocation Flow

```mermaid
sequenceDiagram
    participant AgentLoop
    participant ToolExecutor
    participant Tool
    
    AgentLoop->>ToolExecutor: execute_tools(tool_requests)
    ToolExecutor->>Tool: call(tool_name, arguments)
    Tool-->>ToolExecutor: ToolResult
    ToolExecutor-->>AgentLoop: ToolEvent
```

### Error Handling

The architecture follows consistent error handling patterns:
- Tool errors are captured and returned as `ToolResult` with error status
- The agent loop can be configured to fail on first error or collect all results
- Max token errors raise `MaxTokensReachedException` for unrecoverable states

## Directory Structure

```
strands-py/src/strands/
├── agent/           # Agent core implementation
├── models/         # Model provider implementations
├── tools/          # Tool system and executors
│   ├── mcp/        # MCP client and task support
│   ├── executors/  # Tool execution strategies
│   └── structured_output/  # Structured output support
├── session/        # Session and conversation management
├── event_loop/     # Agent event loop
├── telemetry/      # Metrics and observability
├── multiagent/     # Multi-agent system support
│   └── a2a/        # Agent-to-agent protocol
└── vended_plugins/ # Built-in plugins (skills, etc.)
```

## See Also

- [Agent Loop Documentation](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/)
- [MCP Integration Guide](https://strandsagents.com/docs/)
- [Plugin Development Guide](https://strandsagents.com/docs/)
- [Quick Start Guide](https://strandsagents.com/docs/user-guide/quickstart/)
- [Production Deployment Guide](https://strandsagents.com/docs/user-guide/deploy/operating-agents-in-production/)
- [API Reference](https://strandsagents.com/docs/api/python/strands.agent.agent/)

---

<a id='agent-system'></a>

## Agent System

### Related Pages

Related topics: [Model Providers](#model-providers), [Tool System](#tool-system), [Session & Conversation Management](#session-conversation-mgmt)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)
- [strands-py/src/strands/agent/base.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/base.py)
- [strands-py/src/strands/agent/state.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/state.py)
- [strands-py/src/strands/agent/agent_result.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent_result.py)
- [strands-py/src/strands/agent/_agent_as_tool.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/_agent_as_tool.py)
- [strands-py/src/strands/hooks/events.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/hooks/events.py)
- [strands-py/src/strands/hooks/registry.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/hooks/registry.py)
- [strands-py/src/strands/event_loop/event_loop.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/event_loop/event_loop.py)
</details>

# Agent System

The Agent System is the core component of Strands Agents, providing a model-driven approach to building and running AI agents. From simple conversational assistants to complex autonomous workflows, the Agent System delivers a lightweight yet powerful framework that scales from local development to production deployment.

Source: [strands-py/src/strands/agent/base.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/base.py)

## Overview

The Agent System implements a **model-driven agent loop** that processes inputs, calls tools when necessary, and generates responses. It is designed to be:

- **Lightweight & Flexible**: A simple agent loop that just works and is fully customizable
- **Model Agnostic**: Supports multiple model providers including Amazon Bedrock, Anthropic, Gemini, LiteLLM, Llama, Ollama, OpenAI, and Writer
- **Advanced Capable**: Supports multi-agent systems, autonomous agents, and streaming
- **Tool-Enabled**: Native MCP support for accessing thousands of pre-built tools

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## Architecture

The Agent System follows a layered architecture with the agent loop at its core, interacting with tools, conversation managers, and model providers.

```mermaid
graph TD
    A[User Input] --> B[Agent]
    B --> C[Model Provider]
    C --> D{Stop Reason?}
    D -->|tool_use| E[Tool Executor]
    E --> B
    D -->|end_turn| F[Response]
    B --> G[Conversation Manager]
    B --> H[Telemetry]
    B --> I[Hooks]
```

Source: [strands-py/src/strands/event_loop/event_loop.py:1-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/event_loop/event_loop.py)

### Event Loop

The event loop is the core execution engine of the Agent System. It handles:

1. **Initializing cycle state and metrics** - Setting up per-turn context
2. **Checking execution limits** - Verifying max_tokens and other constraints
3. **Processing messages with the model** - Sending requests to the model provider
4. **Handling tool execution requests** - Managing tool calls and results
5. **Managing recursive calls** - Supporting multi-turn tool interactions
6. **Collecting and reporting metrics** - Tracking performance via telemetry
7. **Error handling and recovery** - Managing exceptions gracefully

Source: [strands-py/src/strands/event_loop/event_loop.py:100-200](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/event_loop/event_loop.py)

#### Cycle Flow

```mermaid
sequenceDiagram
    participant User
    participant Agent
    participant EventLoop
    participant Model
    participant ToolExecutor
    
    User->>Agent: invoke(input)
    Agent->>EventLoop: event_loop_cycle()
    EventLoop->>Model: process_message()
    Model-->>EventLoop: stop_reason = tool_use
    EventLoop->>ToolExecutor: handle_tool_execution()
    ToolExecutor->>Model: execute_tool()
    Model-->>EventLoop: tool_result
    EventLoop->>EventLoop: recurse (next cycle)
    EventLoop-->>Agent: final_response
    Agent-->>User: result
```

### Tool System

Tools extend the agent's capabilities beyond text generation. The Agent System supports:

| Tool Type | Description | Source |
|-----------|-------------|--------|
| MCP Tools | Model Context Protocol tools from external servers | [mcp_client.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py) |
| Python Tools | User-defined Python functions | [tools/](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/) |
| Structured Output | Pydantic model-based output | [structured_output/](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/structured_output/) |

Source: [strands-py/src/strands/agent/agent.py:100-150](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)

## Core Components

### Agent Class

The `Agent` class is the primary entry point for creating AI agents.

```python
from strands import Agent
from strands_tools import calculator

agent = Agent(tools=[calculator])
response = agent("What is the square root of 1764?")
```

Source: [strands-py/src/strands/agent/agent.py:1-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)

#### Agent Parameters

| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| `model` | `Model` | The model to use for inference | `BedrockModel` |
| `system_prompt` | `str` | System prompt for the agent | `None` |
| `tools` | `list[Tool]` | List of tools available to the agent | `[]` |
| `tool_executor` | `ToolExecutor` | Tool execution strategy | `None` |
| `conversation_manager` | `ConversationManager` | Manages conversation history | `SlidingWindowConversationManager` |
| `structured_output_model` | `type[BaseModel]` | Pydantic model for structured output | `None` |
| `plugins` | `list[AgentPlugin]` | Plugins extending agent behavior | `[]` |
| `hooks` | `HookCallback` | Callback hooks for events | `None` |

Source: [strands-py/src/strands/agent/agent.py:50-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent.py)

### State Management

The Agent maintains state across multiple invocations, tracking conversation history and execution context.

```mermaid
classDiagram
    class AgentState {
        +messages: list[Message]
        +request_state: dict
        +invocation_state: InvocationState
    }
    
    class InvocationState {
        +event_loop_cycle_count: int
        +total_tokens: int
        +last_stop_reason: StopReason
    }
```

Source: [strands-py/src/strands/agent/state.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/state.py)

### Agent Result

The `AgentResult` class encapsulates the response from an agent invocation.

```python
from strands.agent.agent_result import AgentResult

result: AgentResult = agent("Hello")
print(result.message)        # The response message
print(result.conversation)  # Full conversation history
print(result.metrics)       # Event loop metrics
```

Source: [strands-py/src/strands/agent/agent_result.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent_result.py)

#### AgentResult Properties

| Property | Type | Description |
|----------|------|-------------|
| `message` | `Message` | The last response message |
| `messages` | `list[Message]` | All messages in conversation |
| `conversation` | `list[Message]` | Full conversation history |
| `metrics` | `EventLoopMetrics` | Performance and usage metrics |
| `request_state` | `dict` | Custom state preserved across calls |

Source: [strands-py/src/strands/agent/agent_result.py:50-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/agent_result.py)

## Agent as Tool

Agents can be used as tools by other agents, enabling multi-agent workflows. This is implemented via the `_AgentAsTool` wrapper class.

```mermaid
graph LR
    A[Parent Agent] -->|calls tool| B[Agent as Tool]
    B --> C[Child Agent]
    C -->|invokes| D[Model]
    D -->|response| C
    C -->|result| B
    B -->|tool result| A
```

Source: [strands-py/src/strands/agent/_agent_as_tool.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/_agent_as_tool.py)

```python
from strands import Agent

child_agent = Agent(model=small_model, tools=[...])
parent_agent = Agent(
    model=large_model,
    tools=[child_agent.as_tool(name="analyzer", description="...")]
)
```

Source: [strands-py/src/strands/agent/_agent_as_tool.py:100-150](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/_agent_as_tool.py)

## Hooks System

The hooks system provides a way to customize agent behavior at key points during execution.

### Available Hooks

| Hook Event | Trigger Point | Use Case |
|------------|---------------|----------|
| `on_tool_start` | Before tool execution | Logging, monitoring |
| `on_tool_end` | After tool execution | Result processing |
| `on_message_start` | Before message processing | Input validation |
| `on_message_end` | After message generation | Output transformation |
| `on_agent_start` | Before agent invocation | Setup, initialization |
| `on_agent_end` | After agent invocation | Cleanup, reporting |

Source: [strands-py/src/strands/hooks/events.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/hooks/events.py)

### Hook Callback Signature

Hooks can be passed as callable callbacks directly in the Agent constructor:

```python
from strands import Agent

def my_hook(event, context):
    print(f"Event: {event}")
    print(f"Context: {context}")

agent = Agent(
    model=model,
    hooks=my_hook,
    tools=[...]
)
```

Source: [strands-py/src/strands/hooks/registry.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/hooks/registry.py)

## Tool Executors

Tool executors define how tools are executed within the agent loop.

### Available Executors

| Executor | Description | Use Case |
|----------|-------------|----------|
| `SequentialToolExecutor` | Executes tools one at a time | Sequential operations, debugging |
| `ConcurrentToolExecutor` | Executes independent tools in parallel | Performance optimization |

Source: [strands-py/src/strands/tools/executors/__init__.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/__init__.py)

### Configuration

```python
from strands import Agent
from strands.tools.executors import ConcurrentToolExecutor

agent = Agent(
    model=model,
    tools=independent_tools,
    tool_executor=ConcurrentToolExecutor()
)
```

## Conversation Management

The conversation manager controls how conversation history is maintained and managed.

### Default Manager

By default, Strands uses `SlidingWindowConversationManager` which maintains a sliding window of the most recent messages.

```python
from strands import Agent
from strands.types.conversation import SlidingWindowConversationManager

agent = Agent(
    model=model,
    conversation_manager=SlidingWindowConversationManager(
        max_messages=20  # Keep last 20 messages
    )
)
```

Source: [strands-py/src/strands/types/conversation.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/types/conversation.py)

### Proactive Context Compression

The v1.40.0 release added proactive context compression to conversation managers, which automatically manages context window usage when approaching limits.

Source: [Release v1.40.0](https://github.com/strands-agents/sdk-python/releases/tag/v1.40.0)

## Structured Output

Agents can return structured data conforming to a Pydantic model.

```python
from strands import Agent
from pydantic import BaseModel

class WeatherResponse(BaseModel):
    temperature: float
    unit: str
    conditions: str

agent = Agent(
    model=model,
    structured_output_model=WeatherResponse
)

result = agent("What's the weather like?")
# result is a WeatherResponse instance
```

Source: [strands-py/src/strands/tools/structured_output/__init__.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/structured_output/__init__.py)

## Telemetry

The Agent System integrates with OpenTelemetry for observability.

### Metrics

| Metric | Type | Description |
|--------|------|-------------|
| `strands.event_loop.cycle_count` | Counter | Number of event loop cycles |
| `strands.event_loop.latency` | Histogram | Event loop cycle latency |
| `strands.tool.call_count` | Counter | Number of tool calls |
| `strands.tool.duration` | Histogram | Tool execution duration |
| `strands.model.time_to_first_token` | Histogram | Time to first token |

Source: [strands-py/src/strands/telemetry/metrics_constants.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/metrics_constants.py)

### Configuration

```python
from strands.telemetry.config import StrandsTelemetry

# Setup with console exporter
StrandsTelemetry().setup_console_exporter()

# Setup with OTLP exporter
StrandsTelemetry().setup_otlp_exporter()
```

Source: [strands-py/src/strands/telemetry/config.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/config.py)

## Common Patterns

### Basic Agent

```python
from strands import Agent

agent = Agent()
response = agent("Hello, world!")
print(response)
```

### Tool-Using Agent

```python
from strands import Agent
from strands_tools import calculator

agent = Agent(tools=[calculator])
response = agent("What is 15 * 23?")
```

### Multi-Modal Agent

```python
from strands import Agent
from strands.types.content import image_url, text

agent = Agent()
response = agent([
    image_url(url="https://example.com/image.jpg"),
    text("What's in this image?")
])
```

### Streaming Agent

```python
from strands import Agent
from strands.models import BedrockModel

model = BedrockModel(streaming=True)
agent = Agent(model=model)

for event in agent.stream("Tell me a story"):
    print(event, end="", flush=True)
```

## Troubleshooting

### MetricsClient Thread Safety

The `MetricsClient` singleton is not thread-safe in versions prior to the fix. If using multi-threaded workloads, ensure you are on the latest version.

Source: [Issue #2342](https://github.com/strands-agents/sdk-python/issues/2342)

### GeminiModel TypeError

When using `safety_settings` with GeminiModel, ensure you handle cases where `usage_metadata` may be `None`. This is fixed in recent versions.

Source: [Issue #2347](https://github.com/strands-agents/sdk-python/issues/2347)

### Max Tokens Reached

When the agent reaches the max_tokens limit, it fails with a `MaxTokensReachedException`. Configure appropriate token limits in your model settings.

Source: [strands-py/src/strands/event_loop/event_loop.py:200-250](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/event_loop/event_loop.py)

## See Also

- [Agent Loop Documentation](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/)
- [Quick Start Guide](https://strandsagents.com/docs/user-guide/quickstart/)
- [MCP Integration](./mcp-integration)
- [Tools System](./tools)
- [Model Providers](./model-providers)
- [Multi-Agent Systems](./multi-agent)
- [API Reference](https://strandsagents.com/docs/api/python/strands.agent.agent/)

---

<a id='model-providers'></a>

## Model Providers

### Related Pages

Related topics: [Agent System](#agent-system), [System Architecture](#architecture-overview)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/models/bedrock.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/bedrock.py)
- [strands-py/src/strands/models/anthropic.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/anthropic.py)
- [strands-py/src/strands/models/gemini.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/gemini.py)
- [strands-py/src/strands/models/openai.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/openai.py)
- [strands-py/src/strands/models/ollama.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/ollama.py)
- [strands-py/src/strands/models/litellm.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/litellm.py)
- [strands-py/src/strands/models/model.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/model.py)
- [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/mistral.py)
- [strands-py/src/strands/models/sagemaker.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/sagemaker.py)
- [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/writer.py)
- [strands-py/src/strands/models/llamacpp.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/llamacpp.py)
- [strands-py/src/strands/models/llamaapi.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/llamaapi.py)
</details>

# Model Providers

Model Providers are the abstraction layer in Strands Agents that enables the framework to work with various LLM backends through a unified interface. Each model provider implements the `Model` interface and handles the specific API requirements, authentication mechanisms, request/response formatting, and feature support for its respective service.

## Overview

Strands Agents is **model agnostic**, meaning you can swap between different LLM providers without changing your agent code. The SDK ships with native support for multiple providers, from cloud-hosted services like Amazon Bedrock and Google Gemini to local deployments via Ollama or LlamaCPP.

```mermaid
graph TD
    A[Agent] --> B[Model Interface]
    B --> C[BedrockModel]
    B --> D[AnthropicModel]
    B --> E[GeminiModel]
    B --> F[OpenAIModel]
    B --> G[OllamaModel]
    B --> H[LiteLLMModel]
    B --> I[MistralModel]
    B --> J[SageMakerModel]
    B --> K[WriterModel]
    B --> L[LlamaCPPModel]
    B --> M[LlamaAPIModel]
```

### Key Capabilities

| Capability | Description |
|------------|-------------|
| **Unified Interface** | All providers implement the same `Model` protocol for consistent API |
| **Streaming Support** | Toggle streaming per-request via `streaming=True/False` |
| **Token Counting** | Built-in `count_tokens()` method with naive estimation using tiktoken |
| **Context Window Management** | Configurable `context_window_limit` per model |
| **Tool Support** | Native function calling and tool use across providers |
| **System Prompt Caching** | Support for caching system prompts (provider-dependent) |

Source: [strands-py/src/strands/models/model.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/model.py)

---

## Base Model Interface

The `Model` abstract class defines the contract that all model providers must implement. This ensures consistent behavior across different backends.

```python
from strands.models import Model

class Model(ABC):
    """Abstract base class for all model providers."""
    
    @property
    @abstractmethod
    def model_id(self) -> str:
        """Returns the model identifier."""
        pass
    
    @abstractmethod
    def generate(self, messages: list[dict], ...) -> dict:
        """Generate a response from the model."""
        pass
    
    @abstractmethod
    async def generate_async(self, messages: list[dict], ...) -> dict:
        """Async version of generate."""
        pass
    
    def count_tokens(self, text: str) -> int:
        """Estimate token count using tiktoken."""
        pass
```

Source: [strands-py/src/strands/models/model.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/model.py)

### Model Configuration

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `model_id` | `str` | Required | Provider-specific model identifier |
| `temperature` | `float` | `0.7` | Sampling temperature (0.0 to 1.0) |
| `max_tokens` | `int` | `4096` | Maximum tokens in response |
| `streaming` | `bool` | `False` | Enable streaming responses |
| `context_window_limit` | `int` | `None` | Custom context window limit |
| `params` | `dict` | `{}` | Provider-specific parameters |

Source: [strands-py/src/strands/models/bedrock.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/bedrock.py)

---

## Available Model Providers

### Amazon Bedrock

`BedrockModel` provides access to AWS Bedrock's managed LLM offerings, including Amazon's own Nova models, Anthropic Claude, and third-party models.

```python
from strands import Agent
from strands.models import BedrockModel

bedrock_model = BedrockModel(
    model_id="us.amazon.nova-pro-v1:0",
    temperature=0.3,
    streaming=True,
)
agent = Agent(model=bedrock_model)
```

**Features:**
- Amazon Nova Pro, Nova Lite, Nova Micro
- Anthropic Claude 3.5 Sonnet, Claude 3 Haiku
- Service tiers (Priority, Standard, Flex) for latency/cost trade-offs
- Tool and system/user cache point support with TTL
- AWS credentials via environment or IAM role

Source: [strands-py/src/strands/models/bedrock.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/bedrock.py)

### Anthropic

`AnthropicModel` provides direct access to Anthropic's Claude models through the Anthropic API.

```python
from strands.models.anthropic import AnthropicModel

anthropic_model = AnthropicModel(
    model_id="claude-sonnet-4-20250514",
    temperature=0.5,
)
```

Source: [strands-py/src/strands/models/anthropic.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/anthropic.py)

### Google Gemini

`GeminiModel` interfaces with Google's Gemini models via the Google AI API.

```python
from strands.models.gemini import GeminiModel

gemini_model = GeminiModel(
    client_args={"api_key": "your_gemini_api_key"},
    model_id="gemini-2.5-flash",
    params={"temperature": 0.7}
)
```

**Note:** There is a known issue where `GeminiModel` may crash with `TypeError` when safety settings block content and `usage_metadata` is `None`. This is tracked in [issue #2347](https://github.com/strands-agents/sdk-python/issues/2347).

Source: [strands-py/src/strands/models/gemini.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/gemini.py)

### OpenAI

`OpenAIModel` supports OpenAI's GPT models and can also use AWS profiles for authentication.

```python
from strands.models.openai import OpenAIModel

openai_model = OpenAIModel(
    model_id="gpt-4o",
    client_args={"api_key": "your-openai-api-key"},
)
```

**AWS Profile Support:** As of v1.39.0, OpenAI provider can authenticate using AWS credentials via profile configuration.

Source: [strands-py/src/strands/models/openai.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/openai.py)

### Ollama

`OllamaModel` enables connecting to locally running Ollama instances for self-hosted models.

```python
from strands.models.ollama import OllamaModel

ollama_model = OllamaModel(
    model_id="llama3.2",
    base_url="http://localhost:11434",
)
```

**Note:** There was a fix in v1.40.0 updating the return type of the `latencyMs` metric for Ollama mode.

Source: [strands-py/src/strands/models/ollama.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/ollama.py)

### LiteLLM

`LiteLLMModel` acts as a proxy layer supporting multiple providers through LiteLLM's unified interface.

```python
from strands.models.litellm import LiteLLMModel

litellm_model = LiteLLMModel(
    model_id="gpt-4o",
    api_key="your-api-key",
)
```

Source: [strands-py/src/strands/models/litellm.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/litellm.py)

### Mistral

`MistralModel` provides access to Mistral AI's models.

```python
from strands.models.mistral import MistralModel

mistral_model = MistralModel(
    model_id="mistral-large-latest",
)
```

Source: [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/mistral.py)

### Amazon SageMaker

`SageMakerModel` enables deployment and inference of models hosted on Amazon SageMaker endpoints.

```python
from strands.models.sagemaker import SageMakerModel

sagemaker_model = SageMakerModel(
    endpoint_name="your-sagemaker-endpoint",
)
```

Source: [strands-py/src/strands/models/sagemaker.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/sagemaker.py)

### Writer

`WriterModel` integrates with Writer's Palmyra models.

```python
from strands.models.writer import WriterModel

writer_model = WriterModel(
    model_id="palmyra-fin",
)
```

Source: [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/writer.py)

### LlamaCPP

`LlamaCPPModel` supports local inference using llama.cpp bindings.

```python
from strands.models.llamacpp import LlamaCppModel

llamacpp_model = LlamaCppModel(
    model_path="/path/to/model.gguf",
)
```

Source: [strands-py/src/strands/models/llamacpp.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/llamacpp.py)

### LlamaAPI

`LlamaAPIModel` provides access to models via the LlamaAPI service.

```python
from strands.models.llamaapi import LlamaAPIModel

llamaapi_model = LlamaAPIModel(
    model_id="llama-3.1-sonar-huge-128k-online",
)
```

Source: [strands-py/src/strands/models/llamaapi.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/llamaapi.py)

---

## Architecture

### Request Flow

```mermaid
sequenceDiagram
    participant Agent
    participant Model as Model Interface
    participant Provider as Specific Provider
    participant API as External API
    
    Agent->>Model: generate(messages)
    Model->>Provider: format_request()
    Provider->>API: API call
    API-->>Provider: raw response
    Provider->>Model: parse_response()
    Model-->>Agent: structured response
    
    Note over Agent,API: Same flow for async via generate_async()
```

### Model Provider Selection

When you create an `Agent`, you can specify which model to use. If no model is provided, the SDK will use default settings.

```mermaid
graph LR
    A[Create Agent] --> B{Model Provided?}
    B -->|Yes| C[Use Specified Model]
    B -->|No| D[Use Default Provider]
    C --> E[Initialize Agent]
    D --> E
```

---

## Common Configuration Patterns

### Streaming Responses

Enable streaming for real-time token generation:

```python
model = BedrockModel(
    model_id="us.amazon.nova-pro-v1:0",
    streaming=True,
)
```

Source: [strands-py/src/strands/models/bedrock.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/bedrock.py)

### Context Window Management

Set a custom context window limit to prevent exceeding provider limits:

```python
model = BedrockModel(
    model_id="anthropic.claude-3-5-sonnet-20241022-v2:0",
    context_window_limit=200000,
)
```

Source: [strands-py/src/strands/models/model.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/models/model.py)

### Provider-Specific Parameters

Pass provider-specific parameters through the `params` dictionary:

```python
model = GeminiModel(
    model_id="gemini-2.5-flash",
    params={
        "temperature": 0.7,
        "top_p": 0.9,
        "max_output_tokens": 2048,
    }
)
```

---

## Known Issues and Limitations

| Issue | Severity | Description | Reference |
|-------|----------|-------------|-----------|
| GeminiModel crash | Bug | Crashes with `TypeError` when safety_settings block content (None usage_metadata) | [#2347](https://github.com/strands-agents/sdk-python/issues/2347) |
| MetricsClient thread-safety | Bug | MetricsClient singleton is not thread-safe | [#2342](https://github.com/strands-agents/sdk-python/issues/2342) |
| Ollama latency metric | Fixed | Return type of latencyMs metric was incorrect | [v1.40.0](https://github.com/strands-agents/sdk-python/releases/tag/v1.40.0) |
| OpenAI AWS profile | Fixed | Added in v1.39.0 | [PR#2230](https://github.com/strands-agents/sdk-python/pull/2230) |

---

## Best Practices

### 1. Specify Model ID Explicitly

Always specify the exact `model_id` to avoid ambiguity:

```python
# Recommended
model = BedrockModel(model_id="us.amazon.nova-pro-v1:0")

# Avoid - may use unexpected default
model = BedrockModel()
```

### 2. Configure Appropriate Timeouts

For long-running tasks, configure appropriate timeouts:

```python
model = OllamaModel(
    model_id="llama3.2",
    timeout=timedelta(minutes=5),
)
```

### 3. Enable Streaming Judiciously

Streaming adds overhead for simple requests. Enable when user experience benefits:

```python
# Enable for interactive use cases
model = BedrockModel(streaming=True)

# Disable for batch operations
model = BedrockModel(streaming=False)
```

### 4. Monitor Token Usage

Use the built-in `count_tokens()` method to monitor usage:

```python
token_count = model.count_tokens("Your text here")
print(f"Estimated tokens: {token_count}")
```

---

## See Also

- [Agent Loop](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/) - How agents use model providers
- [Tool Execution](https://github.com/strands-agents/sdk-python) - Tool use with different models
- [MCP Support](https://github.com/strands-agents/sdk-python) - MCP integration with model providers
- [API Reference](https://strandsagents.com/docs/api/python/strands.models/) - Full API documentation

---

<a id='tool-system'></a>

## Tool System

### Related Pages

Related topics: [Agent System](#agent-system), [MCP Integration](#mcp-integration)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/tools/decorator.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/decorator.py)
- [strands-py/src/strands/tools/executors/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/__init__.py)
- [strands-py/src/strands/tools/executors/concurrent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/concurrent.py)
- [strands-py/src/strands/tools/executors/sequential.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/sequential.py)
- [strands-py/src/strands/tools/structured_output/__init__.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/structured_output/__init__.py)
- [strands-py/src/strands/tools/mcp/mcp_client.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)
- [strands-py/src/strands/tools/mcp/mcp_tasks.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)
- [strands-py/src/strands/tools/watcher.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/watcher.py)
</details>

# Tool System

The Strands Agents Tool System provides a flexible, extensible mechanism for defining, managing, and executing tools that AI agents can invoke during their reasoning loops. Tools enable agents to interact with external systems, perform computations, access data, and execute actions beyond the model's native capabilities.

## Overview

The Tool System consists of several interconnected components that work together to provide a seamless tool execution experience:

```mermaid
graph TD
    A[Agent] -->|invokes| B[Tool Registry]
    B -->|selects| C[Tool Executor]
    C -->|executes| D[Python Tools]
    C -->|executes| E[MCP Tools]
    D -->|returns| F[Tool Result]
    E -->|returns| F
    G[Tool Decorator] -->|defines| D
    H[MCPClient] -->|exposes| E
```

**Key capabilities:**
- Define tools using Python decorators with automatic Pydantic schema generation
- Execute tools sequentially or concurrently based on agent decisions
- Integrate with Model Context Protocol (MCP) servers for extended tooling
- Support hot-reloading of tools from directories for development
- Structured output generation using tool-based constraints

## Tool Definition

### The `@tool` Decorator

The primary mechanism for defining tools in Strands is the `@tool` decorator. This decorator transforms a Python function into a tool that the agent can invoke. Source: [decorator.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/decorator.py)

```python
from strands import Agent, tool

@tool
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression.
    
    Args:
        expression: A mathematical expression to evaluate.
    """
    return str(eval(expression))

agent = Agent(tools=[calculator])
```

### Docstring Parsing

The decorator automatically extracts tool metadata from the function's docstring. The description is derived from the docstring content, excluding the `Args:` section. Source: [decorator.py:100-150](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/decorator.py)

**Docstring format:**
```python
@tool
def word_count(text: str) -> int:
    """Count words in text.

    This description is used by the LLM to understand the tool's purpose.
    
    Args:
        text: The text to count words in.
    
    Returns:
        The number of words in the text.
    """
    return len(text.split())
```

The `@tool` decorator extracts the description by parsing the docstring and excluding everything from `Args:` onwards. This ensures the LLM receives only the purpose description without parameter details.

### Pydantic Schema Generation

Tool parameter schemas are automatically generated from Python type hints using Pydantic. The decorator creates an input model that validates and serializes tool arguments. Source: [decorator.py:150-200](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/decorator.py)

```python
@tool
def search_documents(
    query: str,
    limit: int = 10,
    filters: dict | None = None
) -> list[dict]:
    """Search documents matching a query."""
    ...
```

The generated schema includes:
- Parameter names and types
- Default values (optional parameters)
- Type constraints (min/max for numbers, patterns for strings)
- Required vs optional classification

## Tool Specification

### ToolSpec Data Model

Tools are represented internally by a `ToolSpec` dictionary containing metadata that describes the tool's interface. Source: [tools.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/tools.py)

| Field | Type | Description |
|-------|------|-------------|
| `name` | `str` | Unique identifier for the tool |
| `description` | `str` | Human-readable description for the LLM |
| `inputSchema` | `dict` | JSON Schema describing parameters |

### Input Schema Structure

```json
{
  "name": "calculator",
  "description": "Evaluate a mathematical expression",
  "inputSchema": {
    "json": {
      "type": "object",
      "properties": {
        "expression": {
          "type": "string",
          "description": "A mathematical expression to evaluate"
        }
      },
      "required": ["expression"]
    }
  }
}
```

## Tool Execution

### Execution Strategies

Strands provides two built-in execution strategies for handling tool calls. Source: [executors/__init__.py:1-20](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/__init__.py)

#### Sequential Tool Executor

Tools are executed one at a time in the order determined by the agent. This is the default behavior. Source: [executors/sequential.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/sequential.py)

```python
from strands.tools.executors import SequentialToolExecutor

executor = SequentialToolExecutor()
agent = Agent(tool_executor=executor)
```

#### Concurrent Tool Executor

Tools are executed in parallel when the agent decides they are independent. Source: [executors/concurrent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/executors/concurrent.py)

```python
from strands.tools.executors import ConcurrentToolExecutor

executor = ConcurrentToolExecutor(max_workers=4)
agent = Agent(tool_executor=executor)
```

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `max_workers` | `int` | `4` | Maximum concurrent tool executions |

### Execution Flow

```mermaid
sequenceDiagram
    participant Agent
    participant Executor
    participant Tool1
    participant Tool2
    
    Agent->>Executor: execute_tools([tool_calls])
    
    alt Sequential Execution
        Executor->>Tool1: call(args)
        Tool1-->>Executor: result
        Executor->>Tool2: call(args)
        Tool2-->>Executor: result
    else Concurrent Execution
        Executor->>Tool1: call(args)
        Executor->>Tool2: call(args)
        Tool1-->>Executor: result
        Tool2-->>Executor: result
    end
    
    Executor-->>Agent: [results]
```

## MCP Integration

### Model Context Protocol Overview

The MCP (Model Context Protocol) integration enables Strands agents to use tools from MCP servers. MCP provides a standardized way to expose tools from various sources including local processes, HTTP servers, and cloud services. Source: [mcp_client.py:1-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

### MCPClient Configuration

```python
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp import stdio_client, StdioServerParameters

# Connect to an MCP server
mcp_client = MCPClient(
    lambda: stdio_client(
        StdioServerParameters(
            command="uvx",
            args=["awslabs.aws-documentation-mcp-server@latest"]
        )
    )
)

with mcp_client:
    agent = Agent(tools=mcp_client.list_tools_sync())
    response = agent("Tell me about Amazon Bedrock")
```

### MCP Task-Augmented Execution

For long-running tools, MCP supports task-augmented execution that enables polling for completion and retrieving results asynchronously. Source: [mcp_tasks.py:1-40](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `ttl` | `timedelta` | 1 minute | Task time-to-live duration |
| `poll_timeout` | `timedelta` | 5 minutes | Timeout for polling task completion |

**Task execution flow:**
```mermaid
graph TD
    A[Tool Call] --> B{Server Supports Tasks?}
    B -->|Yes| C[call_tool_as_task]
    B -->|No| D[call_tool Direct]
    C --> E[Get Task ID]
    E --> F[poll_task]
    F --> G{Terminal Status?}
    G -->|No| F
    G -->|Yes| H[get_task_result]
    H --> I[Return Result]
    D --> J[Return Result]
```

> **Note:** MCP task progress updates are not yet fully implemented in the current release. See [GitHub Issue #1812](https://github.com/strands-agents/sdk-python/issues/1812) for tracking progress on this feature.

## Structured Output

### Tool-Based Structured Output

Strands supports structured output generation by constraining the model to use a dedicated tool that returns structured data. This ensures type-safe, predictable responses for data-intensive tasks. Source: [structured_output/__init__.py:1-20](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/structured_output/__init__.py)

```python
from pydantic import BaseModel
from strands import Agent
from strands_tools import calculator  # Must provide structured output

class MathResult(BaseModel):
    expression: str
    result: float
    steps: list[str]

agent = Agent(
    tools=[calculator],
    structured_output=MathResult
)
```

### Converting Pydantic Models to Tools

Use `convert_pydantic_to_tool_spec` to create a tool specification from a Pydantic model:

```python
from strands.tools.structured_output import convert_pydantic_to_tool_spec
from pydantic import BaseModel

class SearchResult(BaseModel):
    title: str
    url: str
    snippet: str

tool_spec = convert_pydantic_to_tool_spec(SearchResult)
```

## Tool Watching and Hot Reloading

### Directory-Based Tool Loading

Strands supports automatic tool discovery and hot-reloading from a directory. This is particularly useful during development. Source: [watcher.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/watcher.py)

```python
from strands import Agent

# Agent watches ./tools/ directory for changes
agent = Agent(load_tools_from_directory=True)
response = agent("Use any tools you find in the tools directory")
```

### Watch Behavior

The tool watcher monitors the specified directory for:
- New `.py` files containing `@tool` decorated functions
- Modifications to existing tool files
- Deletions of tool files

Changes are automatically reflected without restarting the agent.

## Telemetry and Metrics

The Tool System emits telemetry for monitoring and observability:

| Metric | Type | Description |
|--------|------|-------------|
| `strands.tool.call_count` | Counter | Total tool invocations |
| `strands.tool.success_count` | Counter | Successful tool calls |
| `strands.tool.error_count` | Counter | Failed tool calls |
| `strands.tool.duration` | Histogram | Tool execution duration |

Source: [metrics_constants.py:1-25](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/metrics_constants.py)

## Common Patterns

### Building Multi-Step Workflows

```python
from strands import Agent, tool

@tool
def fetch_data(source: str) -> dict:
    """Fetch data from a source."""
    ...

@tool
def transform_data(data: dict, format: str) -> str:
    """Transform data to specified format."""
    ...

@tool
def save_result(data: str, destination: str) -> bool:
    """Save result to destination."""
    ...

agent = Agent(tools=[fetch_data, transform_data, save_result])
result = agent("Fetch user data, transform to JSON, and save to /tmp/data.json")
```

### Combining MCP and Python Tools

```python
from strands import Agent
from strands.tools.mcp import MCPClient
from strands_tools import calculator

# MCP server for database access
db_client = MCPClient(
    lambda: stdio_client(StdioServerParameters(...))
)

# Python tool for calculations
with db_client:
    agent = Agent(tools=[calculator, *db_client.list_tools_sync()])
```

## Troubleshooting

### Tool Not Found

If an agent fails to find a tool:
1. Verify the tool is in the agent's tool list
2. Check that the tool name matches exactly (case-sensitive)
3. Ensure the tool's description is clear and descriptive

### Tool Execution Errors

Common causes:
- **Type validation failures**: Arguments don't match the expected schema
- **Missing required parameters**: Required arguments not provided
- **Timeout errors**: Tool exceeded the configured timeout
- **Import errors**: Tool dependencies not available

### MCP Connection Issues

For MCP server connection problems:
1. Verify the MCP server command and arguments are correct
2. Check that the MCP server package is installed
3. Ensure proper authentication for cloud-based MCP servers
4. Review server logs for specific error messages

## See Also

- [Agent Loop](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/) - Understanding how agents use tools
- [MCP Server](https://github.com/strands-agents/mcp-server) - Building custom MCP servers
- [Agent Skills](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/skills/__init__.py) - Progressive instruction loading
- [Telemetry Configuration](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/telemetry/config.py) - Setting up observability

---

<a id='multi-agent-patterns'></a>

## Multi-Agent Patterns

### Related Pages

Related topics: [Agent System](#agent-system), [Plugin System](#plugins-system)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/multiagent/graph.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/graph.py)
- [strands-py/src/strands/multiagent/swarm.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/swarm.py)
- [strands-py/src/strands/multiagent/base.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/base.py)
- [strands-py/src/strands/multiagent/a2a/server.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/server.py)
- [strands-py/src/strands/multiagent/a2a/executor.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/executor.py)
- [strands-py/src/strands/multiagent/a2a/_converters.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/_converters.py)
- [strands-py/src/strands/plugins/multiagent_plugin.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/multiagent_plugin.py)
- [strands-py/src/strands/plugins/plugin.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/plugin.py)
- [strands-py/src/strands/agent/a2a_agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/a2a_agent.py)
- [strands-py/src/strands/experimental/hooks/multiagent/events.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/hooks/multiagent/events.py)
</details>

# Multi-Agent Patterns

Multi-agent patterns in Strands Agents enable sophisticated AI workflows where multiple specialized agents collaborate to solve complex tasks. Rather than relying on a single agent with all capabilities, multi-agent systems allow you to decompose problems into smaller, focused subtasks handled by specialized agents that can share context, delegate work, and coordinate their activities.

## Overview

The Strands Agents SDK provides two primary orchestration patterns for multi-agent systems:

| Pattern | Description | Use Case |
|---------|-------------|----------|
| **Swarm Orchestrator** | Dynamic, collaborative agent coordination with fluid handoffs | Open-ended tasks requiring diverse expertise |
| **Graph Orchestrator** | Structured, graph-based execution with defined node relationships | Predictable workflows with clear dependencies |

Both patterns are accessible through the `MultiAgentPlugin`, which integrates seamlessly with the standard `Agent` API. Additionally, the SDK supports the **Agent-to-Agent (A2A) protocol** for distributed multi-agent systems where agents may run in separate processes or on different machines.

Source: [strands-py/src/strands/plugins/multiagent_plugin.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/multiagent_plugin.py)

## Architecture

### Multi-Agent System Components

```mermaid
graph TB
    subgraph "Multi-Agent System"
        A["User Request"] --> B["Orchestrator"]
        B --> C1["Agent Node 1"]
        B --> C2["Agent Node 2"]
        B --> C3["Agent Node N"]
        C1 <--> D["Shared Context"]
        C2 <--> D
        C3 <--> D
        C1 --> E["Response"]
        C2 --> E
        C3 --> E
    end
    
    subgraph "Plugin Integration"
        F["MultiAgentPlugin"] --> B
    end
```

### Orchestrator Hierarchy

The multi-agent architecture follows a base class pattern:

```mermaid
classDiagram
    class BaseMultiAgent {
        +agents: dict[str, Agent]
        +results: dict[str, Any]
        +run(request: str) MultiAgentResult
    }
    
    class GraphOrchestrator {
        +nodes: list~Node~
        +edges: list~Edge~
        +execute(request: str) MultiAgentResult
    }
    
    class SwarmOrchestrator {
        +run(request: str) MultiAgentResult
    }
    
    BaseMultiAgent <|-- GraphOrchestrator
    BaseMultiAgent <|-- SwarmOrchestrator
```

Source: [strands-py/src/strands/multiagent/base.py:1-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/base.py)

## Graph Orchestrator

The Graph Orchestrator manages multi-agent execution using a directed graph structure where:

- **Nodes** represent individual agents with specific roles
- **Edges** define execution order and data flow between agents
- **Execution** follows topological ordering based on dependencies

### Node Configuration

| Parameter | Type | Description |
|-----------|------|-------------|
| `agent` | `Agent` | The Strands Agent instance |
| `name` | `str` | Unique identifier for the node |
| `description` | `str` | Description of the node's role |
| `input_schema` | `type[BaseModel]` | Expected input format |
| `output_schema` | `type[BaseModel]` | Output format specification |
| `retry_policy` | `RetryPolicy` | Retry configuration |
| `on_error` | `str` | Error handling strategy |

Source: [strands-py/src/strands/multiagent/graph.py:1-150](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/graph.py)

### Execution Flow

```mermaid
graph LR
    A["User Request"] --> B["Graph Orchestrator"]
    B --> C["Build Execution Plan"]
    C --> D{"Dependencies Ready?"}
    D -->|Yes| E["Execute Node"]
    D -->|No| F["Wait for Dependencies"]
    F --> D
    E --> G{"More Nodes?"}
    G -->|Yes| D
    G -->|No| H["Aggregate Results"]
    H --> I["Response"]
```

### Usage Example

```python
from strands import Agent
from strands.models import BedrockModel
from strands.multiagent.graph import GraphOrchestrator, Node, Edge

# Create specialized agents
researcher = Agent(
    model=BedrockModel(model_id="us.amazon.nova-pro-v1:0"),
    system_prompt="You are a research specialist. Find and summarize relevant information."
)

writer = Agent(
    model=BedrockModel(model_id="us.amazon.nova-pro-v1:0"),
    system_prompt="You are a technical writer. Create clear documentation from research."
)

# Define graph structure
orchestrator = GraphOrchestrator(
    nodes=[
        Node(agent=researcher, name="research", description="Research task"),
        Node(agent=writer, name="writing", description="Documentation task"),
    ],
    edges=[
        Edge(source="research", target="writing"),
    ]
)

# Execute workflow
result = orchestrator.run("Explain quantum computing")
```

Source: [strands-py/src/strands/multiagent/graph.py:100-200](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/graph.py)

## Swarm Orchestrator

The Swarm Orchestrator implements a dynamic collaboration pattern where agents interact fluidly, passing control between each other based on task requirements. Unlike the structured graph approach, swarm execution allows for more open-ended problem solving where the path through agents is determined at runtime.

### Collaboration Model

```mermaid
graph TD
    A["User Request"] --> B["Coordinator Agent"]
    B --> C["Specialist 1"]
    C -->|Handoff| D["Specialist 2"]
    D -->|Handoff| E["Specialist 3"]
    E -->|Complete| F["Response"]
    
    G["Context Sharing"] -.-> C
    G -.-> D
    G -.-> E
```

### Handoff Mechanism

The swarm pattern relies on explicit handoffs between agents. When one agent determines another specialist should handle a task, it returns a structured handoff request that the orchestrator routes to the appropriate agent.

Source: [strands-py/src/strands/multiagent/swarm.py:1-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/swarm.py)

### Usage Example

```python
from strands import Agent
from strands.models import BedrockModel
from strands.multiagent.swarm import SwarmOrchestrator

# Create swarm agents
coordinator = Agent(
    model=BedrockModel(model_id="us.amazon.nova-pro-v1:0"),
    system_prompt="You coordinate complex tasks and delegate to specialists."
)

researcher = Agent(
    model=BedrockModel(model_id="us.amazon.nova-pro-v1:0"),
    system_prompt="You research topics thoroughly and hand off to writers when done."
)

# Create orchestrator
orchestrator = SwarmOrchestrator(
    agents={
        "coordinator": coordinator,
        "researcher": researcher,
    }
)

# Execute with dynamic agent routing
result = orchestrator.run("Research and summarize recent AI developments")
```

## MultiAgentPlugin Integration

The `MultiAgentPlugin` provides a unified interface for integrating both Graph and Swarm orchestrators into the standard Agent workflow.

### Plugin Architecture

```mermaid
graph TB
    A["Agent with Plugin"] --> B["MultiAgentPlugin"]
    B --> C{"Orchestrator Type?"}
    C -->|Graph| D["GraphOrchestrator"]
    C -->|Swarm| E["SwarmOrchestrator"]
    D --> F["Node Execution"]
    E --> G["Agent Handoffs"]
    F --> H["MultiAgentResult"]
    G --> H
```

Source: [strands-py/src/strands/plugins/multiagent_plugin.py:1-80](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/multiagent_plugin.py)

### Plugin Configuration

| Parameter | Type | Description |
|-----------|------|-------------|
| `orchestrator` | `GraphOrchestrator \| SwarmOrchestrator` | The orchestrator to use |
| `name` | `str` | Optional name for the plugin |

### Usage with Agent

```python
from strands import Agent
from strands.models import BedrockModel
from strands.multiagent.graph import GraphOrchestrator, Node
from strands.plugins.multiagent_plugin import MultiAgentPlugin

# Define agents
agent_a = Agent(model=BedrockModel(model_id="us.amazon.nova-pro-v1:0"))
agent_b = Agent(model=BedrockModel(model_id="us.amazon.nova-pro-v1:0"))

# Create orchestrator
orchestrator = GraphOrchestrator(
    nodes=[
        Node(agent=agent_a, name="first"),
        Node(agent=agent_b, name="second"),
    ]
)

# Wrap in plugin
plugin = MultiAgentPlugin(orchestrator=orchestrator)

# Use with standard Agent
agent = Agent(plugins=[plugin])
result = agent("Complex multi-step task")
```

## Agent-to-Agent (A2A) Protocol

The A2A protocol enables communication between agents running in separate processes or on different machines. Strands Agents provides an A2A server implementation that adapts the standard Agent to the A2A protocol.

### A2A Architecture

```mermaid
graph TB
    subgraph "A2A Network"
        A["A2A Client 1"] <-->|"A2A Protocol"| B["A2A Server"]
        C["A2A Client 2"] <-->|"A2A Protocol"| B
        D["A2A Client N"] <-->|"A2A Protocol"| B
    end
    
    B --> E["Strands Agent"]
    B --> F["Task Store"]
    B --> G["Event Queue"]
```

### Server Configuration

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `agent` | `Agent` | Required | The Strands Agent to expose |
| `host` | `str` | `"127.0.0.1"` | Server host |
| `port` | `int` | `9000` | Server port |
| `http_url` | `str` | `None` | Public HTTP URL for the server |
| `serve_at_root` | `bool` | `False` | Whether to serve at root path |
| `version` | `str` | `"0.0.1"` | Agent version |
| `skills` | `list[AgentSkill]` | `None` | Agent capabilities |
| `task_store` | `TaskStore` | `InMemoryTaskStore` | Persistent task storage |

Source: [strands-py/src/strands/multiagent/a2a/server.py:20-60](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/server.py)

### A2A Server Usage

```python
from strands import Agent
from strands.models import BedrockModel
from strands.multiagent.a2a.server import A2AServer

# Create and wrap agent
agent = Agent(
    model=BedrockModel(model_id="us.amazon.nova-pro-v1:0"),
    system_prompt="You are a helpful assistant."
)

# Create A2A server
server = A2AServer(
    agent=agent,
    host="0.0.0.0",
    port=9000,
    skills=[
        {"id": "assistant", "name": "General Assistant", "description": "General help"}
    ]
)

# Start server
server.start()
```

### Task State Management

The A2A protocol maintains task state throughout execution:

| State | Description |
|-------|-------------|
| `completed` | Task finished successfully |
| `failed` | Task encountered an error |
| `canceled` | Task was cancelled |
| `rejected` | Task was rejected |
| `input_required` | Waiting for additional input |
| `auth_required` | Authentication required |

Source: [strands-py/src/strands/multiagent/a2a/_converters.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/a2a/_converters.py)

## Event System

Multi-agent systems emit events for observability and debugging. The event system provides hooks for monitoring agent activities, task state changes, and handoffs.

### Available Events

| Event | Trigger |
|-------|---------|
| `agent_invocation_started` | An agent begins execution |
| `agent_invocation_completed` | An agent finishes successfully |
| `agent_invocation_failed` | An agent encounters an error |
| `handoff_requested` | An agent requests handoff to another |
| `task_state_changed` | A task transitions between states |

Source: [strands-py/src/strands/experimental/hooks/multiagent/events.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/hooks/multiagent/events.py)

## Choosing Between Patterns

| Criteria | Graph Orchestrator | Swarm Orchestrator | A2A |
|----------|-------------------|-------------------|-----|
| **Structure** | Predefined graph | Dynamic, fluid | Distributed, networked |
| **Best For** | Predictable workflows | Exploratory tasks | Microservices, separate deployments |
| **Complexity** | Medium | High | High |
| **Latency** | Low (in-process) | Low (in-process) | Network-dependent |
| **Scalability** | Single process | Single process | Multi-machine |

## Error Handling

### Retry Policies

Both orchestrators support configurable retry policies for failed agent invocations:

```python
from strands.multiagent.graph import Node, RetryPolicy

policy = RetryPolicy(
    max_attempts=3,
    initial_delay=1.0,
    max_delay=60.0,
    exponential_base=2.0
)

node = Node(
    agent=my_agent,
    name="robust_task",
    retry_policy=policy
)
```

### Error Propagation

Errors in multi-agent systems can be handled at multiple levels:

1. **Node level**: Per-agent retry policies
2. **Graph level**: `on_error` configuration to define fallback behavior
3. **Orchestrator level**: Global error handlers

Source: [strands-py/src/strands/multiagent/graph.py:150-200](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/multiagent/graph.py)

## Best Practices

1. **Define Clear Agent Roles**: Each agent should have a well-defined responsibility to avoid ambiguity in handoffs.

2. **Use Appropriate Patterns**:
   - Use **Graph** when the workflow is predictable and agents have clear dependencies
   - Use **Swarm** when tasks are exploratory and the path through agents is determined at runtime
   - Use **A2A** when agents run in separate processes or need to scale independently

3. **Implement Proper Error Handling**: Configure retry policies and error handlers for robust multi-agent systems.

4. **Monitor Agent Handoffs**: Track handoff patterns to optimize agent collaboration.

5. **Consider Context Management**: Shared context between agents can improve efficiency but may increase complexity.

## See Also

- [Agent Loop Documentation](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/)
- [Tool Usage Patterns](https://github.com/strands-agents/sdk-python/wiki/Tool-Usage-Patterns)
- [MCP Integration](https://github.com/strands-agents/sdk-python/wiki/MCP-Integration)
- [Structured Output](https://github.com/strands-agents/sdk-python/wiki/Structured-Output)
- [Agent Builder](https://github.com/strands-agents/agent-builder)
- [A2A Protocol Specification](https://github.com/ag-ui-protocol/ag-ui)

---

<a id='bidirectional-streaming'></a>

## Bidirectional Streaming (Experimental)

### Related Pages

Related topics: [Agent System](#agent-system), [Model Providers](#model-providers)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/experimental/bidi/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/agent/agent.py)
- [strands-py/src/strands/experimental/bidi/agent/loop.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/agent/loop.py)
- [strands-py/src/strands/experimental/bidi/models/gemini_live.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/gemini_live.py)
- [strands-py/src/strands/experimental/bidi/models/nova_sonic.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/nova_sonic.py)
- [strands-py/src/strands/experimental/bidi/models/openai_realtime.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/openai_realtime.py)
- [strands-py/src/strands/experimental/bidi/models/model.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/model.py)
- [strands-py/src/strands/experimental/bidi/io/audio.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/io/audio.py)
- [strands-py/src/strands/experimental/bidi/io/text.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/io/text.py)
- [strands-py/src/strands/experimental/bidi/types/events.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/types/events.py)
- [strands-py/src/strands/experimental/bidi/tools/stop_conversation.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/tools/stop_conversation.py)
</details>

# Bidirectional Streaming (Experimental)

> **⚠️ Experimental Feature**: Bidirectional streaming is currently in experimental status. APIs may change in future releases as we refine the feature based on user feedback and evolving model capabilities.

Bidirectional Streaming enables real-time voice and audio conversations with persistent streaming connections in the Strands Agents SDK. Unlike traditional request-response patterns where each interaction is independent, bidirectional streaming maintains long-running conversations where users can interrupt, provide continuous input, and receive real-time audio responses.

## Overview

The bidirectional streaming feature provides a `BidiAgent` that maintains a persistent connection with supported model providers. This enables:

- **Real-time audio conversations**: Speak directly to the agent and receive spoken responses
- **User interruptions**: Stop the model mid-generation to redirect the conversation
- **Continuous input**: Send text or audio at any point during the conversation
- **Tool execution**: Use tools within persistent streaming sessions
- **Multiple I/O modalities**: Support for both audio and text inputs/outputs

Source: [strands-py/src/strands/experimental/bidi/models/model.py:1-47](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/model.py)

## Supported Model Providers

The following model providers support bidirectional streaming:

| Provider | Model | Version Support |
|----------|-------|-----------------|
| Amazon | Nova Sonic | v1, v2 |
| Google | Gemini Live | Latest |
| OpenAI | Realtime API | Latest |

Each provider implements the `BidiModel` protocol and handles provider-specific connection protocols while exposing a standardized event-based API.

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## Architecture

### High-Level Component Architecture

```mermaid
graph TB
    subgraph "Application Layer"
        App[Application Code]
    end
    
    subgraph "Strands Bidi Layer"
        BidiAgent[BidiAgent]
        BidiLoop[BidiAgentLoop]
    end
    
    subgraph "I/O Layer"
        BidiAudioIO[BidiAudioIO]
        BidiTextIO[BidiTextIO]
    end
    
    subgraph "Model Provider Layer"
        BidiNovaSonic[BidiNovaSonicModel]
        BidiGemini[BidiGeminiLiveModel]
        BidiOpenAI[BidiOpenAIRealtimeModel]
    end
    
    subgraph "External Services"
        NovaSonic[Amazon Nova Sonic]
        GeminiLive[Google Gemini Live]
        OpenAIRealtime[OpenAI Realtime API]
    end
    
    App --> BidiAgent
    BidiAgent --> BidiLoop
    BidiLoop --> BidiAudioIO
    BidiLoop --> BidiTextIO
    BidiLoop --> BidiNovaSonic
    BidiLoop --> BidiGemini
    BidiLoop --> BidiOpenAI
    BidiNovaSonic --> NovaSonic
    BidiGemini --> GeminiLive
    BidiOpenAI --> OpenAIRealtime
```

### BidiAgent Loop Flow

```mermaid
sequenceDiagram
    participant App as Application
    participant BidiAgent as BidiAgent
    participant BidiLoop as BidiAgentLoop
    participant Model as BidiModel
    participant Provider as Model Provider

    App->>BidiAgent: run(inputs, outputs)
    BidiAgent->>BidiLoop: __init__(model, inputs, outputs)
    BidiAgent->>BidiLoop: run()
    BidiLoop->>Model: start(system_prompt, tools, messages)
    Model->>Provider: Establish connection
    Provider-->>Model: Connection established
    Model-->>BidiLoop: Connected

    loop Conversation Loop
        BidiLoop->>Model: receive()
        Provider-->>Model: BidiOutputEvent
        Model-->>BidiLoop: BidiOutputEvent
        
        alt Audio Output
            BidiLoop->>BidiLoop.outputs: audio_io.output().write(audio)
        end
        
        alt Tool Call
            BidiLoop->>BidiLoop: execute tool
            BidiLoop->>Model: send(ToolResultEvent)
        end
        
        alt User Input
            BidiLoop.outputs.input()-->BidiLoop: BidiInputEvent
            BidiLoop->>Model: send(input_event)
        end
    end

    alt Stop Conversation
        App->>BidiAgent: signal stop
        BidiAgent->>BidiLoop: cancel()
        BidiLoop->>Model: stop connection
    end
```

Source: [strands-py/src/strands/experimental/bidi/agent/loop.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/agent/loop.py)

## Installation

### Package Extras

Bidirectional streaming requires additional dependencies beyond the core Strands Agents package:

```bash
# Server-side only (no audio I/O dependencies)
pip install strands-agents[bidi]

# With audio I/O support (includes PyAudio dependency)
pip install strands-agents[bidi,bidi-io]
```

The `bidi` extra includes the core bidirectional streaming components. The additional `bidi-io` extra installs audio processing dependencies required for microphone input and speaker output.

Source: [README.md](https://github.com/strands-agents/sdk-python/blob/main/README.md)

## Core Components

### BidiModel Protocol

The `BidiModel` protocol defines the contract for all bidirectional streaming models:

```python
from strands.experimental.bidi.models.model import BidiModel

class BidiModel(Protocol):
    """Protocol for bidirectional streaming models."""
    
    config: dict[str, Any]
    
    async def start(
        self,
        system_prompt: str | None = None,
        tools: list[ToolSpec] | None = None,
        messages: Messages | None = None,
        **kwargs: Any,
    ) -> None:
        """Establish a persistent streaming connection."""
        ...
    
    async def receive(self) -> AsyncIterable[BidiOutputEvent]:
        """Receive events from the model."""
        ...
    
    async def send(self, content: BidiInputEvent | ToolResultEvent) -> None:
        """Send content to the model."""
        ...
```

Key methods:
- **`start()`**: Establishes the persistent connection before any send/receive operations
- **`receive()`**: Returns an async iterable that yields output events until the connection closes
- **`send()`**: Transmits user input, audio, images, or tool results during an active session

Source: [strands-py/src/strands/experimental/bidi/models/model.py:48-101](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/model.py)

### BidiModelTimeoutError

Bidirectional models are configured with connection time limits (e.g., Nova Sonic keeps connections open for 8 minutes maximum). When a timeout occurs, the agent loop is designed to restart the model connection seamlessly:

```python
from strands.experimental.bidi.models.model import BidiModelTimeoutError

try:
    async for event in model.receive():
        # Process events
        pass
except BidiModelTimeoutError:
    # Handle timeout - agent loop may auto-reconnect
    pass
```

Source: [strands-py/src/strands/experimental/bidi/models/model.py:113-120](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/model.py)

### Event Types

#### Input Events

Input events are sent from the client to the model:

| Event Type | Description | Attributes |
|------------|-------------|------------|
| `BidiTextInputEvent` | Text message from user | `text`, `role` |
| `BidiAudioInputEvent` | Audio data for speech | `audio`, `format`, `sample_rate`, `channels`, `encoding` |
| `BidiImageInputEvent` | Image data for vision | `image`, `mime_type`, `encoding` |
| `ToolResultEvent` | Result from tool execution | `tool_result` |

Source: [strands-py/src/strands/experimental/bidi/types/events.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/types/events.py)

#### Output Events

Output events are received from the model and include:
- Audio output (streamed audio data)
- Text transcripts (user and model speech-to-text)
- Tool calls (requests to execute tools)
- Control signals (session state changes)

### I/O Components

#### BidiAudioIO

Audio I/O component for microphone input and speaker output:

```python
from strands.experimental.bidi.io import BidiAudioIO

audio_io = BidiAudioIO(
    input_sample_rate=16000,    # Microphone sample rate
    output_sample_rate=24000,   # Speaker sample rate
    input_channels=1,
    output_channels=1,
)
```

Source: [strands-py/src/strands/experimental/bidi/io/audio.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/io/audio.py)

#### BidiTextIO

Text I/O component for terminal/console-based interactions:

```python
from strands.experimental.bidi.io import BidiTextIO

text_io = BidiTextIO()
```

Source: [strands-py/src/strands/experimental/bidi/io/text.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/io/text.py)

### BidiAgent

The main agent class for bidirectional streaming:

```python
from strands.experimental.bidi import BidiAgent
from strands.experimental.bidi.models import BidiNovaSonicModel
from strands.experimental.bidi.io import BidiAudioIO, BidiTextIO
from strands.experimental.bidi.tools import stop_conversation
from strands_tools import calculator

# Configure model
model = BidiNovaSonicModel(
    model_id="us.amazon.nova-pro-v1:0",
    session_duration_minutes=5,
)

# Configure I/O
audio_io = BidiAudioIO()
text_io = BidiTextIO()

# Create agent with tools
agent = BidiAgent(
    model=model,
    tools=[calculator, stop_conversation],
)

# Run with audio and text I/O
await agent.run(
    inputs=[audio_io.input(), text_io.input()],  # Speak OR type
    outputs=[audio_io.output(), text_io.output()]
)
```

Source: [strands-py/src/strands/experimental/bidi/agent/agent.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/agent/agent.py)

### Built-in Tools

#### stop_conversation

A built-in tool to gracefully stop the bidirectional streaming session:

```python
from strands.experimental.bidi.tools import stop_conversation

# Available in all BidiAgent instances
agent = BidiAgent(model=model, tools=[stop_conversation])
```

When invoked, this tool signals the agent to end the conversation gracefully, closing the connection cleanly.

Source: [strands-py/src/strands/experimental/bidi/tools/stop_conversation.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/tools/stop_conversation.py)

## Model Implementations

### BidiNovaSonicModel

Amazon Nova Sonic bidirectional streaming model:

```python
from strands.experimental.bidi.models import BidiNovaSonicModel

model = BidiNovaSonicModel(
    model_id="us.amazon.nova-pro-v1:0",
    session_duration_minutes=5,  # Connection timeout (max ~8 min)
    temperature=0.7,
)
```

**Configuration Options:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `model_id` | string | Required | Nova Sonic model identifier |
| `session_duration_minutes` | int | 5 | Connection session duration |
| `temperature` | float | 0.7 | Sampling temperature |
| `max_tokens` | int | 4096 | Maximum output tokens |

Source: [strands-py/src/strands/experimental/bidi/models/nova_sonic.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/nova_sonic.py)

### BidiGeminiLiveModel

Google Gemini Live bidirectional streaming model:

```python
from strands.experimental.bidi.models import BidiGeminiLiveModel

model = BidiGeminiLiveModel(
    model_id="gemini-2.0-flash-live",
    api_key="your_api_key",  # Or set GOOGLE_API_KEY env var
)
```

Source: [strands-py/src/strands/experimental/bidi/models/gemini_live.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/gemini_live.py)

### BidiOpenAIRealtimeModel

OpenAI Realtime API bidirectional streaming model:

```python
from strands.experimental.bidi.models import BidiOpenAIRealtimeModel

model = BidiOpenAIRealtimeModel(
    model_id="gpt-4o-realtime-preview",
    api_key="your_api_key",  # Or set OPENAI_API_KEY env var
)
```

Source: [strands-py/src/strands/experimental/bidi/models/openai_realtime.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/openai_realtime.py)

## Usage Patterns

### Basic Voice Conversation

```python
import asyncio
from strands.experimental.bidi import BidiAgent
from strands.experimental.bidi.models import BidiNovaSonicModel
from strands.experimental.bidi.io import BidiAudioIO

async def main():
    model = BidiNovaSonicModel(model_id="us.amazon.nova-pro-v1:0")
    audio_io = BidiAudioIO()
    
    agent = BidiAgent(model=model)
    
    await agent.run(
        inputs=[audio_io.input()],
        outputs=[audio_io.output()]
    )

asyncio.run(main())
```

### Multi-Modal Input with Voice and Text

```python
import asyncio
from strands.experimental.bidi import BidiAgent
from strands.experimental.bidi.models import BidiGeminiLiveModel
from strands.experimental.bidi.io import BidiAudioIO, BidiTextIO
from strands_tools import calculator

async def main():
    model = BidiGeminiLiveModel(
        model_id="gemini-2.0-flash-live",
        api_key="your_api_key"
    )
    
    audio_io = BidiAudioIO()
    text_io = BidiTextIO()
    
    agent = BidiAgent(
        model=model,
        tools=[calculator]
    )
    
    await agent.run(
        inputs=[audio_io.input(), text_io.input()],  # Speak OR type
        outputs=[audio_io.output(), text_io.output()]  # Both audio and text
    )

asyncio.run(main())
```

### Using Tools in Streaming Sessions

```python
import asyncio
from strands.experimental.bidi import BidiAgent
from strands.experimental.bidi.models import BidiOpenAIRealtimeModel
from strands.experimental.bidi.io import BidiAudioIO
from strands_tools import calculator, python_executor

async def main():
    model = BidiOpenAIRealtimeModel(
        model_id="gpt-4o-realtime-preview",
    )
    
    agent = BidiAgent(
        model=model,
        tools=[calculator, python_executor]
    )
    
    await agent.run(
        inputs=[BidiAudioIO().input()],
        outputs=[BidiAudioIO().output()]
    )

asyncio.run(main())
```

## Connection Lifecycle

```mermaid
stateDiagram-v2
    [*] --> Disconnected: Initial state
    Disconnected --> Connecting: agent.run()
    Connecting --> Connected: Connection established
    Connected --> Receiving: model.receive() loop starts
    Receiving --> Receiving: BidiOutputEvent received
    Receiving --> Sending: User input/tool result
    Sending --> Receiving: Send complete
    Receiving --> Disconnected: Connection closed/timeout
    Receiving --> [*]: Conversation ended
    Connecting --> Error: Connection failed
    Error --> [*]: Error handled
```

### Session Timeout Behavior

Each model provider has specific connection timeout limits:

| Provider | Max Session Duration |
|----------|---------------------|
| Amazon Nova Sonic | ~8 minutes |
| Google Gemini Live | Provider-dependent |
| OpenAI Realtime | Provider-dependent |

When a timeout occurs, the `BidiModelTimeoutError` is raised. The `BidiAgent` loop is designed to handle this gracefully by attempting to restart the connection when appropriate.

Source: [strands-py/src/strands/experimental/bidi/models/model.py:113-120](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/bidi/models/model.py)

## Error Handling

### Common Errors

| Error | Cause | Recovery |
|-------|-------|----------|
| `BidiModelTimeoutError` | Connection exceeded time limit | Agent may auto-reconnect |
| Connection refused | Provider unavailable | Check credentials and network |
| Authentication error | Invalid API key | Verify API key configuration |

### Best Practices

1. **Handle timeouts gracefully**: Always catch `BidiModelTimeoutError` for robust applications
2. **Use context managers**: Ensure proper cleanup of audio resources
3. **Validate I/O configuration**: Ensure sample rates match provider requirements
4. **Monitor connection state**: Implement health checks for long-running sessions

## Differences from Standard Agent

| Feature | Standard Agent | BidiAgent |
|---------|---------------|-----------|
| Connection model | Request-response | Persistent connection |
| User input | Single prompt | Continuous stream |
| Interrupt capability | None | User can interrupt mid-generation |
| Real-time audio | Not supported | Full support |
| Session duration | Per-request | Long-running (minutes) |

## See Also

- [Quickstart Guide](https://strandsagents.com/docs/user-guide/concepts/bidirectional-streaming/quickstart/) - Official quickstart for bidirectional streaming
- [Agent Loop](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/) - Understanding the standard agent loop
- [Model Context Protocol (MCP)](https://strandsagents.com/docs/user-guide/tools/mcp/) - Tool integration via MCP
- [Amazon Bedrock Integration](https://strandsagents.com/docs/user-guide/models/bedrock/) - Nova Sonic model configuration
- [strands-agents GitHub](https://github.com/strands-agents/sdk-python) - SDK repository

---

<a id='session-conversation-mgmt'></a>

## Session & Conversation Management

### Related Pages

Related topics: [Agent System](#agent-system)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/session/session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_manager.py)
- [strands-py/src/strands/session/file_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/file_session_manager.py)
- [strands-py/src/strands/session/s3_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/s3_session_manager.py)
- [strands-py/src/strands/session/repository_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/repository_session_manager.py)
- [strands-py/src/strands/session/session_repository.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_repository.py)
- [strands-py/src/strands/agent/conversation_manager/conversation_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/conversation_manager/conversation_manager.py)
- [strands-py/src/strands/agent/conversation_manager/sliding_window_conversation_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/conversation_manager/sliding_window_conversation_manager.py)
- [strands-py/src/strands/agent/conversation_manager/summarizing_conversation_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/conversation_manager/summarizing_conversation_manager.py)
- [strands-py/src/strands/types/_snapshot.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/types/_snapshot.py)
- [strands-py/src/strands/experimental/checkpoint/checkpoint.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/checkpoint/checkpoint.py)
- [designs/0008-proactive-context-compression.md](https://github.com/strands-agents/sdk-python/blob/main/designs/0008-proactive-context-compression.md)
</details>

# Session & Conversation Management

This page covers the session and conversation management system in Strands Agents SDK. It explains how agents maintain state across interactions, persist sessions to various backends, and manage conversation context to optimize for context window limits.

## Overview

Strands Agents provides a comprehensive session and conversation management system that enables:

- **State Persistence**: Save and restore agent state across application restarts
- **Context Management**: Automatically manage conversation history within model context limits
- **Multiple Storage Backends**: Store sessions in local files, S3, or custom repositories
- **Checkpointing**: Experimental support for capturing and restoring agent execution state

The system is designed to be flexible, allowing developers to choose the appropriate conversation manager and session storage strategy for their use case.

Source: [session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_manager.py)

## Architecture Overview

```mermaid
graph TB
    subgraph "Session Layer"
        SM[SessionManager]
        FS[FileSessionManager]
        S3S[S3SessionManager]
        RSM[RepositorySessionManager]
        SR[SessionRepository]
    end
    
    subgraph "Conversation Layer"
        CM[ConversationManager]
        SWCM[SlidingWindowConversationManager]
        SUCM[SummarizingConversationManager]
    end
    
    subgraph "Agent Layer"
        A[Agent]
        SNAP[Snapshot]
        CP[Checkpoint]
    end
    
    SM --> FS
    SM --> S3S
    SM --> RSM
    RSM --> SR
    
    A --> CM
    CM --> SWCM
    CM --> SUCM
    
    A --> SNAP
    A --> CP
```

## Session Management

The session management system handles persisting and retrieving agent state. Sessions can be stored in different backends depending on deployment requirements.

Source: [session_manager.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_manager.py)

### SessionManager

The `SessionManager` is the main entry point for session operations. It provides a unified interface regardless of the underlying storage backend.

**Key Responsibilities:**
- Load existing sessions by ID
- Save current session state
- Create new sessions
- Delete sessions
- Manage session metadata

Source: [session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_manager.py)

### Storage Backends

Strands supports multiple storage backends for session persistence:

| Backend | Use Case | Source |
|---------|----------|--------|
| `FileSessionManager` | Local development, simple deployments | [file_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/file_session_manager.py) |
| `S3SessionManager` | Production deployments, cloud-native | [s3_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/s3_session_manager.py) |
| `RepositorySessionManager` | Custom storage implementations | [repository_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/repository_session_manager.py) |

#### FileSessionManager

Stores sessions as JSON files in a specified directory. Suitable for single-instance deployments or local development.

```python
from strands.session import FileSessionManager

session_manager = FileSessionManager(
    storage_dir="/path/to/sessions"
)
```

Source: [file_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/file_session_manager.py)

#### S3SessionManager

Stores sessions in Amazon S3 buckets. Ideal for distributed deployments where multiple application instances need to access shared session state.

```python
from strands.session import S3SessionManager

session_manager = S3SessionManager(
    bucket_name="my-sessions-bucket",
    prefix="agents/"
)
```

Source: [s3_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/s3_session_manager.py)

#### RepositorySessionManager

A customizable backend that uses a `SessionRepository` interface. Allows integration with databases or custom storage systems.

```python
from strands.session import RepositorySessionManager, SessionRepository

class MyDatabaseRepository(SessionRepository):
    async def save(self, session_id: str, state: dict) -> None:
        # Custom save logic
        pass
    
    async def load(self, session_id: str) -> dict | None:
        # Custom load logic
        pass

session_manager = RepositorySessionManager(repository=MyDatabaseRepository())
```

Source: [repository_session_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/repository_session_manager.py)

### SessionRepository Interface

The `SessionRepository` defines the contract for session storage operations:

| Method | Description | Parameters |
|--------|-------------|------------|
| `save` | Persist session state | `session_id: str`, `state: dict` |
| `load` | Retrieve session state | `session_id: str` → `dict \| None` |
| `delete` | Remove session | `session_id: str` |
| `list_sessions` | List available sessions | `prefix: str \| None` → `list[str]` |

Source: [session_repository.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/session/session_repository.py)

## Conversation Management

Conversation managers control how conversation history is maintained and optimized. They handle context window management through various strategies.

Source: [conversation_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/conversation_manager/conversation_manager.py)

### Base ConversationManager

The base `ConversationManager` class provides the foundation for conversation management:

**Core Methods:**
| Method | Description |
|--------|-------------|
| `get_messages` | Returns current conversation messages |
| `add_message` | Adds a message to the conversation |
| `add_messages` | Adds multiple messages |
| `trim_messages` | Reduces message history |
| `get_state` | Returns current state for serialization |
| `restore_from_state` | Restores state from serialized data |

Source: [conversation_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/conversation_manager/conversation_manager.py)

### SlidingWindowConversationManager

The default conversation manager that maintains a sliding window of the most recent messages. When the context window approaches its limit, older messages are trimmed from the conversation.

**Features:**
- Configurable window size (number of messages to retain)
- Automatic trimming when context limits are approached
- Fallback trim point for tool-heavy conversations

Source: [sliding_window_conversation_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/conversation_manager/sliding_window_conversation_manager.py)

```python
from strands.agent.conversation_manager import SlidingWindowConversationManager

agent = Agent(
    conversation_manager=SlidingWindowConversationManager(
        max_messages=20,  # Keep last 20 messages
        trim_ratio=0.8    # Start trimming when 80% full
    )
)
```

**Configuration Options:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `max_messages` | `int` | `50` | Maximum number of messages to retain |
| `trim_ratio` | `float` | `0.8` | Ratio of max tokens at which trimming begins |
| `min_messages` | `int` | `2` | Minimum messages to retain after trimming |

Source: [sliding_window_conversation_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/conversation_manager/sliding_window_conversation_manager.py)

### SummarizingConversationManager

This manager periodically summarizes conversation history to preserve important context while reducing token usage. Useful for long-running agent interactions.

**Features:**
- Token-based trimming thresholds
- Automatic summarization of older messages
- Configurable summary triggers

Source: [summarizing_conversation_manager.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/agent/conversation_manager/summarizing_conversation_manager.py)

```python
from strands.agent.conversation_manager import SummarizingConversationManager

agent = Agent(
    conversation_manager=SummarizingConversationManager(
        max_tokens=100000,      # Start summarization at 100k tokens
        summary_trigger=0.9,   # Trigger at 90% of max
        model="anthropic-sonnet-4-20250514"
    )
)
```

## Proactive Context Compression

Introduced in v1.40.0, proactive context compression anticipates context window limits before they are reached. This feature uses a lookup table for context window limits and performs compression in advance rather than reactively.

Source: [designs/0008-proactive-context-compression.md](https://github.com/strands-agents/sdk-python/blob/main/designs/0008-proactive-context-compression.md)

**Key Benefits:**
- Prevents context overflow errors by acting before limits are reached
- Maintains conversation quality by preserving recent context
- Reduces latency spikes from sudden large trims

```mermaid
sequenceDiagram
    participant Agent
    participant CM as ConversationManager
    participant Model
    
    Agent->>CM: add_message(user_input)
    CM->>CM: check_context_usage()
    alt Context approaching limit
        CM->>CM: proactive_trim()
        Note over CM: Trim before limit reached
    end
    Agent->>Model: process_with_messages()
    Model-->>Agent: response
```

Source: [designs/0008-proactive-context-compression.md](https://github.com/strands-agents/sdk-python/blob/main/designs/0008-proactive-context-compression.md)

## Snapshots

Snapshots provide a way to capture and restore the complete state of an agent. They are used internally for session persistence and can be used programmatically for state management.

Source: [types/_snapshot.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/types/_snapshot.py)

### Snapshot Data Structure

| Field | Type | Description |
|-------|------|-------------|
| `scope` | `str` | Identifies the entity being snapshotted (e.g., "agent") |
| `schema_version` | `str` | Version identifier for serialization compatibility |
| `data` | `dict` | Captured state data |
| `app_data` | `dict` | Custom application-specific data |

**Capturable Fields:**
- `messages` — Full conversation history
- `state` — Agent state variables
- `conversation_manager_state` — Conversation manager state
- `interrupt_state` — Current interrupt state
- `system_prompt` — System prompt content blocks

Source: [types/_snapshot.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/types/_snapshot.py)

### Creating and Loading Snapshots

```python
# Create a snapshot
snapshot = agent.create_snapshot(
    fields=["messages", "state", "conversation_manager_state"]
)

# Save snapshot
import json
with open("session.json", "w") as f:
    json.dump(snapshot.to_dict(), f)

# Later, restore from snapshot
import json
with open("session.json", "r") as f:
    data = json.load(f)
    
snapshot = Snapshot.from_dict(data)
agent.load_snapshot(snapshot)
```

## Checkpoint (Experimental)

The checkpoint feature enables capturing and restoring agent execution state during a running conversation. This is particularly useful for:

- Long-running tasks that may need to be interrupted
- Recovery from failures
- State inspection during debugging

Source: [checkpoint/checkpoint.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/checkpoint/checkpoint.py)

```python
from strands.experimental.checkpoint import Checkpoint

# Create a checkpoint
checkpoint = Checkpoint.capture(agent)

# Store or transmit the checkpoint
checkpoint_data = checkpoint.serialize()

# Restore from checkpoint
restored_checkpoint = Checkpoint.deserialize(checkpoint_data)
restored_checkpoint.restore(agent)
```

**Note:** Checkpoint functionality is experimental and subject to change. It was introduced in v1.37.0.

Source: [checkpoint/checkpoint.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/experimental/checkpoint/checkpoint.py)

## Usage Patterns

### Basic Session Usage

```python
from strands import Agent
from strands.session import FileSessionManager

# Create session manager
session_manager = FileSessionManager(storage_dir="./sessions")

# Create agent with session management
agent = Agent(
    tools=[calculator],
    session_manager=session_manager,
    session_id="user-123-session-001"
)

# Agent state is automatically persisted
agent("Hello, analyze this data")
# ... later ...
agent("Continue with the analysis")  # Picks up where left off
```

### Multi-Agent Sessions

```python
from strands import Agent
from strands.session import S3SessionManager

session_manager = S3SessionManager(
    bucket_name="my-app-sessions",
    prefix="multi-agent/"
)

# Shared session across agents
orchestrator = Agent(
    system_prompt="You coordinate a team of specialized agents.",
    session_manager=session_manager,
    session_id="project-alpha"
)

researcher = Agent(
    system_prompt="You research topics thoroughly.",
    session_manager=session_manager,
    session_id="project-alpha"  # Same session
)
```

### Custom Conversation Manager

```python
from strands.agent.conversation_manager import SlidingWindowConversationManager

# Optimize for brief interactions
short_window = SlidingWindowConversationManager(
    max_messages=10,
    trim_ratio=0.7  # Start trimming earlier
)

# Optimize for detailed research
long_window = SlidingWindowConversationManager(
    max_messages=100,
    trim_ratio=0.9  # Allow more context
)

agent = Agent(
    tools=[research_tool, analysis_tool],
    conversation_manager=long_window
)
```

## Common Patterns and Best Practices

### Session ID Strategy

| Strategy | Use Case | Example |
|----------|----------|---------|
| User-based | Per-user conversations | `f"user-{user_id}"` |
| Conversation-based | One conversation per session | `f"conv-{uuid4()}"` |
| Hybrid | User + topic | `f"user-{user_id}:topic-{topic}"` |

### Conversation Manager Selection

| Manager | Best For | Limitations |
|---------|----------|-------------|
| `SlidingWindow` | Most use cases | May lose context for very long conversations |
| `Summarizing` | Long research tasks | Summarization adds latency and cost |

### Error Handling

```python
from strands.exceptions import SessionNotFoundError

try:
    agent = Agent(
        session_manager=session_manager,
        session_id="nonexistent-session"
    )
except SessionNotFoundError:
    # Create new session
    agent = Agent(session_manager=session_manager)
```

## Related Components

### Agent State Management

The agent integrates with session and conversation management through its `state` property:

```python
# Custom state
agent.state["task_progress"] = 0
agent.state["visited_urls"] = []

# Access in system prompt
agent = Agent(
    system_prompt="You have completed {state.task_progress} steps.",
    state=agent.state
)
```

### Telemetry Integration

Session operations are automatically traced when telemetry is enabled:

```python
from strands.telemetry import StrandsTelemetry

StrandsTelemetry.configure(
    service_name="my-agent-service",
    exporter="console"  # or "otlp", "langfuse", etc.
)
```

## See Also

- [Agent Architecture](../agents/agent-architecture) — Overview of the Agent class and its components
- [Tools & MCP Integration](../tools/mcp-integration) — How tools are integrated with conversation management
- [Telemetry & Observability](../telemetry/observability) — Monitoring session operations
- [Context Window Management](../advanced/context-window-management) — Advanced context optimization techniques

---

<a id='mcp-integration'></a>

## MCP Integration

### Related Pages

Related topics: [Tool System](#tool-system), [Agent System](#agent-system)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/tools/mcp/mcp_client.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)
- [strands-py/src/strands/tools/mcp/mcp_agent_tool.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_agent_tool.py)
- [strands-py/src/strands/tools/mcp/mcp_types.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_types.py)
- [strands-py/src/strands/tools/mcp/mcp_tasks.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)
- [strands-py/src/strands/tools/mcp/mcp_instrumentation.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_instrumentation.py)
- [strands-py/src/strands/tools/mcp/mcp_resources.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_resources.py)
</details>

# MCP Integration

## Overview

The Model Context Protocol (MCP) Integration in Strands Agents provides native support for connecting to MCP servers, enabling agents to use tools, resources, and prompts exposed by external MCP-compliant servers. This integration allows seamless access to thousands of pre-built tools through the standardized MCP interface.

The MCP client implementation handles:
- **Transport management** - Establishing and maintaining connections via stdio or HTTP/SSE
- **Tool discovery** - Listing and filtering available tools from MCP servers
- **Tool execution** - Both direct execution and task-augmented execution for long-running operations
- **Resource access** - Reading resources and resource templates from servers
- **Prompt retrieval** - Fetching and using server-defined prompts

Source: [strands-py/src/strands/tools/mcp/mcp_client.py:1-50](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

## Architecture

### Component Overview

The MCP integration consists of several interconnected components:

```mermaid
graph TD
    A[Agent] --> B[MCPClient]
    B --> C[ClientSession]
    C --> D[MCP Server]
    
    B --> E[Tool Provider]
    E --> F[MCPAgentTool]
    
    B --> G[Task Executor]
    G --> H[call_tool_as_task]
    G --> I[call_tool]
    
    C --> J[Resources]
    C --> K[Prompts]
    
    L[Background Thread] -.-> C
    M[Event Loop] -.-> L
```

### Threading Model

The MCP client uses a background thread with its own event loop to manage the MCP connection. This design ensures that the main thread remains responsive while maintaining a stable MCP session.

```mermaid
sequenceDiagram
    participant Main as Main Thread
    participant Background as Background Thread
    participant MCP as MCP Server
    
    Main->>Background: start()
    Background->>MCP: Initialize connection
    MCP-->>Background: Session initialized
    Background-->>Main: Ready signal
    
    loop Tool Calls
        Main->>Background: call_tool_async()
        Background->>MCP: Execute tool
        MCP-->>Background: Tool result
        Background-->>Main: Return result
    end
    
    Main->>Background: close()
    Background->>MCP: Close session
    Background-->>Main: Closed
```

Source: [strands-py/src/strands/tools/mcp/mcp_client.py:180-220](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

## MCPClient Class

### Initialization

The `MCPClient` class is the main entry point for MCP integration:

```python
from strands.tools.mcp import MCPClient
from mcp import stdio_client, StdioServerParameters

client = MCPClient(
    transport_callable=lambda: stdio_client(
        StdioServerParameters(command="uvx", args=["some-mcp-server@latest"])
    ),
    tool_filters={"rejected": ["internal.*"]},  # Optional filtering
    prefix="mcp_",  # Optional tool name prefix
    startup_timeout=timedelta(seconds=30),  # Connection timeout
)
```

### Configuration Options

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `transport_callable` | `Callable` | Required | Factory function that returns transport streams |
| `tool_filters` | `ToolFilters` | `None` | Filter which tools are loaded |
| `prefix` | `str` | `""` | Prefix added to all tool names |
| `startup_timeout` | `timedelta` | 30 seconds | Timeout for initial connection |
| `elicitation_callback` | `Callable` | `None` | Callback for handling elicitation requests |
| `tasks_config` | `TasksConfig` | `None` | Task-augmented execution configuration |

Source: [strands-py/src/strands/tools/mcp/mcp_client.py:100-150](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

## Tool Filtering

The MCP client supports filtering which tools are loaded from a server. This is useful when you want to exclude internal tools or limit the tool surface area.

### Filter Configuration

```python
from strands.tools.mcp import MCPClient, ToolFilters

# Only allow specific tools
filters: ToolFilters = {
    "allowed": ["calculator", "search", "^fetch_.*"],
    "rejected": ["debug.*", "admin.*"]
}

client = MCPClient(
    transport_callable=transport,
    tool_filters=filters
)
```

### Filter Matching Rules

Tools are filtered in this order:
1. If `allowed` is specified, **only** tools matching these patterns are included
2. Tools matching `rejected` patterns are then excluded

Pattern types supported:
- **String exact match**: `"calculator"`
- **Regex pattern**: `"^fetch_.*"` (compiled automatically)
- **Callback function**: Custom filtering logic

```python
def custom_filter(tool: AgentTool, **kwargs) -> bool:
    return "production" in tool.name

filters: ToolFilters = {"allowed": [custom_filter]}
```

Source: [strands-py/src/strands/tools/mcp/mcp_client.py:60-80](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

## Task-Augmented Execution

The MCP specification defines task-augmented execution for long-running tools. This feature allows the SDK to:

1. Create a task for a tool execution
2. Poll for completion status
3. Retrieve the final result

This pattern is particularly useful for tools that take minutes to complete, as it provides better feedback mechanisms.

Source: [strands-py/src/strands/tools/mcp/mcp_tasks.py:1-30](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)

### Task Configuration

```python
from datetime import timedelta
from strands.tools.mcp import MCPClient, TasksConfig

tasks_config: TasksConfig = {
    "ttl": timedelta(minutes=2),        # Task time-to-live
    "poll_timeout": timedelta(minutes=10)  # Polling timeout
}

client = MCPClient(
    transport_callable=transport,
    tasks_config=tasks_config
)
```

### Default Configuration

| Parameter | Default | Description |
|-----------|---------|-------------|
| `ttl` | 1 minute | Task time-to-live before expiration |
| `poll_timeout` | 5 minutes | Maximum time to poll for task completion |

Source: [strands-py/src/strands/tools/mcp/mcp_tasks.py:25-35](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_tasks.py)

### Execution Flow

```mermaid
graph TD
    A[Tool Call Request] --> B{Server supports tasks?}
    B -->|Yes| C{Tool supports tasks?}
    B -->|No| G[Direct call_tool]
    C -->|TASK_REQUIRED| D[Use call_tool_as_task]
    C -->|TASK_OPTIONAL| D
    C -->|forbidden| G
    
    D --> E[Create task]
    E --> F[Poll until complete]
    F --> H[Return result]
    G --> I[Execute directly]
    I --> H
```

The SDK automatically determines whether to use task-augmented execution based on:
1. Server capability: `tasks.requests.tools.call`
2. Tool-level setting: `taskSupport` in tool specification

Source: [strands-py/src/strands/tools/mcp/mcp_client.py:300-380](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

## Resources and Prompts

### Listing Resources

```python
with client:
    # List available resources
    resources = client.list_resources_sync()
    print(f"Found {len(resources.resources)} resources")
    
    # List resource templates
    templates = client.list_resource_templates_sync()
    print(f"Found {len(templates.resourceTemplates)} templates")
```

### Reading Resources

```python
from mcp.types import AnyUrl

# Read a specific resource
resource_uri = AnyUrl("file:///path/to/resource")
content = client.read_resource_sync(resource_uri)
```

Source: [strands-py/src/strands/tools/mcp/mcp_resources.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_resources.py)

### Using Prompts

```python
# List available prompts
prompts = client.list_prompts_sync()

# Get a specific prompt
prompt_result = client.get_prompt_sync(
    prompt_id="my-prompt",
    args={"variable": "value"}
)
```

## Usage Examples

### Basic MCP Server Connection

```python
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp import stdio_client, StdioServerParameters

# Create MCP client
mcp_client = MCPClient(
    transport_callable=lambda: stdio_client(
        StdioServerParameters(
            command="uvx",
            args=["awslabs.aws-documentation-mcp-server@latest"]
        )
    )
)

# Use with agent
with mcp_client:
    agent = Agent(tools=mcp_client.list_tools_sync())
    response = agent("Tell me about Amazon Bedrock")
```

### Filtering Tools

```python
from strands import Agent
from strands.tools.mcp import MCPClient

mcp_client = MCPClient(
    transport_callable=lambda: stdio_client(
        StdioServerParameters(command="uvx", args=["some-server@latest"])
    ),
    tool_filters={
        "rejected": ["internal.*", ".*debug.*"]
    }
)

with mcp_client:
    tools = mcp_client.list_tools_sync()
    print(f"Loaded {len(tools)} tools (filtered)")
```

### With Task-Augmented Execution

```python
from datetime import timedelta
from strands.tools.mcp import MCPClient, TasksConfig

mcp_client = MCPClient(
    transport_callable=transport,
    tasks_config=TasksConfig(
        ttl=timedelta(minutes=5),
        poll_timeout=timedelta(minutes=30)
    )
)
```

## Error Handling

### Common Exceptions

| Exception | Description |
|-----------|-------------|
| `MCPClientInitializationError` | Client failed to initialize (connection timeout, invalid server) |
| `ToolProviderException` | Error during tool execution |
| `SessionNotActiveError` | Attempted operation on closed/inactive session |

### Session State Management

The client maintains a session state that can be checked:

```python
if client._is_session_active():
    tools = client.list_tools_sync()
else:
    print("Client session is not active")
```

Always use the context manager (`with client:`) or explicitly call `start()` and `close()` to manage session lifecycle properly.

Source: [strands-py/src/strands/tools/mcp/mcp_client.py:400-450](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

## Instrumenting MCP Operations

The MCP integration includes OpenTelemetry instrumentation for observability:

```python
from strands.tools.mcp.mcp_instrumentation import mcp_instrumentation

# Instrumentation is automatically enabled when MCPClient is used
# It can be explicitly triggered:
mcp_instrumentation()
```

This provides:
- Trace propagation for tool calls
- Span attributes for MCP operations
- Timing metrics for performance monitoring

Source: [strands-py/src/strands/tools/mcp/mcp_instrumentation.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_instrumentation.py)

## Known Limitations

### Progress Updates for Tasks

As noted in [GitHub Issue #1812](https://github.com/strands-agents/sdk-python/issues/1812), the current implementation does not support progress updates for long-running tasks. The MCP specification defines progress reporting mechanisms (as implemented in FastMCP), but Strands Agents currently lacks this feature.

> **Community Note**: Progress updates are essential for tasks that take minutes to complete, as they provide feedback to users during execution. This is a requested enhancement.

### MetricsClient Thread Safety

As reported in [GitHub Issue #2342](https://github.com/strands-agents/sdk-python/issues/2342), the MetricsClient singleton is not thread-safe. When using multiple MCP clients concurrently, this may cause metrics collection issues.

### Error Flag Preservation

As of v1.38.0, the `CallToolResult.isError` flag is properly preserved in `MCPToolResult`, ensuring error states are correctly propagated from MCP servers.

Source: [strands-py/src/strands/tools/mcp/mcp_client.py:250-280](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py)

## Best Practices

1. **Always use context manager**: Use `with client:` to ensure proper cleanup
2. **Filter tools**: Limit tool exposure with `tool_filters` to reduce attack surface
3. **Set appropriate timeouts**: Configure `startup_timeout` and `tasks_config` based on your server's characteristics
4. **Check session state**: Verify `_is_session_active()` before operations if using manual lifecycle management
5. **Handle errors gracefully**: Catch `MCPClientInitializationError` for connection failures

## See Also

- [Agent Loop Documentation](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/)
- [Tool Executor Strategies](https://github.com/strands-agents/sdk-python/wiki/Tool-Executors)
- [Structured Output Tools](https://github.com/strands-agents/sdk-python/wiki/Structured-Output)
- [Official MCP Specification](https://modelcontextprotocol.io/specification/2025-11-25)
- [GitHub Issue #1812](https://github.com/strands-agents/sdk-python/issues/1812) - Progress Updates Feature Request
- [GitHub Issue #2286](https://github.com/strands-agents/sdk-python/issues/2286) - Monorepo Consolidation (includes MCP Server)

---

<a id='plugins-system'></a>

## Plugin System

### Related Pages

Related topics: [Multi-Agent Patterns](#multi-agent-patterns), [Tool System](#tool-system)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/plugins/plugin.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/plugin.py)
- [strands-py/src/strands/plugins/decorator.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/decorator.py)
- [strands-py/src/strands/plugins/registry.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/registry.py)
- [strands-py/src/strands/plugins/_discovery.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/_discovery.py)
- [strands-py/src/strands/plugins/multiagent_plugin.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/multiagent_plugin.py)
- [strands-py/src/strands/plugins/multiagent_registry.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/multiagent_registry.py)
- [strands-py/src/strands/vended_plugins/skills/agent_skills.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/skills/agent_skills.py)
- [strands-py/src/strands/vended_plugins/skills/skill.py](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/skills/skill.py)
- [designs/0001-plugins.md](https://github.com/strands-agents/sdk-python/blob/main/designs/0001-plugins.md)
</details>

# Plugin System

The Strands Agents Plugin System provides a modular, extensible architecture for adding capabilities to agents. Plugins serve as self-contained packages that can inject tools, modify system prompts, register callbacks, and orchestrate multi-agent workflows. This design allows developers to compose sophisticated agent behaviors from reusable, independently-versioned components.

## Overview

Plugins in Strands are designed around three core principles:

1. **Composition over inheritance**: Plugins are composed into an agent rather than subclassing it
2. **Declarative configuration**: Plugin behavior is defined through structured configuration
3. **Lifecycle hooks**: Plugins participate in the agent's execution lifecycle at defined points

The plugin system supports multiple plugin types including:

- **Agent Plugins**: Inject tools, modify prompts, and register callbacks
- **Multi-Agent Plugins**: Coordinate multiple agents using Swarm or Graph orchestrators
- **Skills Plugins**: Enable progressive disclosure of instructions via AgentSkills.io integration

Source: [designs/0001-plugins.md:1-50](https://github.com/strands-agents/sdk-python/blob/main/designs/0001-plugins.md)

## Architecture

The plugin system comprises several interconnected components that work together to provide a cohesive extension mechanism.

```mermaid
graph TD
    A[Agent] --> B[PluginRegistry]
    B --> C[Plugin Decorator]
    B --> D[Plugin Discovery]
    
    C --> E[BasePlugin]
    E --> F[AgentSkills]
    E --> G[MultiAgentPlugin]
    E --> H[ContextOffloader]
    E --> I[Custom Plugins]
    
    D --> J[tools/ directory]
    D --> K[Python decorators]
    
    L[Agent Loop] --> M[Pre-Cycle Hooks]
    L --> N[Post-Cycle Hooks]
    L --> O[Tool Callbacks]
    
    style E fill:#f9f,stroke:#333,stroke-width:2px
    style A fill:#bbf,stroke:#333,stroke-width:2px
```

### Component Overview

| Component | File | Purpose |
|-----------|------|---------|
| BasePlugin | `plugin.py` | Abstract base class defining plugin interface |
| PluginRegistry | `registry.py` | Central registry managing plugin lifecycle |
| Plugin Decorator | `decorator.py` | Decorator for creating tool-based plugins |
| Plugin Discovery | `_discovery.py` | Auto-discovery of plugins from filesystem |
| MultiAgentPlugin | `multiagent_plugin.py` | Multi-agent orchestration support |
| AgentSkills | `agent_skills.py` | AgentSkills.io integration |

Source: [strands-py/src/strands/plugins/plugin.py:1-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/plugin.py)

## Base Plugin Interface

All plugins inherit from the `BasePlugin` abstract base class, which defines the required interface for plugin implementation.

### Plugin Structure

```python
from strands.plugins import BasePlugin

class MyPlugin(BasePlugin):
    def __init__(self, config: dict | None = None):
        super().__init__(config)
    
    @property
    def name(self) -> str:
        return "my_plugin"
    
    def get_tools(self) -> list[FunctionInfo]:
        """Return list of tools provided by this plugin."""
        return []
    
    def get_system_prompt(self) -> str | None:
        """Return system prompt modifications."""
        return None
    
    def register_callbacks(self, callbacks: dict) -> None:
        """Register lifecycle callbacks."""
        pass
```

### Required Interface Methods

| Method | Return Type | Description |
|--------|-------------|-------------|
| `name` | `str` | Unique plugin identifier |
| `get_tools()` | `list[FunctionInfo]` | Tools provided by the plugin |
| `get_system_prompt()` | `str \| None` | System prompt modifications |
| `register_callbacks()` | `void` | Register lifecycle callbacks |

Source: [strands-py/src/strands/plugins/plugin.py:50-150](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/plugin.py)

## Plugin Registry

The `PluginRegistry` serves as the central authority for managing plugins within an agent. It handles plugin registration, tool aggregation, and lifecycle coordination.

### Registration Flow

```mermaid
sequenceDiagram
    participant Agent
    participant Registry
    participant Plugin1
    participant Plugin2
    
    Agent->>Registry: register_plugin(plugin)
    Registry->>Plugin1: validate()
    Plugin1-->>Registry: validated
    Registry->>Registry: add to _plugins dict
    Registry->>Plugin1: get_tools()
    Plugin1-->>Registry: [tool1, tool2]
    Registry->>Registry: merge into _tools
    
    Note over Registry: Repeat for all plugins
    
    Agent->>Registry: get_all_tools()
    Registry-->>Agent: combined tools list
```

### Registry Operations

| Operation | Method | Description |
|-----------|--------|-------------|
| Register | `register_plugin(plugin)` | Add plugin to registry |
| Get Tools | `get_all_tools()` | Aggregate all plugin tools |
| Get Prompts | `get_combined_system_prompt()` | Merge all system prompts |
| List Plugins | `list_plugins()` | Get registered plugin names |
| Get Plugin | `get_plugin(name)` | Retrieve plugin by name |

Source: [strands-py/src/strands/plugins/registry.py:1-80](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/registry.py)

## AgentSkills Plugin

The AgentSkills plugin integrates with AgentSkills.io to enable progressive disclosure of skill instructions. This follows a pattern where metadata is injected into the system prompt upfront, and full instructions are loaded on demand via a tool.

### Skill Loading Patterns

```python
from strands import Agent
from strands.vended_plugins.skills import AgentSkills, Skill

# Load from filesystem via classmethods
skill = Skill.from_file("./skills/pdf-processing")
skills = Skill.from_directory("./skills/")

# Let the plugin resolve paths automatically
plugin = AgentSkills(skills=["./skills/pdf-processing"])
agent = Agent(plugins=[plugin])
```

### Skill Structure

Each skill consists of:

1. **Metadata** (`metadata.json`): Contains skill name, description, and tags for upfront injection
2. **Instructions** (`instructions.md`): Full skill instructions loaded on demand

### How It Works

```mermaid
graph LR
    A[Skill Directory] -->|Load metadata| B[System Prompt]
    A -->|Load on demand| C[Tool Call]
    
    B --> D[Agent knows skill exists]
    C --> E[Full instructions retrieved]
    
    D --> F[Context Window Efficiency]
    E --> G[Detailed Task Execution]
```

The AgentSkills plugin creates a tool for each skill that, when called, loads and returns the full skill instructions. This allows the agent to request detailed instructions only when needed, preserving context window space.

Source: [strands-py/src/strands/vended_plugins/skills/agent_skills.py:1-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/skills/agent_skills.py)

Source: [strands-py/src/strands/vended_plugins/skills/skill.py:1-60](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/skills/skill.py)

## Multi-Agent Plugin

The MultiAgentPlugin (introduced in v1.41.0) enables coordination of multiple agents using Swarm or Graph orchestrators. This allows complex workflows where different specialized agents collaborate on tasks.

### Orchestration Patterns

| Pattern | Description | Use Case |
|---------|-------------|----------|
| **Swarm** | Dynamic agent handoffs | Complex multi-turn conversations |
| **Graph** | Structured workflow | Sequential or parallel task execution |

### Basic Usage

```python
from strands import Agent
from strands.plugins import MultiAgentPlugin

# Create specialized agents
researcher = Agent(system_prompt="You are a research assistant...")
writer = Agent(system_prompt="You are a technical writer...")

# Create multi-agent plugin with graph orchestrator
multi_agent = MultiAgentPlugin(
    agents={
        "researcher": researcher,
        "writer": writer,
    },
    orchestrator="graph",  # or "swarm"
    entry_point="researcher",
)

agent = Agent(plugins=[multi_agent])
```

### Agent Handoff Flow (Swarm)

```mermaid
graph TD
    A[User Request] --> B[Entry Agent]
    B --> C{Requires Specialist?}
    C -->|Yes| D[Handoff to Specialist]
    C -->|No| E[Handle Request]
    D --> F[Specialist Agent]
    F --> G{Requires Another Agent?}
    G -->|Yes| H[Handoff Back]
    G -->|No| I[Return Result]
    H --> B
```

Source: [strands-py/src/strands/plugins/multiagent_plugin.py:1-100](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/multiagent_plugin.py)

## Plugin Discovery

The plugin discovery mechanism enables automatic loading of plugins from the filesystem, supporting hot-reloading during development.

### Discovery Locations

| Location | Pattern | Description |
|----------|---------|-------------|
| `./tools/` directory | Python files with `@tool` decorator | Auto-discovered tools |
| Plugin packages | Packages inheriting from `BasePlugin` | Explicitly loaded |

### Hot Reloading Configuration

```python
from strands import Agent

# Enable automatic tool loading and reloading from ./tools/
agent = Agent(load_tools_from_directory=True)
response = agent("Use any tools you find in the tools directory")
```

The discovery system watches the specified directory for changes and reloads plugins dynamically without restarting the agent.

Source: [strands-py/src/strands/plugins/_discovery.py:1-60](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/_discovery.py)

## Tool-Based Plugins

The `@tool` decorator provides a simple way to create plugins from Python functions. This is the recommended approach for most use cases.

### Basic Tool Creation

```python
from strands import Agent, tool

@tool
def word_count(text: str) -> int:
    """Count words in text.
    
    This docstring is used by the LLM to understand the tool's purpose.
    """
    return len(text.split())

agent = Agent(tools=[word_count])
response = agent("How many words are in this sentence?")
```

### Tool Specification Extraction

The decorator automatically extracts metadata from the function:

1. **Name**: Function name (or custom override)
2. **Description**: Function's docstring (excluding Args section)
3. **Input Schema**: JSON schema derived from function signature

```mermaid
graph LR
    A[Python Function] -->|Inspect| B[Docstring Parser]
    A -->|Inspect| C[Signature Analyzer]
    B --> D[ToolSpec JSON]
    C --> D
    D --> E[LLM Tool Call]
```

### Decorator Source

The decorator processes the function to create a standardized `ToolSpec`:

```python
def extract_metadata(self) -> ToolSpec:
    """Extract metadata from the function to create a tool specification."""
    func_name = self.func.__name__
    description = self._extract_description_from_docstring()
    input_schema = self.input_model.model_json_schema()
    return {
        "name": func_name,
        "description": description,
        "inputSchema": {"json": input_schema}
    }
```

Source: [strands-py/src/strands/plugins/decorator.py:1-80](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/plugins/decorator.py)

## Lifecycle Hooks

Plugins can register callbacks to participate in the agent's execution lifecycle. Hooks are called at specific points during the agent loop.

### Available Hooks

| Hook | Timing | Purpose |
|------|--------|---------|
| `on_pre_tool_call` | Before each tool call | Modify arguments, log, validate |
| `on_post_tool_call` | After each tool call | Process results, handle errors |
| `on_pre_model_call` | Before model inference | Modify prompts, add context |
| `on_post_model_call` | After model inference | Process responses, extract data |
| `on_cycle_start` | Start of each agent cycle | Initialize cycle state |
| `on_cycle_end` | End of each agent cycle | Cleanup, metrics collection |

### Hook Registration

```python
from strands import Agent

def my_pre_tool_callback(tool_name: str, arguments: dict) -> dict:
    """Modify tool arguments before execution."""
    # Add logging, validation, or argument transformation
    return arguments

agent = Agent(
    callbacks={
        "on_pre_tool_call": my_pre_tool_callback,
    }
)
```

Source: [strands-py/src/strands/vended_plugins/steering/core/handler.py:1-80](https://github.com/strands-agents/sdk-python/blob/main/strands-py/src/strands/vended_plugins/steering/core/handler.py)

## Configuration

### Agent Plugin Configuration

```python
from strands import Agent

agent = Agent(
    plugins=[
        # Pass configuration dict to plugin
        MyPlugin(config={
            "option1": "value1",
            "option2": True,
        }),
    ],
    # Callback hooks
    callbacks={
        "on_pre_tool_call": my_callback,
    },
)
```

### Plugin Configuration Schema

| Parameter | Type | Description |
|-----------|------|-------------|
| `plugins` | `list[BasePlugin]` | List of plugin instances |
| `callbacks` | `dict[str, Callable]` | Lifecycle hook callbacks |
| `load_tools_from_directory` | `str \| bool` | Enable tool discovery |

## Common Patterns

### Pattern 1: Simple Tool Plugin

```python
from strands.plugins import tool

@tool
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression.
    
    Args:
        expression: A mathematical expression to evaluate.
    """
    return str(eval(expression))
```

### Pattern 2: Stateful Plugin

```python
from strands.plugins import BasePlugin

class DataStorePlugin(BasePlugin):
    def __init__(self, config: dict | None = None):
        super().__init__(config)
        self._store = {}
    
    @property
    def name(self) -> str:
        return "data_store"
    
    def get_tools(self) -> list:
        return [self._save, self._load]
    
    @tool
    def _save(self, key: str, value: str) -> str:
        """Save a key-value pair."""
        self._store[key] = value
        return f"Saved: {key}"
    
    @tool
    def _load(self, key: str) -> str:
        """Load a value by key."""
        return self._store.get(key, "Not found")
```

### Pattern 3: Multi-Agent Workflow

```python
from strands.plugins import MultiAgentPlugin

triage = Agent(system_prompt="Route requests to specialists...")
research = Agent(system_prompt="Research topics thoroughly...")
respond = Agent(system_prompt="Generate user-friendly responses...")

workflow = MultiAgentPlugin(
    agents={
        "triage": triage,
        "research": research,
        "respond": respond,
    },
    orchestrator="graph",
    entry_point="triage",
)

agent = Agent(plugins=[workflow])
```

## Troubleshooting

### Plugin Not Loading

1. **Check import paths**: Ensure the plugin module is importable
2. **Verify plugin inheritance**: Plugin must inherit from `BasePlugin`
3. **Check configuration**: Ensure config dict is properly structured

### Tools Not Appearing

1. **Verify `get_tools()` returns**: Must return `list[FunctionInfo]`
2. **Check decorator usage**: Ensure `@tool` decorator is applied
3. **Review tool schema**: Validate input schema is correct JSON

### Callback Not Triggering

1. **Check hook name**: Ensure callback key matches available hooks
2. **Verify signature**: Callbacks must accept expected parameters
3. **Check return value**: Some hooks expect modified arguments to be returned

## See Also

- [Agent Loop Documentation](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/)
- [Tools System](./tools-system.md)
- [MCP Integration](./mcp-integration.md)
- [Telemetry System](./telemetry.md)
- [Multi-Agent Architectures](https://strandsagents.com/docs/user-guide/concepts/agents/multi-agent/)
- [AgentSkills.io Integration](https://agentskills.io/)

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

# Pitfall Log

Project: strands-agents/sdk-python

Summary: 发现 8 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：身份坑 - 仓库名和安装名不一致。

## 1. 身份坑 · 仓库名和安装名不一致

- 严重度：medium
- 证据强度：runtime_trace
- 发现：仓库名 `sdk-python` 与安装入口 `strands-agents` 不完全一致。
- 对用户的影响：用户照着仓库名搜索包或照着包名找仓库时容易走错入口。
- 建议检查：在 npm/PyPI/GitHub 上确认包名映射和官方 README 说明。
- 复现命令：`pip install strands-agents`
- 防护动作：页面必须同时展示 repo 名和真实安装入口，避免用户搜索错包。
- 证据：identity.distribution | github_repo:983715534 | https://github.com/strands-agents/sdk-python | repo=sdk-python; install=strands-agents

## 2. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 建议检查：将假设转成下游验证清单。
- 防护动作：假设必须转成验证项；没有验证结果前不能写成事实。
- 证据：capability.assumptions | github_repo:983715534 | https://github.com/strands-agents/sdk-python | README/documentation is current enough for a first validation pass.

## 3. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 建议检查：补 GitHub 最近 commit、release、issue/PR 响应信号。
- 防护动作：维护活跃度未知时，推荐强度不能标为高信任。
- 证据：evidence.maintainer_signals | github_repo:983715534 | https://github.com/strands-agents/sdk-python | last_activity_observed missing

## 4. 安全/权限坑 · 下游验证发现风险项

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：下游已经要求复核，不能在页面中弱化。
- 建议检查：进入安全/权限治理复核队列。
- 防护动作：下游风险存在时必须保持 review/recommendation 降级。
- 证据：downstream_validation.risk_items | github_repo:983715534 | https://github.com/strands-agents/sdk-python | no_demo; severity=medium

## 5. 安全/权限坑 · 存在安全注意事项

- 严重度：medium
- 证据强度：source_linked
- 发现：No sandbox install has been executed yet; downstream must verify before user use.
- 对用户的影响：用户安装前需要知道权限边界和敏感操作。
- 建议检查：转成明确权限清单和安全审查提示。
- 防护动作：安全注意事项必须面向用户前置展示。
- 证据：risks.safety_notes | github_repo:983715534 | https://github.com/strands-agents/sdk-python | No sandbox install has been executed yet; downstream must verify before user use.

## 6. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 建议检查：把风险写入边界卡，并确认是否需要人工复核。
- 防护动作：评分风险必须进入边界卡，不能只作为内部分数。
- 证据：risks.scoring_risks | github_repo:983715534 | https://github.com/strands-agents/sdk-python | no_demo; severity=medium

## 7. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 建议检查：抽样最近 issue/PR，判断是否长期无人处理。
- 防护动作：issue/PR 响应未知时，必须提示维护风险。
- 证据：evidence.maintainer_signals | github_repo:983715534 | https://github.com/strands-agents/sdk-python | issue_or_pr_quality=unknown

## 8. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 建议检查：确认最近 release/tag 和 README 安装命令是否一致。
- 防护动作：发布节奏未知或过期时，安装说明必须标注可能漂移。
- 证据：evidence.maintainer_signals | github_repo:983715534 | https://github.com/strands-agents/sdk-python | release_recency=unknown

<!-- canonical_name: strands-agents/sdk-python; human_manual_source: deepwiki_human_wiki -->
