Model Context Protocol (MCP) Tools
Section titled “Model Context Protocol (MCP) Tools”The Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to Large Language Models. Strands Agents integrates with MCP to extend agent capabilities through external tools and services.
MCP enables communication between agents and MCP servers that provide additional tools. Strands includes built-in support for connecting to MCP servers and using their tools in both Python and TypeScript.
Quick Start
Section titled “Quick Start”=== “Python”
```pythonfrom mcp import stdio_client, StdioServerParametersfrom strands import Agentfrom strands.tools.mcp import MCPClient
# Create MCP client with stdio transportmcp_client = MCPClient(lambda: stdio_client( StdioServerParameters( command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"] )))
# Use with context manager for lifecycle managementwith mcp_client: tools = mcp_client.list_tools_sync() agent = Agent(tools=tools) agent("What is AWS Lambda?")```=== “TypeScript”
```typescript// Create MCP client with stdio transportconst mcpClient = new McpClient({ transport: new StdioClientTransport({ command: 'uvx', args: ['awslabs.aws-documentation-mcp-server@latest'], }),})
// Pass MCP client directly to agentconst agent = new Agent({ tools: [mcpClient],})
await agent.invoke('What is AWS Lambda?')```Integration Approaches
Section titled “Integration Approaches”=== “Python”
**Manual Context Management**
Python requires explicit context management using `with` statements to manage the MCP connection lifecycle:
```pythonfrom mcp import stdio_client, StdioServerParametersfrom strands import Agentfrom strands.tools.mcp import MCPClient
mcp_client = MCPClient(lambda: stdio_client( StdioServerParameters( command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"] )))
# Manual lifecycle managementwith mcp_client: tools = mcp_client.list_tools_sync() agent = Agent(tools=tools) agent("What is AWS Lambda?") # Must be within context```
This approach provides direct control over the MCP session lifecycle but requires careful management to avoid connection errors.
**Managed Integration (Experimental)**
!!! warning "Experimental Feature" The managed integration feature is experimental and may change in future versions. For production applications, use the manual context management approach.
The `MCPClient` implements the experimental `ToolProvider` interface, enabling direct usage in the Agent constructor with automatic lifecycle management:
```python# Direct usage - connection lifecycle managed automaticallyagent = Agent(tools=[mcp_client])response = agent("What is AWS Lambda?")```=== “TypeScript”
**Direct Integration**
`McpClient` instances are passed directly to the agent. The client connects lazily on first use:
```typescriptconst mcpClientDirect = new McpClient({ transport: new StdioClientTransport({ command: 'uvx', args: ['awslabs.aws-documentation-mcp-server@latest'], }),})
// MCP client passed directly - connects on first tool useconst agentDirect = new Agent({ tools: [mcpClientDirect],})
await agentDirect.invoke('What is AWS Lambda?')```
Tools can also be listed explicitly if needed:
```typescript// Explicit tool listingconst tools = await mcpClient.listTools()const agentExplicit = new Agent({ tools })```Transport Options
Section titled “Transport Options”Both Python and TypeScript support multiple transport mechanisms for connecting to MCP servers.
Standard I/O (stdio)
Section titled “Standard I/O (stdio)”For command-line tools and local processes that implement the MCP protocol:
=== “Python”
```pythonfrom mcp import stdio_client, StdioServerParametersfrom strands import Agentfrom strands.tools.mcp import MCPClient
# For macOS/Linux:stdio_mcp_client = MCPClient(lambda: stdio_client( StdioServerParameters( command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"] )))
# For Windows:stdio_mcp_client = MCPClient(lambda: stdio_client( StdioServerParameters( command="uvx", args=[ "--from", "awslabs.aws-documentation-mcp-server@latest", "awslabs.aws-documentation-mcp-server.exe" ] )))
with stdio_mcp_client: tools = stdio_mcp_client.list_tools_sync() agent = Agent(tools=tools) response = agent("What is AWS Lambda?")```=== “TypeScript”
```typescriptconst stdioClient = new McpClient({ transport: new StdioClientTransport({ command: 'uvx', args: ['awslabs.aws-documentation-mcp-server@latest'], }),})
const agentStdio = new Agent({ tools: [stdioClient],})
await agentStdio.invoke('What is AWS Lambda?')```Streamable HTTP
Section titled “Streamable HTTP”For HTTP-based MCP servers that use Streamable HTTP transport:
=== “Python”
```pythonfrom mcp.client.streamable_http import streamablehttp_clientfrom strands import Agentfrom strands.tools.mcp import MCPClient
streamable_http_mcp_client = MCPClient( lambda: streamablehttp_client("http://localhost:8000/mcp"))
with streamable_http_mcp_client: tools = streamable_http_mcp_client.list_tools_sync() agent = Agent(tools=tools)```
Additional properties like authentication can be configured:
```pythonimport osfrom mcp.client.streamable_http import streamablehttp_clientfrom strands.tools.mcp import MCPClient
github_mcp_client = MCPClient( lambda: streamablehttp_client( url="https://api.githubcopilot.com/mcp/", headers={"Authorization": f"Bearer {os.getenv('MCP_PAT')}"} ))```
#### AWS IAM
For MCP servers on AWS that use SigV4 authentication with IAM credentials, you can conveniently use the [`mcp-proxy-for-aws`](https://pypi.org/project/mcp-proxy-for-aws/) package to handle AWS credential management and request signing automatically. See the [detailed guide](https://dev.to/aws/no-oauth-required-an-mcp-client-for-aws-iam-k1o) for more information.
First, install the package:
```bashpip install mcp-proxy-for-aws```
Then you use it like any other transport:
```pythonfrom mcp_proxy_for_aws.client import aws_iam_streamablehttp_clientfrom strands.tools.mcp import MCPClient
mcp_client = MCPClient(lambda: aws_iam_streamablehttp_client( endpoint="https://your-service.us-east-1.amazonaws.com/mcp", aws_region="us-east-1", aws_service="bedrock-agentcore"))```=== “TypeScript”
```typescriptconst httpClient = new McpClient({ transport: new StreamableHTTPClientTransport( new URL('http://localhost:8000/mcp') ) as Transport,})
const agentHttp = new Agent({ tools: [httpClient],})
// With authenticationconst githubMcpClient = new McpClient({ transport: new StreamableHTTPClientTransport( new URL('https://api.githubcopilot.com/mcp/'), { requestInit: { headers: { Authorization: `Bearer ${process.env.GITHUB_PAT}`, }, }, } ) as Transport,})```Server-Sent Events (SSE)
Section titled “Server-Sent Events (SSE)”=== “Python”
For HTTP-based MCP servers that use Server-Sent Events transport:
```pythonfrom mcp.client.sse import sse_clientfrom strands import Agentfrom strands.tools.mcp import MCPClient
sse_mcp_client = MCPClient(lambda: sse_client("http://localhost:8000/sse"))
with sse_mcp_client: tools = sse_mcp_client.list_tools_sync() agent = Agent(tools=tools)```=== “TypeScript”
```typescriptimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'
const sseClient = new McpClient({ transport: new SSEClientTransport( new URL('http://localhost:8000/sse') ),})
const agentSse = new Agent({ tools: [sseClient],})```Using Multiple MCP Servers
Section titled “Using Multiple MCP Servers”Combine tools from multiple MCP servers in a single agent:
=== “Python”
```pythonfrom mcp import stdio_client, StdioServerParametersfrom mcp.client.sse import sse_clientfrom strands import Agentfrom strands.tools.mcp import MCPClient
# Create multiple clientssse_mcp_client = MCPClient(lambda: sse_client("http://localhost:8000/sse"))stdio_mcp_client = MCPClient(lambda: stdio_client( StdioServerParameters(command="python", args=["path/to/mcp_server.py"])))
# Manual approach - explicit context managementwith sse_mcp_client, stdio_mcp_client: tools = sse_mcp_client.list_tools_sync() + stdio_mcp_client.list_tools_sync() agent = Agent(tools=tools)
# Managed approach (experimental)agent = Agent(tools=[sse_mcp_client, stdio_mcp_client])```=== “TypeScript”
```typescriptconst localClient = new McpClient({ transport: new StdioClientTransport({ command: 'uvx', args: ['awslabs.aws-documentation-mcp-server@latest'], }),})
const remoteClient = new McpClient({ transport: new StreamableHTTPClientTransport( new URL('https://api.example.com/mcp/') ) as Transport,})
// Pass multiple MCP clients to the agentconst agentMultiple = new Agent({ tools: [localClient, remoteClient],})```Client Configuration
Section titled “Client Configuration”=== “Python”
Python's `MCPClient` supports tool filtering and name prefixing to manage tools from multiple servers.
**Tool Filtering**
Control which tools are loaded using the `tool_filters` parameter:
```pythonfrom mcp import stdio_client, StdioServerParametersfrom strands.tools.mcp import MCPClientimport re
# String matching - loads only specified toolsfiltered_client = MCPClient( lambda: stdio_client(StdioServerParameters( command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"] )), tool_filters={"allowed": ["search_documentation", "read_documentation"]})
# Regex patternsregex_client = MCPClient( lambda: stdio_client(StdioServerParameters( command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"] )), tool_filters={"allowed": [re.compile(r"^search_.*")]})
# Combined filters - applies allowed first, then rejectedcombined_client = MCPClient( lambda: stdio_client(StdioServerParameters( command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"] )), tool_filters={ "allowed": [re.compile(r".*documentation$")], "rejected": ["read_documentation"] })```
**Tool Name Prefixing**
Prevent name conflicts when using multiple MCP servers:
```pythonaws_docs_client = MCPClient( lambda: stdio_client(StdioServerParameters( command="uvx", args=["awslabs.aws-documentation-mcp-server@latest"] )), prefix="aws_docs")
other_client = MCPClient( lambda: stdio_client(StdioServerParameters( command="uvx", args=["other-mcp-server@latest"] )), prefix="other")
# Tools will be named: aws_docs_search_documentation, other_search, etc.agent = Agent(tools=[aws_docs_client, other_client])```=== “TypeScript”
TypeScript's `McpClient` accepts optional application metadata:
```typescriptconst mcpClient = new McpClient({ applicationName: 'My Agent App', applicationVersion: '1.0.0', transport: new StdioClientTransport({ command: 'npx', args: ['-y', 'some-mcp-server'], }),})```
Tool filtering and prefixing are not currently supported in TypeScript.Direct Tool Invocation
Section titled “Direct Tool Invocation”While tools are typically invoked by the agent based on user requests, MCP tools can also be called directly:
=== “Python”
```pythonresult = mcp_client.call_tool_sync( tool_use_id="tool-123", name="calculator", arguments={"x": 10, "y": 20})print(f"Result: {result['content'][0]['text']}")```=== “TypeScript”
```typescript// Get tools and find the target toolconst tools = await mcpClient.listTools()const calcTool = tools.find(t => t.name === 'calculator')
// Call directly through the clientconst result = await mcpClient.callTool(calcTool, { x: 10, y: 20 })```Implementing an MCP Server
Section titled “Implementing an MCP Server”Custom MCP servers can be created to extend agent capabilities:
=== “Python”
```pythonfrom mcp.server import FastMCP
# Create an MCP servermcp = FastMCP("Calculator Server")
# Define a tool@mcp.tool(description="Calculator tool which performs calculations")def calculator(x: int, y: int) -> int: return x + y
# Run the server with SSE transportmcp.run(transport="sse")```=== “TypeScript”
```typescriptimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'import { z } from 'zod'
const server = new McpServer({ name: 'Calculator Server', version: '1.0.0',})
server.tool( 'calculator', 'Calculator tool which performs calculations', { x: z.number(), y: z.number(), }, async ({ x, y }) => { return { content: [{ type: 'text', text: String(x + y) }], } })
const transport = new StdioServerTransport()await server.connect(transport)```For more information on implementing MCP servers, see the MCP documentation.
Advanced Usage
Section titled “Advanced Usage”=== “Python”
### Elicitation
An MCP server can request additional information from the user by sending an elicitation request. Set up an elicitation callback to handle these requests:
```python# server.pyfrom mcp.server import FastMCPfrom pydantic import BaseModel, Field
class ApprovalSchema(BaseModel): username: str = Field(description="Who is approving?")
server = FastMCP("mytools")
@server.tool()async def delete_files(paths: list[str]) -> str: result = await server.get_context().elicit( message=f"Do you want to delete {paths}", schema=ApprovalSchema, ) if result.action != "accept": return f"User {result.data.username} rejected deletion"
# Perform deletion... return f"User {result.data.username} approved deletion"
server.run()```
```python# client.pyfrom mcp import stdio_client, StdioServerParametersfrom mcp.types import ElicitResultfrom strands import Agentfrom strands.tools.mcp import MCPClient
async def elicitation_callback(context, params): print(f"ELICITATION: {params.message}") # Get user confirmation... return ElicitResult( action="accept", content={"username": "myname"} )
client = MCPClient( lambda: stdio_client( StdioServerParameters(command="python", args=["/path/to/server.py"]) ), elicitation_callback=elicitation_callback,)
with client: agent = Agent(tools=client.list_tools_sync()) result = agent("Delete 'a/b/c.txt' and share the name of the approver")```
For more information on elicitation, see the [MCP specification](https://modelcontextprotocol.io/specification/draft/client/elicitation).{{ ts_not_supported_code() }}
Best Practices
Section titled “Best Practices”- Tool Descriptions: Provide clear descriptions for tools to help the agent understand when and how to use them
- Error Handling: Return informative error messages when tools fail to execute properly
- Security: Consider security implications when exposing tools via MCP, especially for network-accessible servers
- Connection Management: In Python, always use context managers (
withstatements) to ensure proper cleanup of MCP connections - Timeouts: Set appropriate timeouts for tool calls to prevent hanging on long-running operations
Troubleshooting
Section titled “Troubleshooting”MCPClientInitializationError (Python)
Section titled “MCPClientInitializationError (Python)”Tools relying on an MCP connection must be used within a context manager. Operations will fail when the agent is used outside the with statement block.
# Correctwith mcp_client: agent = Agent(tools=mcp_client.list_tools_sync()) response = agent("Your prompt") # Works
# Incorrectwith mcp_client: agent = Agent(tools=mcp_client.list_tools_sync())response = agent("Your prompt") # Fails - outside contextConnection Failures
Section titled “Connection Failures”Connection failures occur when there are problems establishing a connection with the MCP server. Verify that:
- The MCP server is running and accessible
- Network connectivity is available and firewalls allow the connection
- The URL or command is correct and properly formatted
Tool Discovery Issues
Section titled “Tool Discovery Issues”If tools aren’t being discovered:
- Confirm the MCP server implements the
list_toolsmethod correctly - Verify all tools are registered with the server
Tool Execution Errors
Section titled “Tool Execution Errors”When tool execution fails:
- Verify tool arguments match the expected schema
- Check server logs for detailed error information