# I built an MCP server that lets Claude manage my GitHub profile and DEV.to articles — here's how

> Source: <https://dev.to/enjoy_kumawat/i-built-an-mcp-server-that-lets-claude-manage-my-github-profile-and-devto-articles-heres-how-kid>
> Published: 2026-06-21 08:53:50+00:00

I wanted Claude to know about my developer presence — my repos, my articles, my stats — without me having to paste links every time. So I built a small MCP server that connects both GitHub and DEV.to directly to Claude.

Now I can just say: *"What's my most starred repo?"* or *"Draft a DEV.to article about my latest project"* — and Claude actually does it.

Here's how I built it in under 100 lines of Python.

[Model Context Protocol](https://github.com/modelcontextprotocol/python-sdk) is an open standard that lets you connect external tools and APIs to Claude (and other LLMs). You define tools with a decorator, run a server, and Claude can call those tools during a conversation.

I'm a contributor to the MCP Python SDK, so naturally I wanted to build something real with it.

The server exposes 7 tools across two APIs:

**GitHub:**

`get_github_profile`

— followers, repos, bio`list_repos`

— sort by stars/forks/updated`get_repo_stats`

— stars, forks, issues for any repo**DEV.to:**

`list_articles`

— all your articles with reaction/view counts`create_article`

— draft or publish a new article`update_article`

— edit title, body, or publish state`get_article_stats`

— reactions, comments, page viewsInstall the only dependency:

```
pip install mcp[cli]
```

Create a `.env`

file:

```
GITHUB_TOKEN=your_github_token
GITHUB_USERNAME=your_github_username
DEV_TO_API=your_devto_api_key
DEV_USERNAME=your_devto_username
```

Here's the full server (`server.py`

):

``` python
import os, json, urllib.request
from mcp.server.fastmcp import FastMCP

GITHUB_USERNAME = os.environ.get("GITHUB_USERNAME", "")
DEV_USERNAME = os.environ.get("DEV_USERNAME", "")

mcp = FastMCP("developer-presence")

def _gh(path, method="GET", data=None):
    req = urllib.request.Request(f"https://api.github.com{path}", method=method)
    req.add_header("Authorization", f"token {os.environ['GITHUB_TOKEN']}")
    req.add_header("Accept", "application/vnd.github.v3+json")
    if data:
        req.add_header("Content-Type", "application/json")
        req.data = json.dumps(data).encode()
    with urllib.request.urlopen(req) as r:
        return json.loads(r.read())

def _dev(path, method="GET", data=None):
    req = urllib.request.Request(f"https://dev.to/api{path}", method=method)
    req.add_header("api-key", os.environ["DEV_TO_API"])
    req.add_header("Content-Type", "application/json")
    if data:
        req.data = json.dumps(data).encode()
    with urllib.request.urlopen(req) as r:
        return json.loads(r.read())

@mcp.tool()
def get_github_profile() -> dict:
    """Fetch public GitHub profile stats."""
    u = _gh(f"/users/{GITHUB_USERNAME}")
    return {"login": u["login"], "name": u.get("name"), "bio": u.get("bio"),
            "public_repos": u["public_repos"], "followers": u["followers"]}

@mcp.tool()
def list_repos(sort: str = "updated", limit: int = 10) -> list:
    """List public repos sorted by updated/stars/forks."""
    repos = _gh(f"/users/{GITHUB_USERNAME}/repos?sort={sort}&per_page={min(limit,100)}")
    return [{"name": r["name"], "stars": r["stargazers_count"],
             "language": r.get("language"), "url": r["html_url"]} for r in repos]

@mcp.tool()
def list_articles(per_page: int = 10) -> list:
    """List your DEV.to articles with stats."""
    articles = _dev(f"/articles/me?per_page={min(per_page,30)}")
    return [{"id": a["id"], "title": a["title"],
             "reactions": a.get("positive_reactions_count", 0),
             "page_views": a.get("page_views_count", 0)} for a in articles]

@mcp.tool()
def create_article(title: str, body_markdown: str,
                   tags: list[str] = None, published: bool = False) -> dict:
    """Create a new DEV.to article."""
    payload = {"article": {"title": title, "body_markdown": body_markdown, "published": published}}
    if tags:
        payload["article"]["tags"] = tags
    result = _dev("/articles", method="POST", data=payload)
    return {"id": result["id"], "url": result.get("url")}

if __name__ == "__main__":
    mcp.run()
```

(Full version with all 7 tools on [GitHub](https://github.com/enjoykumawat/developer-presence-mcp))

```
# Dev mode — opens MCP Inspector in browser
mcp dev server.py

# Or wire into Claude Desktop
```

For Claude Desktop, add this to `claude_desktop_config.json`

:

```
{
  "mcpServers": {
    "developer-presence": {
      "command": "python",
      "args": ["/path/to/server.py"],
      "env": {
        "GITHUB_TOKEN": "...",
        "GITHUB_USERNAME": "...",
        "DEV_TO_API": "...",
        "DEV_USERNAME": "..."
      }
    }
  }
}
```

Once connected, I can ask Claude things like:

It sounds simple, but having your developer presence queryable from a conversation is surprisingly useful — especially when you're writing content and want to reference your own work.

Everything is on GitHub: [enjoykumawat/developer-presence-mcp](https://github.com/enjoykumawat/developer-presence-mcp)

If you want to adapt it for your own username, just update the `.env`

file — no other changes needed.

I'm a contributor to the [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk). If you're interested in building MCP servers, feel free to reach out or check out the official SDK — it's a great place to start.

*Follow me here on DEV.to @enjoy_kumawat for more AI tooling content.*
