1. ai
  2. /tutorials
  3. /mcp-server

MCP Server from Scratch

This tutorial walks through a complete MCP server you can run locally, test with the MCP Inspector, and connect to Cursor. We use Python and FastMCP — one file, no compile step.

Last reviewed: June 2026

Reference: Building MCP Servers. Quick config: MCP cheat sheet. TypeScript alternative: same guide's TS skeleton.

Prerequisites

  • Python 3.10+
  • Cursor with MCP support

Official spec: Model Context Protocol.

What We Are Building

A read-only search_docs tool backed by an in-memory map — enough to learn the lifecycle without a database.

Step 1: Create the Server File

Save as mcp_internal_docs.py anywhere on your machine (or in a tools/ folder in your repo):

from mcp.server.fastmcp import FastMCP

DOCS = {
    "auth": "OAuth 2.0 with PKCE. Access tokens expire in 3600 seconds.",
    "billing": "Stripe webhooks POST to /api/webhooks/stripe. Verify signatures.",
    "deploy": "Production deploys via GitHub Actions on tag push v*.*.*.",
}

mcp = FastMCP("internal-docs")


@mcp.tool()
def search_docs(query: str) -> str:
    """Search internal documentation by keyword."""
    q = query.lower()
    matches = [
        f"## {title}\n{body}"
        for title, body in DOCS.items()
        if q in title or q in body.lower()
    ]
    return "\n\n".join(matches) if matches else "No matching documentation."


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

Install the MCP Python package once:

pip install mcp

Docs: MCP Python SDK.

Step 2: Smoke-Test the Server

Run directly — the process waits on stdio (that is normal):

python mcp_internal_docs.py

Ctrl+C to exit. No build or compile step.

Step 3: MCP Inspector

npx @modelcontextprotocol/inspector python mcp_internal_docs.py

In the inspector UI:

  1. Connect to the server
  2. List tools — expect search_docs
  3. Invoke with { "query": "oauth" } — expect auth doc chunk

Inspector: github.com/modelcontextprotocol/inspector

Step 4: Configure Cursor

In your project root (or globally), create .cursor/mcp.json:

{
  "mcpServers": {
    "internal-docs": {
      "command": "python",
      "args": ["/absolute/path/to/mcp_internal_docs.py"]
    }
  }
}

Use an absolute path to the script. Restart Cursor or reload MCP from settings.

Cursor docs: MCP in Cursor.

Step 5: Test in Cursor

Open Ask mode in a repo with this config:

Use the search_docs MCP tool to find how billing webhooks work.
Call the tool before answering — do not guess.

Expected: model calls search_docs with query like "billing" and returns Stripe webhook text.

Step 6: Debug Common Failures

ProblemFix
Server not in MCP listCheck absolute path; run python mcp_internal_docs.py manually
Tool never invokedName the tool explicitly in prompt
Import errorConfirm pip install mcp in the Python env Cursor uses
Stale docs after editSave file and restart Cursor MCP connection
Secrets needed laterAdd "env": { "API_KEY": "${env:API_KEY}" } — never commit values

Step 7: Harden for Real Data

Before pointing at staging DB or APIs:

  1. Keep tools read-only initially
  2. Validate inputs (FastMCP uses type hints; add explicit checks for production)
  3. Return small text payloads
  4. Log tool name + timestamp (not full args with PII)
  5. Get security review per Team AI Policy

Upgrade path: RAG for Codebases for vector search instead of in-memory maps.

For Teams

  • Add server to MCP allowlist in internal policy
  • Code review mcp.json in every PR
  • Pin server script path and Python version in internal docs