@mairwunnx/minecraft-mcp 2.1.0 → 2.2.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.RU.md CHANGED
@@ -100,7 +100,7 @@ MCP_HTTP_TOKEN=a-long-random-secret WORLDAPI_URL=... WORLDAPI_TOKEN=... \
100
100
 
101
101
  Права берутся из WorldAPI: `admin` включает `read`, `write:world` и `write:players`, но не `code`.
102
102
 
103
- **Чтение** (`read`, 27)
103
+ **Чтение** (`read`, 29)
104
104
 
105
105
  - `get_server_status`: версия, игроки, аптайм, TPS, память; `detail` добавляет перцентили тика, CPU и нагрузку по мирам
106
106
  - `list_plugins`, `get_events`, `wait_for_event`: плагины, записанные события и ожидание живого
@@ -110,6 +110,7 @@ MCP_HTTP_TOKEN=a-long-random-secret WORLDAPI_URL=... WORLDAPI_TOKEN=... \
110
110
  - `list_entities`, `get_entity`: мобы и предметы вокруг точки
111
111
  - `list_players`, `list_online_players`, `get_player`, `get_player_inventory`, `get_player_effects`, `get_player_statistics`, `get_player_advancements`, `get_player_sessions`, `check_permission`
112
112
  - `get_leaderboard`, `get_aggregates`, `get_recipes`: рейтинги, итоги сервера и как скрафтить что угодно
113
+ - `list_extensions`, `read_extension`: данные, которыми другие плагины делятся через WorldAPI, — какие бы плагины ни стояли на сервере; их события тоже приходят через `get_events` и `wait_for_event`
113
114
 
114
115
  **Мир** (`write:world`, 10)
115
116
 
@@ -189,9 +190,9 @@ npm run pack:mcpb # бандл для Claude Desktop в build/
189
190
 
190
191
  [WorldAPI](https://github.com/MairwunNx/minecraft-worldapi) — 🌍 плагин для Paper, который выводит сервер в HTTP; этот сервер работает через него.
191
192
 
192
- [MCBuddy Server](https://github.com/mcbuddy-ai/mcbuddy-server) — 🛠️⚡ бэкенд MCBuddy, ИИ-помощника для Minecraft на OpenRouter.
193
+ [Allay Server](https://github.com/allay-ai/server) — 🛠️⚡ бэкенд Allay, ИИ-помощника для Minecraft, на OpenRouter.
193
194
 
194
- [MCBuddy Spigot](https://github.com/mcbuddy-ai/mcbuddy-spigot) — 💬 приводит MCBuddy в игру: спросите ИИ о чём угодно через `/ask` прямо в чате. 🎮
195
+ [Allay](https://github.com/allay-ai/minecraft) — 🪽 приводит Allay в игру: спрашивайте ИИ о чём угодно командой `/ask` прямо в чате. 🎮
195
196
 
196
197
  ## От того же автора
197
198
 
package/README.md CHANGED
@@ -25,18 +25,6 @@ It talks to [WorldAPI](https://github.com/MairwunNx/minecraft-worldapi), a Paper
25
25
  - 📝 **Prompts** for common jobs: a server health check, a lag hunt, a grief investigation, a new player’s guide, and where a player is.
26
26
  - 🔌 **Every client.** Claude Code, Claude Desktop in one click, Cursor, and anything else that speaks MCP, over stdio or HTTP.
27
27
 
28
- ## Try it live
29
-
30
- Moonlands, my hard survival server with friends, runs WorldAPI and lets anyone read it. Point the agent there without a token and ask about a real server:
31
-
32
- ```sh
33
- claude mcp add moonlands -e WORLDAPI_URL=https://minecraft.mairwunnx.com/worldapi/api/v1 -- npx -y @mairwunnx/minecraft-mcp
34
- ```
35
-
36
- [![Play on Moonlands: pure vanilla, hard mobs, AI, claims. minecraft.mairwunnx.com](https://raw.githubusercontent.com/MairwunNx/minecraft-mcp/master/media/og-adv-image.png)](https://minecraft.mairwunnx.com)
37
-
38
- Join at `minecraft.mairwunnx.com`. The site has the live map, player stats, and the hall of fame.
39
-
40
28
  ## Install
41
29
 
42
30
  You need a server with [WorldAPI](https://github.com/MairwunNx/minecraft-worldapi) and a token from it: run `/worldapi token create agent` in game and pick the scopes. Clients that start the server themselves need Node.js 22 or newer.
@@ -100,7 +88,7 @@ Clients connect to `http://host:3333/mcp` and send `Authorization: Bearer <MCP_H
100
88
 
101
89
  Scopes come from WorldAPI: `admin` includes `read`, `write:world`, and `write:players`, but not `code`.
102
90
 
103
- **Read** (`read`, 27)
91
+ **Read** (`read`, 29)
104
92
 
105
93
  - `get_server_status`: version, players, uptime, TPS, memory; `detail` adds tick percentiles, CPU, and load per world
106
94
  - `list_plugins`, `get_events`, `wait_for_event`: plugins, recorded events, and waiting for a live one
@@ -110,6 +98,7 @@ Scopes come from WorldAPI: `admin` includes `read`, `write:world`, and `write:pl
110
98
  - `list_entities`, `get_entity`: mobs and items around a point
111
99
  - `list_players`, `list_online_players`, `get_player`, `get_player_inventory`, `get_player_effects`, `get_player_statistics`, `get_player_advancements`, `get_player_sessions`, `check_permission`
112
100
  - `get_leaderboard`, `get_aggregates`, `get_recipes`: rankings, server totals, and how to craft anything
101
+ - `list_extensions`, `read_extension`: data other plugins share through WorldAPI, whatever the server runs; their events come through `get_events` and `wait_for_event` too
113
102
 
114
103
  **World** (`write:world`, 10)
115
104
 
@@ -189,9 +178,9 @@ AI wrote most of the code and the documentation from my task descriptions, and I
189
178
 
190
179
  [WorldAPI](https://github.com/MairwunNx/minecraft-worldapi)—🌍 the Paper plugin that puts your server on HTTP; this server talks to it.
191
180
 
192
- [MCBuddy Server](https://github.com/mcbuddy-ai/mcbuddy-server)—🛠️⚡ the backend for MCBuddy, an AI assistant for Minecraft, built on OpenRouter.
181
+ [Allay Server](https://github.com/allay-ai/server)—🛠️⚡ the backend for Allay, an AI helper for Minecraft, built on OpenRouter.
193
182
 
194
- [MCBuddy Spigot](https://github.com/mcbuddy-ai/mcbuddy-spigot)—💬 brings MCBuddy into the game: ask the AI anything with `/ask`, right in the chat. 🎮
183
+ [Allay](https://github.com/allay-ai/minecraft)—🪽 brings Allay into the game: ask the AI anything with `/ask`, right in the chat. 🎮
195
184
 
196
185
  ## From the same author
197
186
 
package/dist/server.js CHANGED
@@ -4,6 +4,7 @@ import { registerPrompts } from "./prompts.js";
4
4
  import { registerResources } from "./resources.js";
5
5
  import { blockTools } from "./tools/blocks.js";
6
6
  import { entityTools } from "./tools/entities.js";
7
+ import { extensionTools } from "./tools/extensions.js";
7
8
  import { playerTools } from "./tools/players.js";
8
9
  import { scriptTools } from "./tools/scripts.js";
9
10
  import { serverTools } from "./tools/server.js";
@@ -41,6 +42,7 @@ export function buildServer(config, scopes, feed, fetchImpl) {
41
42
  playerTools(ctx);
42
43
  statsTools(ctx);
43
44
  scriptTools(ctx);
45
+ extensionTools(ctx);
44
46
  registerResources(server, ctx.api, config.token, feed);
45
47
  registerPrompts(server);
46
48
  return { server, tools: ctx.registered };
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { unwrap } from "../client.js";
3
+ import { tool } from "../tool.js";
4
+ export function extensionTools(ctx) {
5
+ const { api, token } = ctx;
6
+ tool(ctx, {
7
+ name: "list_extensions",
8
+ title: "Plugins’ data",
9
+ description: "Other plugins on the server that share their own data through WorldAPI: each one’s namespace, its resources (paths to read with `read_extension`), and its event topics (`ext.<namespace>.<event>`, which `get_events` and `wait_for_event` take like any type). What’s here depends on the server; call this first instead of guessing.",
10
+ scope: "read",
11
+ effect: "read",
12
+ untrusted: true,
13
+ input: {},
14
+ run: () => unwrap(api.GET("/extensions"), token),
15
+ });
16
+ tool(ctx, {
17
+ name: "read_extension",
18
+ title: "Read a plugin’s data",
19
+ description: 'One resource of a plugin from `list_extensions`, read from the plugin as it is now. Fill the path’s `{parameters}`, such as `players/Steve` for `players/{player}`; `query` passes the resource’s own options, such as `{ "limit": "5" }`.',
20
+ scope: "read",
21
+ effect: "read",
22
+ untrusted: true,
23
+ input: {
24
+ namespace: z
25
+ .string()
26
+ .regex(/^[a-z0-9_-]{1,32}$/)
27
+ .describe("The namespace, such as one `list_extensions` shows"),
28
+ path: z.string().min(1).describe("The resource path with its parameters filled in"),
29
+ query: z.record(z.string(), z.string()).optional(),
30
+ },
31
+ run: (args) => {
32
+ // Each segment is encoded on its own, so the path's slashes stay slashes.
33
+ const segments = args.path.split("/").filter(Boolean).map(encodeURIComponent).join("/");
34
+ const url = `/extensions/${args.namespace}/${segments}`;
35
+ return unwrap(api.GET(url, { params: { query: args.query } }), token);
36
+ },
37
+ });
38
+ }
39
+ //# sourceMappingURL=extensions.js.map
@@ -33,7 +33,7 @@ export function serverTools(ctx) {
33
33
  name: "get_events",
34
34
  untrusted: true,
35
35
  title: "Event history",
36
- description: "Recorded events, newest first: player.join, player.leave, player.kick, player.death, player.advancement, player.chat (if the server records chat), world.night_skipped, entity.boss_killed, raid.start, raid.finish, server.status. Filter by type, player, and time.",
36
+ description: "Recorded events, newest first: player.join, player.leave, player.kick, player.death, player.advancement, player.chat (if the server records chat), world.night_skipped, entity.boss_killed, raid.start, raid.finish, server.status, and plugins’ own `ext.<namespace>.<event>` types from `list_extensions`. Filter by type, player, and time.",
37
37
  scope: "read",
38
38
  effect: "read",
39
39
  input: {
@@ -7,7 +7,7 @@ export function statsTools(ctx) {
7
7
  tool(ctx, {
8
8
  name: "get_leaderboard",
9
9
  title: "Leaderboards",
10
- description: "Players ranked by a statistic; ties share a place. Presets: playtime (hours), diamonds, advancements, mobs, travel (km), deaths, blocks, fish, damage (hearts), netherite (ancient debris mined), experience (xp gained; a lower bound, since vanilla keeps no lifetime total). Or any statistic as `category:key`, such as `custom:jump`, `mined:diamond_ore`, `killed:*`. Without a statistic, lists the presets and their units.",
10
+ description: "Players ranked by a statistic; ties share a place. Presets: playtime (hours), diamonds, advancements, mobs, travel (km), deaths, blocks, fish, damage (hearts), netherite (ancient debris mined), experience (xp gained; a lower bound, since vanilla keeps no lifetime total), tamed (animals tamed, WorldAPI 2.4 and newer; also a lower bound). Or any statistic as `category:key`, such as `custom:jump`, `mined:diamond_ore`, `killed:*`. Without a statistic, lists the presets and their units.",
11
11
  scope: "read",
12
12
  effect: "read",
13
13
  input: {
package/dist/version.js CHANGED
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
2
2
  /** This package's version, read from package.json so a release changes it in one place. */
3
3
  export const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
4
4
  /** The oldest WorldAPI whose endpoints match the generated client. */
5
- export const MIN_WORLDAPI = "2.0.0";
5
+ export const MIN_WORLDAPI = "2.3.0";
6
6
  /** Compares two x.y.z versions; anything that isn't one (a dev build) counts as new enough. */
7
7
  export function olderThan(version, minimum) {
8
8
  const parse = (text) => /^(\d+)\.(\d+)\.(\d+)/.exec(text)?.slice(1).map(Number);
@@ -49,6 +49,9 @@
49
49
  },
50
50
  {
51
51
  "name": "Scripts"
52
+ },
53
+ {
54
+ "name": "Extensions"
52
55
  }
53
56
  ],
54
57
  "paths": {
@@ -271,6 +274,187 @@
271
274
  }
272
275
  }
273
276
  },
277
+ "/extensions": {
278
+ "get": {
279
+ "operationId": "getExtensions",
280
+ "summary": "Other plugins’ data: their namespaces, resources, and event topics",
281
+ "description": "Plugins register these through the extension bridge (see the reference). Anonymous clients see only what the plugin marks public.\n\nScope: `read`.",
282
+ "tags": [
283
+ "Extensions"
284
+ ],
285
+ "x-scope": "read",
286
+ "security": [
287
+ {
288
+ "bearer": [
289
+ "read"
290
+ ]
291
+ }
292
+ ],
293
+ "responses": {
294
+ "200": {
295
+ "description": "OK",
296
+ "content": {
297
+ "application/json": {
298
+ "schema": {
299
+ "type": "array",
300
+ "items": {
301
+ "$ref": "#/components/schemas/ExtensionInfo"
302
+ }
303
+ }
304
+ }
305
+ }
306
+ },
307
+ "401": {
308
+ "description": "Missing or unknown token",
309
+ "content": {
310
+ "application/json": {
311
+ "schema": {
312
+ "$ref": "#/components/schemas/ErrorBody"
313
+ }
314
+ }
315
+ }
316
+ },
317
+ "403": {
318
+ "description": "The token lacks the scope",
319
+ "content": {
320
+ "application/json": {
321
+ "schema": {
322
+ "$ref": "#/components/schemas/ErrorBody"
323
+ }
324
+ }
325
+ }
326
+ },
327
+ "429": {
328
+ "description": "Rate limit hit; see Retry-After",
329
+ "content": {
330
+ "application/json": {
331
+ "schema": {
332
+ "$ref": "#/components/schemas/ErrorBody"
333
+ }
334
+ }
335
+ }
336
+ },
337
+ "503": {
338
+ "description": "The server thread didn't answer in time",
339
+ "content": {
340
+ "application/json": {
341
+ "schema": {
342
+ "$ref": "#/components/schemas/ErrorBody"
343
+ }
344
+ }
345
+ }
346
+ }
347
+ }
348
+ }
349
+ },
350
+ "/extensions/{namespace}/{path}": {
351
+ "get": {
352
+ "operationId": "getExtensionsNamespacePath",
353
+ "summary": "One resource of a plugin, read from the plugin as it is now",
354
+ "description": "Query parameters pass through to the plugin; each resource documents its own.\n\nScope: `read`.",
355
+ "tags": [
356
+ "Extensions"
357
+ ],
358
+ "x-scope": "read",
359
+ "security": [
360
+ {
361
+ "bearer": [
362
+ "read"
363
+ ]
364
+ }
365
+ ],
366
+ "parameters": [
367
+ {
368
+ "name": "namespace",
369
+ "in": "path",
370
+ "required": true,
371
+ "description": "namespace",
372
+ "schema": {
373
+ "type": "string"
374
+ }
375
+ },
376
+ {
377
+ "name": "path",
378
+ "in": "path",
379
+ "required": true,
380
+ "description": "path",
381
+ "schema": {
382
+ "type": "string"
383
+ }
384
+ }
385
+ ],
386
+ "responses": {
387
+ "200": {
388
+ "description": "OK",
389
+ "content": {
390
+ "application/json": {
391
+ "schema": {}
392
+ }
393
+ }
394
+ },
395
+ "400": {
396
+ "description": "Invalid request",
397
+ "content": {
398
+ "application/json": {
399
+ "schema": {
400
+ "$ref": "#/components/schemas/ErrorBody"
401
+ }
402
+ }
403
+ }
404
+ },
405
+ "401": {
406
+ "description": "Missing or unknown token",
407
+ "content": {
408
+ "application/json": {
409
+ "schema": {
410
+ "$ref": "#/components/schemas/ErrorBody"
411
+ }
412
+ }
413
+ }
414
+ },
415
+ "403": {
416
+ "description": "The token lacks the scope",
417
+ "content": {
418
+ "application/json": {
419
+ "schema": {
420
+ "$ref": "#/components/schemas/ErrorBody"
421
+ }
422
+ }
423
+ }
424
+ },
425
+ "404": {
426
+ "description": "Not found",
427
+ "content": {
428
+ "application/json": {
429
+ "schema": {
430
+ "$ref": "#/components/schemas/ErrorBody"
431
+ }
432
+ }
433
+ }
434
+ },
435
+ "429": {
436
+ "description": "Rate limit hit; see Retry-After",
437
+ "content": {
438
+ "application/json": {
439
+ "schema": {
440
+ "$ref": "#/components/schemas/ErrorBody"
441
+ }
442
+ }
443
+ }
444
+ },
445
+ "503": {
446
+ "description": "The server thread didn't answer in time",
447
+ "content": {
448
+ "application/json": {
449
+ "schema": {
450
+ "$ref": "#/components/schemas/ErrorBody"
451
+ }
452
+ }
453
+ }
454
+ }
455
+ }
456
+ }
457
+ },
274
458
  "/leaderboards": {
275
459
  "get": {
276
460
  "operationId": "getLeaderboards",
@@ -348,7 +532,7 @@
348
532
  "get": {
349
533
  "operationId": "getLeaderboardsStatistic",
350
534
  "summary": "Players ranked by a statistic, ties sharing a place",
351
- "description": "Online players count live. Use a preset or any statistic as `category:key`, such as `custom:jump`, `mined:diamond_ore`, or `killed:*`. The `experience` preset starts each player from the game's `XpTotal` (experience since their last death, so a lower bound) and adds every pickup after that, since vanilla keeps no lifetime statistic.\n\nScope: `read`.",
535
+ "description": "Online players count live. Use a preset or any statistic as `category:key`, such as `custom:jump`, `mined:diamond_ore`, or `killed:*`. The `experience` preset starts each player from the game's `XpTotal` (experience since their last death, so a lower bound) and adds every pickup after that, since vanilla keeps no lifetime statistic. The `tamed` preset starts from the pets each player owns in the world, counted once, and adds every taming after that.\n\nScope: `read`.",
352
536
  "tags": [
353
537
  "Leaderboards"
354
538
  ],
@@ -10186,6 +10370,88 @@
10186
10370
  ],
10187
10371
  "additionalProperties": false
10188
10372
  },
10373
+ "ExtensionInfo": {
10374
+ "type": "object",
10375
+ "properties": {
10376
+ "namespace": {
10377
+ "type": "string"
10378
+ },
10379
+ "plugin": {
10380
+ "type": "string"
10381
+ },
10382
+ "version": {
10383
+ "type": "string"
10384
+ },
10385
+ "description": {
10386
+ "type": "string"
10387
+ },
10388
+ "resources": {
10389
+ "type": "array",
10390
+ "items": {
10391
+ "$ref": "#/components/schemas/ExtensionResourceInfo"
10392
+ }
10393
+ },
10394
+ "topics": {
10395
+ "type": "array",
10396
+ "items": {
10397
+ "$ref": "#/components/schemas/ExtensionTopicInfo"
10398
+ }
10399
+ }
10400
+ },
10401
+ "required": [
10402
+ "namespace",
10403
+ "plugin",
10404
+ "version",
10405
+ "description",
10406
+ "resources",
10407
+ "topics"
10408
+ ],
10409
+ "additionalProperties": false
10410
+ },
10411
+ "ExtensionResourceInfo": {
10412
+ "type": "object",
10413
+ "properties": {
10414
+ "path": {
10415
+ "type": "string"
10416
+ },
10417
+ "description": {
10418
+ "type": "string"
10419
+ },
10420
+ "public": {
10421
+ "type": "boolean"
10422
+ }
10423
+ },
10424
+ "required": [
10425
+ "path",
10426
+ "description",
10427
+ "public"
10428
+ ],
10429
+ "additionalProperties": false
10430
+ },
10431
+ "ExtensionTopicInfo": {
10432
+ "type": "object",
10433
+ "properties": {
10434
+ "topic": {
10435
+ "type": "string"
10436
+ },
10437
+ "description": {
10438
+ "type": "string"
10439
+ },
10440
+ "public": {
10441
+ "type": "boolean"
10442
+ },
10443
+ "recorded": {
10444
+ "type": "boolean"
10445
+ }
10446
+ },
10447
+ "required": [
10448
+ "topic",
10449
+ "description",
10450
+ "public",
10451
+ "recorded"
10452
+ ],
10453
+ "additionalProperties": false
10454
+ },
10189
10455
  "FillRequest": {
10190
10456
  "type": "object",
10191
10457
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mairwunnx/minecraft-mcp",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "mcpName": "com.mairwunnx/minecraft-mcp",
5
5
  "description": "MCP server that lets AI agents read and manage a Minecraft server through WorldAPI",
6
6
  "keywords": [