Skip to main content
Lesson 4 of 14 · MCP for Developers: Servers and Integrations

Configure a Local MCP Server (STDIO)

Lesson 4 of 14 Updated November 2, 2026 Developer
On this page
  1. Your first local MCP server
  2. Writing the server
  3. Configuring the STDIO client
  4. Test before integrating
  5. Key takeaways

Updated November 2, 2026 — MCP Python SDK v2. This course now follows version 2 of the mcp SDK (2.3.0 as of October 2, 2026): MCPServer replaces FastMCP, transport settings move to run() or streamable_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: the mistralai[agents] extra requires mcp>=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
  • StdioServerParameters configures 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=None inherits the entire environment; an explicit dict limits variables