Magic Tools
Pitfall NotesBy CooconAugust 18, 2026106 views6 min read

MCP Server at 100% CPU: Fixing Runaway Cloudflare Processes

While coding one day, I suddenly noticed the machine's fans spinning wildly. A quick top revealed CPU usage maxed out. Following the trail led to a surprising culprit: not a build task, not the browser, but the Cloudflare MCP server attached to Claude Code.

This article recaps the entire investigation process, the process model issue inherent in stdio-type MCPs, and the final mitigation decision: remove the MCP and switch to the Cloudflare REST API.

Symptoms: Two Processes Maxing Out a Core Each, with a Queue of Zombies Behind Them

First, I checked the processes. Sorted by CPU:

ps aux | grep -i mcp | grep -v grep

Output (redacted):

bjhl  56484  99.8  node .../node_modules/.bin/mcp-server-cloudflare run <account-id>
bjhl  56483  99.1  node .../node_modules/.bin/mcp-server-cloudflare run <account-id>
bjhl  77253   0.0  node .../node_modules/.bin/mcp-server-cloudflare run <account-id>
bjhl  77075   0.0  npm exec @cloudflare/mcp-server-cloudflare run <account-id>
bjhl  56199   0.0  npm exec @cloudflare/mcp-server-cloudflare run <account-id>
bjhl  56197   0.0  npm exec @cloudflare/mcp-server-cloudflare run <account-id>
bjhl  39408   0.0  node .../node_modules/.bin/mcp-server-cloudflare run <account-id>
bjhl  39332   0.0  npm exec @cloudflare/mcp-server-cloudflare run <account-id>

Three highly informative details:

  1. Two instances are each maxing out ~100% CPU (56483 / 56484) — it wasn't a memory leak, but a busy loop, saturating a single core.
  2. There were 7-8 processes for the same MCP server running simultaneously. The PID range is wide (39xxx / 56xxx / 77xxx), indicating they originated from multiple sessions started at different times. Old ones hadn't exited cleanly, and new ones had spun up.
  3. Each instance was actually a pair of processes: an npm exec parent process plus a node child process — because the configuration used the npx -y @cloudflare/mcp-server-cloudflare startup method, requiring a full npm resolution chain each time it was launched.

Why This Happens: The stdio MCP Process Model

Using claude mcp get cloudflare to confirm the configuration:

cloudflare:
  Scope: User config (available in all your projects)
  Status: ✔ Connected

This is a user-scoped stdio-type MCP — meaning every Claude Code session spawns its own dedicated server process upon startup. Open three terminal windows, and you get three separate process sets, explaining why instances accumulate.

The lifecycle contract for stdio MCP is: client exits -> child process's stdin receives EOF -> server exits on its own. However, this relies on the server correctly handling EOF. Based on the scene (and without further profiling its internal code), the most likely scenario for the two CPU-consuming instances is: the parent session is gone, and the server is stuck in a read loop that doesn't handle stream closure — the read returns immediately, the loop retries immediately, thus saturating a core at 100% and never exiting.

The other residual instances with 0% CPU, though quiet, are also products of the same reclamation flaw — they just happened to be idle and didn't enter a busy loop.

In summary, the pitfall encountered here is a combination of three layered problems:

Layer Problem
Usage Pattern An infrequently used operations tool was configured as user-scoped persistent — each session pays the cost of a full process set
Startup Chain The npx startup doubles the process count (npm exec + node), and the first run involves network resolution
Server Implementation The process doesn't exit after the session ends, and can even enter a busy loop

Mitigation: Remove Configuration + Clean Up Processes

The mitigation was straightforward. First, remove the configuration:

claude mcp remove cloudflare -s user
# Removed MCP server cloudflare from user config

Then, kill all remaining processes (including the quiet zombies):

pkill -f "mcp-server-cloudflare"

Verify the cleanup:

ps aux | grep -i cloudflare | grep -v grep
# (no output)
claude mcp list | grep -i cloudflare
# (no output)

CPU usage dropped immediately, and the fans went quiet.

Why Not Reinstall It: For Low-Frequency Operations, APIs Are More Reasonable Than MCP

After removal, a question arises: what about future Cloudflare operations?

The answer is to directly use the Cloudflare REST API. Its API design is mature, well-documented, and a single curl command can perform the same underlying calls as the MCP tool:

# List zones
curl -s "https://api.cloudflare.com/client/v4/zones" \
  -H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[].name'

# Purge cache for a specific zone
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"purge_everything":true}'

# Check DNS records
curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CF_API_TOKEN" | jq '.result[] | {name, type, content}'

AI assistants can already write curl commands — having it call the API directly is no different in capability than having it call through an MCP tool. The difference lies in the cost model:

  • MCP (Persistent): Whether used or not, each session must maintain a process; the quality of stdio implementations varies, and failed reclamation leads to incidents like this.
  • API (On-Demand): Zero cost when unused; troubleshooting involves a straightforward HTTP request path, which is much more transparent.

My decision-making criterion boils down to one sentence: only high-frequency, highly interactive tools requiring state persistence warrant an MCP connection; for low-frequency operations, just have the AI call the API directly. Cloudflare's case is the latter for me — DNS is changed maybe a few times a month; maintaining a persistent process set for it isn't cost-effective.

Takeaway Checklist

  1. Regular Health Checks: Use claude mcp list to see how many servers you have connected. For each, ask if its usage frequency justifies the persistent cost.
  2. Fans Kick On? Check MCP First: Run ps aux | grep mcp, paying attention to whether multiple instances of the same name are piling up — that's a signal of failed session reclamation.
  3. Use User Scope with Caution: User-scoped stdio MCPs spawn processes for "every session × every project"; for tools used only in a specific project, configure them to project scope.
  4. MCPs Started via npx Have Higher Cost: It doubles the process count + requires network access on first launch. For truly high-frequency use, consider a global installation and launch with an absolute path.
  5. Removing ≠ Losing Capability: Most SaaS MCP servers are just thin wrappers over REST APIs; curl + API token is always the fallback path.

FAQ

How do I troubleshoot an MCP server process using 100% CPU?

Start with ps aux | grep mcp, sorted by CPU, to identify the specific process. Check its command line to confirm which MCP server it is. Then count the number of same-named instances — a pile of multiple instances indicates processes from historical sessions weren't retracted. The emergency fix is pkill -f "<server_name>". The root solution is evaluating whether this MCP is worth running persistently; for infrequent tools, simply remove it and switch to the API.

How do I completely remove an MCP server from Claude Code?

First, use claude mcp get <name> to confirm its scope (user / project / local). Then use the corresponding claude mcp remove <name> -s <scope> to remove the configuration. Finally, use pkill -f to clean up any still-running residual processes. Deleting the configuration without killing the processes will let the runaway instances continue consuming CPU.

When should I use MCP vs. directly calling an API?

Tools that are high-frequency, highly interactive, and require maintaining state (like browser sessions or database connections) are suitable for MCP. For low-frequency operational tasks (changing DNS, clearing caches, checking configurations), having the AI write curl commands to call the REST API is more cost-effective — zero persistent cost, and the troubleshooting path is much clearer when issues arise.

References

Get field notes like this every Saturday

Subscribe to Dev Breakfast: daily AI coding picks at 8:00, plus a Saturday roundup of this week's hands-on tests with Claude Code / Codex / local models. Written in Chinese.

Related Articles

Dev Breakfast · 2026-10-02

Today's headline: Figma's MCP Only Accepts Whitelisted Clients, and the MCP's Creator Has Spoken Out. Plus 3 more: turbopuffer Demotes ANN to a Secondary Index: Are Vector Databases Really Dead?; From v0.1.9 to v0.1.13: TileLang Writes GPU Kernels in Python; and more.

daily-intelOct 2, 20267 min
14

Dev Breakfast · 2026-10-01

Today's headline: The Same Prompt: Why Some People's Output Looks Like a Designer Made It. Plus 4 more: Pi.dev: From "No MCP" to Shoving MCP into the Kernel; Gemini 4 Argon Is Priced at $2: Cybersecurity Teams Get It First; and more.

daily-intelOct 1, 20268 min
39

Dev Breakfast · 2026-09-30

Today's headline: Anthropic Prospectus: Revenue Increased 12 Times, Loss of 42 Billion. Plus 4 more: 0.8B Model Trained at Home: Choose One from 254 Options in 28 ms; 7 ESP32-S3 Chips Chained Together to Run a 0.5B 1.58-bit Model; and more.

daily-intelSep 30, 20268 min
83
Claude Code install errors, reproduced: EACCES, a 600s mirror stall, Node 20 silently getting an old version, a region-block install.sh, and the native installer removing your npm copy

Claude Code install errors, reproduced: EACCES, a 600s mirror stall, Node 20 silently getting an old version, a region-block install.sh, and the native installer removing your npm copy

I reproduced every Claude Code install failure I could on macOS: 15 verbatim errors, each with wall time and exit code. npm -g into /usr/local fails with EACCES, exit 243. A cache dir that is merely 0555 gets blamed on root-owned files, with sudo chown advice. From Beijing, npmmirror took 147s and then >600s, npmjs 11-12s (2 samples each). On Node 20, an unpinned install silently lands on 2.1.197. Fetching claude.ai/install.sh from a blocked region gives curl exit 0 and a 447 KB HTML page. The native installer runs npm uninstall -g on your npm copy without saying so; it removed mine.

claude-codetroubleshooting+5
pitfallsSep 29, 202611 min
102

Published by Magic Tools