<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rizèl Scarlett</title>
    <description>The latest articles on Forem by Rizèl Scarlett (@blackgirlbytes).</description>
    <link>https://forem.com/blackgirlbytes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F302741%2F5ed2ea8e-056a-4065-9bed-57d24a96b607.png</url>
      <title>Forem: Rizèl Scarlett</title>
      <link>https://forem.com/blackgirlbytes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/blackgirlbytes"/>
    <language>en</language>
    <item>
      <title>Your Agent Sessions Belong in Your Codebase: Nullius in Verba</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Tue, 19 May 2026 08:56:46 +0000</pubDate>
      <link>https://forem.com/entire/your-agent-sessions-belong-in-your-codebase-nullius-in-verba-3ggd</link>
      <guid>https://forem.com/entire/your-agent-sessions-belong-in-your-codebase-nullius-in-verba-3ggd</guid>
      <description>&lt;p&gt;Your coding agent sessions belong in your codebase. Before I joined &lt;a href="https://entire.io/" rel="noopener noreferrer"&gt;Entire&lt;/a&gt;, the company building the infrastructure to bring your agent sessions into your code, I was already exploring this exact idea on my own.&lt;/p&gt;

&lt;p&gt;In January 2026, I participated in &lt;a href="https://genuary.art/" rel="noopener noreferrer"&gt;Genuary&lt;/a&gt;, a month-long creative coding challenge where artists, designers, and programmers make and share generative art based on a daily prompt. I used my coding agent, goose, to generate the creative code. For me, this was less of an exercise in creative coding and more of a self-taught lesson in orchestrating agents, since doing complex things with agents was on the rise.&lt;/p&gt;

&lt;p&gt;One of the things I built into my process was a repeating workflow where, after every session, my agent automatically committed the &lt;a href="https://github.com/blackgirlbytes/genuary2026/blob/main/genuary/days/day03/transcript.md" rel="noopener noreferrer"&gt;session transcript&lt;/a&gt; into the same repository that held the creative output. It wasn't elegant, because it was literally a huge transcript with every tool call mixed in and almost no structure to make it readable. I did it because some of the creations were so astonishingly beautiful that I wanted my agent and myself to be able to look back later and have enough context to reuse those same patterns for future challenges.&lt;/p&gt;

&lt;p&gt;Three months later, in March, I was working at a company that had built a far more elegant solution to the same problem. Instead of haphazardly dumping whole session transcripts, Entire saves each session as a series of navigable checkpoints. Each checkpoint is a snapshot of a meaningful moment in the session, capturing what the agent did, what changed in your code, and the reasoning that produced the change. Now after using Entire for a few months, I’m realizing that what I had treated as a nice-to-have for myself, I now see as a real necessity for engineers.&lt;/p&gt;

&lt;p&gt;I had this epiphany while doing what my job actually entails, which is advocating for developers. I started noticing a pattern across the developers and community members I talked to. While many of them wanted to track their agent sessions, they did not want those sessions to live in the same codebase. Some people felt their sessions were too embarrassing, full of mistakes or moments where they had been harsh with their agent, because all of us have lost patience with a coding agent that just refuses to understand us. Others felt the sessions were too private. Because Entire already supports &lt;a href="https://docs.entire.io/cli/checkpoints#checkpoint-remote" rel="noopener noreferrer"&gt;storing sessions in a separate repository&lt;/a&gt; and &lt;a href="https://docs.entire.io/security#secret-redaction-always-on" rel="noopener noreferrer"&gt;redacting secrets&lt;/a&gt; by default, I assumed we should be louder about that functionality.&lt;/p&gt;

&lt;p&gt;Surprisingly, one of my teammates disagreed with me. His philosophy was that agent sessions belong alongside your code by default, and that the discomfort developers would eventually go away. Because I am trained to empathize with developers, I initially felt this stance was dogmatic, and I struggled to see eye to eye. Over the past few weeks, though, the idea kept ringing through my mind, I see his perspective. &lt;/p&gt;

&lt;p&gt;Software engineering has never been about flawless first drafts. Our industry thrives precisely because we maintain a transparent, versioned track record of our technical evolution, and when engineers treat interactions with coding agents as ephemeral scratchpads, we end up ignoring a foundational principle of how software actually gets built. Every architectural and logical decision deserves a clear and traceable provenance, and right now that provenance is silently disappearing into chat windows.&lt;/p&gt;

&lt;p&gt;I did some historical research on how deeply embedded proof of work is in our industry, and I learned a lot about what happens when we abandon that proof of work. Here’s what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof of Work in Mathematics
&lt;/h2&gt;

&lt;p&gt;This foundation goes as far back as mathematics, the predecessor of computer science and software engineering. In the 1600s, mathematicians operated inside a genuinely toxic environment, settling disputes through public academic duels with brutal stakes. Winners kept their university chairs, while losers were publicly humiliated and often lost their livelihoods entirely.&lt;/p&gt;

&lt;p&gt;Because the consequences of losing were so severe, practitioners routinely hid their formulas and hoarded their methodologies. That culture of intense secrecy produced constant intellectual property disputes, redundant reverse-engineering, and a fragmented ecosystem that ended up stalling the progress of the entire discipline.&lt;/p&gt;

&lt;p&gt;The turning point came in the 1660s, when the Royal Society of London adopted a new motto, &lt;em&gt;Nullius in verba&lt;/em&gt;, which translates to "take nobody's word for it." From that point on, mathematicians had to publish their complete, step-by-step processes in academic journals rather than only presenting final conclusions. In exchange for that transparency, they received institutional validation and undisputed peer credit, and the field finally had a shared ledger of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof of Work in Software
&lt;/h2&gt;

&lt;p&gt;Three hundred years later, software engineering experienced a similar reckoning. In the 1960s, code was a tangible, physical artifact. Developers punched holes into cardboard cards, organized them into precise decks, and fed those decks into a mainframe. Version control was physical too, because changing a routine meant pulling a specific card out of the deck and slotting a new one into its place.&lt;/p&gt;

&lt;p&gt;Then, code moved to magnetic tape and hard disks and became digitally invisible. Multiple developers modified the same file and accidentally overwrote each other's changes without any shared source of truth. The industry's response was a slow march back toward visibility, moving from local file-locking systems like SCCS and RCS to centralized trackers like CVS and Subversion, and eventually to Git. Git decoupled development pipelines entirely through a distributed, non-linear architecture, but it was hard to use on its own, and it did not pick up real traction until GitHub layered a collaborative interface on top of it. That interface turned version history into a shared social ledger and defined the modern development workflow.&lt;/p&gt;

&lt;p&gt;The pattern is the same one the Royal Society set in motion three centuries earlier. Every time our industry has taken a leap forward, that leap has come from making invisible work visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invisible Agent Work
&lt;/h2&gt;

&lt;p&gt;Agentic workflows are becoming the primary engine of software production, but they're abstracting away our work at the same time.&lt;/p&gt;

&lt;p&gt;By committing only the final file output of an agent session, we aren't hiding our work the way 17th-century mathematicians did. But the effect is the same: we are back to delivering an end product while erasing the lineage of how it was reasoned into existence.&lt;/p&gt;

&lt;p&gt;The prompts you write, the specific files your agent reads, and the back-and-forth debugging it takes to get things right are not just logs. They are first-class development artifacts. When we strip them away from a pull request, the rest of our tooling, our reviewers, and our future selves are all left to take the resulting code at face value.&lt;/p&gt;

&lt;p&gt;That is exactly the position the Royal Society found unworkable in the seventeenth century, and there is no good reason to expect it will work for us either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nullius in Verba
&lt;/h2&gt;

&lt;p&gt;Including your unedited session next to your code feels vulnerable, but so does pushing your first commit to a public repo or opening your first pull request in an open source project. &lt;/p&gt;

&lt;p&gt;That discomfort is not a flaw in the workflow, it is the price of admission for a trustworthy, auditable record of how software actually gets built. &lt;em&gt;Nullius in verba&lt;/em&gt; is still the right principle 300+ years later: take nobody's word for it, not even your agent's. Let the work speak in the place where the work actually lives. That is the direction we are building toward at Entire: making the context behind agent-authored work as visible as the code itself. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Did you like this blog post?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Try out Entire: entire.io &lt;br&gt;
Join our Discord: &lt;a href="https://discord.gg/WUzRcQ5PX4" rel="noopener noreferrer"&gt;https://discord.gg/WUzRcQ5PX4&lt;/a&gt;&lt;br&gt;
Read our docs: docs.entire.io&lt;/p&gt;

&lt;p&gt;If you have additional thoughts, feel free to leave a comment!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>productivity</category>
      <category>entire</category>
    </item>
    <item>
      <title>How to Keep Entire Checkpoints Separate from Your Code</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Fri, 08 May 2026 08:02:39 +0000</pubDate>
      <link>https://forem.com/entire/how-to-keep-entire-checkpoints-separate-from-your-code-50a1</link>
      <guid>https://forem.com/entire/how-to-keep-entire-checkpoints-separate-from-your-code-50a1</guid>
      <description>&lt;p&gt;Storing a record of your agent sessions solves the biggest friction point for developers: limited context. On the surface, this may look like a dormant log, but Entire transforms those records into procedural memory. By default, the &lt;a href="https://docs.entire.io/cli/overview" rel="noopener noreferrer"&gt;Entire CLI&lt;/a&gt; stores your agent history right alongside your code. More specifically, it stores your &lt;a href="https://docs.entire.io/cli/checkpoints" rel="noopener noreferrer"&gt;checkpoints&lt;/a&gt;, snapshots of your prompts, agent transcripts, and the state of your work at each step, on a dedicated branch in the same repository called &lt;a href="https://docs.entire.io/glossary#checkpoints-branch" rel="noopener noreferrer"&gt;&lt;code&gt;entire/checkpoints/v1&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But as valuable as that memory is, it raises a valid question: What if I don’t want anyone else to see the conversations I have with my agent?&lt;/p&gt;

&lt;p&gt;We’ve heard a few consistent reasons why developers want to keep their agent history private:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The conversations are, frankly, a little embarrassing. (I’ve yelled at my agents before. I am not proud of it, but when tokens are few, so is my patience).
&lt;/li&gt;
&lt;li&gt;It can start to feel like surveillance from their employer.
&lt;/li&gt;
&lt;li&gt;It's a &lt;a href="https://docs.entire.io/security" rel="noopener noreferrer"&gt;privacy concern&lt;/a&gt;. Those conversations might include context their company doesn't want to share publicly or with external collaborators.
&lt;/li&gt;
&lt;li&gt;They want to keep your main repo lean and focused on source code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of those reasons resonate, you have two main paths to a more private workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push checkpoints to a separate private repo
&lt;/h2&gt;

&lt;p&gt;This is the sweet spot if you’re working on a public or shared project but still want a history that you (and maybe your trusted teammates) can access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a private repo for your checkpoints&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On GitHub, create an empty private repo with any name you want. In this example, we’ll use &lt;code&gt;myorg/checkpoints-private&lt;/code&gt;. This is where all your agent sessions will live. You don't need to add a README or initialize it. Entire will push the first checkpoint branch on its own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Point Entire at the new repo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From inside your project, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;entire configure &lt;span class="nt"&gt;--checkpoint-remote&lt;/span&gt; github:myorg/checkpoints-private
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The format is &lt;code&gt;provider:owner/repo&lt;/code&gt;. Today, &lt;code&gt;github&lt;/code&gt; is the supported provider. This writes the setting to &lt;a href="https://docs.entire.io/cli/configuration#project-settings" rel="noopener noreferrer"&gt;&lt;code&gt;.entire/settings.json&lt;/code&gt;&lt;/a&gt; under &lt;code&gt;strategy_options.checkpoint_remote&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strategy_options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"checkpoint_remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"repo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myorg/checkpoints-private"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your code will be stored in your main repo, and your agent sessions will go to your new private repo. You can read more about this in the &lt;a href="https://docs.entire.io/cli/checkpoints#checkpoint-remote" rel="noopener noreferrer"&gt;Checkpoint Remote docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep your agent sessions local
&lt;/h2&gt;

&lt;p&gt;If you want the highest level of privacy, you can keep your agent sessions local and opt out of pushing them to remote using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;entire configure &lt;span class="nt"&gt;--skip-push-sessions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This modifies &lt;code&gt;.entire/settings.json&lt;/code&gt; with the following values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strategy_options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"push_sessions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setting still allows you to store your sessions locally. For example, you can still &lt;a href="https://docs.entire.io/cli/checkpoints#benefits" rel="noopener noreferrer"&gt;rewind&lt;/a&gt;, look back at what happened, and use all the local features. However, because the checkpoints never get pushed to GitHub or any remote provider, you cannot retrieve them if you switch devices. Also, your teammates won’t have access to your checkpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if I accidentally paste a secret?
&lt;/h2&gt;

&lt;p&gt;We know that mistakes happen, so we have guardrails in place. Whether you store your history alongside your code, in a private repo, or on your local machine, Entire runs every session through a &lt;a href="https://docs.entire.io/security#secret-redaction-always-on" rel="noopener noreferrer"&gt;redaction pipeline&lt;/a&gt; before it hits git.&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://github.com/betterleaks/betterleaks" rel="noopener noreferrer"&gt;Betterleaks&lt;/a&gt; to automatically scrub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud credentials (AWS, GCP, Azure)
&lt;/li&gt;
&lt;li&gt;Source control tokens (GitHub, GitLab, Bitbucket)
&lt;/li&gt;
&lt;li&gt;Service keys (Stripe, Slack, Discord, Twilio)
&lt;/li&gt;
&lt;li&gt;Private keys (RSA, SSH, PGP)
&lt;/li&gt;
&lt;li&gt;Database connection strings with embedded passwords
&lt;/li&gt;
&lt;li&gt;Bearer tokens, JWTs, and high-entropy strings that look secret-shaped even if they don't match a known pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TLDR; Which one should you pick?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The Goal&lt;/th&gt;
&lt;th&gt;The Command&lt;/th&gt;
&lt;th&gt;Where data lives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Full Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Default&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same repo as your code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Private Collaboration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.entire.io/cli/checkpoints#checkpoint-remote" rel="noopener noreferrer"&gt;&lt;code&gt;--checkpoint-remote&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A separate private repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total Isolation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.entire.io/cli/commands#configure" rel="noopener noreferrer"&gt;&lt;code&gt;--skip-push-sessions&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Your local machine only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note: Redaction runs across all three. Whether your checkpoints live in your code repo, a private repo, or only on your laptop, &lt;a href="https://docs.entire.io/security#secret-redaction-always-on" rel="noopener noreferrer"&gt;secrets get scrubbed&lt;/a&gt; before they're written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We recognize that your agent workflow is going to look different based on who you are, the codebase you're in, and your team's unique security needs. Entire is built to adapt to those needs.&lt;/p&gt;

&lt;p&gt;Ready to dive deeper into configuring your setup? Check out our &lt;a href="https://docs.entire.io/cli/checkpoints#checkpoint-remote" rel="noopener noreferrer"&gt; documentation on checkpoint remote&lt;/a&gt; and &lt;a href="https://docs.entire.io/security" rel="noopener noreferrer"&gt;Security &amp;amp; Privacy docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the comments section, let me know: Do you even care if people see your agent history, or would you rather keep those transcripts private?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>entire</category>
      <category>programming</category>
    </item>
    <item>
      <title>Turning Agent History into Procedural Memory</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Sun, 03 May 2026 23:40:09 +0000</pubDate>
      <link>https://forem.com/blackgirlbytes/turning-agent-history-into-procedural-memory-37f8</link>
      <guid>https://forem.com/blackgirlbytes/turning-agent-history-into-procedural-memory-37f8</guid>
      <description>&lt;p&gt;For about a year, my primary coding agent was goose. Since I worked at Block and served as a Developer Advocate for the project, I was deeply embedded in its ecosystem. I contributed code and provided product feedback that shaped how it functioned.&lt;/p&gt;

&lt;p&gt;Then, I moved to a company called &lt;a href="//entire.io"&gt;Entire&lt;/a&gt; that provides the infrastructure for the agentic software development lifecycle. To do my job well, I have to dogfood our product across the agentic ecosystem. This means I am constantly switching between Claude Code, Codex, and other agents that support hooks to contribute to docs, investigate and resolve bugs, understand new features, and produce content.&lt;/p&gt;

&lt;p&gt;Switching between AI agents made me realize every agent has tradeoffs. Some are faster or more polished, but I find myself deeply missing a specific goose feature called recipes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Operational Glue
&lt;/h2&gt;

&lt;p&gt;Recipes are reusable, shareable workflows. At the core, they are YAML files that describe a process you want goose to run again. You can write the YAML files manually, but I always preferred the magic of clicking a button to package a successful session into a recipe.  &lt;/p&gt;

&lt;p&gt;My work in Developer Relations is creative, but it’s built on repeatable systems. For example, writing a blog post, building a code demo, creating a video are creative. The publishing process is not. Publishing a blog post involves a series of tiny, forgettable steps: checking the folder structure, adding the correct front matter, wiring up the metadata, dropping the image in the right asset folder, opening the PR. Each of these steps take a few minutes, but those minutes add up and become hours of operational glue. At Block, I automated as much of that as possible. I had goose generating release notes in CI/CD and creating documentation tickets in Asana. Some of these ran on a schedule, others I triggered manually. The point was always the same: if I found myself explaining a process to an agent more than once, it was an operational smell that needed to become a reusable asset.&lt;/p&gt;

&lt;p&gt;While my use cases focus on content and community, this pattern is universal. In many fields, employees find themselves frequently explaining the same sequence to an agent, so why not automate this into repeatable workflows?  &lt;/p&gt;

&lt;p&gt;For engineers, those repeated conversations may look like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upgrading a dependency safely
&lt;/li&gt;
&lt;li&gt;Bootstrapping a new microservice
&lt;/li&gt;
&lt;li&gt;Triaging a production error
&lt;/li&gt;
&lt;li&gt;Writing a design doc or RFC
&lt;/li&gt;
&lt;li&gt;Preparing a release PR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The inputs and thinking may vary, but the process: conventions, the file paths, the validation steps, the commands you run, the people you always notify stay the same. And this level of automation is necessary today where employers are demanding more output.&lt;/p&gt;

&lt;h2&gt;
  
  
  The System of Record
&lt;/h2&gt;

&lt;p&gt;I'm constantly jumping between different agents. Each one has its own process for automating workflows, but none of the automation tools hit the mark for me like goose did. While I don’t have access to my treasured repeatable workflows anymore, I do have access to the unique and valuable agent session data that Entire collects. Entire is a CLI-first system of record for agent-assisted development. It captures the context behind your work: the sessions, prompts, responses, tool calls, file changes, and Checkpoints. A Checkpoint is a specific moment where work is tied back to git. It connects the "why" of the agent session to the "what" of the final commit.&lt;/p&gt;

&lt;p&gt;I realized this data isn't just for review, audit, or to sit quietly in the background. It's a source of truth that can be used for building better workflows. I thought, “What if I could use my Entire session history to recreate that ‘package up a session’ magic, but in a way that works across any agent, and works retrospectively?”&lt;/p&gt;

&lt;p&gt;The most popular way people are currently building reusable workflows is with &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;Skills&lt;/a&gt;, so I built an orchestrator skill called &lt;a href="https://github.com/entireio/skills/blob/main/skills/session-to-skill/SKILL.md" rel="noopener noreferrer"&gt;&lt;code&gt;Session-to-Skill&lt;/code&gt;&lt;/a&gt;. It creates Skills for me based on repeated behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Before and After
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before I used to say:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Look at past blog posts in this repo, check the folder structure, and the front matter.”
&lt;/li&gt;
&lt;li&gt;“I want to add a new blog post. Here’s the content:  [insert content copied from google doc here] ”
&lt;/li&gt;
&lt;li&gt;“Create a new PR. Make sure we’ve pulled the latest from main and branch off main before you create this PR.”
&lt;/li&gt;
&lt;li&gt;“Why did you make the word Checkpoints lowercase when I purposely had them capitalized? Please restore that.”
&lt;/li&gt;
&lt;li&gt;“Does the OG image work? What’s the path for me to check that again?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Now, I can say:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Create a blog post from this content [insert content copied from google doc here].”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is possible because I prompted my agent to use the &lt;code&gt;Session-to-Skill&lt;/code&gt; Skill: "Look at my past sessions where I set up blog posts. Find the repeated steps and conventions, then draft a Skill from that data, so I can create blog posts quickly in the future." My agent created a Skill called &lt;code&gt;Create-blog&lt;/code&gt;, which included requirements to properly format the blog, open a PR, and return the path to confirm the OG image rendered.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Well, that’s kind of dumb..
&lt;/h2&gt;

&lt;p&gt;Some may have pushback on this idea of me building an orchestrator Skill because at any moment in a session you can prompt any agent to turn it into a Skill. &lt;/p&gt;

&lt;p&gt;The reality is I don’t have perfect foresight. Most reusable workflows are recognized later. After the third time I publish a blog post, I realize I have been doing the same thing over and over again. By then, the valuable evidence is spread across past sessions.  &lt;/p&gt;

&lt;p&gt;There is also the issue of quality. Asking an agent to summarize a transcript often leads to overfitting and noise. The resulting Skill might include accidental details, temporary file paths, or one-off preferences that happened to be present in that single session. &lt;/p&gt;

&lt;p&gt;Instead my Skill is extracting the answers to the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What was the reusable behavior?
&lt;/li&gt;
&lt;li&gt;What should a future agent know before attempting this again?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't have to remember the session ID from six weeks ago. I just know the work happened. The Skill uses Entire to search my session metadata, checkpoints, and explanations of prior work to find the durable pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Procedural Memory as Infrastructure
&lt;/h2&gt;

&lt;p&gt;My approach creates procedural memory for agents. Procedural memory is the answer to the question, "How do I do this kind of work well, here, in this repo, with this team?" &lt;/p&gt;

&lt;p&gt;Daily engineering work is not net-new. You may receive a new ticket, but somebody has solved this problem before. &lt;/p&gt;

&lt;p&gt;By using Entire's data to generate Skills, I get a layer of determinism and portability. The agent starts with a template based on real work rather than a generic prompt. It encodes patterns that have already succeeded. And because Skills are portable files, I can take my blog-publishing Skill from Claude Code to Codex without re-explaining my workflow and share it with teammates.&lt;/p&gt;

&lt;p&gt;With all this said, I want to urge readers to stop treating our agent sessions as disposable and start turning our history into our infrastructure.&lt;/p&gt;

&lt;p&gt;Check out Entire at &lt;a href="http://entire.io" rel="noopener noreferrer"&gt;entire.io&lt;/a&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>agentskills</category>
      <category>entire</category>
    </item>
    <item>
      <title>Turning Agent History into Procedural Memory</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Sun, 03 May 2026 23:40:08 +0000</pubDate>
      <link>https://forem.com/entire/turning-agent-history-into-procedural-memory-38mo</link>
      <guid>https://forem.com/entire/turning-agent-history-into-procedural-memory-38mo</guid>
      <description>&lt;p&gt;For about a year, my primary coding agent was goose. Since I worked at Block and served as a Developer Advocate for the project, I was deeply embedded in its ecosystem. I contributed code and provided product feedback that shaped how it functioned.&lt;/p&gt;

&lt;p&gt;Then, I moved to a company called &lt;a href="//entire.io"&gt;Entire&lt;/a&gt; that provides the infrastructure for the agentic software development lifecycle. To do my job well, I have to dogfood our product across the agentic ecosystem. This means I am constantly switching between Claude Code, Codex, and other agents that support hooks to contribute to docs, investigate and resolve bugs, understand new features, and produce content.&lt;/p&gt;

&lt;p&gt;Switching between AI agents made me realize every agent has tradeoffs. Some are faster or more polished, but I find myself deeply missing a specific goose feature called recipes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Operational Glue
&lt;/h2&gt;

&lt;p&gt;Recipes are reusable, shareable workflows. At the core, they are YAML files that describe a process you want goose to run again. You can write the YAML files manually, but I always preferred the magic of clicking a button to package a successful session into a recipe.  &lt;/p&gt;

&lt;p&gt;My work in Developer Relations is creative, but it’s built on repeatable systems. For example, writing a blog post, building a code demo, creating a video are creative. The publishing process is not. Publishing a blog post involves a series of tiny, forgettable steps: checking the folder structure, adding the correct front matter, wiring up the metadata, dropping the image in the right asset folder, opening the PR. Each of these steps take a few minutes, but those minutes add up and become hours of operational glue. At Block, I automated as much of that as possible. I had goose generating release notes in CI/CD and creating documentation tickets in Asana. Some of these ran on a schedule, others I triggered manually. The point was always the same: if I found myself explaining a process to an agent more than once, it was an operational smell that needed to become a reusable asset.&lt;/p&gt;

&lt;p&gt;While my use cases focus on content and community, this pattern is universal. In many fields, employees find themselves frequently explaining the same sequence to an agent, so why not automate this into repeatable workflows?  &lt;/p&gt;

&lt;p&gt;For engineers, those repeated conversations may look like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upgrading a dependency safely
&lt;/li&gt;
&lt;li&gt;Bootstrapping a new microservice
&lt;/li&gt;
&lt;li&gt;Triaging a production error
&lt;/li&gt;
&lt;li&gt;Writing a design doc or RFC
&lt;/li&gt;
&lt;li&gt;Preparing a release PR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The inputs and thinking may vary, but the process: conventions, the file paths, the validation steps, the commands you run, the people you always notify stay the same. And this level of automation is necessary today where employers are demanding more output.&lt;/p&gt;

&lt;h2&gt;
  
  
  The System of Record
&lt;/h2&gt;

&lt;p&gt;I'm constantly jumping between different agents. Each one has its own process for automating workflows, but none of the automation tools hit the mark for me like goose did. While I don’t have access to my treasured repeatable workflows anymore, I do have access to the unique and valuable agent session data that Entire collects. Entire is a CLI-first system of record for agent-assisted development. It captures the context behind your work: the sessions, prompts, responses, tool calls, file changes, and Checkpoints. A Checkpoint is a specific moment where work is tied back to git. It connects the "why" of the agent session to the "what" of the final commit.&lt;/p&gt;

&lt;p&gt;I realized this data isn't just for review, audit, or to sit quietly in the background. It's a source of truth that can be used for building better workflows. I thought, “What if I could use my Entire session history to recreate that ‘package up a session’ magic, but in a way that works across any agent, and works retrospectively?”&lt;/p&gt;

&lt;p&gt;The most popular way people are currently building reusable workflows is with &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;Skills&lt;/a&gt;, so I built an orchestrator skill called &lt;a href="https://github.com/entireio/skills/blob/main/skills/session-to-skill/SKILL.md" rel="noopener noreferrer"&gt;&lt;code&gt;Session-to-Skill&lt;/code&gt;&lt;/a&gt;. It creates Skills for me based on repeated behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Before and After
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before I used to say:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Look at past blog posts in this repo, check the folder structure, and the front matter.”
&lt;/li&gt;
&lt;li&gt;“I want to add a new blog post. Here’s the content:  [insert content copied from google doc here] ”
&lt;/li&gt;
&lt;li&gt;“Create a new PR. Make sure we’ve pulled the latest from main and branch off main before you create this PR.”
&lt;/li&gt;
&lt;li&gt;“Why did you make the word Checkpoints lowercase when I purposely had them capitalized? Please restore that.”
&lt;/li&gt;
&lt;li&gt;“Does the OG image work? What’s the path for me to check that again?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Now, I can say:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Create a blog post from this content [insert content copied from google doc here].”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is possible because I prompted my agent to use the &lt;code&gt;Session-to-Skill&lt;/code&gt; Skill: "Look at my past sessions where I set up blog posts. Find the repeated steps and conventions, then draft a Skill from that data, so I can create blog posts quickly in the future." My agent created a Skill called &lt;code&gt;Create-blog&lt;/code&gt;, which included requirements to properly format the blog, open a PR, and return the path to confirm the OG image rendered.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Well, that’s kind of dumb..
&lt;/h2&gt;

&lt;p&gt;Some may have pushback on this idea of me building an orchestrator Skill because at any moment in a session you can prompt any agent to turn it into a Skill. &lt;/p&gt;

&lt;p&gt;The reality is I don’t have perfect foresight. Most reusable workflows are recognized later. After the third time I publish a blog post, I realize I have been doing the same thing over and over again. By then, the valuable evidence is spread across past sessions.  &lt;/p&gt;

&lt;p&gt;There is also the issue of quality. Asking an agent to summarize a transcript often leads to overfitting and noise. The resulting Skill might include accidental details, temporary file paths, or one-off preferences that happened to be present in that single session. &lt;/p&gt;

&lt;p&gt;Instead my Skill is extracting the answers to the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What was the reusable behavior?
&lt;/li&gt;
&lt;li&gt;What should a future agent know before attempting this again?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't have to remember the session ID from six weeks ago. I just know the work happened. The Skill uses Entire to search my session metadata, checkpoints, and explanations of prior work to find the durable pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Procedural Memory as Infrastructure
&lt;/h2&gt;

&lt;p&gt;My approach creates procedural memory for agents. Procedural memory is the answer to the question, "How do I do this kind of work well, here, in this repo, with this team?" &lt;/p&gt;

&lt;p&gt;Daily engineering work is not net-new. You may receive a new ticket, but somebody has solved this problem before. &lt;/p&gt;

&lt;p&gt;By using Entire's data to generate Skills, I get a layer of determinism and portability. The agent starts with a template based on real work rather than a generic prompt. It encodes patterns that have already succeeded. And because Skills are portable files, I can take my blog-publishing Skill from Claude Code to Codex without re-explaining my workflow and share it with teammates.&lt;/p&gt;

&lt;p&gt;With all this said, I want to urge readers to stop treating our agent sessions as disposable and start turning our history into our infrastructure.&lt;/p&gt;

&lt;p&gt;Check out Entire at &lt;a href="http://entire.io" rel="noopener noreferrer"&gt;entire.io&lt;/a&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>agentskills</category>
      <category>entire</category>
    </item>
    <item>
      <title>I Don’t Make Slides Anymore. My Agent and Entire Do It for Me.</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Sat, 25 Apr 2026 17:00:18 +0000</pubDate>
      <link>https://forem.com/entire/i-dont-make-slides-anymore-my-agent-and-entire-do-it-for-me-m17</link>
      <guid>https://forem.com/entire/i-dont-make-slides-anymore-my-agent-and-entire-do-it-for-me-m17</guid>
      <description>&lt;p&gt;Signing up to speak at conferences is fun until the conference date starts approaching and you realize you still have to write and practice your talk. For me, writing the talk isn't the hard part. I have a process of talking to myself on a peaceful walk (or even in the shower), recording my voice, and then inserting the demos afterward. The part I often procrastinate is making the slides. Creating slides used to be fun, but as I’ve grown my career and my family, it's no longer a good use of my time.  &lt;/p&gt;

&lt;p&gt;I've looked for a way to automate slide generation, but most options have been fragile. They generally struggle with formatting and taste. However, a few months ago, some of my teammates at Block discovered the &lt;a href="https://github.com/zarazhangrui/frontend-slides" rel="noopener noreferrer"&gt;frontend slides skill&lt;/a&gt; and introduced me to it. This skill enables agents to build out HTML presentation decks that can be exported as PDFs or PowerPoint presentations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent Skills
&lt;/h2&gt;

&lt;p&gt;If you're not familiar, &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;agent skills&lt;/a&gt; are markdown files that provide instructions for the agent to understand how to use a tool (i.e., a CLI or an MCP server). This way, your agent immediately knows what commands to run and how to navigate the tooling when you make a request.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Use the Frontend Slides Skill
&lt;/h2&gt;

&lt;p&gt;As I mentioned, I already have my talk transcribed, which gives me a talk track to follow. I typically give the transcript to an agent and ask if there are any parts that don't make sense or any gaps for the audience.&lt;/p&gt;

&lt;p&gt;Once the talk track is polished, I give the copy to my agent and prompt it to use the frontend slides skill to build a deck based on the track. I prefer to use Claude Code for this task, as it seems to work really well with the frontend slides skill, but any agent that supports skills should work. The agent then produces a beautiful slide deck for me. It really doesn't look bad or overly generic at all. It has various themes to choose from, and since it's generated with HTML, CSS, and JavaScript, I can prompt my agent to edit parts like making the font bigger, changing colors, and so on.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-2024060044153692197-26" src="https://platform.twitter.com/embed/Tweet.html?id=2024060044153692197"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2024060044153692197-26');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2024060044153692197&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;My favorite thing to add is a presenter view. It doesn't generate that view by default, but I do like to take a peek at my notes as I speak. So I usually tell my agent to implement that view if I press a key like the letter "P," and I make sure it syncs with what everyone else can see. Then, I upload my deck to GitHub Pages. Goodbye, Canva, PowerPoint, and Google Slides.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Entire Enhanced My Workflow
&lt;/h2&gt;

&lt;p&gt;Let me rewind for a second and introduce you to &lt;a href="https://entire.io/" rel="noopener noreferrer"&gt;Entire&lt;/a&gt;. Entire is the company I work for. We're &lt;a href="https://entire.io/vision" rel="noopener noreferrer"&gt;building the next developer platform&lt;/a&gt; for the AI-native software development lifecycle. The team recognized that agents have changed our workflows, so the infrastructure we use should change too.&lt;/p&gt;

&lt;p&gt;Our first tool is a &lt;a href="https://github.com/entireio/cli" rel="noopener noreferrer"&gt;CLI&lt;/a&gt; that captures prompts, agent responses, tool calls, and other session data from the work you do with an agent. That gives you a way to inspect what happened, rewind work from a past session, and stay accountable. For example, if a production outage ever happens, instead of saying, "Oh, the root cause is that my agent did it," you can actually track the decisions made between the agent and the person prompting it. I described this to someone at a conference the other day, and they boiled it down to version control for agentic work, which is honestly exactly what it feels like.&lt;/p&gt;

&lt;p&gt;Now, it took me a while to see how Entire could make my workflow even better, but the founders opened my eyes. I can use Entire while I'm building out a demo and then use that captured work to help build the slide deck later. Entire has a command called &lt;code&gt;entire dispatch&lt;/code&gt;. It generates a markdown summary of the work Entire captured between you and your agent in a repository.&lt;/p&gt;

&lt;p&gt;For example, after experimenting with OCR in a repo, Entire generated this dispatch summary for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Dispatch: blackgirlbytes/pretext-handwriting-demo&lt;/span&gt;

Shipped a full handwriting recognition demo built on Pretext, moving from  
initial scaffolding to a polished scrapbook composition surface within a  
single day.

&lt;span class="gu"&gt;## blackgirlbytes/pretext-handwriting-demo&lt;/span&gt;

&lt;span class="gu"&gt;### Handwriting Recognition&lt;/span&gt;

• Built draw-mode handwriting recognition as the core interaction surface.  
• Added image upload as a second recognition path alongside the drawing  
canvas.  
• Implemented auto-recognition after drawing completes, removing the manual  
trigger step.  
• Added camera mode to round out the three input methods.  
• Removed the explicit draw button to streamline the input UX.

&lt;span class="gu"&gt;### Scrapbook Composition Surface&lt;/span&gt;

• Introduced a scrapbook composition surface for arranging recognized text  
and shapes.  
• Integrated Pretext to handle obstacle-aware text flow around placed shapes.  
• Added animated motion layer to the scrapbook background.  
• Added resize handles to scrapbook shapes for direct manipulation.  
• Matched scrapbook background typography to the handwriter component for  
visual consistency.  
• Moved scrapbook controls into the composition header to consolidate the  
toolbar.  
• Fixed scrapbook layout and output tab rendering, then simplified and  
tightened tab spacing across multiple passes.  
• Corrected background line wrapping and ensured background renders before  
shapes are placed.

&lt;span class="gu"&gt;### API Key &amp;amp; Session Management&lt;/span&gt;

• Added session-based OpenAI key setup to avoid requiring environment-level  
configuration.  
• Hardened API key gate interactions to handle edge cases more reliably.  
• Added environment key setup path as an alternative to session entry.

&lt;span class="gu"&gt;### Documentation&lt;/span&gt;

• Added project agent working rules and intent guidance early in the commit  
sequence.  
• Documented project setup and architecture in the README.  
• Refined and clarified the README project description across two follow-up  
commits.

All core features landed on main on April 1, 2026; the repo is in a  
reviewable state.  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's helpful for me because building a demo usually takes a few days. I'll leave it, come back to it later, and then have to remember what I actually did, what mattered most, and which technical details are worth calling out. Instead of trying to reconstruct all of that from memory, I can use the dispatch summary, give it back to the agent, and ask it to make a strong slide that captures the main technical highlights of the demo. That saves me from having to recall every step I took days or even weeks later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond DevRel
&lt;/h2&gt;

&lt;p&gt;My particular use case works best for folks in Developer Relations or folks who do DevRel-related work like conference speaking, but this can actually work well for various roles. Here are a few:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers demoing completed features to their team. My husband is a developer, and he's expressed that building the slide deck is time-consuming just to show off a feature he built to his team.
&lt;/li&gt;
&lt;li&gt;Hackathon participants demoing their project to judges. Presentation decks often get neglected because everyone is focused on building the actual project during a small window of time.
&lt;/li&gt;
&lt;li&gt;Solutions engineers or sales engineers preparing customer demos. A lot of time goes into building out the demo environment itself, so having help turning that work into a clear deck can save a lot of time.
&lt;/li&gt;
&lt;li&gt;Workshop instructors or developer educators teaching technical material. It can be useful to turn the work captured while building the demo or sample app into slides that explain the flow, architecture, or key takeaways.
&lt;/li&gt;
&lt;li&gt;Engineering managers or tech leads giving project updates. Sometimes the hard part is not the work itself, but summarizing what happened clearly enough for leadership or cross-functional teams.
&lt;/li&gt;
&lt;li&gt;Founders or indie hackers pitching what they built. When you are moving quickly, the last thing you want is to spend hours making slides after already doing the hard part of building the product.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t believe in automating things that deserve a human touch, but I do believe in automating things so I can spend more time with humans. The slides skill has been great for me, but adding Entire to this workflow has made it even easier for me to do that.&lt;/p&gt;

&lt;p&gt;Much of my previous work with GitHub and Block (&lt;a href="https://goose-docs.ai/" rel="noopener noreferrer"&gt;goose&lt;/a&gt;) was focused on using agents to build faster. Recently joining the team at Entire has pushed me to think more about the next layer: making agentic work durable and accountable.&lt;/p&gt;

&lt;p&gt;Building fast with agents is fun, but in practice, I also need to be able to understand what happened, pick work back up later, explain it to other people, and turn it into something useful beyond the moment it was created. I’ll be sharing more of my agent-native workflows as I continue experimenting.&lt;/p&gt;

&lt;p&gt;If you want to learn more about Entire, check out our:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://entire.io/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.entire.io/introduction" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://x.com/EntireHQ" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/jZJs3Tue4S" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And &lt;a href="https://dev.to/blackgirlbytes"&gt;follow me&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>agentskills</category>
      <category>entire</category>
    </item>
    <item>
      <title>8 Things You Didn't Know About Code Mode</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Thu, 19 Feb 2026 06:54:38 +0000</pubDate>
      <link>https://forem.com/goose_oss/8-things-you-didnt-know-about-code-mode-4h71</link>
      <guid>https://forem.com/goose_oss/8-things-you-didnt-know-about-code-mode-4h71</guid>
      <description>&lt;p&gt;Agents fundamentally changed how we program. They enable developers to move faster by disintermediating the traditional development workflow. This means less time switching between specialized tools and fewer dependencies on other teams. Now that agents can execute complicated tasks, developers face a new challenge: using them effectively over long sessions.&lt;/p&gt;

&lt;p&gt;The biggest challenge is context rot. Because agents have limited memory, a session that runs too long can cause them to "forget" earlier instructions. This leads to unreliable outputs, frustration, and subtle but grave mistakes in your codebase. One promising solution is Code Mode. &lt;/p&gt;

&lt;p&gt;Instead of describing dozens of separate tools to an LLM, Code Mode allows an agent to write code that calls those tools programmatically, reducing the amount of context the model has to hold at once. While many developers first heard about Code Mode through &lt;a href="https://blog.cloudflare.com/code-mode/" rel="noopener noreferrer"&gt;Cloudflare's blog post&lt;/a&gt;, fewer understand how it works in practice. &lt;/p&gt;

&lt;p&gt;I have been using Code Mode for a few months and recently ran a small experiment. I asked goose to fix its own bug where the Gemini model failed to process images in the CLI but worked in the desktop app, then open a PR. The fix involved analyzing model configuration, tracing image input handling through the pipeline, and validating behavior across repeated runs. I ran the same task twice: once with Code Mode enabled and once without it.&lt;/p&gt;

&lt;p&gt;Here is what I learned from daily use and my experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Code Mode is Not an MCP-Killer
&lt;/h2&gt;

&lt;p&gt;In fact, it uses MCP under the hood. MCP is a standard that lets AI agents connect to external tools and data sources. When you install an MCP server in an agent, that MCP server exposes its capabilities as MCP tools. For example, goose's primary MCP server called the &lt;code&gt;developer&lt;/code&gt; extension exposes tools like &lt;code&gt;shell&lt;/code&gt; enabling goose to run commands and &lt;code&gt;text_editor&lt;/code&gt;, so goose can view and edit files. &lt;/p&gt;

&lt;p&gt;Code Mode wraps your MCP tools as JavaScript modules, allowing the agent to combine multiple tool calls into a single step. Code Mode is a pattern for how agents interact with MCP tools more efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. goose Supports Code Mode
&lt;/h2&gt;

&lt;p&gt;Code Mode support landed in goose v1.17.0 in December 2025. It ships as a platform extension called "Code Mode" that you can enable in the desktop app or CLI.&lt;/p&gt;

&lt;p&gt;To enable it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Desktop app:&lt;/strong&gt; Click the extensions icon and toggle on "Code Mode"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI:&lt;/strong&gt; Run &lt;code&gt;goose configure&lt;/code&gt; and enable the Code Mode extension&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since its initial implementation, we've added so many improvements!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Code Mode Keeps Your Context Window Clean
&lt;/h2&gt;

&lt;p&gt;Every time you install an MCP server (or "extension" in the goose ecosystem), it adds a significant amount of data to your agent's memory. Every tool comes with a tool definition describing what the tool does, the parameters it accepts, and what it returns. This helps the agent understand how to use the tool.&lt;/p&gt;

&lt;p&gt;These definitions consume space in your agent's context window. For example, if a single definition takes 500 tokens and an extension has five tools, that is 2,500 tokens gone before you even start. If you use multiple extensions, you could easily double or even decuple that number.&lt;/p&gt;

&lt;p&gt;Without Code Mode, your context window could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[System prompt: ~1,000 tokens]
[Tool: developer__shell - 500 tokens]
[Tool: developer__text_editor - 600 tokens]
[Tool: developer__analyze - 400 tokens]
[Tool: slack__send_message - 450 tokens]
[Tool: slack__list_channels - 400 tokens]
[Tool: googledrive__search - 500 tokens]
[Tool: googledrive__download - 450 tokens]
... and so on for every tool in every extension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As your session progresses, useful context gets crowded out by tool definitions you aren't even using: the code you are discussing, the problem you are solving, or the instructions you previously gave. This leads to performance degradation and memory loss. While I used to recommend disabling unused MCP servers, Code Mode offers a better fix. It uses three tools that help the agent discover what tools it needs on demand rather than having every tool definition loaded upfront:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;search_modules&lt;/code&gt; - Find available extensions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read_module&lt;/code&gt; - Learn what tools an extension offers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute_code&lt;/code&gt; - Run JavaScript that uses those tools&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wanted to see how true this was so I ran an experiment: I had goose solve a user's bug and put up a PR with and without code mode. Code Mode used 30% fewer tokens for the same task.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;With Code Mode&lt;/th&gt;
&lt;th&gt;Without Code Mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total tokens&lt;/td&gt;
&lt;td&gt;23,339&lt;/td&gt;
&lt;td&gt;33,648&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input tokens&lt;/td&gt;
&lt;td&gt;23,128&lt;/td&gt;
&lt;td&gt;33,560&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  4. Code Mode Batches Operations Into a Single Tool Call
&lt;/h2&gt;

&lt;p&gt;The token savings do not just come from loading fewer tool definitions upfront. Code Mode also handles the "active" side of the conversation through a method called batching.&lt;/p&gt;

&lt;p&gt;When you ask an agent to do something, it typically breaks your request into individual steps, each requiring a separate tool call. You can see these calls appear in your chat as the agent executes the tasks. For example, if you ask goose to "check the current branch, show me the diff, and run the tests," it might run four individual commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;▶ developer__shell → git branch --show-current

▶ developer__shell → git status

▶ developer__shell → git diff

▶ developer__shell → cargo test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of these calls adds a new layer to the conversation history that goose has to track. Batching combines these into a single execution. When you turn Code Mode on and give that same prompt, you will see just one tool call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;▶ Code Execution: Execute Code
  generating...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside that one execution, it batches all the commands into a script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shell&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;developer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git branch --show-current&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git diff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cargo test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a user, you see the same results, but the agent only has to remember one interaction instead of four. By reducing these round trips, Code Mode keeps the conversation history concise so the agent can maintain focus on the task at hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Code Mode Makes Smarter Tool Choices
&lt;/h2&gt;

&lt;p&gt;When an agent has access to dozens of tools, it sometimes makes a "logical" choice that is technically wrong for your environment. This happens because, in a standard setup, the agent picks tools from a flat list based on short text descriptions. This can lead to a massive waste of time and tokens when the agent picks a tool that sounds right but lacks the necessary context.&lt;/p&gt;

&lt;p&gt;I saw this firsthand during my experiments. I had an extension enabled called agent-task-queue, which is designed to run background tasks with timeouts.&lt;/p&gt;

&lt;p&gt;When I asked goose to run the tests for my PR, it looked at the available tools and saw agent-task-queue. The LLM reasoned that a test suite is a "long-running task," making that extension a perfect fit. It chose the specialized tool over the generic shell.&lt;/p&gt;

&lt;p&gt;However, the tool call failed immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FAILED exit=127 0.0s
/bin/sh: cargo: command not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My environment was not configured to use that specific extension for my toolchain. goose made a reasonable choice based on the description, but it was the wrong tool for my actual setup.&lt;/p&gt;

&lt;p&gt;In the Code Mode session, this mistake never happened. Code Mode changes how the agent interacts with its capabilities by requiring explicit import statements.&lt;/p&gt;

&lt;p&gt;Instead of browsing a menu of names, goose had to be intentional about which module it was using. It chose to import from the developer module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shell&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;developer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cargo test -p goose --lib formats::google&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By explicitly importing developer, Code Mode ensured the tests ran in my actual shell environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Code Mode Is Portable Across Editors
&lt;/h2&gt;

&lt;p&gt;goose is more than an agent; it's also an &lt;a href="https://dev.to/docs/guides/acp-clients"&gt;ACP (Agent Client Protocol)&lt;/a&gt; server. This means you can connect it to any editor that supports ACP, like Zed or Neovim. Plus, any MCP server you use in goose will work there, too.&lt;/p&gt;

&lt;p&gt;I wanted to try this myself, so I set up Neovim to connect to goose &lt;strong&gt;with Code Mode enabled&lt;/strong&gt;. Here's the configuration I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"yetone/avante.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"make"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VeryLazy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"goose"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;acp_providers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"goose"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"goose"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"acp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--with-builtin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"code_execution,developer"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"nvim-lua/plenary.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"MunifTanjim/nui.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key line is the one where I enable Code Mode right inside the editor config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"acp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--with-builtin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"code_execution,developer"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test it, I asked goose to list my Rust files and count the lines of code. Instead of a long stream of individual shell commands cluttering my Neovim buffer, I saw one singular tool call: Code Execution. It worked exactly like it does in the desktop app. This portability means you can build a powerful, efficient agent workflow and take it with you to whatever environment you're most comfortable in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcguhk2fkznykgt6ci94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcguhk2fkznykgt6ci94.png" alt=" " width="442" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Code Mode Performs Differently Across LLMs
&lt;/h2&gt;

&lt;p&gt;I ran my experiments using Claude Opus 4.5. Your results may vary depending on which model you use.&lt;/p&gt;

&lt;p&gt;Code Mode requires the LLM to do things that not all models do equally well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Write valid JavaScript&lt;/strong&gt; - The model has to generate syntactically correct code. Models with stronger code generation capabilities will produce fewer errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow the import pattern&lt;/strong&gt; - Code Mode expects the LLM to import tools from modules like &lt;code&gt;import { shell } from "developer"&lt;/code&gt;. Some models might try to call tools directly without importing, which will fail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the discovery tools&lt;/strong&gt; - Before writing code, the LLM should call &lt;code&gt;search_modules&lt;/code&gt; and &lt;code&gt;read_module&lt;/code&gt; to learn what tools are available. Some models skip this step and guess, leading to hallucinated tool names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle errors gracefully&lt;/strong&gt; - When a code execution fails, the model needs to read the error, understand what went wrong, and try again. Some models are better at this feedback loop than others.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Code Mode is not working well for you, try switching models. A model that excels at code generation and instruction following will generally perform better with Code Mode than one optimized for other tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Code Mode Is Not for Every Task
&lt;/h2&gt;

&lt;p&gt;Code Mode adds overhead. Before executing anything, the LLM has to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;search_modules&lt;/code&gt; to find available extensions&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;read_module&lt;/code&gt; to learn what tools an extension offers&lt;/li&gt;
&lt;li&gt;Write JavaScript code&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;execute_code&lt;/code&gt; to run it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For simple, single-tool tasks, this overhead is not worth it. If you just need to run one shell command or view one file, regular tool calling is faster.&lt;/p&gt;

&lt;p&gt;Based on my experiments, here is when Code Mode makes sense:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Code Mode When&lt;/th&gt;
&lt;th&gt;Skip Code Mode When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;You have multiple extensions enabled&lt;/td&gt;
&lt;td&gt;You only have 1-2 extensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your task involves multi-step orchestration&lt;/td&gt;
&lt;td&gt;Your task is a single tool call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want longer sessions without context rot&lt;/td&gt;
&lt;td&gt;Speed matters more than context longevity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You are working across multiple editors&lt;/td&gt;
&lt;td&gt;You are doing a quick one-off task&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;If you want to experiment with Code Mode, here are some resources:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/docs/guides/acp-clients"&gt;ACP client setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/docs/getting-started/using-extensions"&gt;Extensions guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Previous posts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/blog/2025/12/15/code-mode-mcp"&gt;Code Mode MCP in goose&lt;/a&gt; by Alex Hancock&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/blog/2025/12/21/code-mode-doesnt-replace-mcp"&gt;Code Mode Doesn't Replace MCP&lt;/a&gt; by me&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Community:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Join our &lt;a href="https://discord.gg/goose-oss" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; to share what you learn&lt;/li&gt;
&lt;li&gt;File issues on &lt;a href="https://github.com/block/goose" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; if something does not work as expected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run your own experiments and let us know what you find.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>5 Tips for Building MCP Apps That Work</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Thu, 19 Feb 2026 06:49:01 +0000</pubDate>
      <link>https://forem.com/goose_oss/5-tips-for-building-mcp-apps-that-work-2pme</link>
      <guid>https://forem.com/goose_oss/5-tips-for-building-mcp-apps-that-work-2pme</guid>
      <description>&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/docs/extensions/apps" rel="noopener noreferrer"&gt;MCP Apps&lt;/a&gt; allow you to render interactive UI directly inside any agent supporting the Model Context Protocol. Instead of a wall of text, your agent can now provide a functional chart, a checkout form, or a video player. This bridges the gap in agentic workflows: clicking a button is often clearer than describing the action you hope an agent executes.&lt;/p&gt;

&lt;p&gt;MCP Apps originated as &lt;a href="https://mcp-ui.dev/" rel="noopener noreferrer"&gt;MCP-UI&lt;/a&gt;, an experimental project. After adoption by early clients like goose, the MCP maintainers incorporated it as an official extension. Today, it's supported by clients like goose, MCPJam, Claude, ChatGPT, and Postman.&lt;/p&gt;

&lt;p&gt;Even though MCP Apps use web technologies, building one isn't the same as building a traditional web app. Your UI runs inside an agent you don't control, communicates with a model that can't see user interactions, and needs to feel native across multiple hosts.&lt;/p&gt;

&lt;p&gt;After implementing MCP App support in our own hosts and building several individual apps to run on them, here are the practical lessons we've picked up along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of how UI renders with MCP Apps
&lt;/h2&gt;

&lt;p&gt;At a high level, clients that support MCP Apps load your UI via iFrames. Your MCP App exposes an MCP server with tools and resources. When the client wants to load your app's UI, it calls the associated MCP tool, loads the resource containing the HTML, then loads your HTML into an iFrame to display in the chat interface.&lt;/p&gt;

&lt;p&gt;Here's an example flow of what happens when goose renders a cocktail recipe UI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You ask the LLM "Show me a margarita recipe".&lt;/li&gt;
&lt;li&gt;The LLM calls the &lt;code&gt;get-cocktail&lt;/code&gt; tool with the right parameters. This tool has a UI resource link in &lt;code&gt;_meta.ui.resourceUri&lt;/code&gt; pointing to the resource containing the HTML.&lt;/li&gt;
&lt;li&gt;The client then uses the URI to fetch the MCP resource. This resource contains the HTML content of the view.&lt;/li&gt;
&lt;li&gt;The HTML is then loaded into the iFrame directly in the chat interface, rendering the cocktail recipe.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm049x427yvnpxbl9nsyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm049x427yvnpxbl9nsyy.png" alt="MCP Apps flow diagram showing how UI renders" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a lot that also goes on behind the scenes, such as View hydration, capability negotiation, and CSPs, but this is how it works at a high level. If you're interested in the full implementation of MCP Apps, we highly recommend giving &lt;a href="https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx" rel="noopener noreferrer"&gt;the spec&lt;/a&gt; a read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 1: Adapt to the Host Environment
&lt;/h2&gt;

&lt;p&gt;When building an MCP App, you want it to feel like a natural part of the agent experience rather than something bolted on. Visual mismatches are one of the fastest ways to break that illusion.&lt;/p&gt;

&lt;p&gt;Imagine a user starting an MCP App interaction inside a dark-mode agent, but the app renders in light mode and creates a harsh visual contrast. Even if the app works correctly, the experience immediately feels off.&lt;/p&gt;

&lt;p&gt;By default, your MCP App has no awareness of the surrounding agent environment because it runs inside a sandboxed iframe. It cannot tell whether the agent is in light or dark mode, how large the viewport is, or which locale the user prefers.&lt;/p&gt;

&lt;p&gt;The agent, referred to as the Host, solves this by sharing its environment details with your MCP App, known as the View. When the View connects, it sends a &lt;code&gt;ui/initialize&lt;/code&gt; request. The Host responds with a &lt;code&gt;hostContext&lt;/code&gt; object describing the current environment. When something changes, such as theme, viewport, or locale, the Host sends a &lt;code&gt;ui/notifications/host-context-changed&lt;/code&gt; notification containing only the updated fields.&lt;/p&gt;

&lt;p&gt;Imagine this dialogue between the View and Host:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;View&lt;/strong&gt;: "I'm initializing. What does your environment look like?"&lt;br&gt;
&lt;strong&gt;Host&lt;/strong&gt;: "We're in dark mode, viewport is 400×300, locale is en-US, and we're on desktop."&lt;br&gt;
&lt;em&gt;User switches to light theme&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Host&lt;/strong&gt;: "Update: we're now in light mode."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is your job as the developer to ensure your MCP App makes use of the &lt;code&gt;hostContext&lt;/code&gt; so it can adapt to the environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use hostContext in your MCP App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/ext-apps/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;McpUiHostContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/ext-apps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHostContext&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;McpUiHostContext&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;appInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MyApp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;onAppCreated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onhostcontextchanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setHostContext&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Connecting&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;containerDimensions&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;containerDimensions&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hostContext&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; If you're using the &lt;code&gt;useApp&lt;/code&gt; hook in your MCP App, the hook provides a &lt;code&gt;onhostcontextchanged&lt;/code&gt; listener. You can then use a React &lt;code&gt;useState&lt;/code&gt; to update your app context. The host will provide their context, it's up to you as the app developer to decide what you want to do with that. For example, you can use theme to render light mode vs dark mode, locale to show a different language, or containerDimensions to adjust the app's sizing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tip 2: Control What the Model Sees and What the View Sees
&lt;/h2&gt;

&lt;p&gt;There are cases where you may want to have granular control over what data the LLM has access to, and what data the view can show. The MCP Apps spec specifies three different tool return values that lets you control data flow, each are handled differently by the app host.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;content&lt;/code&gt;: Content is the info that you want to expose to the model. Gives model context.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;structuredContent&lt;/code&gt;: This data is hidden from the model context. It is used to send data over the View for hydration.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_meta&lt;/code&gt;: This data is hidden from the model context. Used to provide additional info such as timestamps, version info.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at a practical example of how we can use these three tool return types effectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view-cocktail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Cocktail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch a cocktail by id with ingredients and images...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The id of the cocktail to fetch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;_meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;resourceUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui://cocktail/cocktail-recipe-widget.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CallToolResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cocktail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;convexClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cocktails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCocktailById&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Loaded cocktail "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cocktail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;".`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Cocktail ingredients: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cocktail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ingredients&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Cocktail instructions: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cocktail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;structuredContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cocktail&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;_meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tool renders a view showing a cocktail recipe. The cocktail data is being fetched from the backend database (Convex). The View needs the entire cocktail data so we pass the data to it via &lt;code&gt;structuredContent&lt;/code&gt;. For the model context, the LLM doesn't need to know the entire cocktail data like the image URL. We can extract the information that the model should know about the cocktail, like the name, ingredients, and instructions. That information can be passed to the model via &lt;code&gt;content&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's important to note that currently, ChatGPT apps SDK handles it differently, where &lt;code&gt;structuredContent&lt;/code&gt; is exposed to both the model and the View. Their model is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;content&lt;/code&gt;: Content is the info that you want to expose to the model. Gives model context.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;structuredContent&lt;/code&gt;: This data is exposed to the model and the View.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_meta&lt;/code&gt;: This data is hidden from the model context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building an app that supports both MCP Apps and ChatGPT apps SDK, this is an important distinction. You may want to conditionally return values, or conditionally render tools based off of whether the client is MCP App support or ChatGPT app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 3: Properly Handle Loading States and Error States
&lt;/h2&gt;

&lt;p&gt;It's pretty typical for the iFrame to render first before the tool finishes executing and the View gets hydrated. You're going to want to let your user know that the app is loading by presenting a beautiful loading state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fay9t9zqifch9ovbaunlp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fay9t9zqifch9ovbaunlp.png" alt="Loading state example showing skeleton UI" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One powerful feature to note: &lt;code&gt;toolInputs&lt;/code&gt; are sent and streamed into the View even before the tool execution is done. This allows you to create cool partial loading states where you can show the user what's being requested while the data is still being fetched.&lt;/p&gt;

&lt;p&gt;To implement this, let's take a look at the same cocktail recipes app. The MCP tool fetches the cocktail data and passes it to the View via &lt;code&gt;structuredContent&lt;/code&gt;. We don't know how long it takes to fetch that cocktail data, could be anywhere from a few ms to a few seconds on a bad day.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view-cocktail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Cocktail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch a cocktail by id with ingredients and images...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The id of the cocktail to fetch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;_meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;resourceUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui://cocktail/cocktail-recipe-widget.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CallToolResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cocktail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;convexClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cocktails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCocktailById&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Loaded cocktail "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cocktail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;".`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;structuredContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cocktail&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the View side (React), the &lt;code&gt;useApp&lt;/code&gt; AppBridge hook has a &lt;code&gt;app.ontoolresult&lt;/code&gt; listener that listens for the tool return results and hydrates your View. While &lt;code&gt;onToolResult&lt;/code&gt; hasn't come in yet and the data is empty, we can render a beautiful loading state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@modelcontextprotocol/ext-apps/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CocktailApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cocktail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCocktail&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CocktailData&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;appInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IMPLEMENTATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;onAppCreated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ontoolresult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractCocktail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setCocktail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cocktail&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CocktailView&lt;/span&gt; &lt;span class="nx"&gt;cocktail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cocktail&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt; : &amp;lt;CocktailViewLoading /&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling errors
&lt;/h3&gt;

&lt;p&gt;We also want to handle errors gracefully. In the case where there's an error in your tool, such as the cocktail data failing to load, both the LLM and the view should be notified of the error.&lt;/p&gt;

&lt;p&gt;In your MCP tool, you should return an &lt;code&gt;error&lt;/code&gt; in the tool result. This is exposed to the model and also passed to the view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view-cocktail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Cocktail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch a cocktail by id with ingredients and images...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The id of the cocktail to fetch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;_meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;resourceUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui://cocktail/cocktail-recipe-widget.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CallToolResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cocktail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;convexClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cocktails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCocktailById&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Loaded cocktail "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cocktail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;".`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;structuredContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cocktail&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Could not load cocktail`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="nx"&gt;error&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in &lt;code&gt;useApp&lt;/code&gt; on the React client side, you can detect whether or not there was an error by looking at the existence of &lt;code&gt;error&lt;/code&gt; from the tool result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 4: Keep the Model in the Loop
&lt;/h2&gt;

&lt;p&gt;Because your MCP App operates in a sandboxed iframe, the model powering your agent can't see what happens inside the app by default. It won't know if a user fills out a form, clicks a button, or completes a purchase.&lt;/p&gt;

&lt;p&gt;Without a feedback loop, the model loses context. If a user buys a pair of shoes and then asks, "When will they arrive?", the model won't even realize a transaction occurred.&lt;/p&gt;

&lt;p&gt;To solve this, the SDK provides two methods to keep the model synchronized with the user's journey: &lt;code&gt;sendMessage&lt;/code&gt; and &lt;code&gt;updateModelContext&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  sendMessage()
&lt;/h3&gt;

&lt;p&gt;Use this for active triggers. It sends a message to the model as if the user typed it, prompting an immediate response. This is ideal for confirming a "Buy" click or suggesting related items right after an action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// User clicks "Buy" - the model responds immediately&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I just purchased Nike Air Max for $129&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Result: Model responds: "Great choice! Want me to track your order?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  updateModelContext()
&lt;/h3&gt;

&lt;p&gt;Use this for background awareness. It quietly saves information for the model to use later without interrupting the flow. This is perfect for tracking browsing history or cart updates without triggering a chat response every time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// User is browsing - no immediate response needed&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateModelContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User is viewing: Nike Air Max, Size 10, $129&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Result: No response. But if the user later asks, "What was I looking at?", the model knows.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tip 5: Control Who Can Trigger Tools
&lt;/h2&gt;

&lt;p&gt;With a standard MCP server, the model sees your tools, interprets the user's prompt, and calls the right tool. If a user says "delete that email," the model decides what that means and invokes the delete tool.&lt;/p&gt;

&lt;p&gt;However, with an MCP App, tools can be triggered in two ways: the model interpreting the user's prompt, or the user interacting directly with the UI.&lt;/p&gt;

&lt;p&gt;By default, both can call any tool. For example, say you build an MCP App that visually surfaces an email inbox and lets users interact with emails. Now there are two potential triggers for your tools: the model acting on a prompt to delete an email, and the user clicking a delete button directly in the App's interface.&lt;/p&gt;

&lt;p&gt;The model works by interpreting intent. If a user says "delete my old emails," the model has to decide what "old" means and which emails qualify. For some actions like deleting emails, that ambiguity can be risky.&lt;/p&gt;

&lt;p&gt;When a user clicks a "Delete" button next to a specific message in your MCP App, there is no ambiguity. They have made an explicit choice.&lt;/p&gt;

&lt;p&gt;To prevent the model from accidentally performing high-stakes actions based on a misunderstanding, you can use tool visibility to restrict certain tools to the MCP App's UI only. This allows the model to display the interface while requiring a human click to finalize the action.&lt;/p&gt;

&lt;p&gt;You can define visibility using these three configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;["model", "app"]&lt;/code&gt; (default) — Both the model and the UI can call it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;["model"]&lt;/code&gt; — Only the model can call it; the UI cannot&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;["app"]&lt;/code&gt; — Only the UI can call it; hidden from the model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how you might implement this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Model calls this to display the inbox&lt;/span&gt;
&lt;span class="nf"&gt;registerAppTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;show-inbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Display the user's inbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;_meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resourceUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui://email/inbox.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getEmails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emails&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// User clicks delete button in the UI&lt;/span&gt;
&lt;span class="nf"&gt;registerAppTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete-email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Delete an email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;emailId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;_meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resourceUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui://email/inbox.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;emailId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deleteEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email deleted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start Building with goose and MCPJam
&lt;/h2&gt;

&lt;p&gt;MCP Apps open up a new dimension for agent interactions. Now it's time to build your own.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test with &lt;a href="https://mcpjam.com/" rel="noopener noreferrer"&gt;MCPJam&lt;/a&gt;&lt;/strong&gt; — the open source local inspector for MCP Apps, ChatGPT apps SDK, and MCP servers. Perfect for debugging and iterating on your app before shipping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run in &lt;a href="https://github.com/block/goose" rel="noopener noreferrer"&gt;goose&lt;/a&gt;&lt;/strong&gt; — an open source AI agent that renders MCP Apps directly in the chat interface. See your app come to life in a real agent environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready to dive deeper? Check out the &lt;a href="https://block.github.io/goose/docs/tutorials/building-mcp-apps" rel="noopener noreferrer"&gt;MCP Apps tutorial&lt;/a&gt; or &lt;a href="https://docs.mcpjam.com/guides/first-mcp-app" rel="noopener noreferrer"&gt;build your first MCP App with MCPJam&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Used RPI to Build an OpenClaw Alternative</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Thu, 19 Feb 2026 06:44:39 +0000</pubDate>
      <link>https://forem.com/goose_oss/how-i-used-rpi-to-build-an-openclaw-alternative-d4d</link>
      <guid>https://forem.com/goose_oss/how-i-used-rpi-to-build-an-openclaw-alternative-d4d</guid>
      <description>&lt;p&gt;Everyone on Tech Twitter has been buying Mac Minis, so they could run a local agentic tool called &lt;a href="https://openclaw.ai/" rel="noopener noreferrer"&gt;OpenClaw&lt;/a&gt;. OpenClaw is a messaging-based AI assistant that connects to platforms such as Discord and Telegram allowing you to interact with an AI agent through DMs or @mentions. Under the hood, it uses an agent called Pi to execute tasks, browse the web, write code, and more.&lt;/p&gt;

&lt;p&gt;Seeing the hype made me want to get my hands dirty. I wanted to see if I could build a lite version for myself. I wanted something minimal that used &lt;a href="https://github.com/block/goose" rel="noopener noreferrer"&gt;goose&lt;/a&gt; as the engine instead of Pi. I tentatively dubbed it AltOpenClaw.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing RPI
&lt;/h2&gt;

&lt;p&gt;My usual move is to just jump in, start breaking things, and refactor as I go. I actually prefer the back and forth conversation with an agent because it helps me learn how the project works in real time. But when I tried that here, I hit a wall fast. goose did not naturally know what OpenClaw was, and it kept hallucinating how to use its own backend. It would forget context mid-conversation or suggest API calls that simply did not exist.&lt;/p&gt;

&lt;p&gt;I realized I needed to change my approach. While I love the iterative learning process, I needed a way to give the agent a better foundation so our pair programming sessions actually made progress. I decided to try the &lt;a href="https://block.github.io/goose/docs/tutorials/rpi" rel="noopener noreferrer"&gt;RPI method (Research, Plan, Implement)&lt;/a&gt;. This is a framework introduced by &lt;a href="https://humanlayer.dev/" rel="noopener noreferrer"&gt;HumanLayer&lt;/a&gt; that trades raw speed for predictability. It is built into goose as a series of recipes. Since I did not fully understand the technical landscape myself, this investment in structure felt like the right move to help us both get on the same page.&lt;/p&gt;




&lt;h3&gt;
  
  
  Research
&lt;/h3&gt;

&lt;p&gt;First, I needed goose to understand what I was building and whether it was even possible. I kicked things off with a detailed research prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/research_codebase topic="learn what openclaw is, how people use it, 
and how it works. learn if goose can actually be used as a backend 
or if that's not yet possible; understand the port issues especially 
if you have an instance of goose that's running to help you build 
an agent that uses goose as a backend. learn if there will be any 
auth issues"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;goose spawned multiple parallel subagents to investigate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key findings from the research:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenClaw uses its own embedded agent runtime (Pi)&lt;/strong&gt;, not goose. This meant there was no existing integration to copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;goose CAN be used as a backend!&lt;/strong&gt; The &lt;code&gt;goosed&lt;/code&gt; server exposes a full HTTP API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port conflicts are manageable.&lt;/strong&gt; We just needed to run on a different port with &lt;code&gt;GOOSE_PORT=3001&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication is simple.&lt;/strong&gt; We could pass a secret key in the &lt;code&gt;X-Secret-Key&lt;/code&gt; header.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The research also mapped out all the relevant API endpoints, such as &lt;code&gt;POST /sessions&lt;/code&gt; to create a new session and &lt;code&gt;POST /sessions/{id}/reply&lt;/code&gt; to handle the actual messaging.&lt;/p&gt;




&lt;h3&gt;
  
  
  Plan
&lt;/h3&gt;

&lt;p&gt;With the research complete, I asked goose to create an implementation plan. This is where we defined the personality and security of the bot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/create_plan ticket-or-context="I want to build a Discord MCP server 
for goose that replicates the popular features of OpenClaw but with 
better security. Core Features: Users can DM the bot or @ it in a 
channel to give goose tasks. goose responds in Discord with results. 
Security requirements: Allowlist (only specific Discord user IDs can 
interact), Approval flow (before goose executes any tool/action, the 
bot posts what it wants to do and waits for user approval), 
Non-allowlisted users get a polite 'you don't have access'"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;goose analyzed the requirements and produced a detailed plan with four phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Phase 1: Project Setup (Discord.js skeleton and allowlist)&lt;/li&gt;
&lt;li&gt;Phase 2: goose HTTP Client (Connecting to the API and handling SSE streaming)&lt;/li&gt;
&lt;li&gt;Phase 3: Tool Approval Flow (The UI for ✅/❌ reactions)&lt;/li&gt;
&lt;li&gt;Phase 4: Polish &amp;amp; Error Handling (Slash commands and session management)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I liked this phased approach because it gave us less to debug at each step. We could handle features in chunks rather than trying to fix everything at once.&lt;/p&gt;




&lt;h3&gt;
  
  
  Implement
&lt;/h3&gt;

&lt;p&gt;With the plan in place, I gave the signal to start building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/implement_plan start building
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two phases were surprisingly smooth. Within an hour, the bot was online and I could actually DM it. Seeing a Discord message trigger a goose session for the first time was a massive win.&lt;/p&gt;

&lt;p&gt;First, we tested if AltOpenClaw could respond to me with a joke!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F276v0dbm3v0rlh1776jp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F276v0dbm3v0rlh1776jp.png" alt="First successful message to the bot" width="800" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, as every developer knows, it was not all perfect. We still ran into some classic real-world hurdles during implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The SSE (Server-Sent Events) format was different than we expected. We spent a good chunk of time debugging why the messages were not appearing until we realized the event structure was nested deeper than anticipated.&lt;/li&gt;
&lt;li&gt;My local path did not have npm properly mapped, which led to a brief detour.&lt;/li&gt;
&lt;li&gt;Discord has a strict limit on message length. If goose wrote a long script, the bot would just crash. We had to implement a chunking system on the fly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, the tool approval feature is still a work in progress. I actually got so excited that the core part of the project was working that I sat down to write this post before finishing the UI for the reactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;The RPI method felt like a superpower, even if it didn't magically delete every bug from the project. There is a big difference between fighting a hallucination and fighting a real technical challenge.&lt;/p&gt;

&lt;p&gt;When I didn't use RPI, goose hallucinated nonexistent endpoints and tried to build a complex MCP server when a simple HTTP API was all we needed. Those are the kinds of bugs that waste hours because you are chasing ghosts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhasi2reabeusta3snutj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhasi2reabeusta3snutj.png" alt="Before RPI: Debugging failures and hallucinations" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead, RPI helped us clear the conceptual fog so we could focus on real implementation details like SSE parsing and character limits.&lt;/p&gt;

&lt;p&gt;By forcing the agent to research first, it built up the context it was missing. It is a bit slower at the start (which I barely have patience for), but it turns the agent into a much more capable partner for that back and forth learning process I enjoy.&lt;/p&gt;

&lt;p&gt;I even had AltOpenClaw push its own &lt;a href="https://github.com/blackgirlbytes/discord-goose-bot" rel="noopener noreferrer"&gt;repository&lt;/a&gt; to GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwwvopyol3gup4ie95494.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwwvopyol3gup4ie95494.png" alt="AltOpenClaw in action, completing a task" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;If you want more reliability from your agent, give the &lt;a href="https://block.github.io/goose/docs/tutorials/rpi" rel="noopener noreferrer"&gt;RPI recipes&lt;/a&gt; in goose a shot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/research_codebase&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/create_plan&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/implement_plan&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/iterate_plan&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>openclaw</category>
      <category>goose</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Wed, 14 Jan 2026 20:03:16 +0000</pubDate>
      <link>https://forem.com/blackgirlbytes/-2nhh</link>
      <guid>https://forem.com/blackgirlbytes/-2nhh</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/amandamartindev/dynamic-mcp-server-discovery-with-goose-3m41" class="crayons-story__hidden-navigation-link"&gt;Dynamic MCP Server discovery with goose&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/amandamartindev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F977362%2F8578b863-ce39-4328-896c-4518adab7271.jpeg" alt="amandamartindev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/amandamartindev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Amanda
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Amanda
                
              
              &lt;div id="story-author-preview-content-3169261" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/amandamartindev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F977362%2F8578b863-ce39-4328-896c-4518adab7271.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Amanda&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/amandamartindev/dynamic-mcp-server-discovery-with-goose-3m41" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jan 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/amandamartindev/dynamic-mcp-server-discovery-with-goose-3m41" id="article-link-3169261"&gt;
          Dynamic MCP Server discovery with goose
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mcp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mcp&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/amandamartindev/dynamic-mcp-server-discovery-with-goose-3m41" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;24&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/amandamartindev/dynamic-mcp-server-discovery-with-goose-3m41#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              3&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            1 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>ai</category>
      <category>mcp</category>
      <category>agents</category>
    </item>
    <item>
      <title>How I Taught My Agent My Design Taste</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Mon, 05 Jan 2026 00:15:14 +0000</pubDate>
      <link>https://forem.com/goose_oss/how-i-taught-my-agent-my-design-taste-3njj</link>
      <guid>https://forem.com/goose_oss/how-i-taught-my-agent-my-design-taste-3njj</guid>
      <description>&lt;p&gt;Can you automate taste? The short answer is no, you cannot automate taste, but I did make my design preferences legible.&lt;/p&gt;

&lt;p&gt;But for those interested in my experiment, I'll share the longer answer: I wanted to participate in &lt;a href="https://genuary.art/" rel="noopener noreferrer"&gt;Genuary&lt;/a&gt;, the annual challenge where people create one piece of creative coding every day in January. &lt;/p&gt;

&lt;p&gt;My goal here wasn't to "outsource" my creativity. Instead, I wanted to use Genuary as a sandbox to learn agentic engineering workflows. These workflows are becoming the standard for how developers work with technology. To keep my skills sharp, I used &lt;a href="https://block.github.io/goose/" rel="noopener noreferrer"&gt;goose&lt;/a&gt; to experiment with these workflows in small, daily bursts.&lt;/p&gt;

&lt;p&gt;By building a system where goose handles the execution, I could test different architectures side-by-side. This experiment allowed me to determine which parts of an agentic workflow actually add value and which parts I should ditch. I spent a few hours focused on infrastructure to buy myself an entire month of workflow data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;a href="https://block.github.io/goose/docs/guides/context-engineering/using-skills" rel="noopener noreferrer"&gt;Skills&lt;/a&gt; are reusable sets of instructions and resources that teach goose how to perform specific tasks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Inspiration
&lt;/h2&gt;

&lt;p&gt;I have to give a huge shout-out to my friend &lt;a href="https://www.linkedin.com/posts/andrewzigler_genuary4-genuary2026-activity-7413652312495149056-5jA-" rel="noopener noreferrer"&gt;Andrew Zigler&lt;/a&gt;. I saw him crushing Genuary and reached out to see how he was doing it. He shared his creations and mentioned he was using a "harness."&lt;/p&gt;

&lt;p&gt;I'll admit, I'd been seeing people use that term all December, but I didn't actually know what it meant. Andrew explained: a harness is just the toolbox you build for the model. It's the set of deterministic scripts that wrap the LLM so it can interact with your environment reliably. He had used this approach to solve a different challenge, building a system that could iterate, submit, and verify itself.&lt;/p&gt;

&lt;p&gt;He justified that if you spend time upfront working on a spec and establishing constraints. Then, you delegate. Once you have deterministic tools with good logging, the agent is incredibly good at looping until it hits its goal. &lt;/p&gt;

&lt;p&gt;My approach is typically very vanilla, and I lean heavily on prompting, but I was open to experimenting since Andrew was getting such excellent results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Harness vs. Skills
&lt;/h2&gt;

&lt;p&gt;Inspired by that conversation, I built two versions of the same workflow to see how they handled the same daily Genuary prompts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Approach 1: Harness + &lt;a href="https://block.github.io/goose/docs/tutorials/recipes-tutorial" rel="noopener noreferrer"&gt;Recipe&lt;/a&gt;&lt;/strong&gt;: This lives in &lt;code&gt;/genuary&lt;/code&gt;. Following Zig's lead, I wrote a shell script to act as the harness. It handles the scaffolding, creating folders and surfacing the daily prompt, so goose doesn't have to guess where to go. The recipe is about 300 lines long and fully self-contained.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach 2: Skills + Recipe&lt;/strong&gt;: This lives in &lt;code&gt;/genuary-skills&lt;/code&gt;. This recipe is much leaner because it delegates the "how" to a skill. The skill contains the design philosophy, references, and examples. I wanted to see how the work changed when the agent had to "discover" its instructions in a bundle rather than following a flat script.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I spent one focused session building the entire system: &lt;a href="https://github.com/blackgirlbytes/genuary2026/blob/main/genuary/genuary.yaml" rel="noopener noreferrer"&gt;recipes&lt;/a&gt;, &lt;a href="https://github.com/blackgirlbytes/genuary2026/blob/main/genuary-skills/.goose/skills/genuary/SKILL.md" rel="noopener noreferrer"&gt;skills&lt;/a&gt;, harness scripts, templates, and &lt;a href="https://github.com/blackgirlbytes/genuary2026/tree/main/.github/workflows" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;. (This happened in the quiet hours of my December break, with my one-year-old sleeping on my lap.) This was about trading short-term effort for long-term leverage. From that point on, the system did the daily work.&lt;/p&gt;

&lt;h2&gt;
  
  
  On Taste
&lt;/h2&gt;

&lt;p&gt;The automation was smooth, but when I reviewed the output, I noticed everything looked suspiciously similar.&lt;/p&gt;

&lt;p&gt;That's when I started to think about the discourse on how you can't teach an agent "taste." I thought about how I develop taste. I honestly develop taste by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seeing what's cool and copying it.&lt;/li&gt;
&lt;li&gt;Knowing what's overplayed because you've seen it too much.&lt;/li&gt;
&lt;li&gt;Following people with "good taste" and absorbing their patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obviously, I approached goose about this problem: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I noticed it always does salmon colored circles..i know we said creative..any ideas on how to make sure it thinks outside the box"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw542qaf5xbiudpa9znsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw542qaf5xbiudpa9znsd.png" alt="Salmon colored circles - a common AI generated cliché" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;goose shared that it was following a p5.js template it retrieved, which included a &lt;code&gt;fill(255, 100, 100)&lt;/code&gt; (salmon!) value and an ellipse example. Since LLMs anchor heavily on concrete examples, the agent was following the code more than my "creative" instructions.&lt;/p&gt;

&lt;p&gt;I removed the salmon circle from the template, but then I took it further: I asked how to ban common AI generated clichés altogether. goose searched discussions, pulled examples, and produced a banned list of patterns that scream "AI-generated."&lt;/p&gt;

&lt;h3&gt;
  
  
  BANNED CLICHÉS
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Banned Patterns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Color Crimes&lt;/td&gt;
&lt;td&gt;Salmon or coral pink, teal and orange combinations, purple-pink-blue gradients.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composition Crimes&lt;/td&gt;
&lt;td&gt;Single centered shapes, perfect symmetry with no variation, generic spirals.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Gold Rule&lt;/td&gt;
&lt;td&gt;If it looks like an AI generated output, do not do it.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ENCOURAGED PATTERNS
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Encouraged Patterns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Color Wins&lt;/td&gt;
&lt;td&gt;HSB mode with shifting hues, complementary palettes, gradients that evolve over time.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composition Wins&lt;/td&gt;
&lt;td&gt;Particle systems with emergent behavior, layered depth with transparency, hundreds of elements interacting.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Movement Wins&lt;/td&gt;
&lt;td&gt;Noise-based flow fields, flocking/swarming, organic growth patterns, breathing with variation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspiration Sources&lt;/td&gt;
&lt;td&gt;Natural phenomena: starlings murmurating, fireflies, aurora, smoke, water.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Gold Rule&lt;/td&gt;
&lt;td&gt;If it sparks joy and someone would want to share it, you're on the right track.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;goose determined this list through pattern recognition. So perhaps, agents can use patterns to reflect my taste, not because they understand beauty, but because I'm explicitly teaching them what I personally respond to.&lt;/p&gt;

&lt;p&gt;I showed Andrew my favorite output of the three days: butterflies lining themselves in a Fibonacci sequence.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn39tlm9r2xe0s4wj5o4h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn39tlm9r2xe0s4wj5o4h.png" alt="Butterflies arranged in a Fibonacci spiral" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;His response was validating:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"WOW that's an incredible Fibonacci… I'd be really curious to know your aesthetic prompting. Mine leans more pixel art and mathematical color manipulation because I've conditioned it that way… I like that yours leaned softer and tried to not look computer-created… like phone wallpaper practically lol..How did you even get that cool thinned line art on the butterflies? It looks like a base image. It's so cool. Did it draw SVGs? Like where did those come from?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because I'd specifically told goose to look at "natural phenomena" and "organic growth," it used Bezier curves for the wings and shifted the colors based on the spiral position to create depth, and a warm amber-to-blue gradient instead of stark black.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling Visual Feedback Loops
&lt;/h2&gt;

&lt;p&gt;Both workflows use the &lt;a href="https://block.github.io/goose/docs/mcp/chrome-devtools-mcp" rel="noopener noreferrer"&gt;Chrome DevTools MCP server&lt;/a&gt; so goose can see the output and iterate on it. This created a conflict where multiple instances couldn't use the same Chrome profile. I didn't want a manual step, so I asked the agent if it was possible to run Chrome DevTools in parallel. The solution was assigning separate user data directories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# genuary recipe example&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stdio&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Chrome Dev Tools&lt;/span&gt;
  &lt;span class="na"&gt;cmd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-y&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chrome-devtools-mcp@latest&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--userDataDir&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/tmp/genuary-harness-chrome-profile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I automated execution so I could study taste, constraint design, and feedback loops.&lt;/p&gt;

&lt;p&gt;The two approaches behaved very differently. The harness-based workflow was more reliable and efficient, but it produced more predictable results. It followed instructions faithfully and optimized for consistency.&lt;/p&gt;

&lt;p&gt;The skills-based approach was messier. It surfaced more surprises, made stranger connections, and required more editorial intervention. But the output felt more like a collaboration than a pipeline.&lt;/p&gt;

&lt;p&gt;What this reinforced for me is that the "AI vs. human" framing is too simplistic. Automation handles repetition and speed well. Taste still lives in constraint-setting, curation, and deciding what should never happen. I ended up not automating taste. Instead, the end result was a system that made my preferences legible enough to be reflected back to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  See the Code
&lt;/h2&gt;

&lt;p&gt;The code and full transcripts live in &lt;a href="https://github.com/blackgirlbytes/genuary2026" rel="noopener noreferrer"&gt;my Genuary 2026 repo&lt;/a&gt;. Each day folder contains the complete conversation history, including the pitches, iterations, and the back-and-forth between me and the agent. You can also view the creations on the &lt;a href="https://genuary2026.vercel.app/" rel="noopener noreferrer"&gt;Genuary 2026 site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>goose</category>
      <category>skills</category>
    </item>
    <item>
      <title>The Worst Thing to Happen to React and Next.js: React2Shell</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Wed, 31 Dec 2025 08:34:22 +0000</pubDate>
      <link>https://forem.com/blackgirlbytes/the-worst-thing-to-happen-to-react-and-nextjs-react2shell-5fdf</link>
      <guid>https://forem.com/blackgirlbytes/the-worst-thing-to-happen-to-react-and-nextjs-react2shell-5fdf</guid>
      <description>&lt;p&gt;"I ain't reading all that. I'm happy for you tho, or sorry that happened."&lt;/p&gt;

&lt;p&gt;That was my internal reaction when I saw the headlines about CVE-2025-55182, more commonly called React2Shell. I had deadlines to meet. I bookmarked the articles, added "read React vulnerability stuff" to my todo list, and kept working. I didn't think it would affect me anyway. Most of my projects are demos for my work in Developer Relations. Who would bother attacking those?&lt;/p&gt;

&lt;p&gt;A few days later, someone messaged me: "Hey, your demo site redirected me to some sketchy crypto site."&lt;/p&gt;

&lt;p&gt;Wait, what? My heart sank to my stomach.&lt;/p&gt;

&lt;p&gt;I frantically combed through my repository. Everything looked fine. The commit history was clean, the codebase unchanged, and I couldn't find any redirect logic that would explain what users were experiencing. I felt that uncomfortable combination of confusion and exposure. Someone was exploiting my project, but I couldn't figure out how they'd gotten in.&lt;/p&gt;

&lt;p&gt;I resorted to asking goose, my AI coding agent, to help me investigate since this was outside my usual debugging territory. It suggested the injection might be happening at the DNS level or somewhere in my Railway deployment infrastructure. I changed my DNS settings, and it looked like that fixed it.&lt;/p&gt;

&lt;p&gt;A few days later, I went to check my demo site to see if things were still the same, and it was redirecting to the crypto site. My hands felt clammy.&lt;/p&gt;

&lt;p&gt;Then a memory surfaced. It felt like I was Raven from That's So Raven. The memory was of a fellow open source contributor who had pinged me saying: "Hey Rizel, make sure you update your Next.js projects. That React vulnerability is really serious." At the time, I thought, "I only have a demo project that needs upgrading. I can do it later."&lt;/p&gt;

&lt;p&gt;That's when it clicked. I realized attackers were exploiting CVE-2025-55182, a vulnerability in React Server Components that allowed them to execute arbitrary JavaScript on my server. They didn't need to touch my codebase at all. They just needed to send specially crafted HTTP requests to my application, and the vulnerable React deserialization would execute their code.&lt;/p&gt;

&lt;p&gt;I upgraded my Next.js and React dependencies immediately, and the redirects finally stopped for good.&lt;/p&gt;

&lt;p&gt;I felt foolish. A trusted colleague had literally warned me, and I'd put it on my "important but not urgent" list because I thought it didn't apply to me, except it did apply to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding React2Shell
&lt;/h2&gt;

&lt;p&gt;React Server Components introduced a new way for React applications to run code on the server. When you write a function marked with &lt;code&gt;'use server'&lt;/code&gt;, React automatically creates endpoints that handle communication between your client and server using something called the Flight protocol. This protocol serializes and deserializes data as it travels back and forth.&lt;/p&gt;

&lt;p&gt;The vulnerability was in how this deserialization worked. When your server received data through these automatically-generated Flight endpoints, it would unpack that data and process it. But the deserialization logic had a flaw. Attackers could craft malicious payloads that, when unpacked, would execute arbitrary code on your server instead of just calling your legitimate server functions.&lt;/p&gt;

&lt;p&gt;Think about it like this: imagine you have a package delivery system where you automatically open and process every package that arrives. CVE-2025-55182 was like someone figuring out they could put a bomb inside a package, and your system would dutifully unpack and activate it without checking what was inside first.&lt;/p&gt;

&lt;p&gt;In my case, the attackers were injecting code that intercepted HTTP responses and redirected users to crypto scam sites. But they could have done much worse. With arbitrary code execution on the server, they could have stolen my database credentials, exfiltrated my environment variables and API keys, installed persistent backdoors, or used my server for crypto mining. I got lucky.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who Was Affected
&lt;/h3&gt;

&lt;p&gt;This vulnerability affected applications using React Server Components across several common setups.&lt;/p&gt;

&lt;h4&gt;
  
  
  Affected versions included:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Next.js App Router on 15.x, 16.x, and 14.x canary releases after 14.3.0-canary.77
&lt;/li&gt;
&lt;li&gt;React versions 19.0, 19.1.0, 19.1.1, and 19.2.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The impact extended beyond Next.js. Any framework experimenting with or building on React Server Components was potentially affected, including Vite RSC plugins and React Router’s unstable RSC APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Was So Severe
&lt;/h3&gt;

&lt;p&gt;Because it required no authentication, no user interaction, and had near-perfect reliability, this vulnerability was rated CVSS 10.0, the maximum severity score. Security researchers observed active exploitation in the wild starting December 5th, just two days after the patches were released, with some of the activity linked to threat groups with suspected ties to state actors.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Do Right Now
&lt;/h2&gt;

&lt;p&gt;If you're running any React or Next.js application with Server Components or Server Actions, upgrade immediately. Not later, not after you finish your current sprint, not once you've checked whether you're actually affected.&lt;/p&gt;

&lt;p&gt;After upgrading, rotate your secrets. Even if you didn't notice any exploitation like I did, if you were vulnerable, you should assume attackers might have accessed your environment variables. Change your database passwords, API keys, deployment tokens, and anything else sensitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;This experience reminded me that I’m not exempt from security vulnerabilities . Even toy projects and demos matter because they're running on real servers with real access to real infrastructure.&lt;/p&gt;

&lt;p&gt;Online security often feels like someone else's problem until it becomes your problem. Don't wait until you're scrambling to understand how your project got compromised.  &lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My Predictions for MCP and AI-Assisted Coding in 2026</title>
      <dc:creator>Rizèl Scarlett</dc:creator>
      <pubDate>Wed, 31 Dec 2025 01:36:04 +0000</pubDate>
      <link>https://forem.com/blackgirlbytes/my-predictions-for-mcp-and-ai-assisted-coding-in-2026-16bm</link>
      <guid>https://forem.com/blackgirlbytes/my-predictions-for-mcp-and-ai-assisted-coding-in-2026-16bm</guid>
      <description>&lt;p&gt;I'm writing this fully aware that predictions about AI often age badly.&lt;/p&gt;

&lt;p&gt;I don't want to sound like those CEOs who confidently announce that AI will replace engineers in six months, only to quietly move the timeline when nothing happens. Instead, this is a personal thought experiment.&lt;/p&gt;

&lt;p&gt;I've been experimenting with AI-assisted coding since it was still taboo to admit you were doing it. I started in 2021 while working at GitHub, helping developers understand the value of well-written prompts through GitHub Copilot. I was an early user of ChatGPT, alongside Claude and many other tools, long before "prompting" became its own discipline.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/inr1fFxvFAw"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Today, I'm a Developer Advocate for &lt;a href="http://block.github.io/goose" rel="noopener noreferrer"&gt;goose&lt;/a&gt;, which serves as a reference implementation for Model Context Protocol and one of the first MCP clients. I use multiple MCP servers daily workflow to solve real problems. &lt;/p&gt;

&lt;p&gt;All of that gives me a decent sense of where things &lt;em&gt;might&lt;/em&gt; head next.&lt;/p&gt;

&lt;p&gt;So I decided to make a few predictions for 2026, mostly to sharpen my own visionary skills. Will any of these come true? Would I tweak them a year from now? Let's find out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These are my personal opinions. I'm not speaking on behalf of my employer or any project I work on.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Prediction 1: AI Code Review Gets Solved
&lt;/h2&gt;

&lt;p&gt;By the end of 2026, I believe we'll have cracked AI code review.&lt;/p&gt;

&lt;p&gt;Right now, one of the biggest bottlenecks in software development, especially in open source, is review capacity. People generate code faster than ever with AI, but that speed shifts pressure downstream. Maintainers, tech leads, and engineering managers now face more pull requests, more diffs, and more surface area to validate.&lt;/p&gt;

&lt;p&gt;We already see AI-powered code review tools, but none fully hit the mark. They often feel noisy, overly rigid, or disconnected from real-world developer workflows. &lt;/p&gt;

&lt;p&gt;Recently, Aiden Bai publicly &lt;a href="https://x.com/aidenybai/status/2004033871244099613?s=20" rel="noopener noreferrer"&gt;shared thoughtful, constructive feedback&lt;/a&gt; on how AI code review tools like CodeRabbit could improve.&lt;/p&gt;

&lt;p&gt;Beyond the controversy around how CodeRabbit responded, the attention his tweet received signaled something important: developers are actively hoping for a better solution.&lt;/p&gt;

&lt;p&gt;By 2026, I expect either an existing product to meaningfully level up or a new company to enter and get it right. This is one of the most pressing problems in the space, and I think the industry will prioritize fixing it.&lt;/p&gt;

&lt;p&gt;If you want to stay on top of developments in AI code review, I recommend following &lt;a href="https://x.com/nnennahacks/status/1998408841285837197?s=20" rel="noopener noreferrer"&gt;Nnenna Ndukwe&lt;/a&gt; / &lt;a class="mentioned-user" href="https://dev.to/nnennahacks"&gt;@nnennahacks&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  

&lt;iframe class="tweet-embed" id="tweet-1998408841285837197-64" src="https://platform.twitter.com/embed/Tweet.html?id=1998408841285837197"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1998408841285837197-64');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1998408841285837197&amp;amp;theme=dark"
  }






&lt;/h2&gt;

&lt;h2&gt;
  
  
  Prediction 2: MCP Apps Become the Default
&lt;/h2&gt;

&lt;p&gt;I think MCP Apps will become a core part of how people interact with AI agents.&lt;/p&gt;

&lt;p&gt;MCP Apps are the successor to &lt;a href="https://mcpui.dev/" rel="noopener noreferrer"&gt;MCP-UI&lt;/a&gt;, which first showed that agents didn't need to respond with text alone, but could render interactive interfaces directly inside the host environment. Think embedded web UIs, buttons, toggles, and selections. Users express intent through interaction rather than explanation.&lt;/p&gt;

&lt;p&gt;As this pattern gained traction, it became clear that interactive interfaces needed first-class support in the protocol itself. MCP Apps build on that momentum and are now being incorporated into the MCP standard.&lt;/p&gt;

&lt;p&gt;Below is a video of MCP-UI in action: &lt;br&gt;


  &lt;iframe src="https://www.youtube.com/embed/PvdptUZ3XeU?start=620"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;This matters beyond developer ergonomics. For years, companies tried to keep users inside their apps with embedded chatbots, hoping increased "stickiness" would drive revenue. That approach never fully worked. Meanwhile, user behavior shifted. People now go directly to AI tools like ChatGPT for answers instead of navigating websites, even if they aren't engineers.&lt;/p&gt;

&lt;p&gt;MCP Apps flip the model. Instead of pulling users into your app, your app meets users inside their AI environment.&lt;/p&gt;

&lt;p&gt;We already see early adoption. &lt;a href="https://developers.openai.com/apps-sdk/" rel="noopener noreferrer"&gt;OpenAI is moving in this direction with ChatGPT&lt;/a&gt;, and &lt;a href="https://youtube.com/shorts/VqIowCU6ZNg" rel="noopener noreferrer"&gt;goose adopted MCP-UI early&lt;/a&gt; and is close to shipping full MCP Apps support. Other platforms are taking similar steps.&lt;/p&gt;

&lt;p&gt;To learn more about MCP Apps, check out this &lt;a href="https://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prediction 3: Agents Become Portable Across Platforms
&lt;/h2&gt;

&lt;p&gt;I think agents will follow users wherever they work.&lt;/p&gt;

&lt;p&gt;Today, I benefit heavily from MCP servers because they make it possible to connect agents to tools and systems. Still, I there's friction. Many users grow attached to a specific agent and want it available across environments without constant reconfiguration.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://agentclientprotocol.com/overview/introduction" rel="noopener noreferrer"&gt;Agent Client Protocol&lt;/a&gt; becomes interesting. ACP allows an agent to run inside any editor or environment that supports the protocol, without tightly coupling it to a specific plugin or extension.&lt;/p&gt;

&lt;p&gt;We felt this pain firsthand with goose. Maintaining a VS Code extension proved difficult. goose would evolve, the extension would lag, and users would hit breakage. ACP changed that dynamic. Instead of tightly coupling the agent to a plugin, the editor becomes the client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://zed.dev/" rel="noopener noreferrer"&gt;Zed Industries&lt;/a&gt; introduced this model. When I tried goose inside the Zed editor, the experience felt noticeably smoother. Editors from &lt;a href="https://www.jetbrains.com/help/ai-assistant/acp.html" rel="noopener noreferrer"&gt;JetBrains&lt;/a&gt; have also adopted the protocol. ACP tends to get less attention than MCP, partly because it's less flashy and partly because the acronym overlaps with other agent-related protocols. Even so, the impact is real.&lt;/p&gt;

&lt;p&gt;Here's where I get more ambitious. I don't think this stops at editors. Over time, agent portability may extend to design tools, browsers, and other platforms. I can imagine bringing goose, Codex, or Claude Code directly into tools like Figma without rebuilding the integration each time. This part is more speculative, but the direction feels plausible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prediction 4: DIY Agent Configuration Hits a Ceiling
&lt;/h2&gt;

&lt;p&gt;This one feels riskier to say out loud, but I think we eventually move away from heavy context engineering and excessive configuration.&lt;/p&gt;

&lt;p&gt;Right now, we compensate for model limitations by adding layers of structure: rules files, memory files, subagents, reusable skills, system prompt overrides, toggles, and switches. All of these help agents behave more reliably, and in many cases, they're necessary, especially for large codebases, legacy systems, and high-impact code changes.&lt;/p&gt;

&lt;p&gt;As an engineer, I find this exciting. Configuring my setup feels participatory. I enjoy shaping how an agent reasons and responds. There's satisfaction in tuning behavior instead of treating AI as a black box.&lt;/p&gt;

&lt;p&gt;But there's another side we haven't fully felt the consequences of yet.&lt;/p&gt;

&lt;p&gt;Every week introduces a new "best practice." Another rule or configuration users feel pressure to adopt. At some point, the overhead may outweigh the benefit. Instead of building, people spend more time configuring the act of building.&lt;/p&gt;

&lt;p&gt;I already see developers opting out. Some reject AI because of poor early experiences. Others reject it because the process feels exhausting. They just want to write code.&lt;/p&gt;

&lt;p&gt;I've seen this pattern before. When Kubernetes became widely adopted, it unlocked enormous power but also exposed developers to infrastructure complexity they weren't meant to manage. The response wasn't to turn every developer into a Kubernetes expert, but to introduce platform teams, DevOps roles, and abstractions that absorbed that complexity.&lt;/p&gt;

&lt;p&gt;I don't want to leave anyone behind in this AI era. &lt;/p&gt;

&lt;p&gt;The thought genuinely makes me sad when we say things like "People will get left behind," so I'm brainstorming ways of how to make sure everyone "eats".&lt;/p&gt;

&lt;p&gt;When we approach a similar inflection point with agents, I see two likely paths forward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Tooling improves to the point where most configuration fades into the background.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Companies formalize roles around AI enablement. I've already seen early versions of this. We have internal AI champions at and enablement groups (led by my manager Angie Jones) that help teams use agents safely and effectively.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Personally, I hope for balance. I enjoy configuration and depth, but I don't think productivity scales if every repo demands a complex setup just to get started.&lt;/p&gt;




&lt;p&gt;Those are my predictions for 2026. Let's revisit this in a year and see what holds up.&lt;/p&gt;

&lt;p&gt;What are your predictions? And what do you think of mine?  &lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>agents</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
