Then I saw this in the article:
>> I've discovered that Claude Desktop supports MCPB. The MCPB provides a Node runtime. Therefore, our application would only contain our code. This means the size of the application would be ~1MB.
I don't know why this is so appealing to me but it is. I currently use Claude Code with a DeepSeek v4 Pro API backend. I will check out if Claude Code itself has MCPB support.
> What surprised me was that Java supported processing ZIP files and XML from its standard runtime.
Makes me feel old.
Java was developed at a time when most people connected to the Internet via dial-up. That meant two things: the runtime is something you download once and has to work offline, and it has to support compressed packages. JAR files are essentially ZIP files with additional metadata stored as files. So yes, that’s why Java has built-in libraries for ZIP files.
The XML came with the “XML fever” of the 2000s. Java was one of the first languages to include XML support, and many of the XML DOM APIs are very Java-oriented. And, of course, it was included in the standard lib because nobody wanted extra module downloads over a slow connection. (and because XML was evolving at that time, it also meant that you had some issues between the SDK libs and user-provided libs)
On the JS/TS side, you also have a few interesting tools to make interacting with XML a bit easier in practice (cheerio, for example). I'm not sure if the purpose in interacting with DOCX and to what extent manipulation is wanted/needed... it's a complex format to say the least, especially with references/embedding.
I've never been a Java fan, having preferred C# along the way, but I don't think I'd use either for something like this just because of the runtime size, if nothing else.
The MCPB format seems to be able to run external processes, even if there's a Node in the middle. So you could also compile the Java version to a native binary with GraalVM and ship that as an MCPB.
There is a zip file of code in the releases though. I wonder if the LPGL 3.0 license covers that?
UPDATE: the zip file is just the repo itself. So everything interesting is in the legalrabbit-docx-mcp.exe, contained in the GitHub releases.
What a tease.
It doesn’t.
On the side of the jpackage: I'm currently using GraalVM compile to native for a Kotlin CLI tool. I do the build in a build container so I use an older glib to ensure compatibility on a wide variety of Linuxes, AND because this way no-one needs to install all the GraalVM requirements by hand. The result is a 57MB binary, that start in a blink of the eye. The downside is long compile times (2 minutes for a simple CLI tool that uses AWS SDK). I think I prefer this of jpackage; but I'm not building a GUI tool.
Btw, you should have looked at dotnet for this as well. There is a very good library ( DocumentFormat.OpenXml) that can handle all docx/xlsx/pptx files. And dotnet can ship standalone binaries (though AOT probably won't work).
It's annoying when acronyms are used without explanation. It's https://github.com/modelcontextprotocol/mcpb , which looks a kind of installation bundle for MCP servers.
That all being said, the JVM has never broken into local apps, even with JavaFx. I think the thought of installing a JVM, plus then installing an app is no longer a workflow most users will tolerate. Electron has proved though people don't mind downloading a multi-gig app anymore however, so maybe this is no longer an issue.
Perhaps because he sucks.
It's easy to write ugly code in any language. You need to think, in order to write good code, in any language. Even then some languages are uglier than others. All my PHP code looks awful in comparison to my ruby code. Even with the same expertise level, the issue remains that PHP is just an uglier language. Similar comparisons can be made for many other languages.
> The biggest issue is no typing.
Well ... it's not. But it is pointless to try to explain this people whose brain is too addicted to "mama told me types must go into everything I touch".
> I worked at Stripe for 4.5 years, so I looked into integrating Sorbet
There you go! This already tainted his ability to THINK.
> Forgot .each in node.children.each do |x|. The error showed up as "unexpected nil" at a random place.
Erm ... how can you forget this while claiming you write a lot of code in ruby?
By the way, I almost never use the do/end synax. It helps me visually to use {}.
A side effect is that:
def foobar(
i = node.children
)
i.each {|element|
check_for_cats_in_this_container(element)
}
end
will also be easier to visually notice missing things
such as ".each" or ".map" or anything. Keep the structure
simple at all times. And no, forgetting .each a lot - that
is a bogus complaint. People using ruby for a little while
don't really do such an error. But let's assume it is that
way: def foobar(
i = node.children
)
i {|element|
check_for_cats_in_this_container(element)
}
end
Well, that visually already looks wrong. What method
did you want to run on the variable i here?> Forgot .children in node.children[i]. The error showed up as > "unexpected nil" at a random place.
Mate - use methods. The guy seems to have written only spaghetti code in ruby. Definitely not "4.5 years of writing ruby". Nah.
Also, "unexpected nil" - first, such an error does not exist, he probably means a nil. Second, why do you not check for nils properly? You don't need types for that. So the issue is you were lazy when writing the code. Understandable, but not a reason to "I need types" as compensation for your own laziness here.
> Using assert_raises in the test obscured a compilation error.
What compilation actually? Then again someone who forgets .each, already disqualified here.
>I used ruby_llm-mcp where the doc mentioned tool.input_schema but the latest version has renamed it to tool.params_schema (issue). It took me a while to figure out since there was no typing hint to help me with it.
Sounds like a project with awful documentation. This is a problem with many ruby projects. For some reason ruby projects hate documentation, with a few exceptions. I never understood this. To me it seems writing ruby is fun, then nobody wants to write documentation. That's really stupid of those people who do. Please look at rack. This thing has no real documentation that is useful. Same with opal and ruby-wasm. Ruby hates documentation. It's my number #1 complaint too.
I am fine with those admitting to this problem. Who I can not stand are the "the code is self-explanatory". This always leads me to assume the code is utter garbage. And this indeed is almost always the case so.
> rubyzip: it has an obscure bug where it produced a corrupted DOCX file. I've encountered this bug with a DOCX file from our customers. I can't share the confidential file to the rubyzip author.
Or it is a fake story. Is this AI generated text? The guy makes up things on the fly.
People, please - stop using AI. It will slop zombify your brain.
You can't get very far in Java development without working with .jar files (which are zip archives).
> Later, I've discovered that Claude Desktop supports MCPB.
> MCP Bundles (.mcpb) are zip archives containing a local MCP server and a manifest.json that describes the server and its capabilities. The format is spiritually similar to Chrome extensions (.crx) or VS Code extensions (.vsix), enabling end users to install local MCP servers with a single click.
Personally, I think there should be a balance. The direct consequence of a barebones stdlib is NPM and having to download hundreds of dependencies for a hello world.
Especially anything related to lambdas, map, filter and co.
Something that was lost in most OOP languages that followed suit, until like a decade later.
First party from Microsoft; feels like it would be the way to go.
Microsoft are incapable of:
1. naming things well
2. keeping those names stableI'll check out Bitwig.
Good point. And both Java and Ruby borrowed from Smalltalk (according to Wikipedia Kotlin does not, but that is: not directly.
Sadly Java did not take Smalltalk's FP inspiration (I guess they were strayed by C++'s lead in that regard), and we needed streams and now Kotlin to fix that :)
Smalltalk's syntax never go really popular though. One could say that was its biggest drawback.
A lot of Smalltalk-style syntax was absolutely massive for a decade or so you could argue, at least under the guise of the gazillions of iPhone apps that were written in Objective-C. This random blog post probably does a better job than me:
> https://richardeng.medium.com/apple-has-been-using-smalltalk...
This would be my guess, I always heard nice things about it and liked many concepts, but the syntax was just plain ugly to me, so I never felt the urge to try it out. I imagine others felt similar.
By tanin — May 19, 2026
We've built a Claude Cowork DOCX plugin in Ruby, Java, and TypeScript. Java is the winner for supporting zip files and XML in its runtime with no issues. However, TypeScript is chosen due to the possibility of MCPB support.
CLAUDE_PLUGIN_ROOT and seems impossible to execute a binary inside the plugin.Recently, we've implemented the same Cowork DOCX plugin 3 times. The first prototype was in Ruby. We reimplemented it in Java in order to make it a desktop app. Then, We reimplemented it in Typescripts (Node) in order to make it compatible with MCPB. Eventually, We've switched to Bun because Cowork plugin apparently doesn't support MCPB.
Since now I have direct experience in implementing the same app in 3 different languages within a span of a month, I've decided to write about it.
For context, a DOCX file is actually a zip file with a bunch of XMLs and other files in it. Therefore, our code must process zip files and XMLs.
💡
If anyone is interested in using our DOCX plugin with Claude Cowork, you can try it out here: https://github.com/LegalRabbit-AI/legalrabbit-docx-claude-plugin
It works well with legal documents, and you can use it together with the Claude for Legal plugin.
The plugin is still in its nascent stage. I'd love for you to drop us an email at tanin@legalrabbit.ai, so we can help you and notify you when there's an update.
At first, I built a prototype in Ruby because my colleague knew Ruby well, and we wanted it to a server-side application. I wrote a lot of Ruby back in 2010s. I felt Ruby was a beautiful language. Unfortunately, I don't feel that anymore.
The biggest issue is no typing.
I worked at Stripe for 4.5 years, so I looked into integrating Sorbet; it was a bit too involved, so I didn't do it. I also looked at RBS but couldn't understand how to integrate it. Admittedly, I didn't spend too much time on this.
Here were some common errors that I encountered:
.each in node.children.each do |x|. The error showed up as "unexpected nil" at a random place..children in node.children[i]. The error showed up as "unexpected nil" at a random place.assert_raises in the test obscured a compilation error. The assert_raises would just complain the error doesn't match the expected error. I had to comment out assert_raises to see the error stacktrace.tool.input_schema but the latest version has renamed it to tool.params_schema (issue). It took me a while to figure out since there was no typing hint to help me with it.In terms of processing a zip file and XMLs, I used:
libxml2. Therefore, it's not exactly a bug for nokogiri to solve.After some time, we've decided that we want a Claude Cowork plugin instead. A Claude plugin supports executing a binary locally, so I've chosen Java for it.
I have my fair share of building a Java desktop application and know jpackage and alike very well (See: my Java electron framework).
What surprised me was that Java supported processing zip files and XML from their standard runtime. I didn't encounter any issue with the zip and XML library at all. It works as expected.
The final single-executable binary is about 88MB. It is 88MB because a JDK is embedded inside.
I used AI to port the code. It took only 3 days to port thousands of lines of code.
Later, I've discovered that Claude Desktop supports MCPB. The MCPB provides a Node runtime. Therefore, our application would only contain our code. This means the size of the application would be ~1MB.
With TypeScript, I have to use the following libraries for processing zip files and XML:
I used AI to port the code from Java to TypeScript. It only took 1.5 days to do.
MCPB is great. However, to operate our DOCX MCP, we need an accompanying skill. Claude Desktop supports this through Claude plugin where both skills and MCPs can be packaged together. However, Claude plugin doesn't support MCPB :S
Claude plugin only supports a single-executable binary. Node doesn't have this feature. Fortunately, Bun has, and it works as expected.
The only gap with Bun is that I cannot figure out how to upload the source map to PostHog because PostHog requires both the minimized JS file and the .js.map file.... but Bun only produces the binary and the .js.map file.
The final file size is ~70MB on Mac and ~120MB on Windows.
In the end, I've decided to stick with TypeScript because of the possibility of MCPB being supported in Claude/Codex plugin.
I was looking at making a Codex plugin but Codex is behind Claude Desktop in terms of the plugin mechanism.
Claude supports the environment variable: CLAUDE_PLUGIN_ROOT , and that enables me to execute the single-executable binary as the stdio MCP server.
Codex doesn't have it. Their plugin structure has a folder for binaries but their doc doesn't mention how to execute the binary in that folder. I tried a couple possible paths but couldn't make it work.
Java is the winner for me. However, as I mentioned earlier, I've decided to stick with TypeScript due to the possibility of future MCPB support without embedding the Node runtime.
Ignoring the possibility of the embedded Node runtime, Java's strict typing makes it easier to code. The Java's built-in libraries for zip and XMLs work as expected.
The maturity of the Java runtimes on both Windows and Mac gives me more confident compared to Bun, which is quite new. To be fair, so far I haven't encountered any issue with Bun apart from being unable to upload source maps to PostHog. Our Bun-built Claude plugin seems to work well on both Mac and Windows.