minecraft-bedrock-mcp-server 0.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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +577 -0
  3. package/dist/bridge/event-route.d.ts +11 -0
  4. package/dist/bridge/event-route.d.ts.map +1 -0
  5. package/dist/bridge/event-route.js +28 -0
  6. package/dist/bridge/event-route.js.map +1 -0
  7. package/dist/bridge/handshake-route.d.ts +11 -0
  8. package/dist/bridge/handshake-route.d.ts.map +1 -0
  9. package/dist/bridge/handshake-route.js +45 -0
  10. package/dist/bridge/handshake-route.js.map +1 -0
  11. package/dist/bridge/poll-route.d.ts +11 -0
  12. package/dist/bridge/poll-route.d.ts.map +1 -0
  13. package/dist/bridge/poll-route.js +28 -0
  14. package/dist/bridge/poll-route.js.map +1 -0
  15. package/dist/bridge/result-route.d.ts +10 -0
  16. package/dist/bridge/result-route.d.ts.map +1 -0
  17. package/dist/bridge/result-route.js +23 -0
  18. package/dist/bridge/result-route.js.map +1 -0
  19. package/dist/config/config-error.d.ts +12 -0
  20. package/dist/config/config-error.d.ts.map +1 -0
  21. package/dist/config/config-error.js +13 -0
  22. package/dist/config/config-error.js.map +1 -0
  23. package/dist/config/environment.d.ts +89 -0
  24. package/dist/config/environment.d.ts.map +1 -0
  25. package/dist/config/environment.js +73 -0
  26. package/dist/config/environment.js.map +1 -0
  27. package/dist/config/tls.d.ts +15 -0
  28. package/dist/config/tls.d.ts.map +1 -0
  29. package/dist/config/tls.js +27 -0
  30. package/dist/config/tls.js.map +1 -0
  31. package/dist/errors/bridge-error.d.ts +41 -0
  32. package/dist/errors/bridge-error.d.ts.map +1 -0
  33. package/dist/errors/bridge-error.js +33 -0
  34. package/dist/errors/bridge-error.js.map +1 -0
  35. package/dist/errors/error-codes.d.ts +13 -0
  36. package/dist/errors/error-codes.d.ts.map +1 -0
  37. package/dist/errors/error-codes.js +41 -0
  38. package/dist/errors/error-codes.js.map +1 -0
  39. package/dist/events/subscription-registry.d.ts +55 -0
  40. package/dist/events/subscription-registry.d.ts.map +1 -0
  41. package/dist/events/subscription-registry.js +64 -0
  42. package/dist/events/subscription-registry.js.map +1 -0
  43. package/dist/http/app.d.ts +26 -0
  44. package/dist/http/app.d.ts.map +1 -0
  45. package/dist/http/app.js +88 -0
  46. package/dist/http/app.js.map +1 -0
  47. package/dist/http/authentication.d.ts +11 -0
  48. package/dist/http/authentication.d.ts.map +1 -0
  49. package/dist/http/authentication.js +36 -0
  50. package/dist/http/authentication.js.map +1 -0
  51. package/dist/http/health-route.d.ts +10 -0
  52. package/dist/http/health-route.d.ts.map +1 -0
  53. package/dist/http/health-route.js +18 -0
  54. package/dist/http/health-route.js.map +1 -0
  55. package/dist/http/metrics-route.d.ts +8 -0
  56. package/dist/http/metrics-route.d.ts.map +1 -0
  57. package/dist/http/metrics-route.js +11 -0
  58. package/dist/http/metrics-route.js.map +1 -0
  59. package/dist/index.d.ts +3 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +134 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/mcp/mcp-route.d.ts +12 -0
  64. package/dist/mcp/mcp-route.d.ts.map +1 -0
  65. package/dist/mcp/mcp-route.js +49 -0
  66. package/dist/mcp/mcp-route.js.map +1 -0
  67. package/dist/mcp/mcp-server-factory.d.ts +20 -0
  68. package/dist/mcp/mcp-server-factory.d.ts.map +1 -0
  69. package/dist/mcp/mcp-server-factory.js +23 -0
  70. package/dist/mcp/mcp-server-factory.js.map +1 -0
  71. package/dist/mcp/session-manager.d.ts +30 -0
  72. package/dist/mcp/session-manager.d.ts.map +1 -0
  73. package/dist/mcp/session-manager.js +38 -0
  74. package/dist/mcp/session-manager.js.map +1 -0
  75. package/dist/mcp/tool-registry.d.ts +15 -0
  76. package/dist/mcp/tool-registry.d.ts.map +1 -0
  77. package/dist/mcp/tool-registry.js +27 -0
  78. package/dist/mcp/tool-registry.js.map +1 -0
  79. package/dist/observability/logger.d.ts +16 -0
  80. package/dist/observability/logger.d.ts.map +1 -0
  81. package/dist/observability/logger.js +19 -0
  82. package/dist/observability/logger.js.map +1 -0
  83. package/dist/observability/metrics.d.ts +27 -0
  84. package/dist/observability/metrics.d.ts.map +1 -0
  85. package/dist/observability/metrics.js +72 -0
  86. package/dist/observability/metrics.js.map +1 -0
  87. package/dist/protocol/command.d.ts +92 -0
  88. package/dist/protocol/command.d.ts.map +1 -0
  89. package/dist/protocol/command.js +37 -0
  90. package/dist/protocol/command.js.map +1 -0
  91. package/dist/protocol/event.d.ts +82 -0
  92. package/dist/protocol/event.d.ts.map +1 -0
  93. package/dist/protocol/event.js +37 -0
  94. package/dist/protocol/event.js.map +1 -0
  95. package/dist/protocol/handshake.d.ts +150 -0
  96. package/dist/protocol/handshake.d.ts.map +1 -0
  97. package/dist/protocol/handshake.js +65 -0
  98. package/dist/protocol/handshake.js.map +1 -0
  99. package/dist/protocol/index.d.ts +14 -0
  100. package/dist/protocol/index.d.ts.map +1 -0
  101. package/dist/protocol/index.js +14 -0
  102. package/dist/protocol/index.js.map +1 -0
  103. package/dist/protocol/protocol-version.d.ts +18 -0
  104. package/dist/protocol/protocol-version.d.ts.map +1 -0
  105. package/dist/protocol/protocol-version.js +28 -0
  106. package/dist/protocol/protocol-version.js.map +1 -0
  107. package/dist/protocol/result.d.ts +79 -0
  108. package/dist/protocol/result.d.ts.map +1 -0
  109. package/dist/protocol/result.js +35 -0
  110. package/dist/protocol/result.js.map +1 -0
  111. package/dist/queue/command-id.d.ts +15 -0
  112. package/dist/queue/command-id.d.ts.map +1 -0
  113. package/dist/queue/command-id.js +38 -0
  114. package/dist/queue/command-id.js.map +1 -0
  115. package/dist/queue/command-queue.d.ts +90 -0
  116. package/dist/queue/command-queue.d.ts.map +1 -0
  117. package/dist/queue/command-queue.js +151 -0
  118. package/dist/queue/command-queue.js.map +1 -0
  119. package/dist/queue/command-throttle.d.ts +38 -0
  120. package/dist/queue/command-throttle.d.ts.map +1 -0
  121. package/dist/queue/command-throttle.js +26 -0
  122. package/dist/queue/command-throttle.js.map +1 -0
  123. package/dist/queue/instrumented-command-queue.d.ts +9 -0
  124. package/dist/queue/instrumented-command-queue.d.ts.map +1 -0
  125. package/dist/queue/instrumented-command-queue.js +39 -0
  126. package/dist/queue/instrumented-command-queue.js.map +1 -0
  127. package/dist/queue/pending-commands.d.ts +35 -0
  128. package/dist/queue/pending-commands.d.ts.map +1 -0
  129. package/dist/queue/pending-commands.js +41 -0
  130. package/dist/queue/pending-commands.js.map +1 -0
  131. package/dist/server-info.d.ts +5 -0
  132. package/dist/server-info.d.ts.map +1 -0
  133. package/dist/server-info.js +22 -0
  134. package/dist/server-info.js.map +1 -0
  135. package/dist/structures/structure-file-store.d.ts +22 -0
  136. package/dist/structures/structure-file-store.d.ts.map +1 -0
  137. package/dist/structures/structure-file-store.js +89 -0
  138. package/dist/structures/structure-file-store.js.map +1 -0
  139. package/dist/tools/block-tools.d.ts +4 -0
  140. package/dist/tools/block-tools.d.ts.map +1 -0
  141. package/dist/tools/block-tools.js +111 -0
  142. package/dist/tools/block-tools.js.map +1 -0
  143. package/dist/tools/command-tools.d.ts +4 -0
  144. package/dist/tools/command-tools.d.ts.map +1 -0
  145. package/dist/tools/command-tools.js +23 -0
  146. package/dist/tools/command-tools.js.map +1 -0
  147. package/dist/tools/common-schemas.d.ts +65 -0
  148. package/dist/tools/common-schemas.d.ts.map +1 -0
  149. package/dist/tools/common-schemas.js +32 -0
  150. package/dist/tools/common-schemas.js.map +1 -0
  151. package/dist/tools/effect-tools.d.ts +4 -0
  152. package/dist/tools/effect-tools.d.ts.map +1 -0
  153. package/dist/tools/effect-tools.js +32 -0
  154. package/dist/tools/effect-tools.js.map +1 -0
  155. package/dist/tools/entity-tools.d.ts +4 -0
  156. package/dist/tools/entity-tools.d.ts.map +1 -0
  157. package/dist/tools/entity-tools.js +133 -0
  158. package/dist/tools/entity-tools.js.map +1 -0
  159. package/dist/tools/event-tools.d.ts +4 -0
  160. package/dist/tools/event-tools.d.ts.map +1 -0
  161. package/dist/tools/event-tools.js +84 -0
  162. package/dist/tools/event-tools.js.map +1 -0
  163. package/dist/tools/index.d.ts +4 -0
  164. package/dist/tools/index.d.ts.map +1 -0
  165. package/dist/tools/index.js +30 -0
  166. package/dist/tools/index.js.map +1 -0
  167. package/dist/tools/inventory-tools.d.ts +4 -0
  168. package/dist/tools/inventory-tools.d.ts.map +1 -0
  169. package/dist/tools/inventory-tools.js +52 -0
  170. package/dist/tools/inventory-tools.js.map +1 -0
  171. package/dist/tools/player-tools.d.ts +4 -0
  172. package/dist/tools/player-tools.d.ts.map +1 -0
  173. package/dist/tools/player-tools.js +117 -0
  174. package/dist/tools/player-tools.js.map +1 -0
  175. package/dist/tools/property-tools.d.ts +4 -0
  176. package/dist/tools/property-tools.d.ts.map +1 -0
  177. package/dist/tools/property-tools.js +45 -0
  178. package/dist/tools/property-tools.js.map +1 -0
  179. package/dist/tools/scoreboard-tools.d.ts +4 -0
  180. package/dist/tools/scoreboard-tools.d.ts.map +1 -0
  181. package/dist/tools/scoreboard-tools.js +68 -0
  182. package/dist/tools/scoreboard-tools.js.map +1 -0
  183. package/dist/tools/server-tools.d.ts +4 -0
  184. package/dist/tools/server-tools.d.ts.map +1 -0
  185. package/dist/tools/server-tools.js +25 -0
  186. package/dist/tools/server-tools.js.map +1 -0
  187. package/dist/tools/structure-file-tools.d.ts +4 -0
  188. package/dist/tools/structure-file-tools.d.ts.map +1 -0
  189. package/dist/tools/structure-file-tools.js +57 -0
  190. package/dist/tools/structure-file-tools.js.map +1 -0
  191. package/dist/tools/structure-tools.d.ts +4 -0
  192. package/dist/tools/structure-tools.d.ts.map +1 -0
  193. package/dist/tools/structure-tools.js +91 -0
  194. package/dist/tools/structure-tools.js.map +1 -0
  195. package/dist/tools/tool-definition.d.ts +68 -0
  196. package/dist/tools/tool-definition.d.ts.map +1 -0
  197. package/dist/tools/tool-definition.js +40 -0
  198. package/dist/tools/tool-definition.js.map +1 -0
  199. package/dist/tools/tool-result.d.ts +17 -0
  200. package/dist/tools/tool-result.d.ts.map +1 -0
  201. package/dist/tools/tool-result.js +52 -0
  202. package/dist/tools/tool-result.js.map +1 -0
  203. package/dist/tools/world-tools.d.ts +4 -0
  204. package/dist/tools/world-tools.d.ts.map +1 -0
  205. package/dist/tools/world-tools.js +97 -0
  206. package/dist/tools/world-tools.js.map +1 -0
  207. package/package.json +69 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 John Chapman (@amzn)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,577 @@
1
+ # Minecraft Bedrock MCP Server
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that bridges MCP
4
+ clients — Claude and other AI agents — to a **Minecraft Bedrock Dedicated Server** (BDS). It
5
+ exposes the Bedrock Script API as MCP tools, so an agent can read and manipulate a live world
6
+ programmatically.
7
+
8
+ ## ⚠️ Built on an experimental API
9
+
10
+ This project depends on Mojang's Bedrock **Script API**, including the **beta** modules
11
+ `@minecraft/server-net` and `@minecraft/server-admin`. The Script API is an evolving surface that
12
+ Mojang revises between Minecraft versions, and **a Bedrock update can change, deprecate, or remove
13
+ APIs this project depends on** — beta modules can be discontinued outright with no stable
14
+ replacement. Pin your BDS to a known-good version, do not auto-update it, and keep the BDS, the
15
+ behavior pack, and this server upgraded together. Treat the whole stack as experimental.
16
+
17
+ ## The two repositories
18
+
19
+ This system is split across two repositories:
20
+
21
+ | Repository | Role |
22
+ | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
23
+ | **`minecraft-bedrock-mcp-server`** (this repo) | The MCP server. Speaks MCP to clients and bridges commands to the world. |
24
+ | **[`minecraft-bedrock-mcp-behavior-pack`](https://github.com/chapmanjw/minecraft-bedrock-mcp-behavior-pack)** | The BDS behavior pack. Runs inside the world and executes commands via the Script API. |
25
+
26
+ This README is the **end-to-end guide** — it covers standing up the whole stack. The behavior
27
+ pack repository documents the pack itself in more depth.
28
+
29
+ ## How it works
30
+
31
+ The server runs on the same host as BDS and exposes two authenticated HTTP surfaces:
32
+
33
+ ```
34
+ MCP clients (Claude Desktop, Cursor, ...)
35
+ | MCP over Streamable HTTP + bearer token
36
+ minecraft-bedrock-mcp-server <-- this repository
37
+ | HTTP long-poll + bearer token
38
+ Bedrock Bridge behavior pack <-- separate repository
39
+ | @minecraft/server Script API
40
+ the Minecraft world
41
+ ```
42
+
43
+ - The **`/mcp`** surface speaks the MCP Streamable HTTP transport to clients.
44
+ - The **`/bridge`** surface is long-polled by the companion behavior pack running inside the world.
45
+
46
+ An MCP tool call enqueues a command; the behavior pack polls for it, executes it through the
47
+ Script API, and posts the result back. The server correlates that result to the waiting tool call
48
+ and returns it to the client.
49
+
50
+ ---
51
+
52
+ # End-to-end setup tutorial
53
+
54
+ This walkthrough takes you from nothing to an AI agent building in your world. It has eight steps:
55
+
56
+ 1. [Set up a Bedrock Dedicated Server](#step-1--set-up-a-bedrock-dedicated-server)
57
+ 2. [Create a compatible world](#step-2--create-a-compatible-world)
58
+ 3. [Transfer the world to the server](#step-3--transfer-the-world-to-the-server)
59
+ 4. [Install the behavior pack](#step-4--install-the-behavior-pack)
60
+ 5. [Install and configure the MCP server](#step-5--install-and-configure-the-mcp-server)
61
+ 6. [Configure the behavior pack](#step-6--configure-the-behavior-pack)
62
+ 7. [Start everything](#step-7--start-everything)
63
+ 8. [Connect Claude Desktop](#step-8--connect-claude-desktop)
64
+
65
+ > **Assumption:** this tutorial assumes the Bedrock Dedicated Server and the MCP server run on the
66
+ > **same Ubuntu (Linux) host**. The commands below are written for Ubuntu. BDS also ships for
67
+ > Windows; if you run it there, adapt the paths and shell commands accordingly — the configuration
68
+ > values are identical.
69
+
70
+ ## Prerequisites
71
+
72
+ - An Ubuntu host (a VM, a spare machine, or a cloud instance) for the server.
73
+ - Node.js 20 or newer on that host.
74
+ - A copy of **Minecraft: Bedrock Edition** on a PC or device — used once to create the world.
75
+ - About 20 minutes.
76
+
77
+ ## Step 1 — Set up a Bedrock Dedicated Server
78
+
79
+ 1. Download the Linux Bedrock Dedicated Server from the official page:
80
+ <https://www.minecraft.net/en-us/download/server/bedrock>. Accept the EULA and privacy policy
81
+ prompts on that page to reveal the download link.
82
+
83
+ 2. Unzip it on the Ubuntu host:
84
+
85
+ ```sh
86
+ sudo mkdir -p /opt/bedrock-server
87
+ sudo unzip bedrock-server-*.zip -d /opt/bedrock-server
88
+ sudo chown -R "$USER" /opt/bedrock-server
89
+ cd /opt/bedrock-server
90
+ ```
91
+
92
+ 3. Do a first test run so BDS generates its default files, then stop it with `Ctrl+C`:
93
+
94
+ ```sh
95
+ LD_LIBRARY_PATH=. ./bedrock_server
96
+ ```
97
+
98
+ If it complains about a missing library, install the basics:
99
+ `sudo apt-get update && sudo apt-get install -y libcurl4 unzip`.
100
+
101
+ 4. Edit `server.properties`. The settings that matter for this project:
102
+
103
+ | Setting | Value | Why |
104
+ | -------------- | ------------------------ | ------------------------------------------------------- |
105
+ | `level-name` | _your world folder name_ | Selects which world under `worlds/` to load. |
106
+ | `allow-cheats` | `true` | Required for `mc_run_command` and command-backed tools. |
107
+ | `gamemode` | `creative` | Recommended so agents can place and break freely. |
108
+
109
+ Leave the rest at their defaults for now. You will set `level-name` in Step 3.
110
+
111
+ Step 7 covers running BDS as a `systemd` service. For more detail (firewall rules, backups),
112
+ search "Bedrock Dedicated Server" in the Minecraft help center at <https://help.minecraft.net>.
113
+
114
+ ## Step 2 — Create a compatible world
115
+
116
+ The behavior pack needs a world created with the **Beta APIs** experiment enabled. That toggle can
117
+ only be set when the world is created **in the Minecraft client** — the dedicated server cannot
118
+ turn it on. So create the world on your PC/device first, then move it to the server.
119
+
120
+ In Minecraft: Bedrock Edition, choose **Create New World** and set:
121
+
122
+ | Setting | Value | Why |
123
+ | ---------------------------- | -------- | ------------------------------------------------------------------ |
124
+ | **Game Mode** | Creative | Agents place/break blocks and spawn entities freely. |
125
+ | **Cheats** (Activate Cheats) | On | Enables commands; pairs with `allow-cheats` on the server. |
126
+ | **Experiments → Beta APIs** | On | **Required.** `@minecraft/server-net` / `-admin` are beta modules. |
127
+
128
+ Then **play the world once** for a few seconds so it is fully written to disk, and exit.
129
+
130
+ ## Step 3 — Transfer the world to the server
131
+
132
+ 1. In Minecraft, on the worlds list, open the world's settings (the pencil icon) and choose
133
+ **Export World**. This produces a `.mcworld` file — which is just a ZIP archive.
134
+
135
+ 2. Copy that file to the Ubuntu host (e.g. with `scp`) and unzip it into a folder under `worlds/`:
136
+
137
+ ```sh
138
+ cd /opt/bedrock-server/worlds
139
+ mkdir "mcp-world"
140
+ unzip ~/exported-world.mcworld -d "mcp-world"
141
+ ```
142
+
143
+ 3. Set `level-name=mcp-world` in `/opt/bedrock-server/server.properties` so BDS loads it.
144
+
145
+ The world's absolute path is now `/opt/bedrock-server/worlds/mcp-world` — you will need it shortly.
146
+
147
+ ## Step 4 — Install the behavior pack
148
+
149
+ 1. Get the pack from the
150
+ [behavior pack repository](https://github.com/chapmanjw/minecraft-bedrock-mcp-behavior-pack) —
151
+ download `bedrock-bridge.mcpack` from its
152
+ [Releases](https://github.com/chapmanjw/minecraft-bedrock-mcp-behavior-pack/releases), or clone
153
+ and `npm run build` it. The pack is the folder holding `manifest.json`, `scripts/main.js`, and
154
+ `pack_icon.png`.
155
+
156
+ 2. Place the pack folder inside the world:
157
+
158
+ ```sh
159
+ mkdir -p "/opt/bedrock-server/worlds/mcp-world/behavior_packs"
160
+ cp -r bedrock-bridge "/opt/bedrock-server/worlds/mcp-world/behavior_packs/"
161
+ ```
162
+
163
+ 3. Activate it for the world by creating (or editing)
164
+ `/opt/bedrock-server/worlds/mcp-world/world_behavior_packs.json`:
165
+
166
+ ```json
167
+ [{ "pack_id": "fa013817-66f2-4a5f-a724-1347f912bd40", "version": [0, 1, 0] }]
168
+ ```
169
+
170
+ The pack's absolute path is now
171
+ `/opt/bedrock-server/worlds/mcp-world/behavior_packs/bedrock-bridge`.
172
+
173
+ ## Step 5 — Install and configure the MCP server
174
+
175
+ Install this server on the same host:
176
+
177
+ ```sh
178
+ npm install -g minecraft-bedrock-mcp-server
179
+ ```
180
+
181
+ Generate two long random secrets — one for MCP clients, one for the behavior pack:
182
+
183
+ ```sh
184
+ openssl rand -hex 32 # use for BRIDGE_CLIENT_TOKEN
185
+ openssl rand -hex 32 # use for BRIDGE_AGENT_TOKEN
186
+ ```
187
+
188
+ Create a `.env` file (copy [.env.example](.env.example) as a starting point):
189
+
190
+ ```sh
191
+ BRIDGE_CLIENT_TOKEN=<first secret>
192
+ BRIDGE_AGENT_TOKEN=<second secret>
193
+ BRIDGE_WORLD_PATH=/opt/bedrock-server/worlds/mcp-world
194
+ BRIDGE_BEHAVIOR_PACK_PATH=/opt/bedrock-server/worlds/mcp-world/behavior_packs/bedrock-bridge
195
+ BRIDGE_HOST=0.0.0.0
196
+ BRIDGE_PORT=8765
197
+ ```
198
+
199
+ See the [configuration reference](#configuration-reference) for every variable.
200
+
201
+ ## Step 6 — Configure the behavior pack
202
+
203
+ The pack reads its configuration from the BDS scripting config directory. Create
204
+ `/opt/bedrock-server/config/default/` and add three files (the behavior pack repository ships
205
+ copy-ready versions under its `config/default/`):
206
+
207
+ **`permissions.json`** — lets the pack load its Script API modules:
208
+
209
+ ```json
210
+ {
211
+ "allowed_modules": ["@minecraft/server", "@minecraft/server-net", "@minecraft/server-admin"]
212
+ }
213
+ ```
214
+
215
+ **`variables.json`** — where the bridge listens (the MCP server's `/bridge` surface):
216
+
217
+ ```json
218
+ { "bridge_url": "http://localhost:8765" }
219
+ ```
220
+
221
+ **`secrets.json`** — the bridge token. The value is the **full `Authorization` header** — the word
222
+ `Bearer`, a space, then the `BRIDGE_AGENT_TOKEN` from your `.env`:
223
+
224
+ ```json
225
+ { "bridge_agent_token": "Bearer <the BRIDGE_AGENT_TOKEN second secret>" }
226
+ ```
227
+
228
+ > The `Bearer ` prefix must be part of the stored secret — the pack receives an opaque handle it
229
+ > cannot read or concatenate, so the scheme prefix has to travel inside the secret itself.
230
+
231
+ ## Step 7 — Start everything
232
+
233
+ You can start the two processes by hand, or — recommended — install them as `systemd` services so
234
+ they survive logout, restart on crash, and start at boot.
235
+
236
+ ### Option A — run in the foreground
237
+
238
+ Start the MCP server, then the Bedrock server, in two terminals:
239
+
240
+ ```sh
241
+ # Terminal 1 — the MCP server
242
+ minecraft-bedrock-mcp-server # reads .env from the working directory
243
+
244
+ # Terminal 2 — the Bedrock Dedicated Server
245
+ cd /opt/bedrock-server && LD_LIBRARY_PATH=. ./bedrock_server
246
+ ```
247
+
248
+ ### Option B — run as systemd services (recommended)
249
+
250
+ This is how the two processes are run in a typical long-lived deployment.
251
+
252
+ 1. Create a dedicated unprivileged user to own and run both processes, and hand it the files:
253
+
254
+ ```sh
255
+ sudo useradd --system --no-create-home --shell /usr/sbin/nologin minecraft
256
+ sudo chown -R minecraft:minecraft /opt/bedrock-server
257
+ ```
258
+
259
+ 2. Move the MCP server's `.env` (from Step 5) somewhere the service can read it, and lock it down
260
+ — it holds both bearer tokens:
261
+
262
+ ```sh
263
+ sudo mkdir -p /etc/minecraft-bedrock-mcp
264
+ sudo cp .env /etc/minecraft-bedrock-mcp/.env
265
+ sudo chown -R minecraft:minecraft /etc/minecraft-bedrock-mcp
266
+ sudo chmod 600 /etc/minecraft-bedrock-mcp/.env
267
+ ```
268
+
269
+ 3. Create `/etc/systemd/system/bedrock-server.service`:
270
+
271
+ ```ini
272
+ [Unit]
273
+ Description=Minecraft Bedrock Dedicated Server
274
+ After=network.target
275
+
276
+ [Service]
277
+ Type=simple
278
+ User=minecraft
279
+ Group=minecraft
280
+ WorkingDirectory=/opt/bedrock-server
281
+ Environment=LD_LIBRARY_PATH=/opt/bedrock-server
282
+ ExecStart=/opt/bedrock-server/bedrock_server
283
+ Restart=on-failure
284
+ RestartSec=10
285
+ StandardInput=null
286
+ StandardOutput=journal
287
+ StandardError=journal
288
+
289
+ [Install]
290
+ WantedBy=multi-user.target
291
+ ```
292
+
293
+ 4. Create `/etc/systemd/system/mc-mcp-server.service`:
294
+
295
+ ```ini
296
+ [Unit]
297
+ Description=Minecraft Bedrock MCP server
298
+ After=network.target bedrock-server.service
299
+
300
+ [Service]
301
+ Type=simple
302
+ User=minecraft
303
+ Group=minecraft
304
+ EnvironmentFile=/etc/minecraft-bedrock-mcp/.env
305
+ ExecStart=/usr/bin/minecraft-bedrock-mcp-server
306
+ Restart=on-failure
307
+ RestartSec=10
308
+ StandardOutput=journal
309
+ StandardError=journal
310
+
311
+ [Install]
312
+ WantedBy=multi-user.target
313
+ ```
314
+
315
+ `ExecStart` points at the global install from Step 5. Run `command -v minecraft-bedrock-mcp-server`
316
+ to confirm its path on your host and adjust the line if it differs.
317
+
318
+ 5. Enable and start both — the MCP server first, so the bridge is listening when the world loads:
319
+
320
+ ```sh
321
+ sudo systemctl daemon-reload
322
+ sudo systemctl enable --now mc-mcp-server bedrock-server
323
+ ```
324
+
325
+ 6. Check status and follow the logs:
326
+
327
+ ```sh
328
+ systemctl status mc-mcp-server bedrock-server
329
+ journalctl -u mc-mcp-server -f
330
+ ```
331
+
332
+ ### Confirm the handshake
333
+
334
+ When the world loads, the behavior pack handshakes with the bridge. The MCP server logs the
335
+ successful handshake, and BDS logs that the `bedrock-bridge` pack's script started. If the
336
+ handshake fails, the most common causes are a token mismatch between `secrets.json` and `.env`, or
337
+ a wrong `bridge_url`.
338
+
339
+ Quick liveness check:
340
+
341
+ ```sh
342
+ curl http://localhost:8765/healthz
343
+ ```
344
+
345
+ ## Step 8 — Connect Claude Desktop
346
+
347
+ The MCP server speaks the **Streamable HTTP** transport. Claude Desktop connects to it through the
348
+ [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) adapter, which bridges a local stdio
349
+ server entry to a remote HTTP endpoint and attaches the bearer token.
350
+
351
+ Edit Claude Desktop's config file:
352
+
353
+ - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
354
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
355
+
356
+ Add a `minecraft-bedrock` entry under `mcpServers`:
357
+
358
+ ```json
359
+ {
360
+ "mcpServers": {
361
+ "minecraft-bedrock": {
362
+ "command": "npx",
363
+ "args": [
364
+ "-y",
365
+ "mcp-remote",
366
+ "http://YOUR-SERVER-HOST:8765/mcp",
367
+ "--header",
368
+ "Authorization:${AUTH_HEADER}"
369
+ ],
370
+ "env": {
371
+ "AUTH_HEADER": "Bearer <the BRIDGE_CLIENT_TOKEN first secret>"
372
+ }
373
+ }
374
+ }
375
+ }
376
+ ```
377
+
378
+ Replace `YOUR-SERVER-HOST` with the host running the MCP server (`localhost` if it is the same
379
+ machine as Claude Desktop). The token is passed via the `AUTH_HEADER` env var so its space is not
380
+ mangled by argument parsing.
381
+
382
+ Restart Claude Desktop fully. The `mc_*` tools appear under the tools (🔌) menu once it
383
+ reconnects. If your Claude Desktop version supports **custom connectors** natively, you can
384
+ instead add the `http://YOUR-SERVER-HOST:8765/mcp` URL directly in **Settings → Connectors** with
385
+ the `Authorization: Bearer <token>` header — that skips `mcp-remote` entirely.
386
+
387
+ ## Sample prompts
388
+
389
+ With at least one player in the world (the agent acts relative to players and the world origin),
390
+ try prompts like these in Claude Desktop:
391
+
392
+ **Building**
393
+
394
+ > "Build a 9×9×5 hollow stone-brick house at around x=100, z=100 on the surface, with a door, a
395
+ > couple of glass windows, and torches inside. Find a flat spot first."
396
+
397
+ > "Clear a 20×20 area to bedrock-flat grass, then lay out a small village square — a fountain in
398
+ > the middle, paths to four corners, and a lamp post at each corner."
399
+
400
+ > "Save the structure I just built as `starter-house` so I can paste copies of it elsewhere."
401
+
402
+ **Interacting with the world**
403
+
404
+ > "What's the time and weather right now? Set it to clear and to midday."
405
+
406
+ > "List the players online and tell me what each is holding and standing on."
407
+
408
+ > "Spawn a ring of twelve armor stands around the nearest player, then give that player a
409
+ > netherite pickaxe."
410
+
411
+ **Reacting to events**
412
+
413
+ > "Subscribe to block-break events, then tell me what gets broken over the next minute."
414
+
415
+ > "Strike lightning wherever a player places a redstone block — keep watching until I say stop."
416
+
417
+ Start small and concrete. The agent works through the tool surface below; vague prompts ("make it
418
+ cooler") give it nothing to aim at.
419
+
420
+ ---
421
+
422
+ # Reference
423
+
424
+ ## Install
425
+
426
+ ```sh
427
+ npm install -g minecraft-bedrock-mcp-server
428
+ ```
429
+
430
+ Run it directly without installing:
431
+
432
+ ```sh
433
+ npx minecraft-bedrock-mcp-server
434
+ ```
435
+
436
+ Or with Docker. Each `v*` tag publishes a multi-arch image (`linux/amd64`,
437
+ `linux/arm64`) to the GitHub Container Registry — pull and run it directly:
438
+
439
+ ```sh
440
+ docker run --env-file .env -p 8765:8765 ghcr.io/chapmanjw/minecraft-bedrock-mcp-server:latest
441
+ ```
442
+
443
+ Pin to a specific version (e.g. `:0.1.0`) for reproducible deployments. To build the
444
+ image from source instead:
445
+
446
+ ```sh
447
+ docker build -t minecraft-bedrock-mcp-server .
448
+ docker run --env-file .env -p 8765:8765 minecraft-bedrock-mcp-server
449
+ ```
450
+
451
+ ## Configuration reference
452
+
453
+ All configuration is through environment variables, validated at startup — the server exits with
454
+ a clear message if a required variable is missing. Copy [.env.example](.env.example) and adjust.
455
+
456
+ | Variable | Default | Description |
457
+ | ------------------------------------ | ------------ | --------------------------------------------- |
458
+ | `BRIDGE_CLIENT_TOKEN` | _(required)_ | Bearer token for MCP clients |
459
+ | `BRIDGE_AGENT_TOKEN` | _(required)_ | Bearer token for the behavior pack |
460
+ | `BRIDGE_WORLD_PATH` | _(required)_ | Absolute path to the active world folder |
461
+ | `BRIDGE_BEHAVIOR_PACK_PATH` | _(required)_ | Absolute path to the behavior pack folder |
462
+ | `BRIDGE_HOST` | `0.0.0.0` | Bind address |
463
+ | `BRIDGE_PORT` | `8765` | Listen port |
464
+ | `BRIDGE_TLS_CERT` / `BRIDGE_TLS_KEY` | _(none)_ | TLS cert/key paths — set both, or neither |
465
+ | `BRIDGE_TRUST_PROXY` | `false` | Trust `X-Forwarded-*` (enable behind a proxy) |
466
+ | `BRIDGE_LOG_LEVEL` | `info` | Log level |
467
+ | `BRIDGE_POLL_TIMEOUT_MS` | `30000` | Bridge long-poll hold time |
468
+ | `BRIDGE_COMMAND_TIMEOUT_MS` | `15000` | How long a tool call awaits a result |
469
+ | `BRIDGE_RATE_LIMIT_RPM` | `60` | Per-token request rate on `/mcp` |
470
+ | `BRIDGE_MAX_BODY_BYTES` | `16777216` | Maximum request body size |
471
+ | `BRIDGE_QUEUE_MAX` | `256` | Maximum outstanding commands |
472
+ | `BRIDGE_METRICS_ENABLED` | `false` | Expose the Prometheus `/metrics` endpoint |
473
+ | `BRIDGE_CORS_ORIGINS` | _(none)_ | Comma-separated allowed CORS origins |
474
+
475
+ ## Endpoints
476
+
477
+ | Route | Auth | Purpose |
478
+ | ------------------------ | ------------ | ------------------------------------ |
479
+ | `POST/GET/DELETE /mcp` | client token | MCP Streamable HTTP transport |
480
+ | `GET /bridge/poll` | agent token | Behavior-pack long-poll for commands |
481
+ | `POST /bridge/result` | agent token | Behavior-pack command results |
482
+ | `POST /bridge/event` | agent token | Behavior-pack world events |
483
+ | `POST /bridge/handshake` | agent token | Behavior-pack version negotiation |
484
+ | `GET /healthz` | none | Liveness probe |
485
+ | `GET /metrics` | client token | Prometheus metrics (when enabled) |
486
+
487
+ ## TLS
488
+
489
+ The bridge carries bearer tokens and world data. **Use TLS** outside a fully trusted network.
490
+
491
+ ### Direct TLS with mkcert (private LAN)
492
+
493
+ [mkcert](https://github.com/FiloSottile/mkcert) issues a certificate trusted by your LAN machines:
494
+
495
+ ```sh
496
+ mkcert -install
497
+ mkcert bedrock-host.lan 192.168.1.50
498
+ ```
499
+
500
+ Point the server at the generated files:
501
+
502
+ ```sh
503
+ BRIDGE_TLS_CERT=./bedrock-host.lan+1.pem
504
+ BRIDGE_TLS_KEY=./bedrock-host.lan+1-key.pem
505
+ ```
506
+
507
+ When both are set, the server listens HTTPS only. When neither is set, it listens HTTP and logs a
508
+ prominent warning at startup.
509
+
510
+ ### TLS at a reverse proxy
511
+
512
+ Alternatively, terminate TLS upstream — Caddy with its internal CA, or Traefik — and run this
513
+ server on plain HTTP bound to `localhost`. Set `BRIDGE_TRUST_PROXY=true` so client IP addresses
514
+ are logged correctly.
515
+
516
+ ## Tool surface
517
+
518
+ 77 tools across 13 domains, consistently named `mc_<domain>_<action>`:
519
+
520
+ | Domain | Examples |
521
+ | --------------- | ----------------------------------------------------------------- |
522
+ | World | `mc_world_get_info`, `mc_world_set_time`, `mc_world_set_weather` |
523
+ | Blocks | `mc_block_get`, `mc_block_fill`, `mc_block_clone` |
524
+ | Structures | `mc_structure_create_from_world`, `mc_structure_place` |
525
+ | Structure files | `mc_structure_file_read`, `mc_structure_file_write` |
526
+ | Entities | `mc_entity_spawn`, `mc_entity_teleport`, `mc_entity_apply_effect` |
527
+ | Players | `mc_player_list`, `mc_player_give_item`, `mc_player_set_gamemode` |
528
+ | Inventory | `mc_inventory_get`, `mc_inventory_set_slot` |
529
+ | Scoreboard | `mc_scoreboard_set_score`, `mc_scoreboard_add_objective` |
530
+ | Properties | `mc_property_get`, `mc_property_set` |
531
+ | Effects | `mc_explosion_create`, `mc_lightning_strike` |
532
+ | Events | `mc_event_subscribe`, `mc_event_poll` |
533
+ | Server | `mc_server_save_world`, `mc_server_get_status` |
534
+ | Escape hatch | `mc_run_command` |
535
+
536
+ ## Stability and versioning
537
+
538
+ This server is a stable foundation for separately built skills and plugins. Its **public
539
+ contract**, governed by semantic versioning, is:
540
+
541
+ - tool **names**,
542
+ - tool **input schemas**,
543
+ - tool **output** (the `result` field of the response envelope),
544
+ - stable **error codes**,
545
+ - the **bridge protocol** envelopes.
546
+
547
+ Internal structure — the HTTP framework, module layout, logging format — is not part of the
548
+ contract and may change at any time. The bridge protocol is versioned independently; its major
549
+ version is negotiated at handshake.
550
+
551
+ Note that this contract is the _server's_ contract. The **underlying Bedrock Script API is not
552
+ stable** (see the warning at the top): a Bedrock update can still break behavior even when this
553
+ server's contract is unchanged.
554
+
555
+ ## Security
556
+
557
+ - Two separate bearer tokens isolate MCP clients from the behavior pack; tokens are compared in
558
+ constant time.
559
+ - Every route except `/healthz` requires a token.
560
+ - Per-token rate limiting on `/mcp`; per-command throttling protects the BDS script watchdog.
561
+ - The command queue is in memory and is not persisted — commands do not survive a restart.
562
+
563
+ See [SECURITY.md](SECURITY.md) to report a vulnerability.
564
+
565
+ ## Development
566
+
567
+ ```sh
568
+ npm install
569
+ npm run typecheck
570
+ npm run lint
571
+ npm test
572
+ npm run build
573
+ ```
574
+
575
+ ## License
576
+
577
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,11 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type { SubscriptionRegistry } from "../events/subscription-registry.js";
3
+ /**
4
+ * Registers `POST /event` (mounted at `/bridge/event`).
5
+ *
6
+ * Validates an event report and buffers each event under its subscription.
7
+ * Events for unknown subscriptions are dropped and logged — not an error for
8
+ * the behavior pack, which may report an event mid-unsubscribe.
9
+ */
10
+ export declare function registerEventRoute(app: FastifyInstance, subscriptions: SubscriptionRegistry): void;
11
+ //# sourceMappingURL=event-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-route.d.ts","sourceRoot":"","sources":["../../src/bridge/event-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAG/E;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,aAAa,EAAE,oBAAoB,GAClC,IAAI,CAqBN"}
@@ -0,0 +1,28 @@
1
+ import { EventReportSchema } from "../protocol/index.js";
2
+ /**
3
+ * Registers `POST /event` (mounted at `/bridge/event`).
4
+ *
5
+ * Validates an event report and buffers each event under its subscription.
6
+ * Events for unknown subscriptions are dropped and logged — not an error for
7
+ * the behavior pack, which may report an event mid-unsubscribe.
8
+ */
9
+ export function registerEventRoute(app, subscriptions) {
10
+ app.post("/event", (request, reply) => {
11
+ const parsed = EventReportSchema.safeParse(request.body);
12
+ if (!parsed.success) {
13
+ return reply
14
+ .code(400)
15
+ .send({ error: { code: "INVALID_INPUT", message: "malformed event report" } });
16
+ }
17
+ let ingested = 0;
18
+ for (const event of parsed.data.events) {
19
+ if (subscriptions.ingest(event))
20
+ ingested += 1;
21
+ }
22
+ if (ingested < parsed.data.events.length) {
23
+ request.log.warn({ received: parsed.data.events.length, ingested }, "some reported events referenced unknown subscriptions");
24
+ }
25
+ return reply.code(202).send({ accepted: ingested });
26
+ });
27
+ }
28
+ //# sourceMappingURL=event-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-route.js","sourceRoot":"","sources":["../../src/bridge/event-route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAoB,EACpB,aAAmC;IAEnC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,KAAK;iBACT,IAAI,CAAC,GAAG,CAAC;iBACT,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,wBAAwB,EAAE,EAAE,CAAC,CAAC;QACnF,CAAC;QAED,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACvC,IAAI,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC;gBAAE,QAAQ,IAAI,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,IAAI,CACd,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,EACjD,uDAAuD,CACxD,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type { SubscriptionRegistry } from "../events/subscription-registry.js";
3
+ /**
4
+ * Registers `POST /handshake` (mounted at `/bridge/handshake`).
5
+ *
6
+ * Negotiates the bridge protocol version on behavior-pack startup. A pack
7
+ * whose major version differs is refused with `409`. On acceptance the
8
+ * response replays active subscriptions so a restarted pack re-arms them.
9
+ */
10
+ export declare function registerHandshakeRoute(app: FastifyInstance, serverVersion: string, pollTimeoutMs: number, subscriptions: SubscriptionRegistry): void;
11
+ //# sourceMappingURL=handshake-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake-route.d.ts","sourceRoot":"","sources":["../../src/bridge/handshake-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAQ/E;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,eAAe,EACpB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,oBAAoB,GAClC,IAAI,CA2CN"}