<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0"><channel><title>Pulumi Blog: Mcp</title><link>https://www.pulumi.com/blog/tag/mcp/</link><description>Pulumi blog posts: Mcp.</description><language>en-us</language><pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate><item><title>Stop Tuning Prompts. Build a Harness.</title><link>https://www.pulumi.com/blog/stop-tuning-prompts-build-a-harness/</link><pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/stop-tuning-prompts-build-a-harness/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/stop-tuning-prompts-build-a-harness/index.png" /&gt;
&lt;p&gt;Anthropic shipped a piece earlier this month called &lt;a href="https://claude.com/blog/how-claude-code-works-in-large-codebases-best-practices-and-where-to-start"&gt;How Claude Code Works in Large Codebases&lt;/a&gt;. I have not read anything more useful about coding agents this year. The core claim, in their words: &lt;em&gt;&amp;ldquo;the ecosystem built around the model—the harness—determines how Claude Code performs more than the model alone.&amp;rdquo;&lt;/em&gt; In my phrasing: in a real codebase, the model is the smaller variable. The layer of context and tooling you wire around the agent matters more than which version of Sonnet or Opus is behind it.&lt;/p&gt;
&lt;p&gt;The post stays high-level, which is the right move for a launch piece. What I want to do here is land it. Same seven pieces, but with the wiring you would actually put in a repo, in the order I would put it.&lt;/p&gt;
&lt;h2 id="how-claude-code-navigates-without-an-index"&gt;How Claude Code navigates without an index&lt;/h2&gt;
&lt;p&gt;Anthropic&amp;rsquo;s writeup says Claude Code works from the live codebase and does not require a &lt;a href="https://claude.com/blog/how-claude-code-works-in-large-codebases-best-practices-and-where-to-start"&gt;codebase index&lt;/a&gt; to be built, maintained, or uploaded. The agent navigates the way an engineer would, with &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, file reads, and reference-following. Anthropic calls this &lt;a href="https://www.anthropic.com/claude-code"&gt;agentic search&lt;/a&gt;, and the upside is obvious: no separate index exists for you to keep fresh.&lt;/p&gt;
&lt;p&gt;The downside is also obvious. An engineer who has never seen your repo and only has shell tools will flounder if you drop them in the root with no map. That is your agent on day one. Everything that follows is about giving it the map.&lt;/p&gt;
&lt;h2 id="the-ai-layer-in-seven-pieces"&gt;The AI layer in seven pieces&lt;/h2&gt;
&lt;p&gt;Every codebase used to have two artifacts engineers cared about: the code and the tests. A third exists now. Call it the AI layer, or the harness, or whatever you want. This layer is the set of context and tools you give your coding agent to operate in this specific repo. Anthropic breaks it into seven pieces, and each one solves a different scaling problem.&lt;/p&gt;
&lt;p&gt;&lt;img src="./harness-layers.png" alt="Three layers of a codebase: code, tests, and the AI layer expanded into CLAUDE.md, hooks, skills, plugins, LSP, MCP servers, and subagents."&gt;&lt;/p&gt;
&lt;p&gt;Anthropic gives each piece a role: CLAUDE.md is the foundation, hooks do self-improvement, skills are progressive disclosure, plugins handle distribution, LSP gives navigation, MCP is extension, subagents split exploration from editing. They are not equal in usage either. CLAUDE.md is read at the start of each session and stays in context for the duration. The others fire when relevant.&lt;/p&gt;
&lt;h2 id="lean-and-layered-claudemd"&gt;Lean and layered CLAUDE.md&lt;/h2&gt;
&lt;p&gt;The single biggest mistake I see is a root &lt;code&gt;CLAUDE.md&lt;/code&gt; that has grown into a small book. Two thousand lines of conventions for parts of the repo the current task will never touch. Every session pays the tax. Anthropic&amp;rsquo;s own guidance is to keep these files focused on what applies broadly so they do not become a drag on performance, and you can feel that drag in practice: the agent gets cautious, slow, and oddly literal.&lt;/p&gt;
&lt;p&gt;Keep the root file lean. What is this repo, broadly. The tech stack. The commands the agent will need (&lt;code&gt;make test&lt;/code&gt;, &lt;code&gt;make lint&lt;/code&gt;, how to run the dev server). General conventions that apply everywhere. That is most of what belongs there.&lt;/p&gt;
&lt;p&gt;Local conventions go in subdirectory &lt;code&gt;CLAUDE.md&lt;/code&gt; files. When the agent starts in a subdirectory, Claude Code walks upward from the working directory and loads every &lt;code&gt;CLAUDE.md&lt;/code&gt; it finds on the way to the repo root, so root context is never lost and intermediate layers stack in the order you would expect. Claude Code can also discover files below the current working directory when it reads files in those subdirectories. That means &lt;code&gt;services/api/CLAUDE.md&lt;/code&gt; only joins the session when the work reaches that service. Same for &lt;code&gt;services/billing/&lt;/code&gt;, the frontend, the data layer.&lt;/p&gt;
&lt;p&gt;If you already know the task is scoped to one service, start the agent in that subdirectory. The working directory becomes the focus, and the agent stays out of unrelated code unless you tell it otherwise. Most of the time, you know.&lt;/p&gt;
&lt;p&gt;Two more cheap wins live in the same neighborhood. Scope the &lt;code&gt;make test&lt;/code&gt; and &lt;code&gt;make lint&lt;/code&gt; commands so the subdirectory version runs only the slice the agent is working in, instead of the whole repo on every change. And version-control your exclusion rules in &lt;code&gt;.claude/settings.json&lt;/code&gt; so the agent never reads &lt;code&gt;dist/&lt;/code&gt;, generated SDKs, or vendored code. Every file the agent skips is tokens you keep for the work that matters. If your directory layout is unconventional or has historical baggage, Anthropic also suggests adding a short codebase map to the root &lt;code&gt;CLAUDE.md&lt;/code&gt; so the agent has somewhere to anchor.&lt;/p&gt;
&lt;h2 id="hooks-that-make-the-harness-self-improving"&gt;Hooks that make the harness self-improving&lt;/h2&gt;
&lt;p&gt;Most teams use hooks as guardrails. Block edits in &lt;code&gt;vendor/&lt;/code&gt;, refuse to delete migrations, kill the run if a secret turns up in a diff. That is fine and you should do it. But hooks have a second life that almost no one uses, and that second life is the more interesting one.&lt;/p&gt;
&lt;p&gt;Both kinds register the same way, in &lt;code&gt;.claude/settings.json&lt;/code&gt;, against named events Claude Code fires during a session:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;SessionStart&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;uv run --directory \&amp;#34;$CLAUDE_PROJECT_DIR\&amp;#34; python .claude/hooks/session_start_context.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;Stop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;uv run --directory \&amp;#34;$CLAUDE_PROJECT_DIR\&amp;#34; python .claude/hooks/propose_claude_md.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A &lt;code&gt;SessionStart&lt;/code&gt; hook fires before the agent has done anything. Whatever the script prints to stdout is injected straight into the session as context, so you can preload the things the agent would otherwise have to spend a turn discovering: the current branch, the uncommitted diff, the last few commits. For a larger team you might fetch the Confluence or Notion page that owns the directory the engineer is working in. Every developer starts each session pre-oriented, with no manual setup.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;SessionStart hook — prints orientation Claude reads as session context.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CLAUDE_PROJECT_DIR&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;git&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;# Orientation&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;## Branch&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rev-parse&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;--abbrev-ref&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;HEAD&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;## Uncommitted changes&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;--porcelain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;(clean)&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;## Recent commits&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;log&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-5&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;--oneline&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;Stop&lt;/code&gt; hook is the more interesting one. It fires when the agent finishes its turn. At that moment the session context is still fresh, the diff is still small, and you have a free shot at a question nobody asks: did anything I changed invalidate the rules I wrote down? Spawn a separate headless Claude session, hand it the diff and the relevant &lt;code&gt;CLAUDE.md&lt;/code&gt; files, ask it to propose updates, and write the result to a markdown review file. You read it when you are ready. The CLAUDE.md files stop going stale on their own.&lt;/p&gt;
&lt;p&gt;The trick is to make the hook itself cheap and dispatch the LLM call in the background, so the end of every turn does not block on a reflection:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Stop hook — dispatch a headless Claude reflection in the background.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# The reflector spawns its own headless Claude, whose Stop hook lands back&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# here. The lock prevents infinite recursion.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;REFLECT_LOCK&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;CLAUDE_PROJECT_DIR&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;git&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;diff&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;HEAD&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;REFLECT_LOCK&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;uv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;run&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;python&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;.claude/hooks/reflect_claude_md.py&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;reflect_claude_md.py&lt;/code&gt; is the part that calls a headless &lt;code&gt;claude&lt;/code&gt; against the diff and writes &lt;code&gt;.claude/claude-md-review.md&lt;/code&gt;. You can grow it from twenty lines to two hundred without ever blocking the agent.&lt;/p&gt;
&lt;p&gt;The pattern that ties the two together: hooks let the harness improve itself in the background while you do the actual work.&lt;/p&gt;
&lt;h2 id="path-scoped-skills"&gt;Path-scoped skills&lt;/h2&gt;
&lt;p&gt;Skills are where the agent learns how to do a &lt;em&gt;thing&lt;/em&gt;. CLAUDE.md is conventions (&amp;ldquo;every route is registered here&amp;rdquo;). Skills are workflows (&amp;ldquo;here is how you add a new route in this repo, end to end&amp;rdquo;). The two overlap, but the framing keeps me honest: rules in CLAUDE.md, recipes in skills.&lt;/p&gt;
&lt;p&gt;The piece of the skills system most teams miss is the path scope. A skill can declare which directories it activates in. A &lt;code&gt;create-api-endpoint&lt;/code&gt; skill that only loads when the agent is editing under &lt;code&gt;services/api/&lt;/code&gt; is invisible the rest of the time. With dozens of skills in a real repo, scoping is the difference between a useful library and a wall of irrelevant prompts.&lt;/p&gt;
&lt;p&gt;The mental model: progressive disclosure for expertise. Most knowledge in a large codebase is local. Load it locally.&lt;/p&gt;
&lt;h2 id="symbol-level-search-through-lsp-and-mcp"&gt;Symbol-level search through LSP and MCP&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;grep&lt;/code&gt; is fine until it isn&amp;rsquo;t. Past six-digit line counts, plain string search gets slow, returns too much, and burns tokens reading files the agent did not need to open. You also lose what every IDE has done for decades: jump-to-definition, find-references, hover-for-types.&lt;/p&gt;
&lt;p&gt;You can give the agent the same navigation. Run a language server locally, wrap it in a small MCP server, expose two or three tools: &lt;code&gt;where_is&lt;/code&gt;, &lt;code&gt;find_references&lt;/code&gt;, &lt;code&gt;goto_definition&lt;/code&gt;. The agent now searches by symbol, not by string. A request like &amp;ldquo;find every place &lt;code&gt;monthly_total_cents&lt;/code&gt; is referenced&amp;rdquo; returns one definition and the actual references, instead of fifty grep hits that mention the substring in unrelated comments.&lt;/p&gt;
&lt;p&gt;This is also where bigger orgs invest. Custom MCP servers that expose internal search systems, the code-ownership graph, the design-doc index. The patterns are the same; the targets are domain-specific. The point is that the agent does not have to brute-force its way through your repo when you already have better tools for finding things.&lt;/p&gt;
&lt;figure&gt;
&lt;img src="./harness-frequency.png" alt="The Claude Code harness as a Gantt-style timeline across a session. CLAUDE.md spans the full session as a foundation. Hooks fire at the start and end. Skills, LSP, and MCP servers light up sporadically. Subagents run as a single longer task between exploration and editing."&gt;
&lt;figcaption&gt;&lt;em&gt;Image: &lt;a href="https://claude.com/blog/how-claude-code-works-in-large-codebases-best-practices-and-where-to-start"&gt;Anthropic, How Claude Code Works in Large Codebases&lt;/a&gt;.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="subagents-for-exploration"&gt;Subagents for exploration&lt;/h2&gt;
&lt;p&gt;The rule I follow: split exploration from editing. A subagent runs in its own context window. You ask which files implement the billing webhook flow, or what the user model looks like across services. It does the digging, and only the summary comes back to your primary session.&lt;/p&gt;
&lt;p&gt;The win is context budget, not parallelism. Exploration is wasteful by nature. The agent reads forty files to find the three that matter, and most of those forty get thrown away. If that happens in your primary session, your editing turns start with a context window already half full of noise. If it happens in a subagent, the noise stays there. You get the answer.&lt;/p&gt;
&lt;p&gt;Use the built-in Explore subagent liberally. Custom subagents earn their place when you have a workflow specific enough that a generic explorer is the wrong tool. The file shape is small: a single markdown file under &lt;code&gt;.claude/agents/&lt;/code&gt;, a short frontmatter block, and a prompt body. &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;tools&lt;/code&gt;, and &lt;code&gt;model&lt;/code&gt; are enough to start:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;name: explorer
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;description: Read-only repo explorer. Map a service or package without burning the main session&amp;#39;s context, then return findings.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tools: Read, Grep, Glob
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;model: sonnet
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;You are a read-only explorer. The parent agent will hand you one service or
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;package to map. Read its &lt;span class="sb"&gt;`CLAUDE.md`&lt;/span&gt; if there is one, then trace entry points,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;the public surface, and dependencies. Return findings as your final response.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;No edits.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restricting &lt;code&gt;tools&lt;/code&gt; to read-only is the load-bearing line. The model only sees the tools you expose, so an explorer subagent without &lt;code&gt;Write&lt;/code&gt; or &lt;code&gt;Edit&lt;/code&gt; has nothing to call when it gets tempted, even if the prompt body forgot to say so. Treat that as a strong default. If you need a hard guarantee, layer a &lt;code&gt;PreToolUse&lt;/code&gt; hook on top.&lt;/p&gt;
&lt;h2 id="dont-let-it-rot"&gt;Don&amp;rsquo;t let it rot&lt;/h2&gt;
&lt;p&gt;The harness is not a one-time setup. Models improve, and rules written for last year&amp;rsquo;s model often constrain this year&amp;rsquo;s. A note like &amp;ldquo;always split refactors into single-file changes&amp;rdquo; might have saved you in 2024 and might block a beneficial cross-file edit in 2026. Anthropic suggests reviewing your CLAUDE.md files every three to six months, or whenever performance feels like it has plateaued after a major model release. The stop-hook reflection gives you a head start. The rest is on you.&lt;/p&gt;
&lt;div class="rounded-lg bg-violet-50 p-6 my-8"&gt;
&lt;p class="heading-4 m-0 mb-3 flex items-center gap-1.5"&gt;Extend the harness to your stack&lt;/p&gt;
&lt;div class="body-base m-0 text-gray-950"&gt;Package your infrastructure workflows as Agent Skills and expose your Pulumi state through the Pulumi MCP server, so your coding agent works your stack the way your team does.&lt;/div&gt;
&lt;a href="https://www.pulumi.com/docs/ai/skills/" data-track="blog-body-cta" class="btn btn-primary mt-4"&gt;
Get started
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--regular size-4" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-arrow-right-regular"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id="assign-an-owner"&gt;Assign an owner&lt;/h2&gt;
&lt;p&gt;The last piece is not technical. The teams that get value out of Claude Code at scale have someone who owns the harness. A small platform-engineering team, or one DRI, or a hybrid PM/engineer doing it half-time. Their job is the same shape as owning a CI pipeline: write the conventions, build the skills, run the LSP wrapper, version the hooks, evangelize what works, retire what does not.&lt;/p&gt;
&lt;p&gt;Plugins are the distribution vehicle. A good harness that lives in one engineer&amp;rsquo;s dotfiles stays tribal. The same harness packaged as a plugin (or a private marketplace) is how a team of five hundred ends up running the same skills, the same MCP servers, and the same hooks without anyone having to remember to copy a config.&lt;/p&gt;
&lt;p&gt;The pattern that fails: ship Claude Code to the org on a Friday, hope adoption goes viral, watch every team grow its own slightly different version of &lt;code&gt;CLAUDE.md&lt;/code&gt; for six months. The pattern that works: a quiet build-out period, a small set of approved skills, a working plugin or two, a documented governance story, then broad access.&lt;/p&gt;
&lt;p&gt;Treat the harness like infrastructure.&lt;/p&gt;
&lt;h2 id="where-to-start"&gt;Where to start&lt;/h2&gt;
&lt;p&gt;The order that has worked for me, in any repo:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Trim the root &lt;code&gt;CLAUDE.md&lt;/code&gt; until it fits on one screen. Move the rest into subdirectories.&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;Stop&lt;/code&gt; hook that proposes updates to those &lt;code&gt;CLAUDE.md&lt;/code&gt; files in headless mode.&lt;/li&gt;
&lt;li&gt;Convert your three most common repeated tasks into path-scoped skills.&lt;/li&gt;
&lt;li&gt;Run a language server behind an MCP server. Stop searching by string.&lt;/li&gt;
&lt;li&gt;Get comfortable dispatching exploration to subagents.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most teams will plateau on step one for a week and find the agent is already noticeably sharper. The rest compounds. I have written more on the agent-tooling shift this is part of in &lt;a href="https://www.pulumi.com/blog/how-building-ai-agents-has-changed/"&gt;How Building AI Agents Has Changed in 2026&lt;/a&gt;, and on the workflow side in &lt;a href="https://www.pulumi.com/blog/top-8-claude-skills-devops-2026/"&gt;The Claude Skills I Actually Use for DevOps&lt;/a&gt; and &lt;a href="https://www.pulumi.com/blog/claude-code-orchestration-frameworks/"&gt;Superpowers, GSD, and GSTACK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The model will keep getting better. The harness is the work.&lt;/p&gt;</description><author>Engin Diri</author><category>ai</category><category>ai-agents</category><category>claude</category><category>mcp</category><category>devops</category></item><item><title>How Building AI Agents Has Changed in 2026</title><link>https://www.pulumi.com/blog/how-building-ai-agents-has-changed/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/how-building-ai-agents-has-changed/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/how-building-ai-agents-has-changed/index.png" /&gt;
&lt;p&gt;Twelve months ago, building an AI agent meant picking a framework, defining your tools, standing up a RAG pipeline, and writing a stack of glue code to wire it all together. That was the default playbook. The post-mortem on six months of work usually went the same way: half the time went into infrastructure that had nothing to do with the agent&amp;rsquo;s actual job.&lt;/p&gt;
&lt;p&gt;That isn&amp;rsquo;t where the work is anymore. Most of the middle layer is gone. The SDKs ship with the tools, the skills system replaced the upfront tool registry, and longer context windows pushed vector search out of the default slot it held all of last year.&lt;/p&gt;
&lt;p&gt;The shape is the same as a lot of infrastructure shifts before it. The hard thing got cheap, the cheap thing got expected, and the question moved up a level.&lt;/p&gt;
&lt;h2 id="the-old-playbook"&gt;The old playbook&lt;/h2&gt;
&lt;p&gt;A 2024 to 2025 agent project looked like this. You picked a framework, usually &lt;a href="https://www.langchain.com/"&gt;LangChain&lt;/a&gt;, &lt;a href="https://www.llamaindex.ai/"&gt;LlamaIndex&lt;/a&gt;, or an early version of &lt;a href="https://ai.pydantic.dev/"&gt;Pydantic AI&lt;/a&gt;. You wrote tool definitions, usually a wrapper around an API the agent would call. You stood up a RAG pipeline: chunk your documents, embed them, pick a vector database, write retrievers, layer reranking on top. Then you wrote the agent loop yourself, including prompt assembly, tool dispatch, retry logic, and observability.&lt;/p&gt;
&lt;p&gt;This was the default for good reasons. Foundation models had short context windows. They didn&amp;rsquo;t ship with file access. They couldn&amp;rsquo;t run code. If you wanted an agent to do anything useful with your data, you had to bring the data to the model in pre-digested chunks.&lt;/p&gt;
&lt;p&gt;The cost wasn&amp;rsquo;t only setup time. It was infra bills, retries against embedding APIs, and a context strategy that fought the model as the model got better. By mid-2025 the retrieval layer was often the bottleneck on quality. The agent would ask a question, get five plausible-looking chunks, and answer from those instead of the document you actually wanted it to read. Chunking decisions made on a Tuesday in March were still hurting answer quality six months later.&lt;/p&gt;
&lt;p&gt;Most teams I talked to in 2025 were tuning their RAG pipeline. Almost nobody enjoyed it.&lt;/p&gt;
&lt;h2 id="the-shift-three-things-changed-at-once"&gt;The shift: three things changed at once&lt;/h2&gt;
&lt;p&gt;Three changes landed close enough together that they collapsed the middle layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Built-in tools.&lt;/strong&gt; The &lt;a href="https://code.claude.com/docs/en/agent-sdk/overview"&gt;Claude Agent SDK&lt;/a&gt; ships with Read, Write, Edit, Bash, Grep, Glob, WebSearch, and WebFetch out of the box. &lt;a href="https://developers.openai.com/codex/sdk"&gt;OpenAI&amp;rsquo;s Codex SDK&lt;/a&gt; is similar in shape, with shell and file tools available to the agent by default. These are the tools every agent project was rebuilding in 2024, often as a side quest to the work the agent was actually meant to do. A &lt;code&gt;Read&lt;/code&gt; that handles binary files. A &lt;code&gt;Bash&lt;/code&gt; that streams output and respects working directory. A &lt;code&gt;Grep&lt;/code&gt; that doesn&amp;rsquo;t choke on large files. The 80% of agent tooling everyone was paying their team to reimplement is now table stakes.&lt;/p&gt;
&lt;p&gt;The consequence is that you can give an agent the ability to do real work with about ten lines of configuration. The flip side is that the differentiator moved up a layer. The value isn&amp;rsquo;t in having &lt;code&gt;Read&lt;/code&gt;. It&amp;rsquo;s in what the agent does with it.&lt;/p&gt;
&lt;p&gt;Anything outside the built-in toolbox plugs in through &lt;a href="https://modelcontextprotocol.io/"&gt;MCP&lt;/a&gt; servers. &lt;a href="https://thenewstack.io/model-context-protocol-roadmap-2026/"&gt;The registry has grown nearly 8x since early 2025&lt;/a&gt;, and every major model vendor now ships first-party support. The picture in 2026 is more layered than that, though. A lot of what used to call for an MCP server is now better served by the agent invoking a CLI through &lt;code&gt;Bash&lt;/code&gt; and wrapping the recipe in a skill. &lt;a href="https://www.scalekit.com/blog/mcp-vs-cli-use"&gt;Benchmarks put CLI-based tool calls at a fraction of the context cost of equivalent MCP calls&lt;/a&gt;, with fewer round-trips and fewer failure modes. MCP still earns its place for protocol-heavy work like browser control, OAuth flows, and streaming services, but it stopped being the automatic answer to &amp;ldquo;how do I give my agent a new capability.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Skills replaced tool stuffing.&lt;/strong&gt; The old way was to register every tool the agent might need at startup, eating context every turn whether the agent used the tool or not. A hundred tools meant a heavy system prompt before the agent had thought about anything. The skills pattern flips that. A skill is a small markdown package with a name and a one-line description. The agent sees the description (around 100 tokens) and only loads the body when it decides the skill is relevant. A hundred skills no longer means a hundred tools&amp;rsquo; worth of context tax. &lt;a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills"&gt;Anthropic frames this as progressive disclosure&lt;/a&gt;: because the body only loads on demand, the amount of content you can bundle into a single skill is effectively unbounded.&lt;/p&gt;
&lt;p&gt;Progressive disclosure isn&amp;rsquo;t a new idea. What&amp;rsquo;s new is that the agent harness now treats it as the default loading strategy instead of something you have to engineer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RAG got demoted.&lt;/strong&gt; This is the change with the biggest blast radius and the smallest amount of commentary. A year ago, &amp;ldquo;we need to add RAG&amp;rdquo; was the reflex answer when somebody asked how an agent would handle a corpus. Today that question splits three ways. If the corpus fits in the context window, put it in. If the agent can grep the filesystem, let it grep. If the corpus is genuinely too large for either, vector search is still right, but you&amp;rsquo;ll find that&amp;rsquo;s a smaller set of cases than it used to be. You can see this in the coding agents that already ship today. &lt;a href="https://cursor.com/blog/fast-regex-search"&gt;Cursor&lt;/a&gt;, Claude Code, and Devin lean on grep, find, and direct file reads more than vector search. &lt;a href="https://www.llamaindex.ai/blog/agentic-rag-with-llamaindex-2721b8a49ff6"&gt;LlamaIndex&amp;rsquo;s own writing on agentic retrieval&lt;/a&gt; is one of the clearer reads on where this is going.&lt;/p&gt;
&lt;p&gt;Vector search didn&amp;rsquo;t get worse. The context around it improved enough that it stopped being the right first move.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-building-ai-agents-has-changed/eras.png" alt="Three eras of agent building. 2024 to 2025: framework plus tools plus RAG plus glue code. Early 2026: SDK with built-in tools plus skills plus MCP. Today: same SDK base, with frameworks layered on only for multi-provider or complex orchestration."&gt;&lt;/p&gt;
&lt;p&gt;Taken together, what got pulled into the SDK is the middle of an agent project: the tools layer, the retrieval layer, and the loop. What&amp;rsquo;s left for the team is the system prompt, the skills, and the policies around what the agent is allowed to do.&lt;/p&gt;
&lt;h2 id="when-you-still-need-a-framework"&gt;When you still need a framework&lt;/h2&gt;
&lt;p&gt;The first reaction to a lot of this is to declare that frameworks are over. They aren&amp;rsquo;t, but the cases where you reach for one have narrowed.&lt;/p&gt;
&lt;p&gt;Pydantic AI is still the right choice when you want strong typing, deterministic output schemas, and an evaluation loop that matches how the rest of your Python codebase already thinks. &lt;a href="https://www.langchain.com/langgraph"&gt;LangGraph&lt;/a&gt; is still the right choice when your problem is genuinely a graph of agent states with branching and human approval steps. &lt;a href="https://openai.github.io/openai-agents-python/"&gt;OpenAI&amp;rsquo;s Agents SDK&lt;/a&gt; is built around explicit handoffs between agents and earns its place when that pattern fits how you want to decompose the work. &lt;a href="https://www.crewai.com/"&gt;CrewAI&lt;/a&gt; is the fastest path I&amp;rsquo;ve seen for prototyping a multi-agent system, as long as you can live with its opinions. Any team running production traffic across multiple model providers is going to want a routing layer that the official SDK from any single vendor isn&amp;rsquo;t going to give them. &lt;a href="https://www.anthropic.com/research/building-effective-agents"&gt;Anthropic&amp;rsquo;s own writing on building effective agents&lt;/a&gt; lands in the same place: start with the simplest thing, add complexity only when the problem demands it.&lt;/p&gt;
&lt;p&gt;The mental model that works for me: start with the SDK, reach for a framework when you outgrow it. &amp;ldquo;Outgrow&amp;rdquo; usually means one of four things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Multi-provider routing.&lt;/strong&gt; You&amp;rsquo;re running production traffic across more than one model vendor and need a routing layer the official SDKs don&amp;rsquo;t ship.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-agent orchestration.&lt;/strong&gt; Your problem genuinely decomposes into separate agents with handoffs, branching, or human approval steps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deterministic typing.&lt;/strong&gt; You need strong schemas and validation around inputs and outputs, and the rest of your codebase already thinks that way.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production observability.&lt;/strong&gt; You need eval loops, replay, or tracing beyond what the SDK provides out of the box.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If none of those four are biting, the SDK is probably enough, and adding a framework on top is a layer you&amp;rsquo;ll regret in six months.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.pulumi.com/blog/how-building-ai-agents-has-changed/decision.png" alt="Decision flow for picking an agent stack. Start with the SDK. If you need multi-provider routing, multi-agent orchestration, deterministic typing, or deeper observability, add a framework. Otherwise stay with the SDK."&gt;&lt;/p&gt;
&lt;div class="rounded-lg bg-violet-50 p-6 my-8"&gt;
&lt;p class="heading-4 m-0 mb-3 flex items-center gap-1.5"&gt;Point your agent at your infrastructure&lt;/p&gt;
&lt;div class="body-base m-0 text-gray-950"&gt;Give your coding agent grounded context from your Pulumi state graph through the Pulumi MCP server and Agent Skills, or reach for Pulumi Neo when you want an infrastructure agent built in.&lt;/div&gt;
&lt;a href="https://www.pulumi.com/docs/ai/" data-track="blog-body-cta" class="btn btn-primary mt-4"&gt;
Get started
&lt;svg xmlns="http://www.w3.org/2000/svg" class="ph-icon ph-icon--regular size-4" fill="currentColor" aria-hidden="true" focusable="false"&gt;&lt;use href="https://www.pulumi.com/icons/sprite.4a9ac1016b9d8a688a5e7e867f96bdf80115a0739af8c688ba04791e15f64461.svg#p-arrow-right-regular"/&gt;&lt;/svg&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id="where-this-lands-for-infrastructure-work"&gt;Where this lands for infrastructure work&lt;/h2&gt;
&lt;p&gt;Two things from the new agent shape map cleanly onto infrastructure work. The first is that &amp;ldquo;built-in tools plus governed actions&amp;rdquo; is the model an IaC platform was already running. The SDK assumes the agent has tools that do real work. The platform assumes those tools have policies, audit logs, and short-lived credentials around them. Those assumptions stack.&lt;/p&gt;
&lt;p&gt;The second is that a state graph is already structured context. You don&amp;rsquo;t need to chunk it. You don&amp;rsquo;t need to embed it. An agent reasoning over a Pulumi stack can grep its way through the program graph the same way it greps a codebase, and the answers are grounded in the same source of truth the rest of your platform uses. I wrote the deeper version in &lt;a href="https://www.pulumi.com/blog/grounded-ai-why-neo-knows-your-infrastructure/"&gt;Grounded AI: Why Neo Knows Your Infrastructure&lt;/a&gt;. The dark-factory and sprawl posts (&lt;a href="https://www.pulumi.com/blog/dark-factory-pattern-pulumi-autonomous-iac/"&gt;The Dark Factory Pattern for Infrastructure&lt;/a&gt; and &lt;a href="https://www.pulumi.com/blog/agent-sprawl-iac-platform-is-the-answer/"&gt;Agent Sprawl Is Here. Your IaC Platform Is the Answer.&lt;/a&gt;) are the places to go if you want to push on this further.&lt;/p&gt;
&lt;h2 id="start-with-the-sdk"&gt;Start with the SDK&lt;/h2&gt;
&lt;p&gt;A year ago, an agent project was 80% glue code and 20% the thing the agent actually did. On most projects today that ratio is flipped. If you&amp;rsquo;ve been sitting on an agent idea, build it the SDK way first and reach for a framework only when you hit something the SDK genuinely can&amp;rsquo;t do. Most teams will be surprised how often they don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s one agent you don&amp;rsquo;t have to build at all. &lt;a href="https://www.pulumi.com/product/neo/"&gt;Pulumi Neo&lt;/a&gt; is the same SDK-first shape applied to the IaC slice: tools that reason directly over your state graph, governed by the controls the rest of your platform already runs on. Save your own SDK time for the agents only you can build.&lt;/p&gt;
&lt;a
href="https://www.pulumi.com/product/neo/"
class="btn btn-primary"
&gt;
See how Pulumi Neo works
&lt;/a&gt;</description><author>Engin Diri</author><category>ai</category><category>ai-agents</category><category>claude</category><category>mcp</category><category>rag</category></item><item><title>Announcing Pulumi Remote MCP Server</title><link>https://www.pulumi.com/blog/remote-mcp-server/</link><pubDate>Tue, 07 Oct 2025 00:00:00 +0000</pubDate><guid>https://www.pulumi.com/blog/remote-mcp-server/</guid><description>
&lt;img src="https://www.pulumi.com/images/generated/blog/remote-mcp-server/index.png" /&gt;
&lt;p&gt;We&amp;rsquo;re excited to announce the Pulumi Remote MCP Server—a hosted service that brings AI-powered infrastructure management to any AI assistant that supports the &lt;a href="https://modelcontextprotocol.io"&gt;Model Context Protocol&lt;/a&gt;. Connect your favorite AI assistant to &lt;code&gt;https://mcp.ai.pulumi.com/mcp&lt;/code&gt; and instantly access your Pulumi Cloud infrastructure, search resources across stacks, and delegate complex automation tasks to &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/neo/"&gt;Pulumi Neo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-evolution-of-pulumi-mcp"&gt;The Evolution of Pulumi MCP&lt;/h2&gt;
&lt;p&gt;Earlier this year, we &lt;a href="https://www.pulumi.com/blog/mcp-server-ai-assistants/"&gt;launched the Pulumi MCP server&lt;/a&gt; as a local npm package that brought AI-assisted infrastructure management to developers&amp;rsquo; machines. The adoption and feedback from users and partners has been positive, validating the power of combining AI assistants with infrastructure-as-code.&lt;/p&gt;
&lt;p&gt;As the MCP ecosystem has matured and more organizations have adopted the protocol, a clear pattern has emerged: remote MCP servers are becoming the industry standard. Remote servers provide a key advantage—&lt;strong&gt;accessibility&lt;/strong&gt;. One endpoint works everywhere, with no per-machine setup.&lt;/p&gt;
&lt;p&gt;Following industry trends and feedback from users and partners, we&amp;rsquo;re introducing the Remote MCP Server to ease installation and version management. The remote server preserves everything developers love about the local version while adding powerful new capabilities like seamless Pulumi Neo integration.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The local MCP server continues to be available and fully supported for developers who prefer local tooling or need offline capabilities.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="why-remote-mcp"&gt;Why Remote MCP?&lt;/h2&gt;
&lt;p&gt;The Pulumi Remote MCP Server runs as a hosted service. Instead of managing local installations, you configure it once and get automatic updates and consistent functionality across all your development environments.&lt;/p&gt;
&lt;h3 id="zero-local-setup-universal-access"&gt;Zero local setup, universal access&lt;/h3&gt;
&lt;p&gt;Instead of installing npm packages, you simply configure your AI assistant with a single URL: &lt;code&gt;https://mcp.ai.pulumi.com/mcp&lt;/code&gt;. That&amp;rsquo;s it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No per-machine installations&lt;/strong&gt; - Works the same on your laptop, desktop, or cloud workstation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No manual updates&lt;/strong&gt; - New features and improvements roll out automatically to all users&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Works with any MCP-compatible AI assistant&lt;/strong&gt; - Cursor, Claude Code, Windsurf, Claude Desktop, and more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For instructions on how to configure different AI assistants, see &lt;a href="https://www.pulumi.com/docs/iac/using-pulumi/mcp-server"&gt;Pulumi MCP Server&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="centralized-authentication--secrets"&gt;Centralized authentication &amp;amp; secrets&lt;/h3&gt;
&lt;p&gt;Remote MCP also solves a critical security challenge: credential management. Instead of scattering Pulumi Access Tokens across laptops, containers, and scripts, the Remote MCP Server uses OAuth-based authentication with your Pulumi Cloud organization.&lt;/p&gt;
&lt;p&gt;When you first connect, a browser window opens where you:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enter your Pulumi Access Token (which is validated server-side)&lt;/li&gt;
&lt;li&gt;Select which organization to access&lt;/li&gt;
&lt;li&gt;Return to your AI assistant - now authenticated&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your credentials are stored securely in Pulumi Cloud, not on your individual machine.&lt;/p&gt;
&lt;h2 id="what-can-you-do-with-it"&gt;What Can You Do With It?&lt;/h2&gt;
&lt;p&gt;The Remote MCP Server is your AI assistant&amp;rsquo;s gateway to your entire Pulumi infrastructure. It combines real-time access to your cloud resources with the power of autonomous infrastructure automation through Pulumi Neo.&lt;/p&gt;
&lt;h3 id="discover-and-query-infrastructure"&gt;Discover and query infrastructure&lt;/h3&gt;
&lt;p&gt;Your AI assistant can instantly explore what you&amp;rsquo;ve deployed across your entire organization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List all stacks in your organization&lt;/li&gt;
&lt;li&gt;Search for specific resources across all stacks&lt;/li&gt;
&lt;li&gt;Find resources by type, name, tags, or any property&lt;/li&gt;
&lt;li&gt;Check for policy violations&lt;/li&gt;
&lt;li&gt;View organization members and their roles&lt;/li&gt;
&lt;li&gt;Identify security gaps, untagged resources, or misconfigured infrastructure&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ask questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Show me all RDS databases without encryption enabled&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Which stacks have resources in us-east-1?&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Find all Lambda functions using deprecated runtimes&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="generate-infrastructure-code"&gt;Generate infrastructure code&lt;/h3&gt;
&lt;p&gt;The MCP server connects directly to the &lt;a href="https://www.pulumi.com/registry/"&gt;Pulumi Registry&lt;/a&gt;, giving your AI assistant access to thousands of cloud resources with complete type information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Browse available resources&lt;/li&gt;
&lt;li&gt;Get detailed resource schemas&lt;/li&gt;
&lt;li&gt;Access property documentation, input/output types, and examples&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your AI assistant can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Look up the exact properties for any cloud resource&lt;/li&gt;
&lt;li&gt;Generate type-safe infrastructure code in TypeScript, Python, Go, or any Pulumi language&lt;/li&gt;
&lt;li&gt;Include proper configurations, security settings, and best practices&lt;/li&gt;
&lt;li&gt;Reference real documentation and examples&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means code generation is more accurate and up-to-date with the latest provider versions.&lt;/p&gt;
&lt;h3 id="autonomous-infrastructure-with-pulumi-neo"&gt;Autonomous infrastructure with Pulumi Neo&lt;/h3&gt;
&lt;p&gt;This is where the Remote MCP Server truly shines. For complex infrastructure tasks that require multiple steps, code changes, testing, and pull requests, your AI assistant can delegate directly to &lt;a href="https://www.pulumi.com/docs/pulumi-cloud/neo/"&gt;Pulumi Neo&lt;/a&gt;—Pulumi&amp;rsquo;s autonomous infrastructure AI agent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What makes Neo special:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Neo isn&amp;rsquo;t just an AI that writes code—it&amp;rsquo;s an AI that &lt;em&gt;ships&lt;/em&gt; infrastructure changes autonomously:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-step planning&lt;/strong&gt; - Neo breaks down complex requests into actionable plans&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code generation at scale&lt;/strong&gt; - Works across multiple stacks and repositories&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automated testing&lt;/strong&gt; - Validates changes before creating pull requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pull request workflows&lt;/strong&gt; - Creates PRs with detailed explanations and comments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous execution&lt;/strong&gt; - Runs in Pulumi Cloud, not consuming your local resources&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Human-in-the-loop&lt;/strong&gt; - Pauses for approval on critical changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Real-world Neo examples:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Security remediation:
&lt;strong&gt;&amp;ldquo;Ask Neo to find all security groups allowing SSH from 0.0.0.0/0 and create a PR restricting them to my office IP&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Neo will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Search your infrastructure for overly permissive security groups&lt;/li&gt;
&lt;li&gt;Create a detailed plan for restricting access&lt;/li&gt;
&lt;li&gt;Generate infrastructure code changes&lt;/li&gt;
&lt;li&gt;Create a pull request with explanations&lt;/li&gt;
&lt;li&gt;Wait for your approval to merge&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Runtime migrations:
&lt;strong&gt;&amp;ldquo;Ask Neo to migrate all Lambda functions from Python 3.8 to Python 3.12 and create PRs for each affected stack&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Neo handles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Finding all Lambda functions with Python 3.8&lt;/li&gt;
&lt;li&gt;Checking for compatibility issues&lt;/li&gt;
&lt;li&gt;Updating runtime configurations&lt;/li&gt;
&lt;li&gt;Running tests to ensure functionality&lt;/li&gt;
&lt;li&gt;Creating separate PRs per stack for easy review&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Policy compliance:
&lt;strong&gt;&amp;ldquo;Ask Neo to scan for policy violations and fix them automatically&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Neo will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identify violations across all stacks&lt;/li&gt;
&lt;li&gt;Generate fixes following your policy rules&lt;/li&gt;
&lt;li&gt;Test changes to ensure compliance&lt;/li&gt;
&lt;li&gt;Create PRs with clear explanations of what was fixed and why&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cost optimization:
&lt;strong&gt;&amp;ldquo;Ask Neo to find idle resources and create a plan to shut them down&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Neo analyzes usage patterns, identifies waste, and proposes infrastructure changes to reduce costs—all autonomously.&lt;/p&gt;
&lt;p&gt;The key difference: your AI assistant identifies &lt;em&gt;what&lt;/em&gt; needs to be done, and Neo &lt;em&gt;does&lt;/em&gt; it—writing code, running tests, creating PRs, and managing the entire workflow in Pulumi Cloud.&lt;/p&gt;
&lt;h2 id="real-world-workflow"&gt;Real-World Workflow&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what a typical session looks like:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; &amp;ldquo;What stacks do I have with &amp;lsquo;production&amp;rsquo; in the name?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI Assistant:&lt;/strong&gt; Uses &lt;code&gt;get-stacks&lt;/code&gt; to list: &lt;code&gt;api-production&lt;/code&gt;, &lt;code&gt;web-production&lt;/code&gt;, &lt;code&gt;data-production&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; &amp;ldquo;Are there any policy violations in those stacks?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI Assistant:&lt;/strong&gt; Uses &lt;code&gt;get-policy-violations&lt;/code&gt; and reports: 3 S3 buckets without encryption, 2 security groups too permissive&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; &amp;ldquo;Ask Neo to fix those violations and create a PR&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI Assistant:&lt;/strong&gt; Uses &lt;code&gt;neo-bridge&lt;/code&gt; to launch Neo task, provides link&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Neo:&lt;/strong&gt; Autonomously creates plan, generates fixes, tests changes, creates PR with detailed explanation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; Review PR, approve, merge&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Ready to try it? Check out our &lt;a href="https://www.pulumi.com/docs/iac/using-pulumi/mcp-server/"&gt;documentation&lt;/a&gt; for configuration instructions for your AI assistant of choice.&lt;/p&gt;
&lt;p&gt;Key points:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Configure once&lt;/strong&gt; - Add &lt;code&gt;https://mcp.ai.pulumi.com/mcp&lt;/code&gt; to your AI assistant&amp;rsquo;s MCP settings&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authenticate&lt;/strong&gt; - Browser popup for token entry and org selection (one time)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start asking&lt;/strong&gt; - Query your infrastructure, generate code, delegate to Neo&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Remote MCP Server is available now for all Pulumi users. No installation required—just configure and connect.&lt;/p&gt;
&lt;h2 id="learn-more"&gt;Learn More&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/iac/using-pulumi/mcp-server/"&gt;Pulumi MCP Server Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/pulumi-cloud/neo/"&gt;Pulumi Neo Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io"&gt;Model Context Protocol&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;re excited to see what you build with AI-assisted infrastructure management. Let us know what you think in our &lt;a href="https://slack.pulumi.com"&gt;Community Slack&lt;/a&gt;!&lt;/p&gt;</description><author>Artur Laksberg</author><category>mcp</category><category>ai</category></item></channel></rss>