<?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: Ismail Amin</title>
    <description>The latest articles on DEV Community by Ismail Amin (@ismail_amin_8e46892dfdeb7).</description>
    <link>https://dev.to/ismail_amin_8e46892dfdeb7</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%2F4228715%2F75d770bc-d2e2-4c4d-a67f-cf6175a2b2db.png</url>
      <title>DEV Community: Ismail Amin</title>
      <link>https://dev.to/ismail_amin_8e46892dfdeb7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ismail_amin_8e46892dfdeb7"/>
    <language>en</language>
    <item>
      <title>I built an MCP tool and the agent never called it. Here's what worked instead.</title>
      <dc:creator>Ismail Amin</dc:creator>
      <pubDate>Thu, 05 Nov 2026 14:02:01 +0000</pubDate>
      <link>https://dev.to/ismail_amin_8e46892dfdeb7/i-built-an-mcp-tool-and-the-agent-never-called-it-heres-what-worked-instead-13fb</link>
      <guid>https://dev.to/ismail_amin_8e46892dfdeb7/i-built-an-mcp-tool-and-the-agent-never-called-it-heres-what-worked-instead-13fb</guid>
      <description>&lt;p&gt;Every codebase has rules nobody wrote down. One package always checks &lt;code&gt;rows.Err()&lt;/code&gt; after iterating. Another logs through &lt;code&gt;slog&lt;/code&gt; and never &lt;code&gt;log.Printf&lt;/code&gt;. Every method on &lt;code&gt;Store&lt;/code&gt; names its receiver &lt;code&gt;s&lt;/code&gt;. Tests in this directory use testify; tests over there use the standard library.&lt;/p&gt;

&lt;p&gt;AI coding agents break these rules constantly, not because they're careless but because they cannot see them. The rules live in the statistics of the code, not in any file. The standard fix is to write them down in a CLAUDE.md or AGENTS.md. Someone has to write it, keep it current, and hope it covers what matters. A typical hand-written file has 20 or 30 rules for an entire repository. The repository has thousands.&lt;/p&gt;

&lt;p&gt;I spent the summer trying to close that gap automatically. The first two attempts failed, and the way they failed is the useful part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 1: give the agent a tool
&lt;/h2&gt;

&lt;p&gt;The obvious design in 2026 is an MCP server. Index the repo, mine its conventions, and expose one tool, &lt;code&gt;get_style_context&lt;/code&gt;, that takes a file path and returns the rules, tendencies and example functions for that file's directory. The agent calls it before editing, reads the rules, writes conforming code.&lt;/p&gt;

&lt;p&gt;I ran a controlled experiment: 15 runs, same tasks, the tool available every time. The agent called it &lt;strong&gt;zero times.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I assumed the tool description was the problem and rewrote it. More imperative. More specific about when to use it. Still zero. The agents did what they always do: read the file, read a neighbour or two, write code. They had no signal that they were missing anything, so they never went looking.&lt;/p&gt;

&lt;p&gt;That's the general lesson, and it's bigger than my tool. &lt;strong&gt;An agent will not voluntarily look up knowledge it doesn't know it lacks.&lt;/strong&gt; A knowledge-lookup tool that depends on the agent's initiative is a tool that doesn't get used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 2: push the rules at it
&lt;/h2&gt;

&lt;p&gt;If the agent won't pull, push. Claude Code has a PreToolUse hook that runs before an Edit or Write. I used it to inject the relevant conventions right before each edit landed.&lt;/p&gt;

&lt;p&gt;Six blind-scored runs on Kubernetes with Claude Opus. The agent wrote the same code with or without the injected rules.&lt;/p&gt;

&lt;p&gt;In hindsight the reason is obvious. By the time PreToolUse fires, the agent has already authored the edit. The content is in the tool call. Rules that arrive at that moment are advice about a decision that's already been made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 3: stop telling, start checking
&lt;/h2&gt;

&lt;p&gt;The thing that works is boring. Don't try to inform the agent at all. Let it write the code, then check the code, and if it breaks a rule hand the violation back as an error.&lt;/p&gt;

&lt;p&gt;OpenTuple does this with a PostToolUse hook on Edit|Write. After every edit it re-parses the file with tree-sitter, works out which directory each function belongs to, and compares the function with the conventions mined from that directory. If something breaks a rule, the hook exits with status 2, which is the code Claude Code uses to show a hook's stderr to the agent. The agent sees this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal/cli/summary.go:9: PrintIndexSummary calls fmt.Fprintf but never Render
  Rule: In internal/cli, functions that call fmt.Fprintf also call Render. [helper_pairing]
  Evidence: 12 of 12 functions in internal/cli that call fmt.Fprintf also call Render (100%); 4% of the functions that do not call fmt.Fprintf call Render.
  Example: reportWrittenFiles (internal/cli/generate.go:218) follows this rule.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's from a real run with Claude Haiku. It was asked to add a summary printer to a CLI package and its first version printed with a bare &lt;code&gt;fmt.Fprintf&lt;/code&gt;. Every other printer in the package styles its output through the theme. The hook said so. The agent's next edit wrapped the text in &lt;code&gt;StyleDim.Render(...)&lt;/code&gt;, the check passed, the session ended. Four turns, nine seconds. Nobody told it about the theme. The repository did.&lt;/p&gt;

&lt;p&gt;Why does this work when the first two didn't? Because an agent treats a hook error the way it treats a failing test. It's not advice it can weigh and discard. It's a blocker it has to clear. The timing that made attempt 2 fail (the hook fires after the content exists) is exactly the timing a check needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard part was silence
&lt;/h2&gt;

&lt;p&gt;Detection turned out to be the easy half. The hard half was not flagging things that aren't wrong.&lt;/p&gt;

&lt;p&gt;A convention checker lives or dies on false positives. The first time it complains about something that was fine, the developer learns the check is noise, and the second time they turn it off. So OpenTuple's rules are deliberately strict:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A rule exists only where &lt;strong&gt;at least 98%&lt;/strong&gt; of a directory's functions follow it, with &lt;strong&gt;at least 10&lt;/strong&gt; of them.&lt;/li&gt;
&lt;li&gt;A helper pairing (functions that call &lt;code&gt;Lock&lt;/code&gt; also call &lt;code&gt;Unlock&lt;/code&gt;) has to beat the paired helper's base rate by &lt;strong&gt;30 points&lt;/strong&gt;. Otherwise, in a package where everything calls &lt;code&gt;Errorf&lt;/code&gt;, everything that calls &lt;code&gt;Close&lt;/code&gt; "pairs" with &lt;code&gt;Errorf&lt;/code&gt; too.&lt;/li&gt;
&lt;li&gt;An absence rule ("nothing here calls &lt;code&gt;panic&lt;/code&gt;") needs enough functions that you'd &lt;em&gt;expect&lt;/em&gt; some to call it at the repo-wide rate. Ten functions that never needed &lt;code&gt;Close&lt;/code&gt; is a small sample, not a policy.&lt;/li&gt;
&lt;li&gt;Test files are their own scope, so a convention of your tests is never imposed on your library code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The measure of this is how quiet it is on code that already follows its own conventions. Running the check across whole repositories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repository&lt;/th&gt;
&lt;th&gt;Files&lt;/th&gt;
&lt;th&gt;Flags&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fzf&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub CLI&lt;/td&gt;
&lt;td&gt;860&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes v1.29&lt;/td&gt;
&lt;td&gt;7,845&lt;/td&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 JS/TS repos&lt;/td&gt;
&lt;td&gt;3,191&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 49 in Kubernetes are real minority deviations: 31 test functions using a different assertion library from the rest of their package, 11 doc comments, 3 receiver names, 2 pairings, 1 logging call outside klog, 1 error-handling site.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like to use
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/CtrlAltDelete-Ops/opentuple/cmd/opentuple@latest
&lt;span class="nb"&gt;cd &lt;/span&gt;your-repo
opentuple init &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;init&lt;/code&gt; indexes the repo (Kubernetes, 9,711 files, takes 27 seconds on an M3), writes a short CLAUDE.md per directory and one AGENTS.md for tools that read a single file, and installs the hook in &lt;code&gt;.claude/settings.local.json&lt;/code&gt;. Then you start a Claude Code session and forget about it. Not on Claude Code? &lt;code&gt;--git-hook&lt;/code&gt; runs the same check on staged files as a pre-commit hook.&lt;/p&gt;

&lt;p&gt;One Go binary. Runs locally. No network requests. Apache-2.0. Go, Python, JavaScript, TypeScript, Rust and C.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I don't know yet
&lt;/h2&gt;

&lt;p&gt;I'll be upfront about the gaps. &lt;code&gt;.tsx&lt;/code&gt; files go through the TypeScript grammar, which has no JSX, so React components are only partly seen. There are no prebuilt binaries yet, so you need Go installed. And my behavioural numbers come from small runs. I know the mechanism works; I don't yet have a large, independently graded measurement of how often it fires on real agent-written code, and I'd like help designing one.&lt;/p&gt;

&lt;p&gt;The repo is at &lt;a href="https://github.com/CtrlAltDelete-Ops/opentuple" rel="noopener noreferrer"&gt;https://github.com/CtrlAltDelete-Ops/opentuple&lt;/a&gt;. If you run it on your codebase, the thing I most want to hear is what it flagged and whether it was right.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>go</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
