Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ strands-agents/
│ │ │ ├── _executor.py # Base executor
│ │ │ ├── concurrent.py # Thread/process pool
│ │ │ └── sequential.py # Sequential execution
│ │ ├── mcp/ # Model Context Protocol
│ │ │ ├── mcp_client.py # MCP client implementation
│ │ │ ├── mcp_agent_tool.py # MCP tool wrapper
│ │ │ ├── mcp_types.py # MCP type definitions
│ │ │ ├── mcp_tasks.py # Task-augmented execution config
│ │ │ └── mcp_instrumentation.py # MCP telemetry
│ │ └── structured_output/ # Structured output handling
│ │ ├── structured_output_tool.py
│ │ ├── structured_output_utils.py
Expand Down Expand Up @@ -110,6 +104,13 @@ strands-agents/
│ │ ├── a2a.py # A2A protocol types
│ │ └── models/ # Model-specific types
│ │
│ ├── mcp/ # Model Context Protocol
│ │ ├── mcp_client.py # MCP client implementation
│ │ ├── mcp_agent_tool.py # MCP tool wrapper
│ │ ├── mcp_types.py # MCP type definitions
│ │ ├── mcp_tasks.py # Task-augmented execution config
│ │ └── mcp_instrumentation.py # MCP telemetry
│ │
│ ├── session/ # Session management
│ │ ├── session_manager.py # Base interface
│ │ ├── file_session_manager.py # File-based storage
Expand Down Expand Up @@ -443,7 +444,7 @@ Enable tasks by passing a `TasksConfig` to `MCPClient`:

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

# Enable with defaults (ttl=1min, poll_timeout=5min)
client = MCPClient(transport, tasks_config={})
Expand Down Expand Up @@ -474,9 +475,9 @@ Task-augmented execution is used when ALL conditions are met:

### Key Files

- `src/strands/tools/mcp/mcp_tasks.py` - `TasksConfig` and defaults
- `src/strands/tools/mcp/mcp_client.py` - Task execution logic (`_call_tool_as_task_and_poll_async`)
- `tests/strands/tools/mcp/test_mcp_client_tasks.py` - Unit tests
- `src/strands/mcp/mcp_tasks.py` - `TasksConfig` and defaults
- `src/strands/mcp/mcp_client.py` - Task execution logic (`_call_tool_as_task_and_poll_async`)
- `tests/strands/mcp/test_mcp_client_tasks.py` - Unit tests
- `tests_integ/mcp/test_mcp_client_tasks.py` - Integration tests
- `tests_integ/mcp/task_echo_server.py` - Test server with task support

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Seamlessly integrate Model Context Protocol (MCP) servers:

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

aws_docs_client = MCPClient(
Expand Down
14 changes: 14 additions & 0 deletions src/strands/mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Model Context Protocol (MCP) integration.

This package provides integration with the Model Context Protocol (MCP), allowing agents to use tools provided by MCP
servers.

- Docs: https://www.anthropic.com/news/model-context-protocol
"""

from .mcp_agent_tool import MCPAgentTool
from .mcp_client import MCPClient, ToolFilters
from .mcp_tasks import TasksConfig
from .mcp_types import MCPTransport

__all__ = ["MCPAgentTool", "MCPClient", "MCPTransport", "TasksConfig", "ToolFilters"]
119 changes: 119 additions & 0 deletions src/strands/mcp/mcp_agent_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""MCP Agent Tool module for adapting Model Context Protocol tools to the agent framework.

This module provides the MCPAgentTool class which serves as an adapter between
MCP (Model Context Protocol) tools and the agent framework's tool interface.
It allows MCP tools to be seamlessly integrated and used within the agent ecosystem.
"""

import logging
from datetime import timedelta
from typing import TYPE_CHECKING, Any

from mcp.types import Tool as MCPTool
from typing_extensions import override

from ..types._events import ToolResultEvent
from ..types.tools import AgentTool, ToolGenerator, ToolSpec, ToolUse

if TYPE_CHECKING:
from .mcp_client import MCPClient

logger = logging.getLogger(__name__)


class MCPAgentTool(AgentTool):
"""Adapter class that wraps an MCP tool and exposes it as an AgentTool.

This class bridges the gap between the MCP protocol's tool representation
and the agent framework's tool interface, allowing MCP tools to be used
seamlessly within the agent framework.
"""

def __init__(
self,
mcp_tool: MCPTool,
mcp_client: "MCPClient",
name_override: str | None = None,
timeout: timedelta | None = None,
) -> None:
"""Initialize a new MCPAgentTool instance.

Args:
mcp_tool: The MCP tool to adapt
mcp_client: The MCP server connection to use for tool invocation
name_override: Optional name to use for the agent tool (for disambiguation)
If None, uses the original MCP tool name
timeout: Optional timeout duration for tool execution
"""
super().__init__()
logger.debug("tool_name=<%s> | creating mcp agent tool", mcp_tool.name)
self.mcp_tool = mcp_tool
self.mcp_client = mcp_client
self._agent_tool_name = name_override or mcp_tool.name
self.timeout = timeout

@property
def tool_name(self) -> str:
"""Get the name of the tool.

Returns:
str: The agent-facing name of the tool (may be disambiguated)
"""
return self._agent_tool_name

@property
def tool_spec(self) -> ToolSpec:
"""Get the specification of the tool.

This method converts the MCP tool specification to the agent framework's
ToolSpec format, including the input schema, description, and optional output schema.

Returns:
ToolSpec: The tool specification in the agent framework format
"""
description: str = self.mcp_tool.description or f"Tool which performs {self.mcp_tool.name}"

spec: ToolSpec = {
"inputSchema": {"json": self.mcp_tool.inputSchema},
"name": self.tool_name, # Use agent-facing name in spec
"description": description,
}

if self.mcp_tool.outputSchema:
spec["outputSchema"] = {"json": self.mcp_tool.outputSchema}

return spec

@property
def tool_type(self) -> str:
"""Get the type of the tool.

Returns:
str: The type of the tool, always "python" for MCP tools
"""
return "python"

@override
async def stream(self, tool_use: ToolUse, invocation_state: dict[str, Any], **kwargs: Any) -> ToolGenerator:
"""Stream the MCP tool.

This method delegates the tool stream to the MCP server connection, passing the tool use ID, tool name, and
input arguments.

Args:
tool_use: The tool use request containing tool ID and parameters.
invocation_state: Context for the tool invocation, including agent state.
**kwargs: Additional keyword arguments for future extensibility.

Yields:
Tool events with the last being the tool result.
"""
logger.debug("tool_name=<%s>, tool_use_id=<%s> | streaming", self.tool_name, tool_use["toolUseId"])

result = await self.mcp_client.call_tool_async(
tool_use_id=tool_use["toolUseId"],
name=self.mcp_tool.name, # Use original MCP name for server communication
arguments=tool_use["input"],
read_timeout_seconds=self.timeout,
)
yield ToolResultEvent(result)
Loading
Loading