<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Manoj Kumar</title>
    <description>The latest articles on DEV Community by Manoj Kumar (@manoj07ar).</description>
    <link>https://dev.to/manoj07ar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4145696%2F8780cd5a-99b0-43bd-8884-d1be32b0e78f.jpg</url>
      <title>DEV Community: Manoj Kumar</title>
      <link>https://dev.to/manoj07ar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manoj07ar"/>
    <language>en</language>
    <item>
      <title>Handshake: which MCP clients will break your server after the 2026-07-28 spec split?</title>
      <dc:creator>Manoj Kumar</dc:creator>
      <pubDate>Mon, 28 Sep 2026 08:32:17 +0000</pubDate>
      <link>https://dev.to/manoj07ar/handshake-which-mcp-clients-will-break-your-server-after-the-2026-07-28-spec-split-12ga</link>
      <guid>https://dev.to/manoj07ar/handshake-which-mcp-clients-will-break-your-server-after-the-2026-07-28-spec-split-12ga</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path One: Ship an Agent That Queries Real Content&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will my MCP server still work with client X after the 2026-07-28 spec split?&lt;/strong&gt; Paste a manifest into Handshake and you get a grid of verdicts, one per feature and client, each with its sources. The spec and client-support content lives in Sanity, and the agent reads it through Sanity Context MCP (GROQ over a dataset, plus a Knowledge Base). A plain function picks each colour, not the model. On 18 held-out cells I labeled from stored quotes, Handshake gets 16 right. Keyword search gets 6.&lt;/p&gt;

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

&lt;p&gt;On 28 July 2026 the Model Context Protocol removed its own handshake: revision &lt;code&gt;2026-07-28&lt;/code&gt; drops &lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;Mcp-Session-Id&lt;/code&gt;, &lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;logging/setLevel&lt;/code&gt;, and &lt;code&gt;resources/subscribe&lt;/code&gt;, and adds &lt;code&gt;server/discover&lt;/code&gt;, per-request &lt;code&gt;_meta&lt;/code&gt;, a required &lt;code&gt;resultType&lt;/code&gt;, and cache hints on list results. Roots, Sampling, and Logging are deprecated, and the earliest removal date on the registry is 28 July 2027. Client docs didn't all keep up: Cursor's MCP page still lists Roots as Supported, and still lists SSE, which the spec deprecated in March 2025.&lt;/p&gt;

&lt;p&gt;Handshake is for people who ship MCP servers. Paste a &lt;code&gt;server/discover&lt;/code&gt; result, an &lt;code&gt;initialize&lt;/code&gt; result, or a short manifest. Rows are the features the server depends on, columns are clients, and each cell gets a colour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Green:&lt;/strong&gt; the client works with that feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amber:&lt;/strong&gt; it works only on the legacy era, or the feature is deprecated and the client still documents it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red:&lt;/strong&gt; it breaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grey:&lt;/strong&gt; I have no source, so the cell stays unknown.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;compute_verdict&lt;/code&gt; picks the colour by joining three stored facts: where the feature sits in the spec, which protocol versions the server advertises, and a support claim with a URL and the date I read it. Gemini only writes the paragraph under the grid. If it tries to relabel a cell, the page still shows the function's answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Four things to try
&lt;/h3&gt;

&lt;p&gt;The demo has eight scenarios. These four make the case.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Legacy files server.&lt;/strong&gt; Inspector is amber on &lt;code&gt;initialize&lt;/code&gt; and &lt;code&gt;logging/setLevel&lt;/code&gt;: it still speaks the legacy era, which has those methods. Claude is amber because Anthropic's 28 July post says modern support is rolling out, not finished. Cursor is grey on &lt;code&gt;initialize&lt;/code&gt;: no page I found says which era it speaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version string only.&lt;/strong&gt; The manifest sets &lt;code&gt;supportedVersions&lt;/code&gt; to &lt;code&gt;2026-07-28&lt;/code&gt; but still requires &lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;Mcp-Session-Id&lt;/code&gt;, and &lt;code&gt;logging/setLevel&lt;/code&gt;. Inspector goes red: it can speak the modern era, the server offers no legacy version, and that revision removed all three. Claude stays amber.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Apps board.&lt;/strong&gt; Clients with a check on the official extension matrix are green. Inspector has no Apps check, so it's grey: an empty cell isn't the same as unsupported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does Cursor support Roots?&lt;/strong&gt; Amber, with two links. &lt;a href="https://cursor.com/docs/mcp" rel="noopener noreferrer"&gt;Cursor's docs&lt;/a&gt; say Supported; the &lt;a href="https://modelcontextprotocol.io/specification/draft/deprecated" rel="noopener noreferrer"&gt;deprecated registry&lt;/a&gt; says Roots were deprecated in &lt;code&gt;2026-07-28&lt;/code&gt;, with a removal window starting July 2027. Keyword search can tell you Sampling is deprecated, but it can't join those two facts into amber. That join is why the content is structured.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;There's no hosted copy of the Next app, so the quickest way in is the public dataset (project &lt;code&gt;hmeltser&lt;/code&gt;, no login): &lt;a href="https://hmeltser.api.sanity.io/v2025-01-01/data/query/production?query=*%5B_type%3D%3D%22feature%22%5D%5B0...5%5D%7Bkey%2Cname%7D" rel="noopener noreferrer"&gt;first five feature documents, as JSON&lt;/a&gt;. The &lt;a href="https://handshake-mcp.sanity.studio/" rel="noopener noreferrer"&gt;Sanity Studio&lt;/a&gt; needs a Sanity login; more links are at the end.&lt;/p&gt;

&lt;p&gt;The grid needs no API key: without &lt;code&gt;SANITY_CONTEXT_TOKEN&lt;/code&gt;, lookups use the same fixture data seeded into the public dataset. That's what the screenshots show, and the colours match the seeded documents.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pasting a server manifest.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F61gaqazsy2pb8rv8v5u9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F61gaqazsy2pb8rv8v5u9.png" alt="Handshake with a pasted server manifest and the compatibility grid below it" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The grid for that manifest. I wrote it to hit all four colours (MCP Apps, &lt;code&gt;initialize&lt;/code&gt;, Roots, HTTP+SSE, JSON-RPC batching), so it isn't a built-in scenario, but the colours are the app's real output.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8vqqa9u4vlcor81xx80u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8vqqa9u4vlcor81xx80u.png" alt="Compatibility grid: five features by twelve clients in green, amber, red, and grey" width="799" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A cell opened to its source links.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgaioyjqkdslim3ul2oel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgaioyjqkdslim3ul2oel.png" alt="A grid cell opened to its verdict and source links" width="740" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The migration list under the grid.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8wc6ua7738qx2wns0c5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8wc6ua7738qx2wns0c5k.png" alt="Migration list with breaking changes and deprecations" width="740" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two GIFs: that manifest turning into the grid, and a cell's sources plus the migration advice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5edckszr74uxiuj1ghcx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5edckszr74uxiuj1ghcx.gif" alt="A manifest pasted into Handshake, the grid appearing, and the Cursor Roots cell opening" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdzm7a7miwon93djjwk9x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdzm7a7miwon93djjwk9x.gif" alt="A grid cell opened to its sources, then the migration advice" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The source isn't public yet, so the parts that matter are quoted inline below. Copyright 2026 Manoj Kumar.&lt;/p&gt;

&lt;p&gt;The core is &lt;code&gt;lib/verdict/compute.ts&lt;/code&gt;. Around it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data/&lt;/code&gt;: revisions, features, clients, and claims. &lt;code&gt;data/heldout.ts&lt;/code&gt; is the quote-labeled set; &lt;code&gt;data/golden.ts&lt;/code&gt; is a rules-consistency file whose score I don't treat as accuracy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/manifest/parse.ts&lt;/code&gt; turns a paste into feature keys plus a flag for "this server advertises only 2026-07-28".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/agent/loop.ts&lt;/code&gt; is an eight-step tool loop; &lt;code&gt;lib/agent/gemini.ts&lt;/code&gt; calls the free Gemini API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;npm test&lt;/code&gt; covers the claims, held-out quotes, consistency file, mock loop, and Gemini retry. &lt;code&gt;npm run eval&lt;/code&gt; prints the held-out comparison first. I wrote the components myself; button styling uses &lt;code&gt;class-variance-authority&lt;/code&gt;, &lt;code&gt;clsx&lt;/code&gt;, and &lt;code&gt;tailwind-merge&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works in code
&lt;/h3&gt;

&lt;p&gt;Three excerpts, copied verbatim from the source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The agent reads Sanity through Context MCP.&lt;/strong&gt; With a token set, the agent's &lt;code&gt;groq_query&lt;/code&gt; calls the &lt;code&gt;groq_query&lt;/code&gt; tool on the &lt;code&gt;handshake-data&lt;/code&gt; endpoint. From &lt;code&gt;lib/agent/execute.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;liveGroq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;listMcpTools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;contextDataUrl&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;contextToken&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groq_query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;handshake-data did not expose groq_query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;handshake-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callMcpTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;contextDataUrl&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;contextToken&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;queryArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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 class="mi"&gt;2500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;errorText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;handshake-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;callMcpTool&lt;/code&gt; in &lt;code&gt;lib/sanity/mcp.ts&lt;/code&gt; refuses any tool name that looks like semantic search or embeddings, so a stray call can't eat the monthly quota.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The colour is a rule, not a vibe.&lt;/strong&gt; The branch behind scenario 2, where the server advertises only &lt;code&gt;2026-07-28&lt;/code&gt; but still leans on a removed feature. From &lt;code&gt;lib/verdict/compute.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rejectsLegacy&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;modernDisposition&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modern&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;supported&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;breaks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;rationale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The server advertises only &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;advertisedVersions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;, and &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was removed in that revision. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;clientName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;catalog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clientSlug&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; documents modern-era support, so there is no legacy version on this server to fall back to.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modern&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;partial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;legacy&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;supported&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;legacy&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;partial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;legacy-only&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;rationale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was removed in 2026-07-28. The server does not advertise a legacy version. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;eraPhrase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;catalog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clientSlug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;legacy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modern&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;rationale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was removed in 2026-07-28 and this server advertises only the modern revision. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;clientName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;catalog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clientSlug&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; has no sourced era claim, so a break is likely but not proven.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemini can't overrule this. The system prompt says so: "You choose what to look up. You do not relabel a cell."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Free-tier Gemini with a fallback.&lt;/strong&gt; A 429 or 503 retries the same model (500ms, then 1500ms backoff), then falls back from &lt;code&gt;gemini-3.8-flash&lt;/code&gt; to &lt;code&gt;gemini-3.5-flash&lt;/code&gt;. I don't call &lt;code&gt;gemini-2.5-flash&lt;/code&gt;: new AI Studio keys get a 404 for that id. From &lt;code&gt;lib/agent/gemini.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LlmRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LlmResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;models&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fallbackModel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;GEMINI_ATTEMPTS_PER_MODEL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isRetryable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
          &lt;span class="nx"&gt;lastError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;retriesLeft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;GEMINI_ATTEMPTS_PER_MODEL&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retriesLeft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;geminiBackoffMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;lastError&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gemini request failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Used Sanity
&lt;/h2&gt;

&lt;p&gt;Sanity holds everything the verdict joins, and the agent reaches it through two Sanity Context MCP endpoints: GROQ over the dataset, and a Knowledge Base. A feature points at the revisions where it arrived, was deprecated, and sometimes was removed. A client has claims, each with a status, an era, and a source. A server has the versions it advertises. A keyword search returns a paragraph: it doesn't bind those rows, or know that a server listing only &lt;code&gt;2026-07-28&lt;/code&gt; has refused the legacy fallback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content model and schema
&lt;/h3&gt;

&lt;p&gt;Six document types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;specRevision&lt;/code&gt;: every published revision I could verify: &lt;code&gt;2024-11-05&lt;/code&gt;, &lt;code&gt;2025-03-26&lt;/code&gt;, &lt;code&gt;2025-06-18&lt;/code&gt;, &lt;code&gt;2025-11-25&lt;/code&gt;, and &lt;code&gt;2026-07-28&lt;/code&gt;. The last is current and modern; the earlier four are final and legacy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;feature&lt;/code&gt;: a protocol feature or official extension, with detector strings for manifests and a migration sentence.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client&lt;/code&gt;: a product. Twelve of them: Claude Desktop, Claude on the web, Claude Code, Cursor, VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, ChatGPT, MCP Inspector, Postman, fast-agent, and Archestra.AI. I left out clients I couldn't tie to a page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;supportClaim&lt;/code&gt;: one sourced statement: supported, partial, or unsupported (no fake unknown rows). Each has a URL, a short quote, a source type, and &lt;code&gt;retrievedAt&lt;/code&gt; of 2026-09-27.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gotcha&lt;/code&gt;: a conflict worth showing next to a cell, like Cursor's Roots row.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sampleServer&lt;/code&gt;: a demo manifest.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deployed schema id: &lt;code&gt;_.schemas.handshake&lt;/code&gt;. Studio runs on the free host at &lt;code&gt;handshake-mcp&lt;/code&gt;, app id &lt;code&gt;cfp4wjaipfhkyfhjfo47800v&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Context MCP endpoints
&lt;/h3&gt;

&lt;p&gt;One Context MCP endpoint can't serve both a dataset and a Knowledge Base. Give it both and the dataset wins; the Knowledge Bases are ignored with no error. So I created two.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;handshake-data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GROQ, dataset &lt;code&gt;production&lt;/code&gt;, filter on the six types&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.sanity.io/v1/context/organizations/oaj2t5f91/mcp/handshake-data&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;handshake-kb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Knowledge Base &lt;code&gt;MCP spec &amp;amp; client docs&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.sanity.io/v1/context/organizations/oaj2t5f91/mcp/handshake-kb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;handshake-data&lt;/code&gt; stayed NOT READY until the schema descriptor existed. After &lt;code&gt;sanity schemas deploy&lt;/code&gt;, its tools are &lt;code&gt;initial_context&lt;/code&gt;, &lt;code&gt;groq_query&lt;/code&gt;, &lt;code&gt;schema_explorer&lt;/code&gt;, and &lt;code&gt;array_field_reader&lt;/code&gt;. &lt;code&gt;handshake-kb&lt;/code&gt; was Ready as soon as the Knowledge Base was built.&lt;/p&gt;

&lt;p&gt;The agent uses these for lookups; &lt;code&gt;parse_manifest&lt;/code&gt; and &lt;code&gt;compute_verdict&lt;/code&gt; stay in the app, with the same rules as the seeded documents. I skip semantic search: the Free plan allows 500 of those queries a month, and a refresh shouldn't spend them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowledge Base
&lt;/h3&gt;

&lt;p&gt;Fifteen markdown files: changelogs, the era definitions, the deprecated registry, the extension matrix, Cursor's docs, Anthropic's rollout post, and the Inspector README. The cap is 150 indexed documents per organization. I didn't point it at a domain or at the dataset.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two source disagreements
&lt;/h3&gt;

&lt;p&gt;Two pairs of sources disagree. &lt;code&gt;compute_verdict&lt;/code&gt; applies both resolutions, and I pasted these instructions into the Knowledge Base so a rebuild keeps the same reading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP+SSE.&lt;/strong&gt; The 2025-03-26 changelog says Streamable HTTP replaced it; the deprecated registry says it's Deprecated since that date, not Removed. A client that still documents SSE (Cursor does) gets amber.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;When the 2025-03-26 changelog says HTTP+SSE was replaced, and the deprecated registry says it is Deprecated, follow the registry for lifecycle and the changelog for the date the deprecation started.

Do not tell a server author that HTTP+SSE is already gone. Do not tell them it is a current transport. A client doc that still lists SSE, such as Cursor's transport table, can be true as a support claim and still produce an amber cell.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Claude enterprise auth.&lt;/strong&gt; Anthropic's 28 July 2026 post says enterprise-managed auth shipped for Claude. The extension matrix has a check for Archestra.AI and none for Claude, so the claim stays partial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;When Anthropic's 28 July 2026 post says enterprise-managed auth shipped for Claude, and the extension support matrix has no Enterprise Auth check for Claude (web) or Claude Desktop, record Claude as partial.

Do not treat the blog post as a green cell. Do not treat the empty matrix cell as unsupported. The claim stays partial until those sources agree. Archestra.AI is the client with an Enterprise Auth check.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caveat: the Knowledge Base Issues view showed 0 conflicts, because each note already tells both sides and the resolution in one file, so the indexer sees one story. To make Issues flag the pairs, I'd add the changelog, the deprecated registry, the Anthropic post, and the extension matrix as separate URL sources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results on a held-out set
&lt;/h3&gt;

&lt;p&gt;The number I quote is from a held-out set of 18 cases, each labeled from a stored quote. I didn't call &lt;code&gt;compute_verdict&lt;/code&gt; while labeling.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cells correct&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Keyword search&lt;/td&gt;
&lt;td&gt;6 of 18&lt;/td&gt;
&lt;td&gt;33%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knowledge Base text only&lt;/td&gt;
&lt;td&gt;2 of 18&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handshake&lt;/td&gt;
&lt;td&gt;16 of 18&lt;/td&gt;
&lt;td&gt;89%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Handshake has a citation on all 18 and misses two. Both are labeled unknown, because the Anthropic post never says Claude implements &lt;code&gt;ping&lt;/code&gt; or &lt;code&gt;logging/setLevel&lt;/code&gt;. The engine treats "rolling out soon" as enough for amber. I left that disagreement in the table.&lt;/p&gt;

&lt;p&gt;A second file of 25 cases matches Handshake on every label, but those labels came from the same rules as the function. That's a consistency check, not an accuracy number, so I don't quote it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Many cells are grey, especially for protocol era, because almost nobody documents it. I won't paint a client red because a blog post is silent.&lt;/li&gt;
&lt;li&gt;The in-memory rate limit doesn't share state across server instances.&lt;/li&gt;
&lt;li&gt;No probing of random public MCP URLs: I don't want the demo opening sockets to servers I haven't reviewed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gemini-3.8-flash&lt;/code&gt; returned 503 ("high demand") once in a live smoke and passed on retry, hence the retry and fallback in excerpt 3. The smoke passed on both Flash models for the held-out Postman MCP Apps cell: local verdict &lt;code&gt;works&lt;/code&gt;, Gemini repeated &lt;code&gt;works&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project ID:&lt;/strong&gt; &lt;code&gt;hmeltser&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset:&lt;/strong&gt; &lt;code&gt;production&lt;/code&gt; (public, Free plan)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public GROQ queries, no login:&lt;/strong&gt; &lt;a href="https://hmeltser.api.sanity.io/v2025-01-01/data/query/production?query=count%28*%5B_type%3D%3D%22feature%22%5D%29" rel="noopener noreferrer"&gt;count of feature documents&lt;/a&gt; and &lt;a href="https://hmeltser.api.sanity.io/v2025-01-01/data/query/production?query=*%5B_type%3D%3D%22feature%22%5D%5B0...5%5D%7Bkey%2Cname%7D" rel="noopener noreferrer"&gt;first five features&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Studio:&lt;/strong&gt; &lt;a href="https://handshake-mcp.sanity.studio/" rel="noopener noreferrer"&gt;https://handshake-mcp.sanity.studio/&lt;/a&gt; (Sanity login)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema:&lt;/strong&gt; &lt;code&gt;_.schemas.handshake&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan:&lt;/strong&gt; Free, no card. Caps: 500 embeddings queries and 1,000 AI credits per month, 150 Knowledge Base documents. This project stays inside them on purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Agent Session
&lt;/h2&gt;

&lt;p&gt;Agent session transcript: &lt;a href="https://dev.to/agent_sessions/handshake-build-session-sanity-challenge-yc5jrj"&gt;Handshake build session&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a sanitized build summary (&lt;code&gt;docs/AGENT_SESSION.md&lt;/code&gt;): the two endpoints, the schema deploy, the held-out numbers, and the Gemini retry. It has no keys or tokens. The uploader's redaction misses some strings, so I checked it before publishing.&lt;/p&gt;

&lt;p&gt;Disclosure, set in the DEV editor: &lt;strong&gt;AI-Assisted&lt;/strong&gt;. I used a coding agent to draft the app and this post, and I'm publishing it under my name.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
