<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ev3lynx727</title>
    <description>The latest articles on DEV Community by Ev3lynx727 (@ev3lynx727).</description>
    <link>https://dev.to/ev3lynx727</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3919940%2Ffda8eb48-9cdf-47bd-848a-32d4c468e386.png</url>
      <title>DEV Community: Ev3lynx727</title>
      <link>https://dev.to/ev3lynx727</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ev3lynx727"/>
    <language>en</language>
    <item>
      <title>The Hybrid Agent: A Retrospective</title>
      <dc:creator>Ev3lynx727</dc:creator>
      <pubDate>Mon, 13 Jul 2026 14:40:45 +0000</pubDate>
      <link>https://dev.to/ev3lynx727/the-hybrid-agent-a-retrospective-a8e</link>
      <guid>https://dev.to/ev3lynx727/the-hybrid-agent-a-retrospective-a8e</guid>
      <description>&lt;h2&gt;
  
  
  The Closing
&lt;/h2&gt;

&lt;p&gt;Every series earns its final paragraph. This one earned a full article.&lt;/p&gt;

&lt;p&gt;The question I started with was: &lt;em&gt;MCP or CLI — which is cheaper?&lt;/em&gt; The answer, after 10 sources, 6 MCP servers, a CLI bridge, a container stack, four layers of wrong assumptions, and five articles, is: &lt;strong&gt;it's the wrong question.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The real question is: &lt;em&gt;What survives?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: The Token Cost Theory
&lt;/h2&gt;

&lt;p&gt;The numbers held up. External benchmarks matched our measurements: MCP at ~170 tokens per call, raw CLI at ~460 (with filtering) or ~3,100 (without). Schema overhead for a 93-tool monolith hit 55,000 tokens — $51,000/month at scale.&lt;/p&gt;

&lt;p&gt;The insight that survived wasn't the cost numbers. It was the G/N ratio: for a tool used on 2 of 20 prompts, &lt;strong&gt;99.3% of MCP tokens are wasted on schema descriptions the agent never needed.&lt;/strong&gt; The problem isn't MCP's per-call cost. It's the injection pattern — loading every schema into every prompt regardless of need.&lt;/p&gt;

&lt;p&gt;Add a gateway. Filter schemas by actual usage. Then MCP's per-call cost becomes negligible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: The Hybrid Architecture
&lt;/h2&gt;

&lt;p&gt;The plugin died with the daemon. The MCP servers didn't. That frozen moment — checking the process table and seeing ark-memory still running on 8102 — is the single insight that shaped everything after.&lt;/p&gt;

&lt;p&gt;Lifecycle independence isn't architectural theory. It's the difference between a system that recovers its own state and one that leaks it, session by session.&lt;/p&gt;

&lt;p&gt;The quote that survived: &lt;em&gt;"A plugin inherits its parent's mortality. An MCP server manages its own."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: The Container Stack
&lt;/h2&gt;

&lt;p&gt;The container decision looked like overhead on day one. Looked like the obvious choice by WSL instance three. The insight that holds across every deployment conversation since: the cost of environment-specific setup scripts compounds with every new target. The cost of the container is paid once.&lt;/p&gt;

&lt;p&gt;The pattern that survived: one image, many clients, zero per-target configuration. From any WSL instance, from Windows, from CI — the same stack, the same servers, the same behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 4: The Decision Tree
&lt;/h2&gt;

&lt;p&gt;PR #1372's 77-day lifecycle taught me more than any benchmark. The four-layer decision tree (plugin vs server, stdio vs SSE, focused vs monolith, MCP vs CLI) was extracted from production failures, not architecture theory. Every wrong choice is documented in a closed PR, a deleted file, or a systemd restart that wiped a session.&lt;/p&gt;

&lt;p&gt;The rule that survived: &lt;em&gt;An MCP server isn't a plugin you run differently. It's a process that survives its client.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Didn't
&lt;/h2&gt;

&lt;p&gt;Not everything from this series survived contact with production.&lt;/p&gt;

&lt;p&gt;The 93-tool GitHub monolith didn't survive. Replaced by 3 focused servers.&lt;/p&gt;

&lt;p&gt;The per-WSL install scripts didn't survive. Replaced by one container.&lt;/p&gt;

&lt;p&gt;The git-push MCP tool didn't survive. Replaced by a 50-token shell command.&lt;/p&gt;

&lt;p&gt;The assumption that better benchmarks lead to better architecture didn't survive either. The numbers helped us find the problem. The failures helped us understand it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed
&lt;/h2&gt;

&lt;p&gt;Before this series, our MCP strategy was: build a tool for everything, deploy it however, optimize later.&lt;/p&gt;

&lt;p&gt;After this series, the strategy is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;MCP for structure, CLI for execution.&lt;/strong&gt; Queries need typed schemas. Actions need zero overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focused servers win.&lt;/strong&gt; 3-8 tools. One domain. ~1,500 tokens per server. Independently deployable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containers are the default deployment.&lt;/strong&gt; One image, any client. WSL, Windows, CI — same stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The decision tree runs before the first line of code.&lt;/strong&gt; The wrong architectural choice at layer 1 compounds through every later layer.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Closing
&lt;/h2&gt;

&lt;p&gt;The stack that survived: &lt;a href="https://github.com/Ev3lynx727/oh-my-mcp" rel="noopener noreferrer"&gt;&lt;code&gt;oh-my-mcp&lt;/code&gt;&lt;/a&gt; gateway for lifecycle management. &lt;a href="https://github.com/Ev3lynx727/commands-rtk" rel="noopener noreferrer"&gt;&lt;code&gt;commands-rtk&lt;/code&gt;&lt;/a&gt; CLI bridge for everything else. Six MCP servers, each with 3-8 tools, each a standalone process with its own port and its own logs.&lt;/p&gt;

&lt;p&gt;Build processes that survive client death. Route schemas that match actual usage. Make the architectural decision before the first line of code — and when you get it wrong, close the PR and rewrite it. It'll take three days, not seventy-seven.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>devops</category>
      <category>devchallenge</category>
    </item>
    <item>
      <title>The Integration Decision Tree: Plugin vs MCP Server vs CLI Bridge</title>
      <dc:creator>Ev3lynx727</dc:creator>
      <pubDate>Mon, 13 Jul 2026 14:21:29 +0000</pubDate>
      <link>https://dev.to/ev3lynx727/the-integration-decision-tree-plugin-vs-mcp-server-vs-cli-bridge-124p</link>
      <guid>https://dev.to/ev3lynx727/the-integration-decision-tree-plugin-vs-mcp-server-vs-cli-bridge-124p</guid>
      <description>&lt;h2&gt;
  
  
  Hook
&lt;/h2&gt;

&lt;p&gt;When building local-first architectures, the instinct is often to tightly couple everything. If a system needs a new shell execution tool or a data filter, the default move is to drop it in as an in-memory plugin. It feels like the cleanest path: it runs in the same process, shares the same lifecycle, and requires zero initial configuration.&lt;/p&gt;

&lt;p&gt;But that tight coupling is exactly where local systems fracture.&lt;/p&gt;

&lt;p&gt;As the environment grows, the plugin model starts exposing its limits. You lose process ancestry when a local daemon restarts. You hit cold caches on reconnections. You lose your audit trail because plugins can't log independently from the host process, burying critical observability.&lt;/p&gt;

&lt;p&gt;What starts as a simple 20-line feature turns into an architectural bottleneck.&lt;/p&gt;

&lt;p&gt;The problem isn't the code itself — it is the execution boundaries. The difference between a fragile monolithic local setup and a resilient, modular ecosystem comes down to the architectural decision made before the first line of code is written: Should this be a Plugin, a standalone MCP Server, or a CLI Bridge?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Wrong Assumption
&lt;/h2&gt;

&lt;p&gt;The pattern we followed at first was: new capability equals new plugin. It was the obvious path — OpenCode's plugin system is well-documented, auto-discovered from &lt;code&gt;~/.config/opencode/plugins/&lt;/code&gt;, zero deployment. Drop a &lt;code&gt;.ts&lt;/code&gt; file, restart, it works.&lt;/p&gt;

&lt;p&gt;The first plugin was fine. The second was fine. By the sixth, we had the same two bugs across three different frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Process ancestry loss.&lt;/strong&gt; Every plugin inherits OpenCode's process table. When the daemon restarts (config change, crash, update), every plugin restarts with it — cold cache, empty connection pools, every in-flight task evaporated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silent death.&lt;/strong&gt; A plugin crash doesn't surface. The parent process catches the exception, logs it to its own log, and continues. The plugin is gone. No one knows until the next session fails to find the tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first time I noticed was when ark-delegator stopped routing tasks. No error. No alert. Just a silent gap in the process table. The plugin had died with the daemon restart three hours earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decision Tree
&lt;/h2&gt;

&lt;p&gt;Four layers. Each layer is one decision, not a configuration. The wrong choice at any layer compounds through every later choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: Plugin or MCP Server?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plugin?
→ Same process: zero config, zero network, zero deployment
→ Same mortality: daemon restart = plugin restart
→ Same visibility: plugin crash = silent death
→ No independent logging: must use parent's logger

MCP Server?
→ Own process: survives client death, restarts independently
→ Own logs: stdout/stderr captured by supervisor
→ Network boundary: SSE port, reachable from any client
→ +58ms overhead: stdio establishment per request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Answer: If the tool must survive a daemon restart — and every production tool should — it's an MCP server. The plugin is for prototyping, not production.&lt;/p&gt;

&lt;p&gt;We discovered this ordering wrong. We started with plugins because they were easier. We should have started with servers because they survive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2: Stdio or SSE?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stdio (child process)?
→ Zero network: works offline, no latency
→ No auth: stdin/stdout within same machine
→ Version-controlled: pin exact npm version
→ Process isolation: crash doesn't cascade
→ 2.7GB RAM per session — 45 processes for 6 servers

SSE (remote endpoint)?
→ Shareable: one server, N clients
→ Survives sessions: client disconnects, server stays
→ Port-based: numbered allocation, no collision
→ Docker-compatible: map ports, exec from any container
→ ~3 seconds startup per server (supergateway)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Answer: Stdio for single-machine, single-session dev. SSE for anything shared between clients, sessions, or machines.&lt;/p&gt;

&lt;p&gt;Our first approach was npx-based stdio for everything: &lt;code&gt;npx @ev3lynx/oh-my-mcp&lt;/code&gt; spawned three processes (sh → npx → node), each server consumed ~450MB, and every new WSL instance needed the same install. We ran out of RAM at 6 servers. The switch to SSE via supergateway cut per-server overhead in half and made them shareable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3: Focused or Monolith?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Focused (3-8 tools)?
→ 8,000-10,000 tokens total schema for all 6 servers
→ Agent loads only what it needs for current operation
→ Independent versioning: update one without touching others
→ Easy to replace: swap one server, not the catalog

Monolith (93 tools)?
→ 55,000 tokens of schema — $51,000/month at scale
→ Everything in every prompt, used or not
→ One bad tool update breaks the whole server
→ Hard to audit: 93 tools, who uses which?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Answer: If a tool group fits in 3-8 operations, it's a focused server. If it exceeds 8, split. The constraint isn't technical — it's cognitive. 8 tools is the number of items the human brain can hold in working memory.&lt;/p&gt;

&lt;p&gt;We made this mistake once: a single GitHub server with 93 tools. The schema tax was 55K tokens. We never used 80 of those tools. The fix was replacing it with 3 focused servers (repo ops, issue tracking, code search).&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 4: MCP Tool or CLI Bridge?
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MCP Tool?
→ Structured output: known schema, capped response
→ 170 tokens per call, schema overhead per session
→ Best for: query, search, read operations

CLI Bridge?
→ Unstructured: whatever the command returns (RTK-filtered)
→ 50 tokens per call, zero schema overhead
→ Best for: execute, push, write operations
→ Audit trail: JSONL logger, exit codes, duration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Answer: Read through MCP, write through CLI. Queries need structure. Actions need speed.&lt;/p&gt;

&lt;p&gt;We spent a week building an MCP tool for &lt;code&gt;git push&lt;/code&gt;. It returned "done" in JSON. We deleted it and went back to &lt;code&gt;git push origin main&lt;/code&gt; — 50 tokens, same result, fewer lines of code to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rule That Handles 95% of Decisions
&lt;/h2&gt;

&lt;p&gt;After four layers of mistakes, one rule covers almost everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is the operation a query?         → MCP tool (structured, typed)
Is it a side effect with small output? → CLI bridge (fast, audited)
Does it need to survive a restart? → MCP server (own process)
Is it a prototype?                → Plugin (fast, temporary)
Does it run on more than one machine? → Container (one image, many clients)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't theory. These are the rules we extracted from production failures. Every wrong choice is documented in a closed PR, a deleted file, or a systemd restart that wiped a session.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Closing
&lt;/h2&gt;

&lt;p&gt;The PR that took 77 days wasn't slow because the code was hard. It was slow because we were solving the wrong problem — fitting a server-shaped capability into a plugin-shaped hole. The moment we admitted the architecture was wrong, the implementation took three days.&lt;/p&gt;

&lt;p&gt;The stack: &lt;a href="https://github.com/Ev3lynx727/oh-my-mcp" rel="noopener noreferrer"&gt;&lt;code&gt;oh-my-mcp&lt;/code&gt;&lt;/a&gt; + &lt;a href="https://github.com/Ev3lynx727/commands-rtk" rel="noopener noreferrer"&gt;&lt;code&gt;commands-rtk&lt;/code&gt;&lt;/a&gt;. The decision tree: four layers, one rule. The lesson: the architectural decision before the first line of code determines whether delivery takes 77 days or three.&lt;/p&gt;

&lt;p&gt;An MCP server isn't a plugin you run differently. It's a process that survives its client.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
    <item>
      <title>Hybrid Container Governance: Scaling Patterns That Work</title>
      <dc:creator>Ev3lynx727</dc:creator>
      <pubDate>Fri, 10 Jul 2026 16:26:44 +0000</pubDate>
      <link>https://dev.to/ev3lynx727/hybrid-container-governance-scaling-patterns-that-work-1bih</link>
      <guid>https://dev.to/ev3lynx727/hybrid-container-governance-scaling-patterns-that-work-1bih</guid>
      <description>&lt;h2&gt;
  
  
  Hook
&lt;/h2&gt;

&lt;p&gt;The third WSL instance didn't need a reinstall. That's when I knew the pattern worked.&lt;/p&gt;

&lt;p&gt;WSL 1 had the full stack — six ark-* MCP servers, oh-my-mcp gateway, server-commands-rtk, all configured, tested, running. WSL 2 was planned for the same setup. So was WSL 3. Each one would need &lt;code&gt;npx @ev3lynx/oh-my-mcp&lt;/code&gt;, the npm dependencies, the config files, the supergateway wrappers, the port allocations, the CLI bridge, the permission schema in opencode.jsonc.&lt;/p&gt;

&lt;p&gt;I started scripting the install. Halfway through, I stopped and deleted the script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing the same stack N times is the wrong answer. The right answer is one installation, served.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Container Decision
&lt;/h2&gt;

&lt;p&gt;The architecture we'd built was already multi-process — oh-my-mcp managing six child servers, each with its own port, its own lifecycle, its own restart budget. The step from "multi-process on one machine" to "multi-WSL via one container" was smaller than expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ghcr.io/anomalyco/opencode:dev-debian&lt;/span&gt;

&lt;span class="c"&gt;# oh-my-mcp gateway&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @ev3lynx/oh-my-mcp

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; supervisord.conf /etc/supervisor/conf.d/opencode-stack.conf&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["supervisord", "-c", "/etc/supervisor/conf.d/opencode-stack.conf"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container runs two supervised processes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;opencode serve --hostname 0.0.0.0 --port 4096&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;oh-my-mcp --port 8090&lt;/code&gt; (management API on 8080)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ports exposed: 4096 (OpenCode SSE), 8080 (management), 8090 (MCP gateway).&lt;/p&gt;

&lt;p&gt;Volume mounts for persistence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;~/.config/opencode:/home/ev3lynx/.config/opencode&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;~/.local/share/opencode:/home/ev3lynx/.local/share/opencode&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;~/server:/home/ev3lynx/server&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;~/.ssh:/home/ev3lynx/.ssh&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From any WSL instance: &lt;code&gt;docker exec -it opencode-stack opencode&lt;/code&gt;. Same env, same config, same servers. Zero npm installs per distro.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Poorly-Designed Alternative
&lt;/h2&gt;

&lt;p&gt;But I didn't start with a container. I started the wrong way.&lt;/p&gt;

&lt;p&gt;The first WSL instance got the full manual setup: &lt;code&gt;npx @ev3lynx/oh-my-mcp&lt;/code&gt; install, fnm multishell resolution, port allocation for each ark server, supergateway wrappers for every stdio→SSE bridge, systemd user service registration, and a fire-and-forget shell script that was already one edit away from breaking silently.&lt;/p&gt;

&lt;p&gt;When WSL 2 needed the same thing, I ran the script again. It failed because &lt;code&gt;systemctl --user&lt;/code&gt; doesn't behave the same across WSL instances — Debian's systemd vs Alpine's OpenRC vs Ubuntu's snap-wrapped systemd. The paths were different. The fnm node version was different. The &lt;code&gt;/run/user/1000/&lt;/code&gt; socket path was absent on the second instance because the user ID was 1001.&lt;/p&gt;

&lt;p&gt;I spent an afternoon fixing environmental drift between two WSL instances that were supposed to be identical. I caught myself debugging the wrong thing: not the architecture, not the code — but the &lt;em&gt;installation path&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That was the moment the container decision made itself. Not because containers are trendy. Because I was debugging installation scripts instead of shipping features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conflict
&lt;/h2&gt;

&lt;p&gt;The tension isn't technical. The container is trivially simple — a Dockerfile, a docker-compose.yml, a volume mapping. The tension is architectural: we had built an MCP server ecosystem without a deployment story. Every server in the ark-* family was designed as a standalone process with clear boundaries (focused tools, independent lifecycle). But standalone doesn't mean deployable. A process without a reproducible environment is a test that passes on your machine.&lt;/p&gt;

&lt;p&gt;Here's the voice that argues against it:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"It's three WSL instances. You can script the install. Docker adds overhead, a learning curve for the next person, another moving part. A Makefile and a README note is cheaper."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This voice isn't wrong — it's looking at the right cost but the wrong time horizon. At WSL 3, the Makefile is still fine. At WSL 5, when they're different distros (Debian, Ubuntu, Alpine, Fedora, Arch), each with different systemd quirks, different package managers, different default shells — the Makefile becomes a liability audit. At a CI runner and a team member's machine and a deployment target, it's a full-time maintenance job.&lt;/p&gt;

&lt;p&gt;The cost of the container is paid once. The cost of environment-specific setup scripts compounds with every new target.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Governance
&lt;/h2&gt;

&lt;p&gt;Six servers is a manageable number. Sixteen is not — unless you have a pattern that makes each one predictable.&lt;/p&gt;

&lt;p&gt;Every ark-* server follows the same template:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A focused tool set&lt;/strong&gt; — 3-8 tools, one domain (memory, gists, exec, resolve, delegation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supergateway stdin→SSE wrapper&lt;/strong&gt; — port 8100-series, registered in oh-my-mcp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standalone process&lt;/strong&gt; — systemd or supervised, never a plugin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cacache or sqlite storage&lt;/strong&gt; — disk-backed, survives restart&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache stats + prune tools&lt;/strong&gt; — self-maintaining, no cron needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Adding a new server is five files, not five hours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server/ark-&amp;lt;name&amp;gt;/
├── package.json         # @ev3lynx/ark-&amp;lt;name&amp;gt;
├── tsconfig.json        # extends base
├── src/server.ts        # tool handlers
└── .github/workflows/   # CI (ci.yml, publish.yml)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build → &lt;code&gt;oh-my-mcp add --path server/ark-&amp;lt;name&amp;gt;&lt;/code&gt; → registered. No config file edit. No port conflict check. The gateway discovers it, assigns a port, and routes traffic.&lt;/p&gt;

&lt;p&gt;The governance rule that handles 90% of decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Would this tool be used on most sessions?&lt;/strong&gt; → Add it to an existing focused server (3→8 tools)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Would this tool be used rarely or by a specific client?&lt;/strong&gt; → New ark-* server (1 domain, 3-8 tools)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is this a write-once-read-never operation?&lt;/strong&gt; → CLI bridge, no server needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the Container Makes Possible
&lt;/h2&gt;

&lt;p&gt;The container stack isn't just about deployment repeatability. It changes the capability surface entirely:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From any WSL instance:&lt;/strong&gt; &lt;code&gt;docker exec -it opencode-stack opencode&lt;/code&gt; — same servers, same config, same memory graph. Sessions are portable across distros. Switch from Debian to Alpine to Arch without losing connection state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From Windows:&lt;/strong&gt; ark-delegator resolves via &lt;code&gt;http://localhost:8106&lt;/code&gt; — the port is mapped through Docker and accessible from the Windows host. The Windows OpenCode client sees the same MCP servers as every WSL instance. No network bridging. No WSL path translation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From CI:&lt;/strong&gt; The same container image. &lt;code&gt;docker run opencode-stack opencode eval "run tests"&lt;/code&gt; — CI gets the exact same tool set as interactive sessions. No drift between development and pipeline environments.&lt;/p&gt;

&lt;p&gt;This is the pattern that survives: one image, many clients, zero per-target configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Closing
&lt;/h2&gt;

&lt;p&gt;The Makefile argument was right about the cost — just wrong about which cost compounds. The container is heavier on setup day. It's lighter on every subsequent day. And on the day WSL 5 shows up with a package manager you've never heard of, the container costs nothing at all.&lt;/p&gt;

&lt;p&gt;The stack: &lt;a href="https://github.com/Ev3lynx727/oh-my-mcp" rel="noopener noreferrer"&gt;&lt;code&gt;oh-my-mcp&lt;/code&gt; gateway&lt;/a&gt; + &lt;a href="https://github.com/Ev3lynx727/commands-rtk" rel="noopener noreferrer"&gt;&lt;code&gt;commands-rtk&lt;/code&gt; CLI bridge&lt;/a&gt;. The template: five files per ark-* server. The rule: one image, many clients — install once, exec everywhere.&lt;/p&gt;

&lt;p&gt;Build tools that survive restarts. Build processes that survive machine boundaries. Build stacks that don't care which WSL distro the terminal opened today.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>devops</category>
      <category>docker</category>
      <category>mcp</category>
    </item>
    <item>
      <title>How We Use MCP and CLI Together for Dynamic AI Agent Workflows</title>
      <dc:creator>Ev3lynx727</dc:creator>
      <pubDate>Thu, 09 Jul 2026 11:50:48 +0000</pubDate>
      <link>https://dev.to/ev3lynx727/how-we-use-mcp-and-cli-together-for-dynamic-ai-agent-workflows-2j7j</link>
      <guid>https://dev.to/ev3lynx727/how-we-use-mcp-and-cli-together-for-dynamic-ai-agent-workflows-2j7j</guid>
      <description>&lt;h2&gt;
  
  
  Hook
&lt;/h2&gt;

&lt;p&gt;The Windows agent went silent mid-session. It had been routing tasks through ark-delegator — a cross-environment bridge that lives in WSL. But the MCP server behind it had crashed with OpenCode's last daemon restart, because it was registered as a plugin. Same process. Same failure. Same data loss.&lt;/p&gt;

&lt;p&gt;I checked the process table. ark-memory was still running on port 8102. ark-exec was healthy on 8101. Every MCP server was alive — because they were managed by systemd, not by OpenCode. The plugin was dead because plugins live inside the agent's process. The MCP servers were fine because MCP servers are independent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That was the day I stopped treating MCP servers as plugins.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;oh-my-mcp (systemd user service)
  ├── port 8080: Management API
  ├── port 8090: Gateway (MCP proxy)
  └── child processes (supervised):
        ├── 8101  ark-exec        → exec MCP tools
        ├── 8102  ark-memory      → memory knowledge graph
        ├── 8103  ark-resolve     → URI resolution
        ├── 8104  mempalace       → vector semantic search
        ├── 8105  ark-gist        → GitHub Gist operations
        └── 8106  ark-delegator   → cross-agent task routing

server-commands-rtk (the CLI bridge)
  ├── stdin/stdout → spawns processes
  ├── RTK filter   → ~90% token reduction on large output
  └── audit logger → append-only JSONL with rotation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six MCP servers, each with 3-8 focused tools. One CLI bridge for everything else. Total schema overhead: ~8,000-10,000 tokens across all six — less than a single GitHub MCP server alone.&lt;/p&gt;

&lt;p&gt;Every server is a standalone process. Every one can restart independently. Every one is reachable from any client on the network via SSE on its port. The plugin model gives you in-process speed. The MCP server model gives you survival.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture Decision
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# oh-my-mcp config.yaml&lt;/span&gt;
&lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ark-exec:        transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
  &lt;span class="na"&gt;ark-memory:      transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
  &lt;span class="na"&gt;ark-resolve:     transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
  &lt;span class="na"&gt;mempalace:       transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
  &lt;span class="na"&gt;ark-gist:        transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
  &lt;span class="na"&gt;ark-delegator:   transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All MCP. All focused. No monolithic 93-tool server. The decision was deliberate: six narrow servers mean the agent loads only the schemas it needs for the current operation, not a catalog of everything it might ever do.&lt;/p&gt;

&lt;p&gt;For raw commands, we route through server-commands-rtk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;rtk_run_process&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npm view @ev3lynx/md-analyzer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/home/ev3lynx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;check latest version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// Returns: { stdout, stderr, exitCode, duration_ms, error_type }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the hybrid in practice. Structured operations through MCP (memory, search, CRUD). Everything else through a CLI wrapper that happens to look like an MCP tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;Package downloads check — a real trace from running both paths through our &lt;a href="https://github.com/Ev3lynx727/commands-rtk" rel="noopener noreferrer"&gt;commands-rtk CLI bridge&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;npmPackageDownloads("md-analyzer")
→ 170 tokens, 3 fields

npm view @ev3lynx/md-analyzer | grep -A5 downloads
→ 2,000+ tokens unfiltered, ~400 with RTK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MCP tool wins: 2.4x cheaper than raw+RTK, 12x cheaper than unfiltered.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over a thousand such checks per week — that token difference adds up. Not enough to matter on one call. Enough to matter across a team's collective session hours at $3-15/M tokens depending on your model tier.&lt;/p&gt;

&lt;p&gt;Git push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
→ ~50 tokens &lt;span class="nb"&gt;command&lt;/span&gt; + ~50 tokens output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Raw CLI wins: zero schema overhead, same result.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The rule: if the output has a known schema, build a tool. If the command is an action with small output, run it raw.&lt;/p&gt;

&lt;h3&gt;
  
  
  The audit logger
&lt;/h3&gt;

&lt;p&gt;server-commands-rtk logs every command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-08T10:01:27Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git push origin main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exitCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rtk_filtered"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the insight that MCP-vs-CLI debates miss: &lt;strong&gt;audit trails don't require MCP.&lt;/strong&gt; A CLI wrapper with structured logging gives you the same compliance surface as MCP's structured I/O. The difference is architectural — logging at the bridge layer, not the protocol layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conflict
&lt;/h2&gt;

&lt;p&gt;The next morning I ran &lt;code&gt;systemctl --user status oh-my-mcp&lt;/code&gt;. Every service was green. ark-exec, ark-memory, ark-gist — all running, all within their restart budgets. The plugin had died. The MCP servers had not.&lt;/p&gt;

&lt;p&gt;It's not MCP vs CLI. It's single-process vs independent-process.&lt;/p&gt;

&lt;p&gt;The failing plugin shared OpenCode's process table. When the daemon restarted, the plugin restarted with it — but it surfaced with empty connection pools, a cold cache, and every in-flight task evaporated. The MCP server on port 8106 had never noticed the restart. Its client had disconnected. It kept receiving. It was still there when the next session connected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lifecycle independence.&lt;/strong&gt; A plugin inherits its parent's mortality. An MCP server manages its own.&lt;/p&gt;

&lt;p&gt;This sounds architectural-theoretical until you run a 24-hour agent managing stateful connections across six backends. Then it reveals itself as the difference between a system that recovers its own state and one that leaks it, session by session.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Resolution
&lt;/h2&gt;

&lt;p&gt;The pattern we settled on is three-tier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MCP server (focused, 3-8 tools)
  → structured operations the agent uses every session
  → independent lifecycle, restart survives client death

CLI bridge (server-commands-rtk with RTK + audit)
  → ad-hoc commands, small output, side effects
  → audit logging without MCP schema overhead

Hybrid dispatch (decision at the agent level)
  → Query/search/read? MCP tool
  → Execute/write/push? Raw CLI
  → Audit required? CLI bridge with structured logger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;call_mcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;has_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;run_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run_raw_rtk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# CLI with audit logging
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs in production for every session. The gateway routes, the MCP servers serve, and the CLI bridge handles everything else with logging.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;p&gt;Six focused MCP servers beat one general one. Our ~8K total schema overhead is less than a single GitHub MCP server at ~14K. Fewer tools per server means the agent carries less dead weight on every request.&lt;/p&gt;

&lt;p&gt;The gateway pattern is the right default. Near-zero overhead (~20 tokens/prompt). MCP-quality outputs. Linear scaling. No single-server blob.&lt;/p&gt;

&lt;p&gt;And the rule that handles 95% of decisions: &lt;strong&gt;MCP for structure, CLI for execution.&lt;/strong&gt; Build tools that return answers, not documents. Run commands that do things, not queries that return everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Closing
&lt;/h2&gt;

&lt;p&gt;The MCP backlash is real, but the target is wrong. The problem isn't the protocol — it's installing everything into one process. A plugin crashes with its host. An MCP server survives, restarts, and waits for the next client.&lt;/p&gt;

&lt;p&gt;Lifecycle independence isn't a nice-to-have. It's the feature that makes the rest possible.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Stack details: Lifecycle managed via the &lt;a href="https://github.com/Ev3lynx727/oh-my-mcp" rel="noopener noreferrer"&gt;&lt;code&gt;oh-my-mcp&lt;/code&gt;&lt;/a&gt; (port 8080); CLI wrapping, RTK filtering, and audit logging handled by &lt;a href="https://github.com/Ev3lynx727/commands-rtk" rel="noopener noreferrer"&gt;&lt;code&gt;commands-rtk&lt;/code&gt;&lt;/a&gt;. Core `ark-&lt;/em&gt;&lt;code&gt; services run in &lt;/code&gt;server/` as standalone MCP servers, not plugins.*&lt;/p&gt;

</description>
      <category>agents</category>
      <category>cli</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
    <item>
      <title>Tools vs Raw Commands - The Token Cost Theory - Part 1</title>
      <dc:creator>Ev3lynx727</dc:creator>
      <pubDate>Wed, 08 Jul 2026 10:52:09 +0000</pubDate>
      <link>https://dev.to/ev3lynx727/tools-vs-raw-commands-the-token-cost-theory-d1g</link>
      <guid>https://dev.to/ev3lynx727/tools-vs-raw-commands-the-token-cost-theory-d1g</guid>
      <description>&lt;h2&gt;
  
  
  The Hook
&lt;/h2&gt;

&lt;p&gt;Scalekit ran 75 benchmark runs comparing CLI and MCP agents on identical GitHub tasks. CLI was &lt;strong&gt;10 to 32x cheaper&lt;/strong&gt; and &lt;strong&gt;100% reliable&lt;/strong&gt;. MCP hit only 72% — failing on more than a quarter of tasks (&lt;a href="https://www.scalekit.com/blog/mcp-vs-cli-use" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Perplexity's CTO reported that MCP tool descriptions consumed &lt;strong&gt;72% of the available context window&lt;/strong&gt; before the agent performed any actual work (&lt;a href="![%20](https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/fxr7vpoxn3shvtww1jk1.png)/"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The narrative is everywhere: MCP is the future, CLI is legacy. But the numbers tell a different story — and one where the real answer isn't "pick one."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Token Cost Theory
&lt;/h2&gt;

&lt;p&gt;Every interaction an AI agent makes has a token price — input tokens (the instruction) + output tokens (the response). Multiply by the token rate, and you get the real cost of a single operation.&lt;/p&gt;

&lt;p&gt;But there's a hidden variable: &lt;strong&gt;schema overhead&lt;/strong&gt;. Every MCP server exposes tool definitions — names, descriptions, JSON input schemas. These get injected into the LLM context on every single request, whether you call the tool or not.&lt;/p&gt;

&lt;p&gt;PolicyLayer measured 3,875 MCP servers and found a median of &lt;strong&gt;2,064 tokens per server&lt;/strong&gt; in tool definition overhead (&lt;a href="https://policylayer.com/token-cost" rel="noopener noreferrer"&gt;source&lt;/a&gt;). A GitHub MCP server alone clocks &lt;strong&gt;14,406 tokens&lt;/strong&gt;. Connect GitHub + Linear + Supabase: &lt;strong&gt;24,116 tokens&lt;/strong&gt; — 12% of a 200K context window, before the first message.&lt;/p&gt;

&lt;h3&gt;
  
  
  The numbers, in my agent
&lt;/h3&gt;

&lt;p&gt;I measured three modes across common operations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool Call:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input:  50 tokens (tool name + args)
Output: 100 tokens (structured JSON response)
Framing: 20 tokens
Total:  ~170 tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Raw Command (with RTK filtering):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input:  30 tokens (command string)
Output: 350 tokens (RTK-filtered stdout)
Wrapper: 80 tokens (exit code, error type)
Total:  ~460 tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Raw Command (unfiltered):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input:  30 tokens
Output: 3,000 tokens (raw terminal output)
Wrapper: 80 tokens
Total:  ~3,110 tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The spread: &lt;strong&gt;1x, 2.7x, 18x&lt;/strong&gt; — same intent, different delivery.&lt;/p&gt;

&lt;h3&gt;
  
  
  The real numbers from benchmarks
&lt;/h3&gt;

&lt;p&gt;Independent research paints a wider gap. Afroze Amjad benchmarked graph-cli as both CLI and MCP, finding MCP's per-turn cost was &lt;strong&gt;consistently higher&lt;/strong&gt; — the schema catalog ships with every request (&lt;a href="https://afrozeamjad.com/writing/mcp-vs-cli-token-benchmark/" rel="noopener noreferrer"&gt;source&lt;/a&gt;). When tools hold 50+ schemas, that overhead compounds.&lt;/p&gt;

&lt;p&gt;The worst case: a 93-tool GitHub MCP server at &lt;strong&gt;55,000 tokens&lt;/strong&gt; of schema alone. At $3/M input tokens (Claude Sonnet), that's &lt;strong&gt;$0.17 per request for schema the agent doesn't use&lt;/strong&gt;. At 10,000 requests/day: &lt;strong&gt;$51,000/month&lt;/strong&gt; in schema tax (&lt;a href="https://onlycli.github.io/OnlyCLI/blog/mcp-token-cost-benchmark/" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters at Scale
&lt;/h2&gt;

&lt;p&gt;Per-operation costs are academic until you stack them. Here's 100 operations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;100 ops&lt;/th&gt;
&lt;th&gt;Token cost&lt;/th&gt;
&lt;th&gt;Relative&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool calls&lt;/td&gt;
&lt;td&gt;17,000&lt;/td&gt;
&lt;td&gt;~$0.17&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw + RTK&lt;/td&gt;
&lt;td&gt;46,000&lt;/td&gt;
&lt;td&gt;~$0.46&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.7x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw unfiltered&lt;/td&gt;
&lt;td&gt;311,000&lt;/td&gt;
&lt;td&gt;~$3.11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The difference between a tool-oriented agent and a raw-command-oriented one over 100 retrievals: &lt;strong&gt;$0.29 vs $2.94&lt;/strong&gt;. At 1,000 operations, that's $2.90 vs $29.40.&lt;/p&gt;

&lt;p&gt;But these numbers assume &lt;strong&gt;one MCP server&lt;/strong&gt;. Most agents run 5+ servers (GitHub, Slack, Postgres, Sentry, Filesystem). At 5 servers averaging 2,064 tokens each, that's &lt;strong&gt;10,320 tokens per request just to keep tool descriptions warm&lt;/strong&gt; — whether the agent runs one operation or zero.&lt;/p&gt;

&lt;h3&gt;
  
  
  The G/N ratio is the missing variable
&lt;/h3&gt;

&lt;p&gt;The cost structure flips based on how often you actually use a tool (&lt;a href="https://blog.mornati.net/the-future-of-agentic-tooling-mcp-servers-vs-cli-a-data-driven-comparison" rel="noopener noreferrer"&gt;source&lt;/a&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool frequency&lt;/th&gt;
&lt;th&gt;Session cost (20 prompts, 2 GitHub ops)&lt;/th&gt;
&lt;th&gt;vs. CLI baseline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Low (G/N &amp;lt; 5%)&lt;/td&gt;
&lt;td&gt;61,654 tokens (Native MCP)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;137x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium (G/N 5-40%)&lt;/td&gt;
&lt;td&gt;892 tokens (Gateway MCP)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High (G/N &amp;gt; 40%)&lt;/td&gt;
&lt;td&gt;892-1,492 tokens&lt;/td&gt;
&lt;td&gt;Negligible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a tool used on 2 of 20 prompts (10% frequency), &lt;strong&gt;99.3% of MCP tokens are wasted on schema descriptions&lt;/strong&gt; the agent never needed (&lt;a href="https://blog.mornati.net/the-future-of-agentic-tooling-mcp-servers-vs-cli-a-data-driven-comparison" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conflict
&lt;/h2&gt;

&lt;p&gt;But here's the catch: &lt;strong&gt;tools aren't free to build.&lt;/strong&gt; Every dedicated tool must be written, tested, maintained, and updated when the upstream API changes. &lt;code&gt;npmPackageDownloads&lt;/code&gt; is 20 lines of TypeScript. The &lt;code&gt;npm view&lt;/code&gt; CLI command it replaces was written by npm's team.&lt;/p&gt;

&lt;p&gt;The CEO voice in me asks: &lt;em&gt;Why am I duplicating infrastructure that already exists?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the scene that answers that. I'm sitting in WSL, my agent wants to check a package version. It calls &lt;code&gt;npm view&lt;/code&gt; — a raw shell command. The output comes back: 2,140 lines of package metadata, README, maintainer list, dependency tree, signature hashes. What I wanted was one field. What I got was a firehose.&lt;/p&gt;

&lt;p&gt;The tool call: 170 tokens. The raw command: 2,500+ tokens. &lt;strong&gt;The tool was 15x cheaper on that single operation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Afroze Amjad found the same pattern with graph-cli: CLI saves on cost, MCP saves on latency and discovery. The right answer depends on which dimension your workload optimizes for (&lt;a href="https://afrozeamjad.com/writing/mcp-vs-cli-token-benchmark/" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Estimation without measurement is anxiety wearing arithmetic as a disguise. So I measured.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Resolution
&lt;/h2&gt;

&lt;p&gt;Three patterns cut MCP's token cost dramatically:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Schema filtering.&lt;/strong&gt; Instead of injecting all 93 GitHub tool schemas, a routing layer returns only the 2-3 tools relevant to the current request. This alone cuts MCP token usage by ~90% (&lt;a href="https://tyk.io/learning-center/mcp-vs-cli-for-ai-agents-enterprise-comparison-guide/" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Gateway pattern.&lt;/strong&gt; Near-zero fixed overhead (~20 tokens/prompt), MCP-quality structured outputs, linear scaling regardless of backend services (&lt;a href="https://blog.mornati.net/the-future-of-agentic-tooling-mcp-servers-vs-cli-a-data-driven-comparison" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Hybrid dispatch.&lt;/strong&gt; The most practical setup (&lt;a href="https://dev.to/tim_zhang11/i-measured-mcp-vs-cli-for-agent-tool-use-mcp-used-17x-more-tokens-per-call-3egc"&gt;source&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;┌─ Query / retrieval?      → MCP tool (structured, typed, fast)
├─ Execute / side effect?  → Raw CLI (small output, no schema needed)
├─ Large output expected?  → MCP tool (schema caps response size)
└─ Filter / pipe needed?   → Raw CLI (jq, grep, UNIX pipes work)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dispatch matrix cut one practitioner's token usage by &lt;strong&gt;60%&lt;/strong&gt; — $8.64/day to $3.45/day (&lt;a href="https://dev.to/tim_zhang11/i-measured-mcp-vs-cli-for-agent-tool-use-mcp-used-17x-more-tokens-per-call-3egc"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Closing
&lt;/h2&gt;

&lt;p&gt;The MCP backlash is real, but the target is wrong. The problem isn't the protocol. It's the native injection pattern — loading every schema into every prompt, whether the agent needs it or not. The schema tax dominates until you actively manage it.&lt;/p&gt;

&lt;p&gt;MCP's per-call cost is a deployment problem, not a protocol problem. Good tool design means routing the tool surface to match actual usage patterns, not defaulting to everything for everyone.&lt;/p&gt;

&lt;p&gt;Build tools that return answers, not documents. Route schemas that match actual usage, not every possibility. And when the task is a one-shot retrieval with a CLI you already know: &lt;strong&gt;run the command.&lt;/strong&gt; The token market thanks you either way.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Sources: &lt;a href="https://www.scalekit.com/blog/mcp-vs-cli-use" rel="noopener noreferrer"&gt;Scalekit MCP vs CLI Benchmark&lt;/a&gt; · &lt;a href="https://afrozeamjad.com/writing/mcp-vs-cli-token-benchmark/" rel="noopener noreferrer"&gt;Afroze Amjad graph-cli comparison&lt;/a&gt; · &lt;a href="https://onlycli.github.io/OnlyCLI/blog/mcp-token-cost-benchmark/" rel="noopener noreferrer"&gt;OnlyCLI token cost benchmark&lt;/a&gt; · &lt;a href="https://blog.mornati.net/the-future-of-agentic-tooling-mcp-servers-vs-cli-a-data-driven-comparison" rel="noopener noreferrer"&gt;Mornati MCP vs CLI deep-dive&lt;/a&gt; · &lt;a href="https://policylayer.com/token-cost" rel="noopener noreferrer"&gt;PolicyLayer MCP cost calculator&lt;/a&gt; · &lt;a href="https://dev.to/tim_zhang11/i-measured-mcp-vs-cli-for-agent-tool-use-mcp-used-17x-more-tokens-per-call-3egc"&gt;Dev.to hybrid dispatch case study&lt;/a&gt; · &lt;a href="https://tyk.io/learning-center/mcp-vs-cli-for-ai-agents-enterprise-comparison-guide/" rel="noopener noreferrer"&gt;Tyk enterprise comparison guide&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>mcp</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Architecture That Almost Shipped: A Builder Reflection on Deadlines, Cost Anxiety, and What Survives Both</title>
      <dc:creator>Ev3lynx727</dc:creator>
      <pubDate>Fri, 12 Jun 2026 19:37:58 +0000</pubDate>
      <link>https://dev.to/ev3lynx727/the-architecture-that-almost-shipped-a-builder-reflection-on-deadlines-cost-anxiety-and-what-5f6g</link>
      <guid>https://dev.to/ev3lynx727/the-architecture-that-almost-shipped-a-builder-reflection-on-deadlines-cost-anxiety-and-what-5f6g</guid>
      <description>&lt;p&gt;&lt;strong&gt;By Ev3lynx · CTO · Builder · AI Systems Architect&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The competition closed at midnight. My build was green at 11:47. I never hit submit.&lt;/p&gt;

&lt;p&gt;That terminal window is still in my session history. The cursor blinking at a prompt I walked away from. The browser tabs: YouTube Music on lo-fi, the RSVP page for #JuaraVibeCoding's kick-off — 5,183 people signed up — and a &lt;code&gt;pyproject.toml&lt;/code&gt; I'd been polishing for three weeks.&lt;/p&gt;

&lt;p&gt;I closed the laptop. I'd submit in the morning.&lt;/p&gt;

&lt;p&gt;By morning, the deadline had passed.&lt;/p&gt;

&lt;p&gt;This is the story of &lt;a href="https://github.com/Ev3lynx727/ghostclaw" rel="noopener noreferrer"&gt;Ghostclaw&lt;/a&gt; — a hybrid Python/TypeScript systems architecture built for Google Indonesia's #JuaraVibeCoding 2026. Designed, built, production-tested. Never submitted. Ships every day.&lt;/p&gt;

&lt;p&gt;This is not a postmortem. It's a reminder that what survives the deadline is often more valuable than what meets it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Context
&lt;/h2&gt;

&lt;p&gt;In May 2026, Google Indonesia launched #JuaraVibeCoding — a nationwide study jam promoting vibe coding with Google AI Studio, Gemini, and Cloud Run. The engineering track on May 20 drew 768 RSVPs. Campus events at IPB, Airlangga, and Brawijaya followed through end of May. Top entries received swag and e-wallet rewards.&lt;/p&gt;

&lt;p&gt;We saw it as the perfect proving ground for an architecture pattern we had been refining across multiple projects — the &lt;strong&gt;hybrid module&lt;/strong&gt;: a single package that ships as a CLI tool, an importable library, and an MCP server for AI agents, all sharing one service layer.&lt;/p&gt;

&lt;p&gt;The project: &lt;a href="https://github.com/Ev3lynx727/ghostclaw" rel="noopener noreferrer"&gt;Ghostclaw&lt;/a&gt; — a code analysis engine, a pluggable plugin system, and an MCP server, rolled into one &lt;code&gt;pip install&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Ghostclaw Actually Is
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## Four entry points, one codebase
&lt;/span&gt;&lt;span class="n"&gt;ghostclaw&lt;/span&gt;             &lt;span class="c1"&gt;# CLI — analyze, review, report
&lt;/span&gt;&lt;span class="n"&gt;ghostclaw&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;watcher&lt;/span&gt;     &lt;span class="c1"&gt;# Continuous file watcher
&lt;/span&gt;&lt;span class="n"&gt;ghostclaw&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;compare&lt;/span&gt;     &lt;span class="c1"&gt;# Diff-based regression analysis
&lt;/span&gt;&lt;span class="n"&gt;ghostclaw&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;         &lt;span class="c1"&gt;# MCP server — 14 tools for AI agents
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four binaries from a single &lt;code&gt;hatchling&lt;/code&gt; build. Each entry point is a &lt;code&gt;click.Group&lt;/code&gt; subclass registered via &lt;code&gt;project.scripts&lt;/code&gt;. The base &lt;code&gt;Command&lt;/code&gt; ABC handles argument parsing, error formatting, and output serialization. Adding a subcommand means writing one class and one line in &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The plugin system uses &lt;code&gt;pluggy&lt;/code&gt; with entry-point discovery. Third-party developers extend Ghostclaw without forking — install a separate PyPI package that registers a &lt;code&gt;ghostclaw.plugins&lt;/code&gt; entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[project.entry-points."ghostclaw.plugins"]&lt;/span&gt;
&lt;span class="py"&gt;my_analyzer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my_package.plugin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server exposes 14 tools — code analysis, NTP calibration, IMF economic data, DNS dig, currency conversion. Every tool is a pure function decorated with &lt;code&gt;@mcp.tool()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The TypeScript counterpart ships as a dual ESM/CJS package with submodule exports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.mjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.cjs"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"./mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/mcp/index.mjs"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"./watcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/watcher/index.mjs"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every subpath individually typed, individually importable, individually tree-shakeable.&lt;/p&gt;

&lt;p&gt;This was the architecture we wanted to submit as our competition entry. Not as a demo, but as a reference implementation of the hybrid module pattern at production scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conversation That Killed the Submission
&lt;/h2&gt;

&lt;p&gt;I remember the exact moment. Wednesday night, 1 AM. I was staring at the environment variables I'd wired into the config loader — every one prefixed with &lt;code&gt;GHOSTCLAW_&lt;/code&gt;. The CLI was passing integration tests. The MCP server was responding to tool calls. linter was clean.&lt;/p&gt;

&lt;p&gt;And I was calculating.&lt;/p&gt;

&lt;p&gt;"Okay — Cloud Run, one always-on instance, memory overhead for MCP... maybe 20 dollars a month. Plus Rust build in CI, that's GitHub Actions minutes. Dashboard if I add one..."&lt;/p&gt;

&lt;p&gt;I reached for a calculator.&lt;/p&gt;

&lt;p&gt;The voice in my head that plays CFO when the real one is sleeping pushed back: &lt;em&gt;"We don't even know if we'll place. Is this worth the time?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer should have been obvious. The MCP server doesn't bind a port. It reads stdin and writes stdout. The CLI runs and exits. Zero database, zero always-on services, zero infrastructure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No database? ✅ Zero&lt;/li&gt;
&lt;li&gt;No server to keep alive? ✅ Zero&lt;/li&gt;
&lt;li&gt;MCP runs on stdio? ✅ Zero&lt;/li&gt;
&lt;li&gt;CI on GitHub Actions? ✅ Zero&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I didn't check. I &lt;em&gt;estimated&lt;/em&gt;. And estimation without data is just anxiety wearing arithmetic as a disguise.&lt;/p&gt;

&lt;p&gt;I closed the calculator. I told myself I'd figure it out tomorrow.&lt;/p&gt;

&lt;p&gt;Tomorrow was June 1. The deadline had passed.&lt;/p&gt;

&lt;p&gt;That's the part I don't talk about in standups. Not that the code was hard — it wasn't. The architecture was sound. The deploy cost was zero.&lt;/p&gt;

&lt;p&gt;The thing that killed the submission was a conversation I had with myself at a desk, with a calculator, at 1 AM.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern That Outlived the Deadline
&lt;/h2&gt;

&lt;p&gt;The submission window closed. The workshops ended.&lt;/p&gt;

&lt;p&gt;The architecture settled into daily production use.&lt;/p&gt;

&lt;p&gt;Within two weeks, we extracted Ghostclaw's structural patterns into reusable skills — documented blueprints that scaffold new projects with the same architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;hybrid-bot-server-builder&lt;/strong&gt; — CLI + Telegram/Discord bot, one service layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hybrid-module-esm-builder&lt;/strong&gt; — TypeScript dual ESM/CJS + submodule exports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hybrid-module-py-builder&lt;/strong&gt; — Python src layout, pluggy plugins, maturin addons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each is a complete project template. Run it, get a working hybrid module with CI, tests, docs, and publish config. No manual wiring.&lt;/p&gt;

&lt;p&gt;Ghostclaw continues as the reference implementation — the project where all three patterns converge: Python hybrid package, TypeScript dual-ship module, and MCP server, sharing one service layer through protocol-agnostic interfaces.&lt;/p&gt;

&lt;p&gt;Two downstream projects now use this architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;snapx-app&lt;/strong&gt; — hybrid CLI + Telegram bot with provider fallback chains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;wristworks&lt;/strong&gt; — TypeScript dual-ship with native Rust via napi-rs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The submission never happened. The pattern is in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  There's No Numbered Advice Here
&lt;/h2&gt;

&lt;p&gt;Three weeks after the deadline, I needed to scaffold a new Python project — a bot server with a CLI interface, Telegram integration, and provider fallback chains. The old me would have started from scratch, copied files from an old project, and spent two hours wiring everything together.&lt;/p&gt;

&lt;p&gt;Instead, I ran a skill I'd extracted from Ghostclaw's architecture. It generated the full project structure in three seconds.&lt;/p&gt;

&lt;p&gt;That skill exists because Ghostclaw's architecture was built to be extracted. The &lt;code&gt;src/&lt;/code&gt; layout. The &lt;code&gt;Command&lt;/code&gt; ABC. The pluggy hooks. The &lt;code&gt;try/except ImportError&lt;/code&gt; fallback chains that let it degrade gracefully from full-featured to minimal. Every pattern was written with extraction in mind — even when I didn't know what I was extracting for.&lt;/p&gt;

&lt;p&gt;That's the takeaway, without bullet points: &lt;strong&gt;Build what outlives you.&lt;/strong&gt; Not the demo. Not the submission. The architecture. The patterns. The reusable thing that makes the next project faster, cleaner, and more likely to ship.&lt;/p&gt;

&lt;p&gt;Because deadlines come and go. A well-abstracted project template ships every week.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part That Matters
&lt;/h2&gt;

&lt;p&gt;A few people have asked: was it worth it? Building an entire architecture for a competition we never entered?&lt;/p&gt;

&lt;p&gt;Yes. Because the architecture is still here. The competition is over. Ghostclaw ships on every &lt;code&gt;pip install&lt;/code&gt;. The skills scaffold new projects every week. The pattern propagates.&lt;/p&gt;

&lt;p&gt;That's the part that matters: not whether you hit a deadline, but whether what you built survives the deadlines you miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ghostclaw:&lt;/strong&gt; &lt;a href="https://github.com/Ev3lynx727/ghostclaw" rel="noopener noreferrer"&gt;github.com/Ev3lynx727/ghostclaw&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built for #JuaraVibeCoding 2026 — submitted late, shipping daily.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ghostclaw</category>
      <category>python</category>
      <category>typescript</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
