Configure a Local MCP Server (STDIO)
On this page
Updated November 2, 2026 — MCP Python SDK v2. This course now follows version 2 of the
mcpSDK (2.3.0 as of October 2, 2026):MCPServerreplacesFastMCP, transport settings move torun()orstreamable_http_app(), and an application exposed under a real hostname must declare that hostname (transport_security). This is also the version required by the Mistral SDK: themistralai[agents]extra requiresmcp>=2.2.0,<3.0. Sources: What’s new in v2 · Migration Guide · Deploy & scale.
Your first local MCP server
STDIO mode is the natural starting point for developing and testing an MCP server: everything runs on your machine, with no deployment, no public URL, and no certificates to manage. You edit a file, restart, and observe. In this lesson, you will write a local MCP server in Python and then connect it to a client.
As for prerequisites, you need Python 3.10 or higher, an up-to-date pip, and preferably a dedicated virtual environment. The last point isn’t cosmetic: MCP packages evolve quickly, and a separate venv per project prevents you from breaking a working server when installing a dependency for another.
# Create a virtual environment
python -m venv mcp-env
source mcp-env/bin/activate # Linux/Mac
# mcp-env\Scripts\activate # Windows
# Official MCP SDK (v2) and Mistral SDK with its agents extra
pip install "mcp[cli]>=2.2,<3" "mistralai[agents]"
Writing the server
With MCPServer, the high-level class in the official SDK (formerly FastMCP), a local server takes just a few lines. You instantiate an application by giving it a descriptive name, then decorate your Python functions with @app.tool() to expose them.
# serveur_local.py
from mcp.server.mcpserver import MCPServer
# Initialize the server with a descriptive name
app = MCPServer("Local Demo Server")
@app.tool()
def greet(name: str) -> str:
"""Greets a person by name.
Args:
name: The first name of the person to greet
Returns:
A personalized greeting message
"""
return f"Hello {name}! Welcome to the world of MCP."
@app.tool()
def calculate(operation: str, a: float, b: float) -> str:
"""Performs a simple mathematical calculation.
Args:
operation: The operation to perform (add, sub, mul, div)
a: First number
b: Second number
Returns:
The result of the calculation
"""
ops = {
"add": a + b,
"sub": a - b,
"mul": a * b,
"div": a / b if b != 0 else "Error: division by zero"
}
result = ops.get(operation, "Unknown operation")
return f"{a} {operation} {b} = {result}"
This file exposes two tools. Note the role of the docstrings: they don’t just document your code for a colleague; they literally become the description the model reads to decide whether to call calculate instead of greet. A sloppy docstring here leads to erratic calls later.
Configuring the STDIO client
On the client side, you aren’t connecting to an address; you are explaining how to start the server. That is the role of StdioServerParameters.
# client_stdio.py
from mcp import StdioServerParameters
from mistralai.extra.mcp.stdio import MCPClientSTDIO
from pathlib import Path
# Path to your server
path_to_server = Path("serveur_local.py")
# STDIO server configuration
server_params = StdioServerParameters(
command="python", # The command to launch the server
args=[str(path_to_server)], # The arguments (the Python file)
env=None, # Environment variables (optional)
)
# Create the MCP client
mcp_client = MCPClientSTDIO(stdio_params=server_params)
Three parameters deserve detail.
command
This is the system command that launches the server process: "python" for a Python server, "node" for a JavaScript server, or the direct path to a binary if your server is compiled.
# Python
command="python"
# Node.js
command="node"
# A compiled executable
command="./mon_serveur_mcp"
args
This is the list of arguments passed to that command. For a Python script, this is at minimum the file path, but nothing stops you from passing your server’s options here.
# Simple script
args=["serveur_local.py"]
# With additional arguments
args=["serveur_local.py", "--port", "8080", "--verbose"]
env
This is the dictionary of environment variables passed to the server process: the channel through which you pass it an API key, a database URL, or a debug flag.
import os
server_params = StdioServerParameters(
command="python",
args=["serveur_local.py"],
env={
**os.environ, # Inherit from the current environment
"API_KEY": "your_api_key",
"DATABASE_URL": "postgresql://...",
"DEBUG": "true",
},
)
The behavior of this parameter often surprises people. With env=None, the server inherits the entire environment of the parent process; with a dictionary, it sees strictly only the listed variables. If you provide a dictionary without dumping os.environ into it, don’t be surprised if your server can no longer find its PATH or your API key: the behavior is perfectly logical, simply radical.
Test before integrating
Before plugging your server into an agent, test it in isolation with the MCP Inspector. The reasoning is simple: if a call fails once the server is connected to a model, you won’t know if the problem lies in your code or the model’s decision. The Inspector removes this ambiguity.
npx @modelcontextprotocol/inspector python serveur_local.py
The command opens a local web interface where you can list the exposed tools, inspect the description and parameters the model will receive, trigger a manual call with arguments of your choice, and verify the exact format of the return value. Make it a habit to use this systematically: it’s five minutes that saves an hour.
Finally, here is a file organization that ages well, with the server, the client, dependencies, secrets kept out of the repository, and unit tests for your tools:
mon-projet-mcp/
├── serveur_local.py # The MCP server
├── client_stdio.py # The client script
├── requirements.txt # Dependencies
├── .env # Environment variables (never committed)
└── tests/
└── test_tools.py # Unit tests for tools
Key takeaways
- An STDIO server launches as a local process
StdioServerParametersconfigures the command, arguments, and environment variables- Your tools’ docstrings serve as the description for the model
- The MCP Inspector (
npx @modelcontextprotocol/inspector) is essential for testing env=Noneinherits the entire environment; an explicit dict limits variables