How to Build a Remote MCP Server: A Step-by-Step Guide to Exposing Your Database
Learn how to build and deploy a secure, remote Model Context Protocol (MCP) server to provide AI applications with access to your database, enabling scalable and accessible enterprise integrations.
What You'll Learn
- How to connect an MCP server to a remote, hosted database like PostgreSQL.
- Best practices for securely managing database credentials using environment variables.
- +3 more
Time & Difficulty
Time: 1 hour
Level: Intermediate
What You'll Need
- Python 3.10+
- uv (or pip)
- +2 more
Prerequisites
- Familiarity with Python
- Basic understanding of APIs and databases
- A free account with a cloud database provider (e.g., Neon, Supabase)
Introduction: Why Build a Remote MCP Server?
While local MCP servers are excellent for personal productivity, the true enterprise power of MCP is unlocked with remote servers. A remote server acts like a standard web API, making your data and tools securely accessible to authorized AI applications over the internet.
This approach is essential for:
- Team Collaboration: Allowing multiple users and AI agents to access a single, consistent source of data.
- Cloud Integration: Connecting to databases, services, and infrastructure hosted in the cloud.
- Scalability: Handling a high volume of requests without being tied to a single user’s machine.
- Centralized Security: Managing access and authentication from a single, controlled point.
In this guide, we will build a production-oriented remote MCP server that connects to a hosted PostgreSQL database---a foundational pattern for robust business intelligence and data analytics use cases.
📂 Complete Code Repository
The complete, production-ready code for this tutorial is available on GitHub:
Repository: https://github.com/Unlock-MCP/remote-mcp-server
This repository includes:
- Complete working server code with PostgreSQL integration
- Deployment configurations for Render and Vercel
- Database setup scripts and sample data
- Comprehensive documentation and examples
- Environment templates and security best practices
You can follow along with this guide step-by-step, or clone the repository to get started immediately.
Key Concept: Choosing the Right Transport
The core difference between a local and a remote server is the transport mechanism.
- Stdio (Standard Input/Output): Used for local servers. The client application runs the server as a subprocess on your local machine. It’s simple and secure for local files but not accessible over a network.
- Streamable HTTP: The standard for remote servers. Your MCP server runs as a web service, communicating with clients via HTTP requests, just like a modern web API. This is what we will use today.
Streamable HTTP transport uses HTTP POST requests for client-to-server communication and optional Server-Sent Events (SSE) streams for server-to-client communication, following the JSON-RPC 2.0 protocol.
Step 1: Set Up Your Python Environment
You have two options to get started:
Option A: Clone the Complete Repository
# Clone the repository with all code and examples
git clone https://github.com/Unlock-MCP/remote-mcp-server.git
cd remote-mcp-server
# Create and activate a virtual environment
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
uv add "mcp[cli]" fastapi uvicorn psycopg2-binary python-dotenv
Option B: Build from Scratch
# Create a new directory for our project
uv init remote-db-server
cd remote-db-server
# Create and activate a virtual environment
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies: mcp, a web framework (fastapi), a server (uvicorn),
# a postgresql driver, and a library for environment variables.
uv add "mcp[cli]" fastapi uvicorn psycopg2-binary python-dotenv
# Create our server file
touch server.py
Step 2: Set Up a Remote PostgreSQL Database
For a realistic remote server, we need a remote database. We’ll use PostgreSQL, a powerful open-source relational database.
-
Create a Hosted Database:
Go to a managed database provider like Neon, Supabase, or your preferred cloud provider (AWS RDS, Google Cloud SQL) and create a free PostgreSQL database.-
Get the Connection String:
Once created, find the database connection URI (or connection string). It will be a single URL that contains all the information needed to connect, looking something like this: postgresql://user:password@host:port/dbname. -
Store Credentials Securely:
CRITICAL: Never hard-code credentials in your source code. We’ll use a .env file to store the connection string locally. This file should not be committed to version control.
Create a file named .env in your project root:
# .env DATABASE_URL="your-postgresql-connection-string-goes-here"
- Populate with Sample Data:
Connect to your new database using an SQL client (like DBeaver, Postico, or a web-based client provided by your host) and run the following SQL to create and populate a products table:
CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, category TEXT, price NUMERIC(10, 2), stock INTEGER ); INSERT INTO products (name, category, price, stock) VALUES ('Laptop Pro', 'Electronics', 1499.99, 50), ('Smart Watch', 'Electronics', 299.50, 120), ('Office Chair', 'Furniture', 175.00, 80), ('Project Planner', 'Office Supplies', 25.99, 200);
-
Step 3: Build the MCP Server Logic
Now, let’s write the core MCP logic in server.py to connect to our remote database.
# server.py
import os
import json
import psycopg2
from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP
from decimal import Decimal
# Load environment variables from the .env file
load_dotenv()
# Initialize a stateless HTTP server. 'stateless_http=True' is crucial
# as it tells FastMCP not to manage sessions, leaving it to our web framework.
mcp = FastMCP(
name="CompanyDB_Postgres_Server",
description="Provides access to the company product database.",
stateless_http=True
)
DATABASE_URL = os.getenv("DATABASE_URL")
# Helper function to handle Decimal serialization in JSON
def json_default(obj):
if isinstance(obj, Decimal):
return float(obj)
raise TypeError
# Define a RESOURCE to expose the database schema
@mcp.resource("db://schema", title="Database Schema")
def get_schema() -> str:
"""Provides the database schema for the 'products' table as a string."""
# For a real application, you would query the information_schema.
# For this guide, we'll return a static DDL string for simplicity.
return """
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
category TEXT,
price NUMERIC(10, 2),
stock INTEGER
);
"""
# Define a TOOL for running read-only queries
@mcp.tool(title="Query Products DB")
def query_database(sql_query: str) -> str:
"""
Executes a read-only SQL query against the products database.
Only SELECT statements are permitted for security.
"""
if not sql_query.strip().upper().startswith("SELECT"):
return json.dumps({"error": "Security violation: Only SELECT queries are allowed."})
conn = None
try:
conn = psycopg2.connect(DATABASE_URL)
cur = conn.cursor()
cur.execute(sql_query)
column_names = [desc[0] for desc in cur.description]
results = [dict(zip(column_names, row)) for row in cur.fetchall()]
cur.close()
# Format as a JSON string for clear, structured output
return json.dumps(results, indent=2, default=json_default)
except (Exception, psycopg2.DatabaseError) as error:
return json.dumps({"error": str(error)})
finally:
if conn is not None:
conn.close()
Step 4: Expose the Server with FastAPI
The MCP logic is ready. Now we’ll use FastAPI to expose it via an HTTP endpoint.
Append this code to the bottom of your server.py file:
# server.py (continued)
from fastapi import FastAPI
# Create a FastAPI application instance
app = FastAPI(
title="UnlockMCP Remote Database Server",
description="An MCP server providing access to a company database, exposed via HTTP."
)
# Mount the MCP server's HTTP application to a path.
# All MCP communication will happen through this endpoint (e.g., /mcp).
app.mount("/mcp", mcp.streamable_http_app())
# Add a root endpoint for health checks or basic info
@app.get("/")
def read_root():
return {"message": "MCP Database Server is running. Access MCP at /mcp"}
To run your server locally for development, use uvicorn:
uvicorn server:app --host 0.0.0.0 --port 8000 --reload
Your server is now running on your local machine, but it’s connecting to your remotedatabase.
Step 5: Security - The Critical Step for Remote Servers
Exposing a server to the internet introduces security risks. While a full security implementation is beyond this guide’s scope, these principles are non-negotiable for a production environment.
Security First: Never deploy a remote MCP server in production without proper security.
-
Use HTTPS (TLS): Your server must be served over HTTPS to encrypt traffic. This is typically handled by your deployment platform or a reverse proxy.
- Implement Authentication: Protect your server from unauthorized access. The MCP specification includes a robust Authorization framework based on OAuth 2.1. The mcp Python SDK supports this, allowing you to configure FastMCP with an auth_server_provider to protect your tools.
Step 6: Deploy Your Remote Server
For your server to be truly remote and accessible to other clients, you must deploy it to a cloud hosting platform.
-
Prepare for Deployment:
Create a requirements.txt file for your hosting provider:uv pip freeze > requirements.txt
-
Choose a Hosting Platform:
Services like Render, Vercel, or Cloudflare Workers make deploying Python web applications straightforward. Follow their instructions for deploying a Python (FastAPI) application from a Git repository. -
Set Environment Variables:
During the deployment setup on your chosen platform, you will need to configure the DATABASE_URL environment variable. Set its value to your PostgreSQL connection string. This securely provides the credential to your live application without exposing it in your code.
-
Once deployed, your platform will give you a public URL (e.g., https://your-app-name.onrender.com). This is the remote address of your MCP server.
Step 7: Testing Your Deployed Server
You can test your live, deployed server with any MCP client that supports remote connections, such as the MCP Inspector.
# Replace the URL with your actual deployed application URL
npx @modelcontextprotocol/inspector https://your-app-name.onrender.com/mcp
The Inspector will connect to your globally-accessible server. You can then navigate to the Tools tab, select the query_database tool, and execute a query like SELECT * FROM products WHERE category = ‘Electronics’;.
Conclusion and Next Steps
Congratulations! You have successfully built and deployed a remote MCP server connected to a real, hosted database. You’ve learned how to:
- Connect to a remote data source like PostgreSQL
- Securely manage credentials using environment variables
- Use FastAPI to make your server available over the network
- Deploy your server to the cloud for global accessibility
🚀 Take It Further
The complete code and additional resources are available in the GitHub repository:
https://github.com/Unlock-MCP/remote-mcp-server
The repository includes:
- Deployment guides for multiple platforms
- Advanced examples with more complex queries
- Security enhancements and authentication patterns
- Database setup scripts for easy onboarding
- Contributing guidelines for community development
Using Your Server
You’ve built, secured, and deployed a remote MCP server. Now what? The final steps are to integrate it into your AI tools and share your work so others can benefit and build upon it.
Integrating with MCP Clients
Your deployed server is now ready to be used by any MCP client that supports remote Streamable HTTP connections. Here’s the typical workflow for adding it to an application like Claude Code:
-
Find Your Server’s URL: From your deployment platform (e.g., Render), get the public URL for your application, such as
https://your-mcp-server.onrender.com
. Your MCP endpoint is at the path you mounted, so the full URL ishttps://your-mcp-server.onrender.com/mcp
. -
Add it to Your Client: In your MCP client’s settings, look for an option to add a remote MCP server. You will be prompted to enter the URL from the previous step.
-
Authenticate: If you’ve implemented authentication, the client will guide you through the login process, which typically involves a browser-based OAuth flow.
Once connected, the CompanyDB_Postgres_Server
and its tools will appear in your client’s list of available integrations, ready for you and your team to use.
This foundation provides a scalable, secure pattern for exposing enterprise data and functionality to AI applications. From here, you can:
- Add More Tools: Implement tools for writing or updating data (with careful permission checks)
- Implement Full Authentication: Integrate a complete OAuth 2.1 flow to manage access
- Enhance Functionality: Add more complex business logic to your tools, connecting to other APIs or services
📚 Related Resources
- Repository: Remote MCP Server - Complete working code and examples
- MCP Documentation: Model Context Protocol - Official protocol specification
- Local MCP Example: MCP Docs Server - Local filesystem MCP server tutorial
🤝 Contributing
Found an improvement or want to add features? The repository welcomes contributions:
- Report issues and bugs
- Submit feature requests
- Contribute code improvements
- Share deployment experiences
Visit the repository to get involved!
Additional Resources
Related Guides
A Developer's Guide to MCP Security: Beyond the Basics
Centralize your understanding of MCP security with this comprehensive guide. Learn practical steps for authenticating servers, preventing prompt injection, validating URIs, and managing secrets.
Building Your First MCP Server with Python
A step-by-step tutorial on how to create and run a basic Model Context Protocol (MCP) server using the Python SDK, FastMCP.
Connect Claude to Your Business Files with MCP
Step-by-step guide to setting up Claude AI to read, analyze, and work with your business documents and spreadsheets automatically.
Want More Step-by-Step Guides?
Get weekly implementation guides and practical MCP tutorials delivered to your inbox.
Subscribe for Weekly Guides