Getting Started with MCP in Python: SDK, Tool Use, and Testing
- 8 minutes to read
Table of Contents
How Is Model Context Protocol (MCP) Used in Python?
The Model Context Protocol (MCP) is an open standard that connects AI models to external tools, data sources, and workflows. The official MCP Python SDK handles the underlying protocol logic so you can easily build servers and clients.
In this article we’ll provide three technical guides to help you get started with MCP in Python:
Python SDK quick start:
- Install the official mcp package and create an MCP server with FastMCP.
- Expose Python functions as MCP tools, run the server, and configure an MCP client.
- Test the tools from an MCP client to verify they work correctly.
Tutorial showing MCP tool use in Python:
- Import the required packages
- Add a helper function for API requests
- Add a helper function for formatting alerts
- Expose a tool for weather alerts
- Expose a tool for forecasts
- Run the MCP server
- Configure an MCP client
- Test the tools
MCP Python testing guide:
- Create an MCP server
- Install the testing dependencies
- Create an in-memory client session
- Call the tool from the test
- Verify the tool output
- Expand the test suite
Why MCP Matters for Python Developers
MCP matters for Python developers because Python is widely used for backend services, automation, data engineering, AI tooling, and internal developer platforms. With MCP, Python developers can expose these existing systems to AI agents in a structured and reusable way. Instead of building a custom plugin or API integration for each AI application, they can create an MCP server that makes Python functions, scripts, databases, or workflows available through a standard interface.
For Python teams, MCP also makes it easier to build agentic applications that can safely interact with real tools. A developer can define Python-based tools for tasks such as querying a database, reading files, running internal automation, checking logs, creating tickets, or calling business APIs. MCP-compatible clients can then discover and use those tools without tightly coupling the AI application to the underlying implementation. This improves portability, reduces integration work, and helps teams move from simple chatbot prototypes to more practical AI systems that can take action across real development and business environments.
What is the MCP Python SDK?
The MCP Python SDK is the official Python library for building applications that use the Model Context Protocol. It supports both sides of the protocol, allowing developers to create MCP servers that expose tools, resources, and prompts, as well as MCP clients that connect to any MCP server.
The SDK handles the protocol details so developers can focus on implementing functionality. Instead of manually defining schemas, parsing requests, validating input, or implementing transport logic, developers define Python functions with type hints and docstrings. The SDK uses this information to expose them through MCP.
The SDK also supports the standard MCP transport mechanisms, including standard input/output (stdio), Streamable HTTP, and Server-Sent Events (SSE). This allows the same server or client implementation to communicate across different environments without changing application logic.
Getting Started with the MCP Python SDK
The MCP Python SDK is available on PyPI as the mcp package. You can install it with:
Use:
pip install mcp[cli]
Installing the package also installs the dependencies required to support MCP features. These include:
- httpx and httpx-sse for HTTP, Streamable HTTP, and Server-Sent Events (SSE) transports
- pydantic and jsonschema for data validation and JSON schema support
- starlette, sse-starlette, and uvicorn for HTTP transport endpoints
- python-multipart for HTTP body parsing
- pydantic-settings for settings management in FastMCP
- pywin32, which provides Windows-specific support for the CLI tools.
The SDK also offers an optional cli dependency group. This installs typer and python-dotenv, which are used by the MCP command-line tools.
Tutorial #1: Tool Use with MCP Servers in Python
This tutorial is based on the official MCP documentation.
The following example shows how to build an MCP server that exposes Python functions as tools. The server provides two weather tools: one for active alerts and one for forecasts.
The following example shows how to build an MCP server that exposes Python functions as tools. The server provides two weather tools: one for active alerts and one for forecasts.
1. Import the Required Packages
Add the core imports to weather.py. This example uses FastMCP to create the server and httpx to call the National Weather Service API.
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# Create the weather MCP service
weather_server = FastMCP(name="weather-service")
# National Weather Service configuration
API_ROOT_URL = "https://api.weather.gov"
CLIENT_USER_AGENT = "python-mcp-weather/1.0"
FastMCP uses type hints and docstrings to generate tool definitions. This lets the client understand what each tool does and what arguments it accepts.
2. Add a Helper Function for API Requests
The server needs a reusable function for calling the weather API. This function sets the required headers, sends the request, and returns parsed JSON data.
async def fetch_nws_json(endpoint: str) -> dict[str, Any] | None:
"""Retrieve and decode a JSON response from the NWS service."""
request_headers = {
"User-Agent": CLIENT_USER_AGENT,
"Accept": "application/geo+json",
}
try:
async with httpx.AsyncClient(timeout=30.0) as http_client:
result = await http_client.get(
endpoint,
headers=request_headers,
)
result.raise_for_status()
return result.json()
except httpx.HTTPError:
return None
3. Add a Helper Function for Formatting Alerts
API responses are not always useful in raw JSON form. Format each alert into text that the model can use in its response.
def render_weather_alert(alert_item: dict[str, Any]) -> str:
"""Convert an NWS alert object into model-friendly text."""
details = alert_item.get("properties", {})
return (
f"Event: {details.get('event', 'Unknown')}\n"
f"Area: {details.get('areaDesc', 'Unknown')}\n"
f"Severity: {details.get('severity', 'Unknown')}\n"
f"Description: {details.get('description', 'No description available')}\n"
f"Instructions: {details.get('instruction', 'No specific instructions provided')}"
)
4. Expose a Tool for Weather Alerts
Register a Python function as an MCP tool with @mcp.tool(). This tool accepts a two-letter US state code and returns active weather alerts.
@weather_server.tool()
async def list_active_alerts(state_code: str) -> str:
"""Return active weather alerts for a US state.
Args:
state_code: Two-letter US state abbreviation, such as CA or NY.
"""
endpoint = f"{API_ROOT_URL}/alerts/active/area/{state_code.upper()}"
payload = await fetch_nws_json(endpoint)
if not payload or "features" not in payload:
return "Unable to retrieve weather alerts."
alert_items = payload["features"]
if not alert_items:
return "No active alerts were found for this state."
formatted_alerts = [
render_weather_alert(alert)
for alert in alert_items
]
return "\n---\n".join(formatted_alerts)
The SDK reads the state_code: str type hint and the docstring to describe the tool to the client. The language model can then decide when this tool is useful.
5. Expose a Tool for Forecasts
The forecast tool accepts latitude and longitude values. It first requests the forecast grid endpoint, then uses the returned forecast URL to fetch the detailed forecast.
@weather_server.tool()
async def retrieve_forecast(latitude: float, longitude: float) -> str:
"""Return the next five forecast periods for a location.
Args:
latitude: Geographic latitude.
longitude: Geographic longitude.
"""
location_endpoint = f"{API_ROOT_URL}/points/{latitude},{longitude}"
location_data = await fetch_nws_json(location_endpoint)
if not location_data:
return "Unable to retrieve forecast data for this location."
forecast_endpoint = (
location_data.get("properties", {}).get("forecast")
)
if not forecast_endpoint:
return "The forecast endpoint was not included in the location response."
forecast_data = await fetch_nws_json(forecast_endpoint)
if not forecast_data:
return "Unable to retrieve the detailed forecast."
forecast_periods = (
forecast_data.get("properties", {}).get("periods", [])
)
if not forecast_periods:
return "No forecast periods were returned for this location."
formatted_periods: list[str] = []
for period in forecast_periods[:5]:
formatted_periods.append(
(
f"{period.get('name', 'Forecast period')}:\n"
f"Temperature: {period.get('temperature', 'Unknown')}°"
f"{period.get('temperatureUnit', '')}\n"
f"Wind: {period.get('windSpeed', 'Unknown')} "
f"{period.get('windDirection', '')}\n"
f"Forecast: {period.get('detailedForecast', 'No forecast available')}"
)
)
return "\n---\n".join(formatted_periods)
This tool returns only the next five forecast periods, which keeps the output useful without sending too much data back to the model.
6. Run the MCP Server
Add a main() function that starts the server over stdio.
def main() -> None:
"""Start the weather MCP server using stdio transport."""
weather_server.run(transport="stdio")
if __name__ == "__main__":
main()
Then start the server:
uv run weather.py
For stdio-based servers, do not write logs to standard output. MCP uses stdout for JSON-RPC messages, so normal print() calls can break communication. Use standard error or a logging library instead.
import logging
import sys
logger = logging.getLogger(__name__)
# Do not use stdout for diagnostic messages
# print("Handling weather request")
# Write directly to stderr
print("Handling weather request", file=sys.stderr)
# Logging defaults to stderr when configured normally
logging.basicConfig(level=logging.INFO)
logger.info("Handling weather request")
7. Configure an MCP Client
To use the server from Claude for Desktop, add it to the client configuration file. The configuration tells the client how to launch the Python MCP server.
{
"mcpServers": {
"weather-service": {
"command": "uv",
"args": [
"run",
"--directory",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
"weather.py"
]
}
}
}
Use the absolute path to the project directory. On Windows, use double backslashes (\\) or forward slashes (/) in the JSON path.
8. Test the Tools
After restarting the client, check that the weather server appears in the available connectors or tools. Then ask questions that require the server.
What's the weather in Elgin, Illinois?
What are the active Tornado alerts in Texas?
These examples work with US locations because the server uses the National Weather Service API.
Tutorial #2: How to Test an MCP Python Server
Testing an MCP server is similar to testing any other Python application, but instead of sending requests over a network, the MCP Python SDK lets you create an in-memory client and server session. This makes tests fast and avoids the need to start a separate server process.
1. Create an MCP Server
Assume you have a simple server that exposes a single tool.
from mcp.server import FastMCP
app = FastMCP("Calculator")
@app.tool()
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
2. Install the Testing Dependencies
The example uses pytest as the test framework and inline-snapshot to compare tool responses against expected snapshots.
pip install inline-snapshot pytest
3. Create an In-Memory Client Session
The MCP Python SDK provides the create_connected_server_and_client_session() helper, which creates a connected client and server using an in-memory transport. This allows tests to call MCP tools directly without configuring stdio or HTTP transports.
from collections.abc import AsyncGenerator
import pytest
from mcp.client.session import ClientSession
from mcp.shared.memory import create_connected_server_and_client_session
from server import app
@pytest.fixture
def anyio_backend() -> str:
return "asyncio"
@pytest.fixture
async def client_session() -> AsyncGenerator[ClientSession, None]:
async with create_connected_server_and_client_session(
app,
raise_exceptions=True,
) as session:
yield session
The client_session fixture provides a connected ClientSession that can invoke the server’s tools exactly as an MCP client would.
4. Call the Tool from the Test
Once the session is available, call the tool by name and provide its input arguments as a dictionary.
result = await client_session.call_tool(
"multiply",
{"a": 1, "b": 2},
)
This executes the multiply tool through the MCP protocol, even though both the client and server are running in the same process.
5. Verify the Tool Output
The example uses inline-snapshot to compare the returned CallToolResult with the expected response.
from inline_snapshot import snapshot
from mcp.types import CallToolResult, TextContent
@pytest.mark.anyio
async def test_call_multiply_tool(
client_session: ClientSession,
) -> None:
result = await client_session.call_tool(
"multiply",
{"a": 1, "b": 2},
)
assert result == snapshot(
CallToolResult(
content=[
TextContent(
type="text",
text="2",
)
],
structuredContent={
"result": 2,
},
)
)
The snapshot verifies both the text returned by the tool and its structured output. This makes it easier to detect unexpected changes in tool behavior while keeping test code concise.
6. Expand the Test Suite
After validating a simple tool, you can add additional test cases that cover different inputs, error conditions, and other tools exposed by the server. Because the client and server communicate over an in-memory transport, these tests can exercise MCP functionality without launching a separate server process.
Connecting Security Operations Platforms to the Model Context Protocol
Modern security operations leverage the Model Context Protocol (MCP) to bridge the gap between large language models and security analytics platforms. By standardizing how artificial intelligence assistants interact with security context, organizations can integrate advanced reasoning into their daily workflows without custom code or vendor lock-in.
Bring Your Own AI in the New-Scale Platform
The New-Scale Security Operations Platform connects with enterprise AI models, such as Claude, Gemini, or ChatGPT, through a dedicated MCP server. This integration provides a secure gateway for external LLMs to interact with the full security context of New-Scale, including alerts, event logs, and behavioral timelines.
Capabilities of the New-Scale MCP server:
- Triage and investigation: External AI assistants query alerts and cases, search event logs using natural language, and retrieve behavioral timelines to assist analysts in real time.
- Claude Code Skills Pack: Pre-packaged skills provide structured templates that guide models through complex tasks like alert triage, case prioritization, and detection tuning.
- Rule and parser management: Analysts use natural language prompts to query coverage scores, identify framework gaps, and refine correlation rules.
- Automated triage: The server supports autonomous workflows that enrich, evaluate, and close low-risk alerts without manual human intervention.
Community-Driven MCP for LogRhythm SIEM
LogRhythm SIEM provides a community-driven MCP server that connects local or proprietary LLMs to the local SIEM environment. This open-source wrapper, accessible via GitHub, allows security teams to build custom agentic workflows on top of their existing SIEM deployment.
Capabilities of the LogRhythm SIEM MCP server:
- Natural language query: Analysts query event data and investigate alarms using direct conversational prompts.
- Built-in analyst skills: The server comes with pre-configured templates that help local models interpret logs, triage events, and identify data patterns.
- Workflow optimization: Administrators use local AI assistants to streamline routine triage and troubleshoot complex detection configurations.
- Community collaboration: The open-source model allows teams to share skills, tools, and custom integrations across the broader security community.
To learn more about how you can get involved with the New-Scale MCP, click here.
Learn More About Exabeam
Learn about the Exabeam platform and expand your knowledge of information security with our collection of white papers, podcasts, webinars, and more.
-
Blog
What Is the Difference Between Exabeam and CrowdStrike for SIEM and Security Operations?
- Show More