I just released my first MCP server: mcp-cloudflare-crunchtools. It lets Claude Code (and other MCP-compatible AI assistants) manage Cloudflare DNS records, Transform Rules, Page Rules, and cache. But this post isn’t really about Cloudflare—it’s about how I built this server and why I think it matters for the emerging MCP ecosystem. As I’ve gotten deeper into this MCP world, I’ve realized there’s a lot of low quality MCP servers, designed and developed in strange ways. Some appear to be learning projects, others give me the sense that they’re almost AI slop/spam trying to drive crypto donations.
I kept thinking to myself, I think I’d trust a generic MCP server built by Claude more than these ones I could download from some rando on the Internet. And so, I decided to build a small portfolio of MCP servers and publish them. I’m doing this for the benefit of open source and learning. This is the first MCP server from Crunchtools, but it won’t be the last. Expect more, for things like WordPress, Mediawiki, Request Tracker, Pagure, and other open source projects I use.
The architectural decisions I’m laying out here will carry forward to every MCP server we publish. If you’re building MCP servers yourself, maybe this thinking will be useful.
What is MCP?
The Model Context Protocol (MCP) is an open standard for connecting AI applications to external systems. Think of it like USB-C for AI—a standardized way to plug tools, data sources, and APIs into assistants like Claude. Instead of every AI app building custom integrations, MCP provides a common interface that works everywhere.
This is a big deal. It means we can build one MCP server for Cloudflare and it works with Claude Code, Cursor, Gemini CLI or any other MCP-compatible client that comes along.
Why We Built This
I’ve been using Claude Code for development work, and I kept finding myself needing to manage DNS records and cache settings. The workflow was: leave Claude, open the Cloudflare dashboard, make changes, come back. It broke my flow. MCP solves this. Now I can say “purge the cache for crunchtools.com” and it just happens. Create DNS records, set up URL rewrites, manage page rules—all without leaving my terminal. I can create a ticket for a new site, and let Claude finish the entire workflow, from creating DNS, to making sure the site works when its done. But here’s the thing: MCP servers handle sensitive credentials. My Cloudflare API token has access to every domain I manage. I couldn’t just throw something together and hope for the best. This needed to be built right. Other MCP servers I found didn’t make me feel confident in their designs.
Built With Claude Code, Designed By Humans
Yes, I used Claude Code to write this MCP server. There’s a certain recursion to using an AI assistant to build tools for AI assistants. But let me be clear about what that means—and what it doesn’t. Claude Code is fast. It can scaffold a project, implement API integrations, write tests, and handle the mechanical parts of coding at a pace I can’t match. That’s valuable. But speed without direction is just fast chaos. The brain power still has to come from somewhere (I’m not convinced jobs are going away, I think it’s going to be more like Jevons Paradox):
- Architecture decisions — I researched and used many MCP servers before writing this one. So many of them have flaws I don’t like, and in traditional open source fashion, I decided to scratch my own itch. Some MCP servers even expect you to connect to a service running workers on Cloudflare itself, which exposes a you to man-in-the-middle attacks. This MCP server was designed to keep everything local on your computer. I considered Typescript, Rust and Python. We landed on Python because it’s cross-platform, secure, and runs reasonably efficient. Also, FastMCP appears to be growing in popularity.
- Security model — Claude Code doesn’t wake up thinking about threat actors. I worry a lot about man-in-the-middle attacks when downloading and using MCP servers. I don’t like plugins in wordpress to make the MCP server (yes, I’ve seen this). I don’t like worker nodes on Cloudflare running to do some strange stuff. These MCP servers are designed to keep all credentials locally, and reach directly out to the API. So many MCP servers that you can download and use, violate these very basic principles.
- Language and tooling choices — This required real thinking before any code was written. This is the part people miss when they hear “I built it with AI.” The keyboard time shrinks. The thinking time doesn’t. I chose: Claude Code, Python, Pypi, and Hummingbird containers (shameless plug for a recent blog Przemysław and I wrote).
- Licensing: The code is AGPL-3.0 licensed: github.com/crunchtools/mcp-cloudflare
The Architecture Decisions Behind Crunchtools MCP Servers
Before writing any code, I spent time thinking through the foundational choices that would shape not just this project, but every MCP server we publish going forward. These decisions matter because they compound—get them right once and every future project benefits.
Why Python (and Not Rust or TypeScript)
This wasn’t obvious. MCP has SDKs for multiple languages, and each has real advantages:
Rust would give us memory safety, small binaries, and excellent performance. For a network-heavy MCP server making API calls, the performance argument is less compelling—we’re waiting on HTTP responses, not crunching numbers. TypeScript has a strong MCP SDK and a lot of popularity, but I’m just less comfortable with Node.js in general. I’ve programmed off and on in Python for many years and have a feel fro the ecosystem. This may be my own bias.
Python won for several reasons:
- FastMCP is excellent. It handles MCP protocol details cleanly and lets me focus on the integration logic.
- Pydantic gives us runtime type validation that’s actually useful for security—we can enforce that zone IDs are exactly 32-character hex strings, not just trust that callers send valid data.
- httpx is a modern, async-capable HTTP client that handles the Cloudflare API elegantly.
- Claude Code writes Python extremely well. This matters when you’re pair-programming with an AI. The iteration speed is noticeably faster than with Rust. I’ve tried to vibe code a couple of rust projects and actually ran into real roadblocks (for now), though I’m still “Rust Curious” 🙂
- Cross-platform support is straightforward. Python runs everywhere, and tools like
uvxmake zero-install execution possible.
Why Hummingbird Container Images
Container security is something I think about a lot. Most developers pull python:3.12 and move on. That image works, but it carries baggage—literally hundreds of CVEs in a typical scan. Here’s what I see regularly:
| Base Image | Typical CVE Count |
|---|---|
| python:3.12 (Debian) | 100-200+ |
| python:3.12-slim | 50-100 |
| python:3.12-alpine | 10-30 |
| Hummingbird Python | <10 |
Hummingbird is Red Hat’s effort to solve this problem in the container-native world: near-zero CVEs. The philosophy is simple—include only what’s necessary to run Python applications. No compilers, no extra shells, no accumulated cruft from general-purpose distributions.
Why this matters for MCP servers:
- Quicker download/startup time – When MCP servers are starting and stopping a bunch, we want a tiny footprint
- Smaller attack surface — Fewer packages means fewer things that can be exploited. An attacker can’t leverage a vulnerability in a package that isn’t installed.
- Faster security response — When you have 200 packages, keeping up with CVE patches is a job. When you have 20, it’s manageable.
- uv pre-installed — Hummingbird images come with the
uvpackage manager, which is significantly faster than pip for dependency resolution and installation. This speeds up container builds and reduces CI time. - Non-root by default — The images run as a non-root user out of the box. Defense in depth.
I could have used Alpine for small image sizes, but Alpine uses musl libc instead of glibc, which causes subtle compatibility issues with some Python packages. Hummingbird gives me the small footprint without the compatibility headaches. This choice will carry forward to every Crunchtools MCP server. We’ll always build on Hummingbird.
Why stdio Transport
MCP supports multiple transport mechanisms. The two main options are:
- stdio — Communication over stdin/stdout
- HTTP/SSE — Communication over HTTP with Server-Sent Events
We use stdio exclusively. Here’s why:
- No network exposure — An stdio-based MCP server has no listening ports. There’s nothing for an attacker to scan, nothing to accidentally expose to the internet. The server only communicates with the parent process that spawned it.
- Universal compatibility — stdin/stdout work everywhere. Linux, macOS, Windows, containers, VMs, CI runners. No firewall rules to configure, no ports to allocate, no TLS certificates to manage.
- Simple security model — The only way to talk to the server is to be the process that launched it. There’s no authentication to implement because there’s no network access to authenticate.
- Process lifecycle — When the parent process (Claude Code) exits, the MCP server exits. Clean, predictable lifecycle management with no orphaned processes or dangling connections.
HTTP transport makes sense for MCP servers that need to be shared across multiple clients or run as persistent services. For developer tools running locally, stdio is simpler and more secure.
Security First
Most MCP servers I’ve seen treat security as an afterthought. That’s a mistake. These servers sit between AI assistants and production infrastructure. A compromised MCP server could mean:
- Leaked API tokens
- DNS hijacking
- Traffic redirection
- Cache poisoning
Claude was actually very helpful in designing this defense in depth model:
Layer 1: Token Protection
- API tokens stored as
SecretStr(never accidentally logged) - Environment variable only—never in files, never in command args
- Scrubbed from all error messages
Layer 2: Input Validation
- Every input validated through Pydantic models
- Allowlists for record types and actions
- Zone IDs must be exactly 32-character hex strings
- Unknown fields rejected outright
Layer 3: API Hardening
- Hardcoded API base URL (prevents SSRF attacks)
- TLS certificate validation
- Request timeouts and response limits
Layer 4: No Dangerous Operations
- No filesystem access
- No shell execution
- No
eval()orexec()
Layer 5: Supply Chain Security
- Weekly automated CVE scanning via GitHub Actions
- Dependabot enabled
- Container built on Hummingbird for minimal CVE exposure
I documented all of this in SECURITY.md. If you’re building MCP servers, steal my threat model.
Cross-Platform by Design
MCP servers should work everywhere. Not just on my Linux workstation, but on macOS, Windows, in containers, in CI/CD pipelines. That drove several decisions:
Multiple installation methods:
uvx mcp-cloudflare-crunchtools(zero install, just run)pip install mcp-cloudflare-crunchtools(traditional)podman run quay.io/crunchtools/mcp-cloudflare(containerized)
Python 3.10+ because it’s widely available and provides the type hinting features we wanted.
stdio transport because it’s universal—no ports, no firewalls, no networking complexity.
Container image built on Hummingbird—minimal footprint with low CVE exposure.
The First of Many
This Cloudflare server establishes the template. Every Crunchtools MCP server going forward will share:
- Python + FastMCP for the implementation
- Hummingbird container images for minimal CVE exposure
- The same module structure for consistency and maintainability
- Defense-in-depth security with documented threat models
- Triple distribution via uvx, pip, and container images
- Automated CVE scanning through GitHub Actions
- AGPL-3.0 licensing to ensure improvements flow back to the community
I’m already planning additional MCP servers for other services I use regularly. The goal is a library of MCP servers I can trust with production credentials—servers that work everywhere, follow security best practices, and are built on a foundation I’ve thought through carefully.
The code is written with Claude Code. The architecture is designed by humans. That combination moves fast without cutting corners.
Try The Container
claude mcp add mcp-cloudflare-crunchtools \
--env CLOUDFLARE_API_TOKEN=your_token_here \
-- podman run -i --rm -e CLOUDFLARE_API_TOKEN quay.io/crunchtools/mcp-cloudflare
Try The Native Python
claude mcp add mcp-cloudflare-crunchtools \
--env CLOUDFLARE_API_TOKEN=your_token \
-- uvx mcp-cloudflare-crunchtools
Then ask Claude to list your zones, create a DNS record, or purge your cache. If you have thoughts on security with MCP servers, please share them because I want to learn and improve these. I’m still learning in this space, and do not claim to be an expert. But, I do know that my intentions are better than many people creating and publishing MCP servers today! Now, I have Claude working on my next MCP server for WordPress, because I hate all of those that I’ve found as well!