Local vs Remote MCP Servers: When to Use Each
Compare local (stdio) and remote (SSE/HTTP) MCP server deployments. Learn when to use each approach with practical examples and trade-offs.
Local vs Remote MCP Servers
Local MCP servers run on your machine using the stdio transport, while remote MCP servers run on external infrastructure using HTTP-based transports. The choice between them affects security, performance, scalability, and the types of tools you can build. Most MCP deployments use a combination of both.
This guide provides a detailed comparison to help you choose the right deployment model for each use case.
Quick Comparison
| Factor | Local (stdio) | Remote (SSE / Streamable HTTP) |
|---|---|---|
| Where it runs | User's machine (child process) | Cloud server, VM, or container |
| Transport | stdin/stdout | HTTP + SSE or Streamable HTTP |
| Authentication | OS process permissions | OAuth 2.1 |
| Setup | Command + args in config | URL + auth configuration |
| Latency | ~1-5ms protocol overhead | ~50-200ms network overhead |
| Concurrent users | Single user | Multiple users |
| Data locality | Data stays on machine | Data traverses network |
| Server lifecycle | Tied to host app | Independent |
| Scaling | Not applicable | Horizontally scalable |
| Network required | No | Yes |
| Best for | File access, local tools, dev workflows | Cloud APIs, SaaS, shared tools |
Local MCP Servers (stdio Transport)
How Local Servers Work
When you configure a local MCP server, the host application spawns it as a child process. Communication happens through the process's standard input (stdin) and standard output (stdout):
┌─────────────────────────────┐
│ Host Application │
│ (Claude Desktop, Cursor) │
│ │
│ ┌───────────────────────┐ │
│ │ MCP Client │ │
│ │ │ │
│ │ writes to → stdin │ │
│ │ reads from ← stdout │ │
│ └───────────┬───────────┘ │
│ │ │
└──────────────┼──────────────┘
│ (process pipes)
┌──────────────┼──────────────┐
│ ┌───────────▼───────────┐ │
│ │ MCP Server │ │
│ │ (Child Process) │ │
│ │ │ │
│ │ reads from ← stdin │ │
│ │ writes to → stdout │ │
│ │ logs to → stderr │ │
│ └───────────────────────┘ │
│ │
│ Local Machine │
└─────────────────────────────┘
Configuration
Local servers are configured with a command and arguments:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/alice/projects"],
},
"git": {
"command": "uvx",
"args": ["mcp-server-git", "--repository", "/Users/alice/projects/myapp"]
},
"sqlite": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite", "/Users/alice/data/app.db"]
}
}
}
Common launch patterns:
| Pattern | Command | Use Case |
|---|---|---|
| npx | npx -y @scope/package | Node.js servers from npm |
| uvx | uvx package-name | Python servers from PyPI |
| node | node /path/to/server.js | Local Node.js servers |
| python | python /path/to/server.py | Local Python servers |
| docker | docker run -i image | Containerized local servers |
| binary | /path/to/binary | Compiled servers (Go, Rust) |
Advantages of Local Servers
1. Data stays on your machine. No file contents, database queries, or sensitive information crosses the network. This is critical for:
- Source code access in development environments
- Local database queries with sensitive data
- Personal documents and files
- Any scenario with strict data residency requirements
2. Zero network configuration. No URLs, no DNS, no firewalls, no TLS certificates. The server runs as a local process, and communication happens through OS pipes.
3. Minimal latency. The protocol overhead for a stdio message is typically under 5 milliseconds. The bottleneck is always the tool execution itself, not the transport.
4. No authentication overhead. The server inherits the user's OS permissions. No OAuth flows, no token management, no credential storage.
5. Simple development workflow. For building and testing MCP servers:
# Start a server directly for testing
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | node my-server.js
# Or use the MCP Inspector
npx @modelcontextprotocol/inspector node my-server.js
Limitations of Local Servers
Single-user only. A stdio server is tied to one host process. You cannot share it across team members or applications on different machines.
Lifecycle coupling. When the host application closes, all its local servers are terminated. Conversely, if a server crashes, the host must restart it.
No horizontal scaling. Each user runs their own server instance. If a tool requires significant compute, it uses the user's local resources.
Platform dependencies. Local servers must be compatible with the user's operating system. A server built for Linux may not run on Windows without modification.
Example: Building a Local File Server
from mcp.server.fastmcp import FastMCP
from pathlib import Path
import os
mcp = FastMCP("secure-file-server")
# Restrict to a specific directory
ALLOWED_DIR = Path(os.environ.get("ALLOWED_DIR", os.path.expanduser("~")))
def validate_path(path: str) -> Path:
"""Ensure the path is within the allowed directory."""
resolved = (ALLOWED_DIR / path).resolve()
if not str(resolved).startswith(str(ALLOWED_DIR.resolve())):
raise ValueError(f"Access denied: path must be within {ALLOWED_DIR}")
return resolved
@mcp.tool()
def read_file(path: str) -> str:
"""Read the contents of a file.
Args:
path: Relative path within the allowed directory
"""
file_path = validate_path(path)
if not file_path.exists():
raise FileNotFoundError(f"File not found: {path}")
return file_path.read_text()
@mcp.tool()
def list_directory(path: str = ".") -> str:
"""List files and directories at the given path.
Args:
path: Relative path within the allowed directory
"""
dir_path = validate_path(path)
if not dir_path.is_dir():
raise ValueError(f"Not a directory: {path}")
entries = sorted(dir_path.iterdir())
return "\n".join(
f"{'[DIR] ' if e.is_dir() else ' '}{e.name}"
for e in entries
)
@mcp.tool()
def search_files(query: str, file_pattern: str = "*") -> str:
"""Search for files containing a text pattern.
Args:
query: Text to search for
file_pattern: Glob pattern for file names (e.g., '*.py', '*.ts')
"""
matches = []
for file_path in ALLOWED_DIR.rglob(file_pattern):
if file_path.is_file():
try:
content = file_path.read_text()
if query.lower() in content.lower():
rel_path = file_path.relative_to(ALLOWED_DIR)
matches.append(str(rel_path))
except (UnicodeDecodeError, PermissionError):
continue
if not matches:
return f"No files matching '{file_pattern}' contain '{query}'"
return f"Found {len(matches)} files:\n" + "\n".join(matches[:50])
if __name__ == "__main__":
mcp.run()
Remote MCP Servers (HTTP-Based Transports)
How Remote Servers Work
Remote MCP servers are standalone services accessible over HTTP. They support two transport mechanisms:
HTTP with SSE (Server-Sent Events)
The original remote transport from the initial MCP specification:
┌──────────────┐ ┌──────────────┐
│ MCP Client │ HTTP POST │ Remote │
│ (in Host) │ ──────────────────►│ MCP Server │
│ │ │ │
│ │ SSE stream │ │
│ │ ◄──────────────────│ │
└──────────────┘ └──────────────┘
POST /mcp/message → Client sends requests
GET /mcp/sse → Client subscribes to server events
Streamable HTTP
The modern transport added in the March 2025 specification revision:
┌──────────────┐ ┌──────────────┐
│ MCP Client │ HTTP POST │ Remote │
│ (in Host) │ ──────────────────►│ MCP Server │
│ │ │ │
│ │ Streaming resp │ │
│ │ ◄──────────────────│ │
└──────────────┘ └──────────────┘
POST /mcp → Client sends requests; server can respond
with regular HTTP or upgrade to streaming
Configuration
Remote servers are configured with a URL and authentication:
{
"mcpServers": {
"team-tools": {
"url": "https://mcp.mycompany.com/sse",
"transport": "sse",
"headers": {
"Authorization": "Bearer <token>"
}
},
"cloud-api": {
"url": "https://api.example.com/mcp",
"transport": "streamable-http",
"oauth": {
"authorizationUrl": "https://auth.example.com/authorize",
"tokenUrl": "https://auth.example.com/token",
"clientId": "my-app-id",
"scopes": ["read", "write"]
}
}
}
}
Advantages of Remote Servers
1. Multi-user access. A single remote server can serve multiple clients simultaneously. This is essential for:
- Team-shared tools and databases
- Organization-wide service integrations
- SaaS vendor-hosted MCP endpoints
2. Independent lifecycle. The server runs independently of any client. It can be:
- Deployed, updated, and scaled without affecting clients
- Monitored with standard observability tools
- Maintained by a dedicated team
3. Centralized management. For enterprises, remote servers enable:
- Centralized authentication and authorization
- Audit logging of all tool usage
- Rate limiting and quota management
- Compliance enforcement
4. Scalability. Remote servers can be:
- Horizontally scaled behind load balancers
- Deployed across multiple regions
- Auto-scaled based on demand
- Container-orchestrated with Kubernetes
5. Richer compute. The server can leverage cloud resources:
- GPU instances for ML-powered tools
- Large memory instances for data processing
- Database connections to cloud-hosted databases
- Direct access to cloud services (S3, BigQuery, etc.)
Limitations of Remote Servers
Network dependency. Requires a stable network connection. Not suitable for offline or air-gapped environments.
Latency. Network round-trips add 50-200ms per request. For tools that make multiple API calls, this compounds.
Authentication complexity. OAuth 2.1 flows require implementation of authorization endpoints, token management, and refresh logic.
Infrastructure costs. Running remote servers requires hosting, monitoring, and maintenance infrastructure.
Security surface. Network exposure increases the attack surface compared to local servers. Requires TLS, input validation, rate limiting, and other defenses.
Example: Building a Remote Server
TypeScript (with SSE transport):
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
import { z } from "zod";
const app = express();
const server = new McpServer({
name: "team-analytics",
version: "1.0.0",
});
// Register tools
server.tool(
"query_analytics",
"Query the team analytics database",
{
metric: z.string().describe("Metric name (pageviews, signups, revenue)"),
period: z.string().describe("Time period (today, this_week, this_month)"),
groupBy: z.string().optional().describe("Group by dimension"),
},
async ({ metric, period, groupBy }) => {
const results = await analyticsDB.query(metric, period, groupBy);
return {
content: [{ type: "text", text: formatAnalyticsResults(results) }],
};
}
);
// SSE endpoint
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/message", res);
await server.connect(transport);
});
// Message endpoint
app.post("/message", async (req, res) => {
// Handle incoming MCP messages
await transport.handleMessage(req, res);
});
app.listen(3001, () => {
console.log("MCP server running on http://localhost:3001");
});
Python (with SSE transport):
from mcp.server.fastmcp import FastMCP
import uvicorn
mcp = FastMCP("team-analytics")
@mcp.tool()
async def query_analytics(
metric: str,
period: str = "this_week",
group_by: str | None = None
) -> str:
"""Query the team analytics database.
Args:
metric: Metric name (pageviews, signups, revenue)
period: Time period (today, this_week, this_month)
group_by: Optional dimension to group by
"""
results = await analytics_db.query(metric, period, group_by)
return format_results(results)
if __name__ == "__main__":
# Run with SSE transport for remote access
mcp.run(transport="sse", host="0.0.0.0", port=3001)
Decision Framework
Use This Flowchart
Does the tool need to access local files or resources?
├── Yes → LOCAL (stdio)
│
└── No → Does the tool need to serve multiple users?
├── Yes → REMOTE (HTTP)
│
└── No → Does the tool wrap a cloud API?
├── Yes → Either works; REMOTE for shared access
│
└── No → Does data need to stay on the user's machine?
├── Yes → LOCAL (stdio)
└── No → Either works; evaluate by preference
Decision Matrix by Use Case
| Use Case | Recommended | Rationale |
|---|---|---|
| Filesystem access | Local | Data locality, security |
| Local Git operations | Local | Filesystem access needed |
| Local database (SQLite, dev Postgres) | Local | Data stays on machine |
| Cloud database (managed Postgres, MongoDB Atlas) | Remote | Cloud-to-cloud is faster |
| GitHub/GitLab API | Either | Local works fine; remote enables team sharing |
| Slack/Teams integration | Remote | Cloud API, multi-user value |
| AWS/GCP/Azure management | Remote | Cloud services, shared access |
| Browser automation | Local | Needs local browser process |
| Code execution sandbox | Local | Security isolation via containers |
| Company-internal APIs | Remote | Shared access, centralized auth |
| Personal productivity (calendar, email) | Either | Personal data suggests local; convenience suggests remote |
| Enterprise data warehouse | Remote | Centralized, multi-user, governed |
Hybrid Deployments
Most production MCP setups use both local and remote servers:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/alice/code"]
},
"git": {
"command": "uvx",
"args": ["mcp-server-git", "--repository", "/Users/alice/code/myapp"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxx" }
},
"team-db": {
"url": "https://mcp.mycompany.com/database",
"transport": "sse",
"headers": { "Authorization": "Bearer xxx" }
},
"slack": {
"url": "https://mcp.mycompany.com/slack",
"transport": "sse",
"headers": { "Authorization": "Bearer xxx" }
}
}
}
In this configuration:
- filesystem and git are local (access user's code)
- github is local but calls a remote API (personal token)
- team-db and slack are remote (shared team resources)
Security Comparison
Local Server Security Model
┌──────────────────────────────────────────────┐
│ User's Machine │
│ │
│ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Host App │ │ MCP Server │ │
│ │ (User's │◄──►│ (Child Process) │ │
│ │ process) │ │ Same user perms │ │
│ └─────────────┘ └─────────────────────┘ │
│ │
│ Trust: OS process isolation │
│ Auth: None needed (same user) │
│ Data: Never leaves machine │
└──────────────────────────────────────────────┘
Key considerations:
- The server runs with the same permissions as the user
- No network exposure (no attack surface from the network)
- Trust depends on the server code itself (only install trusted servers)
- Host should enforce principle of least privilege (restrict directories, commands)
Remote Server Security Model
┌─────────────────┐ ┌─────────────────────────────┐
│ User's Machine │ │ Remote Infrastructure │
│ │ TLS │ │
│ ┌───────────┐ │◄───────►│ ┌─────┐ ┌────────────┐ │
│ │ Host App │ │ │ │Auth │ │ MCP Server │ │
│ │ + Client │ │ │ │(OAuth│───►│ │ │
│ └───────────┘ │ │ │2.1) │ │ + Tools │ │
│ │ │ └─────┘ └────────────┘ │
│ │ │ │
└─────────────────┘ │ Trust: OAuth + TLS │
│ Auth: OAuth 2.1 tokens │
│ Data: Encrypted in transit │
└─────────────────────────────┘
Key considerations:
- OAuth 2.1 provides industry-standard authentication
- TLS encrypts all data in transit
- Server must validate all inputs (defense in depth)
- Rate limiting prevents abuse
- Audit logging enables compliance
- Server runs in a controlled environment (containers, cloud security)
For a comprehensive guide to MCP security, see MCP Security Model.
Performance Comparison
Latency Breakdown
| Phase | Local (stdio) | Remote (SSE) |
|---|---|---|
| Transport overhead | ~1ms | ~50-100ms |
| Serialization | ~1ms | ~1ms |
| Tool execution | Variable | Variable |
| Response formatting | ~1ms | ~1ms |
| Total overhead | ~3ms | ~52-102ms |
For a tool that queries a database taking 200ms:
- Local total: ~203ms
- Remote total: ~252-302ms
The difference is negligible for most use cases. The tool execution time dominates.
Throughput
| Factor | Local | Remote |
|---|---|---|
| Concurrent tool calls | Limited by local CPU/memory | Scalable with infrastructure |
| Requests per second | Hundreds (per process) | Thousands (with scaling) |
| Bottleneck | Tool execution | Network or tool execution |
Reliability
| Factor | Local | Remote |
|---|---|---|
| Single point of failure | User's machine | Server infrastructure |
| Crash recovery | Host restarts process | Auto-restart, health checks |
| Network dependency | None | Full dependency |
| Offline capability | Full | None |
Migration: Local to Remote
When to Migrate
Consider migrating a local server to remote when:
- Multiple team members need the same tool
- You need centralized logging and monitoring
- The tool's compute requirements exceed local capacity
- You want to manage access control centrally
Migration Steps
- Containerize the server: Package it in Docker
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3001
CMD ["node", "index.js", "--transport", "sse", "--port", "3001"]
-
Add authentication: Implement OAuth 2.1 or token-based auth
-
Deploy: Push to your cloud platform (AWS, GCP, Azure, etc.)
-
Update client configuration: Change from command to URL
// Before (local)
{
"my-server": {
"command": "node",
"args": ["./server.js"]
}
}
// After (remote)
{
"my-server": {
"url": "https://mcp.mycompany.com/my-server/sse",
"transport": "sse",
"headers": {
"Authorization": "Bearer <token>"
}
}
}
For a complete deployment guide, see Deploying Remote MCP Servers.
Summary
The choice between local and remote MCP servers depends on data locality requirements, user count, security constraints, and infrastructure preferences. Local servers excel at file access, development tools, and single-user scenarios. Remote servers excel at cloud integrations, team collaboration, and enterprise deployments.
Most real-world MCP configurations combine both: local servers for filesystem and development tools, remote servers for cloud APIs and shared resources. The protocol's transport-agnostic design makes this hybrid approach seamless.
Continue learning:
- MCP Architecture -- Full architecture breakdown
- MCP Security Model -- Security for both deployment models
- Deploying Remote MCP Servers -- Production deployment guide
- Browse MCP Servers -- Find servers for your use case
Frequently Asked Questions
What is a local MCP server?
A local MCP server runs on the same machine as the host AI application, communicating through standard input/output (stdio). The host spawns the server as a child process and exchanges JSON-RPC messages through stdin/stdout. Local servers are ideal for filesystem access, local development tools, and any scenario where data should not leave the user's machine.
What is a remote MCP server?
A remote MCP server runs on a different machine (typically a cloud server) and communicates with clients over HTTP using Server-Sent Events (SSE) or the Streamable HTTP transport. Remote servers require authentication (OAuth 2.1), can serve multiple clients simultaneously, and are ideal for SaaS integrations, shared team tools, and cloud-hosted services.
When should I use a local MCP server?
Use a local server when you need to access local files and directories, when data should stay on the user's machine for security or privacy reasons, when you want zero network configuration, when latency must be minimal, or when the tool is specific to a single user's environment (like their code editor or local database).
When should I use a remote MCP server?
Use a remote server when the tool wraps a cloud API or SaaS service, when multiple users need to share the same server, when the server requires significant compute resources, when you want the server to run independently of any client, or when you need centralized management, logging, and monitoring.
Can I run the same MCP server locally and remotely?
Yes. Many MCP server implementations support both transport modes. The server logic remains the same — only the transport layer changes. You can develop locally with stdio for testing and deploy the same server with HTTP transport for production. The official SDKs make it straightforward to support multiple transports.
What is the latency difference between local and remote MCP servers?
Local servers (stdio) add minimal overhead — typically under 5 milliseconds for the protocol layer. Remote servers add network latency, typically 50-200 milliseconds depending on the server location. In practice, the tool execution time (API calls, database queries) dominates overall latency in both cases.
How does authentication differ between local and remote servers?
Local servers inherit the user's operating system permissions — no additional authentication is needed. Remote servers use OAuth 2.1 for authentication, which involves redirect-based authorization flows, access tokens, and token refresh. The host application manages the OAuth flow and includes the token with each request.
Is it safe to run local MCP servers?
Local servers run with the same permissions as the user's process. This means they can access anything the user can access. Safety depends on trusting the server code — only install servers from trusted sources, review the permissions they require, and use filesystem servers with restricted directory access. The host application should prompt for consent before executing tools with side effects.
Related Guides
A comprehensive breakdown of the MCP architecture — how clients, servers, hosts, and transports work together to enable AI-tool communication.
The complete guide to MCP security — OAuth 2.1 authentication, permission models, transport security, and securing your MCP deployments.
Production deployment guide for remote MCP servers — Docker containerization, cloud hosting (AWS, GCP, Azure), scaling, and monitoring.