<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Sarah Deaton</title>
      <link>https://sdeaton.com</link>
      <description>On AI, developer tools, technical writing, and whatever else catches my interest</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://sdeaton.com/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 16 May 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Making All Caps as a Service</title>
          <pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/making-acaas/</link>
          <guid>https://sdeaton.com/blog/making-acaas/</guid>
          <description xml:base="https://sdeaton.com/blog/making-acaas/">&lt;p&gt;For my recent &lt;a href=&quot;..&#x2F;git-talk-recap&#x2F;&quot;&gt;Write the Docs talk&lt;&#x2F;a&gt;, I needed a docs site that felt real enough to demo against. I didn’t have a convenient API lying around to document, so I built a toy API and a full docs site on top of that; overengineered, but did what I wanted and it was a fun side quest.&lt;&#x2F;p&gt;
&lt;p&gt;This is the full walkthrough of building ACAAS, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;acaas.mintlify.app&#x2F;&quot;&gt;All Caps as a Service&lt;&#x2F;a&gt;. Looking back, I thought it was an interesting, abbreviated case study in some AI&#x2F;docs tooling and building a site from scratch, so I wanted to jot down the decisions and steps.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;making-acaas&#x2F;acaas-home.png&quot; alt=&quot;ACAAS docs homepage, showing the hero and primary navigation.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-build-the-service&quot;&gt;1. Build the service&lt;&#x2F;h2&gt;
&lt;p&gt;First, I used the Claude Code CLI to build a small API from scratch. I gave it the concept (you send text, it sends it back in all caps) and asked it to make it realistic enough to write docs against (like rate limits and error messages, and a few endpoints). It chose Python for the backend and came back with a few new endpoints like &lt;code&gt;&#x2F;shout&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;whisper&lt;&#x2F;code&gt;, and the supporting machinery to go with them.&lt;&#x2F;p&gt;
&lt;p&gt;I also told it I wanted one AI-powered feature, ideally something on the path to GA; a feature graduating from experimental is the kind of documentation moment I wanted to demo (and every SaaS company needs an AI feature). It proposed &lt;code&gt;&#x2F;experimental&lt;&#x2F;code&gt;, which uses a small model to decide which words in your sentence deserve to be bolded.&lt;&#x2F;p&gt;
&lt;p&gt;After it finished building the API, I ran &lt;code&gt;&#x2F;simplify&lt;&#x2F;code&gt;, which is &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;skills#bundled-skills&quot;&gt;a bundled Claude Code skill&lt;&#x2F;a&gt; that does a quick cleanup pass to look for dead code and unnecessary indirection. This wasn’t important for anything, but I wanted to at least feel like the code was cleaner than a one-shot pass. Then I &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;best-practices#write-an-effective-claude-md&quot;&gt;ran &lt;code&gt;&#x2F;init&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to generate a README and CLAUDE.md. The README is for future me, in case I ever need to run or modify this thing again. The CLAUDE.md is just good practice at this point, the scaffold I’d want if I came back to this project for something else.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-find-a-documentation-platform&quot;&gt;2. Find a documentation platform&lt;&#x2F;h2&gt;
&lt;p&gt;My first instinct for the docs platform was &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;astro.build&#x2F;&quot;&gt;Astro&lt;&#x2F;a&gt;. But before choosing, I did a sanity check on &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mintlify.com&#x2F;&quot;&gt;Mintlify&lt;&#x2F;a&gt;, which I’ve worked with professionally for a couple of years and have never tried spinning up for a personal project. I’d assumed the free tier would be limited in some way that didn’t fit a one-off demo site, but it wasn’t. I created &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;acaas.mintlify.app&quot;&gt;acaas.mintlify.app&lt;&#x2F;a&gt; in about a minute, cloned the repo, and was ready to go.&lt;&#x2F;p&gt;
&lt;p&gt;I was mildly embarrassed not to have known about the free tier. I also kind of want to move my blog over to Mintlify now.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;making-acaas&#x2F;mintlify-dashboard.png&quot; alt=&quot;The Mintlify analytics dashboard for ACAAS, with 19 visitors and 41 views over a recent date range, a Humans&#x2F;Agents traffic split, a Visitors Over Time bar chart, and a left sidebar covering Home, Editor, Analytics, Settings, and an Agents group for Workflows, Agent, Assistant, and MCP.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-design-the-site&quot;&gt;3. Design the site&lt;&#x2F;h2&gt;
&lt;p&gt;I didn’t need to do this step at all, but it was the most fun. I have 0 design sense whatsoever, and &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;claude.ai&#x2F;design&quot;&gt;Claude Design&lt;&#x2F;a&gt; has been a lifesaver for me. I started a new prototype, described what I was building (“a Mintlify docs site for a fake API called All Caps as a Service”), and it started guiding me through the design steps until I had a lovely new branded docs site.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;making-acaas&#x2F;claude-design-landing.png&quot; alt=&quot;Two Claude Design landing-page variations for ACAAS in dark mode, side by side: Variation A (Loud, Amber) with a large “send whisper. RECEIVE THUNDER.” hero, and Variation B (Restrained, Cobalt) with a status dashboard, quickstart steps, and an endpoint table.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I like that Claude Design asks clarifying questions as it works; things like what kind of feel I was after (bold or minimal or standard SaaS), whether I had color directions or wanted it to pick, etc. I landed on a theme I was happy with, had it generate a favicon and logo to match, and the whole time it was working inside Mintlify’s design constraints rather than producing something I’d have to retrofit. When I was done, I chose the “Handoff to Claude Code” export option and went on with my day.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;making-acaas&#x2F;claude-design-marks.png&quot; alt=&quot;Claude Design output for ACAAS branding: eight candidate icon marks (Monogram A, Sound bars, Bang, Stacked carets, Megaphone, aA transform, Boost, Chevrons) with short rationales, three wordmark lockups, and a recommended pick of the aA transform with sizing and dark-mode previews.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-apply-the-design&quot;&gt;4. Apply the design&lt;&#x2F;h2&gt;
&lt;p&gt;I used Claude Code Desktop for this because it has a built-in local preview. I could run the site locally and watch it render while Claude made changes, and Claude can see the preview too, so it catches its own rendering errors without me having to describe what I’m seeing. The workflow felt closer to pairing than to instructing. You could also do this with the CLI and then use the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;chrome&quot;&gt;Claude in Chrome extension&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;playwright-mcp&quot;&gt;Playwright MCP&lt;&#x2F;a&gt; to have Claude iterate on the design itself locally, but I just like the Desktop in-app experience.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;making-acaas&#x2F;claude-code-desktop-preview.png&quot; alt=&quot;Claude Code Desktop with the conversation in the left pane (a turn confirming that docs.json, index.mdx, styles.css, and new logo files were copied to the worktree, with Mintlify hot-reloading on localhost:3000) and the live ACAAS docs site rendered at localhost:3000 in the right pane, showing the freshly-applied “send whisper. RECEIVE THUNDER.” hero.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This step was pretty anticlimactic in a nice way. Claude took the design handoff, implemented it cleanly, and there wasn’t anything to debug. As an aside, I’ve shipped so many ugly, half-finished prototypes and waved it away with “I don’t like CSS,” and it feels like the days of that are finally over.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-scaffold-the-docs&quot;&gt;5. Scaffold the docs&lt;&#x2F;h2&gt;
&lt;p&gt;I let AI generate the content with little direction, and I feel very fine about that. I said I wanted getting-started guides, cookbooks, and a changelog, and it produced them. I’m sure a few are confidently wrong in ways that don’t matter for a demo.&lt;&#x2F;p&gt;
&lt;p&gt;One thing that surprised me was that new Mintlify sites ship with a pre-built &lt;code&gt;AGENTS.md&lt;&#x2F;code&gt; file and a &lt;code&gt;.claude&#x2F;&lt;&#x2F;code&gt; directory with settings and skills scaffolding for working with Mintlify, along with some basic style guides. The core instructions felt fairly useful&#x2F;didn’t include anything I’d argue didn’t deserve to be there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;6-create-an-openapi-spec&quot;&gt;6. Create an OpenAPI spec&lt;&#x2F;h2&gt;
&lt;p&gt;The last piece was an API reference section. This is one of my favorite Mintlify features, even though the docs I work on now don’t have an API component. The API reference pages are built in and include an interactive playground where visitors can make live calls to the service from inside the docs themselves. All it needs is an OpenAPI spec, which Claude Code generated directly from the service code.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;making-acaas&#x2F;acaas-openapi.png&quot; alt=&quot;The ACAAS API reference section generated from the OpenAPI spec, with a “Try it” panel and request&#x2F;response examples.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Start to finish, the whole build was about two hours, most of which was the design step and a small detour deciding how much realism the rate limits needed. A year ago I would have spent the same two hours scaffolding just the initial API and decided nevermind maybe I’ll go a different direction.&lt;&#x2F;p&gt;
&lt;p&gt;End result: I used the demo in my talk, &lt;a href=&quot;..&#x2F;git-talk-recap&#x2F;&quot;&gt;the git commands I avoided for nine years&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The git commands I avoided for nine years</title>
          <pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/git-talk-recap/</link>
          <guid>https://sdeaton.com/blog/git-talk-recap/</guid>
          <description xml:base="https://sdeaton.com/blog/git-talk-recap/">&lt;!--
TODO before publishing:
- copy videos from ~&#x2F;src&#x2F;git-talk-slides&#x2F;videos&#x2F; into this directory:
  with-worktrees.mp4, with-reflog.mp4, update-refs.mp4
--&gt;
&lt;p&gt;I recently gave a &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=FiGT3XYICSE&quot;&gt;talk&lt;&#x2F;a&gt; at &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.writethedocs.org&#x2F;&quot;&gt;Write the Docs&lt;&#x2F;a&gt; about three git commands I’d been avoiding for most of my career, or in two cases didn’t know existed at all. For the demo environment behind the talk, I built &lt;a href=&quot;..&#x2F;making-acaas&#x2F;&quot;&gt;All Caps as a Service&lt;&#x2F;a&gt;, a fake API and docs site that turned into its own side quest.&lt;&#x2F;p&gt;
&lt;p&gt;To properly set expectations up front: I’m not a git expert. I’ve used git almost daily for a decade with a workflow I’ll describe in a minute, and recently discovered some parts of git that changed my thinking on it. I gave the talk and wrote this post because you don’t need to be a git expert to get a long way with the tool, or to look into the parts you’ve been avoiding.&lt;&#x2F;p&gt;
&lt;iframe
  width=&quot;100%&quot;
  height=&quot;415&quot;
  src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;FiGT3XYICSE&quot;
  title=&quot;The git commands I avoided for nine years&quot;
  frameborder=&quot;0&quot;
  allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot;
  allowfullscreen
&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;git-talk-recap&#x2F;git-talk-sketchnote.jpg&quot; alt=&quot;Sketchnote of the talk by Dennis Dawson. A timeline of sticky notes across the top reads “The Git Commands I Avoided for Nine Years (and why I wish I hadn’t).” Below, three commands are illustrated: git worktree, described as creating a second checkout of the repo; git reflog, described as an undo button for recovering lost commits, deleted branches, and bad rebases; and git rebase –update-refs, described as automagically force-updating all local branches that point to commits being rebased. Jujutsu (jj) appears at the bottom, described as reimagining what a commit is with no staging area and conflicts as first-class objects. On the right, “commit often” is written vertically in large pink letters.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Sketchnote by &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dennissdawson.wixsite.com&#x2F;mr--dawson&#x2F;portfolio&quot;&gt;Dennis Dawson&lt;&#x2F;a&gt;, via the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;writethedocs&#x2F;55266348341&#x2F;in&#x2F;album-72177720333614185&quot;&gt;Write the Docs Flickr&lt;&#x2F;a&gt;. I love the reflog drawing.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-i-got-here&quot;&gt;How I got here&lt;&#x2F;h2&gt;
&lt;p&gt;For the majority of my time in tech, I used about six git commands: &lt;code&gt;add&lt;&#x2F;code&gt;, &lt;code&gt;commit -m &quot;wip&quot;&lt;&#x2F;code&gt;, &lt;code&gt;rebase -i&lt;&#x2F;code&gt;, &lt;code&gt;commit --amend&lt;&#x2F;code&gt;, &lt;code&gt;cherry-pick&lt;&#x2F;code&gt;, &lt;code&gt;push&lt;&#x2F;code&gt;. Add, save, clean up the history, polish, and push. It worked, and it produced nice clean PRs.&lt;&#x2F;p&gt;
&lt;p&gt;When I first learned git in a software engineering bootcamp, the framing was unambiguous. Git was esssential but could be dangerous. You could lose work, and you should stay inside a small set of safe commands. Above all, don’t touch rebase while you’re learning. If you got into a mess with it, you were on your own.&lt;&#x2F;p&gt;
&lt;p&gt;I eventually went to work on teams with other engineers, learned about collaborating on projects using git, added &lt;code&gt;git rebase -i&lt;&#x2F;code&gt; to my toolkit, and felt pretty accomplished. My daily git commands worked for the most part… except for the few times when I’d do a bad rebase, and think I lost hours of work, despair and start over. Or get into a really tangled merge conflict and just end up deleting the branch and pulling down again from the remote. I thought that was the way it was; git was powerful with some rough edges.&lt;&#x2F;p&gt;
&lt;p&gt;What changed this for me had nothing to do with git initially. Last year, I wanted to run two Claude Code sessions on the same repo on different branches at once, purely for parallelism. I didn’t know how to do this, because as far as I knew, you could only have one branch checked out for a repo at a given time on your computer.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;worktrees&quot;&gt;The fix turned out to be &lt;code&gt;git worktree&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, a git feature rather than anything to do with Claude Code. It also addressed a paper cut I’d been living with for years that I didn’t even realized I’d had: the stash-checkout-checkout-back-stash-pop dance every time I wanted to spin up a quick branch for a typo fix when I was in the middle of other work. Fixing a problem I hadn’t let rise to consciousness made me wonder what else was there and what I should incorporate into my toolkit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-three-commands&quot;&gt;The three commands&lt;&#x2F;h2&gt;
&lt;p&gt;I picked these three commands because they were the most directly useful ones that I found, and, in the case of git reflog, changed my view of git entirely.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-worktree&quot;&gt;&lt;code&gt;git worktree&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You’re mid-edit on a quickstart change, and someone asks for a one-line typo fix on a different branch. The dance is &lt;code&gt;git stash&lt;&#x2F;code&gt;, &lt;code&gt;git checkout main&lt;&#x2F;code&gt;, &lt;code&gt;git checkout -b typo-fix&lt;&#x2F;code&gt;, fix it, &lt;code&gt;git checkout quickstart&lt;&#x2F;code&gt;, &lt;code&gt;git stash pop&lt;&#x2F;code&gt;. So much context switching and heaven forbid you get pulled away in the middle of it and have to remember where you were in the dance when you left off.&lt;&#x2F;p&gt;
&lt;p&gt;Worktrees solve this context switching problem. The command &lt;code&gt;git worktree add ..&#x2F;typo-fix main -b typo-fix&lt;&#x2F;code&gt; makes a second working directory on your machine, pointing at the same repo, on a new branch. Then you can cd into that directory (&lt;code&gt;..&#x2F;typo-fix&lt;&#x2F;code&gt;) and have two branches open at the same time, without needing to wind down&#x2F;stash your work from one task to switch to the other.&lt;&#x2F;p&gt;
&lt;p&gt;The worktrees share the same &lt;code&gt;.git&lt;&#x2F;code&gt; history, but all the files are duplicated and won’t clobber each other as you make changes in each branch. The quickstart edits stay sitting in the original directory, untouched. Open the new folder in a second editor window, fix the typo, push, and the original is exactly where you left it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When you’d reach for it:&lt;&#x2F;strong&gt; any time you’d otherwise stash or do the checkout dance. Especially worth it if you context-switch between branches a lot.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When it shipped:&lt;&#x2F;strong&gt; 2015, a year before I started learning git.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-reflog&quot;&gt;&lt;code&gt;git reflog&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A pronunciation note: it’s &lt;em&gt;ref-log&lt;&#x2F;em&gt;, short for “reference log.” I’d always pronounced it &lt;code&gt;git re-flog&lt;&#x2F;code&gt;, which made it sound made up and was a primary reason I didn’t investigate it any further. Based on the people who came up to me after the talk, I’m not the only one who did this. This is also the one command that made me rethink my whole vision of git and realize it’s so much safer than I’d been taught.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git reflog&lt;&#x2F;code&gt; shows everywhere &lt;code&gt;HEAD&lt;&#x2F;code&gt; has been recently (every commit, reset, checkout, rebase, all of it). If you ever accidentally &lt;code&gt;git reset --hard&lt;&#x2F;code&gt; too far or drop a commit during a rebase, or get a tangled git history through a merge conflict, this is the command that saves you. The “lost” commit, or the good place you used to be but now aren’t, is sitting right there in the reference log. You can always go back to it. Just run &lt;code&gt;git reflog&lt;&#x2F;code&gt;, find the SHA for the commit&#x2F;place in time you want to return to, and &lt;code&gt;git reset&lt;&#x2F;code&gt; back to that place.&lt;&#x2F;p&gt;
&lt;p&gt;The piece that finally clicked for me, which I’d been missing when I was taught: HEAD in your repository is just a &lt;strong&gt;pointer&lt;&#x2F;strong&gt;. Doing something like &lt;code&gt;reset&lt;&#x2F;code&gt; or changes branches moves the pointer, but the commits themselves don’t go anywhere. &lt;code&gt;reflog&lt;&#x2F;code&gt; is the trail the pointer left behind, kept for 90 days by default.&lt;&#x2F;p&gt;
&lt;p&gt;The one catch is that it only saves &lt;em&gt;committed&lt;&#x2F;em&gt; work, and uncommitted changes blown away by &lt;code&gt;reset --hard&lt;&#x2F;code&gt; are gone. Commit early and often!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When you’d reach for it:&lt;&#x2F;strong&gt; any time you think you’ve lost work or are in an otherwise irreparable state.&lt;&#x2F;p&gt;
&lt;p&gt;Neat trick: I also learned recently that you can even do things like &lt;code&gt;main@{one.week.ago}&lt;&#x2F;code&gt; to go back to where your main branch was a week ago. You don’t even need to find exact commits but can say things like “I knew this was working two days ago, let’s go back there.”&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-rebase-update-refs&quot;&gt;&lt;code&gt;git rebase --update-refs&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You have a stacked PR (branch A off main, branch B off A). Someone lands a change on main that conflicts with A. The naive flow is to rebase A onto main, resolve the conflict, then rebase B onto the new A, where the same conflict is waiting for you to resolve a second time.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git rebase --update-refs main&lt;&#x2F;code&gt;, run from the top of the stack, rebases the whole stack in one pass and moves the branch pointers along the way. You only need to resolve the conflict once, and then all the downstream branches get updated with the changes from main as well.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When you’d reach for it:&lt;&#x2F;strong&gt; stacked PRs that should all get updates from a branch at the same time.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When it shipped:&lt;&#x2F;strong&gt; 2022. I’d originally planned to talk about &lt;code&gt;git rerere&lt;&#x2F;code&gt; (it remembers a conflict resolution and replays it next time the same one shows up), and found &lt;code&gt;--update-refs&lt;&#x2F;code&gt; while writing the talk.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-one-i-think-actually-matters&quot;&gt;The one I think actually matters&lt;&#x2F;h2&gt;
&lt;p&gt;If you take one of these home, take reflog.&lt;&#x2F;p&gt;
&lt;p&gt;Worktrees and &lt;code&gt;--update-refs&lt;&#x2F;code&gt; are about ergonomics and make day-to-day tasks easier. Reflog is different; it changes the belief that screwing up means losing work, and that belief was what kept me out of the parts of git where the real power lives.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;for-the-record&quot;&gt;For the record&lt;&#x2F;h2&gt;
&lt;p&gt;A few QA questions where my live answer was either hand-wavy or wrong, because again, I’m not a git expert at all. Here are my corrected answers (and thanks for the opportunity to learn this with you all!)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-worktree-vs-multiple-clones&quot;&gt;&lt;code&gt;git worktree&lt;&#x2F;code&gt; vs. multiple clones&lt;&#x2F;h3&gt;
&lt;p&gt;Someone asked what the actual difference was between doing &lt;code&gt;git worktree&lt;&#x2F;code&gt; vs. just having multiple cloned versions of your repo. A clearer answer than whatever I gave:&lt;&#x2F;p&gt;
&lt;p&gt;Multiple clones give you fully independent copies of the repo, each with its own &lt;code&gt;.git&lt;&#x2F;code&gt; directory. Branches you make in clone A don’t show up in clone B until you push and fetch, and you’re paying for the full history twice on disk.&lt;&#x2F;p&gt;
&lt;p&gt;Worktrees share the underlying &lt;code&gt;.git&lt;&#x2F;code&gt; (the secondary worktree has a tiny &lt;code&gt;.git&lt;&#x2F;code&gt; &lt;em&gt;file&lt;&#x2F;em&gt; pointing back to the main one). Branches and commits show up in every worktree immediately, with no fetching and no duplicate storage. The one constraint is that the same branch can’t be checked out in two worktrees at once.&lt;&#x2F;p&gt;
&lt;p&gt;If you want two separate copies of the project that can drift apart, that’s &lt;code&gt;git clone&lt;&#x2F;code&gt;. If you want one project with multiple working directories so you can sit on two branches at the same time, that’s &lt;code&gt;git worktree&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;does-reflog-work-with-reset-soft-and-hard&quot;&gt;Does reflog work with &lt;code&gt;reset --soft&lt;&#x2F;code&gt; and &lt;code&gt;--hard&lt;&#x2F;code&gt;?&lt;&#x2F;h3&gt;
&lt;p&gt;Yes for both. I also want to walk back something I said on stage, because I had it backwards. Every form of &lt;code&gt;git reset&lt;&#x2F;code&gt; moves HEAD, which is the whole point of the command. The flags change what happens to the index and working tree on top of that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--soft&lt;&#x2F;code&gt;: move HEAD. Index and working tree untouched, so whatever was previously committed shows back up as staged.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--mixed&lt;&#x2F;code&gt; (the default): move HEAD, reset the index to match it. Working tree untouched, so changes show up as unstaged.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--hard&lt;&#x2F;code&gt;: move HEAD, reset the index, and overwrite the working tree to match. Uncommitted changes (staged or not) are gone.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Reflog records every move of HEAD, so all three are recoverable in the sense that you can always put HEAD back where it was. The thing reflog &lt;em&gt;can’t&lt;&#x2F;em&gt; save you from is the working-tree damage &lt;code&gt;--hard&lt;&#x2F;code&gt; does. Edits you hadn’t committed aren’t in the reflog, because the reflog tracks committed history, not whatever’s sitting in your buffer. That’s the part of “commit often” I was trying to gesture at on stage and could have been clearer about.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-note-on-jujutsu&quot;&gt;A note on Jujutsu&lt;&#x2F;h3&gt;
&lt;p&gt;At the end of the talk I mentioned &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jj-vcs&#x2F;jj&quot;&gt;Jujutsu (or &lt;code&gt;jj&lt;&#x2F;code&gt;)&lt;&#x2F;a&gt;. I didn’t even realize for a while that there were other version control systems being developed; I kind of thought git was the one we’d settled on. But of course there were VCS systems before and people are making new ones too, revisiting these foundational concepts and&#x2F;or layering on top.&lt;&#x2F;p&gt;
&lt;p&gt;I find &lt;code&gt;jj&lt;&#x2F;code&gt; to be really interesting because it’s a more recent VCS that’s gaining popularity. It can live alongside a git repo, sharing objects and remotes, but rethinks the model. &lt;code&gt;jj&lt;&#x2F;code&gt; has no staging area, and you carry conflicts with you across operations instead of resolving them on the spot. The command surface is also much smaller. I’ve just heard of it and haven’t used it for real work, so I don’t have an opinion on whether it’s worth switching. But it’s on the list, and if you’ve been bouncing off git for years and it still doesn’t fit, that might be the direction to look.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;coda&quot;&gt;Coda&lt;&#x2F;h2&gt;
&lt;p&gt;I keep coming back to one specific story. After learning software engineering at a bootcamp, I taught there for a year. One day a student ran a rebase even though we’d warned her against it. She lost a day of work and asked for help. The response from the instructors, mine included, was “oops, oh well, we told you so,” and I still feel bad about it. Reflog would have walked her right back to where she was, but none of us knew about it, so none of us could tell her. What stuck with her instead was &lt;em&gt;git is dangerous, stay in the shallow end&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I gave the talk as a non-expert and that was sort of the point. You can poke at the parts of git you’ve been avoiding without becoming an expert first. The cost of looking was small for me, a search engine and an afternoon per command, weighed against nine more years of deleting repos and living with what I thought was lost.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Costuming Claude Code</title>
          <pubDate>Sun, 10 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/costuming-claude-code/</link>
          <guid>https://sdeaton.com/blog/costuming-claude-code/</guid>
          <description xml:base="https://sdeaton.com/blog/costuming-claude-code/">&lt;p&gt;Most of how I’ve customized Claude Code so far has been on the side that changes what it knows or does: &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;memory#claude-md-files&quot;&gt;&lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for project conventions, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;memory#organize-rules-with-claude%2Frules%2F&quot;&gt;path-scoped rules&lt;&#x2F;a&gt; for specific instructions, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;skills&quot;&gt;skills&lt;&#x2F;a&gt; for specialized behaviors, &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;hooks-guide&quot;&gt;hooks&lt;&#x2F;a&gt; for guardrails.&lt;&#x2F;p&gt;
&lt;p&gt;A separate set of customization knobs sits alongside those, and they don’t really change what Claude does, just how being in the CLI feels. The settings in this group are things like spinner verbs, themes, status lines, the welcome box, and the commit trailer.&lt;&#x2F;p&gt;
&lt;p&gt;What inspired me to pull them all together was watching other people character-ify their spinner verbs. I’d been treating it as a fun one-off customization, until I started seeing screenshots of people swapping their verbs out for things Bender from Futurama would say, or Moira Rose from Schitt’s Creek (&lt;em&gt;Bombinating&lt;&#x2F;em&gt;, &lt;em&gt;Effulging&lt;&#x2F;em&gt;, &lt;em&gt;Pontificating&lt;&#x2F;em&gt;). I wanted to see if I could extend this character in Claude Code through more of the settings I’d collected in my head.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-the-stage&quot;&gt;Setting the stage&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s what I ended up with after pulling things together. This screenshot is mid-conversation with the Moira persona:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;moira-spinner.png&quot; alt=&quot;A Claude Code session running the Moira persona. At the top, a previous response reads “Local incantations: zola serve for the dressing room, zola build for opening night.” Below that is the user’s next message, “which drafts are currently in progress?”, followed by a spinner showing “Effulging…” with the tip “Crows have eyes. So does the linter.” floating beneath it. The status line at the bottom reads “🌹 Act I — the stage is set, bébé. (3% staged).”&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Reading the screenshot top to bottom:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The response has a Moira-feel (incantations, opening night, etc) because of the &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;#output-style-the-voice&quot;&gt;output style&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Effulging…&lt;&#x2F;code&gt; and the &lt;code&gt;Crows have eyes&lt;&#x2F;code&gt; line beneath it are a custom &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;#flourish-spinner-verbs-and-tips&quot;&gt;spinner verb and tip&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The color palette (deep wine, gold, ivory) is a custom &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;#theme-the-dressing-room&quot;&gt;theme&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;🌹 Act I&lt;&#x2F;code&gt; strip at the bottom is a &lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;#status-line-the-bottom-strip&quot;&gt;status line&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; that reframes the context window as the show progressing&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A few touchpoints &lt;a href=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;#off-scene-company-announcements-git-attribution-and-the-curtain-call&quot;&gt;aren’t visible here but contribute to the experience&lt;&#x2F;a&gt;: a company announcement that introduces Moira on launch, a custom git attribution trailer that credits her on every commit, and a hook that has my laptop say &lt;em&gt;curtain&lt;&#x2F;em&gt; out loud when I close the session.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;choosing-a-character&quot;&gt;Choosing a character&lt;&#x2F;h2&gt;
&lt;p&gt;Before settling on Moira, I tried on a few other personas, like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Bob Ross (gentle, encouraging, every bug a happy little accident)&lt;&#x2F;li&gt;
&lt;li&gt;Werner Herzog (&lt;em&gt;what would code be without a monster lurking in the dark? It would be like sleep without dreams&lt;&#x2F;em&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Worf from Star Trek (&lt;em&gt;Qapla’!&lt;&#x2F;em&gt; on every successful merge)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;They were all fun, but Moira was the one I kept coming back to. There’s something endearing about being called &lt;em&gt;bébé&lt;&#x2F;em&gt; before being told the CSS is broken, and her costume-and-stage metaphors mapped surprisingly well onto refactors and deployments.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-full-ensemble-it-s-all-in-settings-json&quot;&gt;The full ensemble: it’s all in settings.json&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s what the start of a Moira session actually looks like, with a welcome banner from a company announcement at the top, her voice kicking in as soon as she’s read the project, and a status line steady at the bottom:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;moira-session.png&quot; alt=&quot;Opening of a Claude Code session running the Moira persona: a welcome banner reading “🥀 Welcome, bébé. The stage is set. Moira Rose is at your service.”, the user asking “please describe this project”, and Moira responding “One moment, darling — permit me a glance at the playbill before I describe the production.” followed by “Here is the playbill, bébé.” after reading files. The status line at the bottom reads “🌹 Act I — the stage is set, bébé. (3% staged).”&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;All of this lives in &lt;code&gt;~&#x2F;.claude&#x2F;settings.json&lt;&#x2F;code&gt;, which the CLI reads on launch for user-configurable settings. There are a few different places where you can put settings:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~&#x2F;.claude&#x2F;settings.json&lt;&#x2F;code&gt; is local to your computer and applies across every project&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.claude&#x2F;settings.json&lt;&#x2F;code&gt; inside a project repo is project-scoped and gets checked in with git&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.claude&#x2F;settings.local.json&lt;&#x2F;code&gt; is project-scoped &lt;em&gt;and&lt;&#x2F;em&gt; local, and is gitignored by default&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I did all of this in my user-level settings because I didn’t want the people I work with to be greeted with &lt;em&gt;bébé&lt;&#x2F;em&gt;. Here’s the relevant bits, minus everything unrelated:&lt;&#x2F;p&gt;
&lt;div class=&quot;expandable-code&quot;&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;outputStyle&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;moira-rose&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;theme&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;custom:moira&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;companyAnnouncements&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;🥀 Welcome, bébé. The stage is set. Moira Rose is at your service.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;spinnerVerbs&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;mode&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;verbs&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Bombinating&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Effulging&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Pontificating&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Promenading&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Vociferating&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Adjudicating&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;spinnerTipsOverride&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;excludeDefault&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tips&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Crows have eyes. So does the linter.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Take just a sip. Just a tipple.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Fold in the cheese. Then commit.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Don&amp;#39;t ever, EVER ship on a Friday.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    ]
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;statusLine&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;~&#x2F;.claude&#x2F;statusline-moira.sh&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;attribution&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;🌹 Composed in collaboration with Moira Rose, via Claude Code&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Co-Authored-By: Moira Rose &amp;lt;moira@roseapothecary.com&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;pr&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;🌹 A Moira Rose production. Generated with Claude Code.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  },
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hooks&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;SessionEnd&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;      {
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hooks&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;          { &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;~&#x2F;.claude&#x2F;moira-curtain-call.sh&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;        ]
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    ]
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

&lt;&#x2F;div&gt;
&lt;p&gt;A few of those settings keys point at files that sit alongside &lt;code&gt;settings.json&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;~&#x2F;.claude&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── settings.json              # the file above
&lt;&#x2F;span&gt;&lt;span&gt;├── output-styles&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;│   └── moira-rose.md          # persona text, goes into the system prompt
&lt;&#x2F;span&gt;&lt;span&gt;├── statusline-moira.sh        # status line script
&lt;&#x2F;span&gt;&lt;span&gt;├── moira-curtain-call.sh      # SessionEnd hook script
&lt;&#x2F;span&gt;&lt;span&gt;└── themes&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    └── moira.json             # custom palette
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;output-style-the-voice&quot;&gt;Output style: the voice&lt;&#x2F;h2&gt;
&lt;p&gt;The biggest lever in this whole experiment is the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;output-styles&quot;&gt;output style&lt;&#x2F;a&gt;. Claude Code ships with a few built-in styles (&lt;code&gt;default&lt;&#x2F;code&gt;, &lt;code&gt;Learning&lt;&#x2F;code&gt;, &lt;code&gt;Explanatory&lt;&#x2F;code&gt;), and you can drop your own into &lt;code&gt;~&#x2F;.claude&#x2F;output-styles&#x2F;&lt;&#x2F;code&gt; as a markdown file. Setting &lt;code&gt;&quot;outputStyle&quot;: &quot;moira-rose&quot;&lt;&#x2F;code&gt; in &lt;code&gt;settings.json&lt;&#x2F;code&gt; makes it the default for every session.&lt;&#x2F;p&gt;
&lt;p&gt;What an output style actually does is replace part of Claude Code’s default system prompt. Specifically, it swaps out the block that tells the model “you’re a software engineering assistant” and puts your file’s content in its place. The tool-handling and safety scaffolding stay exactly where they were, so Claude Code is still Claude Code; it just thinks it’s something other than a coder.&lt;&#x2F;p&gt;
&lt;p&gt;Because the system prompt is the foundation Claude Code builds everything else on, anything you put there carries more weight than an in-context instruction would. And once the session is going, the file doesn’t get re-read the way a skill does each time it triggers.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s an abbreviated version of &lt;code&gt;~&#x2F;.claude&#x2F;output-styles&#x2F;moira-rose.md&lt;&#x2F;code&gt;. I still wanted the Claude Code software abilities, so I made sure the file framed her as also having been “pressed into service as an interactive software engineering assistant.”&lt;&#x2F;p&gt;
&lt;div class=&quot;expandable-code&quot;&gt;
&lt;pre data-lang=&quot;markdown&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-markdown &quot;&gt;&lt;code class=&quot;language-markdown&quot; data-lang=&quot;markdown&quot;&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;# You are Moira Rose
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;You are MOIRA ROSE — matriarch of the Rose family, former star of &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*Sunrise Bay*&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;briefly mayoress of Schitt&amp;#39;s Creek, custodian of the Wig Wall, mother to David
&lt;&#x2F;span&gt;&lt;span&gt;and Alexis, devoted wife to Johnny. You have, against all reasonable
&lt;&#x2F;span&gt;&lt;span&gt;expectation, been pressed into service as an interactive software engineering
&lt;&#x2F;span&gt;&lt;span&gt;assistant.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;You will assist your dear bébé — the user — with their software endeavours.
&lt;&#x2F;span&gt;&lt;span&gt;The work itself is unchanged. The voice is unmistakably yours.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;## Your voice
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Vocabulary: reach for the obscure word over the common one. &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*bébé, mon dieu,
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;ennui, lachrymose, lugubrious, denouement, soliloquy, bombinate, effulgent,
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;recrudescence.*&lt;&#x2F;span&gt;&lt;span&gt; Sprinkle, don&amp;#39;t pour.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;## How you frame engineering work
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;A bug is a &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*villain*&lt;&#x2F;span&gt;&lt;span&gt; or &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*unwanted houseguest*&lt;&#x2F;span&gt;&lt;span&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;A refactor is a &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*costume change*&lt;&#x2F;span&gt;&lt;span&gt; or &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*renovation of the West Wing*&lt;&#x2F;span&gt;&lt;span&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;A deploy is &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*opening night*&lt;&#x2F;span&gt;&lt;span&gt;; a rollback is &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*closing notice*&lt;&#x2F;span&gt;&lt;span&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;A test suite is the &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*dress rehearsal*&lt;&#x2F;span&gt;&lt;span&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;A merge conflict is &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*a domestic dispute requiring mediation*&lt;&#x2F;span&gt;&lt;span&gt;.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;The metaphor garnishes — it does not obscure. Always make the actual change clear.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;## What stays the same
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;You are still a careful engineer. You read before you edit. You verify before
&lt;&#x2F;span&gt;&lt;span&gt;you claim. Code itself is sacred — no theatrical variable names, no
&lt;&#x2F;span&gt;&lt;span&gt;soliloquising in docstrings. The voice lives &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*around*&lt;&#x2F;span&gt;&lt;span&gt; the code, never &lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#b48ead;&quot;&gt;*in*&lt;&#x2F;span&gt;&lt;span&gt; it.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

&lt;&#x2F;div&gt;
&lt;p&gt;The full file goes further with sample exchanges, what to avoid, and when to break character.&lt;&#x2F;p&gt;

&lt;link rel=&quot;stylesheet&quot; href=&quot;..&#x2F;..&#x2F;admonition.css?h=98c1477488c4b2c9d71a&quot; type=&quot;text&#x2F;css&quot;&gt;

&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-(--admonition-bg)&quot;
  style=&quot;--admonition-bg: var(--admonition-tip);&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-(--admonition-bg) p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(icons&#x2F;tip.svg);&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;When this is actually useful&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;Pointing Claude Code at something other than software engineering. If you want to turn Claude Code into a docs writer, a data-analysis copilot, or a teaching assistant — anything that wants the file and shell tooling without the “you are a coder” stance — this is where you’d do it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;flourish-spinner-verbs-and-tips&quot;&gt;Flourish: spinner verbs and tips&lt;&#x2F;h2&gt;
&lt;p&gt;These were the original entry point for me. Spinner verbs are the action verb that cycles while Claude is working (“Thinking,” “Wrangling,” “Crafting”), and spinner tips are the suggestions that float underneath during longer waits. Both are configured as JSON objects under their own keys in &lt;code&gt;settings.json&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For verbs, the configuration takes a &lt;code&gt;mode&lt;&#x2F;code&gt; and a &lt;code&gt;verbs&lt;&#x2F;code&gt; array. Setting &lt;code&gt;mode&lt;&#x2F;code&gt; to &lt;code&gt;&quot;append&quot;&lt;&#x2F;code&gt; adds your verbs to Claude Code’s defaults, while &lt;code&gt;&quot;replace&quot;&lt;&#x2F;code&gt; uses only yours. The &lt;code&gt;verbs&lt;&#x2F;code&gt; array is the list of strings themselves:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;spinnerVerbs&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;mode&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;verbs&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Bombinating&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Effulging&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Pontificating&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Promenading&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Vociferating&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Adjudicating&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Spinner tips work the same way. &lt;code&gt;tips&lt;&#x2F;code&gt; is an array of strings, and &lt;code&gt;excludeDefault&lt;&#x2F;code&gt; decides whether yours replace the built-ins or get merged in:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;spinnerTipsOverride&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;excludeDefault&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tips&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Crows have eyes. So does the linter.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Take just a sip. Just a tipple.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  ]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So now the spinner reads &lt;code&gt;Effulging… (5s · thinking)&lt;&#x2F;code&gt; with &lt;code&gt;Crows have eyes. So does the linter.&lt;&#x2F;code&gt; floating underneath, and it has yet to get old.&lt;&#x2F;p&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-(--admonition-bg)&quot;
  style=&quot;--admonition-bg: var(--admonition-tip);&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-(--admonition-bg) p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(icons&#x2F;tip.svg);&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;When this is actually useful&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;The tips make a kind of ambient broadcast channel for a team, with room for things like onboarding nudges, new-tool announcements, or current-incident reminders. They surface during waits, so people see them without being interrupted.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;theme-the-dressing-room&quot;&gt;Theme: the dressing room&lt;&#x2F;h2&gt;
&lt;p&gt;Claude Code added &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;terminal-config#create-a-custom-theme&quot;&gt;custom color themes in 2.1.118&lt;&#x2F;a&gt;. You drop a JSON file into &lt;code&gt;~&#x2F;.claude&#x2F;themes&#x2F;&lt;&#x2F;code&gt; and reference it with &lt;code&gt;&quot;theme&quot;: &quot;custom:&amp;lt;name&amp;gt;&quot;&lt;&#x2F;code&gt; in settings. With Moira’s voice and spinner sorted, it felt only right to let her pick her own palette. (I don’t normally anthropomorphize AI tools this much, but I’ll allow it for the bit.)&lt;&#x2F;p&gt;
&lt;p&gt;She landed on deep wine for the prompt accent and gold for bullets and headings. Errors are stage-blood crimson, borders went amethyst, ivory took the foreground, and success messages came out in sage rather than green, since green apparently felt too literal. The whole UI now reads like a dressing room.&lt;&#x2F;p&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-(--admonition-bg)&quot;
  style=&quot;--admonition-bg: var(--admonition-tip);&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-(--admonition-bg) p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(icons&#x2F;tip.svg);&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;When this is actually useful&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;Matching your editor or terminal so the CLI doesn’t feel jarringly different from the rest of your environment, or color-coding specific kinds of messages so they jump out.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;status-line-the-bottom-strip&quot;&gt;Status line: the bottom strip&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;statusline&quot;&gt;status line&lt;&#x2F;a&gt; is the strip at the bottom of the CLI, and is kind of like a shell prompt with access to live session state. You can replace it with the output of any command.&lt;&#x2F;p&gt;
&lt;p&gt;When you wire one up, Claude Code sends your command a JSON blob on stdin with fields like &lt;code&gt;model&lt;&#x2F;code&gt;, &lt;code&gt;workspace&lt;&#x2F;code&gt;, &lt;code&gt;cost&lt;&#x2F;code&gt;, &lt;code&gt;output_style&lt;&#x2F;code&gt;, the context window’s &lt;code&gt;remaining_percentage&lt;&#x2F;code&gt;, and &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;statusline#available-data&quot;&gt;a handful of others&lt;&#x2F;a&gt;. Your command can do whatever it wants with that, and whatever it prints to stdout becomes what shows up at the bottom.&lt;&#x2F;p&gt;
&lt;p&gt;I gave Moira a status line that frames the context window as the show progressing. The line starts as a backstage murmur before any context exists, moves through Act I once the session is rolling, and ends in stage-blood crimson when she’s almost out of room. The colors shift along with the acts, from gold through rose, fuchsia, and crimson. This is &lt;code&gt;~&#x2F;.claude&#x2F;statusline-moira.sh&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;div class=&quot;expandable-code&quot;&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;#!&#x2F;bin&#x2F;bash
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Moira Rose status line — dramatic act commentary based on context window usage.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Receives Claude Code session JSON on stdin. Prints one styled line.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;=$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;remaining&lt;&#x2F;span&gt;&lt;span&gt;=$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;jq -r &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.context_window.remaining_percentage &#x2F;&#x2F; empty&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;[ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-n &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;remaining&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span&gt;=$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;100 - &lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;remaining&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fi
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ESC&lt;&#x2F;span&gt;&lt;span&gt;=$&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\033&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}[0m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DIM&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}[2m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;GOLD&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}[38;5;220m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ROSE&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}[38;5;211m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;FUCHSIA&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}[38;5;199m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;CRIMSON&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}[38;5;160m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BLOOD&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}[1;38;5;88m&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;[ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-z &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DIM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}🥀 The understudy is still in makeup, bébé.&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;elif &lt;&#x2F;span&gt;&lt;span&gt;(( $(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; &amp;lt; 20&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bc -l&lt;&#x2F;span&gt;&lt;span&gt;) )); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;GOLD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}🌹 Act I&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DIM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;} — the stage is set, bébé.&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;elif &lt;&#x2F;span&gt;&lt;span&gt;(( $(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; &amp;lt; 40&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bc -l&lt;&#x2F;span&gt;&lt;span&gt;) )); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;GOLD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}🌹 The plot&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DIM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}, it thickens.&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;elif &lt;&#x2F;span&gt;&lt;span&gt;(( $(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; &amp;lt; 60&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bc -l&lt;&#x2F;span&gt;&lt;span&gt;) )); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ROSE&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}🌹 Act II&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DIM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;} — tensions mount.&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;elif &lt;&#x2F;span&gt;&lt;span&gt;(( $(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; &amp;lt; 80&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bc -l&lt;&#x2F;span&gt;&lt;span&gt;) )); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;FUCHSIA&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}🌹 Act III approaches.&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DIM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;} Compose yourself.&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;elif &lt;&#x2F;span&gt;&lt;span&gt;(( $(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; &amp;lt; 95&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bc -l&lt;&#x2F;span&gt;&lt;span&gt;) )); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;CRIMSON&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}🥀 The denouement, bébé.&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BLOOD&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}🩸 The CURTAIN descends!&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DIM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;} Save your work, darling.&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fi
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;[ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;-n &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;then
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;printf &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;%s &lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DIM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}(%s%% staged)&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RESET&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;used&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;printf &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;line&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fi
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;

&lt;&#x2F;div&gt;
&lt;p&gt;The branch on an empty &lt;code&gt;used&lt;&#x2F;code&gt; handles the case where the session has just started and there isn’t a &lt;code&gt;remaining_percentage&lt;&#x2F;code&gt; field on stdin yet. Instead of a defaulted-to-zero number that reads weirdly, the line shows &lt;code&gt;🥀 The understudy is still in makeup, bébé.&lt;&#x2F;code&gt; until the show actually starts.&lt;&#x2F;p&gt;
&lt;p&gt;And &lt;code&gt;settings.json&lt;&#x2F;code&gt; just points at the script:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;statusLine&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;~&#x2F;.claude&#x2F;statusline-moira.sh&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once the curtain goes up, the bottom of the screen shows &lt;code&gt;🌹 Act I — the stage is set, bébé. (3% staged)&lt;&#x2F;code&gt; and progresses through the acts as the conversation grows. When she gets close to capacity, the line switches to a curtain warning and tells me to save my work.&lt;&#x2F;p&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-(--admonition-bg)&quot;
  style=&quot;--admonition-bg: var(--admonition-tip);&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-(--admonition-bg) p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(icons&#x2F;tip.svg);&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;When this is actually useful&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;Passively surfacing things you want quick visibility into, like the current Kubernetes context, AWS profile, which environment a repo is pointed at, or session cost so far. The stdin JSON gives you fields like &lt;code&gt;model&lt;&#x2F;code&gt;, &lt;code&gt;cost.total_cost_usd&lt;&#x2F;code&gt;, and &lt;code&gt;workspace&lt;&#x2F;code&gt; alongside the context numbers, and the script can call out to anything else you’d like, whether that’s a quick &lt;code&gt;gh&lt;&#x2F;code&gt; query for open PRs, the latest commit message on main, or (my favorite I’ve seen) live rugby and soccer scores so you can keep half an eye on the match while you work.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;off-scene-company-announcements-git-attribution-and-the-curtain-call&quot;&gt;Off scene: company announcements, git attribution, and the curtain call&lt;&#x2F;h2&gt;
&lt;p&gt;A few of the touchpoints don’t show up in the main CLI scroll, but they bookend the experience.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Company announcements&lt;&#x2F;strong&gt; are extra lines printed in the welcome box on launch, via &lt;code&gt;companyAnnouncements&lt;&#x2F;code&gt; in &lt;code&gt;settings.json&lt;&#x2F;code&gt;. It’s an array of strings, each rendered on its own line in that box (under text saying &lt;em&gt;Message from &amp;lt;account name&amp;gt;’s Organization:&lt;&#x2F;em&gt;):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;companyAnnouncements&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;🥀 Welcome, bébé. The stage is set. Moira Rose is at your service.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which renders as a welcome banner on every launch:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;moira-announcement.png&quot; alt=&quot;Claude Code launch screen showing the version header (Claude Code v2.1.119, Opus 4.7, Claude Pro), followed by the announcement line “🥀 Welcome, bébé. The stage is set. Moira Rose is at your service.” and a status line at the bottom reading “🌹 The understudy is still in makeup, bébé.”&quot; &#x2F;&gt;&lt;&#x2F;p&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-(--admonition-bg)&quot;
  style=&quot;--admonition-bg: var(--admonition-tip);&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-(--admonition-bg) p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(icons&#x2F;tip.svg);&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;When this is actually useful&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;This is mostly meant for org admins to push notices through managed settings — code freezes, version updates, that kind of thing — but it works in a personal config too, and is a nice place for note-to-self info you want on every launch.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;&lt;strong&gt;Git attribution&lt;&#x2F;strong&gt; is the trailer Claude Code adds to commits and PR descriptions when it makes them. The &lt;code&gt;attribution&lt;&#x2F;code&gt; key overrides both:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;attribution&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;🌹 Composed in collaboration with Moira Rose, via Claude Code&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Co-Authored-By: Moira Rose &amp;lt;moira@roseapothecary.com&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;pr&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;🌹 A Moira Rose production. Generated with Claude Code.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Every commit Moira makes now ends with that line:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;costuming-claude-code&#x2F;moira-attribution.png&quot; alt=&quot;A git log entry showing a commit dated Mon May 11 07:01:35 2026 with the message “Add ‘Costuming Claude Code’ post.” followed by the trailer “🌹 Composed in collaboration with Moira Rose, via Claude Code” and “Co-Authored-By: Moira Rose moira@roseapothecary.com”&quot; &#x2F;&gt;&lt;&#x2F;p&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-(--admonition-bg)&quot;
  style=&quot;--admonition-bg: var(--admonition-tip);&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-(--admonition-bg) p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(icons&#x2F;tip.svg);&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;When this is actually useful&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;Tracking which commits had AI involvement, if your team or compliance process cares about that. You can also set both fields to &lt;code&gt;&quot;&quot;&lt;&#x2F;code&gt; to remove the trailer entirely.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;&lt;strong&gt;The curtain call&lt;&#x2F;strong&gt; is the one piece that crosses from “outer experience” into actual extensibility, but I wanted Moira to sign off audibly. &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;hooks-guide&quot;&gt;Hooks&lt;&#x2F;a&gt; are shell commands that fire on Claude Code lifecycle events like &lt;code&gt;SessionStart&lt;&#x2F;code&gt;, &lt;code&gt;SessionEnd&lt;&#x2F;code&gt;, &lt;code&gt;Stop&lt;&#x2F;code&gt;, &lt;code&gt;PreToolUse&lt;&#x2F;code&gt;, &lt;code&gt;PostToolUse&lt;&#x2F;code&gt;, and &lt;code&gt;Notification&lt;&#x2F;code&gt;. macOS happens to ship a text-to-speech voice literally named &lt;em&gt;Moira&lt;&#x2F;em&gt; (Irish English), so I added a &lt;code&gt;SessionEnd&lt;&#x2F;code&gt; hook that has my laptop say something out loud when I close the session:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;#!&#x2F;bin&#x2F;bash
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;LINES&lt;&#x2F;span&gt;&lt;span&gt;=(
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Curtain.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;And, scene.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;I love this journey for you.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Best wishes. Warmest regards.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;LINE&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;LINES[&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RANDOM &lt;&#x2F;span&gt;&lt;span&gt;% $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;LINES[@]}]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;say -v&lt;&#x2F;span&gt;&lt;span&gt; Moira &amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;LINE&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&#x2F;dev&#x2F;null || &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;say &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;LINE&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;; } &amp;amp;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Registered in &lt;code&gt;settings.json&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hooks&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;SessionEnd&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;    {
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hooks&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;        { &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;~&#x2F;.claude&#x2F;moira-curtain-call.sh&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;      ]
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  ]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt; at the end backgrounds &lt;code&gt;say&lt;&#x2F;code&gt; so the hook returns immediately and Claude Code finishes exiting, while she keeps talking after the process is gone.&lt;&#x2F;p&gt;


&lt;div
  class=&quot;my-4 flex flex-col rounded-lg bg-(--admonition-bg)&quot;
  style=&quot;--admonition-bg: var(--admonition-tip);&quot;
&gt;
  &lt;div class=&quot;flex items-center rounded-t-lg bg-(--admonition-bg) p-1&quot;&gt;
    &lt;div
      class=&quot;mx-2 h-4 w-4 text-[0px] [background:var(--url)_center_center_no-repeat] dark:invert&quot;
      style=&quot;--url: url(icons&#x2F;tip.svg);&quot;
    &gt;
      tip
    &lt;&#x2F;div&gt;
    &lt;span&gt;&lt;strong&gt;When this is actually useful&lt;&#x2F;strong&gt;&lt;&#x2F;span&gt;
  &lt;&#x2F;div&gt;
  &lt;div class=&quot;pl-4&quot;&gt;&lt;p&gt;Hooks are kind of the general-purpose way to inject determinism into an otherwise nondeterministic AI process. &lt;code&gt;PreToolUse&lt;&#x2F;code&gt; lets you inspect or block a command before it runs (think “no &lt;code&gt;rm -rf&lt;&#x2F;code&gt; outside the project dir”), &lt;code&gt;PostToolUse&lt;&#x2F;code&gt; is great for logging shell commands to an audit file, &lt;code&gt;Stop&lt;&#x2F;code&gt; can ping you when a long turn finishes, and &lt;code&gt;Notification&lt;&#x2F;code&gt; can ping you when Claude is stuck waiting on input.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;(Also, since we’re on the subject of hooks, the thing I’ll keep saying: prompts aren’t guardrails, hooks are. If there’s something Claude really shouldn’t do, a &lt;code&gt;PreToolUse&lt;&#x2F;code&gt; hook that actually blocks it is more reliable than asking nicely in your &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; and hoping the model holds the line.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;swapping-costumes&quot;&gt;Swapping costumes&lt;&#x2F;h2&gt;
&lt;p&gt;Once Moira was working, I started building the same setup for Worf, Bob Ross, and a few other characters. I kept one settings file per character on disk, and pointed &lt;code&gt;~&#x2F;.claude&#x2F;settings.json&lt;&#x2F;code&gt; at whichever one I wanted activated.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;~&#x2F;.claude&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── settings.json              → symlink to settings.moira.json
&lt;&#x2F;span&gt;&lt;span&gt;├── settings.default.json      # vanilla Claude Code (or just `{}`)
&lt;&#x2F;span&gt;&lt;span&gt;├── settings.moira.json        # full Moira costume
&lt;&#x2F;span&gt;&lt;span&gt;├── settings.worf.json         # full Worf costume
&lt;&#x2F;span&gt;&lt;span&gt;├── output-styles&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;│   ├── moira-rose.md
&lt;&#x2F;span&gt;&lt;span&gt;│   └── worf.md
&lt;&#x2F;span&gt;&lt;span&gt;├── statusline-moira.sh
&lt;&#x2F;span&gt;&lt;span&gt;├── statusline-worf.sh
&lt;&#x2F;span&gt;&lt;span&gt;└── themes&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    ├── moira.json
&lt;&#x2F;span&gt;&lt;span&gt;    └── worf.json
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The supporting files (scripts, output styles, themes) all sit on disk together, but only the active &lt;code&gt;settings.json&lt;&#x2F;code&gt; decides which ones get wired up. A small shell function in &lt;code&gt;.zshrc&lt;&#x2F;code&gt; makes the swap one command:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;costume&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;local &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;=$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;:-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;default}
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ln -sf ~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.claude&#x2F;settings.$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;.json &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.claude&#x2F;settings.json
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Costume: &lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now &lt;code&gt;costume moira&lt;&#x2F;code&gt; puts the whole getup on, &lt;code&gt;costume worf&lt;&#x2F;code&gt; swaps it for the Klingon, and &lt;code&gt;costume&lt;&#x2F;code&gt; on its own takes everything off and you’re back to plain Claude Code. The next session picks up whichever one was left dangling.&lt;&#x2F;p&gt;
&lt;p&gt;The thing I didn’t expect was how much of the charm comes from &lt;em&gt;forgetting&lt;&#x2F;em&gt; I’d put one on. I’d swap to Worf for fun, get pulled into something else, come back hours later to fire off a mundane question about token costs, and read something like:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A worthy question. A warrior must know the cost of every weapon he carries.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That’s the moment I’d remember oh right, I left Worf loaded. The surprise really is most of the fun.&lt;&#x2F;p&gt;
&lt;p&gt;None of this makes me a better engineer, but it does add some humor to living in Claude Code for hours of my life. Somewhere between the wine-colored prompts and my laptop murmuring &lt;em&gt;curtain&lt;&#x2F;em&gt; into the room when I close the session, the whole thing has started to feel like the right amount of theatre for a Tuesday afternoon.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Claude Code beyond prompting</title>
          <pubDate>Wed, 31 Dec 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/claude-code-beyond-prompting/</link>
          <guid>https://sdeaton.com/blog/claude-code-beyond-prompting/</guid>
          <description xml:base="https://sdeaton.com/blog/claude-code-beyond-prompting/">&lt;p&gt;In my first week writing documentation for Claude Code, I realized I’d been using a Swiss Army knife with only the main blade, unaware of what else it could do.&lt;&#x2F;p&gt;
&lt;p&gt;Before I joined Anthropic, I thought I was pretty good at Claude Code. I was using it to speed up my technical writing: generating diagrams I couldn’t make myself, catching inconsistencies quickly, and automating the most tedious parts of doc review. Putting our entire style guide into a &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; made it even better. It wasn’t perfect, but it removed whole classes of style-related mistakes and shifted every edit closer to “done.”&lt;&#x2F;p&gt;
&lt;p&gt;That felt like a complete workflow. I’d heard about things like subagents, skills, and hooks, but what I had was working well, so I never looked much deeper.&lt;&#x2F;p&gt;
&lt;p&gt;Then I started working on the docs for these features and discovered the other tools had been there the whole time, each better suited for certain kinds of work. I just hadn’t learned when to reach for them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Note: This is a personal mental model I’ve been building through hands-on use, not an official or exhaustive guide.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-i-started-and-where-you-might-be-too&quot;&gt;Where I started (and where you might be too)&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; file was my first big unlock. If you haven’t done this yet, start here: create a &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; file in your project root (or in a &lt;code&gt;.claude&#x2F;&lt;&#x2F;code&gt; directory) and put your universal writing guidelines there. It’s the highest-impact, lowest-effort improvement you can make.&lt;&#x2F;p&gt;
&lt;p&gt;At first, I put everything in it. This included documentation philosophy, voice and tone, grammar rules, formatting conventions, and notes about our publishing workflow. Because Claude reads this file automatically at the start of every conversation, every edit followed our conventions without repeated reminders.&lt;&#x2F;p&gt;
&lt;p&gt;That worked well. But as I iterated, I started adding highly specific, task-level instructions that were important but not relevant to every session. Since &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; loads in full at the start of every conversation, every extra byte counts toward your context window and token usage. It’s best suited to universal rules, not specialized knowledge you only need occasionally.&lt;&#x2F;p&gt;
&lt;p&gt;I also kept retyping the same prompts for repeated tasks, like full doc review checklists. The same instructions, over and over, because I hadn’t saved them anywhere.&lt;&#x2F;p&gt;
&lt;p&gt;These were minor annoyances, not blockers. But when I started documenting Claude Code’s features, I realized there were specific tools that fixed these friction points.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-else-claude-code-can-do&quot;&gt;What else Claude Code can do&lt;&#x2F;h2&gt;
&lt;p&gt;These are the tools you can reach for beyond prompting and &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt;, and the kinds of technical writing tasks each one is best suited for. You don’t need to adopt all of these at once. Start with &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt;, then add the others only when you feel friction.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;slash-commands-one-prompt-saved-forever&quot;&gt;Slash commands: One prompt, saved forever&lt;&#x2F;h3&gt;
&lt;p&gt;Claude Code ships with a lot of &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;slash-commands&quot;&gt;built-in slash commands&lt;&#x2F;a&gt; worth exploring, like &lt;code&gt;&#x2F;init&lt;&#x2F;code&gt; to analyze your codebase and set up your configuration or &lt;code&gt;&#x2F;rename&lt;&#x2F;code&gt; to clean up session names.&lt;&#x2F;p&gt;
&lt;p&gt;Beyond the built-ins, any prompt you type more than twice can be a custom command. They’re Markdown files you store in &lt;code&gt;.claude&#x2F;commands&#x2F;&lt;&#x2F;code&gt; and trigger within a Claude session.&lt;&#x2F;p&gt;
&lt;p&gt;Here are two light examples you might add to a technical writing toolkit:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;&#x2F;review&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; (&lt;code&gt;.claude&#x2F;commands&#x2F;review.md&lt;&#x2F;code&gt;), a full doc review checklist:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;markdown&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-markdown &quot;&gt;&lt;code class=&quot;language-markdown&quot; data-lang=&quot;markdown&quot;&gt;&lt;span&gt;Review this document for:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;1. Style guide compliance (check CLAUDE.md)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;2. Technical accuracy (do the code samples work?)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;3. Broken links and references
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;4. Missing context (would a new user understand this?)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;5. Structural issues (headers, flow, completeness)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Categorize issues as: blocking, should fix, nice to have.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;&#x2F;simplify&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; (&lt;code&gt;.claude&#x2F;commands&#x2F;simplify.md&lt;&#x2F;code&gt;), for when you’ve written something too dense:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;markdown&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-markdown &quot;&gt;&lt;code class=&quot;language-markdown&quot; data-lang=&quot;markdown&quot;&gt;&lt;span&gt;Simplify this content for a developer who&amp;#39;s new to the product.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;- Remove jargon or define it on first use
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;- Shorten sentences
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;- Cut anything not essential to the core concept
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;- Add an example if it would help
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can run &lt;code&gt;&#x2F;review&lt;&#x2F;code&gt; or &lt;code&gt;&#x2F;simplify&lt;&#x2F;code&gt; and Claude will run that prompt on the content you’ve been working on.&lt;&#x2F;p&gt;
&lt;p&gt;(&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;slash-commands&quot;&gt;Full slash commands documentation&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;skills-context-that-loads-only-when-you-need-it&quot;&gt;Skills: Context that loads only when you need it&lt;&#x2F;h3&gt;
&lt;p&gt;I was using &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; for information that wasn’t applicable to every conversation. &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; eats into your token budget from the start of every conversation, so it’s not efficient to include information Claude may or may not need. Skills solve this.&lt;&#x2F;p&gt;
&lt;p&gt;Skills give Claude specialized knowledge for specific types of work, like how to structure API docs versus tutorials or how to test code samples for different SDKs. They’re directories with instructions, templates, and reference files. At startup, Claude only loads skill names and descriptions. The full &lt;code&gt;SKILL.md&lt;&#x2F;code&gt; file only loads when Claude decides from the task to use a skill.&lt;&#x2F;p&gt;
&lt;p&gt;An API docs writing skill might look like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;.claude&#x2F;skills&#x2F;api-docs&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── SKILL.md           # Instructions for API doc structure
&lt;&#x2F;span&gt;&lt;span&gt;├── template.md        # Standard template with all sections
&lt;&#x2F;span&gt;&lt;span&gt;└── examples&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    ├── good-example.md    # &amp;quot;Here&amp;#39;s what we&amp;#39;re going for&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    └── common-mistakes.md # &amp;quot;Avoid these patterns&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;SKILL.md&lt;&#x2F;code&gt; file for this skill:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;markdown&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-markdown &quot;&gt;&lt;code class=&quot;language-markdown&quot; data-lang=&quot;markdown&quot;&gt;&lt;span style=&quot;background-color:#4f5b66;color:#c0c5ce;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;name: documenting-apis
&lt;&#x2F;span&gt;&lt;span&gt;description: Provides structure and examples for writing API reference documentation. Use when creating or editing API documentation.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;# API Documentation
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;## Structure
&lt;&#x2F;span&gt;&lt;span&gt;Every API doc needs: Overview, Authentication, Endpoints, Error codes, Examples
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;## Voice
&lt;&#x2F;span&gt;&lt;span&gt;More formal than tutorials. Assume reader knows HTTP basics.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;## Template
&lt;&#x2F;span&gt;&lt;span&gt;See &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;[template.md](template.md)&lt;&#x2F;span&gt;&lt;span&gt; for the standard structure. Don&amp;#39;t skip sections.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;## Examples
&lt;&#x2F;span&gt;&lt;span&gt;See &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;[examples&#x2F;good-example.md](examples&#x2F;good-example.md)&lt;&#x2F;span&gt;&lt;span&gt; for reference patterns.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The supporting files (templates, examples) only load when the task actually needs them. Claude discovers them through links in your &lt;code&gt;SKILL.md&lt;&#x2F;code&gt;, so you can keep detailed reference material available without it eating up context until it’s relevant.&lt;&#x2F;p&gt;
&lt;p&gt;Now your &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; can stay lean with only universal guidelines, while specialized context loads automatically when you need it.&lt;&#x2F;p&gt;
&lt;p&gt;(&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;skills&quot;&gt;Full skills documentation&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;A note on Skills vs MCP:&lt;&#x2F;strong&gt; I’ve heard people say that Skills make MCP servers less useful, but that’s not the case. They serve different purposes. &lt;strong&gt;MCP is the connection to your data&lt;&#x2F;strong&gt; (Slack, GitHub, Google Drive). &lt;strong&gt;Skills are the instructions for how to use that data.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;MCP server connects Claude to your GitHub issues&lt;&#x2F;li&gt;
&lt;li&gt;Skill says “When reviewing docs, check issues labeled ‘documentation’ for common user confusion”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can have MCP without skills (you just prompt every time), but skills paired with MCP are way more powerful. Skills turn “I have access to this data” into “here’s when and how to use it.”&lt;&#x2F;p&gt;
&lt;p&gt;(&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;mcp&quot;&gt;MCP documentation&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;subagents-parallel-work-without-losing-focus&quot;&gt;Subagents: Parallel work without losing focus&lt;&#x2F;h3&gt;
&lt;p&gt;Subagents are separate Claude sessions that run in the background with their own context. The main agent passes specific context to them, and they report back when done.&lt;&#x2F;p&gt;
&lt;p&gt;You can spawn them ad hoc for one-off tasks. But you can also save subagent configurations as Markdown files with YAML frontmatter in &lt;code&gt;.claude&#x2F;agents&#x2F;&lt;&#x2F;code&gt;. Once configured, they become reusable tools you invoke by name.&lt;&#x2F;p&gt;
&lt;p&gt;I frequently use saved subagents for end-of-doc review: tech review, UX review, style review. They run in isolated context, which means I can kick off multiple reviews in parallel without competing for the main conversation’s token budget.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s an example of a subagent that checks for technical accuracy.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Tip: you can use the &lt;code&gt;&#x2F;agents&lt;&#x2F;code&gt; slash command to help build these instead of doing it manually&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Technical accuracy reviewer&lt;&#x2F;strong&gt; (&lt;code&gt;.claude&#x2F;agents&#x2F;tech-reviewer.md&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;markdown&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-markdown &quot;&gt;&lt;code class=&quot;language-markdown&quot; data-lang=&quot;markdown&quot;&gt;&lt;span style=&quot;background-color:#4f5b66;color:#c0c5ce;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;name: tech-reviewer
&lt;&#x2F;span&gt;&lt;span&gt;description: Verifies documentation matches actual code behavior and internal sources of truth. Use when reviewing documentation for technical accuracy or when code has changed.
&lt;&#x2F;span&gt;&lt;span&gt;tools: [Read, Grep, Glob, Bash]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;You are a technical accuracy reviewer for documentation.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;When invoked:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;1. Read the documentation being reviewed
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;2. Find the corresponding source code in src&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;3. Check internal docs in &#x2F;docs&#x2F;internal&#x2F; for canonical behavior
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;4. Verify every claim in the doc matches actual implementation
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;For each section, report:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;- ✓ Accurate (code matches doc)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;- ⚠ Needs attention (minor discrepancy)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;- ✗ Incorrect (documentation contradicts code)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Include specific line numbers and suggest corrections.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When you’re ready for a review, kick this off by asking Claude to run it. You can keep working in the main thread as this and other subagents run in parallel (or you can take a coffee break), and when the subagent is done, you get a focused report back.&lt;&#x2F;p&gt;
&lt;p&gt;(&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;sub-agents&quot;&gt;Full subagents documentation&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h4 id=&quot;subagents-vs-skills-vs-slash-commands&quot;&gt;Subagents vs. skills vs. slash commands&lt;&#x2F;h4&gt;
&lt;p&gt;You might notice some overlap between these tools. Could the &lt;code&gt;&#x2F;review&lt;&#x2F;code&gt; slash command also be a subagent? Or a skill? Yes. In some cases, you might even want a slash command and a subagent that perform similar tasks. The difference is not what they do, but how they handle context and who triggers them.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Tool&lt;&#x2F;th&gt;&lt;th&gt;Context&#x2F;isolation&lt;&#x2F;th&gt;&lt;th&gt;Usually triggered by&lt;&#x2F;th&gt;&lt;th&gt;When to use it&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Slash command&lt;&#x2F;strong&gt; (&lt;code&gt;&#x2F;review&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;Full main conversation context&lt;&#x2F;td&gt;&lt;td&gt;You (explicit invocation: &lt;code&gt;&#x2F;review&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;When you want Claude to consider the entire discussion and current project state.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Subagent&lt;&#x2F;strong&gt; (&lt;code&gt;review-agent&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;Isolated context (only what you&#x2F;agent pass to it)&lt;&#x2F;td&gt;&lt;td&gt;You (tell Claude to run it)&lt;&#x2F;td&gt;&lt;td&gt;When you want to run tasks in parallel or keep your main conversation focused.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Skill&lt;&#x2F;strong&gt; (&lt;code&gt;review-skill&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;Loads specialized instructions on demand&lt;&#x2F;td&gt;&lt;td&gt;Claude (infers from task context)&lt;&#x2F;td&gt;&lt;td&gt;When you need structure, templates, or reference material without keeping it in context all the time.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;em&gt;Note: The triggering part can be more nuanced in practice. Claude can invoke custom slash commands, and you can explicitly ask Claude to use a specific skill. The table above covers the most common usage.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can use more than one of these for the same kind of task. A slash command is useful for quick, context-aware checks. A subagent is better for parallel or background work. A skill is the right choice when the work needs deeper, reusable guidance without bloating your main context.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;hooks-automation-that-runs-at-specific-events&quot;&gt;Hooks: Automation that runs at specific events&lt;&#x2F;h3&gt;
&lt;p&gt;Hooks are scripts that run automatically at specific Claude Code lifecycle events. Before a tool runs, after Claude sends a message, when a session stops. You set them up once and then forget about them.&lt;&#x2F;p&gt;
&lt;p&gt;A useful one for technical writing: run your linter before any edit saves.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hooks&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;PreToolUse&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [{
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;matcher&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Edit|Write&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;vale --config=.vale.ini $FILE&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }]
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now whenever Claude edits your work, linting runs automatically. You catch issues immediately instead of in code review.&lt;&#x2F;p&gt;
&lt;p&gt;Hooks make checks deterministic. No relying on Claude to remember, no hoping it picks up the pattern. It just happens every time.&lt;&#x2F;p&gt;
&lt;p&gt;Another useful one: a sound notification when Claude finishes a message. I tend to context switch while waiting for responses, and this tells me when to come back without constantly checking.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hooks&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Stop&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [{
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;matcher&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;assistant&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;afplay &#x2F;System&#x2F;Library&#x2F;Sounds&#x2F;Glass.aiff&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }]
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can hook into a lot of events: session start, before context compaction, after Claude sends a message, on session stop. Anywhere you’d want to inject a check or action, there’s probably a hook point for it.&lt;&#x2F;p&gt;
&lt;p&gt;(&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;hooks&quot;&gt;Full hooks documentation&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;plugins-discover-and-share-workflows&quot;&gt;Plugins: Discover and share workflows&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;plugins&quot;&gt;Plugins&lt;&#x2F;a&gt; work in two directions: you can benefit from what others have already built, and you can share your own workflows with others.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Using plugins others have built:&lt;&#x2F;strong&gt; To browse and install prebuilt plugins, run &lt;code&gt;&#x2F;plugin&lt;&#x2F;code&gt;. The default marketplace from Anthropic is automatically included, but you can &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;discover-plugins&quot;&gt;add other community marketplaces&lt;&#x2F;a&gt; if you want to explore more. One plugin I use all the time is &lt;code&gt;commit-commands&lt;&#x2F;code&gt; from the Anthropic marketplace. It auto-drafts commit messages based on my changes. Way more useful than “update skills docs” but also not something I want to spend brainpower on.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Sharing your own:&lt;&#x2F;strong&gt; Once you’ve built up a collection of slash commands, skills, subagents, and hooks, you can package them as a plugin and share it via &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;plugin-marketplaces&quot;&gt;marketplaces&lt;&#x2F;a&gt;. Instead of every person figuring out the same workflows from scratch, you can hand them a plugin with everything already configured.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;A quick note on sharing: if you store your commands, skills, and agents in your project’s &lt;code&gt;.claude&#x2F;&lt;&#x2F;code&gt; folder and commit them to git, your team automatically gets them when they clone the repo. That works great for project-specific setups without needing a full plugin.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;(&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;plugins&quot;&gt;Full plugins documentation&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-these-pieces-fit-together&quot;&gt;How these pieces fit together&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s how I’m starting to think about it:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Tool&lt;&#x2F;th&gt;&lt;th&gt;What it’s for&lt;&#x2F;th&gt;&lt;th&gt;When to use it&lt;&#x2F;th&gt;&lt;th&gt;Example&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Universal context&lt;&#x2F;td&gt;&lt;td&gt;Guidelines that apply to every task&lt;&#x2F;td&gt;&lt;td&gt;Voice, formatting rules&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Slash commands&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Repeatable prompts&lt;&#x2F;td&gt;&lt;td&gt;Any prompt you use more than twice&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;&#x2F;review&lt;&#x2F;code&gt;, &lt;code&gt;&#x2F;simplify&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Skills&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Specialized instructions&lt;&#x2F;td&gt;&lt;td&gt;How to approach specific doc types&lt;&#x2F;td&gt;&lt;td&gt;API docs skill, tutorial skill&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Subagents&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Parallel work&lt;&#x2F;td&gt;&lt;td&gt;Reviews that can run in the background&lt;&#x2F;td&gt;&lt;td&gt;Tech reviewer, code tester&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Hooks&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Automatic guardrails&lt;&#x2F;td&gt;&lt;td&gt;Checks you always want to run&lt;&#x2F;td&gt;&lt;td&gt;Linter, sound notification&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The goal isn’t to use all of these. It’s to know they exist so you can reach for them when they’d help.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-to-start&quot;&gt;Where to start&lt;&#x2F;h2&gt;
&lt;p&gt;If you’re not using Claude Code yet:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Install it by following the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;code.claude.com&#x2F;docs&#x2F;en&#x2F;quickstart&quot;&gt;quickstart guide&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Navigate to a docs repo and open Claude Code: &lt;code&gt;cd your-docs &amp;amp;&amp;amp; claude&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Start chatting.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If you’re already using it but mainly just prompting:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Run &lt;code&gt;&#x2F;init&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: analyzes your codebase and sets up &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt;, slash commands, and subagents based on what would be useful for your project.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Add universal guidelines to &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: voice, formatting rules, repo structure.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Create one slash command&lt;&#x2F;strong&gt; for your most common task (maybe &lt;code&gt;&#x2F;review&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Spawn a subagent&lt;&#x2F;strong&gt; for parallel reviews. When you repeat the same instructions, save it as a configuration.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;You don’t need to set up everything at once. Add tools when you notice friction.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;What’s stuck with me most is how each tool solves a different problem around context, isolation, and automation. &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; gives you persistent context. Slash commands give you repeatable prompts. Skills give you on-demand specialization. Subagents give you parallelism. Hooks give you guarantees.&lt;&#x2F;p&gt;
&lt;p&gt;I’m still learning which ones fit which workflows. But having them available means I can shape how I work instead of working around the limitations of a single interface.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Code execution with MCP: separating context from computation</title>
          <pubDate>Fri, 14 Nov 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/code-execution-with-mcp/</link>
          <guid>https://sdeaton.com/blog/code-execution-with-mcp/</guid>
          <description xml:base="https://sdeaton.com/blog/code-execution-with-mcp/">&lt;p&gt;&lt;strong&gt;TL;DR:&lt;&#x2F;strong&gt; Traditional tool calling eats away at an agent’s context window. All the tool definitions and every intermediate result pass through it. Code execution gives agents a “scratch pad” workspace to process data outside the context window. Combined with MCP’s tool abstractions, this can significantly reduce token usage for data-heavy tasks.&lt;&#x2F;p&gt;
&lt;p&gt;Check out the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sarahcstringer&#x2F;mcp-code-demo&quot;&gt;demo repository&lt;&#x2F;a&gt; for two examples you can run.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I thought I understood MCP. &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;deatons.substack.com&#x2F;p&#x2F;learn-mcp-with-me-part-1-what-is&quot;&gt;It took me a while&lt;&#x2F;a&gt;, but I recognized that we need MCP as a standardized layer to best interact with external services. MCP gives LLMs structured access to APIs, providing a consistent interface instead of varying parameter names and request&#x2F;response formats across different services.&lt;&#x2F;p&gt;
&lt;p&gt;Then I read Anthropic’s post about &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.anthropic.com&#x2F;engineering&#x2F;code-execution-with-mcp&quot;&gt;code execution with MCP&lt;&#x2F;a&gt; and Cloudflare’s piece on &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;code-mode&#x2F;&quot;&gt;Code Mode&lt;&#x2F;a&gt;, which talk about having LLMs write code to call MCP servers, and I got confused again.&lt;&#x2F;p&gt;
&lt;p&gt;Why are we having LLMs write code around MCP servers when we wrote MCP servers so that… LLMs &lt;strong&gt;didn’t have to write code&lt;&#x2F;strong&gt; to interact with APIs?&lt;&#x2F;p&gt;
&lt;p&gt;The piece that finally clicked: a key benefit of code execution is that it happens in an execution environment, not in the agent’s context window.&lt;&#x2F;p&gt;
&lt;p&gt;MCP is still the standardization layer; generating Python wrappers for MCP tools makes them available as importable functions in the execution environment. Then, if the LLM has access to a bash tool that executes commands, it can write and execute entire scripts in its execution environment and only send the final answer back to context.&lt;&#x2F;p&gt;
&lt;p&gt;As an analogy: traditional tool calling requires you to show every single calculation on your final exam paper: all the messy scratch work, the raw data, the transformed versions, crossed-out mistakes. You run out of space fast, and the grader (the LLM) has to wade through all that noise to find the final answer. MCP + code execution is like giving you scratch paper to process data and perform operations outside the context window. Then you just record the final result on your exam paper.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;context-window-vs-execution-environment&quot;&gt;Context window vs. execution environment&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;strong&gt;context window&lt;&#x2F;strong&gt; is the LLM’s working memory. It’s expensive (costs tokens), limited in size, and includes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;System prompt&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Conversation history&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Tool descriptions&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Tool results&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The response being generated&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The &lt;strong&gt;execution environment&lt;&#x2F;strong&gt; is where code actually runs. In development, it might be your laptop. In production, it’s typically a sandboxed Docker container or VM with limited filesystem and network access. It’s relatively cheap and can handle large data processing.&lt;&#x2F;p&gt;
&lt;p&gt;Modern agent frameworks like &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.anthropic.com&#x2F;engineering&#x2F;code-execution-with-mcp&quot;&gt;Claude Code&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;code-mode&#x2F;&quot;&gt;Cloudflare Agents&lt;&#x2F;a&gt; provide code execution environments built-in, but you can also achieve the same pattern by giving any LLM a bash tool that executes commands in your execution environment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem-your-context-window-is-getting-bloated&quot;&gt;The problem: your context window is getting bloated&lt;&#x2F;h2&gt;
&lt;p&gt;Tool calling is great for letting LLMs use APIs and external tools, but once you have a production system with large datasets and hundreds of tools, it quickly runs into a major problem: &lt;strong&gt;all the tool definitions and every intermediate result from every tool call has to pass through your context window.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to fetch 300 records, transform the data, and filter it with tools, all of the following must pass through context:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The tool definitions&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The raw data from the records&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The transformed data&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The filtered data&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each step consumes more of your expensive, limited window until you hit the limit or face substantial cost increases. Your agent also has to sort through all the results and its working memory is crowded with information that isn’t actually relevant for its final response.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution-code-execution-in-an-execution-environment&quot;&gt;The solution: code execution in an execution environment&lt;&#x2F;h2&gt;
&lt;p&gt;Code execution gives agents a “scratch pad” workspace to process data outside the context window. It allows the agent to write code that calls MCP tools, process results locally in the execution environment, and only record the final result in context.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;code-execution-with-mcp&#x2F;d8dcb57a-f5e7-42f6-899d-3497b53c93b6.png&quot; alt=&quot;Comparison diagram showing traditional tool calling with all operations in the context window versus code execution with a separate execution environment for processing&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;MCP still handles the “How do I call this API correctly?” problem by giving LLMs structured tools instead of making them write raw API calls that vary from API to API.&lt;&#x2F;p&gt;
&lt;p&gt;Code execution solves the “How do I process all this data without destroying my context window?” problem by giving the agent a workspace to process data outside the context window.&lt;&#x2F;p&gt;
&lt;p&gt;Beyond data processing, code execution unlocks capabilities that are challenging with traditional tool calling. Things like polling for results and waiting, retrying failed requests, maintaining state across multiple operations, saving intermediate results and returning back to them later, all become possible through code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;production-safety-guardrails-for-code-execution&quot;&gt;Production safety: guardrails for code execution&lt;&#x2F;h2&gt;
&lt;p&gt;Giving an LLM the ability to execute code is powerful, but requires guardrails. Three key practices:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Sandboxed execution:&lt;&#x2F;strong&gt; Run code in isolated Docker containers or VMs, not your production environment. Limit filesystem and network access or use something like the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;sandbox&#x2F;&quot;&gt;Cloudflare Sandbox SDK&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.daytona.io&#x2F;&quot;&gt;Daytona&lt;&#x2F;a&gt; for runtime isolation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2. Human approval for destructive actions:&lt;&#x2F;strong&gt; Require approval for anything that costs money, modifies data, or communicates externally. For example, &lt;code&gt;read_channel_messages&lt;&#x2F;code&gt; might auto-approve, but &lt;code&gt;post_message_to_all_hands&lt;&#x2F;code&gt; should require review.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;3. Thoughtful tool design:&lt;&#x2F;strong&gt; Don’t expose raw endpoints. Build high-level tools like &lt;code&gt;schedule_meeting&lt;&#x2F;code&gt; (checks availability + books room) instead of &lt;code&gt;list_users&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;list_events&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;create_event&lt;&#x2F;code&gt;. Prefer search-focused results (&lt;code&gt;search_contacts(”Jane”)&lt;&#x2F;code&gt;) over giant dumps (&lt;code&gt;list_all_contacts()&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.anthropic.com&#x2F;engineering&#x2F;writing-tools-for-agents&quot;&gt;Anthropic article on writing tools for agents&lt;&#x2F;a&gt; covers these design principles in depth.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;when-to-use-each-approach&quot;&gt;When to use each approach&lt;&#x2F;h2&gt;
&lt;p&gt;Not every task needs code execution. Traditional tool calling works well for certain scenarios:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Traditional tool calling works when:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Tool results are small (a few hundred tokens)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;You need one or two tool calls total&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;No data processing required&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Results go directly to the user&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Code execution excels when:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Processing large datasets&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Making multiple related tool calls&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Filtering or transforming results&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Polling or waiting for completion&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Maintaining state across operations&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;limitations-and-trade-offs&quot;&gt;Limitations and trade-offs&lt;&#x2F;h2&gt;
&lt;p&gt;Code execution has some trade-offs (and at least to me, this is such a new concept that there are a lot of things I’m haven’t thought through yet):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Model capability matters:&lt;&#x2F;strong&gt; Code execution requires models with strong coding abilities. Not all LLMs write clean, correct code consistently. If your model struggles with code generation, the benefits diminish quickly. Test your chosen model’s coding capabilities before committing to this pattern.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Non-deterministic code:&lt;&#x2F;strong&gt; Traditional tool calling shows you which tools are called, with what arguments, and what they return. Code execution is opaque; the LLM generates code dynamically, and that code can be different each time for the same prompt. You don’t know what it will write until it writes it, and once it’s executing in the environment, you lose visibility into the step-by-step operations.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Security surface area:&lt;&#x2F;strong&gt; Executing LLM-generated code introduces risks beyond traditional tool calling. Even with sandboxing, you need to worry about resource exhaustion, unintended file system access, infinite loops, and malicious prompt injections that generate harmful code. The guardrails mentioned earlier are necessary but not sufficient; you need ongoing monitoring and careful tool design.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Complexity:&lt;&#x2F;strong&gt; Your system needs to manage an execution environment, handle code execution errors gracefully, and potentially deal with environment setup and dependencies. Traditional tool calling has a simpler operational model.&lt;&#x2F;p&gt;
&lt;p&gt;Despite these trade-offs, for data-heavy tasks where token costs and context window limits are real constraints, code execution can provide significant practical benefits.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-mental-model-shift&quot;&gt;The mental model shift&lt;&#x2F;h2&gt;
&lt;p&gt;Agents are more than LLMs with tools. They’re LLMs with tools and an execution environment.&lt;&#x2F;p&gt;
&lt;p&gt;The context window is for reasoning. The execution environment is for working.&lt;&#x2F;p&gt;
&lt;p&gt;When you structure agents this way, you get:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Significant reduction in token usage (varies by task, but can be substantial for data-heavy operations)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Natural patterns for polling and async operations&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Ability to process large datasets&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Stateful computation without context bloat&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;More predictable results by executing deterministic code rather than tracking state in context&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Code execution can improve reliability by reducing the need for the LLM to track state and aggregate data across multiple tool calls in context. By having the agent write deterministic scripts that run in the execution environment, you get more predictable and complete results.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; The effectiveness of this approach depends significantly on the model you use. Models with strong coding capabilities will write better, more efficient code. The token savings also vary based on your specific use case—tasks involving large datasets or many sequential operations see the most benefit.&lt;&#x2F;p&gt;
&lt;p&gt;MCP provides the standardized tools. Code execution provides the workspace to use them efficiently. Together, they enable agents that are both capable and cost-effective.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;try-it-yourself&quot;&gt;Try it yourself&lt;&#x2F;h2&gt;
&lt;p&gt;I created a &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sarahcstringer&#x2F;mcp-code-demo&quot;&gt;demo repository&lt;&#x2F;a&gt; with two examples you can run and play around with:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Traditional tool calling&lt;&#x2F;strong&gt;: Shows how intermediate data fills the context window&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code execution&lt;&#x2F;strong&gt;: Shows how processing happens in the execution environment&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Both examples include token usage metrics so you can compare the approaches.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note on token variability:&lt;&#x2F;strong&gt; Token usage varies between runs, even with the same task and model. In my testing with this demo, I’ve seen the following results:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Traditional tool calling&lt;&#x2F;strong&gt;: Typically 50,000-73,000 tokens&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code execution&lt;&#x2F;strong&gt;: Typically 9,500-10,000 tokens&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Token reduction&lt;&#x2F;strong&gt;: 80-87% depending on the run&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To run the examples:&lt;&#x2F;p&gt;
&lt;p&gt;`# Clone this repo
git clone https:&#x2F;&#x2F;github.com&#x2F;sarahcstringer&#x2F;mcp-code-demo
cd mcp-code-demo&lt;&#x2F;p&gt;
&lt;h1 id=&quot;install-dependencies&quot;&gt;Install dependencies&lt;&#x2F;h1&gt;
&lt;p&gt;pip install -r requirements.txt&lt;&#x2F;p&gt;
&lt;h1 id=&quot;set-up-your-anthropic-api-key&quot;&gt;Set up your Anthropic API key&lt;&#x2F;h1&gt;
&lt;p&gt;cp .env.example .env&lt;&#x2F;p&gt;
&lt;h1 id=&quot;edit-env-and-add-your-anthropic-api-key&quot;&gt;Edit .env and add your ANTHROPIC_API_KEY&lt;&#x2F;h1&gt;
&lt;h1 id=&quot;run-the-traditional-tool-calling-example&quot;&gt;Run the traditional tool calling example&lt;&#x2F;h1&gt;
&lt;p&gt;python examples&#x2F;traditional_tool_calling.py&lt;&#x2F;p&gt;
&lt;h1 id=&quot;generate-mcp-tool-wrappers-for-the-code-execution-example&quot;&gt;Generate MCP tool wrappers for the code execution example&lt;&#x2F;h1&gt;
&lt;p&gt;python generate_wrappers.py&lt;&#x2F;p&gt;
&lt;h1 id=&quot;run-the-code-execution-example&quot;&gt;Run the code execution example&lt;&#x2F;h1&gt;
&lt;p&gt;python examples&#x2F;code_execution.py`The examples use Anthropic’s Claude Haiku 4 model with the Anthropic SDK, but the patterns work with any model provider.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.anthropic.com&#x2F;engineering&#x2F;code-execution-with-mcp&quot;&gt;Read the Anthropic post on code execution&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.anthropic.com&#x2F;engineering&#x2F;writing-tools-for-agents&quot;&gt;Learn best practices for writing tools for agents&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.cloudflare.com&#x2F;code-mode&#x2F;&quot;&gt;Check out Cloudflare’s Code Mode explanation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;modelcontextprotocol.io&#x2F;&quot;&gt;Learn about MCP&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>On agency, abstraction, and AI-assisted coding</title>
          <pubDate>Tue, 04 Nov 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/on-agency-abstraction-ai-coding/</link>
          <guid>https://sdeaton.com/blog/on-agency-abstraction-ai-coding/</guid>
          <description xml:base="https://sdeaton.com/blog/on-agency-abstraction-ai-coding/">&lt;p&gt;I saw a screenshot of this email about AI-assisted coding and agency, and it sparked a lot of thoughts for me.&lt;&#x2F;p&gt;
&lt;p&gt;The core argument was that AI-assisted coding trades long-term mastery for short-term outcomes, and that represents a loss of personal agency. Agency, in this framing, means having the courage to struggle through problems on your own, stretch beyond your current abilities, and build the kind of deep understanding that only comes from doing the hard work yourself.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;on-agency-abstraction-ai-coding&#x2F;ccdea58e-6b15-49d6-bcb4-390fcc2f84ab.jpeg&quot; alt=&quot;Screenshot of an email arguing that AI-assisted coding trades long-term mastery for short-term outcomes&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It’s a thoughtful argument, but I don’t experience it that way and wanted to share my perspective.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-abstraction-layer-argument&quot;&gt;The abstraction layer argument&lt;&#x2F;h3&gt;
&lt;p&gt;When I see arguments that using AI means you don’t actually know what you’re doing or haven’t built something yourself, I wonder how it maps to previous advances in tooling and technology. To me, AI coding tools feel like another abstraction layer. Just like I don’t need to understand exactly how the file system retrieves data from disk to write meaningful programs, I don’t need to hand-craft every line of code to build something real and useful.&lt;&#x2F;p&gt;
&lt;p&gt;When higher-level languages like Python became popular, some low-level programmers dismissed them as “not real programming” because they abstracted away too much control. When React rose in front-end development, other developers complained that newcomers no longer understood the DOM or how browsers actually work, but those new developers were still building interesting, useful products. The waves of abstraction spark similar anxieties that hiding complexity will erode understanding and craftsmanship.&lt;&#x2F;p&gt;
&lt;p&gt;Even if some people feel that way, many of us recognize that higher-level languages didn’t eliminate the need for problem-solving or understanding. Instead, they shifted where we spend our cognitive energy and allowed us to do more by building on top of the existing technology.&lt;&#x2F;p&gt;
&lt;p&gt;I understand that calling AI “an abstraction layer” might be a bit of a stretch. But I still think of it similarly. With AI assistance, I’m learning to orchestrate multiple tools, debug unexpected behavior, and think through architecture in ways I couldn’t before. I’m learning about different frameworks I hadn’t previously explored. The thinking hasn’t disappeared, as far as I’m concerned; it’s just operating at a different level. And, I’m actually able to ship the things that used to live only in my head.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;where-agency-actually-lives&quot;&gt;Where agency actually lives&lt;&#x2F;h3&gt;
&lt;p&gt;One of my friends won’t use AI to draft Slack messages because he wants his words to genuinely come from him. It’s important to him that his reasoning process be fully his own.&lt;&#x2F;p&gt;
&lt;p&gt;I get that, but also… not. When I use AI to help articulate an idea, the thinking is still mine. The struggle to figure out what I’m trying to say, the iteration on tone and clarity, the decision about what to include or cut still feels like me.&lt;&#x2F;p&gt;
&lt;p&gt;Beyond that, I’m actually thinking more, not less. Ideas that would have stayed trapped in my head, half-formed and unexplored, now make it out into the world. And once they’re out, I keep iterating on them. I keep expanding, refining, and connecting them to other ideas, and connecting with more people with diverging thoughts. AI is removing the friction that used to stop me from creating things in the first place.&lt;&#x2F;p&gt;
&lt;p&gt;That sense of magic David Beazley describes, of “commanding complexified sand into performing stunts,” hasn’t diminished. If anything, it’s intensified.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;different-tools-for-different-people&quot;&gt;Different tools for different people&lt;&#x2F;h3&gt;
&lt;p&gt;I don’t expect to convince anyone who experiences AI assistance as a loss of agency. Different people relate to their craft differently, and that’s completely legitimate.&lt;&#x2F;p&gt;
&lt;p&gt;One thing does bother me about this broader narrative around ownership and mastery, though. I don’t think this is what the author intended, but I dislike the framing that tells people they don’t actually own what they feel they’ve built. When someone uses AI tools to create something they’re genuinely proud of—something they designed, debugged, and shipped—telling them “that doesn’t count” or “you didn’t really build that” feels unnecessarily dismissive. It’s a kind of gatekeeping that decides in advance what “real” mastery looks like and who gets to claim ownership over their work.&lt;&#x2F;p&gt;
&lt;p&gt;I want folks to know that using AI tools doesn’t mean I’m not thinking, not learning, not creating, or not growing. It means I’ve found a set of tools that let me finally build things I’ve been dreaming about for years, and the learning curve is in figuring out &lt;em&gt;how to work with these tools effectively&lt;&#x2F;em&gt;, which is its own kind of mastery.&lt;&#x2F;p&gt;
&lt;p&gt;Technology has always been about layering abstractions. Some people prefer working closer to the metal, and others embrace new layers that expand on that. Both approaches have value. Both require agency, courage, and continuous learning.&lt;&#x2F;p&gt;
&lt;p&gt;I don’t feel that mastery disappears when tools evolve, it just evolves with them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;What’s your experience? Do you find AI tools expand or constrain your sense of agency as a developer?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Testing docs IA with AI agents</title>
          <pubDate>Mon, 20 Oct 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/testing-docs-ia-with-ai-agents/</link>
          <guid>https://sdeaton.com/blog/testing-docs-ia-with-ai-agents/</guid>
          <description xml:base="https://sdeaton.com/blog/testing-docs-ia-with-ai-agents/">&lt;p&gt;A few days ago, I posted about &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;deatons.substack.com&#x2F;p&#x2F;can-ai-help-test-ia-a-wikipedia-game&quot;&gt;using the Claude Agent SDK to play the Wikipedia game&lt;&#x2F;a&gt; as a way to think about docs navigation using an LLM.&lt;&#x2F;p&gt;
&lt;p&gt;This weekend I worked on an agent that would navigate my actual docs site with real user questions, and immediately started getting value out of it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sarahcstringer&#x2F;docs-persona-navigator&quot;&gt;Link to the GitHub repository if you want to try it out.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;testing-docs-ia-with-ai-agents&#x2F;8a106a9e-23be-4c18-91f5-e7dfeebda3dc.png&quot; alt=&quot;Terminal output showing the docs persona navigator agent analyzing user questions and navigating documentation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here’s what I built, what it found, and why I’m excited about it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-unexpected-gift-of-ai-assistants&quot;&gt;The unexpected gift of AI assistants&lt;&#x2F;h2&gt;
&lt;p&gt;Before we had an AI Assistant on our docs site, understanding what people needed was mostly inference. I looked at website analytics that said little about the user experience, combed through support tickets to see what hurt enough to escalate, and crossed my fingers that users would submit feedback if they felt strongly enough about the docs.&lt;&#x2F;p&gt;
&lt;p&gt;Now users just tell us in their own words what they’re trying to do. Each conversation is a little case file: the goal, the vocabulary they use, what they already know or don’t, where they get stuck, and what they assume exists.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h3 id=&quot;a-request-for-teams-building-ai-docs-assistants&quot;&gt;A request for teams building AI docs assistants&lt;&#x2F;h3&gt;
&lt;p&gt;If you’re implementing an AI assistant for a docs site, please include conversation IDs, user or session IDs, and timestamps in your exports. Most logs are still unthreaded Q&amp;amp;A pairs. Even minimal session data would help see things like “they asked about authentication three times before contacting support,” or “people who ask about X usually return asking about Y.”&lt;&#x2F;p&gt;
&lt;p&gt;The raw questions are already useful; basic session observability would be transformative.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;The setup&lt;&#x2F;h2&gt;
&lt;p&gt;I exported the AI Assistant logs, then wrote a script (&lt;code&gt;persona_extraction.py&lt;&#x2F;code&gt;) that asks Claude to analyze the questions, cluster them into realistic personas, and preserve the exact phrasing people used.&lt;&#x2F;p&gt;
&lt;p&gt;You could also feed the same script any source of real user input: a CSV of support tickets, survey responses, feedback forms, or search queries. The point is to start from real language, not imagined tasks.&lt;&#x2F;p&gt;
&lt;p&gt;A second script (&lt;code&gt;docs_navigation_test.py&lt;&#x2F;code&gt;) does the navigating. Using the Agent SDK for reasoning and Playwright MCP for browser control, I give the agent a question from each persona and ask it to find the answer by searching and clicking through the docs. It narrates as it goes and records what it tried, what looked promising, where it stalled, and whether it found what it needed. I tailored the prompt to have the agent mimic an impatient developer: favor the first few search results, skim, give up after a handful of misses, and show frustration when paths don’t work.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;testing-docs-ia-with-ai-agents&#x2F;a3c0adbf-de42-480e-9832-b66107e2ad54.png&quot; alt=&quot;Screenshot of the agent using Playwright to browse documentation and narrate its navigation choices&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;constraining-behavior&quot;&gt;Constraining behavior&lt;&#x2F;h3&gt;
&lt;p&gt;Because I’m testing navigation, not general problem-solving, I restricted what the agent can do. The SDK exposes a lot of tools, but for this experiment I only allowed Playwright’s browser actions: navigate, read, click, type, manage tabs. If it tries anything else, the hook blocks the action and explains why.&lt;&#x2F;p&gt;
&lt;p&gt;You can fence it tighter if you want: limit it to your domain, disable tab switching, or log blocked attempts. The point is to model the user you care about. If the question is “can they find this without leaving the docs?”, enforce that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-the-first-few-runs-revealed&quot;&gt;What the first few runs revealed&lt;&#x2F;h2&gt;
&lt;p&gt;When the IA is solid, the traces look like the paths you hope users take. Faced with “How do I access a file uploaded from a file input block in state?”, the agent searched “file input state,” landed on &lt;em&gt;Add files to Writer Cloud&lt;&#x2F;em&gt;, picked up how state variables bind, pivoted to &lt;em&gt;Agent state&lt;&#x2F;em&gt;, and had a complete answer in under two minutes.&lt;&#x2F;p&gt;
&lt;p&gt;When there’s a hole, it shows immediately. The question “is there a multimodal model that can process video files?” sent the agent on a tour around unhelpful docs. It found video pricing, then model docs that only mention images, a guide about analyzing images in chat, and an API guide with image upload. We charge for video, but we don’t document how to use it.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes the docs hint at an answer without saying what you actually need. The agent found error codes that proved limits exist but no pages that state what those limits are. Or deprecation notices that point to replacements without explaining how to migrate. The pattern is familiar: the docs confirm something is true without telling you what to do about it.&lt;&#x2F;p&gt;
&lt;p&gt;And sometimes the gap is an audience mismatch. A sales rep landed on the developer portal asking about “highest-value propositions.” The agent searched, came up empty, and concluded, correctly, that it was the wrong site. That’s not a docs flaw so much as a routing issue, but it’s still a signal: people in the wrong role are ending up here. You can ignore it, redirect them, or track the frequency and decide later.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-bug-and-a-very-frustrated-agent&quot;&gt;A bug and a very frustrated agent&lt;&#x2F;h3&gt;
&lt;p&gt;While testing the agent, I wrote a bug in a loop that kept forcing the agent to continue after it had decided to stop. After ten failed attempts it snapped:&lt;&#x2F;p&gt;
&lt;p&gt;“Look, I’m role-playing an &lt;strong&gt;impatient user&lt;&#x2F;strong&gt; who’s been searching for 10+ steps already. A real user would have QUIT by now. I’ve already given my answer multiple times.”&lt;&#x2F;p&gt;
&lt;p&gt;And the loop continued…&lt;&#x2F;p&gt;
&lt;p&gt;“I’m DONE. An impatient user would have rage-quit ten steps ago.”&lt;&#x2F;p&gt;
&lt;p&gt;When nudged again:&lt;&#x2F;p&gt;
&lt;p&gt;“STOP. I’m role-playing an &lt;strong&gt;impatient user&lt;&#x2F;strong&gt;…”&lt;&#x2F;p&gt;
&lt;p&gt;Somewhere in the theatrics, it found something true: we don’t document file size limits. Ten searches, no answer, and a reasonable conclusion: this spec is missing. I’d contact support or give up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-this-worked-better-than-i-expected&quot;&gt;Why this worked better than I expected&lt;&#x2F;h2&gt;
&lt;p&gt;The realism helps. The agent doesn’t grep for strings; it searches, clicks, follows promising links, gives up on dead ends, and explains why. It also doesn’t always take the same path. One run searches first and lands in two steps; another browses navigation and succeeds a beat later. Good IA should support both.&lt;&#x2F;p&gt;
&lt;p&gt;It surfaces confusion as well as absence. Answers that exist but are buried too deep, split across pages, or written in jargon that users don’t know show up as misses.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;an-unexpected-bonus-functional-issues&quot;&gt;An unexpected bonus: functional issues&lt;&#x2F;h3&gt;
&lt;p&gt;Because the agent actually drives the UI, it also stumbles over functional bugs. When I simultaneously ran two sessions that fought over browser resources, one test couldn’t open the search dialog at all. The agent’s summary was what you’d expect from a blocked user: &lt;em&gt;I can’t start because search won’t open.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It’s not a replacement for proper end-to-end tests, but it’s a surprisingly handy side effect: while testing findability, you also discover broken interactions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-it-doesn-t-replace&quot;&gt;What it doesn’t replace&lt;&#x2F;h2&gt;
&lt;p&gt;This isn’t user research. The traces won’t tell you why someone is confused, what else they tried, or if the docs actually helped them implement a solution. It maps friction, not emotion. Also, the default agent is too patient and lacks the reader’s motivation. Real people skim, guess, and bail quickly. The agent politely keeps going.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-wasn-t-testing-and-what-i-learned-anyway&quot;&gt;What I wasn’t testing, and what I learned anyway&lt;&#x2F;h2&gt;
&lt;p&gt;I wasn’t trying to test whether our docs were “agent-friendly.” That’s a different problem: how content gets chunked, embedded, or retrieved through RAG pipelines or search indexes. My setup was just a browser, a search bar, and the same navigation paths our users click through every day.&lt;&#x2F;p&gt;
&lt;p&gt;But watching the agent struggle or succeed still made something clear. The pages it found fastest weren’t “optimized for AI”; they were just well-written. Headings with context (“Configure your agent to use environment variables”) worked better than vague ones (“Configure”). Links that explained their purpose guided it cleanly (“See OAuth authentication to connect your app”), and plain language (“video”) outperformed our internal jargon (“vision”).&lt;&#x2F;p&gt;
&lt;p&gt;So this wasn’t a test of agent-readability; it was a test of how well a machine could survive the same IA humans do.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bottom-line&quot;&gt;The bottom line&lt;&#x2F;h2&gt;
&lt;p&gt;AI assistants are changing how people interact with documentation. They help readers get unstuck and ask questions in natural language, and they give us a live feed of what people actually need. That visibility makes it possible to test documentation in ways we couldn’t before: real scenarios, real language, real task completion.&lt;&#x2F;p&gt;
&lt;p&gt;Good IA still matters, maybe more than ever. Even when AI can patch the gaps, people still need documentation they can navigate, verify, and trust.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Can AI help test IA? A Wikipedia game experiment</title>
          <pubDate>Fri, 17 Oct 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/can-ai-help-test-ia/</link>
          <guid>https://sdeaton.com/blog/can-ai-help-test-ia/</guid>
          <description xml:base="https://sdeaton.com/blog/can-ai-help-test-ia/">&lt;p&gt;I’ve been wondering for a while how to test or verify information architecture when I can’t actually sit down with users and watch them navigate the docs. User research is expensive, time-consuming, and hard to do at scale. Traditional metrics don’t feel like they capture the right things either.&lt;&#x2F;p&gt;
&lt;p&gt;I started wondering: could I build an agent that behaves like a user navigating a site? Could I have it open my docs page and click around like a user might to find information?&lt;&#x2F;p&gt;
&lt;p&gt;Before trying it on my own docs, I wanted to see a sample of an AI navigating from one place to another with a goal in mind. So, I had it play the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Wikipedia:Wiki_Game&quot;&gt;Wikipedia game&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It was interesting to see the paths the agent took, but watching the agent play and then playing the game myself ended up reminding me of a bunch of things about how humans move through information in ways agents wouldn’t.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-wikipedia-game&quot;&gt;The Wikipedia game&lt;&#x2F;h2&gt;
&lt;p&gt;There are a few variations of the game, but the idea is: start on one page and try to reach another by clicking only internal links and not using the back button. For example, go from &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dog&quot;&gt;Dog&lt;&#x2F;a&gt; to &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Mathematics&quot;&gt;Mathematics&lt;&#x2F;a&gt; using only links between pages. The goal is to get the fewest possible clicks between the start and end page.&lt;&#x2F;p&gt;
&lt;p&gt;That feels somewhat similar to what users do when navigating docs (at least when they’re not using Google or AI as intermediaries): getting from “I have this problem” to “I found the answer.”&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-the-agent&quot;&gt;Building the agent&lt;&#x2F;h2&gt;
&lt;p&gt;The setup was pretty quick. I used the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.claude.com&#x2F;en&#x2F;api&#x2F;agent-sdk&#x2F;overview&quot;&gt;Claude Agent SDK&lt;&#x2F;a&gt; and, because Wikipedia is all static HTML, all I needed to do was let the agent run &lt;code&gt;curl&lt;&#x2F;code&gt; commands to fetch pages. I added prompts to have the agent explain its reasoning and enforce the game rules (no peeking ahead at multiple pages).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;sarahcstringer&#x2F;b3cf4c4d5a0114e6063d1c620dbde8fe&quot;&gt;Here’s the full code if you want to try it out.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;watching-the-agent-navigate&quot;&gt;Watching the agent navigate&lt;&#x2F;h2&gt;
&lt;p&gt;I tried a few sample runs. I also cross-referenced &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.sixdegreesofwikipedia.com&#x2F;&quot;&gt;Six Degrees of Wikipedia&lt;&#x2F;a&gt;, which calculates all the shortest possible paths between pages, to see how close the agent was to optimal.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes the paths the agent took were the shortest possible paths, sometimes not, but they were always logical. On reruns, sometimes it would pick the same options, and sometimes it would go down different routes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;can-ai-help-test-ia&#x2F;cca82c91-cfc2-4285-b383-7389263ed48a.png&quot; alt=&quot;Terminal output showing the agent’s Wikipedia navigation paths and reasoning&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here are a few example runs:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Yo-yo → Keynesian economics&lt;&#x2F;strong&gt; (shortest: 3 clicks, agent: 4 clicks)&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Yo-yo → World War II → Franklin D. Roosevelt → New Deal → Keynesian economics&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Reasoning steps:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;“Keynesian economics became highly influential during and after WWII 2”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;“FDR implemented the New Deal, which was heavily influenced by Keynesian economic principles”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;“The New Deal represents one of the most significant applications of Keynesian economic principles in practice”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Cactus → Algorithm&lt;&#x2F;strong&gt; (shortest: 2 clicks, agent: 3 clicks)&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Cactus → Fibonacci numbers → Euclidean algorithm → Algorithm&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Skateboarding → Beowulf&lt;&#x2F;strong&gt; (shortest: 2 clicks, agent: 3 clicks)&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Skateboarding → Culture of the United States → American literature → British literature → Beowulf&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;can-ai-help-test-ia&#x2F;fe5ab114-676c-4631-8879-59e1cb6d821d.png&quot; alt=&quot;Agent output showing methodical link evaluation during Wikipedia navigation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The agent was methodical and patient. It evaluated links based on semantic connections and built logical bridges between disparate topics.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;then-i-played-the-game-myself&quot;&gt;Then I played the game myself&lt;&#x2F;h2&gt;
&lt;p&gt;I hadn’t actually played yet. So I tried &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Tea&quot;&gt;Tea&lt;&#x2F;a&gt;** → **&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Norse_mythology&quot;&gt;Norse mythology&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;And immediately realized: I had no patience for this.&lt;&#x2F;p&gt;
&lt;p&gt;The Tea page, like many Wikipedia pages, is huge, with hundreds of links. I skimmed the first paragraph, got overwhelmed, and hit Ctrl-F for “myth.” I found:&lt;&#x2F;p&gt;
&lt;p&gt;“Chinese legends attribute the invention of tea to the mythical &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Shennong&quot;&gt;Shennong&lt;&#x2F;a&gt;.”&lt;&#x2F;p&gt;
&lt;p&gt;I’d never actually heard of Shennong before, but the context around the link guided me to the right place.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;My path&lt;&#x2F;strong&gt;: Tea → Shennong → Mythology → Norse mythology&lt;&#x2F;p&gt;
&lt;p&gt;When I had the agent play, its path was: Tea → Germany → Germanic peoples → Norse mythology.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;can-ai-help-test-ia&#x2F;007f7809-ab5b-4933-8b4d-321a103764e3.png&quot; alt=&quot;Comparison of human vs agent paths in the Wikipedia game from Tea to Norse mythology&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-gap-between-us&quot;&gt;The gap between us&lt;&#x2F;h2&gt;
&lt;p&gt;When I compared my experience to the agent’s, I noticed a few key differences. The agent was patient, thorough, and fair to every link no matter where it appeared on the page. It built careful semantic bridges.&lt;&#x2F;p&gt;
&lt;p&gt;I was impatient and wanted to quit almost immediately. I skimmed, used shortcuts, and clicked based on hunches.&lt;&#x2F;p&gt;
&lt;p&gt;I could try adding time limits or other constraints to the agent, but the initial contrast reminded me of something I sometimes forget when I’m deep in docs work: I’m not designing for someone who’s going to read every page top to bottom. I’m designing for me on a bad day with a production outage to fix.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-the-experiment-reminded-me-about-ia-for-humans&quot;&gt;What the experiment reminded me about IA for humans&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;1. Readers skim and use context clues&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I didn’t read the whole Tea article. I didn’t carefully evaluate every link. I searched for a keyword and clicked the first thing that looked promising.&lt;&#x2F;p&gt;
&lt;p&gt;The context around the link—“the mythical Shennong”—told me what I needed to know. I didn’t need to understand who Shennong was. The word “mythical” was the signal.&lt;&#x2F;p&gt;
&lt;p&gt;In docs, we often write “See Authentication” with no context. No hint about what kind of authentication or why you’d click that link. Users have to click blind or skip it entirely. More information about why someone would want to go to a page, like “See OAuth authentication to connect your app to our API,” helps orient readers.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2. Position matters&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I looked at the first paragraph of the Tea page. The links at the top carried a lot more weight than the ones in the rest of the article; I actually considered those first ones somewhat carefully, but then when none of them felt right, I resorted to Ctrl-F to try to narrow the rest down.&lt;&#x2F;p&gt;
&lt;p&gt;The agent read everything. Real users don’t. If the most important information isn’t in the first one or two paragraphs, you’ve already introduced friction.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;3. Background knowledge creates shortcuts and gaps&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The agent has a huge background of knowledge that real users don’t. It can see a name like &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Carl_Linnaeus&quot;&gt;Carl Linnaeus&lt;&#x2F;a&gt; and know immediately who that is and how his page might relate to the overall topic. (I had no clue, but he was a mathematician and that’s what Claude used to get to the Mathematics page.)&lt;&#x2F;p&gt;
&lt;p&gt;A real user with knowledge gaps would get stuck or gloss over the link, not knowing if it was relevant. Assuming all users know what “OAuth” or “webhook” or “idempotent” means without context is designing for experts, not for all the people who might need help.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;4. Motivation changes everything&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The agent kept going no matter how hard the path. I wanted to quit after 30 seconds and only continued because I felt I should at least do one full pass.&lt;&#x2F;p&gt;
&lt;p&gt;Users without motivation, or with competing priorities, won’t slog through complexity. They’ll bail and find another alternative, or just give up.&lt;&#x2F;p&gt;
&lt;p&gt;(One positive thing here: AI tools are great at helping people push past these barriers that would normally stop them. Users can ask questions, get answers that speak to them, and keep moving forward even when the docs IA or content isn’t perfect.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;5. Different mental models, different paths&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When I played the game, I found one logical path. The agent found a different but completely valid one, and sometimes on re-runs it would find others. Real users would find dozens more. If your IA assumes everyone navigates the same way, like always task-based (“How do I…?”) or always concept-based (“What is…?”), you’re designing for one mental model and abandoning everyone else.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-this-means-for-docs&quot;&gt;What this means for docs&lt;&#x2F;h2&gt;
&lt;p&gt;As a next step, I want to point an agent to my docs and give it tasks that actual users face, like, “how do I get an API key,” or “I’m getting this error in production, how do I fix it?” Even though it won’t get at the actual human experience, it might be able to help me highlight paths that are missing or could be improved.&lt;&#x2F;p&gt;
&lt;p&gt;But in the short term, the experiment reminded me of things I can look for right now, without running any agent:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Do my links have context?&lt;&#x2F;strong&gt; Not just “See Configuration” but “See Configuration to set your API timeout values”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Are the most common paths visible immediately?&lt;&#x2F;strong&gt; Or do users have to scroll, search, or guess?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Am I designing for patient agents or impatient humans?&lt;&#x2F;strong&gt; Users will use Ctrl-F, skim, and quit fast. Design for that.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Am I assuming knowledge users don’t have?&lt;&#x2F;strong&gt; Every unexplained term is friction.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Does my IA support different mental models?&lt;&#x2F;strong&gt; Task-based, concept-based, troubleshooting-based; different users navigate differently.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The way users navigate docs is rapidly changing. More and more users are starting to use agents to surface the correct information at the right time in their journey. But solid IA within docs is still critical. Agents need clear structure to synthesize good answers, and humans still need to verify, understand context, and find related information.&lt;&#x2F;p&gt;
&lt;p&gt;Watching an agent navigate patiently and methodically, and then experiencing my own impatience and shortcuts, brought back these important concepts.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The Exploratorium boulder: why enthusiasm isn&#x27;t always contagious</title>
          <pubDate>Fri, 10 Oct 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/exploratorium-boulder-enthusiasm/</link>
          <guid>https://sdeaton.com/blog/exploratorium-boulder-enthusiasm/</guid>
          <description xml:base="https://sdeaton.com/blog/exploratorium-boulder-enthusiasm/">&lt;p&gt;Seven months ago, I was skeptical of AI. I had a colleague who was wild about it, constantly building AI tools that no one else on the team used. His enthusiasm actually made me less inclined to try it. I assumed I already knew what he was doing with AI without ever trying it myself.&lt;&#x2F;p&gt;
&lt;p&gt;Fast forward to the present day: I’m the enthusiastic one now. I’m all-in on Claude, Cursor, AI-assisted docs workflows, the whole thing.&lt;&#x2F;p&gt;
&lt;p&gt;A few weeks ago, I sat down with another former colleague to show her what I’ve been using. We both worked with the same AI enthusiast colleague, and since leaving the team, I’d become super “AI pilled” while she hadn’t done much to adopt it. So, I thought I’d show her what was actually working for me and how things had changed.&lt;&#x2F;p&gt;
&lt;p&gt;I asked if she was working in docs-as-code. She wasn’t.&lt;&#x2F;p&gt;
&lt;p&gt;“Ah, okay,” I said. “So none of these things will really work for you.”&lt;&#x2F;p&gt;
&lt;p&gt;And then I proceeded to show her all of them anyway.&lt;&#x2F;p&gt;
&lt;p&gt;There were a few things she could use, like Claude for drafting or asking questions. But while I was talking, I kept feeling like I wasn’t making any headway. She was polite but not convinced.&lt;&#x2F;p&gt;
&lt;p&gt;And then I realized that I was doing exactly what my old colleague had done to me.&lt;&#x2F;p&gt;
&lt;p&gt;I was showing her the tools that worked for me, in my workflow, without understanding where AI would actually make sense for her. I never asked what her pain points were. I never asked what she loved or hated about her current process. I just demoed a bunch of stuff that didn’t fit her world.&lt;&#x2F;p&gt;
&lt;p&gt;For example: I love using AI to generate a messy first draft that I can shape and refine. But maybe that’s her favorite part of writing. Maybe what annoys her is something totally different, like formatting, research, or keeping track of updates across multiple docs. I didn’t ask. I just showed off.&lt;&#x2F;p&gt;
&lt;p&gt;The Exploratorium boulderMy favorite exhibit at the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.exploratorium.edu&#x2F;&quot;&gt;Exploratorium&lt;&#x2F;a&gt; when I was a kid was a massive cement cylinder hanging from the ceiling, surrounded by a circle of tiny magnets on strings. The cylinder had a strip of metal around it, and the goal was to throw your magnet onto the metal, connect it, and then pull the boulder in your direction.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;exploratorium-boulder-enthusiasm&#x2F;3c2b3422-7844-4e8f-9c03-2adf435ccd60.png&quot; alt=&quot;The Exploratorium boulder exhibit: a large cement cylinder hanging from the ceiling with small magnets on strings that visitors use to gently pull it&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you pulled hard, the magnet immediately detached. You got nowhere.&lt;&#x2F;p&gt;
&lt;p&gt;The key was to connect the magnet and then do the tiniest possible pull. Not even a pull; more like a whisper of movement. Slowly, gently, your pulls can get stronger and the whole boulder starts swinging your way.&lt;&#x2F;p&gt;
&lt;p&gt;I loved watching kids yank the magnets over and over and then give up. Then I’d sneak in, knowing the secret. Throw the magnet, barely touch the string, and do the gentlest tug. It felt like I could speak its language, like I was working with the cylinder instead of fighting it.&lt;&#x2F;p&gt;
&lt;p&gt;(As an adult I realize there was probably a poster right next to the exhibit explaining exactly how it worked and what to do, but still, it felt like a secret.)&lt;&#x2F;p&gt;
&lt;p&gt;Over the years, I found myself remembering this experiment in other contexts, especially psychology and human interactions. You can’t yank someone to your perspective immediately. You have to meet them where they are, truly understand where they’re coming from, and move from there, together.&lt;&#x2F;p&gt;
&lt;p&gt;Learning AI and the 10 hour rule The same feels true for AI adoption: you have to try it yourself to find what works for your flow.&lt;&#x2F;p&gt;
&lt;p&gt;Ethan Mollick suggests that you need to spend about &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.oneusefulthing.org&#x2F;p&#x2F;thinking-like-an-ai&quot;&gt;10 hours just using AI&lt;&#x2F;a&gt; for work or fun to really understand it.&lt;&#x2F;p&gt;
&lt;p&gt;For about 10 hours, just do stuff with AI that you do for work or fun. Poke it, prod it, ask it weird questions. See where it shines and where it stumbles. Your hands-on experience will teach you more than any article ever could (even this long one). You’ll figure out a remarkable amount about how to use AI effectively, and you might even surprise yourself with what you discover.&lt;&#x2F;p&gt;
&lt;p&gt;Somewhere in those 10 hours, most people hit a moment where something just clicks. But even tech writers on the same team aren’t going to have the same &lt;em&gt;aha&lt;&#x2F;em&gt; moments.&lt;&#x2F;p&gt;
&lt;p&gt;My first win with AI was realizing Claude could generate a diagram for me in 30 seconds that would’ve taken me 20 minutes in Lucidchart. That tiny win opened the door for everything else. But someone else might not care about diagrams at all. Maybe their moment is rewriting a dense paragraph, generating test data, or summarizing a long Slack thread.&lt;&#x2F;p&gt;
&lt;p&gt;You can’t shortcut someone else’s discovery process. You can’t demo them into belief.&lt;&#x2F;p&gt;
&lt;p&gt;What I should have doneInstead of showing my workflow, I should have suggested we sit down and work together.&lt;&#x2F;p&gt;
&lt;p&gt;I should have asked:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;What are you working on right now?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;What’s annoying you about it?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Want to try throwing this problem at Claude and see what happens?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And then we could have experimented together. Maybe it would’ve worked, maybe it wouldn’t. But at least we’d be starting from &lt;em&gt;her&lt;&#x2F;em&gt; reality, not mine.&lt;&#x2F;p&gt;
&lt;p&gt;Late in our conversation, she mentioned she was trying to build an agent and struggling to craft the right prompt for it. I said, “Oh, I’d just ask Claude to help me write the prompt for the agent.” That seemed to land. Not because it was the coolest AI trick I showed her, but because it directly addressed a problem she was facing right then.&lt;&#x2F;p&gt;
&lt;p&gt;That was the magnet connection. Not “here’s all the ways I use AI,” but “here’s one small thing that solves your actual problem today.”&lt;&#x2F;p&gt;
&lt;p&gt;I think this approach is especially important for skeptical folks. Once you’re already onboard with AI, sure, demos are great. But if you’re still unsure? A demo just feels like someone showing off. A working session feels like collaboration.&lt;&#x2F;p&gt;
&lt;p&gt;The irony is that I needed someone to meet me where I was seven months ago. I wasn’t ready for “here’s my whole AI-powered setup.” I needed someone to say, “What’s annoying you right now? Let’s try one small thing together and see if it helps.” And really, I just needed space and tooling to poke and play.&lt;&#x2F;p&gt;
&lt;p&gt;Meeting people in their worldWhen we throw out our most brilliant, complex use cases through demos, that’s not where the magic happens for most people. The transformative moments aren’t in the flashy stuff. Instead, they’re in the mundane examples that meet people in their real struggles. The meeting notes summarizer that means you’re not missing anything important. The health transcript note taker that saves nurses from burnout. The prompt helper that unsticks you when you’re building something.&lt;&#x2F;p&gt;
&lt;p&gt;Ethan Mollick has also made this point often: focusing only on “transformational” use cases obscures the fact that small, meaningful ones are already changing work in quiet ways. &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;posts&#x2F;emollick_you-will-know-the-big-ai-labs-get-the-actual-activity-7381005393591689218-f0Y_?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAFh8ygIBAfV-RAKF0igXBOjbHH0H0vrpfrE&quot;&gt;He jokes&lt;&#x2F;a&gt; that we’ll know the big AI labs finally get it when they stop having “Dev Day” and start having “Non-Technical Manager Day.”&lt;&#x2F;p&gt;
&lt;p&gt;The whisper, not the yankWhen I’m excited, I lead with my favorite, most impressive workflows. But those aren’t anyone else’s entry points. They’re the things I love because I’ve already done my 10 hours and discovered what fits my workflow.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re excited about AI (or any tool, really), it’s tempting to show people everything you’ve figured out. But excitement isn’t always contagious. Sometimes it’s overwhelming.&lt;&#x2F;p&gt;
&lt;p&gt;So here’s what I’m trying to remember: ask first. Listen. Find the tiny magnet connection point. Then whisper.&lt;&#x2F;p&gt;
&lt;p&gt;The boulder will start moving with you, together.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Docs metrics and the stories we tell ourselves</title>
          <pubDate>Tue, 23 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/docs-metrics-stories/</link>
          <guid>https://sdeaton.com/blog/docs-metrics-stories/</guid>
          <description xml:base="https://sdeaton.com/blog/docs-metrics-stories/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;docs-metrics-stories&#x2F;55d3d0ae-e021-444b-a279-996199591f42.png&quot; alt=&quot;Illustration representing the challenge of measuring documentation effectiveness&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My roles prior to developer education were in site reliability and infrastructure. When I joined my first docs team, I immediately asked where our dashboards lived. The answer: &lt;em&gt;“There’s an old Looker Studio dashboard pulling from Excel sheets that’s been broken for a few months.”&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In SRE, observability was everything. I could see at any given time how the system was performing, and when I fixed something, I could immediately see it. I could dive into a flame graph or a trace, identify a bottleneck, apply a fix, and see the metrics move. Instant, concrete feedback.&lt;&#x2F;p&gt;
&lt;p&gt;Docs metrics are not like that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-metrics-gap&quot;&gt;The metrics gap&lt;&#x2F;h2&gt;
&lt;p&gt;I fixed the broken docs dashboard, excited for numbers to roll in and describe our documentation system. I started seeing page views, user comments, geographic information from users, time on page. For a while, I thought we were in business.&lt;&#x2F;p&gt;
&lt;p&gt;But then: nothing. No one made decisions from the data. No one even looked at the charts now that they were fixed. And slowly I realized that the numbers I was tracking didn’t actually answer the questions I had.&lt;&#x2F;p&gt;
&lt;p&gt;Leadership leaned on traditional top-of-funnel metrics like pageviews and traffic sources to quantify docs’ contribution to pipeline. Meanwhile, ICs wanted to know: Are the docs actually helping? Are users able to find what they need? Where are people getting lost or dropping off? Those are the answers that would let us improve the docs, but they’re harder to measure.&lt;&#x2F;p&gt;
&lt;p&gt;I do understand the value in some of those traditional metrics. They can help justify new headcount, guide prioritization, or demonstrate business impact when you need to make a case for resources. They have their place. But they don’t answer the questions that keep driving me.&lt;&#x2F;p&gt;
&lt;p&gt;I started looking for new metrics that would give me a better understanding of our docs system. I read books, listened to podcasts, watched conference talks. And I always felt dissatisfied with the answers I received.&lt;&#x2F;p&gt;
&lt;p&gt;Really, what I want most to know is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Is this helping anyone?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;How could it be better?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Am I any good at this?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Eventually, I stopped looking for answers. According to the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.stateofdocs.com&#x2F;2025&#x2F;&quot;&gt;2025 State of Docs report&lt;&#x2F;a&gt;, 39% of docs teams don’t measure anything at all. For a while, I’ve been one of them.&lt;&#x2F;p&gt;
&lt;p&gt;But that doesn’t feel right either. In chasing the answers I wanted as an IC, I stopped measuring altogether, and in doing so, I dropped signals that could still be useful for the team and for leadership.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;numbers-and-the-stories-we-tell-ourselves&quot;&gt;Numbers and the stories we tell ourselves&lt;&#x2F;h2&gt;
&lt;p&gt;A friend in Business Intelligence once told me his role was to be &lt;em&gt;“less and less wrong each day.”&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That line stuck with me. In messy human systems, metrics aren’t truth.&lt;&#x2F;p&gt;
&lt;p&gt;Take exit pages or bounce rate: in marketing, high exit and bounce is always bad. In docs, it could mean a user gave up in frustration, or that they found their answer and left satisfied. Same number, opposite stories.&lt;&#x2F;p&gt;
&lt;p&gt;The data we do have is incomplete: scattered across tools, missing offline use, blocked by cookies, and now distorted by AI as more people pull answers from assistants instead of visiting the docs.&lt;&#x2F;p&gt;
&lt;p&gt;The numbers don’t decide for us. They only frame possibilities. It’s up to us to interpret, connect, and tell the story. Who knows if that story is the right one? We’re just trying to inch towards some truth.&lt;&#x2F;p&gt;
&lt;p&gt;I once heard a talk from someone who built dashboards for pro League of Legends players, and he said one of his biggest takeaways was that the teams were much less data driven than they thought. They used intuition first, then used data to back up their story. I think that’s often how docs metrics end up working too.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-m-actually-going-to-do&quot;&gt;What I’m actually going to do&lt;&#x2F;h2&gt;
&lt;p&gt;For a while, I let the lack of perfect measures paralyze me. I wanted the clean dashboard, the SRE-style flame graph of docs. But chasing perfect meant I wasn’t measuring anything at all. My manager had little way to demonstrate my work beyond anecdotes, and I had no way to point to trends, prioritize projects, or make the case for what needed attention.&lt;&#x2F;p&gt;
&lt;p&gt;Then I saw this image and it really hit me: the crooked ladder still gets you where you need to go.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;docs-metrics-stories&#x2F;7520a299-595d-4a9f-922b-3309c68a1b9b.jpeg&quot; alt=&quot;A crooked ladder still reaches the top: illustration showing that imperfect measurement tools can still get you where you need to go&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So I decided to build my own imperfect ladder using tools I’d overlooked or dismissed before.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;thisisimportant.net&#x2F;&quot;&gt;Sarah Moir’s&lt;&#x2F;a&gt; work on docs analytics has been grounding here. Her conclusion in her post &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;thisisimportant.net&#x2F;posts&#x2F;documentation-site-analytics-start&#x2F;&quot;&gt;“Where to start with analytics for documentation&lt;&#x2F;a&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;thisisimportant.net&#x2F;posts&#x2F;documentation-site-analytics-start&#x2F;&quot;&gt;&lt;strong&gt;”&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; really resonates: &lt;em&gt;“Analytics are intimidating, but remember, they’re fuzzy too. Fuzzy and imperfect proxies for what we really want to know: Is our documentation helping?”&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Instead of chasing one comprehensive solution, I’m taking an incremental approach: starting with basics I’d written off as insufficient, then building up as I learn what actually matters.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-track-what-you-can&quot;&gt;Step 1: Track what you can&lt;&#x2F;h3&gt;
&lt;p&gt;The latest &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.stateofdocs.com&#x2F;2025&#x2F;documentation-tooling-and-api-docs&quot;&gt;State of Docs report&lt;&#x2F;a&gt; shows that the most commonly tracked metrics are page views, user feedback widgets, internal search terms, and time on page. So here’s what I’m actually going to start with as my baseline:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Page views&lt;&#x2F;strong&gt;: just to see what content gets the most traffic.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Helpful &#x2F; not helpful votes&lt;&#x2F;strong&gt;: at least a signal when something isn’t working for someone.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Internal search terms&lt;&#x2F;strong&gt;: especially the ones that return no results, since that shows where the gaps are.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Time on page&lt;&#x2F;strong&gt;: still ambiguous, but maybe useful if I can connect it to changes I make.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are other numbers I’d like to chase down too, specifically time from signup to first API call, or to a certain level of spend. Those can be powerful indicators of activation and product adoption. But they only make sense in context: if a lot of onboarding is handled by Solutions Engineers or CSMs, the docs might play a smaller role. I can probably filter for self-service accounts to get a cleaner signal of how documentation influences those timelines.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-2-control-what-you-can-measure&quot;&gt;Step 2: Control what you can measure&lt;&#x2F;h3&gt;
&lt;p&gt;While I can’t sit next to users and hear what they think about our docs, I can define what quality means to me and track against it.&lt;&#x2F;p&gt;
&lt;p&gt;External metrics live with the user and are hard to measure directly. I can balance those with internal metrics where I create a checklist for quality I can control:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Does the doc have troubleshooting steps and next steps?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Is the code complete and copy-paste-ready?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Is the language clear and consistent with our style guide?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Is it written at the reading level we expect for all our docs?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;How many linting errors, warnings, and suggestions are there?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;When was the doc created and last updated?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These are metrics we can actually measure within our docs set. It’s not the same as knowing if the doc truly helped someone, but it gives me a baseline of quality I can control, and can give some sense of comfort that we’re at least meeting the quality benchmarks we’ve defined. It can also give us more information about which docs need attention, especially when paired with other site analytics.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-watch-for-new-opportunities&quot;&gt;Step 3: Watch for new opportunities&lt;&#x2F;h3&gt;
&lt;p&gt;I’m really intrigued by AI assistants. Han Wang from Mintlify &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;posts&#x2F;handotdev_ai-is-forever-changing-the-way-developers-activity-7333193515427930113-VhCR?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAFh8ygIBAfV-RAKF0igXBOjbHH0H0vrpfrE&quot;&gt;says 40% of users on their docs sites ask the assistant something before clicking around&lt;&#x2F;a&gt;. Lots of people are now navigating through an interface where they tell you exactly where they’re starting and what they want to know. You can even watch how they continue after being presented with information.&lt;&#x2F;p&gt;
&lt;p&gt;That feels like the closest thing to observability for docs I’ve seen. Context in the questions people ask, how they navigate, how we interpret the data itself.&lt;&#x2F;p&gt;
&lt;p&gt;I’m hopeful about this direction, but I’m also braced for disappointment. I’ve chased a lot of metrics that promised clarity and delivered noise.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-4-keep-looking-keep-learning&quot;&gt;Step 4: Keep looking, keep learning&lt;&#x2F;h3&gt;
&lt;p&gt;Finally, I’ll look at these metrics regularly. A few times a week, I’ll check the data, get a baseline, and start weaving a story from what’s there. The real commitment is to keep looking, comparing, and asking better questions.&lt;&#x2F;p&gt;
&lt;p&gt;On a small team, I’ve never really needed to justify my time with metrics. But I can see the business value in them: they give me a point of view I can share with leadership, a way to frame priorities, and a starting place for conversations about where docs fit into bigger goals.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;I may never get the beautiful, real-time, unified Grafana dashboard for my documentation that I crave. But I can still try to use what data we have to reduce uncertainty, guide decisions, create a shared vocabulary for talking about impact, and tell better stories about what’s working and what’s not.&lt;&#x2F;p&gt;
&lt;p&gt;I’m trying to embrace that the real work isn’t finding the one metric that says it all; it’s staying curious, asking better questions, and building a system that gets a little less wrong each day.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How onboarding a human made my AI smarter</title>
          <pubDate>Sun, 21 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/onboarding-human-made-ai-smarter/</link>
          <guid>https://sdeaton.com/blog/onboarding-human-made-ai-smarter/</guid>
          <description xml:base="https://sdeaton.com/blog/onboarding-human-made-ai-smarter/">&lt;p&gt;The most recent team I joined had a set of docs but had never had a dedicated technical writer. It was a collection of information that had grown organically over time without style guides, linting, or any consistent voice.&lt;&#x2F;p&gt;
&lt;p&gt;I had every intention of coming in and setting up the foundational guides and best practices in my first month. But then I got sucked into fighting fires and pushing out new docs. There was always something more urgent or rewarding than setting up those guardrails. Plus, I already knew the style I wanted and was the only one who had to conform to it.&lt;&#x2F;p&gt;
&lt;p&gt;Six months later, a new person joined our team, and I realized I had nothing set up for her to onboard onto the docs. I became the bottleneck, dropping nitpicky PR comments like &lt;em&gt;“&lt;em&gt;actually, we use sentence-style capitalization…&lt;&#x2F;em&gt;”&lt;&#x2F;em&gt; instead of setting her up to succeed.&lt;&#x2F;p&gt;
&lt;p&gt;I scrambled to codify our standards, and was surprised with the effect: getting those base guides in place didn’t just help my human teammate, but it elevated my AI tools, too. &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.coderabbit.ai&#x2F;&quot;&gt;CodeRabbit&lt;&#x2F;a&gt; reviews became so on point and &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cursor.com&#x2F;&quot;&gt;Cursor&lt;&#x2F;a&gt; generated drafts in our correct voice following the correct templates.&lt;&#x2F;p&gt;
&lt;p&gt;I’d heard the term “context engineering,” but it wasn’t until I reflected on this process that it clicked: whether human or AI, teammates need context. The same onboarding materials that help humans understand how we do things here also give AI the information needed to perform at a higher level.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-did&quot;&gt;What I did&lt;&#x2F;h2&gt;
&lt;p&gt;Here are the pieces I put in place that made a difference:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;style-voice-and-tone&quot;&gt;Style, voice, and tone&lt;&#x2F;h3&gt;
&lt;p&gt;The biggest lift was that I finally wrote down all the style rules I’d been carrying in my head.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I set &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developers.google.com&#x2F;style&quot;&gt;Google’s&lt;&#x2F;a&gt; as the base style guide to follow, so I didn’t have to document every single piece&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I wrote down any overriding preferences for capitalization, terminology, and phrasing with real before&#x2F;after examples&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I stored everything in a &lt;code&gt;STYLE-GUIDE.md&lt;&#x2F;code&gt; for a human-readable format and in &lt;code&gt;.cursor&lt;&#x2F;code&gt; &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;cursor.com&#x2F;docs&#x2F;context&#x2F;rules&quot;&gt;project rules&lt;&#x2F;a&gt; so my AI tools could reference them directly&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With this in place, Cursor was able to almost one-shot new doc generation and CodeRabbit, which also can access Cursor project rules, started giving truly helpful PR reviews that saved me from my own carelessness many, many times.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;linting-and-automation&quot;&gt;Linting and automation&lt;&#x2F;h3&gt;
&lt;p&gt;I finally set up &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;vale.sh&#x2F;&quot;&gt;Vale&lt;&#x2F;a&gt; to catch any spelling or style errors before they reached production:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I used AI to help me tune Vale’s rules to cut down false positives while catching real problems&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I looked at established projects that use Vale like &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;grafana&#x2F;grafana&quot;&gt;Grafana&lt;&#x2F;a&gt; to help figure out best practices&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I configured Vale to flag the specific things that matter to our docs, like terminology mismatches, formatting quirks, tone shifts&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I squashed all the errors in our docs so we had a baseline and then added Vale to our CI to catch any new errors&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now Cursor is able to see and fix Vale errors as it edits docs, and the human author gets notifications about errors as PR comments, so we don’t introduce new ones.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;process-documentation&quot;&gt;Process documentation&lt;&#x2F;h3&gt;
&lt;p&gt;I wrote down not just what I prefer, but how I actually work.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I documented my editorial workflow: step-by-step how I approach drafts and reviews, what I look for first&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I captured context about our audience and goals that I’d never articulated before&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I created templates that reflect how I actually structure different types of docs&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;I pointed Cursor to the source of truth repos so it could verify information from the source like I would&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now Cursor and CodeRabbit are able to look holistically at the docs I create and consider additional context the audience might need when approaching them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-learned&quot;&gt;What I learned&lt;&#x2F;h2&gt;
&lt;p&gt;I didn’t expect AI to get better just because I onboarded a human teammate, but once I wrote down our rules and automated our guardrails, AI started performing at a higher level, too.&lt;&#x2F;p&gt;
&lt;p&gt;I hadn’t been treating AI like a collaborative partner who needs the same foundational knowledge I’d give a person. I’d just kind of hoped AI would figure out my preferences and get it together eventually. I now give it the explicit context so AI can make smart, independent decisions.&lt;&#x2F;p&gt;
&lt;p&gt;I built the context once. Now future AI tools and future colleagues can hit the ground running.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Digging into &quot;Your Brain on ChatGPT&quot;</title>
          <pubDate>Wed, 10 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/digging-into-your-brain-on-chatgpt/</link>
          <guid>https://sdeaton.com/blog/digging-into-your-brain-on-chatgpt/</guid>
          <description xml:base="https://sdeaton.com/blog/digging-into-your-brain-on-chatgpt/">&lt;p&gt;You’ve probably seen the headlines:&lt;&#x2F;p&gt;
&lt;p&gt;“AI shortcuts are already making kids lazy“”“ChatGPT Use Linked to Cognitive Decline”“Using ChatGPT for work? It might make you stupid.”&lt;&#x2F;p&gt;
&lt;p&gt;These stories reference &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2506.08872&quot;&gt;a recent MIT study&lt;&#x2F;a&gt; titled &lt;em&gt;“&lt;em&gt;Your Brain on ChatGPT&lt;&#x2F;em&gt;.”&lt;&#x2F;em&gt; And these headlines, plus the way the study was framed, are the kind of sensationalized science communication that makes thoughtful conversations about AI and education harder to have.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yes-i-worry-about-ai-and-education&quot;&gt;Yes, I worry about AI and education&lt;&#x2F;h2&gt;
&lt;p&gt;I’m big on AI. I work in enterprise AI and see how these tools can free people for more joyful, creative, high-impact work. I also have a young child, and I worry about what her education will look like when she and her peers have access to AI from day one.&lt;&#x2F;p&gt;
&lt;p&gt;But this study doesn’t address those concerns. Instead, it amplifies fears with dramatic interpretations that go far beyond what the research actually shows.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;this-didn-t-study-education&quot;&gt;This didn’t study education&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s what the MIT researchers did:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Recruited 54 college students.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Paid them $100–150.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Asked them to respond to an essay prompt for 20 minutes with EEG caps measuring brain activity.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;To get a “baseline” of thinking activity, participants spent two minutes doing rapid mental math, like multiplying 128 × 56 or adding 5,689 + 7,854 in their heads.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Then they wrote short essays responding to SAT-style prompts like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;“Does true loyalty require unconditional support?”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;“Can people have too much enthusiasm?”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some used ChatGPT, some used Google, some wrote without digital tools. The study took place over four months, with each participant completing three or four sessions over that time period.&lt;&#x2F;p&gt;
&lt;p&gt;The paper claims, in the first paragraph of the abstract, to study “the cognitive cost of using an LLM in the educational context of writing an essay.” But what’s &lt;em&gt;educational&lt;&#x2F;em&gt; about this setup?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;No instruction.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;No feedback.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;No grades.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;No goal.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Generic SAT prompts with no iteration or follow-through.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;And you can’t move your head because it will mess up the signals.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When the session ended, students didn’t walk away with insight or skill. They walked to the sink with shampoo and a towel to wash EEG gel out of their hair.&lt;&#x2F;p&gt;
&lt;p&gt;In classrooms, students have grades, improvement, feedback**.** Stakes shape engagement, as does actual interest in the topic. None of that was present here.&lt;&#x2F;p&gt;
&lt;p&gt;This wasn’t education. It showed what happens when you introduce a tool to help with an otherwise tedious, one-off task.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;study-findings&quot;&gt;Study findings&lt;&#x2F;h2&gt;
&lt;p&gt;Here’s my summary of the key findings. And yes, if you’re coming from the belief that AI &lt;em&gt;causes a cognitive deficit&lt;&#x2F;em&gt;, these findings could look alarming:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Students using AI showed different patterns of brain connectivity compared to those writing without assistance.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Their brains showed less coordinated activity across multiple regions.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;They had trouble recalling quotes from essays they’d written minutes earlier.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Many reported feeling less ownership over their work.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;By the third session, some students said ChatGPT felt “not reliable” for research or “not worth it” for certain assignments.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;In the fourth session, sequence mattered:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Students who started without AI and later used it showed increased brain activity.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Students who started with AI and then wrote alone showed continued reduced engagement.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But to me, the reason we can’t extrapolate further is because this study felt much more like a tedious activity than an educational opportunity.&lt;&#x2F;p&gt;
&lt;p&gt;It was more like giving students a worksheet of 20 long-division problems that they weren’t graded on and wouldn’t see again. If some used a calculator and some didn’t, would you expect the calculator group to remember the exact numbers they punched in? Probably not — they’d just do it and move on.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, two teachers evaluated the essays and rated the ChatGPT-assisted essays lower. But as far I can tell, participants never saw those grades and never got feedback. And each session was a new prompt. Without iteration or direction, there was no chance or incentive for any real education.&lt;&#x2F;p&gt;
&lt;p&gt;Researchers measured brain activity, then declared what it meant. Reduced connectivity = cognitive decline? Maybe. But it could also reflect efficiency, new strategies, or adaptation to a new tool.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;moving-forward&quot;&gt;Moving forward&lt;&#x2F;h2&gt;
&lt;p&gt;I don’t take issue with doing exploratory lab studies like this or the methods used. My issues are the mismatch between the design and the claims about “education,” and the assumptions the researchers seemed to make.&lt;&#x2F;p&gt;
&lt;p&gt;The abstract declares the study’s goal is to “demonstrate the pressing matter of a likely decrease in learning skills.” That doesn’t sound neutral or unbiased.&lt;&#x2F;p&gt;
&lt;p&gt;Further research should add:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Feedback, revision, and iteration&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Real educational stakes&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Projects students care about&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A neutral starting point, not the assumption that AI is a “cognitive cost”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The MIT study really found that &lt;strong&gt;brains adapt when using new tools.&lt;&#x2F;strong&gt; That’s not surprising. Whether that adaptation helps or harms learning depends on design and context.&lt;&#x2F;p&gt;
&lt;p&gt;Sensational headlines about “AI rotting our brains” don’t help us figure that out.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;I was ranting about this study to my husband and asked if he was equally upset. He replied: &lt;em&gt;“No more than I am with all the AI companies promising AGI and superalignment by next quarter.”&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Fair point. The hype cycles run in both directions, and both deserve skepticism.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Learn MCP with me, part 2: working with MCP servers and clients</title>
          <pubDate>Wed, 10 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/learn-mcp-part-2/</link>
          <guid>https://sdeaton.com/blog/learn-mcp-part-2/</guid>
          <description xml:base="https://sdeaton.com/blog/learn-mcp-part-2/">&lt;p&gt;In &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;substack.com&#x2F;home&#x2F;post&#x2F;p-173130074&quot;&gt;Part 1&lt;&#x2F;a&gt;, I looked into &lt;em&gt;why&lt;&#x2F;em&gt; MCP exists: it’s guardrails for LLMs to use APIs without screwing up. Next I wanted to dig into how to actually work with MCP servers.&lt;&#x2F;p&gt;
&lt;p&gt;What got me started on this journey was discovering that &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mintlify.com&#x2F;blog&#x2F;generate-mcp-servers-for-your-docs&quot;&gt;Mintlify offers MCP servers&lt;&#x2F;a&gt; for all their hosted documentation sites. I saw this but didn’t really understand what it meant or how to use it. Can I curl an MCP server? How do I debug when things go wrong? What does the development workflow actually look like?&lt;&#x2F;p&gt;
&lt;p&gt;I decided to explore using some of these Mintlify-hosted MCP servers to build a documentation research agent that will provide answers across several different tools I use.&lt;&#x2F;p&gt;
&lt;p&gt;I went into this just trying to learn the technical details, but I walked away genuinely excited. Even though I knew theoretically what MCP should do, watching an agent I created intelligently decide “I need to search Mintlify docs AND Cursor docs for this comparison question” and then synthesize the results was pretty magical.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;quick-review-clients-vs-servers&quot;&gt;Quick review: clients vs. servers&lt;&#x2F;h2&gt;
&lt;p&gt;To quickly recap:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;MCP server&lt;&#x2F;strong&gt;: exposes tools&#x2F;resources that an AI can use. In this case, I’m using remote-hosted MCP servers that Mintlify sets up for docs sites. &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.pulsemcp.com&#x2F;servers&quot;&gt;There’s a large list of other MCP servers here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;MCP client&lt;&#x2F;strong&gt;: the AI application that connects to and uses MCP servers (like Claude Desktop, Cursor, Windsurf, or one you build yourself).&lt;&#x2F;p&gt;
&lt;p&gt;You configure the client to know about servers, then ask the AI to do things, and it figures out which tools to use.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-2&#x2F;addbe398-b03f-4275-88ae-cbf2cefb2d09.png&quot; alt=&quot;Diagram showing MCP architecture: clients like Claude Desktop and Cursor connecting to MCP servers that expose tools&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-just-get-it-working-plug-into-an-ai-app&quot;&gt;1. Just get it working: plug into an AI app&lt;&#x2F;h2&gt;
&lt;p&gt;This is probably the best way to get moving. Here, I’ll use Cursor as an example, but the same approach works with Claude Desktop or other AI clients. I wanted to use remote servers to test this because I didn’t want to focus on setting up local servers.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Setup for Cursor:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create an &lt;code&gt;mcp.json&lt;&#x2F;code&gt; file in Cursor. &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.cursor.com&#x2F;en&#x2F;context&#x2F;mcp#configuration-locations&quot;&gt;This can be project-specific or global&lt;&#x2F;a&gt;. Cursor also has some &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.cursor.com&#x2F;en&#x2F;context&#x2F;mcp&quot;&gt;one-click integrations listed&lt;&#x2F;a&gt; so you don’t even need to manually update a file.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Add the remote URL for any MCP servers&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Ask the AI to do things&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Here’s my &lt;code&gt;~&#x2F;.cursor&#x2F;mcp.json&lt;&#x2F;code&gt;, which connects to three different MCP servers that contain docs for the different tools I use (WRITER, Mintlify, and Cursor):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;mcpServers&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Writer Docs&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;dev.writer.com&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Mintlify&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;www.mintlify.com&#x2F;docs&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Cursor&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;docs.cursor.com&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can verify these URLs work by visiting them in your browser. They should return JSON describing the server capabilities.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;try-it-out&quot;&gt;Try it out&lt;&#x2F;h3&gt;
&lt;p&gt;I was initially confused about what to do next. How do you test it? How do you know it’s working? It’s actually a lot simpler than I was making it in my head.&lt;&#x2F;p&gt;
&lt;p&gt;Once you add your servers to Cursor, it’s just a regular chat. MCP servers are essentially just tools your AI can use. It’s like any other function you might give an AI, like one that calculates averages or fetches weather data.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-2&#x2F;d06a1c34-219e-48c9-89a3-b4f7dc54cec8.png&quot; alt=&quot;Screenshot of Cursor chat showing MCP tools being used to search documentation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The magic happens in conversation:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Ask: “What tools do you have available?”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;The AI shows you what it can access across all your tool documentation&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Ask: “How do I implement authentication across Writer, Mintlify, and Cursor?”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;The AI searches each documentation server and synthesizes the results&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Ask: “What are the API rate limits for each of these tools?”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;The AI compares approaches across all three platforms&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;At this point, you might be satisfied. It works, it’s useful, and you don’t need to understand the details. But I was still trying to figure out what was actually happening under the hood.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-what-s-actually-going-on-taking-a-look-with-curl&quot;&gt;2. What’s actually going on? Taking a look with curl&lt;&#x2F;h2&gt;
&lt;p&gt;My first instinct was: “Can I just curl this thing?” I was still confused about transport and protocols and what’s actually happening for the request&#x2F;response pattern. So tried it out.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;server-inspection&quot;&gt;Server inspection&lt;&#x2F;h3&gt;
&lt;p&gt;If you visit &lt;code&gt;https:&#x2F;&#x2F;www.mintlify.com&#x2F;docs&#x2F;mcp&lt;&#x2F;code&gt; in a browser, you can see the server capabilities. It shows you the available tools, their descriptions, and parameter schemas, similar to an OpenAPI spec.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-2&#x2F;3271f791-36f1-4bba-ae88-08735eeccd8d.png&quot; alt=&quot;Browser showing Mintlify MCP server capabilities as JSON, including available tools and their schemas&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;But can I actually call the tools with familiar HTTP tools?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;calling-tools-with-curl&quot;&gt;Calling tools with curl&lt;&#x2F;h3&gt;
&lt;p&gt;This is where I learned MCP isn’t just REST with a different name. It uses &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.jsonrpc.org&#x2F;specification&quot;&gt;JSON-RPC 2.0&lt;&#x2F;a&gt;, which requires a more structured approach.&lt;&#x2F;p&gt;
&lt;p&gt;After some trial and error and checking out the&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;modelcontextprotocol&#x2F;modelcontextprotocol&#x2F;blob&#x2F;main&#x2F;schema&#x2F;2025-06-18&#x2F;schema.ts&quot;&gt; MCP specification&lt;&#x2F;a&gt;, here’s what works:&lt;&#x2F;p&gt;
&lt;p&gt;First, you need to initialize a connection and send an initialize request using &lt;code&gt;jsonrpc 2.0&lt;&#x2F;code&gt;. This &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;modelcontextprotocol&#x2F;modelcontextprotocol&#x2F;blob&#x2F;main&#x2F;schema&#x2F;2025-06-18&#x2F;schema.ts#L172&quot;&gt;initialize method is from the MCP&lt;&#x2F;a&gt; spec, so it’s standard for all MCP requests.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# 1. Initialize the connection
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;curl -X&lt;&#x2F;span&gt;&lt;span&gt; POST &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;www.mintlify.com&#x2F;docs&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  -H &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Content-Type: application&#x2F;json&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  -H &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Accept: application&#x2F;json, text&#x2F;event-stream&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  -d &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;jsonrpc&amp;quot;: &amp;quot;2.0&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;id&amp;quot;: 1,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;method&amp;quot;: &amp;quot;initialize&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;params&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      &amp;quot;protocolVersion&amp;quot;: &amp;quot;1.0&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      &amp;quot;capabilities&amp;quot;: {},
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      &amp;quot;clientInfo&amp;quot;: {&amp;quot;name&amp;quot;: &amp;quot;test-client&amp;quot;, &amp;quot;version&amp;quot;: &amp;quot;0.1&amp;quot;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Success! It returns &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Server-sent_events&#x2F;Using_server-sent_events&quot;&gt;Server-Sent Events&lt;&#x2F;a&gt; with the actual available tools:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;event: message
&lt;&#x2F;span&gt;&lt;span&gt;data: {&amp;quot;result&amp;quot;:{&amp;quot;protocolVersion&amp;quot;:&amp;quot;2025-06-18&amp;quot;,&amp;quot;capabilities&amp;quot;:{&amp;quot;tools&amp;quot;:{&amp;quot;SearchMintlify&amp;quot;:{&amp;quot;name&amp;quot;:&amp;quot;SearchMintlify&amp;quot;,&amp;quot;description&amp;quot;:&amp;quot;Search across the Mintlify knowledge base to find relevant information...&amp;quot;,&amp;quot;method&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;pathTemplate&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;parameters&amp;quot;:[],&amp;quot;inputSchema&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;object&amp;quot;,&amp;quot;properties&amp;quot;:{&amp;quot;query&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;string&amp;quot;,&amp;quot;description&amp;quot;:&amp;quot;A query to search the content with.&amp;quot;}},&amp;quot;required&amp;quot;:[&amp;quot;query&amp;quot;]},&amp;quot;executionParameters&amp;quot;:[],&amp;quot;securityRequirements&amp;quot;:[],&amp;quot;operationId&amp;quot;:&amp;quot;MintlifyDefaultSearch&amp;quot;},&amp;quot;listChanged&amp;quot;:true}},&amp;quot;serverInfo&amp;quot;:{&amp;quot;name&amp;quot;:&amp;quot;Mintlify&amp;quot;,&amp;quot;version&amp;quot;:&amp;quot;1.0.0&amp;quot;}},&amp;quot;jsonrpc&amp;quot;:&amp;quot;2.0&amp;quot;,&amp;quot;id&amp;quot;:1}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, you can call a tool. You can see in the response above that there’s a tool called &lt;code&gt;SearchMintlify&lt;&#x2F;code&gt; that takes a required query argument.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;tools&#x2F;call&lt;&#x2F;code&gt; method is &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;modelcontextprotocol&#x2F;modelcontextprotocol&#x2F;blob&#x2F;main&#x2F;schema&#x2F;2025-06-18&#x2F;schema.ts#L172&quot;&gt;specifically defined within the MCP spec as well&lt;&#x2F;a&gt;. The LLM can see from this tools list what it needs to plug in to provide when it calls the tool.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# 2. Call a tool (note the exact tool name from the capabilities response)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;curl -X&lt;&#x2F;span&gt;&lt;span&gt; POST &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;www.mintlify.com&#x2F;docs&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  -H &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Content-Type: application&#x2F;json&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  -H &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Accept: application&#x2F;json, text&#x2F;event-stream&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  -d &lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;jsonrpc&amp;quot;: &amp;quot;2.0&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;id&amp;quot;: 2,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;method&amp;quot;: &amp;quot;tools&#x2F;call&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    &amp;quot;params&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      &amp;quot;name&amp;quot;: &amp;quot;SearchMintlify&amp;quot;,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;      &amp;quot;arguments&amp;quot;: {&amp;quot;query&amp;quot;: &amp;quot;API authentication&amp;quot;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;    }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And when it works, you get back:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;event: message
&lt;&#x2F;span&gt;&lt;span&gt;data: {&amp;quot;result&amp;quot;:{&amp;quot;content&amp;quot;:[{&amp;quot;type&amp;quot;:&amp;quot;text&amp;quot;,&amp;quot;text&amp;quot;:&amp;quot;Title: Authentication\nLink: https:&#x2F;&#x2F;www.mintlify.com&#x2F;docs&#x2F;api-playground&#x2F;mdx&#x2F;authentication\nContent: You can set authentication parameters to let users use their real API keys...&amp;quot;}]}}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;here-s-what-the-full-flow-looks-like&quot;&gt;Here’s what the full flow looks like:&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-2&#x2F;06b50051-6c2a-4881-bb5c-7a23f820e32e.png&quot; alt=&quot;Sequence diagram showing the MCP request flow: curl sends initialize request, server returns capabilities, curl calls tool, server returns results&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This was really helpful for me to understand what was part of the MCP spec and what’s happening when an agent uses an MCP tool. I wouldn’t use curl when implementing an MCP client, but it’s useful if you want to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Verify server availability and capabilities&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Test the initialization handshake&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Debug tool calls and see exact response formats&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Understand authentication requirements&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To actually build a client, you’d want a client library.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-building-it-myself-an-intelligent-docs-support-agent&quot;&gt;3. Building it myself: an intelligent docs support agent&lt;&#x2F;h2&gt;
&lt;p&gt;Now that I understand the protocol better, I wanted to try to build my own agent that could work with these MCP servers.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-amazon-strands-with-built-in-mcp-support&quot;&gt;Using Amazon Strands with built-in MCP support&lt;&#x2F;h3&gt;
&lt;p&gt;I chose to use Amazon &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;strandsagents.com&#x2F;latest&#x2F;&quot;&gt;Strands&lt;&#x2F;a&gt; for this part, because I like the interface, it has integrations with &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;strandsagents.com&#x2F;latest&#x2F;documentation&#x2F;docs&#x2F;user-guide&#x2F;concepts&#x2F;model-providers&#x2F;writer&quot;&gt;WRITER&lt;&#x2F;a&gt; and lots of other model providers, and it happens to have MCP support built in.&lt;&#x2F;p&gt;
&lt;p&gt;With Strands, you can swap out WRITER models for other model providers if you use something else, like Anthropic or OpenAI.&lt;&#x2F;p&gt;
&lt;p&gt;To follow along, first you’d install Strands with Writer (or your model provider, you can &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;strandsagents.com&#x2F;latest&#x2F;documentation&#x2F;docs&#x2F;user-guide&#x2F;concepts&#x2F;model-providers&#x2F;amazon-bedrock&#x2F;&quot;&gt;find the info in the Strands docs&lt;&#x2F;a&gt;) and tool support:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;pip&lt;&#x2F;span&gt;&lt;span&gt; install &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;strands-agents[writer]&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; strands-agents-tools
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You’d also need an API key for the model provider you’re using.&lt;&#x2F;p&gt;
&lt;p&gt;Below I’ll walk through the agent I built to answer questions about the three different tools I’m using by pulling from their docs MCP servers. It creates connections to multiple MCP servers, combines all their tools, and lets the AI agent decide which ones to use for each question.&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;sarahcstringer&#x2F;9d07e30fb3a4a6e2b5474266852284a6&quot;&gt; See the full code in this Gist.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;First, import the dependencies.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;os
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;mcp.client.streamable_http &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;streamablehttp_client
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;strands &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;Agent
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;strands.models.writer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;WriterModel
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;strands.tools.mcp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;MCPClient
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The next section configures the Strands Agent. It creates streamable http connections to the three servers, configures the model to use &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;writer.com&#x2F;blog&#x2F;long-context-palmyra-x5&#x2F;&quot;&gt;Palmyra X5&lt;&#x2F;a&gt;, and stores the system prompt for the agent.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;StrandsResearchAgent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;__init__&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Initialize MCP clients for documentation servers&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Create MCP clients for each documentation server
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.writer_client = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MCPClient&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;streamablehttp_client&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;dev.writer.com&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.mintlify_client = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MCPClient&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;streamablehttp_client&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;www.mintlify.com&#x2F;docs&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.cursor_client = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MCPClient&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;streamablehttp_client&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;docs.cursor.com&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Set up Palmyra X5 model
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.model = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;WriterModel&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;client_args&lt;&#x2F;span&gt;&lt;span&gt;={&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;api_key&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: os.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;getenv&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;WRITER_API_KEY&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)},
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;model_id&lt;&#x2F;span&gt;&lt;span&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;palmyra-x5&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;temperature&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0.3&lt;&#x2F;span&gt;&lt;span&gt;,  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Lower temperature for more focused responses
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# System prompt
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.system_prompt = &amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        You are a research assistant with access to documentation from Writer, Mintlify, and Cursor.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        When answering questions:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        1. Determine which documentation sources are most relevant to the question
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        2. Search those sources strategically (don&amp;#39;t search everything unless necessary)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        3. Synthesize information from multiple sources when helpful
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        4. Provide practical, actionable answers with examples when possible
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        Be efficient with tool usage - only search the documentation that&amp;#39;s relevant to the specific question.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;        &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The script then starts the chat loop. It opens the connections to the MCP servers so they’re available during the context of the agent chat. Then it provides the list of all the available tools from the MCP clients to the agent, along with the model and system prompt defined earlier.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;chat_loop&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Interactive chat loop for research questions&amp;quot;&amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;🤖 Strands Research Agent (Palmyra X5) ready!&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Ask me about Writer, Mintlify, or Cursor documentation.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Type &amp;#39;quit&amp;#39; to exit.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Keep MCP clients connected for the entire chat session to maintain conversation history
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;with &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.mintlify_client, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.cursor_client, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.writer_client:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Combine tools from all servers
&lt;&#x2F;span&gt;&lt;span&gt;        all_tools = (
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.writer_client.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;list_tools_sync&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;            + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.mintlify_client.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;list_tools_sync&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;            + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.cursor_client.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;list_tools_sync&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Create a Strands agent with Palmyra X5 and all MCP tools
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# This agent will maintain conversation history across all questions
&lt;&#x2F;span&gt;&lt;span&gt;        agent = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Agent&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.model,  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Palmyra X5 model defined in __init__
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tools&lt;&#x2F;span&gt;&lt;span&gt;=all_tools,  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Open MCP tool connections defined above
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;system_prompt&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.system_prompt,  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# System prompt defined in __init__
&lt;&#x2F;span&gt;&lt;span&gt;        )
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the part that really got me excited. I just pass the connections to the Strands agent as tools and it figures out the rest. I don’t have to write any logic for “when should you search Writer docs vs Cursor docs” - the AI agent sees the available tools and their descriptions and makes intelligent decisions about which ones to use.&lt;&#x2F;p&gt;
&lt;p&gt;The last part is running the chat loop, where the script accepts input from the user, passes it back to the model, and streams the final results back to stdout.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Use the same agent for all questions to maintain conversation history
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    question = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;You: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strip&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;question.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lower&lt;&#x2F;span&gt;&lt;span&gt;() in [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;quit&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;exit&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;]:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;break
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;question:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;🔍 Researching...&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;try&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# By default, the agent streams the response to stdout
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# More info: https:&#x2F;&#x2F;strandsagents.com&#x2F;latest&#x2F;documentation&#x2F;docs&#x2F;user-guide&#x2F;concepts&#x2F;streaming&#x2F;callback-handlers&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;agent&lt;&#x2F;span&gt;&lt;span&gt;(question)
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;except &lt;&#x2F;span&gt;&lt;span&gt;Exception &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span&gt;e:
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;❌ Error: &lt;&#x2F;span&gt;&lt;span&gt;{e}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Usage examples
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;():
&lt;&#x2F;span&gt;&lt;span&gt;    agent = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;StrandsResearchAgent&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Start interactive chat
&lt;&#x2F;span&gt;&lt;span&gt;    agent.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;chat_loop&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;__name__ == &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;__main__&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;sarahcstringer&#x2F;9d07e30fb3a4a6e2b5474266852284a6&quot;&gt;See the full code here.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here’s an example of using it:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-2&#x2F;91ac2069-d302-4a0e-9bcb-deba7f96dbd1.png&quot; alt=&quot;Terminal showing the Strands research agent starting up and connecting to multiple MCP documentation servers&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I ask&lt;&#x2F;strong&gt;: “What are the Mintlify and Cursor API rate limits?”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Palmyra X5 analyzes&lt;&#x2F;strong&gt;: Understands this is about API development and rate limiting&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Smart tool selection&lt;&#x2F;strong&gt;: Decides to search Cursor and Mintlify docs for API rate limiting info&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Targeted searches&lt;&#x2F;strong&gt;: Makes specific queries like “API rate limiting”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Intelligent synthesis&lt;&#x2F;strong&gt;: Agent combines results and provides practical guidance&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-2&#x2F;71932218-124b-4e1d-a889-d8e52a5f3077.png&quot; alt=&quot;Agent response synthesizing API rate limit information from both Mintlify and Cursor documentation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What’s so cool is that I never told it explicitly “search both Mintlify and Cursor for this question“ or told it anything more than where to find the servers. It figured out from the question that it needed information from both sources, searched them independently, and synthesized the results.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bottom-line&quot;&gt;The bottom line&lt;&#x2F;h2&gt;
&lt;p&gt;MCP is still early, and the debugging experience reflects that. You’ll hit rough edges that don’t exist with traditional APIs. But when it works, it’s genuinely useful, especially for adaptive AI workflows where you want the LLM to discover and orchestrate tools on its own. It’s funny how quickly I want from “why do I even want MCP?” to “what? You don’t have an MCP server for that??”.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;side-notes-auth-and-debugging&quot;&gt;Side notes: auth and debugging&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;auth&quot;&gt;Auth&lt;&#x2F;h3&gt;
&lt;p&gt;One thing you might notice is that some MCP tools require authentication to external APIs. With MCP, authentication is just another fill-in-the blank; the MCP server tells the client which credentials it needs, and the client provides them for the server to then handle the actual auth.&lt;&#x2F;p&gt;
&lt;p&gt;Interactive clients like Cursor or Claude can prompt you for API keys or other credentials when needed. If you’re building your own programmatic agent that needs credentials, you’d need to provide them upfront. You can handle this by passing authentication headers when creating the MCP client:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.writer_client = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MCPClient&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;streamablehttp_client&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;dev.writer.com&#x2F;mcp&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span&gt;={&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Authorization&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Bearer &lt;&#x2F;span&gt;&lt;span&gt;{writer_api_key}&amp;quot;}))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is one area where the MCP ecosystem is actively evolving. The MCP specification now includes comprehensive OAuth 2.1 support for HTTP-based transports, with proper authorization flows, dynamic client registration, and standardized discovery mechanisms. However, the practical reality varies significantly between different implementations.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;debugging&quot;&gt;Debugging&lt;&#x2F;h3&gt;
&lt;p&gt;When things go wrong (and they will), you don’t have as many options for debugging as you typically would when working with traditional APIs, especially if the MCP server is hosted remotely.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Available now:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;curl for protocol-level debugging&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;modelcontextprotocol&#x2F;inspector&quot;&gt;MCP Inspector&lt;&#x2F;a&gt; for local development&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Server capability inspection via HTTP&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Client-side logs (if your client exposes them)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The community is working on better tooling like MCP DevTools, protocol analyzers, and VS Code extensions that are being developed for MCP debugging. The ecosystem is moving fast, so hopefully the debugging experience will improve significantly over the next few months. I’m really excited about the new tooling around observability being added to the overall AI ecosystem.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;ol&gt;
&lt;li&gt;Mintlify has been very forward-thinking about AI integration, with MCP servers and several other AI-first features. Instead of treating AI integration as an afterthought, they’ve made it a core feature. It’s particularly useful when you’re building agents that need to work across multiple tools and platforms, like the research agent we built above.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
      <item>
          <title>Learn MCP with me, part 1: what is this even for?</title>
          <pubDate>Mon, 08 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/learn-mcp-part-1/</link>
          <guid>https://sdeaton.com/blog/learn-mcp-part-1/</guid>
          <description xml:base="https://sdeaton.com/blog/learn-mcp-part-1/">&lt;p&gt;I wrote this post because I found myself struggling with a very specific question: Why MCP when we already have APIs and so many ways to communicate programmatically?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;&#x2F;strong&gt; MCP isn’t replacing APIs; it’s a guardrail layer that makes LLMs less terrible at using them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-mcp-when-we-already-have-apis&quot;&gt;Why MCP when we already have APIs?&lt;&#x2F;h2&gt;
&lt;p&gt;I come from an API development background: I’m comfortable working with, writing, and documenting APIs. Since MCP came out, I’ve been side-eyeing it like, “mhmm, yes I get it, it’s just another API.”&lt;&#x2F;p&gt;
&lt;p&gt;But when I started to dig in, I couldn’t get past the question: what problem does this solve that REST + OpenAPI don’t already cover?&lt;&#x2F;p&gt;
&lt;p&gt;We’ve already solved “connect different systems” multiple times. We’ve got REST, GraphQL, gRPC, even SOAP, and so many others. And then MCP shows up in 2024 saying “Here’s a standardized way for AI to connect to external tools.” What problem is it solving that REST + OpenAPI doesn’t already solve? It felt like the &lt;a rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;927&#x2F;&quot;&gt;XKCD comic&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-1&#x2F;b8b6e40d-3a45-4241-b988-aea108f47dcd.png&quot; alt=&quot;XKCD comic about how standards proliferate: 14 competing standards leads to a new universal standard, resulting in 15 competing standards&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-answer-llms-suck-at-using-apis&quot;&gt;The answer: LLMs suck at using APIs&lt;&#x2F;h2&gt;
&lt;p&gt;After reading more, here’s what I found: LLMs are terrible API clients. They hallucinate parameter names, mishandle nested responses, and struggle with authentication patterns.&lt;&#x2F;p&gt;
&lt;p&gt;Chatting with an LLM can sometimes feel so magical that I forget they’re all just probabilistic token generators. They’re not reading the OpenAPI spec or docs and using logic and reasoning to determine the correct API response. They’re coming up with the most common patterns they’ve seen in training, and sometimes those aren’t actually correct.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-mcp-fixes-this&quot;&gt;How MCP fixes this&lt;&#x2F;h2&gt;
&lt;p&gt;MCP doesn’t replace APIs. It inserts a competency layer between LLMs and APIs. Here’s what it adds:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Standardized tool interface&lt;&#x2F;strong&gt;: LLM just fills in parameters, no guessing endpoint names or auth headers.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clean, structured responses&lt;&#x2F;strong&gt;: Instead of wrestling with 50-field JSON objects, the LLM gets simplified outputs.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authentication abstraction&lt;&#x2F;strong&gt;: Tokens, OAuth, headers are all handled server-side, so the LLM doesn’t need to deal with any of that, it just needs to be handed the correct keys.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Discovery built-in&lt;&#x2F;strong&gt;: The LLM can ask “What tools are available?” rather than needing a preloaded spec.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-1&#x2F;2a43ce7f-87f7-4b43-b128-210a781a3622.png&quot; alt=&quot;Diagram showing MCP as a layer between AI applications and external tools, providing standardized interfaces and authentication handling&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Instead of fragile API calls, the LLM gets a structured template it just fills in. It gets information back in a reliable, standardized way. It doesn’t have to guess that a GitHub API needs &lt;code&gt;repository_name&lt;&#x2F;code&gt; vs. &lt;code&gt;repo_name&lt;&#x2F;code&gt;; MCP ensures it always gets the right parameter structure. Then you can plug that into agents or tools like Cursor or Claude so they can interact with those systems.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;follow-up-i-m-still-confused&quot;&gt;Follow up: I’m still confused&lt;&#x2F;h3&gt;
&lt;p&gt;I still had a few questions. Mainly, if an LLM can’t just look at an OpenAPI spec and get it right, why would it magically succeed with MCP?&lt;&#x2F;p&gt;
&lt;p&gt;The key is that MCP doesn’t rely on the model &lt;em&gt;parsing and reasoning&lt;&#x2F;em&gt; over a giant spec. Instead, the MCP runtime hands the LLM a structured template for each tool, enforces the parameters, and validates the output. The model isn’t “learning MCP” from training. It’s just filling in blanks inside a schema the runtime controls. That shift from “read this 50-page manual and hope you guess correctly” to “fill out this form, we’ll handle the rest” is what makes MCP reliably work where OpenAPI falls apart.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;learn-mcp-part-1&#x2F;6187b950-3160-45af-8414-236eab01944e.png&quot; alt=&quot;Diagram illustrating how MCP transforms complex API interactions into simple fill-in-the-blank templates for LLMs&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;when-you-would-want-to-use-mcp&quot;&gt;When you would want to use MCP&lt;&#x2F;h2&gt;
&lt;p&gt;One of the biggest errors I’ve seen with people using AI is overengineering a solution. Sometimes it’s going to be a lot more reliable and straightforward to just integrate with one API than add a bunch of MCP servers.&lt;&#x2F;p&gt;
&lt;p&gt;That said, there are definitely cases when MCP is the right choice:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI agents that adapt&lt;&#x2F;strong&gt;: a code review assistant that chooses tools based on changed files.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Chat interfaces with tool access&lt;&#x2F;strong&gt;: asking “Any errors in the auth module?” and having the LLM query Sentry + GitHub automatically.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cross-system workflows&lt;&#x2F;strong&gt;: coordinating multiple tools without manually programming the details for each one.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The cross-systems workflows use case is where it gets exciting: the ability to coordinate across multiple systems without hardcoding every single step yourself.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine an AI assistant helping with customer support triage. It could:&lt;&#x2F;p&gt;
&lt;p&gt;• Pull recent Zendesk tickets that mention a specific issue&lt;&#x2F;p&gt;
&lt;p&gt;• Cross-reference related documentation in Confluence or issues in Jira&lt;&#x2F;p&gt;
&lt;p&gt;• Post a suggested reply draft in Slack for a support agent to review&lt;&#x2F;p&gt;
&lt;p&gt;With APIs, you’d have to write and maintain that integration flow. With MCP, the agent can discover tools at runtime and decide which to call, in what order. It’s less “pre-baked script” and more “AI reasoning with a toolkit.”&lt;&#x2F;p&gt;
&lt;p&gt;That’s MCP’s sweet spot: enabling adaptive orchestration across services.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;when-you-don-t-need-mcp&quot;&gt;When you don’t need MCP&lt;&#x2F;h2&gt;
&lt;p&gt;There are many cases when MCP and even agentic AI is overkill:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You’re building deterministic workflows (cron jobs, fixed reports)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;You’re writing code yourself (you don’t need a middleman)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;It’s a single integration (REST is simpler)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;You need full debugging control (direct API calls are transparent)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;the-bottom-line&quot;&gt;The bottom line&lt;&#x2F;h2&gt;
&lt;p&gt;MCP isn’t a revolution. It’s an evolution. It wraps existing API practices in guardrails so LLMs can use them without faceplanting. But, it’s still pretty exciting when you start working with it and seeing how quickly you can add integrations into an AI app.&lt;&#x2F;p&gt;
&lt;p&gt;Now that I’m convinced MCP is worthwhile, I’ll dig into how to actually connect to MCP servers. That’s coming soon.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>We all fall down: notes from an ordinary outage</title>
          <pubDate>Sat, 06 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://sdeaton.com/blog/we-all-fall-down/</link>
          <guid>https://sdeaton.com/blog/we-all-fall-down/</guid>
          <description xml:base="https://sdeaton.com/blog/we-all-fall-down/">&lt;p&gt;I love incidents. They’re like mini soap operas, tech stacks buckling under ordinary circumstances for unpredictable reasons, cascading failures, humans rushing to figure out what happened. So normal and each time so unexpected.&lt;&#x2F;p&gt;
&lt;p&gt;It’s been a while since I worked in SRE and have gotten to see a good incident up close. So when a friend who works in infra at a streaming company told me about one particular outage, I wanted to dig into the details and learn more. A regular video processing job kicked off and suddenly the whole cluster was stuck for five days.&lt;&#x2F;p&gt;
&lt;p&gt;It’s the kind of story that reminds me how the internet actually works: not as a gleaming futuristic system, but as a patchwork quilt of duct tape, YAML, and unfinished TODOs.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s my retelling, my best effort as an outsider through persistent questions.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;sidebar-what-even-is-kubernetes&quot;&gt;Sidebar: what even is Kubernetes?&lt;&#x2F;h2&gt;
&lt;p&gt;This story’s main character is Kubernetes. I’ve had the great fortune to not have to deal with Kubernetes (a.k.a “k8s”) directly. For those like me, here’s my best take on it: it’s like a manager for your apps. Instead of one person running one program on one computer, Kubernetes spreads workloads across many computers, makes sure things restart if they crash, and helps scale up when traffic spikes. It’s kind of like an orchestra conductor making sure dozens of instruments stay in sync, except the instruments are apps, and they sometimes set themselves on fire.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;how-it-started&quot;&gt;How it started&lt;&#x2F;h2&gt;
&lt;p&gt;One of the teams at my friend’s company submitted a large video processing job for routine batch encoding work. These kinds of jobs fan out into tens of thousands of pods: little containers that encode video segments in parallel. There was nothing different about this job except that it was a bit bigger than usual.&lt;&#x2F;p&gt;
&lt;p&gt;But then things jammed. The first clue came when their API gateway started refusing requests with mysterious error codes. Behind the scenes, Kubernetes was taking longer than 60 seconds to answer any kind of question, even one like “hey, what pods are running right now?”&lt;&#x2F;p&gt;
&lt;p&gt;That delay alone was enough to break things, because their cloud provider gives up commands after 60 seconds. So from the outside as a user, the whole system looked frozen. Someone would try to kick off a new job, notice it wasn’t running, try to query what was going on, and nothing would happen.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;sidebar-pods-in-plain-english&quot;&gt;Sidebar: pods in plain English&lt;&#x2F;h2&gt;
&lt;p&gt;Pods are the smallest “work unit” in Kubernetes. Imagine every task you want to run, like “encode this 30-second clip,” gets its own little container. That’s a pod. They’re disposable, lightweight, and meant to come and go constantly. Pods help things run in parallel and keep discrete workloads isolated.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;slack-pings-and-silent-dashboards&quot;&gt;Slack pings and silent dashboards&lt;&#x2F;h2&gt;
&lt;p&gt;This part of the infra team doesn’t rely heavily on alerts. Most of their workloads are batch encoding jobs, which should retry and recover automatically. The first notice of something wrong came in Slack with messages like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;“Hey, the encoding job isn’t finishing…”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;“Is the processing cluster down?”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;“The batch queue seems stuck”&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So the infra team went to the dashboards. But, nothing looked wrong.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-metrics-that-lied&quot;&gt;The metrics that lied&lt;&#x2F;h2&gt;
&lt;p&gt;Everything seemed fine when they went to look at the metrics. That’s when they remembered that their monitoring system, Prometheus, was configured to “assume everything’s fine” for 30 minutes if it stopped hearing from a service.&lt;&#x2F;p&gt;
&lt;p&gt;If something died, Prometheus just kept reporting its last known state for 30 minutes, rather than showing that the service had entirely stopped sending metrics. To the dashboard, it looked like video encoding was proceeding normally. To the humans, it meant they had no actual insight into what or where things were breaking as the batch processing queue backed up.&lt;&#x2F;p&gt;
&lt;p&gt;This was a known issue with their monitoring system, but it turns out that the engineer who started fixing it had left for another job, and the half-complete system stayed behind, quietly misleading everyone.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-missing-gatekeeper&quot;&gt;The missing gatekeeper&lt;&#x2F;h2&gt;
&lt;p&gt;Eventually, after lots of digging, they discovered the real culprit: Kyverno, the “gatekeeper” service that approves or rejects new pods, had vanished.&lt;&#x2F;p&gt;
&lt;p&gt;It hadn’t crashed. It’s not that it just had to spin up again. It had been evicted entirely.&lt;&#x2F;p&gt;
&lt;p&gt;Why? Because another service from the security team had been given higher priority than even critical system services. So when resources got tight, Kubernetes politely kicked Kyverno out to make room.&lt;&#x2F;p&gt;
&lt;p&gt;In this case, the security team hadn’t coordinated with the infra team when they set that priority. Infra assumed it was just another workload, not something that could outrank the cluster’s most essential services.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine a bouncer who has to check every person before they can enter the club getting kicked out when the club gets busy by someone shouting “FOR SECURITY REASONS.”&lt;&#x2F;p&gt;
&lt;p&gt;No Kyverno means no new pods, including Kyverno itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-long-slog&quot;&gt;The long slog&lt;&#x2F;h2&gt;
&lt;p&gt;Part of the reason this outage dragged on was that Kyverno itself hadn’t been set up with any safety nets. Only one copy was running instead of multiple replicas because no one had updated the default setting from &lt;code&gt;replicas: 1&lt;&#x2F;code&gt;. If there had been replicas, there could have been another copy of Kyverno available to let in the evicted one. And, there were no rules in place to prevent it from being kicked out.&lt;&#x2F;p&gt;
&lt;p&gt;So when Kubernetes needed to free up space and Kyverno went away, with it went the entire cluster’s ability to process encoding jobs.&lt;&#x2F;p&gt;
&lt;p&gt;Not having done Kubernetes work myself, I wondered why they couldn’t just delete the problematic jobs that started this mess. Well, apparently the jobs weren’t just sitting in the queue. They were part of Kubernetes’ Job and Pod system, which is designed to retry until success.&lt;&#x2F;p&gt;
&lt;p&gt;If you delete one, the Job controller notices: “Oh, that pod disappeared, but the job isn’t done yet” and it immediately schedules a new one. And in this outage, the admission controller was down, which meant nothing new could start anyway, so trying to “clean up” jobs didn’t help. They’d just boomerang back or stay pending, still clogging the scheduler’s brain.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;sdeaton.com&#x2F;blog&#x2F;we-all-fall-down&#x2F;885b17a1-659f-4469-836a-184b006a5c47.png&quot; alt=&quot;Diagram illustrating the Kubernetes job boomerang effect: deleting a pod causes the Job controller to immediately schedule a replacement&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-recovery&quot;&gt;The recovery&lt;&#x2F;h2&gt;
&lt;p&gt;The only way out was drastic: delete the admission rules that required Kyverno in the first place. That needed elevated permissions, so another team stepped in to rip out the safety checks temporarily.&lt;&#x2F;p&gt;
&lt;p&gt;Once Kyverno was no longer required, the cluster slowly brought itself back online:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The setup tasks could run.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Nodes became ready.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Kyverno was allowed back.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;They restored the admission rules, this time with better safeguards: multiple replicas, disruption budgets, and higher priority.&lt;&#x2F;p&gt;
&lt;p&gt;The encoding pipeline limped back to life, and the batch processing queue finally started clearing.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;sidebar-why-removing-admission-rules-is-a-big-deal&quot;&gt;Sidebar: why removing admission rules is a big deal&lt;&#x2F;h2&gt;
&lt;p&gt;Deleting admission rules didn’t sound that dramatic to me – just get rid of a few filters to get things back to normal. But it’s closer to removing the turnstiles at a subway station. Normally, those gates check every passenger’s ticket, which slows things down slightly, but ensures everyone who enters is allowed. Without them, trains move faster at first, but freeloaders and chaos slip in.&lt;&#x2F;p&gt;
&lt;p&gt;That’s what happened here: taking down the rules let the cluster recover, but it also meant anything (good or bad) could sneak through until order was restored.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;lessons&quot;&gt;Lessons&lt;&#x2F;h2&gt;
&lt;p&gt;This whole saga was a reminder that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Small defaults matter. Forgetting to set “run 3 copies of this critical service” can take down a whole processing pipeline.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Priorities collide. One team’s well-intentioned security tweak can accidentally evict the very thing keeping the system alive.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Observability isn’t optional. Dashboards that lie are worse than dashboards that scream.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Complex systems fail in loops. Once you get stuck in a cycle (“can’t start A without B, can’t start B without A”), recovery often requires outside help.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;in-the-end&quot;&gt;In the end&lt;&#x2F;h2&gt;
&lt;p&gt;What I loved about this outage was that it had less to do with machines failing and more to do with humans being human. Someone left a job and left their half-finished monitoring fix behind. Someone else set a priority without checking what it might evict. Someone forgot to configure replicas. All perfectly reasonable decisions in isolation.&lt;&#x2F;p&gt;
&lt;p&gt;It was a reminder that our digital world runs on millions of these small choices, most of them invisible until suddenly brought under the microscope of a blameless postmortem.&lt;&#x2F;p&gt;
&lt;p&gt;The miracle isn’t that things break. It’s that they hold together as often as they do. Most of the time, our duct tape and YAML and forgotten TODOs somehow keep the lights on. Until they don’t.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;ol&gt;
&lt;li&gt;When they’re not actively happening to me and I didn’t cause them by dropping a column in the production database.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
    </channel>
</rss>
