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:
- Connect to the server
- List tools — expect
search_docs - 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
| Problem | Fix |
|---|---|
| Server not in MCP list | Check absolute path; run python mcp_internal_docs.py manually |
| Tool never invoked | Name the tool explicitly in prompt |
| Import error | Confirm pip install mcp in the Python env Cursor uses |
| Stale docs after edit | Save file and restart Cursor MCP connection |
| Secrets needed later | Add "env": { "API_KEY": "${env:API_KEY}" } — never commit values |
Step 7: Harden for Real Data
Before pointing at staging DB or APIs:
- Keep tools read-only initially
- Validate inputs (FastMCP uses type hints; add explicit checks for production)
- Return small text payloads
- Log tool name + timestamp (not full args with PII)
- 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.jsonin every PR - Pin server script path and Python version in internal docs