Skip to main content
intermediate Featured
Difficulty: 4/5
Published: 6/25/2025
By: UnlockMCP Team

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)
remote-server database api security python fastapi postgresql deployment

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 NeonSupabase, 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 RenderVercel, 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:

  1. 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 is https://your-mcp-server.onrender.com/mcp.

  2. 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.

  3. 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

🤝 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!

Related Guides

Want More Step-by-Step Guides?

Get weekly implementation guides and practical MCP tutorials delivered to your inbox.

Subscribe for Weekly Guides