<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Dukehoff Blog</title>
  <subtitle>Large Java systems, migrations, developer tooling and coding agents.</subtitle>
  <link href="https://dukehoff.com/blog/feed.xml" rel="self" />
  <link href="https://dukehoff.com/" />
  <updated>2026-10-23T00:00:00Z</updated>
  <id>https://dukehoff.com/</id>
  <author>
    <name>Anton Epple</name>
  </author>
  <entry>
    <title>dukehoff-decide: typed, calibrated decisions from small language models</title>
    <link href="https://dukehoff.com/en/blog/dukehoff-decide/" />
    <updated>2026-10-23T00:00:00Z</updated>
    <id>https://dukehoff.com/en/blog/dukehoff-decide/</id>
    <category term="announcement" label="announcement" />
    <category term="release" label="release" />
    <content type="html">&lt;p&gt;Many LLM calls in business applications ask a question whose answer is one of a few options. Which
team handles this ticket? Is this message spam? How urgent is it? The usual way is to send the
question to a large model, ask for JSON, parse the text and hope the answer is one of the allowed
values. And when the model says it is sure, there is no way to tell whether that means anything.&lt;/p&gt;
&lt;p&gt;dukehoff-decide turns such a question into a Java type and answers it with a small model that runs
locally. You get a valid value of that type, a probability for every option, and a gate that
decides on its own only where those probabilities are reliable. It is open source under the Apache
License 2.0, and version 0.1.0 is on Maven Central.&lt;/p&gt;
&lt;h2&gt;What it looks like&lt;/h2&gt;
&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Question&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Triage an incoming customer support ticket&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Triage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Question&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;How urgent is this ticket?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Priority&lt;/span&gt; priority&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Question&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Which team should handle it?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Team&lt;/span&gt; team&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Question&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Does the customer threaten to cancel?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; churnRisk&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Decision&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Triage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; triage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ticketText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
d&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                      &lt;span class=&quot;token comment&quot;&gt;// always a valid Triage&lt;/span&gt;
d&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;distribution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;priority&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// Map&amp;lt;Priority, Double&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;triage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requireConfidence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Outcome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Confident&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Triage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dec&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;          &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Outcome&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Uncertain&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Triage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; dec&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; why&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendToHuman&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dec&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; why&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every field becomes a question with a fixed set of options: a boolean is yes or no, an enum has one
option per constant, a scale has one option per step. The model answers each question with a single
token, and dukehoff-decide reads the probabilities of all options from the model&#39;s
log-probabilities. There is no text to parse, so there is no invalid answer.&lt;/p&gt;
&lt;h2&gt;Why calibration matters&lt;/h2&gt;
&lt;p&gt;Raw model probabilities are usually overconfident, and small models often favour particular
options. So you calibrate once, offline, with a few hundred historical cases whose answer you know,
for example tickets and the team that finally handled them. dukehoff-decide fits a correction per
field and writes a report: accuracy before and after, how many cases a gate at each threshold would
decide automatically, how accurate those are, and which options get confused.&lt;/p&gt;
&lt;p&gt;The report is worth reading. A warning such as &amp;quot;option CONTACT receives 16 of 30 errors&amp;quot; points to an
option whose description matches too much. In our tests, rewording one such description raised
accuracy from 76% to 88–90%, more than a model 4.7 times as large did.&lt;/p&gt;
&lt;h2&gt;What we measured&lt;/h2&gt;
&lt;p&gt;Customer messages from a public dataset, 11 categories, Qwen2.5 1.5B (4-bit) with llama.cpp,
300 examples for calibration and 300 different ones for the test:&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;calibration&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;before&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;after&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;category correct&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;87.7%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;93.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decided automatically (threshold 0.9)&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;50%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;71%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;of those correct&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;94.0%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;99.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Always guessing the most frequent category would be right 21.3% of the time.&lt;/p&gt;
&lt;p&gt;The second test matters as much. On a dataset of support tickets where queue and priority mostly
cannot be inferred from the text, the models still claimed over 99% confidence. Raw scores would
have routed 42% to 65% of the tickets automatically, with under 10% of them fully correct. After
calibration, none were routed automatically. That is the right answer: the gate knows when the model
doesn&#39;t.&lt;/p&gt;
&lt;p&gt;Details, both datasets and how to rerun them are in the
&lt;a href=&quot;https://github.com/dukehoff-gmbh/dukehoff-decide#results&quot;&gt;README&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Get it&lt;/h2&gt;
&lt;p&gt;You need Java 21 and a server with an OpenAI-compatible API that returns log-probabilities. We
verified it with Ollama and llama.cpp.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dependency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;groupId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;com.dukehoff.decide&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;groupId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;artifactId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;dukehoff-decide-openai&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;artifactId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0.1.0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dependency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Spring Boot, use &lt;code&gt;dukehoff-decide-spring-boot-starter&lt;/code&gt; instead. It creates a &lt;code&gt;DecisionTask&lt;/code&gt; bean
per type and comes with a health check and Micrometer metrics. Getting started, the Spring Boot setup
and the limitations are in the &lt;a href=&quot;https://github.com/dukehoff-gmbh/dukehoff-decide&quot;&gt;README on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to replace calls to a large model in your application with a small one you can run
yourself, or need help with the labelled examples for calibration, &lt;a href=&quot;https://dukehoff.com/en/contact/&quot;&gt;get in touch&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Dukehoff MCP for Java: an IDE for coding agents</title>
    <link href="https://dukehoff.com/en/blog/dukehoff-mcp-for-java/" />
    <updated>2026-10-18T00:00:00Z</updated>
    <id>https://dukehoff.com/en/blog/dukehoff-mcp-for-java/</id>
    <category term="announcement" label="announcement" />
    <category term="release" label="release" />
    <content type="html">&lt;p&gt;Imagine being a Java developer in 1996, before modern IDEs, with nothing but command-line
tools and maybe Notepad. To rename a method, you search for its name, then open each of
maybe 22 files to check whether a hit refers to that method or to an unrelated one
with the same name, and replace each occurrence with sed or manually. You get there eventually,
and once it compiles, you&#39;re probably done. It just takes a lot of steps and time.&lt;/p&gt;
&lt;p&gt;You may be surprised, but that&#39;s pretty much how coding agents work in 2026 when you ask them to rename a method.&lt;/p&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; preload=&quot;none&quot; width=&quot;1280&quot; height=&quot;804&quot; poster=&quot;https://dukehoff.com/images/dukehoff-mcp/rename-without.jpg&quot;&gt;
&lt;source src=&quot;https://dukehoff.com/images/dukehoff-mcp/rename-without.mp4&quot; type=&quot;video/mp4&quot;&gt;
&lt;/video&gt;&lt;/p&gt;
&lt;p class=&quot;source&quot;&gt;Without the server: Claude Code with Sonnet 5.5 renames a method in Apache Commons Lang. 32 s model time, 994 tokens.&lt;/p&gt;
&lt;p&gt;Dukehoff MCP for Java gives the agent the analysis and refactoring operations of
an IDE as tools: find usages, rename, move, change signature, safe delete, inline.
The agent passes the method&#39;s signature to the rename tool, and the server finds the method,
renames it everywhere, overrides included, writes the files, and returns the diff plus
a compile check that names any new errors.&lt;/p&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; preload=&quot;none&quot; width=&quot;1280&quot; height=&quot;804&quot; poster=&quot;https://dukehoff.com/images/dukehoff-mcp/rename-with.jpg&quot;&gt;
&lt;source src=&quot;https://dukehoff.com/images/dukehoff-mcp/rename-with.mp4&quot; type=&quot;video/mp4&quot;&gt;
&lt;/video&gt;&lt;/p&gt;
&lt;p class=&quot;source&quot;&gt;With the server: the same task. 10 s model time, 230 tokens.&lt;/p&gt;
&lt;p&gt;Some Java IDEs have integrated tools like this, but the way we develop software has
changed. Many developers now start an agent in a terminal, let it write the code,
and review the result in the pull request. Dukehoff MCP for Java runs headless,
so your agent doesn&#39;t need a running IDE to use these tools. And when you do work
in an IDE, the same tools are there too: in VS Code next to Copilot, or in a running NetBeans.&lt;/p&gt;
&lt;p&gt;Be nice to your agent and give it the right tools for the tedious, repetitive and
error-prone tasks. The server is open source, and for exactly these tasks it saves
you and your agent turns, tokens and time.&lt;/p&gt;
&lt;h2&gt;What it saves&lt;/h2&gt;
&lt;dl class=&quot;results&quot;&gt;
&lt;div&gt;&lt;dt&gt;−82%&lt;span&gt;turns&lt;/span&gt;&lt;/dt&gt;&lt;dd&gt;&lt;strong&gt;Rename a method used in 22 files&lt;/strong&gt;9 turns instead of 51, $0.22 instead of $0.56.&lt;span class=&quot;setup&quot;&gt;NetBeans, &lt;code&gt;java/&lt;/code&gt; folder, Opus 5.5&lt;/span&gt;&lt;/dd&gt;&lt;/div&gt;
&lt;div&gt;&lt;dt&gt;−91%&lt;span&gt;cost&lt;/span&gt;&lt;/dt&gt;&lt;dd&gt;&lt;strong&gt;Find the callers of one overload&lt;/strong&gt;91% less cost, 85% less time.&lt;span class=&quot;setup&quot;&gt;Apache Commons Lang, Sonnet 5&lt;/span&gt;&lt;/dd&gt;&lt;/div&gt;
&lt;div&gt;&lt;dt&gt;−40%&lt;span&gt;turns&lt;/span&gt;&lt;/dt&gt;&lt;dd&gt;&lt;strong&gt;Modernize 84 places among 144 candidates&lt;/strong&gt;40% fewer turns, about 22% less cost and 39% less time than writing its own scripts.&lt;span class=&quot;setup&quot;&gt;Told to use the rules, Sonnet 5.5&lt;/span&gt;&lt;/dd&gt;&lt;/div&gt;
&lt;/dl&gt;
&lt;p&gt;Each figure compares the same agent with and without the server.&lt;/p&gt;
&lt;p&gt;These are real numbers we measured with Claude models. Find the details
in &lt;a href=&quot;https://github.com/dukehoff-gmbh/dukehoff-mcp/blob/main/eval/RESULTS.md&quot;&gt;eval/RESULTS.md&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are not alone with this idea. Netflix and JetBrains recently reported that giving
agents the headless code intelligence of IntelliJ IDEA made them typically 20–30% more efficient,
and up to about 65% in the best case
(&lt;a href=&quot;https://blog.jetbrains.com/ai/2026/10/how-netflix-made-ai-coding-agents-more-efficient/&quot;&gt;JetBrains blog&lt;/a&gt;).
Their tooling is proprietary: the editor extension needs an IntelliJ IDEA Ultimate subscription,
and support for terminal agents is still to come. Dukehoff MCP for Java is open source, free,
and works today with Claude Code, Codex, Copilot and others, in the terminal, in VS Code and in NetBeans.&lt;/p&gt;
&lt;h2&gt;Why we hit the Jackpot with NetBeans&lt;/h2&gt;
&lt;p&gt;Apache NetBeans is free and open source, and it understands Java with javac itself,
the same compiler your build uses: types, overloads and overrides are resolved exactly,
not approximated. It runs without a window just as well; its LSP server, which powers
the Apache NetBeans extension for VS Code, does that every day. And it has a mature
refactoring engine, built over more than twenty years.&lt;/p&gt;
&lt;p&gt;Beyond that, NetBeans has a hidden gem: Jackpot, a rule-based rewrite engine.
A Jackpot rule matches code by its structure and types, not by its text, so a
migration changes what it should and leaves look-alikes alone.
Agents can write such rules themselves, and we ship tested rule sets for common migrations:
JUnit 4 to 5, Java modernization, and javax to jakarta.&lt;/p&gt;
&lt;h2&gt;Get it&lt;/h2&gt;
&lt;p&gt;Dukehoff MCP for Java is free and open source under the Apache License 2.0. Pick the way that fits how you work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude Code, Codex, Copilot CLI and other terminal agents:&lt;/strong&gt; download &lt;code&gt;dukehoff-mcp-&amp;lt;version&amp;gt;.tar.gz&lt;/code&gt; (macOS, Linux) or &lt;code&gt;.zip&lt;/code&gt; (Windows) from the &lt;a href=&quot;https://github.com/dukehoff-gmbh/dukehoff-mcp/releases/latest&quot;&gt;latest release&lt;/a&gt;, unpack it, and let it print the registration for your agent: &lt;code&gt;dukehoff-mcp --config claude&lt;/code&gt; (&lt;a href=&quot;https://github.com/dukehoff-gmbh/dukehoff-mcp#installation&quot;&gt;installation&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VS Code with GitHub Copilot:&lt;/strong&gt; install &lt;em&gt;Dukehoff MCP for Java&lt;/em&gt; from the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=dukehoff.dukehoff-mcp-java&quot;&gt;Visual Studio Marketplace&lt;/a&gt;, or search for &amp;quot;Dukehoff MCP&amp;quot; in VS Code&#39;s Extensions view. It brings the Apache NetBeans extension along; the tools appear in Copilot&#39;s agent mode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NetBeans:&lt;/strong&gt; download &lt;code&gt;dukehoff-mcp-netbeans-&amp;lt;version&amp;gt;.nbm&lt;/code&gt; and &lt;code&gt;dukehoff-mcp-launcher-&amp;lt;version&amp;gt;.jar&lt;/code&gt; from the &lt;a href=&quot;https://github.com/dukehoff-gmbh/dukehoff-mcp/releases/latest&quot;&gt;latest release&lt;/a&gt;, install the plugin under Tools &amp;gt; Plugins &amp;gt; Downloaded, and connect your terminal agent to the running IDE with &lt;code&gt;--ide&lt;/code&gt; (&lt;a href=&quot;https://github.com/dukehoff-gmbh/dukehoff-mcp#with-a-running-netbeans-ide&quot;&gt;how&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You need a JDK 17 or newer; VS Code 1.101 or newer for the extension, NetBeans 31 or
newer for the plugin. To make your agent reach for the tools on its own, add the short
section from &lt;a href=&quot;https://github.com/dukehoff-gmbh/dukehoff-mcp#telling-the-agent-when-to-use-the-tools&quot;&gt;Telling the agent when to use the tools&lt;/a&gt; to
your &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you have a migration where the meaning of a change depends on types,
such as an in-house framework, a vendor library swap or a Java EE to Jakarta EE
move beyond the APIs, we can help: &lt;a href=&quot;https://dukehoff.com/en/contact/&quot;&gt;get in touch&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Dukehoff MCP for Java runs on Apache NetBeans. It is not part of, nor endorsed by, the Apache
NetBeans project or the Apache Software Foundation. &amp;quot;Apache&amp;quot;, &amp;quot;NetBeans&amp;quot; and &amp;quot;Apache NetBeans&amp;quot; are
trademarks of the Apache Software Foundation.&lt;/p&gt;
</content>
  </entry>
</feed>
