vitaminmcp 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/checksums.json +7 -7
- package/package.json +1 -1
- package/runner/runner.mjs +6 -0
- package/runner/src/actions.mjs +36 -17
- package/runner/src/bots.mjs +2 -0
- package/runner/src/dispatch.mjs +2 -1
package/README.md
CHANGED
|
@@ -7,11 +7,9 @@ This package is the launcher. It fetches the jars it needs on first run and spea
|
|
|
7
7
|
MCP client — it is not the whole product on its own: the agent is a Paper plugin, and it goes on
|
|
8
8
|
the Minecraft server.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Or in `.mcp.json`, `claude_desktop_config.json`, or whatever your client calls it:
|
|
10
|
+
It speaks plain stdio, so it works in any MCP client — Claude Code, Cursor, Codex, Gemini CLI,
|
|
11
|
+
Windsurf, Claude Desktop, VS Code. Register it wherever your client keeps MCP servers
|
|
12
|
+
(`.mcp.json`, `.cursor/mcp.json`, `~/.gemini/settings.json`, `claude_desktop_config.json`, …):
|
|
15
13
|
|
|
16
14
|
```json
|
|
17
15
|
{
|
|
@@ -24,8 +22,14 @@ Or in `.mcp.json`, `claude_desktop_config.json`, or whatever your client calls i
|
|
|
24
22
|
}
|
|
25
23
|
```
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
`
|
|
25
|
+
Clients with a CLI take the same thing as a command, e.g.
|
|
26
|
+
`claude mcp add vitaminmcp -- npx -y vitaminmcp` or
|
|
27
|
+
`codex mcp add vitaminmcp -- npx -y vitaminmcp`.
|
|
28
|
+
|
|
29
|
+
The server publishes a `setup` MCP prompt that walks through the other half — putting
|
|
30
|
+
`VitaminMCP.jar` in the Minecraft server's `plugins/`, restarting it, and connecting. In Claude
|
|
31
|
+
Code that surfaces as `/mcp__vitaminmcp__setup` (named after whatever the server was registered
|
|
32
|
+
as — `/mcp` lists it); in any client, just ask:
|
|
29
33
|
|
|
30
34
|
> **Prompt:** Set up VitaminMCP on my Minecraft server at ~/servers/test and connect to it.
|
|
31
35
|
|
|
@@ -39,12 +43,13 @@ agent leaves its host, ports and token where this server reads them.
|
|
|
39
43
|
- Open, read, click and assert on inventories and plugin GUIs
|
|
40
44
|
- Wait for events and conditions instead of sleeping
|
|
41
45
|
- Read live server state: events, logs, exceptions, permissions
|
|
42
|
-
- Paper / Purpur 1.21 through 1
|
|
46
|
+
- Paper / Purpur 1.21 through 26.1, from one install
|
|
43
47
|
|
|
44
48
|
## Requires
|
|
45
49
|
|
|
46
50
|
- **Java 21 or later** on this machine — the jars run on the JVM. Point `JAVA_HOME` at it, or have
|
|
47
|
-
`java` on `PATH
|
|
51
|
+
`java` on `PATH`. (The Paper server itself needs Java 25 from 26.1 on; that is Paper's
|
|
52
|
+
requirement, and the agent loads there unchanged)
|
|
48
53
|
- **Node 18.17 or later** when using the source runner fallback. If Node is absent, the launcher
|
|
49
54
|
selects a pinned platform runner asset instead.
|
|
50
55
|
- **Paper 1.21 or later** on the Minecraft server, with `VitaminMCP.jar` in its `plugins/`
|
package/checksums.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.0
|
|
2
|
+
"version": "3.1.0",
|
|
3
3
|
"jars": {
|
|
4
|
-
"mcp-server.jar": "
|
|
4
|
+
"mcp-server.jar": "f64ee16d5aaa115a9d4e019adccf830b74ebe8683d2ade5e05b7db0cf9692d66"
|
|
5
5
|
},
|
|
6
6
|
"assets": {
|
|
7
|
-
"bot-runner-win-x64.exe": "
|
|
8
|
-
"bot-runner-linux-x64": "
|
|
9
|
-
"bot-runner-linux-arm64": "
|
|
10
|
-
"bot-runner-darwin-x64": "
|
|
11
|
-
"bot-runner-darwin-arm64": "
|
|
7
|
+
"bot-runner-win-x64.exe": "8af36a3e4a3678db5246777c1c8080bea230b50a742dff173758d0513df74e98",
|
|
8
|
+
"bot-runner-linux-x64": "2f344983a5558d36cf03e4bcf55e0752324d159aebefc18f711c28704cf48264",
|
|
9
|
+
"bot-runner-linux-arm64": "fa6c49b5db5b6cc84b62d295bbef2ace49305400a7480e86baa009e9629d120c",
|
|
10
|
+
"bot-runner-darwin-x64": "1769db980857d36204bf93e14bbe5ba329435032b116c8003a12043ba817168a",
|
|
11
|
+
"bot-runner-darwin-arm64": "b137677b9117af944d006ff0da0e94b524875c86ebfa2596c2b856b9ef63fd2e",
|
|
12
12
|
"bot-runner-viewer-win-x64.tgz": "e39a36cdf92b9223aa7c9610f8231afc7a577e8ed007bc3571895bd945baf3e1"
|
|
13
13
|
}
|
|
14
14
|
}
|
package/package.json
CHANGED
package/runner/runner.mjs
CHANGED
|
@@ -16,6 +16,12 @@ import { Dispatch } from './src/dispatch.mjs';
|
|
|
16
16
|
import * as protocol from './src/protocol.mjs';
|
|
17
17
|
import { pingProtocol, versionForProtocol } from './src/version.mjs';
|
|
18
18
|
|
|
19
|
+
// stdout is the protocol channel; a dependency's console.log (mineflayer did this on 26.1, with a
|
|
20
|
+
// stack trace) desynchronises every later reply. `write` below is the only path to stdout.
|
|
21
|
+
for (const level of ['log', 'info', 'debug']) {
|
|
22
|
+
console[level] = (...messages) => process.stderr.write(`${messages.map(String).join(' ')}\n`);
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
async function main() {
|
|
20
26
|
const argv = process.argv.slice(2);
|
|
21
27
|
if (argv.length < 2) {
|
package/runner/src/actions.mjs
CHANGED
|
@@ -234,26 +234,45 @@ export function useEntity(bot, name, x, y, z, radius, type) {
|
|
|
234
234
|
);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
237
|
+
if (interactCarriesMouseButton(bot)) {
|
|
238
|
+
// Both packets, in this order, because that is what a real client sends and what the Java
|
|
239
|
+
// runner reproduces: a plugin listening only for the second sees nothing without the first.
|
|
240
|
+
bot._client.write('use_entity', {
|
|
241
|
+
target: entityId,
|
|
242
|
+
mouse: INTERACT_AT,
|
|
243
|
+
x: 0.0,
|
|
244
|
+
y: 1.0,
|
|
245
|
+
z: 0.0,
|
|
246
|
+
hand: MAIN_HAND,
|
|
247
|
+
sneaking: false,
|
|
248
|
+
});
|
|
249
|
+
bot._client.write('use_entity', {
|
|
250
|
+
target: entityId,
|
|
251
|
+
mouse: INTERACT,
|
|
252
|
+
hand: MAIN_HAND,
|
|
253
|
+
sneaking: false,
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
// 26.1: one packet with a hand and a hit position; attacks have their own packet.
|
|
257
|
+
bot._client.write('use_entity', {
|
|
258
|
+
target: entityId,
|
|
259
|
+
hand: MAIN_HAND,
|
|
260
|
+
location: { x: 0.0, y: 1.0, z: 0.0 },
|
|
261
|
+
sneaking: false,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
254
264
|
return entityId;
|
|
255
265
|
}
|
|
256
266
|
|
|
267
|
+
/** Through 1.21.11 `use_entity` has a `mouse` field; from 26.1 it has a `location` instead. */
|
|
268
|
+
function interactCarriesMouseButton(bot) {
|
|
269
|
+
const fields = bot.registry?.protocol?.play?.toServer?.types?.packet_use_entity?.[1];
|
|
270
|
+
if (!Array.isArray(fields)) {
|
|
271
|
+
throw new Error('The protocol definition for use_entity is missing, so the bot cannot interact with entities.');
|
|
272
|
+
}
|
|
273
|
+
return fields.some((field) => field?.name === 'mouse');
|
|
274
|
+
}
|
|
275
|
+
|
|
257
276
|
/** Left-clicks the nearest tracked entity. */
|
|
258
277
|
export function attackEntity(bot, name, x, y, z, radius, type) {
|
|
259
278
|
requireInWorld(bot, name);
|
package/runner/src/bots.mjs
CHANGED
|
@@ -153,6 +153,8 @@ export function connectionOptions(host, port, version, id, clientIp) {
|
|
|
153
153
|
auth: 'offline',
|
|
154
154
|
version,
|
|
155
155
|
checkTimeoutInterval: LOGIN_TIMEOUT_MILLIS,
|
|
156
|
+
// mineflayer's default console.logs bot errors, and stdout is the protocol channel.
|
|
157
|
+
logErrors: false,
|
|
156
158
|
};
|
|
157
159
|
if (clientIp && clientIp.trim()) {
|
|
158
160
|
options.fakeHost = addressField(host, clientIp.trim(), id);
|
package/runner/src/dispatch.mjs
CHANGED
|
@@ -30,7 +30,8 @@ export class Dispatch {
|
|
|
30
30
|
try {
|
|
31
31
|
return await this.#run(verb, command);
|
|
32
32
|
} catch (error) {
|
|
33
|
-
|
|
33
|
+
// A newline in the message would become a second stdout line, read as the next reply.
|
|
34
|
+
return protocol.encode(protocol.ERROR, verb, protocol.sanitize(error?.message ?? error));
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|