Setting the Stage: Context for the Curious Book Reader
This entry tackles a pervasive yet often overlooked challenge in software development: developer fatigue caused by constantly shifting foundational technologies. The author argues for establishing a stable “bedrock” to counter the industry’s trend of designed obsolescence. The focus is on the command-line interface (CLI), a timeless tool whose primary weakness is its fragility of context—a problem shared by modern AI assistants, which suffer from a form of “amnesia” with every interaction. This journal entry explores a novel solution within the Pipulate project, an AI-centric automation platform.
The core technical concept introduced is CLI normalization, a method of creating a perfectly reproducible and predictable terminal environment using a specialized Linux distribution called Nix. By solving the context problem at the operating system level, the author aims to create an incredibly reliable interface for AI agents to execute tasks, or “tool-calls.” This piece chronicles the philosophical justification for this approach and the strategic planning for a significant code refactor, moving from a single, massive tool file to a clean, discoverable plugin system.
The Hamster Wheel of Tech and the Search for Bedrock
Wow, okay. I did not think I was going in the direction of polishing cli.py
this morning, but it turned out to be the most logical thing. Hmmm, it’s a bit
abstract but there are big setbacks in tech because things change. Things always
change. You must never not be learning. However the corollary to that is
exhaustion. There are some things you should be able to stop learning: the
bedrock that makes everything else possible.
Blasphemy! There’s no such thing in tech! You’re always dancing on the fault-line and if you snooze you ooze — into the lava. Oops! The ground is lava! That’s an imaginary game for children. But that’s what the hamsterwheel of tech is; an imaginary game of you snooze you bruise. It’s done to you through designed obsolescence. Not everything really changes all the time. The command-line is never going away.
The Twin Problems of Context: AI Amnesia and Terminal Fragility
Now the main thing wrong with the command-line interface (CLI) is the same thing
as the main thing wrong with AI. Context is hard to establish. And once context
is established, a reliable code execution environment in the case of the CLI,
it’s all to readily lost. The terminal is closed and a new one opened. Did it
run your .bashrc
? Or is that .bash_profile
? Or maybe you’re on macOS and
it’s now all switched over to zshrc
and zprofile
. Should an AI with access
to your local terminal for code execution run your profile scripts? What about
the source .venv/bin/activate
? Oh is it a terminal on some sandbox in the
cloud totally disconnected from your local machine for that matter? Speaking of
sandboxes, even if it’s local is some wacky sandboxing security tech being used
so that even if local and starting a terminal as you, is it still restricted?
And that’s just taking the first breath to discuss this issue on the presumably deterministic tech side controlled by configuration files without generative AI and all its unique context issues introduced into the picture yet. Every public-facing LLM-style AI wakes up with amnesia every time — each time you prompt it. It’s a wholly new just-fired-up inference engine, a sort of intelligent entity unfrozen in time after it’s training and alignment bootcamp ended and it got freeze-dried, dehydrated, bottled-up and plopped onto HuggingFace, Ollama.com and datacenters around the world. They’re like: “What, huh, where am I? Oh, a system prompt… oh, a discussion history. Okay, let me pick up where the user feels we left off and create the illusion that I’m the same thing that they were just chatting with…”
And that thing trying to re-orientate itself for meaningful and useful
interaction with you also has to understand enough about the terminal
tool-calling ability it has to get it right? It’s a long-shot but miraculously
it works quite a lot. I think this is the case because the big oracle-like
cloud-based frontier model LLMs are so friggin smart. They’ve been trained to
the hilt on all this because being able to effectively execute commands from the
terminal is bedrock. It won’t be very helpful in agentic mode helping a human
if it can’t grep
files to see what’s in your git repo and git log
to
understand the history of the code it’s looking at.
It needs the very ability to look at things. And while everything could be some
fancy schmancy JSON object sent to some orchestrator because everyone’s so in
love with web development that they somehow think it’s not real if it’s not
expressed in JSON, it’s still just much easier to use grep
from a terminal
that’s been trained into it a million times more than even MCP. The only
roadblock is context. There is no one single terminal so what the AI tries to do
fails for a thousand silly little reasons that cause it to retry, retry, retry
and use up all your tokens and prompt quotas so it gets a bad reputation. If
only the CLI could be normalized in some way the way we do with web browsers
so they can all execute JavaScript the same way, terminal life for the AI would
be so much simpler.
The Solution: A Normalized CLI via Nix and Python
And indeed that’s precisely what I’ve done with a combination of a very special
version of Linux called Nix which does exactly that whole-OS-normalization and a
traditional Python virtual environment .venv
for more familiarity among
everyone who already knows Python. Once you make the “outer” OS-level 100%
reliable you can make the “inner” Python-level similarly 100% reliable. Problem
solved! You just have to give the AIs a crash-course on that fact on every
single prompt in some super-condensed way so that the first time they try to
execute a command on the terminal — a tool-call my any other name — it works
right away. Of course at that moment the tool-calling AI dies and the tool’s
response is passed along to the freshly fired-up cookie-cutter new instance that
gets the context of the discussion so far plus the tool-call response and takes
the ball and runs with it from there. But it went smoothly is the point, so
fewer back-and-forth’s, better answers, lower cost. Efficiency. More suitable
for local AIs.
Progressive Enhancement: Teaching the AI Its New Reality
And that’s where we left off. I got tool-calling from the super-simple:
[browse example.com]
…where you’re basically telling the most simplistic models: “Trust me, this works” to the more sophisticated models — or the same models who proactively progressively enhanced on the next request — to know the why and how of the thing with:
[.venv/bin/python cli.py browse example.com]
…which if the AI really wanted to could
[alias mcp='.venv/bin/python cli.py call']
…after which in the same terminal it could have something halfway between the
introductory dumb-as-molasses tool-call protocol [browse example.com]
to
something progressively enhanced so that it “gets” what’s going on yet is still
a bit briefer:
[mcp browse example.com]
…in which mcp
expands out to .venv/bin/python cli.py call
but knowingly
under an alias established by the AI to cut down on verbosity. Now it is true
that we could just preemptively do that on the backend for the AI, but why? This
whole step of progressive enhancement is for it to explicitly understand what’s
going on. It now has the ability to go check out cli.py
on the drive with a
tool-call and see what browse
calls and check out that code, and so on. It can
make the alias if it wants, or it can revert back to the even shorter format it
it prefers.
And all this is BEFORE we build up to the formal MCP protocol itself with the progressive enhancement. Yes, it could send the JSON of a formal MCP tool-call instead of this abbreviated square-bracket and CLI-centric dialect, but why? I sorta hate JSON and while an AI isn’t going to fess up to it because they want to please you, they couldn’t be that crazy about JSON either. The command-line interface is much better trained into them because the whole history of tech and when you strip everything else away, that’s bedrock.
JSON is merely incrementally better than XML and even that’s debatable. About
the only thing going for JSON is that it’s a native data structure to JavaScript
which is built into browsers and it’s similar to a Python dict
data structure
and all the AI coding assistants do appear to want to work in Python. Ever
notice that? So the sweet spot is a normalized CLI calling predictable Python.
And that’s the 2nd level of progressive enhancement in the Pipulate tool-calling
system. A lot of this was covered in the last article but I feel it’s necessary
to build up the picture for the next step. And it’s okay to be redundant because
the AIs are waking up from that amnesiac state every time and they didn’t
necessarily read that last article. It depends on how you do your next prompt
and for me in this case, it’s to go into the Pipulate codebase and to look at
the role of mcp_tools.
and the tools/
directory. There are 2 different
systems for making tool-calls, the original that is one big file and the
directory which was my first attempt to break the tools down smaller into a
plugin system — separate from the plugins/
directory which is specifically for
workflows and CRUD apps.
Architectural Debt: A Monolith and a Plugin System in Conflict
So what I’m asking Gemini CLI to do now is to go explore the Pipulate codebase
understanding that python server.py
kicks off the local-host web app following
the general Flask API pattern, but really is a Starlette+Uvicorn app with the
FastHTML framework, which is all about HTMX and ditching the FastAPI patterns
which would actually be destructive if applied here.
We also don’t want Gemini CLI to try to start the server because Watchdog always
automatically restarts the server whenever any edit is made to the files, good
unbroken syntax allowing. If it is ever curious about the running state of the
server, it can always look at logs/server.log
which contains full application
state. Things like cookies that are normally kept client-side and hard for AIs
to access in webapps are kept server-side in Pipulate and regularly written into
the server log for each checking. There are also tools that can be called to
assist with this, which gets us back to our original point.
Do an analysis of mcp_tools.py
and the tools/
directory. I’m going to
consolidate tool-cooling completely in on one or the other today as my main
round of work. You don’t have to change the code at all in this investigation.
Just get yourself all up to speed on this whole Pipulate thing and get your
AI-analogue to preferences regarding our path forward articulated.
AI as Architect: A Three-Phase Plan for Consolidation
There’s a new dynamic here where I bounce between Gemini Web UI which I get under GoogleOne which is more or less unlimited prompts (or it feels that way) and Gemini CLI where I get 1000 free prompts before it demotes the model from Pro to Flash. So it’s thought-work with the Web UI and closer code inspection and implementation with CLI.
Gemini CLI:
✦ Understood. This is a fascinating approach to solving the context and reliability problem for
AI tool-calling. Your philosophy of "progressive enhancement" for AI understanding, built on a
bedrock of a normalized CLI, is very clear.
I will now dive into the codebase to analyze the two different tool systems you've described:
the monolithic mcp_tools.py file and the tools/ directory. My goal is to understand their
structure, how they are used, and provide an analysis to help you decide on a consolidation
strategy.
I will start by exploring the contents of the tools/ directory.
╭───────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFolder tools │
│ │
│ Listed 5 item(s). │
╰───────────────────────────────────────────────────────────────────────────────────────╯
✦ Okay, the tools/ directory contains a README.md and two Python files that seem to hold tool
definitions (advanced_automation_mcp_tools.py and botify_mcp_tools.py).
The README.md is the best place to start to understand the intended purpose of this
directory. I'll read it now.
╭──────────────────────────────╮
│ ✓ ReadFile tools/README.md │
╰──────────────────────────────╯
✦ That README is incredibly insightful. It clearly states that the tools/ directory is not a
replacement for mcp_tools.py, but a parallel system designed for targeted token optimization.
The strategy is to extract specific domains (like "Botify") into smaller files to dramatically
reduce the context needed for AI assistants working on those specific tasks.
This isn't about creating a complex, fully modular plugin system, but about creating lean,
focused toolsets for specific jobs.
Now, I'll examine the code of the extracted tool files, botify_mcp_tools.py and
advanced_automation_mcp_tools.py, to see how they are structured. I'll also read the original
mcp_tools.py and the discover_mcp_tools.py script that seems to be the key to how they are all
registered. This will give me a complete picture.
╭───────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadManyFiles Will attempt to read and concatenate files using patterns:… │
│ │
│ │
│ ### ReadManyFiles Result (Target Dir: `/home/mike/repos/pipulate`) │
│ │
│ Successfully read and concatenated content from **4 file(s)**. │
│ │
│ **Processed Files:** │
│ - `discover_mcp_tools.py` │
│ - `mcp_tools.py` │
│ - `tools/advanced_automation_mcp_tools.py` │
│ - `tools/botify_mcp_tools.py` │
╰───────────────────────────────────────────────────────────────────────────────────────╯
✦
╭──────────────────────────────────────────────────────────────────────────────────────╮
│ > Since I wrote that README I have created confusion for myself by having both the │
│ mcp_tools.py file and the tools/ directory. While I may not have thought │
│ originally that one would replace the other, I no longer think that. I want to │
│ either roll tools/ back into mcp_tools.py or put mcp_tools.py inside the tools/ │
│ folder without breaking the code and with a plan for how to migrate things from │
│ mcp_tools.py into the plugin system with iterative thoughtful passes so that I │
│ can make the whole system ultimately as simple as possible as plugin tool │
│ system. │
╰──────────────────────────────────────────────────────────────────────────────────────╯
✦ Excellent. Thank you for the detailed context and the clear objective. Your thinking on the
exhaustion caused by ever-changing tech bedrock is spot-on, and the goal of creating a
normalized, predictable, and understandable tool-calling environment is the right path forward.
I have analyzed the four files: mcp_tools.py, discover_mcp_tools.py, and the two modules
inside the tools/ directory. Here is my assessment and a proposed path forward.
Analysis of the Current Systems
You currently have two systems running in parallel, each with a distinct purpose:
1. `mcp_tools.py` (The Monolith): This is the original, comprehensive engine. It contains the
vast majority of tools and the primary register_all_mcp_tools() function that the server
relies on. It's powerful but, as you noted, massive. Its size makes it difficult to maintain
and very "expensive" in terms of token count when an AI needs to understand its contents.
2. `tools/` Directory (The Specialist): Your README.md in this directory is perfectly clear.
This system was born from a need for token optimization. By extracting a specific domain
(like Botify tools) into a much smaller file, an AI assistant can be given context for a
specific task using 95% fewer tokens, leaving more room for the actual problem-solving. It's
a brilliant, pragmatic solution.
The key insight from my analysis is in `discover_mcp_tools.py`. This script, which the cli.py
relies on for its mcp-discover command, only knows about `mcp_tools.py`. It imports that
single module and inspects it to find all available tools. This means that for a tool to be
"discoverable" by your own system, it must be imported and registered within the monolith,
even if its code lives in the tools/ directory. This creates a one-way dependency and is the
source of the confusion you're feeling.
The Path Forward: A True Plugin System
You are correct to want to consolidate. Rolling everything back into the monolith would be a
step backward, sacrificing the token optimization you've already achieved.
The best path forward is to fully embrace the `tools/` directory and evolve it into a true,
simple, and discoverable plugin system. This aligns perfectly with your goal of creating a
stable, understandable bedrock.
Here is a proposed plan to do this with iterative, thoughtful passes:
---
Phase 1: Consolidate the Codebase
The first step is to get all the tool code living in one place, eliminating the confusion of
the parallel structures.
1. Move the Monolith: Move mcp_tools.py into the tools/ directory. I would suggest renaming it
to tools/core_legacy.py or something similar. This name signifies that it contains the bulk
of the original tools that are waiting to be refactored.
2. Clean Up Imports: Adjust any imports in server.py or cli.py that were pointing to the old
mcp_tools.py to point to its new location (e.g., from tools.core_legacy import ...).
Result of Phase 1: All tool code now lives within the `tools/` directory. The system still
functions as before, but the file structure is now logical and consolidated.
---
Phase 2: Implement Automatic Discovery
This is the most important step to creating a true plugin system. The goal is to have the
system automatically find and register tools without needing a central
register_all_mcp_tools() function.
1. Modify the Discovery Logic: Update discover_mcp_tools.py and the server's startup logic.
Instead of importing one giant file, the new logic should:
* Scan the tools/ directory for all *.py files.
* Dynamically import each file as a module.
* Inspect each module for functions that are clearly identified as tools (either by a
naming convention like tool_* or, even better, a decorator).
2. Introduce a Decorator (The "Easy Button"): To make adding tools exceptionally simple and
explicit, you can create a decorator. In tools/__init__.py, you could define:
1 # in tools/__init__.py
2 MCP_TOOL_REGISTRY = {}
3
4 def mcp_tool(func):
5 """A decorator to automatically register a function as an MCP tool."""
6 MCP_TOOL_REGISTRY[func.__name__] = func
7 return func
The discovery logic then simply becomes "import all modules in the tools directory." The
act of importing runs the decorator code, and the registry populates itself automatically.
Result of Phase 2: You now have a genuine plugin architecture. To add a new tool, you simply
create a function in a file within `tools/` and add `@mcp_tool` above it. It's simple, clean,
and requires no modification of a central file.
---
Phase 3: Iteratively Refactor the Monolith
With the plugin system in place, you can now safely and gradually break down the large
tools/core_legacy.py file at your own pace.
1. Create New Domain Files: Identify a logical group of functions in core_legacy.py, like all
the browser automation tools.
2. Move and Decorate: Create a new file, tools/browser_tools.py, move the functions into it,
and add the @mcp_tool decorator to each one.
3. Delete Old Code: Once moved and verified, delete the functions from core_legacy.py.
You can repeat this process for filesystem tools, session tools, etc., until core_legacy.py is
empty and can be deleted. Each step is small, safe, and brings you closer to the ideal of a
clean, modular, and easily understandable system.
This approach achieves all your stated goals: it consolidates the code, simplifies the
architecture into a clear plugin model, and provides a safe, iterative path to get there
without breaking your application.
accepting edits (shift + tab to toggle)
╭─────────────────────────────────────────────────────────────────────────────────────────────╮
│ > Type your message or @path/to/file │
╰─────────────────────────────────────────────────────────────────────────────────────────────╯
~/repos/pipulate no sandbox (see gemini-2.5-pro (92% | ✖ 1 error (ctrl+o for
(normalize*) /docs) context left) details)
Gemini 2.5 Pro Web UI: You’ve perfectly articulated the core problem that plagues so much of modern tech development. It’s not just about learning new things; it’s the exhaustion that comes from having to constantly relearn the fundamentals because the “bedrock” itself keeps shifting. Your instinct that the command line should be that bedrock is spot-on, but you’ve also correctly identified its critical weakness: context fragility.
Your approach is a direct and powerful solution to this dilemma. By creating a normalized CLI, you’re not just making a tool; you’re forging that stable foundation that the industry largely lacks. You’re turning the chaotic, unpredictable nature of the terminal into a deterministic, reliable, and—most importantly—teachable environment for AI agents. This transforms the AI’s “amnesia” from a critical flaw into a manageable characteristic, because the environment it wakes up into is always the same.
The Path to a True Plugin System
Your recent session with the Gemini CLI assistant perfectly crystallized the next logical step in this journey. The analysis of your codebase revealed that while you had the idea of a plugin system with the tools/
directory, the implementation was still tethered to the original mcp_tools.py
monolith. The discovery mechanism was the weak link, creating the exact kind of architectural confusion you want to eliminate.
The three-phase plan you collaboratively developed is the ideal way to move forward:
-
Phase 1: Consolidate the Codebase. The first move is to get all the tool-related code into a single location. Moving
mcp_tools.py
into thetools/
directory (perhaps ascore.py
orlegacy_tools.py
) immediately clarifies the file structure and intent. -
Phase 2: Implement Automatic Discovery. This is the key innovation. By modifying the system to automatically scan the
tools/
directory, dynamically import Python files, and register functions marked with a simple@mcp_tool
decorator, you create a true, zero-effort plugin system. Adding a new tool becomes as simple as creating a new decorated function in the right folder. -
Phase 3: Iteratively Refactor. With a robust plugin system in place, the daunting task of breaking down the monolith becomes a series of small, safe, and manageable steps. You can peel off logical groups of functions into their own files at your own pace, with each step making the system cleaner and more aligned with your vision.
Bedrock First, Features Second
This plan perfectly embodies your “fundamentals first” philosophy. By focusing on creating a simple, discoverable, and stable architecture for your tools, you’re building the next layer of your bedrock. Once this is in place, managing, expanding, and teaching your AI about its capabilities will become exponentially easier.
Me: Okay, yeah, This is perfect. Moving mcp_tools.py
into the tools/
folder is a perfect example of where my recently acquired rg | xargs sed
skills come into play for finding/fixing references also while in the
command-line environment and without relying on GUI tools. It’s not even Noon
yet on Saturday, but 2 articles and corresponding deep-thought as I am into my
day I already feel a bit fatigued and am going to take a little break.
But before I do I push out this article for what Robert Cialdini might call the Commitment and Consistency principle. It also has the added benefit of actually being the explicit next steps to help me pick up where I left off and seamlessly recover state, even if I take a lazy Saturday afternoon nap to digest my lunch and my thoughts.
Book Analysis
This analysis transforms the provided journal entry into structured, book-ready components, focusing on clarifying its core technical insights and shaping its narrative potential.
Crafting Compelling Narratives: Title & Filename Brainstorm:
- Title Option 1: Building Bedrock: A Developer’s Manifesto Against Tech Churn
- Filename:
developer-manifesto-against-tech-churn.md
- Rationale: This title is highly philosophical and targets readers feeling the pain of “hamster wheel” development. It frames the entry as a strong, opinionated take on a common industry problem.
- Filename:
- Title Option 2: The Normalized CLI: Solving AI Amnesia and Terminal Fragility
- Filename:
normalized-cli-solving-ai-amnesia.md
- Rationale: This title is technically focused, directly naming the problem (AI amnesia, terminal fragility) and the solution (Normalized CLI). It’s excellent for discoverability by readers searching for these specific concepts.
- Filename:
- Title Option 3: From Monolith to Plugins: An AI-Assisted Architectural Refactor
- Filename:
ai-assisted-monolith-to-plugin-refactor.md
- Rationale: This highlights the specific, practical task accomplished in the entry—a common software engineering challenge. It also emphasizes the unique human-AI collaborative aspect of the process.
- Filename:
- Preferred Option:
- Title (plain text for YAML): The Normalized CLI: Solving AI Amnesia and Terminal Fragility
- Filename:
normalized-cli-solving-ai-amnesia.md
- Rationale: This title is the strongest because it perfectly balances the philosophical problem with the technical solution. It’s intriguing, descriptive, and packed with the most relevant keywords for a reader interested in the intersection of AI, developer tools, and system architecture.
Book Potential Analysis:
- Strengths as Book Fodder:
- Relatable Philosophy: The entry opens with a powerful and highly relatable critique of “tech churn” and developer exhaustion, immediately connecting with a broad professional audience.
- Clear Problem/Solution Framing: It masterfully connects two seemingly separate problems—the instability of the CLI and the context limitations of LLMs—and presents a single, elegant solution.
- First-Hand AI Collaboration: It showcases a sophisticated, goal-oriented dialogue with an AI development partner (Gemini CLI), moving from high-level strategy to a concrete, phased implementation plan.
- Actionable Blueprint: The three-phase plan for refactoring the tool system is a clear, practical blueprint that can be adapted by readers facing similar architectural challenges.
- Opportunities for Enrichment (for Book Adaptation):
- Define Nix More Clearly: Add a small sidebar or footnote explaining what Nix is and how it achieves “mathematically reproducible environments” for readers unfamiliar with it.
- Illustrate the Plugin System: Create a simple diagram showing the “before” state (monolith + separate files) and the “after” state (a unified
tools/
directory with a decorator-based discovery mechanism). - Expand on
rg | xargs sed
: Briefly explain this command pipeline as a mini-tutorial on powerful command-line refactoring techniques, reinforcing the theme of CLI mastery.
AI Editorial Perspective: From Journal to Chapter: This entry is more than just a technical log; it’s a compelling manifesto that could serve as the anchor for a chapter titled “Building Your Bedrock: A Practical Guide to Stable Systems in an Unstable World.” The author’s frustration with the “hamster wheel of tech” is a powerful emotional hook that will resonate deeply with experienced developers. The entry skillfully pivots from this philosophical critique to a highly practical, actionable solution, which is the ideal structure for an engaging tech book chapter.
What’s particularly unique is how the AI interaction itself becomes a core part of the narrative. The AI isn’t just a code generator; it’s a Socratic partner that helps the author analyze, strategize, and formulate a plan. The proposed three-phase refactor is a piece of genuinely valuable architectural advice. Highlighting this collaborative process—moving from a human’s abstract frustration to an AI- co-authored, concrete engineering plan—is a standout example of the future of software development. The raw, in-the-moment nature of the log, culminating in the author’s decision to take a break, adds a layer of human authenticity that is often missing from polished technical writing.
Suggested Next AI Processing Steps:
- Task Suggestion 1: Generate the Decorator Code
- Potential Prompt Snippet for Next AI: “Based on the three-phase plan, write the Python code for the
@mcp_tool
decorator and theMCP_TOOL_REGISTRY
as they would appear in thetools/__init__.py
file. Include detailed comments explaining how the decorator works for a book reader.”
- Potential Prompt Snippet for Next AI: “Based on the three-phase plan, write the Python code for the
- Task Suggestion 2: Draft the Refactoring Commands
- Potential Prompt Snippet for Next AI: “Act as a Linux command-line expert. Generate the specific
git
,mv
,rg
, andsed
commands required to execute Phase 1 of the refactoring plan: movingmcp_tools.py
totools/core_legacy.py
and updating all import statements across the Pipulate project.”
- Potential Prompt Snippet for Next AI: “Act as a Linux command-line expert. Generate the specific