Zero Dependencies, 250KB, 486 Tests: What I Learned Building an MCP Client A developer built mcptoon, a zero-dependency CLI tool that reduces AI agent context window usage by keeping MCP tool schemas out of context. The project, which uses only Python's standard library, was motivated by the uv security incident and resulted in hand-rolled HTTP, CLI, validation, and terminal formatting code, adding significant development cost but providing deeper protocol understanding. This is not a product pitch. It's an engineering diary. If you want the pitch, the README is here . This is about the cost of zero. Six weeks ago I started building mcptoon https://github.com/activeing123/mcptoon — a CLI tool that sits between AI agents Claude Code, Cursor, Codex and MCP servers. The problem it solves: MCP tool schemas get injected into your context window as JSON. 255 tools = ~91K tokens of JSON braces, brackets, quotes, and commas — before any actual work happens. mcptoon keeps schemas out of context. The agent runs shell commands. Only the compact result enters context. But none of that is what I want to talk about. I want to talk about the decision that shaped everything: zero dependencies . pyproject.toml dependencies = Not "minimal dependencies." Not "few dependencies." Zero. The trigger was the uv security incident https://github.com/astral-sh/uv/issues/9423 . A transitive dependency in a popular Python tool had a supply chain vulnerability. Thousands of projects were affected. Not because they did anything wrong — because someone upstream did something wrong. I looked at my own pip install history. How many packages had I installed in the last year? Hundreds. Each one pulling in its own dependency tree. How many of those dependencies had I audited? Zero. So when I started mcptoon, I made a rule: no third-party imports. Python standard library only. This sounded reasonable in theory. In practice, it meant I was about to hand-roll a lot of things. requests → hand-write an HTTP client The standard library has http.client and urllib . They work. But they're verbose. Here's what a POST request looks like with urllib : python import json, urllib.request def http post url, data, headers=None : body = json.dumps data .encode "utf-8" req = urllib.request.Request url, data=body, headers={"Content-Type": "application/json", headers or {} } with urllib.request.urlopen req, timeout=30 as resp: return json.loads resp.read .decode "utf-8" That's 8 lines. With requests , it would be 1: python import requests resp = requests.post url, json=data, headers=headers, timeout=30 Cost: ~200 lines of HTTP plumbing streaming SSE, error handling, retry logic, auth . With requests , maybe 30 lines. Was it worth it? For SSE Server-Sent Events parsing — yes, I learned how the protocol actually works. For basic HTTP — no, it was just plumbing. click or argparse extensions → hand-write CLI parsing Python's stdlib argparse is... fine. But click is so much nicer. Decorators, subcommands, context, help text generation. With argparse , I ended up with a 400-line CLI dispatch function: python def main : parser = argparse.ArgumentParser prog="mcptoon" sub = parser.add subparsers dest="command" ... 15 subcommands, each with its own args ... add cmd = sub.add parser "add" add cmd.add argument "name" add cmd.add argument "--stdio", nargs="+" add cmd.add argument "--url" ... etc for every command Cost: ~400 lines of argument parsing. With click , maybe 150 lines. pydantic → hand-write validation MCP servers return JSON. Without pydantic , every response is a dict and you validate by hand: python def validate tool result result : if not isinstance result, dict : raise ValueError "Expected dict" if "content" not in result: raise ValueError "Missing 'content'" for item in result "content" : if "type" not in item: raise ValueError "Each content item needs 'type'" if item "type" == "text" and "text" not in item: raise ValueError "text content missing 'text' field" Cost: ~300 lines of validation across the codebase. With pydantic , models would self-validate. rich → hand-write terminal formatting This one actually surprised me. I didn't need rich . ANSI escape codes work fine: python def bold text : return f"\033 1m{text}\033 0m" def green text : return f"\033 32m{text}\033 0m" def dim text : return f"\033 2m{text}\033 0m" Cost: ~50 lines. Not bad. pytest plugins → plain unittest -style tests Actually, I do use pytest as a dev dependency in project.optional-dependencies . But no pytest-mock , no pytest-cov , no responses , no httpx for mocking. Just unittest.mock : python from unittest.mock import patch, MagicMock @patch "mcptoon.client.MCPClient. stdio request" def test call tool mock request : mock request.return value = {"result": {"content": {"type": "text", "text": "hello"} }} client = MCPClient stdio= "echo", "test" result = client.call tool "search", {"q": "test"} assert result "content" 0 "text" == "hello" Cost: More verbose test setup. But 486 tests still run in 0.5 seconds because there are no heavy fixtures. bash $ pip install mcptoon Downloaded 250KB. Installed in 0.3s. For comparison, a typical MCP client with requests , pydantic , click , rich : requests + its deps: ~5MB pydantic + its deps: ~15MB click : ~200KB rich : ~5MBmcptoon is 1% of that . bash $ pip audit mcptoon No vulnerabilities found. Because there's nothing to audit beyond stdlib. When the next supply chain attack hits npm or PyPI, mcptoon users are unaffected. Not because I was clever — because there's nothing to attack. Most Python CLI tools are developed on macOS/Linux and "should work on Windows." With zero dependencies, there are no platform-specific binary wheels to worry about. No uvloop that doesn't support Windows. No uvicorn worker model differences. Just sys.platform checks for .cmd vs binary names: python def resolve cmd cmd : if sys.platform == "win32" and not cmd 0 .endswith ".cmd" : if shutil.which cmd 0 + ".cmd" : cmd = cmd 0 + ".cmd" + cmd 1: return cmd mcptoon works on Windows, macOS, and Linux. Not "should work" — "tested on all three." bash $ time pip install mcptoon real 0m0.3s $ time pip install