# I Built a Custom MCP Server That Publishes My Blogs for Me: A Debugging Log

> Source: <https://dev.to/d1n35h_v/i-built-a-custom-mcp-server-that-publishes-my-blogs-for-me-a-debugging-log-2p5j>
> Published: 2026-08-01 04:50:58+00:00

I've been deep in AI engineering prep lately — deployed a couple of full-stack AI projects, fine-tuned an open-weight LLM, and been grinding DSA for placements. Somewhere in the middle of all that, I got curious about MCP (Model Context Protocol) and decided to actually build something with it instead of just reading about it.

The idea was simple: what if Claude could read a rough draft blog post sitting on my machine, clean it up, and publish it straight to Dev.to — without me touching the Dev.to UI at all?

Turns out, that's exactly what MCP is for.

MCP is a protocol that lets an AI model like Claude call tools you define — read files, hit APIs, run scripts — instead of just generating text. You write a small server exposing "tools" (plain Python functions with docstrings), plug it into Claude Desktop's config, and suddenly Claude can *act*, not just talk.

I set up two servers for this:

`publish_blog_to_devto`

tool, wrapping the Dev.to APII started with `uv`

for Python package management — much faster than plain pip/venv.

**First hiccup:** PowerShell couldn't find `uv`

right after installing it, because my terminal session was already open before the installer updated PATH. Closing and reopening the terminal fixed that instantly. A small thing, but easy to panic over if you don't know why it's happening.

**Second hiccup** was more interesting. I created a fresh venv and tried `uv add "mcp[cli]"`

, but it kept failing dependency resolution — because my `pyproject.toml`

had `requires-python = ">=3.9"`

left over from when the project was first scaffolded against system Python 3.9, but the actual MCP SDK needs 3.10+. I fixed it by pinning explicitly:

```
uv python pin 3.12
uv venv --python 3.12
```

Then I edited `requires-python`

to `>=3.10`

in `pyproject.toml`

, and the install went through clean.

Here's the one that actually got me for a while. After a supposedly successful install, importing `fastmcp`

kept throwing `ModuleNotFoundError: No module named 'mcp.server.fastmcp'`

— even though `import mcp`

worked fine and the folder clearly had a `server`

directory in it.

It turned out `uv pip show mcp`

revealed the installed package was version `2.0.0`

, with dependencies like `httpx2`

, `mcp-types`

, `pyjwt`

, and `pywin32`

— none of which belong to the real MCP SDK. There's an unrelated package squatting the name `mcp`

on PyPI, and it got pulled in instead of the real `modelcontextprotocol`

SDK.

The fix was to pin the version range explicitly:

```
uv remove mcp
uv add "mcp[cli]>=1.2.0,<2.0.0"
```

That pulled in the legit SDK (1.29.0 at the time), and `from mcp.server.fastmcp import FastMCP`

finally worked.

**Lesson:** if `uv add somepackage`

"succeeds" but nothing makes sense afterward, check `uv pip show`

— don't assume the name on PyPI is the project you think it is.

Claude Desktop reads its MCP server list from `claude_desktop_config.json`

, under a top-level `mcpServers`

key. This file has other unrelated app preferences in it now, so it's easy to accidentally add your server as a sibling of `mcpServers`

instead of nested *inside* it — which silently does nothing. I learned that one the hard way, staring at Settings → Developer → Local MCP servers wondering why only `filesystem`

showed up.

Correct shape:

```
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Technical\\mcp-code"]
    },
    "devto": {
      "command": "C:\\Users\\vdine\\.local\\bin\\uv.exe",
      "args": ["--directory", "C:\\Technical\\custom-mcp\\devto-mcp-server", "run", "dev-server.py"]
    }
  }
}
```

One more Windows-specific gotcha: Claude Desktop doesn't always inherit your shell's PATH, so `"command": "uv"`

alone sometimes fails to launch. Using the full path from `where.exe uv`

fixed it for good.

After a full quit-and-reopen of Claude Desktop (not just closing the window — it lingers in the tray), both servers showed up as **running**.

Before wiring anything into Claude Desktop, I tested the server standalone using:

```
uv run mcp dev src/mcp_server_demo/__init__.py
```

This spins up the MCP Inspector — a local web UI where you can call your tools directly and see raw request/response payloads, without needing Claude in the loop at all. Genuinely useful for catching bugs early instead of debugging through a chat interface.

With both servers connected, the actual publishing step is almost anticlimactic — I just talk to Claude normally:

"I have a file blog.txt in [folder], refine its content, then publish it to Dev.to as a draft with relevant tags."

Claude chains the tools itself:

`read_text_file`

(filesystem server) to pull the raw draft`publish_blog_to_devto`

(my custom server) with the title, markdown body, and tagsNo copy-pasting into Dev.to's editor, no manual formatting. I set `published: false`

first to review as a draft before making it live — cheap insurance against publishing something half-baked.

Most of the real learning here wasn't about MCP the protocol — it was standard environment debugging: PATH issues, Python version pinning, a squatted package name, and a JSON nesting mistake. MCP itself, once the environment was sane, was maybe 20 lines of Python with a docstring.

That's probably the underrated lesson: agent tooling is only as reliable as the boring plumbing underneath it. Get the venv, the package versions, and the config shape right, and the "AI does the interesting part" takes care of itself.

Next up, I'm planning to add a couple more tools to the same server — maybe one that pulls my GitHub commit history to help draft "what I built this week" posts automatically.
