vitaminmcp 3.2.0 → 3.2.2

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/checksums.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
- "version": "3.2.0",
2
+ "version": "3.2.2",
3
3
  "jars": {
4
- "mcp-server.jar": "19e54259d2afb5171b6af61fb9b5e8dc41178807eef9886b0105568cab26cab6"
4
+ "mcp-server.jar": "ccc3d22de5b86b77acd3a253d406943a22c50226b8ca7cdfb5f103086ccfc520"
5
5
  },
6
6
  "assets": {
7
- "bot-runner-win-x64.exe": "e79b3e4a3e5ce571d5f18f199265ef2bf2de4b94102fc537c48055687d93c501",
8
- "bot-runner-linux-x64": "75ec38cbd3facd07734ab147ef3ac7feae4bcfa909b39e5d2baf422b5e52c044",
9
- "bot-runner-linux-arm64": "f9f35216e07ff75f3f27729068af3afd40da1fb1b5126a92cfab512ae7fb8631",
10
- "bot-runner-darwin-x64": "15116f74ac073944683de21c0ee4e91f87ad079edfeed12121c54547f84ac859",
11
- "bot-runner-darwin-arm64": "bd71e5b21f5a33f81411f99a7137abb0663f355f6ef54e17504bb6087b965fc6",
7
+ "bot-runner-win-x64.exe": "8da5317732e3c1a3a3a7a41aeb1e075551a914c37ad3f46a8fd5c2f986d9313c",
8
+ "bot-runner-linux-x64": "8c54d6a84ea6b0da06ecaaf91bb1da7206a54f1a1ad6bd0ec8ee00f17e87e55e",
9
+ "bot-runner-linux-arm64": "70e2c29c63806d9692c8d9e531618d92b1326d5f1d242d2303c8a79aee637084",
10
+ "bot-runner-darwin-x64": "eac2673b2c12ad99c840e3d826a86956972a84a4f54750b116b7caf2a787065a",
11
+ "bot-runner-darwin-arm64": "3869506c398dfb05a46b760bf6d76a919107132274201db5439b003e05874907",
12
12
  "bot-runner-viewer-win-x64.tgz": "e39a36cdf92b9223aa7c9610f8231afc7a577e8ed007bc3571895bd945baf3e1"
13
13
  }
14
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitaminmcp",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "mcpName": "io.github.Backas03/vitaminmcp",
5
5
  "description": "MCP server for testing Minecraft plugins: drives a real Paper server and real protocol bots from an AI agent.",
6
6
  "keywords": [
package/runner/runner.mjs CHANGED
@@ -6,6 +6,11 @@
6
6
  *
7
7
  * node runner.mjs <host> <port> [protocol]
8
8
  *
9
+ * Without a protocol it pings the server. VITAMINMCP_SERVER_VERSION, when set, is the Minecraft
10
+ * version the agent reports, and it wins over a ping that disagrees (see `selectProtocol`). It is
11
+ * an environment variable rather than an argument so a runner and an MCP server of different
12
+ * releases still understand each other's command line.
13
+ *
9
14
  * Everything it prints on stdout is protocol. Diagnostics go to stderr, which the MCP server
10
15
  * inherits — a stray `console.log` here is a desynchronised session, not a stray log line.
11
16
  */
@@ -14,7 +19,7 @@ import { createInterface } from 'node:readline';
14
19
  import { BotRegistry } from './src/bots.mjs';
15
20
  import { Dispatch } from './src/dispatch.mjs';
16
21
  import * as protocol from './src/protocol.mjs';
17
- import { pingProtocol, versionForProtocol } from './src/version.mjs';
22
+ import { pingProtocol, selectProtocol, versionForProtocol } from './src/version.mjs';
18
23
 
19
24
  // stdout is the protocol channel; a dependency's console.log (mineflayer did this on 26.1, with a
20
25
  // stack trace) desynchronises every later reply. `write` below is the only path to stdout.
@@ -32,10 +37,24 @@ async function main() {
32
37
  const host = argv[0];
33
38
  const port = Number(argv[1]);
34
39
 
40
+ const serverVersion = process.env.VITAMINMCP_SERVER_VERSION || undefined;
41
+
35
42
  let bots;
36
43
  let negotiated;
44
+ let pinged;
37
45
  try {
38
- negotiated = argv.length > 2 ? Number(argv[2]) : await pingProtocol(host, port);
46
+ if (argv.length > 2) {
47
+ negotiated = Number(argv[2]);
48
+ } else {
49
+ ({ protocol: negotiated, pinged } = selectProtocol(await pingProtocol(host, port), serverVersion));
50
+ }
51
+ if (pinged !== undefined) {
52
+ process.stderr.write(
53
+ `The server-list ping answered protocol ${pinged}, but the agent reports Minecraft `
54
+ + `${serverVersion} (protocol ${negotiated}). Something in front of the server answered `
55
+ + `the ping, a proxy or ViaVersion; using ${negotiated}.\n`,
56
+ );
57
+ }
39
58
  bots = new BotRegistry(host, port, versionForProtocol(negotiated));
40
59
  } catch (error) {
41
60
  // The Java side reads this line and reports it as the reason the runner did not start, so it
@@ -45,7 +64,10 @@ async function main() {
45
64
  process.exit(1);
46
65
  }
47
66
 
48
- write(protocol.encode(protocol.READY, String(negotiated)));
67
+ // A third field only when the ping disagreed, so the line a caller already parses is unchanged.
68
+ write(pinged === undefined
69
+ ? protocol.encode(protocol.READY, String(negotiated))
70
+ : protocol.encode(protocol.READY, String(negotiated), String(pinged)));
49
71
 
50
72
  const dispatch = new Dispatch(bots);
51
73
  const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });
@@ -141,26 +141,16 @@ function delay(millis) {
141
141
  /** Runs a command as the bot. The leading slash is not part of the packet. */
142
142
  export function command(bot, name, line) {
143
143
  requireInWorld(bot, name);
144
- bot._client.write('chat_command', {
145
- command: line.startsWith('/') ? line.slice(1) : line,
146
- timestamp: BigInt(Date.now()),
147
- salt: 0n,
148
- argumentSignatures: [],
149
- messageCount: 0,
150
- acknowledged: Buffer.alloc(3),
151
- checksum: 0,
152
- });
144
+ bot.chat(line.startsWith('/') ? line : `/${line}`);
153
145
  }
154
146
 
155
- /**
156
- * Says something in chat.
157
- *
158
- * Through `/say`, exactly as the Java runner does it. A bot cannot send a signed chat message
159
- * without a Mojang key, and an unsigned one is refused from 1.19 on, so the command is the only
160
- * route that reaches the same listeners.
161
- */
147
+ /** Sends player chat through the library's signing and acknowledgement implementation. */
162
148
  export function chat(bot, name, message) {
163
- command(bot, name, `say ${message}`);
149
+ requireInWorld(bot, name);
150
+ if (message.startsWith('/')) {
151
+ throw new Error('Use the command action for slash commands; chat sends player chat.');
152
+ }
153
+ bot.chat(message);
164
154
  }
165
155
 
166
156
  /** Right-clicks a block — which is how a container or a plugin menu gets opened. */
@@ -76,3 +76,39 @@ export function versionForProtocol(protocol) {
76
76
  .join(', ')}). It supports ${mc.supportedVersions.join(', ')}.`,
77
77
  );
78
78
  }
79
+
80
+ /**
81
+ * The protocol a Minecraft version speaks, from minecraft-data's table of every release it has
82
+ * heard of. That table is wider than the versions it carries data for, so a release the runner
83
+ * cannot speak yet still has a number here.
84
+ */
85
+ export function protocolForVersion(minecraftVersion) {
86
+ return minecraftData.versionsByMinecraftVersion?.pc?.[minecraftVersion]?.version;
87
+ }
88
+
89
+ /**
90
+ * Which protocol to speak: the one the agent says its server runs, over the one the ping answered.
91
+ *
92
+ * A ping is answered by whatever holds the port, and two common things answer it with the
93
+ * protocol the ping was written in rather than the server's own: a proxy that echoes it, and
94
+ * ViaVersion with ViaBackwards, which rewrites the status reply to the client's protocol whenever
95
+ * it can translate. The ping is written in PING_VERSION, so either one made the runner join as
96
+ * 1.21.1 — on a 26.x server, through a chain of translations, without a word. The agent runs inside
97
+ * the server, and nothing answers for it.
98
+ *
99
+ * Without a version from the agent the ping decides, as it always did. `pinged` is returned only
100
+ * when the two disagree, so the caller can say so.
101
+ */
102
+ export function selectProtocol(pinged, serverVersion) {
103
+ if (!serverVersion) {
104
+ return { protocol: pinged };
105
+ }
106
+ const declared = protocolForVersion(serverVersion);
107
+ if (declared === undefined) {
108
+ throw new Error(
109
+ `The agent reports Minecraft ${serverVersion}, which this runner's minecraft-data does not `
110
+ + 'know: the dependency is older than the server.',
111
+ );
112
+ }
113
+ return declared === pinged ? { protocol: declared } : { protocol: declared, pinged };
114
+ }