
Introduction to MCP with Strands Agents
Learn the basics of Model Context Protocol, with examples using FastMCP and Strands Agents.
Note: This article is part of a series on creating agentic applications with Amazon Bedrock and Strands Agents. Please check out the series overview for the list of related articles and guidance on setting up your development environment.
Overview of Model Context Protocol (MCP)
The Model Context Protocol (MCP) is an open standard for connecting AI agents to external tools and data sources. It follows a client-server architecture: an MCP client (typically an agent) connects to an MCP server, which exposes capabilities the client can discover and invoke.
Strands Agents supports defining tools as Python functions , which works well when the tool logic lives in the same codebase as the agent. MCP is useful when you want to share tools across multiple agents or frameworks, expose tools written in other languages, or consume third-party tools.
MCP primitives
MCP servers can expose three types of primitives: tools, resources, and prompts:
- Tools are callable functions with typed inputs and outputs
- Resources are read-only text or binary content like documents, images, data files, etc.
- Prompts are reusable prompt templates
We'll demonstrate MCP tools in the code examples below.
MCP transports
The protocol defines two standard transports, stdio and Streamable HTTP:
- stdio has the client launch the server as a local subprocess and communicate over stdin/stdout (like a command line app)
- Streamable HTTP has the server run independently, with the client using HTTP requests to communicate with the server
Code examples
A note on mcp vs. fastmcp Python libraries
Strands Agents includes Anthropic's mcp Python library , which includes version 1 of the FastMCP library.
Since then, FastMCP 2 came out, with significant enhancements and thorough documentation. One example: FastMCP 2 supports using
Annotated for tool field definitions (like Strands does), which is nice when moving from local Strands Python tools to MCP tools.So it's a bit ridiculous, but we're importing two different MCP libraries in the examples below. Welcome to bleeding-edge technology!
The "PFJ" scene from Monty Python's Life of Brian covers it nicely:

Creating a basic MCP server using FastMCP
In math_fastmcp_server.py we create an MCP server using the fastmcp Python library:
Python
"""Math MCP server implemented with fastmcp library"""import mathfrom typing import Annotatedfrom fastmcp import FastMCPmcp = FastMCP(name="MathServer")@mcp.tool(description="Calculates the cosine of x")def cosine(x: Annotated[float, "The value of x in radians"]) -> float:"""Cosine tool"""return math.cos(x)@mcp.tool(description="Calculates the sine of x")def sine(x: Annotated[float, "The value of x in radians"]) -> float:"""Sine tool"""return math.sin(x)@mcp.tool(description="Divides x by y")def divide(x: Annotated[float, "The numerator"], y: Annotated[float, "The denominator"]) -> float:"""Divide tool"""return x / y@mcp.tool(description="Raise x to the power y")def raise_to_power(x: Annotated[float, "The base"], y: Annotated[float, "The exponent"]) -> float:"""Raise to power tool"""return math.pow(x, y)if __name__ == "__main__":mcp.run()
Since our clients are using the stdio transport layer , we don't need to run this script directly. The client apps will execute the script themselves.
Listing tools from an MCP server
In list_mcp_tools.py we load the MCP server from math_fastmcp_server.py through stdio and display its tools:
Python
"""List MCP tools"""import jsonfrom mcp import stdio_client, StdioServerParametersfrom strands.tools.mcp import MCPClientstdio_mcp_client = MCPClient(lambda: stdio_client(StdioServerParameters(command="python", args=["math_fastmcp_server.py"])))with stdio_mcp_client:tools = stdio_mcp_client.list_tools_sync()for mcp_tool in tools:print("-" * 50)print(mcp_tool.tool_name)print("-" * 50)print(json.dumps(mcp_tool.tool_spec, indent=4))
Run the following from your terminal:
Bash
python list_mcp_tools.py
This will output the tool names and tool specs retrieved from math_fastmcp_server.py. Here is an example of one of the tool specs:
JSON
--------------------------------------------------raise_to_power--------------------------------------------------{"inputSchema": {"json": {"properties": {"x": {"description": "The base","type": "number"},"y": {"description": "The exponent","type": "number"}},"required": ["x","y"],"type": "object"}},"name": "raise_to_power","description": "Raise x to the power y","outputSchema": {"json": {"properties": {"result": {"type": "number"}},"required": ["result"],"type": "object","x-fastmcp-wrap-result": true}}}
Using the MCP server with Strands Agents
In mcp_client.py we load math_fastmcp_server.py through stdio and invoke it with a Strands agent. The
MCPClient must be used inside a with block, which manages the lifecycle of the connection to the MCP server. It opens the stdio transport on entry and shuts it down on exit.Python
"""Basic local MCP client"""from mcp import stdio_client, StdioServerParametersfrom strands import Agentfrom strands.tools.mcp import MCPClientstdio_mcp_client = MCPClient(lambda: stdio_client(StdioServerParameters(command="python", args=["math_fastmcp_server.py"])))with stdio_mcp_client:tools = stdio_mcp_client.list_tools_sync()agent = Agent(tools=tools)agent("What is the secant squared of 1.4?")
Run the following command from your terminal:
Bash
python mcp_client.py
Example output:
text
I need to calculate the secant squared of 1.4. The secant is the reciprocal of cosine, so sec²(x) = 1/cos²(x).Let me first find the cosine of 1.4, then calculate the secant squared.Tool #1: cosineNow I need to calculate 1/cos²(1.4). First, let me square the cosine value:Tool #2: raise_to_powerNow I'll calculate the reciprocal to get sec²(1.4):Tool #3: divideThe secant squared of 1.4 is approximately **34.62**.
Interactive CLI chat with MCP tools
In cli_mcp_chat.py we wrap the same MCP server in a simple command-line chat loop. The chat loop runs inside the
with block so the MCP connection stays open for the entire conversation. If the connection closes, the agent loses access to the MCP tools.Python
"""A simple command-line interface for chatting with an agent."""from mcp import stdio_client, StdioServerParametersfrom strands import Agentfrom strands.tools.mcp import MCPClientBOT_PREFIX = "\033[1;36m\n🤖Bot:"HUMAN_PREFIX = "\033[0;33m\n\n🙂Me:\n"def main():"""Runs a CLI-based chat with the agent"""mcp_client = MCPClient(lambda: stdio_client(StdioServerParameters(command="python", args=["math_fastmcp_server.py"])))with mcp_client:tools = mcp_client.list_tools_sync()agent = Agent(tools=tools)print(f"""{BOT_PREFIX}Welcome! Type your message below and hit return to send.Type "exit", "quit", "done", "bye" or press Ctrl-C to exit.""")while True: # loop until exitedtry:prompt = input(f"{HUMAN_PREFIX}")if prompt.lower() in ["exit", "quit", "done", "bye"]:print("EXITING!!!")breakprint(f"{BOT_PREFIX}")agent(prompt)except KeyboardInterrupt:print("\nBye!\n")breakif __name__ == "__main__":main()
Run the following command from your terminal:
Bash
python cli_mcp_chat.py
Example output:
text
🤖Bot:Welcome! Type your message below and hit return to send.Type "exit", "quit", "done", "bye" or press Ctrl-C to exit.🙂Me:what is the tangent of 1.385?🤖Bot:To calculate the tangent of 1.385, I need to use the identity tan(x) = sin(x)/cos(x). Let me calculate the sine and cosine of 1.385 first.Tool #1: sineTool #2: cosineNow I'll divide the sine by the cosine to get the tangent:Tool #3: divideThe tangent of 1.385 radians is approximately **5.32**.🙂Me:exit
Using the local MCP server from Kiro
You can also connect your local MCP server to Kiro for interactive testing. In the
~/.kiro/settings/mcp.json file, add an entry under "mcpServers" . The command path must point to the Python binary inside your active virtual environment, and the args path must be the absolute path to your MCP server script. Use which python in your terminal (with the venv activated) to get the correct Python path.JSON
{"mcpServers": {"local-math-mcp": {"command": "/Users/your_user_name/path_to_venv/.venv/bin/python","args": ["/Users/your_user_name/path_to_code/math_fastmcp_server.py"],"disabled": false,"autoApprove": ["cosine","raise_to_power","divide","sine"]}}}
You can then invoke your local MCP tools from Kiro:

This is a quick and dirty way to experiment with MCP servers.
Many published MCP servers are distributed as Python or Node.js packages. When you see an MCP server configured with
"command": "uvx" , that's using uv to run the server in a temporary isolated environment, downloading the package and its dependencies on the fly without permanently installing anything.Check out the code for the Strands MCP Servers or AWS MCP servers to see how others have built and distributed their MCP servers.
You should also check out this blog on creating MCP servers from the official MCP site. It includes a Python example using uv, plus examples with other programming languages.
Alternative: using the mcp library for your server
If you wanted to use the official
mcp Python library (which includes FastMCP 1), you can still define tools with field descriptions. You just have to use a Pydantic Field object:Python
"""Math MCP server implemented with mcp library"""import mathfrom pydantic import Fieldfrom mcp.server import FastMCP# Create a server instancemcp = FastMCP(name="MathServer")@mcp.tool(description="Calculates the cosine of x.")def cosine(x: float = Field(description="The value of x in radians")) -> float:"""Cosine tool"""return math.cos(x)@mcp.tool(description="Calculates the sine of x")def sine(x: float = Field(description="The value of x in radians")) -> float:"""Sine tool"""return math.sin(x)@mcp.tool(description="Divides x by y")def divide(x: float = Field(description="The numerator"), y: float = Field(description="The denominator")) -> float:"""Divide tool"""return x / y@mcp.tool(description="Raise x to the power y")def raise_to_power(x: float = Field(description="The base"), y: float = Field(description="The exponent")) -> float:"""Raise to power tool"""return math.pow(x, y)if __name__ == "__main__":mcp.run()
Next steps
Learn more:
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article