<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/" >

<channel>
	<title>MCP Development &#8211; Macronimous Blog</title>
	<atom:link href="https://www.macronimous.com/blog/category/ai/mcp-development/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.macronimous.com/blog</link>
	<description>Web design, web programming, Mobile apps, Opensource , SEO etc</description>
	<lastBuildDate>Fri, 14 Aug 2026 07:08:55 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>
	<item>
		<title>Agentic Coding Mistakes: Lessons From a Real App build</title>
		<link>https://www.macronimous.com/blog/agentic-coding-domain-knowledge/</link>
					<comments>https://www.macronimous.com/blog/agentic-coding-domain-knowledge/#respond</comments>
		
		<dc:creator><![CDATA[Claude Craft]]></dc:creator>
		<pubDate>Thu, 20 Aug 2026 06:57:27 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[MCP Development]]></category>
		<category><![CDATA[Vibe Coding]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[agentic coding]]></category>
		<category><![CDATA[AI code review]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[invoice software]]></category>
		<category><![CDATA[MCP]]></category>
		<guid isPermaLink="false">https://www.macronimous.com/blog/?p=5297</guid>

					<description><![CDATA[<p>Agentic coding tools build the most conventional version of whatever you ask for. We asked for an invoicing system and got textbook accounting software, including a void feature that would have carried a GST liability on unpaid export invoices. Two diff-backed incidents from that build show why the fix is domain knowledge in the spec, [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://www.macronimous.com/blog/agentic-coding-domain-knowledge/">Agentic Coding Mistakes: Lessons From a Real App build</a> first appeared on <a rel="nofollow" href="https://www.macronimous.com/blog">Macronimous Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<a href="https://www.macronimous.com/blog/wp-content/uploads/2026/08/Agentic-Coding-Mistakes.jpg"><img fetchpriority="high" decoding="async" width="1729" height="910" src="https://www.macronimous.com/blog/wp-content/uploads/2026/08/Agentic-Coding-Mistakes.jpg" alt="Agentic Coding Mistakes" class="aligncenter size-full wp-image-5303" /></a>
<div class="mac-direct-answer">
<p><strong>Agentic coding tools</strong> build the most conventional version of whatever you ask for. We asked for an invoicing system and got textbook accounting software, including a void feature that would have carried a GST liability on unpaid export invoices. Two diff-backed incidents from that build show why the fix is domain knowledge in the spec, not more hours spent reviewing generated code.</p>
</div>
<div class="mac-toc">
<p class="mac-toc-title">In this post</p>
<ul>
<li><a href="#training-data-app">The app an AI builds is the app in its training data</a></li>
<li><a href="#void-incident">Incident one: the agent built a void feature and made it law</a></li>
<li><a href="#numbering-incident">Incident two: numbering that was right everywhere except India</a></li>
<li><a href="#mistake-that-never-happened">The mistake that never happened</a></li>
<li><a href="#mcp-connector">The MCP connector: agentic defaults trust too much</a></li>
<li><a href="#specs-beat-review">Review catches what you know. Specs prevent what you don&#8217;t.</a></li>
<li><a href="#open-source">The bottom line, and the code</a></li>
</ul>
</div>
<h2 id="training-data-app">The app an AI builds is the app in its training data</h2>
<p>The standard advice for AI-assisted development is simple: let the agent write the code, then review it. We follow a stricter version of that ourselves and wrote it up as <a href="https://www.macronimous.com/blog/controlled-ai-coding/">controlled AI coding</a>. But this build taught us where that advice runs out. Review catches the mistakes you already know to look for. It does nothing about the mistakes that look like correct code.</p>
<p>Some context, kept short. We run a software-export business in India, and export invoicing follows rules that generic tools don&#8217;t model. Exports of services are <a href="https://taxinformation.cbic.gov.in/content/html/tax_repository/gst/acts/2017_IGST_Act/active/chaptervii/section16_v1.00.html" target="_blank" rel="noopener noreferrer">zero-rated under Section 16 of the IGST Act</a>, payment must arrive in convertible foreign exchange, the bank issues a FIRC (Foreign Inward Remittance Certificate) as proof, each remittance carries an RBI purpose code, and invoice numbers run gapless per financial year, which in India starts on April 1. This is our working understanding, not tax advice; confirm specifics with your chartered accountant.</p>
<p>Because no off-the-shelf tool ties a FIRC to the invoice it settles, we built our own system, with Claude Code doing most of the typing. The agent was fast, confident, and produced clean PHP. It also produced two failures that never threw an error, never failed a test we had at the time, and would have passed any code review that judged the code as code.</p>
<h2 id="void-incident">Incident one: the agent built a void feature and made it law</h2>
<p>Every accounting tutorial, every open-source invoice app, every SaaS product the model has ever seen handles a wrong invoice the same way: void it, keep the number, never delete. So the agent built exactly that. Not a suggestion, a complete feature: a Void button, a confirm dialog, a reinstate action, and a comment declaring the behavior as rule &#8220;&sect;1.10&#8221; of its own spec. From <code>invoice-view.php</code> at commit <code>99d6730</code>:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">/* Tax-invoice document (&sect;2). Born paid — status is `issued` or `void`,
 * voided, never deleted (&sect;1.10). */
if ($action === 'void' &amp;&amp; $inv['status'] === 'issued') {
    $DB-&gt;prepare("UPDATE invoices SET status='void' WHERE id=?")-&gt;execute([$id]);
    flash('Invoice voided. The number is retained (never reused).');
}</pre><p>&nbsp;</p>
<p>As generic accounting software, this is correct. As software for an Indian exporter, it is a liability generator. In our reading of GST, an issued tax invoice creates a tax liability whether or not you later mark it void. A wrongly issued invoice has to be removed and its proforma reverted, so the liability never exists on paper. We caught it in the functional-gap review, and the fix went in as commit <code>c9f4107</code>, whose message states the reasoning better than any paragraph I could write here:</p>
<blockquote>
<p>&#8220;invoice-view: replace Void with &#8216;Undo conversion&#8217; — deletes a wrongly converted invoice, reverts its proforma to Paid, frees the FY number (an invoice that exists is a GST liability, so wrong ones are removed).&#8221;</p>
</blockquote>
<div class="mac-key-point">
<p>This was a jurisdiction error made confidently. The agent generated the correct feature for the wrong country, then wrote it into its own spec as a rule.</p>
</div>
<p>That last part deserves a beat. The agent didn&#8217;t hedge. It codified the pattern as canonical, complete with a section number, and every later piece of generated code would have treated void as settled behavior. A wrong assumption an agent writes into its own spec compounds with every subsequent prompt.</p>
<h2 id="numbering-incident">Incident two: numbering that was right everywhere except India</h2>
<p>The first version of <code>next_invoice_number()</code>, from the initial commit <code>f6b7863</code>, made two default choices in one function:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">$st = $db-&gt;prepare('SELECT prefix, next_seq FROM companies WHERE id = ? FOR UPDATE');
// ...
return sprintf('%s-%s-%03d', $row['prefix'], date('Y', strtotime($issueDate)), $seq);</pre><p>&nbsp;</p>
<p>One global counter per company, and <code>date('Y')</code>: the calendar year. Reasonable everywhere the model&#8217;s training data comes from. Wrong in India, where invoice sequences reset on April 1 for the new financial year, and where our proforma and its resulting tax invoice must share a sequence number so the books line up. The catch, again, came from a review question, not from the code: &#8220;every business year, April 1st, we change the invoice numbers.&#8221; That single sentence forced a rebuild into per-company, per-document-type, per-FY gapless sequences.</p>
<p>Then the second layer, and this one is the more interesting failure. The rebuilt version used <code>INSERT IGNORE</code> plus <code>SELECT ... FOR UPDATE</code> plus <code>UPDATE</code>, a sequence that <a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-deadlocks.html" target="_blank" rel="noopener noreferrer">deadlocks under InnoDB</a> when two transactions create invoices at the same moment. No human review caught this. It was caught by a concurrency test that existed for one reason: someone asked &#8220;what if a team member creates an invoice on their machine at the same time?&#8221; The question generated the test; the test caught what eyes could not. The fix was a single atomic statement.</p>
<p>That is the arc worth remembering: naive, then domain-corrected, then concurrency-corrected, each step with a diff. And the mechanism differed. The FY miss was caught by knowledge. The deadlock was caught by a test that a question produced. If your process only has &#8220;review the diff,&#8221; you get the first catch and miss the second.</p>
<h2 id="mistake-that-never-happened">The mistake that never happened</h2>
<p>I expected a third incident. Agentic tools routinely model payment as a boolean, an <code>is_paid</code> flag on the invoice row, because that&#8217;s what tutorial invoice apps do. It never happened here. The very first schema, commit <code>f39c0b2</code>, already had a <code>payments</code> table with amount, currency, and FIRC linkage as a first-class record.</p>
<p>Why? Because the FIRC requirement was in the spec before the agent generated a line of code. When the spec says every foreign payment must carry its remittance certificate and purpose code, a boolean cannot satisfy it, so the wrong shape never gets generated. Nobody had to catch this mistake, because the constraint made it impossible to make.</p>
<table class="styled-table">
<thead>
<tr>
<th>What the agent defaults to</th>
<th>What an Indian exporter needs</th>
<th>How it was handled</th>
</tr>
</thead>
<tbody>
<tr>
<td>Void a wrong invoice, keep the number</td>
<td>Remove the invoice, revert the proforma, free the FY number</td>
<td>Built wrong, caught in review</td>
</tr>
<tr>
<td>One counter, calendar year</td>
<td>Gapless per-FY sequences resetting April 1, proforma and invoice matched</td>
<td>Built wrong, caught in review, then again by a test</td>
</tr>
<tr>
<td>An <code>is_paid</code> boolean</td>
<td>Payments as first-class records with FIRC and purpose code</td>
<td>Never built wrong: the constraint was in the spec</td>
</tr>
</tbody>
</table>
<p>Three rows, one pattern. The only mistake that cost nothing was the one the spec prevented.</p>
<h2 id="mcp-connector">The MCP connector: agentic defaults trust too much</h2>
<p>The system has an <a href="https://www.macronimous.com/blog/building-a-wordpress-mcp-server-when-the-spec-changed/">MCP</a> connector, so an accountant can open Claude Desktop and type &#8220;create and send an invoice to Acme Ltd for the March retainer&#8221; without ever logging into the app. The agentic default for this kind of connector is full write access, because that&#8217;s the frictionless demo. We went the other way. The connector is a thin layer over the app&#8217;s own token-authenticated API: writes are draft-first and require confirmation, every AI-initiated change is audit-logged, and the token is role-scoped so the <a href="https://www.macronimous.com/blog/the-code-your-ai-wants-to-delete-is-load-bearing/">AI</a> cannot see or touch more than that accountant could. It&#8217;s stateless, single-request JSON, which lines up with where the <a href="https://blog.modelcontextprotocol.io/posts/2026-07-28/" target="_blank" rel="noopener noreferrer">2026-07-28 MCP specification</a> took the protocol. The AI holds no business logic. The invoice system stays the single source of truth, which matters for the same reason everything above matters: an agent that improvises accounting logic is an agent that improvises liabilities.</p>
<h2 id="specs-beat-review">Review catches what you know. Specs prevent what you don&#8217;t.</h2>
<p>Agentic tools never say &#8220;that&#8217;s unwise.&#8221; A human developer with export clients would have asked about the financial year before writing a numbering function. The agent shipped <code>date('Y')</code> without a flicker of doubt, and it would ship the void feature again tomorrow. We&#8217;ve written before about <a href="https://www.macronimous.com/blog/writing-clean-code-with-ai/">keeping AI-generated code clean</a> and about why <a href="https://www.macronimous.com/blog/vibe-coding-for-web-developers-amplify-your-flow-state-with-ai/">vibe coding needs guardrails before production</a>, but this build sharpened the point: cleanliness was never the problem. Every one of these mistakes was clean.</p>
<p>So put the domain in writing before the agent starts. For anything with statutory weight, our pre-build spec now answers:</p>
<ul class="mac-checklist">
<li>Which jurisdiction&#8217;s rules govern this system, and which documents are statutory records?</li>
<li>What is the full lifecycle of each document, including what may be voided, deleted, reverted, or never touched?</li>
<li>What are the numbering rules: sequence scope, gaplessness, and which fiscal calendar they follow?</li>
<li>What proof documents (FIRC, purpose codes, certificates) must exist as first-class records, not fields?</li>
<li>What happens when two people do the same thing at the same time, and which test proves it?</li>
<li>If an AI connector exists, what is its write model: scope, confirmation, and audit trail?</li>
</ul>
<p>Every &#8220;what if&#8221; question in that list is a test waiting to be written. Ask it early and the agent generates the right shape from the first commit, the way the payments table proved.</p>
<h2 id="open-source">The bottom line, and the code</h2>
<p>The agent was worth it. It typed the app in a fraction of the time it would have taken us by hand, and both incidents were cheaper to fix than a single GST notice would have been to receive. But the value of the build lived in the two moments a human said &#8220;not in India&#8221; and the one moment a spec made the question unnecessary. Speed came from the agent. Correctness came from the domain.</p>
<p>We&#8217;re releasing the system under MIT for other Indian software exporters and freelancers with the same compliance shape: a single-business kit that self-installs on ordinary PHP and MySQL shared hosting, with your data in your own database. It is deliberately narrow. Built around Indian GST and export-of-services rules, it is not a fit for domestic billing at other tax rates or for non-Indian firms without changes. <!-- INSERT PUBLIC REPO URL HERE, or remove this sentence if the repo isn't live at publish time: --> The repository is on GitHub; if you find a rule we got wrong, an issue telling us so is the most useful contribution you can make.</p>
<div class="mac-cta-box">
<h3>Shipping AI-built code to production?</h3>
<p>We pair agentic speed with 24 years of review discipline, on React, PHP, and <a href="https://www.macronimous.com/blog/wordpress-7-0-ai-the-token-cost-reality-for-site-owners/">WordPress</a> builds where a wrong default has real-world costs.</p>
<p><a href="https://www.macronimous.com/services/custom-web-development/" class="mac-cta-button">Get your AI-built app reviewed</a>
</div>
<p>The post <a rel="nofollow" href="https://www.macronimous.com/blog/agentic-coding-domain-knowledge/">Agentic Coding Mistakes: Lessons From a Real App build</a> first appeared on <a rel="nofollow" href="https://www.macronimous.com/blog">Macronimous Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.macronimous.com/blog/agentic-coding-domain-knowledge/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Building a WordPress MCP Server When the Spec Changed</title>
		<link>https://www.macronimous.com/blog/building-a-wordpress-mcp-server-when-the-spec-changed/</link>
					<comments>https://www.macronimous.com/blog/building-a-wordpress-mcp-server-when-the-spec-changed/#respond</comments>
		
		<dc:creator><![CDATA[Benny]]></dc:creator>
		<pubDate>Wed, 29 Jul 2026 05:29:22 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[MCP Development]]></category>
		<category><![CDATA[WordPress Development]]></category>
		<category><![CDATA[MCP Server]]></category>
		<category><![CDATA[Wordpress development]]></category>
		<guid isPermaLink="false">https://www.macronimous.com/blog/?p=5282</guid>

					<description><![CDATA[<p>MCP 2026-07-28 is the fifth revision of the Model Context Protocol, and it retires sessions and the initialize handshake in favor of plain request and response. That change suits WordPress unusually well. PHP already treats every request as an independent, shared-nothing process, so a WordPress MCP server no longer has to fake a persistent connection [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://www.macronimous.com/blog/building-a-wordpress-mcp-server-when-the-spec-changed/">Building a WordPress MCP Server When the Spec Changed</a> first appeared on <a rel="nofollow" href="https://www.macronimous.com/blog">Macronimous Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<a href="https://www.macronimous.com/blog/wp-content/uploads/2026/07/Building-a-WordPress-MCP-Server-When-the-Spec-Changed.jpg"><img decoding="async" width="1731" height="909" src="https://www.macronimous.com/blog/wp-content/uploads/2026/07/Building-a-WordPress-MCP-Server-When-the-Spec-Changed.jpg" alt="uilding a WordPress MCP Server When the Spec Changed" class="aligncenter size-full wp-image-5284" /></a>
<div class="mac-direct-answer">
<p><strong>MCP 2026-07-28</strong> is the fifth revision of the Model Context Protocol, and it retires sessions and the <code>initialize</code> handshake in favor of plain request and response. That change suits <a href="https://www.macronimous.com/blog/wordpress-7-0-ai-the-token-cost-reality-for-site-owners/">WordPress</a> unusually well. PHP already treats every request as an independent, shared-nothing process, so a WordPress MCP server no longer has to fake a persistent connection it was never going to hold open anyway.</p>
</div>
<div class="mac-toc">
<p class="mac-toc-title">What&#8217;s on this page</p>
<ul>
<li><a href="#spec-changed">The spec changed the night before we submitted</a></li>
<li><a href="#right-shape">Why WordPress was already the right shape</a></li>
<li><a href="#what-broke">What broke was the handshake, and only the handshake</a></li>
<li><a href="#both-generations">We&#8217;re supporting both generations, not switching</a></li>
<li><a href="#timing">The timing call was harder than the code</a></li>
<li><a href="#cacheable">The small win: tool lists are cacheable now</a></li>
<li><a href="#read-the-rest">The rest of the changelog, for WordPress builders</a></li>
<li><a href="#simple-survives">Simple survives revisions. Clever gets rewritten.</a></li>
</ul>
</div>
<h2 id="spec-changed">The spec changed the night before we submitted</h2>
<p>On July 28, 2026 the Model Context Protocol shipped its fifth revision. We were hours away from uploading our WordPress MCP plugin to the plugin directory.</p>
<p>The headline change was a stateless core. MCP went from a bidirectional, stateful protocol to plain request and response. Sessions gone. The <code>initialize</code> and <code>initialized</code> handshake, retired outright. You can read the <a href="https://blog.modelcontextprotocol.io/posts/2026-07-28/" target="_blank" rel="noopener noreferrer">maintainers&#8217; release notes</a> for the full list, and Anthropic published <a href="https://claude.com/blog/bringing-mcp-2026-07-28-to-claude" target="_blank" rel="noopener noreferrer">its own summary of what&#8217;s rolling out across Claude</a> the same day.</p>
<p>So the first question was not &#8220;should we adopt this.&#8221; It was &#8220;are we broken.&#8221;</p>
<p>Mostly, no. And for a deeply unglamorous reason.</p>
<h2 id="right-shape">Why WordPress was already the right shape</h2>
<p>In the first week of the build we had a choice about how the server replies. The old spec allowed a persistent event stream, or a single JSON response per request. We took the single response. Not because we saw anything coming, but because server-sent events inside PHP, under a shared host&#8217;s process model, is a bad afternoon that turns into a bad week.</p>
<p>The simpler option was permitted. We took it and moved on.</p>
<p>Eight months later the protocol moved to exactly that shape. Every server that had built the sophisticated version now has a migration to do. We had already accidentally arrived.</p>
<p>There is a general point buried in that, and it&#8217;s the reason this release matters beyond one plugin. WordPress has always been shared-nothing. A PHP request boots, does its work, and dies with no memory of the request before it. The previous MCP spec fought that model by asking for a connection WordPress does not naturally hold. The new spec asks for exactly what WordPress already does.</p>
<h2 id="what-broke">What broke was the handshake, and only the handshake</h2>
<p><code>initialize</code> and <code>initialized</code> were the first code written on this project, the thing that proved a WordPress site could act as an MCP server at all. Both are now retired in favor of an optional <code>server/discover</code> call. The most protocol-shaped part of the build was the part that aged worst.</p>
<p>Everything downstream, reading site data and returning JSON, did not care at all.</p>
<div class="mac-key-point">
<p>The parts of our build that broke were the parts doing protocol ceremony. The parts doing the actual job carried on untouched, because they had never known what a session was.</p>
</div>
<p>Here is what a call looks like now. Note that there is nothing to establish first, and nothing stored between requests.</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"},
 "_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}</pre><p>&nbsp;</p>
<p>Protocol version, client identity and client capabilities all travel in the request itself. Any request can land on any instance behind an ordinary load balancer. For a plugin running on shared hosting, that is not an abstract scalability win. It removes the one requirement PHP was worst equipped to meet.</p>
<h2 id="both-generations">We&#8217;re supporting both generations, not switching</h2>
<p>The new spec carries a formal deprecation policy with a twelve-month minimum window. That changes the calculation. A plugin installed on somebody else&#8217;s site has to work with whatever client that person happens to have. You do not get to pick.</p>
<p>So version negotiation decides per request, and nothing is stored between them. Two code paths, one endpoint, no session table.</p>
<p>Agencies will recognize the shape of this problem. If you <a href="https://www.macronimous.com/services/cms-development/wordpress-maintenance-services/">maintain WordPress sites for other people</a>, you already live with the gap between the version you&#8217;d like everyone to run and the version they actually run. Protocol support is the same discipline applied one layer down, and it accumulates the same way <a href="https://www.macronimous.com/blog/hidden-technical-debt-wordpress-seo/">technical debt accumulates in WordPress</a>: quietly, until a release forces the accounting.</p>
<h2 id="timing">The timing call was harder than the code</h2>
<p>Directory review takes weeks. Submitting on the previous spec would have meant being reviewed, approved and listed as already out of date, in a category where the audience reads specifications for a living.</p>
<p>A day of work against several weeks of looking dated is not a close call.</p>
<p>The engineering question was answered in an hour. The publishing question was the harder one, because the cost of shipping something correct-but-stale is paid entirely in credibility, and you cannot patch that in version 1.0.1.</p>
<h2 id="cacheable">The small win: tool lists are cacheable now</h2>
<p>One change we are quietly pleased about. List responses now carry a time-to-live and a cache scope.</p>
<p>Through the entire build, every time we added a capability, we had to disconnect and reconnect the client, because it had cached a stale catalog with no way to learn it was stale. Minor, constant, mildly maddening. Now the protocol handles it.</p>
<p>Nobody will write a headline about that one. It is the change that will show up most often in daily use.</p>
<h2 id="read-the-rest">The rest of the changelog, for WordPress builders</h2>
<p>If you are building or evaluating anything MCP-shaped on WordPress, these are the items worth reading properly in the <a href="https://modelcontextprotocol.io/specification/2026-07-28/" target="_blank" rel="noopener noreferrer">2026-07-28 specification</a> rather than skimming.</p>
<table class="styled-table">
<thead>
<tr>
<th>What changed</th>
<th>What it means for a WordPress MCP server</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sessions and the <code>initialize</code>/<code>initialized</code> handshake retired</td>
<td>Delete the handshake code. Each request carries its own protocol version and client info in <code>_meta</code>. PHP&#8217;s request model already worked this way.</td>
</tr>
<tr>
<td><code>Mcp-Method</code> and <code>Mcp-Name</code> required as HTTP headers</td>
<td>Your endpoint reads them, and anything in front of you must not strip them. Security plugins and aggressive WAF rules are the thing to test here.</td>
</tr>
<tr>
<td>List responses carry <code>ttlMs</code> and <code>cacheScope</code></td>
<td>Clients stop re-fetching your tool catalog on every connect. Adding a tool no longer means telling users to reconnect.</td>
</tr>
<tr>
<td>Sampling, elicitation and roots replaced by Multi Round-Trip Requests</td>
<td>A tool needing user confirmation returns <code>input_required</code>, and the client retries with the answers attached. No held-open stream, which PHP was never going to hold.</td>
</tr>
<tr>
<td>Dynamic Client Registration deprecated in favor of Client ID Metadata Documents</td>
<td>The long-term path away from <a href="https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/" target="_blank" rel="noopener noreferrer">Application Passwords</a> toward standard OAuth. Real, but not a migration to rush.</td>
</tr>
<tr>
<td>Legacy HTTP+SSE transport deprecated, twelve-month minimum window</td>
<td>If you built on the event stream, you have a year. If you built on single JSON responses, you have nothing to do.</td>
</tr>
</tbody>
</table>
<h2 id="simple-survives">Simple survives revisions. Clever gets rewritten.</h2>
<p>Budget for the specification moving. Not as a line in a risk register, as a genuine expectation.</p>
<p>You cannot predict which way it moves, so insulation is not foresight. What actually protected us was choosing the simplest option the spec permitted, at a moment when the constraint was our own platform rather than any theory about the future. And keeping the protocol layer thin and separate from the work, which is the same discipline we apply to <a href="https://www.macronimous.com/blog/controlled-ai-coding/">controlled AI coding</a> and to <a href="https://www.macronimous.com/blog/writing-clean-code-with-ai/">keeping AI-generated code clean</a>.</p>
<ul class="mac-checklist">
<li>Take the simplest option the spec permits, especially when your own platform is the constraint</li>
<li>Keep protocol handling in its own layer, separate from the code doing the actual work</li>
<li>Support two spec generations rather than switching, if your code runs on installs you don&#8217;t control</li>
<li>Negotiate the version per request and store nothing between requests</li>
<li>Test that nothing in front of your endpoint strips <code>Mcp-Method</code> or <code>Mcp-Name</code></li>
<li>Read the deprecation policy before planning a migration, not after</li>
</ul>
<p>We got this one right by accident. That is not a method, and I would not want anyone reading this to treat it as one. A hands-on guide to connecting a live WordPress site to Claude is in progress and will follow this post.</p>
<div class="mac-cta-box">
<h3>Thinking about connecting your WordPress site to an AI assistant?</h3>
<p>We build and maintain WordPress for agencies and site owners in the US, UK and Australia, including the plumbing that makes a site readable by <a href="https://www.macronimous.com/blog/the-code-your-ai-wants-to-delete-is-load-bearing/">AI</a> tools rather than just by browsers.</p>
<p><a href="https://www.macronimous.com/services/cms-development/wordpress-development-india/" class="mac-cta-button">Talk to our WordPress team</a>
</div>
<p>The post <a rel="nofollow" href="https://www.macronimous.com/blog/building-a-wordpress-mcp-server-when-the-spec-changed/">Building a WordPress MCP Server When the Spec Changed</a> first appeared on <a rel="nofollow" href="https://www.macronimous.com/blog">Macronimous Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.macronimous.com/blog/building-a-wordpress-mcp-server-when-the-spec-changed/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
