HOW-TO · RAG
How to Connect MCP Client to Remote MCP Server
Target environment
Ubuntu 24.04 · Ollama 0.4.x
PREREQUISITES
MCP server deployed with network access, Python 3.10+
What this does
MCP clients connect to remote servers over SSE (Server-Sent Events) or WebSocket transports, enabling LLMs to call tools hosted on different machines or services.
Steps
- Run an MCP server with SSE transport. Expose tools over HTTP.
from mcp.server import Server
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route
server = Server("remote-tools")
@server.tool("hello")
async def hello(name: str) -> str:
return f"Hello, {name}!"
sse = SseServerTransport("/messages")
async def handle_sse(request):
async with sse.connect_sse(request.scope, request.receive, request._send) as (read, write):
await server.run(read, write)
app = Starlette(routes=[
Route("/sse", endpoint=handle_sse),
Route("/messages", endpoint=sse.handle_post_message, methods=["POST"]),
])
- Run the server with Uvicorn.
pip install uvicorn
uvicorn app:app --host 0.0.0.0 --port 8000
- Connect a client to the remote server. Use
SseClientfor SSE transport.
from mcp.client import sse_client
from mcp import Client
async def connect_remote():
async with sse_client("http://localhost:8000/sse") as (read, write):
client = Client(read, write)
tools = await client.list_tools()
for t in tools:
print(f"Remote tool: {t.name}: {t.description}")
result = await client.call_tool("hello", {"name": "Remote"})
print(result.content[0].text)
- Handle authentication. Pass tokens via headers or query parameters.
async def connect_with_auth():
headers = {"Authorization": "Bearer sk-my-token"}
async with sse_client(
"http://localhost:8000/sse",
headers=headers
) as (read, write):
client = Client(read, write)
tools = await client.list_tools()
print(f"Authenticated. Found {len(tools)} tools.")
- Reconnection on disconnection. Wrap the client in a retry loop.
import asyncio
async def persistent_client(url: str, max_retries=3):
for attempt in range(max_retries):
try:
async with sse_client(url) as (read, write):
client = Client(read, write)
yield client
break
except Exception as e:
if attempt == max_retries - 1:
raise
await asyncio.sleep(2 ** attempt)
Verification
curl -N http://localhost:8000/sse
# Expected: SSE stream with endpoint event
Common failures
- CORS errors with browser clients. If the client runs in a browser, the server must include CORS headers. Use Starlette middleware.
- SSE endpoint not found. The client URL must point to the exact SSE route (e.g.,
/sse). A trailing slash mismatch causes 404. - Connection timeout. Remote servers behind firewalls or NAT may drop idle SSE connections. Configure keepalive or use WebSocket transport instead.
- Version mismatch - The installed package or runtime differs from the command shown; check the version first and rerun the smallest verification command.
- Local environment drift - Another service, virtual environment, model, or path is being used; print the active binary path and configuration before changing the guide steps.
Related guides
- How to Set Up MCP Server with Standard Tools
- How to Create Custom MCP Tools for Your Application