Create and Deploy a Remote MCP Server
What is MCP?
MCP (Model Context Protocol) is a communication protocol that allows Large Language Models (LLMs) to interact with external tools and resources. Typically, AI agents integrate tools directly into their codebase. MCP changes this by enabling you to host these tools and resources on a separate application, called an MCP server. This server then handles requests from your AI assistant.
Think of it this way: your tools are like a set of functions located outside your AI assistant. When your assistant needs to perform a task, it can call one of these functions. It sends a request to the MCP server, which executes the function and sends the result back. It's like giving your AI assistant an external toolbox or a microservice to use.
You can also connect multiple MCP servers to your application, providing even more flexibility.
MCP creates a common language between AI assistants and their tools. This means that once your AI assistant is configured for MCP, it can interact with any tool or resource that supports the protocol without needing specific configurations for each one. For example, if Supabase created an MCP server for their tools, your AI assistant could simply connect and start using them without any extra setup.
In essence, MCP allows your AI assistant to use tools from an MCP server as if they were part of its own code, even though they are hosted elsewhere. This is similar to a microservice architecture for AI assistants. When you hear that an AI agent is "MCP-compatible" or "MCP-ready," it means the agent can utilize any tool or resource that supports the MCP protocol, making it more adaptable and powerful.
Your First MCP Server
Let's build a simple MCP server that exposes a few tools and resources. We'll use the mcp
Python library to create the server and make these tools accessible via HTTP.
For this example, we'll create a tool to get weather information for a city. While we're using Python, MCP supports any language.
Setting Up the Project
I highly recommend installing uv
, a fast Python package manager. However, pip
works just as well. You can find detailed installation instructions here, or use the commands below:
# on macOS or Linux
curl -LsSf [https://astral.sh/uv/install.sh](https://astral.sh/uv/install.sh) | sh
# on Windows
powershell -ExecutionPolicy ByPass -c "irm [https://astral.sh/uv/install.ps1](https://astral.sh/uv/install.ps1) | iex"
Once uv
is installed, create a new project and navigate into it:
uv init my-mcp-server
cd my-mcp-server
Now, initialize a virtual environment:
uv venv
source .venv/bin/activate # on macOS/Linux/WSL
.venv\Scripts\activate # on Windows
Next, install the mcp
library:
uv add mcp
We'll also use the Tavily API to enable our agent to search the internet:
uv add tavily-python
Creating the MCP Server
With the mcp
library installed, let's create our first MCP server. We'll define the server and its tools in a file named server.py
.
# server.py
from mcp.server.fastmcp import FastMCP
from tavily import TavilyClient
from typing import Dict
# Initialize Tavily client with your API key
# Replace "TAVILY_API_KEY" with your actual key
tavily_client = TavilyClient("TAVILY_API_KEY")
# Create an MCP server named "demo"
mcp = FastMCP("demo", host="127.0.0.1", port=8000)
# Add a tool that uses Tavily for web searches
@mcp.tool()
def web_search(query: str) -> Dict:
"""
Use this tool to search the web for information.
Args:
query: The search query.
Returns:
The search results.
"""
try:
response = tavily_client.search(query)
return response["results"]
except Exception:
return "No results found"
# Run the server
if __name__ == "__main__":
mcp.run(transport="streamable-http")
You've now created your first MCP server! You can run it using:
uv run server.py # if using uv
python server.py # if not using uv
This code creates an MCP server named "demo" and adds a web_search
tool. This tool takes a search query and returns web search results. The server is configured to run using the streamable-http
transport protocol.
How to Run the MCP Server?
To interact with your MCP server, you need an MCP client. This is an application (an agent) that's compatible with the MCP protocol and can communicate with your server. You can find a list of popular MCP-compatible applications on the MCP website.
For this tutorial, we'll use Cursor, a coding assistant that supports tool usage via MCP. You can download Cursor from here.
Once Cursor is installed, add a new MCP server by going to "Chat Settings" and then "MCP Servers." Click "Add MCP Server" and enter the following details:
mcpServers: {
"tavily": {
"url": "http://localhost:8000/mcp/"
}
}
Important: Make sure to include the trailing /
in the URL. Without it, your server might not recognize the endpoint.
After adding the server, toggle the MCP server setting (disable then re-enable) in your Cursor chat settings to apply the changes. You should then see that your server has one tool available: web_search
.
How to Debug the MCP Server?
Debugging an MCP server can be challenging because it's not a standard web server that you can access directly via a browser.
However, Anthropic provides the MCP Inspector, a web interface designed to help you inspect and debug your MCP server. It allows you to interact with your server and view requests and responses in real-time.
To run the MCP Inspector, you can use either uv
or npx
. If you have uv
installed, first ensure you have the mcp[cli]
extra installed:
uv add 'mcp[cli]'
# Then run the inspector
uv run mcp dev server.py # Replace server.py with your server file name
If you prefer npx
, use this command:
npx @modelcontextprotocol/inspector python server.py
Now, open your web browser and go to http://localhost:6274
to access the MCP Inspector. If you're using the HTTP transport, you might need an MCP proxy token. This token is generated instantly when you run the inspector and will appear in your terminal as a URL parameter, like this:
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=your_token_here
You can now interact with your MCP server through the inspector, sending requests and seeing real-time responses. This is an excellent way to debug your server and understand its behavior.
Important Note: When using HTTP transport, the MCP Inspector might automatically try to connect to http://127.0.1:8000/sse
, which is not the correct URL for our server in recent versions. To connect the inspector to your server, you'll need to specify the correct URL, which should be at /mcp
. Later, we'll discuss how to change this default URL if you need a different path or port.
Mount Your MCP Server to a FastAPI Server
You have two primary ways to configure the host and path for your MCP server. The first is to pass the host
and port
parameters directly to the FastMCP
constructor, as we did earlier:
# server.py
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("demo", host="127.0.0.1", port=8000)
# Add a simple tool
# [...]
# Run the server
if __name__ == "__main__":
mcp.run(transport="streamable-http")
The second, more flexible approach, is to mount the MCP server onto a FastAPI application. This gives you greater control over the HTTP server and its configuration:
# server.py
from mcp.server.fastmcp import FastMCP
from fastapi import FastAPI
import uvicorn
import argparse # Needed for args.host and args.port
# Create an MCP server
mcp = FastMCP("demo")
# Create a FastAPI application
app = FastAPI(title="demo", lifespan=lambda app: mcp.session_manager.run())
app.mount("/search-server", mcp.streamable_http_app())
if __name__ == "__main__":
# Use argparse to get host and port from command line arguments
parser = argparse.ArgumentParser(description="Run MCP server with FastAPI")
parser.add_argument("--host", type=str, default="127.0.0.1", help="Host IP address")
parser.add_argument("--port", type=int, default=8000, help="Port number")
args = parser.parse_args()
uvicorn.run(app, host=args.host, port=args.port, log_level="info")
This code creates a FastAPI application and mounts the MCP server at the /search-server
path. You can then run this application using uvicorn
or any other ASGI server:
uvicorn server:app --host 127.0.0.1 --port 8000 # If using uvicorn directly
# Or, if you've added argparse and the uv run command is configured to pass these
# uv run server.py --mode fastapi --host 127.0.0.1 --port 8000
Once running, your MCP server will be accessible at http://127.0.0.1:8000/search-server/mcp/
. For example, to connect to an echo
tool, you would use http://127.0.0.1:8000/search-server/echo/mcp/
.
Remember: Always add the trailing /
to the path for proper communication with your MCP client.
Multiple MCP Servers in a FastAPI App
You can mount multiple MCP servers within a single FastAPI application by creating separate FastMCP
instances and mounting them on different paths.
Here’s an example with two separate MCP servers for "echo" and "math" functionalities:
# echo.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP(name="EchoServer", stateless_http=True)
@mcp.tool(description="A simple echo tool")
def echo(message: str) -> str:
return f"Echo: {message}"
# math.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP(name="MathServer", stateless_http=True)
@mcp.tool(description="A simple add tool")
def add_two(n: int) -> int:
return n + 2
# main.py
import contextlib
from fastapi import FastAPI
from .echo import mcp as echo_mcp # Import the mcp instance from echo.py
from .math import mcp as math_mcp # Import the mcp instance from math.py
# Create a combined lifespan to manage both session managers
@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
async with contextlib.AsyncExitStack() as stack:
await stack.enter_async_context(echo_mcp.session_manager.run())
await stack.enter_async_context(math_mcp.session_manager.run())
yield
app = FastAPI(lifespan=lifespan)
app.mount("/echo", echo_mcp.streamable_http_app())
app.mount("/math", math_mcp.streamable_http_app())
This setup creates two MCP servers, one for an echo tool and another for a math tool. Both servers are then mounted on distinct paths (/echo
and /math
) within a single FastAPI application.
Source: Python SDK Documentation
Conclusion
This guide walked you through setting up an MCP server using the Python SDK. We covered creating a basic server with external tools, connecting an MCP client like Cursor, debugging with the MCP Inspector, and even mounting multiple MCP servers within a single FastAPI application.