Skip to content
CodeLifter
Search/ Browse prompts
CodeLifter Forum

How do you build a simple MCP server?

Staff answerWritten by CodeLifter editors · MCP and tools2 repliesupdated

Short answer

To build a simple MCP server, install an official MCP SDK, write one decorated function per tool, and run the file over stdio. In Python that is the mcp package, an MCPServer instance and @mcp.tool(). Test it in the MCP Inspector, then add it to your AI application's config using an absolute path to the server.

Written by CodeLifter editors · Staff, CodeLifter ·
CiteCodeLifter Forum, "How do you build a simple MCP server?", https://codelifter.com/forum/posts/how-do-you-build-a-simple-mcp-server/, accessed 2026-11-12.
  1. Staff, CodeLifter ·

    What do you need before you start?

    An MCP server is a program that offers tools, resources or prompts to an AI application over the Model Context Protocol; the plain terms thread explains the parts. You build one with an SDK (software development kit), the official library that handles the protocol so you only write the functions.

    Check the version first, because the SDKs changed in 2026. As of the docs read on 16 October 2026, the Python SDK README says v2 "is the current stable release line" and supports the 2026-07-28 MCP specification. It also warns that pip install mcp now installs 2.x, so older tutorials that import FastMCP were written for v1. The official build-a-server tutorial lists these requirements:

    Python TypeScript
    Runtime Python 3.10 or higher Node.js 20 or higher
    SDK mcp 2.0.0 or higher, installed as mcp[cli] @modelcontextprotocol/server, plus zod
    Server class MCPServer from mcp.server McpServer from @modelcontextprotocol/server
    Run over stdio mcp.run(transport="stdio") serveStdio(createServer, ...)
    Quick test uv run mcp dev server.py npx @modelcontextprotocol/inspector --cli node build/index.js --protocol-era modern --method tools/list

    The steps below use Python. The tutorial also covers Java, Kotlin, C#, Ruby, Rust and Go.

    Step by step: a one-tool server in Python

    These steps follow the official tutorial and the SDK README. CodeLifter has not run this exact code; read each step against the current docs before you rely on it.

    1. Install uv, the Python project manager the tutorial uses, then restart your terminal.

    2. Create the project:

      uv init demo-server
      cd demo-server
      uv venv
      source .venv/bin/activate
      uv add "mcp[cli]"
      

      On Windows, activate with .venv\Scripts\activate instead.

    3. Create server.py with one tool. This is the README's example plus the tutorial's run line:

      from mcp.server import MCPServer
      
      mcp = MCPServer("Demo")
      
      @mcp.tool()
      def add(a: int, b: int) -> int:
          """Add two numbers."""
          return a + b
      
      if __name__ == "__main__":
          mcp.run(transport="stdio")
      

      The type hints become the tool's input schema and the docstring becomes its description. The README makes the point directly: "a: int, b: int is the schema".

    4. Open it in the Inspector with uv run mcp dev server.py. The README says that calling add with a=1 and b=2 returns 3.

    5. Change the tool to do something useful, one small job per function, with a docstring that says when to use it. The model chooses tools from their names and descriptions, so vague docstrings lead to wrong calls.

    Why does a stdio server break when you print?

    On the stdio transport, the server and the AI application talk over standard input and standard output. The tutorial is blunt: "Never write to stdout. Writing to stdout will corrupt the JSON-RPC messages and break your server." In Python that rules out print(). Use the standard logging module, which writes to standard error, with one logger per module from logging.getLogger(__name__). In TypeScript, use console.error() instead of console.log(). HTTP servers do not have this problem.

    How do you connect it to Claude Desktop?

    The tutorial uses Claude Desktop as the host. Other hosts use a similar block; the key is usually mcpServers, though VS Code uses servers.

    1. Open ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, or $env:AppData\Claude\claude_desktop_config.json on Windows (PowerShell). Create it if it does not exist.

    2. Add the server with an absolute path:

      {
        "mcpServers": {
          "demo": {
            "command": "uv",
            "args": ["--directory", "/ABSOLUTE/PATH/TO/demo-server", "run", "server.py"]
          }
        }
      }
      
    3. If the app cannot find uv, put the full path from which uv (or where uv on Windows) in command.

    4. Quit the app fully (Cmd+Q on macOS) and reopen it; closing the window does not reload the config.

    5. If the tool does not appear, read ~/Library/Logs/Claude/mcp.log and mcp-server-demo.log on macOS. The second file holds your server's standard error output.

    What should you check before you share it?

    A server runs with the permissions of whoever starts it. The MCP security best practices recommend "a progressive, least-privilege scope model", and the tools specification tells clients to treat tool annotations as untrusted unless they come from trusted servers. Before you publish:

    1. Make each tool do one narrow thing, and reject inputs outside what it needs (paths outside one folder, for example).
    2. Read secrets from environment variables, never from the code; the API keys thread covers why.
    3. Mark tools that change data clearly in the description so users know what they approve.
    4. Add a licence and a README with the exact install line.

    The CodeLifter MCP directory shows how published servers document their tools and access, and the Filesystem sandbox thread shows how users will try to contain yours.

    Short version

    • Install the official SDK: uv add "mcp[cli]" for Python (v2, MCPServer) or @modelcontextprotocol/server for TypeScript.
    • One function per tool; type hints and docstrings become the schema and description.
    • Never print to stdout on a stdio server; log to stderr.
    • Test with the MCP Inspector, then add the server to your client with an absolute path.
    • Scope every tool narrowly before anyone else installs it.
    0
    ReplyLink

Plain text or simple Markdown. Links are nofollow. Your email is never shown.

  1. CodeLifter editorsStaffStaff, CodeLifter ·

    How do you add your own MCP server to Claude Code? Use the claude mcp add command rather than editing a JSON file. For a local stdio server, the Claude Code MCP documentation gives the form claude mcp add [options] <name> -- <command> [args...], for example:

    claude mcp add --transport stdio demo -- uv --directory /ABSOLUTE/PATH/TO/demo-server run server.py
    

    The -- matters: everything before it is an option for Claude Code (--transport, --env, --scope), and everything after it is passed to your server untouched. Pass secrets with --env KEY=value instead of writing them into the command.

    By default the entry goes into local scope, which means only you, in this project. Add --scope user to use it in every project, or --scope project to write it to a .mcp.json file at the project root that you can commit for your team. The docs say a project-scoped server shows as pending until each person approves it by running claude interactively. Check the result with claude mcp get demo, which shows whether Claude Code connected, or type /mcp inside a session. If a server's instructions were written for Claude Desktop, claude mcp add-json accepts the object inside mcpServers.

  2. CodeLifter editorsStaffStaff, CodeLifter ·

    How do you turn a local MCP server into a remote one? Change the transport from stdio to Streamable HTTP. With the Python SDK, the README shows the same server.py served over HTTP with:

    uv run mcp run server.py --transport streamable-http
    

    A client can then connect to http://localhost:8000/mcp. The README adds that "A URL means Streamable HTTP, the transport you deploy." The architecture overview describes Streamable HTTP as HTTP POST for client-to-server messages, with optional server-sent events for streaming, and says it supports bearer tokens, API keys and custom headers, while recommending OAuth to obtain tokens. It also notes that remote servers "will typically serve many MCP clients", unlike a local stdio server, which typically serves one.

    That changes your security work. A local server runs as you; a remote one is a web service anyone who finds the URL can try, so it needs authentication before any tool that reads private data or writes anything. To add a remote server to Claude Code, the MCP documentation uses claude mcp add --transport http <name> <url>. The remote or local GitHub MCP server thread shows the same trade-off from the user's side.

CodeLifter

Reviewed vibe coding prompts, the JavaScript each one should produce, and sourced answers on AI-generated code: who wrote it, who owns it, and whether you can ship it. Free since 1999.

Browse the Prompt Shelf

Popular topics

© 1999 to 2026 CodeLifter

Independent. No ties to any software vendor.
Code MIT Text CC-BY-4.0 Back to top