Technical Writing
Writing
Practical write-ups on building production AI systems - MCP servers, running agents, data pipelines, computer use. Not tutorials; notes from real deployments.
My agent tool loop had careful bounds on iterations, tokens, and tool output. All of them were scoped to one run, and a single event could start fifty of them.
Adding a REST surface to an MCP server was mostly an exercise in refusing to write anything twice. The interesting decisions were all about what the adapter is allowed to know.
The test everyone would point at to answer 'what stops a restricted key holding a dangerous permission' had hardcoded a copy of the list it was supposed to be guarding.
I wanted a tool that lets an AI set up its own scheduled jobs. The safe subset I first wrote down turned out to be a privilege escalation that outlives the conversation.
Three sync engines committed 'I have seen this item' to disk before attempting to queue the automation event it owed. A crash in that gap lost the event permanently, and nothing anywhere recorded that it was owed.
An audit of my own file-writing tools found three separate ones that would silently destroy an existing file, all with the same shape, none of them intentional.
The standing advice for large MCP servers is to collapse 100 tools into a dozen area tools with an action parameter. My server ships 123 flat tools across 20 prefixes, deliberately, and the reason is the permission layer.
Closing out a few weeks of field notes on rewriting my own infrastructure: the Mac and Windows builds share no code, and a shared fixture file just caught a real behavioral divergence between them before either shipped.
One dependency got rejected before I wrote a line against it. Another got added, then partially reverted after it broke the build in a way nothing on my side could fix.
I dropped Quartz and wrote my own cron evaluator. Daylight saving time found three real bugs in it before a single scheduled job ever missed a fire.
Checking a list of running processes to prevent a second launch has a race condition baked into how you'd naturally test it: one launch at a time.
I built the permission system for my new MCP server while it only had grant-free utility tools, before there was anything worth locking down. That order was deliberate.
Synthetic test fixtures only cover the malformed input you already imagined. Real archives contain the malformed input you didn't.
The same one-line bug hit two unrelated parsers, months apart, because Swift's Character type doesn't mean what a line-splitting function needs it to mean.
Most systems only have two states in the code, working and crashed, when real failures need a third: running with a known, stated reason why one part isn't.
I'm replacing the Java service that has run my agent fleet since early 2025 with a native Mac app I intend to sell. Field notes from the first real decisions.
The individual habits - plan first, match the model to the phase, review AI with AI - are worth more chained into one command than run by hand. Here's the single command I use to take a task from plan to commit.
Retrieval is how you get the right piece of your own material in front of the model at the moment it's needed, and most teams overbuild it before they've earned the complexity.
When an AI agent does the wrong thing, don't argue with the model. Read the sequence of tool calls it made and what each returned; the divergence is right there.
A long-running AI session accumulates stale detours and corrections that dilute attention. The skill is noticing when a conversation has gone stale and cutting a clean one, carrying forward only what still matters.
When another process consumes a model's answer, free-form prose is a liability. Constrain the output to a defined shape and validate it at the boundary instead of regex-parsing English.
Reading AI output and deciding it looks right is vibes-based grading that hides regressions; a small repeatable set of test cases with known-good answers gives you a real signal instead of a feeling.
Left alone, an AI regresses to generic patterns. It only follows your conventions when you maintain one authoritative reference library and point every project at it.
A dedicated gotchas file logs the sharp edges your AI keeps hitting, so the model consults it before acting instead of re-learning the same painful lesson every session.
Running every phase of a task on your most expensive model wastes money and rarely improves output. Use a strong model to plan and review, a fast model to generate, and match each phase to what it actually needs.
The quality of AI output is decided by the context you supply, not by clever wording. Point the model at the right files, paste the real errors, and state the constraints it cannot see.
The team-scale version of the reference library - put your coding conventions, patterns, and gotchas in a shared git repo every engineer's AI reads and everyone updates as they learn.
Moving from pasting text into a chat window to giving the model real tools it can call to read files, query a database, and take actions on your behalf.
I develop a Windows-only desktop app from my Mac by splitting the agent's world in two: a mounted share is where it edits, an SSH session into a Parallels VM is where it builds and tests.
Why the first thing a model produces is a draft, and how to build a separate adversarial review pass with a clean context that catches what the generator missed.
The highest-leverage habit in AI-assisted development is making the model write a plan first, correcting it while it is cheap, and only then letting it implement.
An AI navigates your codebase by reading it, the way a new engineer would but with a narrower view. A repo that is discoverable to a human is discoverable to a model, and the same things that confuse people send the model down wrong paths.
Models degrade when a task is too big to hold clearly at once. The fix is decomposition: break large work into scoped units that each fit in the model's working memory with room left for reasoning.
The practical security hygiene of working with AI - keeping secrets out of prompts, treating untrusted content as a prompt-injection vector, scoping tool permissions tightly, and gating consequential actions behind human review.
The model is the least stable dependency in your stack, so pin its exact version, record which model produced which output, and adopt upgrades on purpose instead of by accident.
AI cost accrues a fraction of a cent at a time across thousands of calls, so it hides easily. Instrument token spend, track cache hits, and alert on anomalies.
The skill that separates people who get leverage from AI from people who get burned is knowing which work to hand off and which to keep behind a human gate.
The graduation path from a prompt you keep re-running by hand into a standing agent that fires on a schedule, fetches its own inputs, and writes its own outputs.
A practical look at Claude computer use and browser automation in a real production workflow - what works, what doesn't, and where the real limits are.
How to build a production AI data parsing pipeline that extracts structured data from unstructured documents - architecture, tradeoffs, and what actually works.
Architecture decisions and implementation patterns for a Spring Boot MCP server backing a fleet of autonomous Claude agents.
What production AI agent infrastructure actually requires - the scheduler, the MCP server, agent definitions, observability, and what makes agents reliable vs. unpredictable.
More technical notes are cross-linked from klass.wiki, my broader reference library.