CODING
MCP Server v2.0: Modernizing the Toolchain
Apr 15, 2026
Six months after reaching 1.0.0, metalsmith-plugin-mcp-server hits 2.0.0 with its first real breaking change: the toolchain every scaffolded plugin inherits has been modernized. ESLint and Prettier are out, replaced by Biome. Mocha, Chai, and c8 are out, replaced by Node's native test runner and its experimental coverage reporter. The result is simpler plugins, fewer dependencies, and one less rake to step on.
(Background: the original weekend build and the 1.0 retrospective.)
When I first scaffolded plugins, I reached for what everyone else was using: ESLint for linting, Prettier for formatting, Mocha with Chai for tests, c8 for coverage.
What I didn't fully appreciate was the dependency tax each new plugin was paying. A fresh scaffold pulled in ESLint plus two plugins for Prettier integration, Prettier itself, Mocha, Chai, and c8. Seven config surfaces to maintain, and a transitive dependency tree that took longer to install than the plugin took to write.
Meanwhile, the MCP server itself had quietly migrated. I'd switched it to Biome for linting and formatting, then moved the test suite to node:test when the native runner stabilized in Node 22. The MCP server was lean. The plugins it generated were not.
What Changed
The migration itself was mechanical. Biome replaces both ESLint and Prettier with a single binary and a single config file.node:testplusnode:assert/strictreplaces Mocha and Chai with zero dependencies.node --test --experimental-test-coverageproduceslcovoutput directly, soc8goes too.
Concretely, per scaffolded plugin:
devDependencies: 9 → 5
Removed: c8, eslint, eslint-config-prettier, eslint-plugin-prettier, mocha, prettier
Added: @biomejs/biome
Config files: 3 → 1
Removed: eslint.config.js, prettier.config.js, .c8rc.json
Added: biome.jsonFour fewer packages, two fewer config files, and no more debating whether ESLint's formatting rules should yield to Prettier's. Biome is both.
Inside the MCP server, the simplification was smaller but real: a net reduction of about 80 lines across templates and config generation, and three template files deleted outright.
The Breaking Parts
This is a 2.0 release because the MCP tool contracts changed. The validate tool's checks enum no longer acceptseslint— passbiomeinstead. The configs and show-template enums dropeslintandprettier. Scaffolded plugins now require Node 22 or newer, because that's where the coverage reporter destinations stabilize.
None of this is load-bearing for most users, but anyone scripting against the MCP server will notice. The migration for existing plugins is a three-step process:
rm eslint.config.js prettier.config.js .c8rc.json .mocharc.*
npx metalsmith-plugin-mcp-server configs .
# Then update test imports from mocha/chai to node:test/node:assert/strictThe test import change is the only part that's properly manual. Everything else is a one-liner.
Tool Evolution
Tools bring their own dependencies. Every dependency is a contract: you're promising to keep it updated, to understand its config, to debug it when it breaks. Seven dependencies to get from empty directory to passing tests was a contract I hadn't meant to write.
Node's native test runner changes the math. It's not a new thing to learn — if you know JavaScript, you know enough. Biome is a new thing, but it replaces two old things, and its config is small enough to read in one sitting. Net complexity goes down.
The broader lesson is that tools evolve and are worth revisiting frequently. The ecosystem moves. Native runtime capabilities that didn't exist are now stable.
What Plugin Authors Get
If you're writing a new Metalsmith plugin as of today,npx metalsmith-plugin-mcp-server scaffold my-plugin ...gives you:
- One config file for lint and format (biome.json)
- Test files that import from
node:test— nomocha/chai - Coverage via npm run coverage with no extra tool
You can still reach for external tools if you need them. Nothing here is exclusive. But the default shape is smaller.
Upgrading Existing Plugins
A breaking change is only as painful as its migration. For the plugins you already have on the old toolchain, the upgrade path is a single command:
npx metalsmith-plugin-mcp-server@latest install-claude-mdThat command used to just install AI assistant guidance. As of 2.0.1, the CLAUDE.md it installs includes a Migration from Legacy Toolchain section — a set of detection rules and step-by-step instructions aimed at Claude (or any AI assistant reading the file).
The workflow is:
1 Runinstall-claude-mdon your existing plugin 2 Open the plugin in Claude Code and ask: "Is this plugin on the legacy toolchain? If so, migrate it." 3 Claude reads CLAUDE.md, detects legacy files (eslint.config.js, .c8rc.json,mochain devDeps, etc.), and walks through the migration, confirming before each destructive step.
Most of it is mechanical. Delete the old configs, runconfigs. to generatebiome.json, update package.json dependencies and scripts, bumpengines.node to >= 22.0.0. The one part that can't be fully automated is rewriting test imports — convertingmocha+chaipatterns tonode:test+node:assert/strict. Claude handles that file by file, which is exactly the kind of tedious mechanical task AI assistants are good at and humans hate.
The principle from 1.0.0 carries forward: rather than write a migration script that makes guesses, write guidance that lets the AI make informed decisions in context. The plugin's existing CLAUDE.md, package.json, and test files are all the context the AI needs. The MCP server just has to tell it what to look for and what good looks like.
If you'd rather do it by hand, the steps are in the CLAUDE.md migration section itself. It's about five minutes ofrm, a regenerated config, and a test-import sweep.