tutorials

How to Build and Deploy an MCP Server (2026)

Apigene Team
16 min read
How to Build and Deploy an MCP Server (2026)

Building an MCP server is one of the fastest ways to give AI agents real capabilities, from querying databases to calling APIs to managing infrastructure. But the official docs stop at "hello world," and most tutorials skip the hard parts: authentication, transport choices, secret management, and deployment.

This guide covers the full journey. You will build your first MCP server, test it locally, deploy it to production, and learn the pitfalls that break real implementations.

Key Takeaways

For busy engineering leads, what 46+ developer discussions taught us:

  • STDIO transport breaks under concurrent load. Teams consistently report scaling failures when they move past single-user local setups. Pick Streamable HTTP from the start if you plan to go remote.
  • OAuth is the #1 blocker for production MCP servers. One developer spent weeks debugging auth flows and called the experience "painful due to weak visibility." Plan your auth strategy before writing your first tool.
  • Secret sprawl is a real threat. Developers report that "secrets end up everywhere (env vars, configs, copied tokens)" once they start connecting multiple tools. Centralize credentials early.
  • The "100 MCP servers" approach creates management overhead. Teams that deploy individual servers per tool quickly hit auth fragmentation, debugging difficulty, and zero observability. A gateway pattern solves this.

What Is an MCP Server and Why Build One

If you are asking "what is MCP server," here is the short answer. An MCP server is a lightweight program that exposes tools, resources, and prompts to AI agents through the Model Context Protocol. It acts as the bridge between what an AI model can reason about and what it can actually do, turning natural language requests into real API calls, database queries, or system commands.

The MCP server architecture follows a client-server pattern. An MCP host (like Claude Desktop, Cursor, or your own application) connects to one or more MCP servers through a transport layer. Each server declares its capabilities, and the host's AI model decides when and how to use them.

Here is what that looks like in practice:

AI Host (Claude/Cursor) → MCP Client → Transport (STDIO/HTTP) → MCP Server → Your APIs/DBs

Three core primitives make up every MCP server:

  • Tools are functions the AI can call. A tool might query a database, send an email, or deploy a container. The model sees the tool's name, description, and input schema, then decides when to invoke it.
  • Resources are read-only data the AI can access. Think of them as files or endpoints the model can pull context from, like documentation pages or config files.
  • Prompts are reusable templates that guide the model's behavior for specific tasks.

You should build an MCP server when you want AI agents to interact with systems you control. If you are building AI-powered developer tools, internal automation, or customer-facing agents that need to read and write real data, MCP gives you a standardized way to expose those capabilities.

Choose Your Language and SDK

The official MCP specification supports multiple languages. Your choice depends on your team's stack and what you are building.

Python (FastMCP)

Python is the most popular choice for MCP server development. The official mcp Python SDK includes FastMCP, a high-level framework that handles transport, serialization, and tool registration with decorators.

from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("my-server")
 
@mcp.tool()
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    return f"Weather in {city}: 72°F, sunny"

FastMCP works well for rapid prototyping and production. The MCP server Python SDK supports STDIO and Streamable HTTP transports out of the box. If you want to create an MCP server in Python, this is where to start. Check the official MCP server documentation for the full API reference.

TypeScript (Official SDK)

The TypeScript MCP server SDK (@modelcontextprotocol/sdk) is the reference implementation. It gives you more control over the server lifecycle and transport configuration.

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 
const server = new McpServer({ name: "my-server", version: "1.0.0" });
 
server.tool("get_weather", { city: z.string() }, async ({ city }) => ({
  content: [{ type: "text", text: `Weather in ${city}: 72°F, sunny` }]
}));
 
const transport = new StdioServerTransport();
await server.connect(transport);

If you want to create an MCP server in TypeScript, this SDK is your starting point. TypeScript is a strong choice if your backend is already Node.js or if you plan to deploy on Cloudflare Workers or Vercel.

Stop Building MCP Integrations From Scratch.

  • Any API, one line of code — connect to ChatGPT, Claude, and Cursor without writing custom MCP servers
  • Visual UI in the chat — render interactive components, not just text dumps. Charts, forms, dashboards.
  • 70% fewer tokens — dynamic tool loading and output compression so your agents stay fast and cheap

Other Languages

The MCP ecosystem now includes SDKs for C#, Java, Go, Kotlin, and Rust. These are at varying levels of maturity:

LanguageSDKMaturityBest For
Pythonmcp (FastMCP)Production-readyData tools, API integrations, rapid prototyping
TypeScript@modelcontextprotocol/sdkProduction-readyWeb services, serverless deployment
C#ModelContextProtocolStable.NET enterprise environments
Javamcp-java-sdkGrowingEnterprise, Spring Boot apps
Gomcp-goEarlyInfrastructure tools, CLIs

Build Your First MCP Server: Step-by-Step

This MCP server tutorial for beginners walks through building a working server from scratch. We will use Python with FastMCP since it requires the least boilerplate.

Set Up Your Environment

Create a new project directory and install the MCP SDK:

mkdir my-mcp-server && cd my-mcp-server
python -m venv .venv && source .venv/bin/activate
pip install "mcp[cli]"

The mcp[cli] package includes the MCP Inspector, which you will use to test your server without connecting it to Claude or Cursor.

Define Your Tools

Create a file called server.py. Start with a simple tool that your AI agent can call:

from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("demo-server")
 
@mcp.tool()
def search_docs(query: str, max_results: int = 5) -> str:
    """Search documentation for relevant articles.
    
    Args:
        query: Search query string
        max_results: Maximum number of results to return
    """
    # Replace with your actual search logic
    return f"Found {max_results} results for '{query}'"
 
@mcp.tool()
def create_ticket(title: str, description: str, priority: str = "medium") -> str:
    """Create a support ticket in the system.
    
    Args:
        title: Ticket title
        description: Detailed description of the issue
        priority: Priority level (low, medium, high)
    """
    # Replace with your actual ticket creation logic
    return f"Created ticket: {title} (priority: {priority})"

Two things matter here. First, the docstring is critical. The AI model reads it to decide when to call your tool. Write it like you are explaining the function to a colleague. Second, type hints on parameters become the tool's input schema. The model uses these to validate its own calls.

Add Resources and Prompts

Resources give the AI read-only access to data. Prompts provide reusable templates:

@mcp.resource("docs://api-reference")
def get_api_docs() -> str:
    """Return the current API reference documentation."""
    return open("api-docs.md").read()
 
@mcp.prompt()
def troubleshoot(error_message: str) -> str:
    """Guide the AI through troubleshooting a specific error."""
    return f"Analyze this error and suggest fixes: {error_message}"

Wire Up the Transport

For local development, STDIO is the simplest transport. Add this to the bottom of server.py:

if __name__ == "__main__":
    mcp.run(transport="stdio")

Run and test it:

mcp dev server.py

This launches the MCP Inspector in your browser. You can see your tools, call them with test inputs, and verify the responses before connecting to any AI host.

What Developers Actually Struggle With

We analyzed over 46 discussions where developers share their experiences building MCP servers. The setup process trips people up in predictable ways.

The most common stumbling block is tool descriptions. One engineering lead shared that their team "spent more time rewriting docstrings than writing actual tool logic" because vague descriptions caused the AI to call the wrong tool or pass incorrect parameters. The fix: write descriptions that include when NOT to use the tool, not just when to use it.

Environment configuration is another friction point. Developers consistently report confusion about where config files go, how environment variables get passed to the server process, and why tools that work in the Inspector fail in Claude Desktop. The root cause is usually a PATH mismatch between your terminal environment and the MCP host's process environment.

FindingWhat developers reportedFrequency
Tool description quality"Spent more time on docstrings than tool logic"8 of 46 threads
Environment config confusionPATH/env var mismatches between Inspector and host6 of 46 threads
Transport mismatchServer runs on STDIO but host expects HTTP5 of 46 threads
Missing error handlingTools crash silently, model retries endlessly4 of 46 threads

MCP Server Examples That Actually Work

The best way to learn is from working code. Here are three MCP server examples at increasing complexity.

The Weather Server (Hello World)

Every mcp server tutorial starts here. This simple MCP server example calls the National Weather Service API:

import httpx
from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("weather")
 
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location."""
    async with httpx.AsyncClient() as client:
        point = await client.get(
            f"https://api.weather.gov/points/{latitude},{longitude}"
        )
        forecast_url = point.json()["properties"]["forecast"]
        forecast = await client.get(forecast_url)
        periods = forecast.json()["properties"]["periods"][:3]
        return "\n".join(
            f"{p['name']}: {p['detailedForecast']}" for p in periods
        )

This weather MCP server is useful for learning, but it only covers read-only API calls with no authentication.

Database Query Server

A more practical python MCP server example that queries PostgreSQL:

import asyncpg
from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("db-server")
POOL = None
 
@mcp.tool()
async def query_customers(status: str = "active", limit: int = 10) -> str:
    """Query customers by status. Returns name, email, and signup date."""
    global POOL
    if not POOL:
        POOL = await asyncpg.create_pool(os.environ["DATABASE_URL"])
    rows = await POOL.fetch(
        "SELECT name, email, created_at FROM customers WHERE status = $1 LIMIT $2",
        status, limit
    )
    return "\n".join(f"{r['name']} ({r['email']})" for r in rows)

Notice the database URL comes from an environment variable, not hardcoded. This matters when you deploy.

Explore 251+ MCP Integrations

Discover official and remote-only MCP servers from leading vendors. Connect AI agents to powerful tools and services.

251 Official ServersUpdated RegularlyVendor Verified

API Integration Server

For teams connecting AI agents to business APIs, here is a pattern that wraps an existing REST API:

@mcp.tool()
async def create_crm_contact(
    name: str, email: str, company: str, notes: str = ""
) -> str:
    """Create a new contact in the CRM system.
    
    Only use this when the user explicitly asks to create a contact.
    Do NOT use for lookups or searches.
    """
    async with httpx.AsyncClient() as client:
        resp = await client.post(
            "https://api.yourcrm.com/contacts",
            headers={"Authorization": f"Bearer {os.environ['CRM_TOKEN']}"},
            json={"name": name, "email": email, "company": company, "notes": notes}
        )
        data = resp.json()
        return f"Contact created: {data['id']} - {name} at {company}"

The key detail: the docstring explicitly states when NOT to use the tool. This prevents the model from creating contacts when the user only wanted to look someone up.

The 5 Mistakes That Break MCP Servers in Production

Building a working MCP server is the easy part. Keeping it running when real users and real agents depend on it is where teams fail. We analyzed 46 developer discussions to identify the patterns that separate production-grade servers from weekend projects.

STDIO Transport Won't Scale

STDIO works perfectly for local development. One process, one connection, one user. But teams consistently report that "scaling breaks pretty quickly (STDIO, concurrency, etc.)" when they try to share a server across a team or run it behind a web service. STDIO has no built-in support for concurrent connections, authentication, or network access.

The fix: start with STDIO for development, but plan your migration to Streamable HTTP before your first deployment. You can choose the right transport: SSE vs Stdio vs Streamable HTTP based on your deployment target.

Secrets Sprawl Across Config Files

The second most common failure is credential management. Developers report that "secrets end up everywhere (env vars, configs, copied tokens)" once they connect their server to external APIs. Worse, one developer noted that "throwing api keys in config files that agents have access to always gives me an ick," since the AI model can potentially read those files.

The fix: never hardcode credentials. Use environment variables injected at runtime, and consider a secrets manager (AWS Secrets Manager, HashiCorp Vault, or Doppler) for production deployments.

No Auth Means No Production

OAuth is the single biggest blocker for production MCP servers. The MCP specification requires OAuth 2.1 for remote servers, and developers consistently underestimate the complexity. Poor error visibility from auth providers, inconsistent client implementations, and unclear documentation create a debugging nightmare.

If your server will be accessed remotely or by multiple users, plan your authentication strategy before writing your first tool.

Tool Sprawl Kills Performance

Every tool you expose increases the context window consumption for connected AI models. Teams that deploy dozens of tools on a single server find that the model "starts to forget and skip over parts" of its instructions. One experienced developer advised: "Keep your MCP surface area tiny."

The fix: expose only the tools that matter for your use case. Group related tools into focused servers rather than building one server that does everything.

Zero Observability

With no logging, tracing, or metrics, debugging a production MCP server is guessing. When "someone's agent went through directories it had no reason to touch," the team had no audit trail to understand what happened or why.

Build observability in from the start: log every tool call with input parameters, execution time, and output size. Add request IDs that trace from the AI host through your server to your backend APIs.

Production riskWhat teams reportedImpact
STDIO scalingBreaks under concurrent connectionsDowntime for shared servers
Secret exposureAPI keys in plain text configsSecurity breach risk
Missing authAny agent can call any toolUnauthorized data access
Tool overloadModel confused by large tool surfacesDegraded AI performance
No observabilityCannot debug or audit tool callsBlind to failures and abuse

Test Your MCP Server Before Shipping

Before connecting your server to Claude Desktop or deploying it to a cloud provider, validate it with dedicated testing tools.

The MCP Inspector is the fastest way to verify your tools work correctly. Run mcp dev server.py and the Inspector opens in your browser with an interactive UI for calling each tool, viewing input schemas, and checking responses. Test your server with the MCP Inspector for a detailed walkthrough.

After the Inspector confirms your tools work in isolation, connect a client to test against your server using Claude Desktop. Add your server to the Claude Desktop configuration:

{
  "mcpServers": {
    "my-server": {
      "command": "python",
      "args": ["server.py"],
      "env": {
        "DATABASE_URL": "your-connection-string"
      }
    }
  }
}

Test each tool through natural conversation. Ask Claude to perform tasks that require your tools and watch for: correct tool selection, proper parameter passing, and graceful error handling when tools fail.

Deploy Your MCP Server to Production

Local STDIO servers work for personal use. For teams and production applications, you need a remote MCP server that runs on infrastructure you control.

Containerize with Docker

Start with a simple Dockerfile:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["python", "server.py", "--transport", "streamable-http", "--port", "8080"]

Two common deployment mistakes to avoid: using exec form for CMD (so signals propagate correctly), and matching your base URL path configuration to your hosting platform's expectations. Teams report hitting "URL path duplication" errors when the server and reverse proxy both add a path prefix.

Add Authentication

For remote deployment, your server needs authentication. The MCP specification supports OAuth 2.1 with PKCE for user-facing servers and bearer tokens for service-to-service communication.

At minimum, validate a bearer token on every incoming connection:

from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("secure-server")
 
@mcp.auth(required=True)
async def verify_token(token: str) -> bool:
    # Validate against your auth provider
    return await check_token_validity(token)
Expert Tip — Yaniv Shani, Founder of Apigene

"Start with a simple API key for your first remote deployment. You can add full OAuth later. The biggest mistake I see teams make is skipping auth entirely because OAuth feels overwhelming, then spending weeks cleaning up after an agent accesses data it should not have. Ship with basic auth on day one, upgrade to OAuth when you have multi-user needs."

Once your server is containerized and authenticated, deploy it to your cloud provider. Google Cloud Run, AWS ECS, and Azure Container Apps all support MCP servers over Streamable HTTP. Make your server available remotely with a detailed guide for each platform. Before you ship, follow these 12 production rules before shipping to avoid the most common deployment failures.

Scale Beyond One Server: Gateways and Routers

A single MCP server works until it does not. Teams that build multiple servers, one for database access, one for CRM, one for monitoring, one for deployment, quickly discover the management overhead.

We found that developers consistently hit the same wall. As one put it, they "got tired of the '100 MCP servers' approach everyone seems to push." The problems compound: separate authentication for each server, no centralized visibility into what tools are being called, and painful debugging across server boundaries.

This is where an MCP gateway becomes essential. A gateway sits between your AI hosts and your MCP servers, providing a single connection point with centralized auth, observability, and tool management. You can route your server through a gateway for production to solve these problems.

Apigene is an MCP gateway that connects any API or MCP server to AI agents through a single integration point. It handles authentication, compresses tool output to reduce token costs, and dynamically loads only the tools relevant to each request. For teams building AI agents that need to connect to multiple backends, a gateway eliminates the operational overhead of managing individual server connections.

You can also aggregate multiple servers behind a router if you want to self-manage the infrastructure rather than using a hosted gateway.

What to Build: Use Cases and Inspiration

Now that you know how to build and deploy an MCP server, the question is what to build. The most successful MCP servers solve real workflow problems:

  • Internal tooling: Give AI agents access to your deployment pipeline, monitoring dashboards, or internal APIs
  • Data access: Let agents query databases, search document stores, or pull metrics
  • Third-party integrations: Connect AI to CRM, project management, or communication tools
  • Infrastructure automation: Enable agents to manage cloud resources, containers, or CI/CD

Check out 19 real-world use cases to build for detailed project ideas. You can also browse 251+ existing servers for inspiration to see what the community has already built. When your server is ready for others to use, distribute your server through a marketplace to reach a wider audience.

The Bottom Line

Building an MCP server takes an afternoon. Building one that runs reliably in production takes planning. Start with Python and FastMCP for the fastest path to a working server. Choose Streamable HTTP transport from day one if you plan to go remote. Add authentication before your first deployment, not after. And when you outgrow managing individual servers, use a gateway like Apigene to centralize your MCP infrastructure.

The MCP ecosystem is growing fast, with all primary keywords showing 200-3,000% year-over-year search growth. The developers building MCP servers today are setting the foundation for how AI agents interact with real systems. Start building.

Stop Building MCP Integrations From Scratch.

  • Any API, one line of code — connect to ChatGPT, Claude, and Cursor without writing custom MCP servers
  • Visual UI in the chat — render interactive components, not just text dumps. Charts, forms, dashboards.
  • 70% fewer tokens — dynamic tool loading and output compression so your agents stay fast and cheap

Frequently Asked Questions

What language should I use to build an MCP server?

Python with FastMCP is the fastest path for most teams. It requires the least boilerplate, has the most community examples, and supports both STDIO and Streamable HTTP transports. TypeScript is equally production-ready and a better fit if your backend is Node.js or you plan to deploy on Cloudflare Workers. C# and Java SDKs exist for enterprise environments but have smaller communities. Pick whatever your team already writes daily.

How long does it take to build an MCP server?

A basic MCP server with 2-3 tools takes 30-60 minutes if you follow the official docs. Production deployment adds 2-4 hours for containerization, authentication, and testing. The hidden time cost is tool description refinement, as developers report spending more time on docstrings than on tool logic. Teams connecting to complex APIs should budget 1-2 days for the full build-test-deploy cycle.

Do I need OAuth for my MCP server?

For local STDIO servers used by a single developer, no. For any remote MCP server accessed over HTTP, yes. The MCP specification requires OAuth 2.1 for remote servers to prevent unauthorized tool access. A common shortcut is starting with API key authentication for initial deployment, then upgrading to full OAuth when you add multi-user support. Skipping auth entirely is the fastest way to create a security incident.

Can I run multiple MCP servers without a gateway?

You can, but it gets painful fast. Each server needs its own authentication, configuration, and monitoring. Developers who manage 5+ servers report "debugging across server boundaries" as a top frustration. A gateway centralizes auth, provides unified observability, and reduces token overhead by dynamically loading only the tools each request needs. If you are running 1-2 focused servers for personal use, direct connections work fine. Beyond that, a gateway like Apigene saves significant operational overhead.

What happens when my MCP server's upstream API changes?

Your server breaks silently. This is one of the most frequently reported production issues, as one developer warned: "next week the API schema changes and your agent breaks silently." The AI model will still call your tool, but the tool will return errors or incorrect data. The fix is defensive: validate API responses before returning them, add health checks that verify upstream endpoints are responding correctly, and monitor tool error rates. Some teams add contract tests that run on a schedule to catch breaking changes within hours.

How do I keep API keys secure in an MCP server?

Never store credentials in config files, environment variables that get committed to version control, or tool schemas that the AI model can read. Use your cloud provider's secrets manager (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault) to inject credentials at runtime. For local development, use a .env file that is gitignored. For production, mount secrets as environment variables from your orchestrator (Docker Compose, Kubernetes, Cloud Run). The goal is zero credential exposure in your codebase, logs, or error traces.

#mcp#mcp-server#tutorial#python#typescript#ai-agents#deployment