Releases: Backas03/VitaminMCP
Release list
3.0.1
Documentation only — no behaviour changed, and nothing breaks. Two places told you to enable server settings nothing needs, which is worse than saying nothing: both widen what the server trusts for no benefit.
Fixed
The verification skill made three claims that no longer held.
- Bots were said to need
settings.bungeecord: trueinspigot.yml. They do not. That setting is the opt-in for aclientIpspoofing test, and enabling it otherwise only makes the server trust the forwarding handshake. agentToolswas attributed toserver_info. It comes back fromsession_start.- The symptom of connecting too soon after a restart was named as an error that does not appear. A bot spawned before the Minecraft port is up fails with a connection timeout that names nothing.
The setup prompt announced three bot settings and listed one. online-mode=false is the one setting bots need.
Added to the skill
Tools it had never mentioned: bot_run_scenario and its steps, that a reused bot name is the player the server already remembers, bot_inspect's message cursor, bot_view, several named sessions at once, session_reset, and the player_state wait condition.
Full Changelog: 3.0.0...3.0.1
3.0.0
Native runners for every supported platform, and bot_inspect can prove when a reply arrived.
Breaking
bot_inspect's messages is no longer an array of strings. Each message now carries the order it arrived in, the millisecond it reached the client, and the text — so you can say the reply landed 400ms after the command. Anything reading messages[i] as a string has to change.
A runner from an older version will not work with this one. The mismatch is refused on the first bot_inspect, with a message saying which half is old.
Added
Native runners for Linux x64, Linux arm64, macOS Intel and Apple Silicon, joining Windows x64 — so Node is no longer a requirement anywhere.
bot_inspect takes a cursor and answers with messageCursor and messagesDropped. Call it before the action, keep the cursor, pass it back afterwards, and you get that action's reply rather than the whole backlog. messagesDropped is nonzero when what you wanted has already fallen out of the 100-message window. A cursor from another session or a replaced bot is refused.
Paper 1.21.11 joins the supported range, which now passes end to end on eight versions from 1.21.1.
The world viewer is downloaded only when bot_view asks for a world, and it is a 31MB download rather than 269MB. It is published for Windows x64; other platforms say so and accept a local one.
Fixed
session_startno longer lists sessions whose runner has exited, which could make an unnamed call ambiguous.use_blockwaits for the bot to see the block before interacting, rather than racing a change made by the command before it.
Full Changelog: 2.2.0...3.0.0
2.2.0
A caller can now read what a plugin declares, and several answers that used to read as their opposite have been corrected.
Added
state_query kind="plugin" — what a plugin declares, and what it actually loaded. The commands with the permission node gating each, the permissions with their defaults, and the live config. Until now, to test a permission you had to already know its node: state_query could test one and never list one.
A plugin's config is where database passwords and API tokens live, and this tool is readable by anything holding the agent's token. Values whose key looks like a secret come back (redacted) with the key still shown, so you can see the setting exists and ask a human.
One limit: a command's permission is null whenever its plugin.yml does not declare one, which is common. Read the permissions list too rather than concluding a command is ungated.
bot_spawn reports gameMode and op. Gamemode changes how every later observation reads — creative masks a full-inventory failure, and item grants behave differently.
break_block says what became of the dig. It used to answer "sent", which made a dig a plugin cancelled and a dig that never arrived the same observation. Now:
| the block is gone | broke <material> |
| the block is unchanged | something cancelled the break |
| the server never acted on it | the join window; no BlockBreakEvent is fired |
| the bot cannot see the block | its chunk has not arrived, so nothing was sent |
bot_spawn waits until the ground under the bot is loaded, rather than returning while the bot could still act on a block it has not been told about.
Fixed — answers that read as their opposite
bot_inspect's items was [] for a bot holding a full inventory. items has only ever meant the open menu's contents, and no menu was open — but it reads as "this player has nothing". It is now null, with a note saying where the player's own inventory lives.
exceptions_recent starts empty at every boot. An empty answer reads as "this never happens" when it means "not since this server started". It now says so.
bot_run_scenario's failure evidence covered the whole retention window and was led by whatever was most frequent, with the run's own earlier successes counted in. It is now the events and log lines from the failing step's own window. wait_for's timeout evidence is filtered to the player the condition names.
assert_message checked once and moved on, which is a race against any plugin that does not answer within the same tick, and it failed with wording that reads as a verdict when the truth is "not yet". It now waits, with timeoutMillis.
state_query kind="block" echoed the world argument back rather than the world it read, so omitting it answered "world": null.
logs_query no longer returns the agent's own activity. A search for a plugin's output was coming back largely as your own calls and echoes of results you had just read. Those lines stay on the console. Colour codes are stripped from captured messages.
EndermanAttackPlayerEvent joins the high-frequency exclusions, where it was crowding everything else out of events_summary.
Upgrading
One response shape changed. bot_inspect's items is null rather than [] when no menu is open, so a caller checking for an empty array has to check for null. Nothing else breaks.
Worth knowing
A reused bot name is the player the server already remembers. Inventory, position, advancements and anything a plugin stored against it survive from earlier runs. Use an unused name when a first join is the thing under test.
as does not fire PlayerCommandPreprocessEvent; a scenario's command step does. On a server whose plugin cancels or rewrites commands in that listener, as tests something else.
command_exec with as cannot tell you whether the command worked. dispatched: true with empty output is what a successful command looks like, what a late async reply looks like, and what a plugin refusing on the action bar looks like. Read bot_inspect to tell them apart.
Full Changelog: 2.1.1...2.2.0
2.1.1
The Windows runner asset drops from 564MB to 134MB. That file is what a user without Node installed downloads in order to spawn a bot, which is why this is a release rather than something held for later.
Asking for a Minecraft version outside the supported range now fails saying it was never included, instead of behaving as though the game had no blocks.
Nothing breaks against 2.1.0.
Fixed
The docs stated a permission policy that Paper changes between versions. A non-op is refused /list on Paper 1.21.8 and runs it on Paper 1.21.1, but the tool description, the usage docs and the verification skill all told you a non-op would be refused. What command_exec reports was never wrong — it asks the server rather than assuming — only the text around it.
Verified at both ends of the supported range: all 21 compatibility checks pass on Paper 1.21.8 and on 1.21.1, driven by the 134MB executable.
Full Changelog: 2.1.0...2.1.1
2.1.0
command_exec now says why a command was not dispatched. It used to answer dispatched: false with an empty output for three different outcomes, and a caller could not tell them apart — including from a command that ran and did nothing, which looks identical. The player is told nothing either, not even "unknown command", so there was no way to recover the reason afterwards.
reason says |
What happened |
|---|---|
was refused before it executed, because ... does not have <node> |
The command exists and the sender may not use it. Grant the node, or op the bot |
this server has no command named '...' |
Nothing by that name. Check the spelling, or whether the plugin registered it |
nothing on this server answers to '...' |
Asked by a bot that is not op, where the two cannot be told apart. Run it as the console to settle it |
Nothing breaks — the field is additive. Minor rather than patch because for an MCP server the tool schema and description are the interface.
Full Changelog: 2.0.0...2.1.0
2.0.0
The bot side is rebuilt on mineflayer. What that buys is everything below: a client that really walks, that can fight and place blocks, and that you can watch in a browser.
Breaking
The Java bot runner is removed. runnerJar now names a Node script or a native runner executable, not a jar. If you pass an explicit path, it has to change. If you let it default, the launcher already points at the right thing.
Bots no longer use BungeeCord forwarding by default. settings.bungeecord: true in spigot.yml is no longer required for a normal login. It remains for tests that need a spoofed address or identity, and those opt in with clientIp.
Added
Real movement. move_to walks to its destination, so pressure plates and movement listeners fire the way they do for a player. mode: "teleport" places the bot at a coordinate for setup. A route that does not exist and a walk that ran out of time are reported as different failures.
Verbs the Java runner never had. Attack, hotbar selection, dropping, block placement, jump, sneak, sprint, look_at, and assert_reachable, which changes nothing and is satisfied only by a complete path. Health, hunger, experience and active effects are now readable.
bot_view — a live localhost page for one bot. what="world" renders what the bot sees; what="inventory" is a live page of the open menu. The world viewer is optional and is not downloaded until you ask for it.
A Windows x64 native runner, so Node is not a requirement there.
A Claude Code plugin, carrying the operational knowledge of driving this.
Upgrading
- If you set
settings.bungeecord: trueonly for VitaminMCP, turn it off. It makes the server trust the forwarding handshake, and nothing here needs it any more. - Native runners are Windows x64. Linux and macOS run the Node source runner, which needs Node installed.
Full Changelog: 1.5.1...2.0.0
1.5.1
1.5.0's MCP registry entry cannot be resolved. It was published under a misspelled name, and a published version cannot be corrected in place — so this release carries the right one.
Nothing else changed, and nothing breaks. 1.5.0 works if you already have it; it just cannot be found through the registry. If you installed it, take this one.
Full Changelog: 1.5.0...1.5.1
1.5.0
Installing this stopped being a four-step manual procedure. It used to begin with "download three jars and put each somewhere different". It is now two halves: one line for the client, and a command that does the server side for you.
Nothing breaks against 1.4.2.
Added
An npx launcher.
claude mcp add vitaminmcp -- npx -y vitaminmcp
It verifies what it downloads, so a corrupted or tampered file is refused rather than run. The bot runner is large and arrives in the background — the server answers immediately, and the first call that actually needs a bot waits for it.
/mcp__vitaminmcp__setup installs the plugin half. The part that happens on the Minecraft server used to be a README section you followed by hand. It is now a prompt that walks it: check the server version, place the jar, restart, read the console, connect. It asks before downloading anything and before changing server.properties, and says what each bot setting does before offering to set it.
session_start takes no arguments for a server on this machine. The agent generates its own token on first start, so there is nothing to copy out of a log. The response reports resolvedFrom, so a session that reached something unexpected says so rather than looking like a working one.
A server on another machine still needs host and token together. With several agents running, an omitted mcpPort is an error listing them rather than a pick.
Listed in the MCP registry, so it can be found rather than only linked to.
Fixed
runnerJar can now actually be omitted. It was documented as optional and worked only if you passed an explicit path.
Full Changelog: 1.4.2...1.5.0
1.4.2
Two MCP sessions can now drive one server. Both failures below showed up the same way — one session worked and the other appeared not to connect — and neither said why.
Fixed
wait_for starved every other client. The agent served requests from four threads, and wait_for sits on its thread for up to a minute while a condition comes true. Four concurrent waits therefore held every thread the agent had, and each further request — from any session — queued until one finished. A second session's call never returned and its MCP client timed out.
Measured against a real agent, four waits in flight:
| idle | four waits running | |
|---|---|---|
| before | 0.08s | 28.01s |
| after | 0.08s | 0.00s |
Waits now run on a pool of their own, bounded at 32. Past that a caller gets 429 and a message saying so, rather than joining a queue and later reporting a timeout it never spent waiting.
A bot name already on the server evicted whoever held it. A bot's UUID is derived from its name — that is what makes Tester1 the same player every run — so two sessions using one name are one player, and the server admits a player once. It resolves the clash by taking the newcomer and kicking the incumbent.
Which side lost was a race. Often the second bot_spawn was rejected; sometimes it won and the first session's bot vanished with nothing reported, its own bot_spawn having already returned success.
bot_spawn now asks whether that player is online before connecting and refuses if so, leaving the existing bot alone. A rejection that still races through explains itself instead of arriving as the bare translation key multiplayer.disconnect.duplicate_login.
Upgrading
Give each session its own bot names. Lobby_Tester1 and Survival_Tester1 rather than Tester1 twice. Names are identities here, so sharing one across sessions was never going to mean two players — the difference is that it now says so instead of quietly dropping one.
Nothing else changes. session_start was never the problem: two of them against one agent both return in 0.53s, before and after.
1.4.1
A patch release over 1.4.0. If you are on 1.4.0, take this one — its jars are otherwise identical.
Fixed
initialize reported the wrong version. serverInfo answered 1.0.0-SNAPSHOT, hardcoded, and wrong since 1.0.0. It is what an MCP client displays for this server, so the one place you could read the version was the one place that never got updated — 1.4.0's own jar still calls itself 1.0.0-SNAPSHOT.
The number now comes from the jar manifest, which Gradle fills from the project version, so there is no second copy to forget. Running from a directory rather than a jar — an IDE or a test run — reports dev.
Upgrading
Replace the three jars. Nothing else changes: no tool, parameter or behaviour differs from 1.4.0.
Everything in 1.4.0 — several sessions at once for proxied networks, and the resource-pack hang fix — is in this release too. Read those notes if you are coming from 1.3.0.