If you are a Python or TypeScript developer who wants Claude, GPT, or Cursor to reach databases, call APIs, and read files, you already know the core gap: models have no built-in hands. Training data has cutoffs and models cannot execute external actions on their own. This guide takes you from an empty project to a production-ready MCP Server. You will implement Tools, Resources, and Prompts, run both stdio and HTTP+SSE transports, and ship with Docker. When you finish, you can plug your toolchain into Cursor or Claude Desktop. Node tiers are on the NOVAKVM pricing page.
[ SECTION_01 ] // WHAT_IS_MCP What is MCP: from Function Calling to a unified tool protocol
AI tool integration followed a clear arc. Function Calling (OpenAI, 2023) let models emit structured JSON call payloads. Plugins and GPT Actions wrapped HTTP endpoints as chat plugins. MCP (Model Context Protocol), open-sourced by Anthropic in November 2024, standardizes how models discover, describe, and invoke tools. Build once; Claude Desktop, Cursor, VS Code Continue, and other hosts can reuse the same server.
- LLM capability ceiling: No live data, no direct filesystem or database access. External tools supply the senses and hands.
- Schema fragmentation: OpenAI Function Calling, Claude Tool Use, and LangChain Agents each define tool schemas differently. Switching models or IDEs means rewriting adapters.
- Missing discovery: Traditional REST APIs do not tell an AI what they can do. MCP runtimes expose a live capability list via
tools/list. - Session and context: MCP keeps a persistent connection for multi-step agent workflows. Stateless REST is harder to chain for complex tasks.
- Security and permissions: The server can restrict which tools write, which resources are read-only, and block model overreach on production data.
The client-server JSON-RPC architecture breaks down as follows:
- Host: Claude Desktop, Cursor, VS Code — the user-facing shell.
- MCP Client: Embedded in the host; maintains a 1:1 session with each server.
- MCP Server (yours): Exposes three primitives — Tools (executable actions), Resources (read-only data), Prompts (reusable prompt templates).
- Transport: Local dev uses stdio (child process stdin/stdout). Remote deploy uses HTTP + SSE (Server-Sent Events push).
- Protocol: All messages follow JSON-RPC 2.0, e.g.
initialize→tools/list→tools/call→notifications/cancelled. - Lifecycle: Handshake → capability negotiation → steady state → graceful shutdown. The client can
pingto keep the session alive.
| Dimension | MCP | OpenAI FC | LangChain Tools |
|---|---|---|---|
| Standardization | Open protocol, cross-vendor | Tied to OpenAI API | Framework-internal format |
| Tool discovery | Runtime tools/list |
Schema embedded per request | Code registration, static |
| Resources / Prompts | Native support | Not supported | Requires custom wrappers |
| Transport | stdio / HTTP+SSE | HTTPS API | In-process calls |
| Native IDE integration | Cursor, Claude Desktop, etc. | ChatGPT plugin ecosystem | Needs a bridge layer |
MCP is not about whether you can call an API. It is about how AI uniformly discovers, selects, and correctly invokes tools — the core problem of the agent era.
[ SECTION_02 ] // DEV_ENV Dev environment setup: Python mcp SDK vs TypeScript SDK
The official stack offers two first-class SDKs: the Python mcp package (with the FastMCP high-level API) and TypeScript @modelcontextprotocol/sdk. Python fits rapid prototypes and data/ML workloads. TypeScript fits Node.js ecosystems and frontend-heavy teams. This walkthrough centers on Python FastMCP; the TypeScript layout mirrors it.
mkdir my-mcp-server && cd my-mcp-server
python3 -m venv .venv
source .venv/bin/activate
pip install "mcp[cli]" httpx pydantic python-dotenv
npm install -g @modelcontextprotocol/inspector
Recommended project layout:
my-mcp-server/
├── server.py
├── tools/
├── resources/
├── prompts/
├── tests/
├── Dockerfile
├── requirements.txt
└── .env
Three tools for debugging and host integration:
- MCP Inspector: Official visual debugger. Lists tools, resources, and prompts; triggers calls manually.
- Claude Desktop: Edit
~/Library/Application Support/Claude/claude_desktop_config.json(macOS) to register a stdio server. - Cursor: Settings → MCP → add server config. Supports stdio and remote HTTP endpoints.
[ SECTION_03 ] // HELLO_WORLD Hello World: run your first MCP Server in 10 lines
FastMCP gives you a minimal server with a say_hello tool:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-server")
@mcp.tool()
def say_hello(name: str) -> str:
return f"Hello, {name}! MCP Server is running."
if __name__ == "__main__":
mcp.run()
Verify with Inspector:
npx @modelcontextprotocol/inspector python server.py
Open the Inspector UI in your browser. In the Tools panel, call say_hello with {"name": "NOVAKVM"}. You should get the greeting back.
Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"hello-server": {
"command": "/path/to/my-mcp-server/.venv/bin/python",
"args": ["/path/to/my-mcp-server/server.py"]
}
}
}
Cursor MCP config: Create .cursor/mcp.json at the project root, or add equivalent JSON under Cursor Settings → MCP. Restart Cursor. In Agent mode you should see the say_hello tool.
[ SECTION_04 ] // TOOLS Tools in depth: five tool types and error-handling patterns
In MCP, the function signature is the documentation. Parameter names, type hints, and docstrings become JSON Schema for the model. Use Pydantic models for complex inputs:
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
query: str = Field(..., description="Search keywords")
limit: int = Field(10, ge=1, le=100, description="Max results")
@mcp.tool()
async def search_docs(input: SearchInput) -> list[dict]:
...
Production servers typically ship these five tool categories:
- calculator: Safe math expression evaluation. Avoid raw
eval; parse withastor a dedicated library. - file_read / file_write: Restrict to a root directory (chroot-style) to block path traversal. Writes may need confirmation or an extension whitelist.
- fetch_url: Async HTTP requests — example below.
- db_query: Read-only SQL (
SELECT) with parameterized queries. Put writes in a separate tool with audit logging. - get_current_time: Return an ISO 8601 timestamp so the model knows the current time.
import httpx
@mcp.tool()
async def fetch_url(url: str, timeout: int = 30) -> str:
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.get(url)
resp.raise_for_status()
return resp.text[:50000]
Error-handling patterns:
- Catch exceptions inside tools and return structured error text instead of crashing the process. The client passes the result back to the model for retry or alternate strategy.
- Set timeouts and retry limits on external APIs. Prevent infinite call loops from the model.
- Read API keys and connection strings from environment variables only. Never return secrets in tool output or Resources.
- Write tools should return an operation summary (what changed, rows affected) so the model can confirm with the user.
[ SECTION_05 ] // RESOURCES Resources protocol: read-only data vs executable tools
Resource vs Tool: A Resource is read-only context (config files, user profiles, log snippets). The model pulls it via resources/read. A Tool is an executable action (query, write, HTTP call). Resources fit information the AI needs to see but not modify.
URI scheme conventions:
config://app/settings— static config registered at startup.user://{id}/profile— dynamic template; the client supplies an id and the server generates content on demand.file://— maps to the local filesystem (requires a path sandbox).
Resources support four MIME categories: text/plain, application/json, binary blob (Base64-encoded), and stream (chunked push for large files).
@mcp.resource("config://app/settings")
def get_app_settings() -> str:
return json.dumps({"env": "production", "version": "1.2.0"})
@mcp.resource("user://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
profile = db.fetch_user(user_id)
return json.dumps(profile)
A filesystem resource server can recursively register files under a directory. The AI sees project structure and key config during coding without calling a read tool per file.
[ SECTION_06 ] // PROMPTS Prompts: reusable multi-turn conversation scaffolds
An MCP Prompt is a predefined template the host can list and inject into the conversation. Use it for code review, incident postmortems, API design reviews, and other repeatable workflows. Prompts accept placeholder parameters and support multi-turn message structures.
from mcp.types import PromptMessage, TextContent
@mcp.prompt()
def code_review_prompt(language: str, focus: str = "security") -> list[PromptMessage]:
return [
PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"Review the following {language} code for {focus} issues. List problems and improvements:\n\n"
)
)
]
Multi-turn example: round one sets role and constraints, round two injects the code placeholder, round three asks for structured JSON output (severity / file / line / suggestion). Teams can codify best practices as Prompts so new members skip rewriting system prompts.
[ SECTION_07 ] // HTTP_TRANSPORT HTTP transport: from stdio to Streamable HTTP remote service
| Dimension | stdio | HTTP + SSE |
|---|---|---|
| Deployment | Local child process | Remote server / container |
| Network | None required | Public or private reachability |
| Horizontal scale | Single machine | Load balancer + session affinity |
| Auth | Process isolation | Bearer Token / API Key |
| Best fit | Personal dev, Claude Desktop | Team-shared, 24/7 cloud hosting |
FastMCP supports streamable-http transport. Switch with one line:
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)
Production HTTP deploys need these layers:
- Bearer Token / API Key: Validate the
Authorizationheader at the reverse proxy or middleware. - CORS: If a browser-based Inspector crosses origins, allow the correct source domains.
- Rate limiting: Throttle by IP or API key to stop model-driven call storms.
- TLS: Public exposure requires HTTPS. Terminate SSL with Caddy or Nginx.
[ SECTION_08 ] // DEBUG_TEST Debug, test, and troubleshoot: Inspector, pytest, and a six-step deploy checklist
MCP Inspector workflow: After launch, the left panel shows the server capability tree. The Tools panel accepts JSON parameters for manual calls. Notifications shows server push events. Logs shows stderr. Inspector restarts the child process when you change code.
import pytest
from server import say_hello
def test_say_hello():
result = say_hello("World")
assert "Hello, World!" in result
@pytest.mark.asyncio
async def test_fetch_url():
result = await fetch_url("https://httpbin.org/get")
assert "origin" in result
| Symptom | Likely cause | Fix |
|---|---|---|
| Tools not listed | Missing decorator or server failed to start | Check stderr; reconnect Inspector |
| JSON serialization error | Returned datetime, Decimal, or other non-JSON types | Call json.dumps manually or cast to str |
| Call timeout | Slow external API, no timeout set | Set timeout inside the tool; raise client limit |
| Permission denied | Path outside sandbox or bad DB credentials | Verify ALLOWED_PATHS and .env |
Six-step production deploy checklist:
- Pin dependencies: Run
pip freeze > requirements.txt. Fix Python and mcp SDK versions in CI to avoid upstream breaking changes. - Write a Dockerfile: Multi-stage build; final image contains runtime deps only. Run the server as a non-root user.
- Configure environment variables: API keys, DB connection strings, and ALLOWED_PATHS go through secrets management. No hardcoding.
- Pick a hosting platform: Railway or Render for fast validation; AWS Lambda with an HTTP adapter for low-frequency calls; Cloud Run or a VPS for 24/7 always-on.
- Add monitoring: Structured JSON logs, a
/healthendpoint, Prometheus metrics, or Sentry exception reporting. - Verify Cursor / Claude integration: Put remote URL and Bearer Token in MCP config. Call
tools/listto confirm the full tool list before rolling out.
[ SECTION_09 ] // PRODUCTION Production deployment: Docker, hosting platforms, and observability
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
USER nobody
EXPOSE 8080
CMD ["python", "server.py"]
Hosting platform options:
- Railway / Render: Git-push deploys. Free tiers suit MVPs. Watch cold-start impact on SSE long connections.
- AWS Lambda: Pay per invocation. Needs an HTTP-to-stdio adapter. Fits low-frequency internal tools.
- Google Cloud Run: Container-native with autoscaling. Set min-instances ≥ 1 to keep SSE sessions alive.
- VPS / bare metal: Full control over network and disk. Best when sensitive data must stay on-premises.
Observability stack:
- Logging: structlog or python-json-logger. Log tool name, duration_ms, and status on every call.
- Prometheus: Expose
mcp_tool_calls_totalandmcp_tool_duration_secondscounters and histograms. - Sentry: Capture unhandled exceptions. Tag with server version for faster rollback.
Version compatibility: The MCP spec revision 2025-03-26 introduced Streamable HTTP. Before deploy, confirm your client (Cursor version, Claude Desktop version) and server SDK support the same transport. Document minimum versions in README, e.g. mcp>=1.2.0.
[ SECTION_10 ] // ECOSYSTEM Hands-on: ChromaDB knowledge-base MCP plus 2026 ecosystem and learning path
Capstone project — a knowledge-base MCP Server: store document vectors in ChromaDB or Qdrant, use watchfiles to re-index on directory changes, and expose search_knowledge and write_note tools. Plug it into Cursor and the agent can search team docs instead of relying on training data alone.
@mcp.tool()
async def search_knowledge(query: str, top_k: int = 5) -> list[dict]:
embedding = await embed(query)
results = collection.query(query_embeddings=[embedding], n_results=top_k)
return [{"text": doc, "score": score} for doc, score in zip(...)]
@mcp.tool()
async def write_note(title: str, content: str) -> str:
doc_id = collection.add(documents=[content], metadatas=[{"title": title}])
return f"Note saved: {doc_id}"
Community MCP servers worth reusing:
- @modelcontextprotocol/server-filesystem — sandboxed file read/write
- server-github — issues, PRs, code search
- server-brave-search — live web search
- server-postgres — read-only SQL queries
- server-slack — channel messages and notifications
2026 MCP ecosystem snapshot:
- Ecosystem scale: By 2026, public MCP servers exceed 10,000. Combined GitHub stars across modelcontextprotocol org repos top 50,000.
- Vendor adoption: OpenAI announced native MCP support in Q1 2026; Google Gemini and Microsoft Copilot followed in Q2. Governance moved to AAIF under the Linux Foundation.
- Enterprise integration cost: Industry surveys report roughly 38–55% lower AI toolchain integration cost after standardizing on MCP interfaces.
Suggested learning path:
- Read the official spec and understand the JSON-RPC message flow
- Build Hello World with FastMCP and debug in Inspector
- Implement at least three real Tools (file, HTTP, DB)
- Add Resources and Prompts to experience all three primitives
- Switch to HTTP transport and Docker-deploy to the cloud
- Connect Cursor and complete a knowledge-base or GitHub integration project
Use the public references below to verify specs and SDKs. If upstream repos change, treat the linked sources as authoritative.
Model Context Protocol — official specification and documentation
modelcontextprotocol/python-sdk — Python MCP SDK
modelcontextprotocol/typescript-sdk — TypeScript MCP SDK
modelcontextprotocol/inspector — MCP Inspector debugging tool
Running an MCP Server on a laptop that sleeps causes recurring failures: tools/list session drops, expired OAuth tokens, full disks corrupting vector stores, and SSE disconnects after network changes. These issues affect daily dev more than model choice. Docker on a laptop still inherits host sleep. Free-tier PaaS cold starts break long-lived connections. Lambda is a poor fit for always-on SSE.
For 24/7 MCP tooling, stable SSH, and predictable Apple Silicon compute, moving Cursor Agent and your custom server to dedicated bare metal is often the better trade. NOVAKVM offers multi-region Mac Mini M4 / M4 Pro flexible rentals for Cursor MCP, remote Claude Desktop dev, and co-located vector stores. See the pricing page, order on the order page, and deployment questions on the help center.