MCP (Model Context Protocol) is an open standard that lets AI agents interact with external tools. By building a custom MCP server for your codebase, you can give AI assistants like Claude or Cursor the ability to read files, search code, and retrieve documentation – all without manual copying. This tutorial walks you through creating a lightweight MCP server using Python.

This tutorial assumes basic Python knowledge and Node.js (for the MCP Inspector). You'll also need an LLM client that supports MCP (e.g., Claude Desktop, Cursor, or continue.dev).

What We'll Build

We'll create an MCP server with two tools:

  • read_file – reads the content of a given file path
  • search_code – finds files matching a regex pattern in the project

Both will be constrained to the project directory for security.

Step 1: Set Up the Project

Create a new directory and initialize a Python project with the MCP SDK:

mkdir my-mcp-server
cd my-mcp-server
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
pip install mcp

Step 2: Write the Server

Create server.py with the following code. We'll use the mcp library to define tools.

import os
import re
from pathlib import Path
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions

# Restrict to a project directory – change this to your actual project path
PROJECT_ROOT = Path("/path/to/your/project").resolve()

app = Server("codebase-reader")

@app.list_tools()
async def list_tools():
    return [
        {
            "name": "read_file",
            "description": "Read the contents of a file relative to the project root",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "path": {"type": "string", "description": "Relative file path (e.g., src/main.py)"}
                },
                "required": ["path"]
            }
        },
        {
            "name": "search_code",
            "description": "Search for files matching a regex pattern in the project",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "pattern": {"type": "string", "description": "Regex pattern to match against file paths"}
                },
                "required": ["pattern"]
            }
        }
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "read_file":
        rel_path = arguments["path"]
        full_path = (PROJECT_ROOT / rel_path).resolve()
        # Prevent path traversal
        if not str(full_path).startswith(str(PROJECT_ROOT)):
            raise ValueError("Path outside project root")
        if not full_path.exists():
            raise FileNotFoundError(f"File not found: {rel_path}")
        content = full_path.read_text(encoding="utf-8")
        return {"content": content}
    elif name == "search_code":
        pattern = arguments["pattern"]
        matching_files = [
            str(p.relative_to(PROJECT_ROOT))
            for p in PROJECT_ROOT.rglob("*")
            if p.is_file() and re.search(pattern, str(p))
        ]
        return {"files": matching_files[:50]}  # limit to 50 results
    else:
        raise ValueError(f"Unknown tool: {name}")

if __name__ == "__main__":
    app.run(transport="stdio")
Make sure to change PROJECT_ROOT to the actual path of your codebase. Also, the server uses stdio transport – it's the simplest for local MCP.

Step 3: Test with MCP Inspector

Launch the MCP Inspector (a Node.js CLI tool) to verify:

npx @modelcontextprotocol/inspector python server.py

This opens a web UI where you can list tools and call them. Try read_file with a relative path like README.md.

Step 4: Connect to an AI Client

Configure your AI tool to use this MCP server. For example, in Claude Desktop:

  • Open Settings → Developer → Edit Config
  • Add to mcpServers in claude_desktop_config.json:
{
  "mcpServers": {
    "codebase-reader": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

Restart Claude. Now you can ask Claude to “Read the main logging file” or “Find all test files” and it will use your server.

Security Note

This server only allows reading files, never writing. Always run MCP servers with minimal permissions. For production, consider adding authentication or limiting to specific file types.

Next Steps

You can extend the server with more tools:

  • get_symbol_definition – parse Python/JS to extract function definitions
  • list_directory – show contents of a folder
  • run_command – execute shell commands (be careful!)

MCP is rapidly evolving. Check the official docs for the latest patterns.

Now your AI assistant has native read access to your entire codebase. No more copy-pasting! This workflow dramatically improves the quality of generated code and reduces context window waste.