@mairwunnx/minecraft-mcp 2.1.1 → 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
@@ -88,7 +88,7 @@ Clients connect to `http://host:3333/mcp` and send `Authorization: Bearer <MCP_H
88
88
 
89
89
  Scopes come from WorldAPI: `admin` includes `read`, `write:world`, and `write:players`, but not `code`.
90
90
 
91
- **Read** (`read`, 27)
91
+ **Read** (`read`, 29)
92
92
 
93
93
  - `get_server_status`: version, players, uptime, TPS, memory; `detail` adds tick percentiles, CPU, and load per world
94
94
  - `list_plugins`, `get_events`, `wait_for_event`: plugins, recorded events, and waiting for a live one
@@ -98,6 +98,7 @@ Scopes come from WorldAPI: `admin` includes `read`, `write:world`, and `write:pl
98
98
  - `list_entities`, `get_entity`: mobs and items around a point
99
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`
100
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
101
102
 
102
103
  **World** (`write:world`, 10)
103
104
 
@@ -177,9 +178,9 @@ AI wrote most of the code and the documentation from my task descriptions, and I
177
178
 
178
179
  [WorldAPI](https://github.com/MairwunNx/minecraft-worldapi)—🌍 the Paper plugin that puts your server on HTTP; this server talks to it.
179
180
 
180
- [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.
181
182
 
182
- [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. 🎮
183
184
 
184
185
  ## From the same author
185
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.1",
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": [