{"slug": "building-an-mcp-server-in-python-and-connecting-it-to-claude-code", "title": "Building an MCP server in Python (and connecting it to Claude Code)", "summary": "A developer built an MCP server in Python in about 10 minutes using the official SDK, implementing a single tool that counts words, characters, and lines in text. The server connects to Claude Code via stdio, demonstrating how the Model Context Protocol standardizes AI tool integrations.", "body_md": "An MCP server is a small app that extends an AI model's capabilities by giving it access to custom tools, a particular set of data or workflows. It's based on the Model Context Protocol, which is an open standard for connecting AI apps with these external sources.\n\nThe most straightforward way to create an MCP server is to use the official SDK, implement a single function and mark it as a tool and then expose it through stdio (standard input/output) which you can register in Claude Code; it basically boils down to a single Python file with a single tool and connecting it end-to-end took us around 10 minutes.\n\nGenerally, the Model Context Protocol defines two sides:\n\nAs for the main purpose of the Model Context Protocol — before it was introduced, every AI app needed its own custom integration with every tool; the Model Context Protocol replaces this with a single standard connector, so to say it's like USB-C for the AI world — you have a single standardised port instead of having to use a separate cable with every device. In terms of the protocol, a tool is just a function that the model can decide to call.\n\nSo if you want to build an MCP server, you do it when you want your model to have access to some resources you have (like your internal API or database for example) which aren't available through any of the already-published servers.\n\nLet's have a look at a minimal example of what such server might look like — a single Python file with a single tool that returns the number of words, characters and lines in the input text.\n\nTo set up the project we used uv (a CLI for managing Python projects) and installed the official SDK:\n\n```\nuv init word-count-mcp\ncd word-count-mcp\nuv add \"mcp[cli]\"\n```\n\n`uv init word-count-mcp`\n\n— initialises a new project called \"word-count-mcp\" with an uv project file`uv add \"mcp[cli]\"`\n\n— adds the mcp package to the project as a dependency with its CLI extras; it also creates a virtual environment and a lockfile`uv init`\n\nfor a new project, it creates a sample main.py file which you can remove if you don't need itTo implement the tool, create server.py:\n\n``` python\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"word-count\")\n\n@mcp.tool()\ndef word_count(text: str) -> dict:\n    \"\"\"Count the words, characters, and lines in a block of text.\n\n    Args:\n        text: The text to analyze.\n    \"\"\"\n    words = text.split()\n    return {\n        \"words\": len(words),\n        \"characters\": len(text),\n        \"characters_no_spaces\": len(\"\".join(text.split())),\n        \"lines\": len(text.splitlines()),\n    }\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"stdio\")\n```\n\nThese three lines do the entire job:\n\n`FastMCP(\"word-count\")`\n\n— creates an instance of the FastMCP class and names it \"word-count\"`@mcp.tool()`\n\n— annotates a function as a tool; the SDK reads its type hints and the docstring to build the tool's schema so the model knows what it does and what arguments it takes; if you don't provide any type hints, the schema will be ambiguous`mcp.run(transport=\"stdio\")`\n\n— runs the server through stdio, which means that the server waits for requests via stdin and sends responses via stdoutNow run it to make sure everything works as expected:\n\n```\nuv run server.py\n```\n\nWhen you run it, it blocks until it receives some input, which is expected as it's a stdio server and hence it does nothing until a client connects; stop it with Ctrl+C.\n\nAnd then, from the project folder, register it in Claude Code:\n\n```\nclaude mcp add word-count -- uv run --directory \"$(pwd)\" server.py\n```\n\nThe part after `--`\n\nis the command to run the server. `claude mcp add`\n\nregisters it as a stdio server by default; the `--directory \"$(pwd)\"`\n\nbit is a uv flag that points it at the current directory, so no matter from which folder you start Claude Code, it'll work.\n\nAnd here's the confirmation that we've added a new stdio server called \"word-count\" to our local config:\n\n```\nAdded stdio MCP server word-count with command: uv run --directory /…/word-count-mcp server.py to local config\n```\n\nIf you run `claude mcp list`\n\nafterwards, you can see that it's Connected, which means that Claude Code started the server, connected through the Model Context Protocol and received a valid list of tools from it.\n\n```\nword-count: uv run --directory /…/word-count-mcp server.py - ✔ Connected\n```\n\nSo let's try using this tool now. Run Claude Code on your machine and ask it to use the word-count tool:\n\nUse the word_count tool on this text: \"MCP turns Claude into a client for your own tools\"\n\nClaude Code sees the word_count tool (full name mcp_*word-count*_word_count), asks for your permission and calls the server which returns its output as is, here are the counts for the sample text:\n\n```\n{\n  \"words\": 10,\n  \"characters\": 49,\n  \"characters_no_spaces\": 40,\n  \"lines\": 1\n}\n```\n\nSo you can see that it's a round-trip from Claude Code through the server to the Python function and back to Claude Code.\n\nIf you want, you can also test it with the MCP Inspector — the SDK's CLI launches it via `mcp dev`\n\nand it runs at localhost:6274, letting you explore tools and experiment with them by providing arguments and seeing their raw output.\n\n```\nuv run mcp dev server.py\n```\n\nIt's very useful for testing servers before integrating them with AI apps like Claude Code.\n\nBut there are some common pitfalls worth looking out for when working with the Model Context Protocol:\n\n`claude mcp add`\n\ntakes `-s`\n\nto decide where the registration is saved: local (the default, which is why you saw “local config” above) = only for you in the current project, user = you across all projects, project = written to .mcp.json so you can commit it and share it with your team; use local to experiment, project for sharingSo… When should you create your own MCP server? Anytime you need the model to access some resources that only you have (like your internal API or a database). If it's a system that's popular enough (like GitHub), there's usually a standard server for it but if you want to give your model access to some custom thing, you need to build this bridge.\n\nThe barrier of entry is really low — it's just about defining a function and annotating it with a single decorator; if you can write Python, you can write an MCP tool. But the more advanced challenges appear once your tool actually starts using some systems — authentication, error handling, rate limits, reliability etc. This word-count tool is pretty benign as it's deterministic and inert but when your tool needs to connect with a database or spend money, the actual engineering work starts.\n\nThe thing is that there's a difference between \"works\" and \"production-grade\". If you want to build an MCP server that works, it takes 10 minutes; if you want to have a production-grade MCP server, it might take 10 months. This is the kind of work DSPLCE does — we help AI companies take their AI-built software the last mile. Have a look at the [example on our GitHub](https://github.com/dsplce-co/word-count-mcp), clone and connect it in a couple of minutes.", "url": "https://wpnews.pro/news/building-an-mcp-server-in-python-and-connecting-it-to-claude-code", "canonical_source": "https://dev.to/dsplce-co/building-an-mcp-server-in-python-and-connecting-it-to-claude-code-4ibk", "published_at": "2026-07-25 15:46:57+00:00", "updated_at": "2026-07-25 16:01:58.673583+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "large-language-models"], "entities": ["Claude Code", "Model Context Protocol", "FastMCP", "uv"], "alternates": {"html": "https://wpnews.pro/news/building-an-mcp-server-in-python-and-connecting-it-to-claude-code", "markdown": "https://wpnews.pro/news/building-an-mcp-server-in-python-and-connecting-it-to-claude-code.md", "text": "https://wpnews.pro/news/building-an-mcp-server-in-python-and-connecting-it-to-claude-code.txt", "jsonld": "https://wpnews.pro/news/building-an-mcp-server-in-python-and-connecting-it-to-claude-code.jsonld"}}