MCPServerStdio: Local Tool Integration via Standard I/O
Master MCPServerStdio for connecting agents to local tool servers via standard I/O, including subprocess management, npx-based servers, filesystem operations, and automatic lifecycle handling.
How MCPServerStdio Works
MCPServerStdio is the simplest MCP transport. Your agent spawns the MCP server as a child process, then communicates with it by writing JSON-RPC messages to the process's stdin and reading responses from stdout. No network, no ports, no HTTP — just pipes.
This makes Stdio ideal for local development, filesystem tools, database access on the same machine, and any scenario where the tool server runs alongside the agent.
Basic Setup
from agents.mcp import MCPServerStdio
# The params dict mirrors how you would run the command in a terminal
server = MCPServerStdio(
name="Filesystem",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/documents"],
},
)
The params object accepts:
flowchart TD
START["MCPServerStdio: Local Tool Integration via Standa…"] --> A
A["How MCPServerStdio Works"]
A --> B
B["Basic Setup"]
B --> C
C["Launching Local Subprocesses"]
C --> D
D["npx-Based MCP Servers"]
D --> E
E["Python-Based MCP Servers"]
E --> F
F["Filesystem Server Deep Dive"]
F --> G
G["Automatic Process Lifecycle Management"]
G --> H
H["Environment Variables and Secrets"]
H --> DONE["Key Takeaways"]
style START fill:#4f46e5,stroke:#4338ca,color:#fff
style DONE fill:#059669,stroke:#047857,color:#fff
- command — The executable to run (e.g.,
npx,python,node) - args — Command-line arguments passed to the executable
- env — Optional environment variables for the subprocess
- cwd — Optional working directory for the subprocess
Launching Local Subprocesses
When you enter the async with block, MCPServerStdio spawns the subprocess and performs the MCP initialization handshake:
import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStdio
async def main():
server = MCPServerStdio(
name="SQLite",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite",
"--db-path", "/tmp/my-database.db"],
},
)
async with server:
# At this point, the subprocess is running and tools are discovered
# The server has reported its available tools via the MCP handshake
agent = Agent(
name="Database Assistant",
instructions="Help users query and manage the SQLite database.",
mcp_servers=[server],
)
result = await Runner.run(
agent,
input="What tables exist in the database? Show me the schema.",
)
print(result.final_output)
# Exiting the async with block kills the subprocess cleanly
asyncio.run(main())
npx-Based MCP Servers
The most common pattern uses npx to run MCP servers published as npm packages. The -y flag auto-confirms the install prompt:
# Filesystem access
fs_server = MCPServerStdio(
name="Filesystem",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"],
},
)
# Git operations
git_server = MCPServerStdio(
name="Git",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git",
"--repository", "/path/to/repo"],
},
)
# GitHub API (requires GITHUB_TOKEN env var)
github_server = MCPServerStdio(
name="GitHub",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {"GITHUB_TOKEN": "ghp_your_token_here"},
},
)
# PostgreSQL queries
postgres_server = MCPServerStdio(
name="Postgres",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres",
"postgresql://user:pass@localhost:5432/mydb"],
},
)
Python-Based MCP Servers
You can also write MCP servers in Python using the mcp package:
flowchart TD
CENTER(("Core Concepts"))
CENTER --> N0["command — The executable to run e.g., n…"]
CENTER --> N1["args — Command-line arguments passed to…"]
CENTER --> N2["env — Optional environment variables fo…"]
CENTER --> N3["cwd — Optional working directory for th…"]
CENTER --> N4["read_file — Read the contents of a file"]
CENTER --> N5["write_file — Create or overwrite a file"]
style CENTER fill:#4f46e5,stroke:#4338ca,color:#fff
# my_tools_server.py
from mcp.server.fastmcp import FastMCP
import httpx
mcp = FastMCP("My Custom Tools")
@mcp.tool()
async def get_weather(city: str) -> str:
"""Get current weather for a city."""
async with httpx.AsyncClient() as client:
resp = await client.get(
"https://api.weatherapi.com/v1/current.json",
params={"key": "YOUR_KEY", "q": city},
)
data = resp.json()
current = data["current"]
return f"{city}: {current['temp_c']}C, {current['condition']['text']}"
@mcp.tool()
def calculate_bmi(weight_kg: float, height_m: float) -> str:
"""Calculate BMI given weight in kg and height in meters."""
bmi = weight_kg / (height_m ** 2)
category = (
"underweight" if bmi < 18.5
else "normal" if bmi < 25
else "overweight" if bmi < 30
else "obese"
)
return f"BMI: {bmi:.1f} ({category})"
if __name__ == "__main__":
mcp.run(transport="stdio")
Connect this server to your agent:
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
custom_server = MCPServerStdio(
name="Custom Tools",
params={
"command": "python",
"args": ["my_tools_server.py"],
"cwd": "/path/to/server",
},
)
Filesystem Server Deep Dive
The filesystem MCP server is one of the most useful. It provides these tools:
- read_file — Read the contents of a file
- write_file — Create or overwrite a file
- edit_file — Make targeted edits to an existing file
- list_directory — List files and subdirectories
- search_files — Search for files matching a pattern
- get_file_info — Get metadata (size, modified date, permissions)
- create_directory — Create a new directory
- move_file — Move or rename a file
The path argument to the server defines the allowed root directory. The server will refuse to access files outside this directory, providing a security boundary:
async def file_management_agent():
server = MCPServerStdio(
name="Filesystem",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem",
"/home/user/projects"],
},
)
async with server:
agent = Agent(
name="Project Organizer",
instructions="""You help organize project files.
You can read, create, move, and search files.
Always explain what you are doing before making changes.
Never delete files without explicit confirmation.""",
mcp_servers=[server],
)
result = await Runner.run(
agent,
input="Find all Python files in the project and create an index.md listing them with their first docstring",
)
print(result.final_output)
Automatic Process Lifecycle Management
MCPServerStdio handles the full subprocess lifecycle:
- Startup: Spawns the process when entering
async with - Initialization: Performs the MCP handshake to discover tools
- Communication: Routes tool calls via stdin/stdout during agent execution
- Cleanup: Sends SIGTERM, waits briefly, then SIGKILL if needed on exit
If the subprocess crashes mid-conversation, the SDK raises an error that you can catch and handle:
async def resilient_agent():
server = MCPServerStdio(
name="Tools",
params={"command": "npx", "args": ["-y", "my-mcp-server"]},
)
try:
async with server:
agent = Agent(
name="Resilient Agent",
instructions="Help users with their tasks.",
mcp_servers=[server],
)
result = await Runner.run(agent, input="Do something useful")
print(result.final_output)
except Exception as e:
print(f"MCP server error: {e}")
# Fall back to agent without MCP tools, or restart the server
Environment Variables and Secrets
Pass secrets to MCP servers via environment variables, never as command-line arguments (which are visible in process listings):
server = MCPServerStdio(
name="GitHub",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"],
"PATH": os.environ["PATH"], # Inherit PATH for npx resolution
},
},
)
Note that when you specify env, it replaces the entire environment. You typically want to include PATH so the subprocess can find npx and node.
Multiple Stdio Servers
Running multiple Stdio servers is straightforward — each gets its own subprocess:
async def multi_tool_agent():
fs = MCPServerStdio(
name="Files",
params={"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]},
)
db = MCPServerStdio(
name="Database",
params={"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite",
"--db-path", "/workspace/data.db"]},
)
git = MCPServerStdio(
name="Git",
params={"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git",
"--repository", "/workspace"]},
)
async with fs, db, git:
agent = Agent(
name="Full-Stack Dev",
instructions="""You are a development assistant with access to
the filesystem, a SQLite database, and git.
Use these tools to help developers with their tasks.""",
mcp_servers=[fs, db, git],
)
result = await Runner.run(
agent,
input="Read the schema from schema.sql, create the tables in the database, then commit the changes",
)
print(result.final_output)
MCPServerStdio is the workhorse transport for local development and single-machine deployments. It is fast, simple, and requires no infrastructure beyond the subprocess itself.
Written by
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.