Javadoc: v1.0.1
Spigot/Paper plugin that runs an embedded Model Context Protocol server over HTTP(S): tools and resources for the world directory, console, logs, and players, gated by bearer tokens and per-tool policies. Uses the MCP Java SDK (streamable HTTP).
- MCP over HTTP(S) (Jetty); bind address, port, and path in config
- Bearer token or
X-MCP-Token; per-profile YAML underaccess.<profile>.tools.<toolId> - Built-in tools/resources:
fs_read,fs_list,fs_write,server_command,server_logs,players_list,player_get - Other plugins can register MCP tools via the Bukkit service
MinecraftServerMcp /mcp reloadreloads tokens and policies (does not unregister tools)
| Version | |
|---|---|
| Paper or Spigot | 1.21+ |
| Java | 21 |
- Install the shaded JAR from Releases (not
*-plain.jar) intoplugins/. - Start once, then edit
plugins/MinecraftServerMCP/config.yml. - Set at least one
access.<name>.tokento a long random secret. The plugin disables itself if no tokens are configured. - Point MCP clients at
http://<host>:<port><mcpPath>(defaults:8765,/mcp) with headerAuthorization: Bearer <token>orX-MCP-Token: <token>. - Prefer
bindAddress: "127.0.0.1"unless the client runs on another machine. Optional HTTPS is configured underssl(requires a full restart to change).
Example listener + token:
bindAddress: "127.0.0.1"
port: 8765
mcpPath: "/mcp"
access:
prod:
token: "<use openssl rand -hex 32 or similar>"Full keys: see config.yml (inline comments).
Don't forget to update the URL and credentials.
{
"mcpServers": {
"minecraft-server-mcp": {
"url": "http://127.0.0.1:8765/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}Policy YAML: access.<profile>.tools.<id>.
| Id | Type | Notes |
|---|---|---|
fs_read |
Resource | minecraft-server-mcp://fs/read{?path} |
fs_list |
Resource | minecraft-server-mcp://fs/list{?path} |
fs_write |
Tool | |
server_command |
Tool | commandWhitelist / commandBlacklist |
server_logs |
Tool | maxLogLines, maxLogBytes, … |
players_list |
Resource | minecraft-server-mcp://players/list |
player_get |
Resource | minecraft-server-mcp://player/get{?name} |
Use a prefix on custom tool names (e.g. myplugin_action) so they do not collide with built-ins.
API reference: Javadoc v1.0.1 (MinecraftServerMcp, built-ins, config types).
plugin.yml:
name: MyPlugin
# ...
softdepend: [MinecraftServerMCP]Use depend if MCP must be present before yours loads.
2. Add compile-only dependencies (JitPack)
The library is built from this repo as eu.mrapik:minecraft-server-mcp. The published POM declares api dependencies (MCP Java SDK BOM + mcp + JetBrains annotations), so Gradle and Maven pull MCP / Reactor / etc. transitively—you do not list those yourself.
You still need compileOnly Spigot API (Paper repo or Spigot snapshots).
Gradle (Kotlin DSL)
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
// maven("https://repo.papermc.io/repository/maven-public/") // or Spigot snapshots — your usual setup
}
dependencies {
compileOnly("eu.mrapik:minecraft-server-mcp:VERSION")
compileOnly("org.spigotmc:spigot-api:1.21.11-R0.1-SNAPSHOT")
}Gradle (Groovy)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
compileOnly 'eu.mrapik:minecraft-server-mcp:VERSION'
compileOnly 'org.spigotmc:spigot-api:1.21.11-R0.1-SNAPSHOT'
}Use a Git tag (e.g. v1.0.1) or commit hash as VERSION—see JitPack.
Maven
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>eu.mrapik</groupId>
<artifactId>minecraft-server-mcp</artifactId>
<version>VERSION</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.11-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>Do not bundle the shaded MinecraftServerMCP plugin inside your plugin JAR. At runtime only the server’s plugins/ copy is used.
Resolve MinecraftServerMcp after this plugin has enabled, implement McpToolDefinition, and unregister when your plugin disables. Tool id() must match McpSchema.Tool#name() and your access.*.tools.<id> key.
import eu.mrapik.minecraftservermcp.api.MinecraftServerMcp;
import eu.mrapik.minecraftservermcp.api.McpToolDefinition;
import eu.mrapik.minecraftservermcp.api.McpToolInvocation;
import eu.mrapik.minecraftservermcp.util.ToolResults;
import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.McpJsonDefaults;
import io.modelcontextprotocol.spec.McpSchema;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Mono;
import java.util.Map;
public final class MyPlugin extends JavaPlugin {
private final McpJsonMapper json = McpJsonDefaults.getMapper();
@Override
public void onEnable() {
var reg = Bukkit.getServicesManager().getRegistration(MinecraftServerMcp.class);
if (reg == null) {
getLogger().warning("MinecraftServerMCP not loaded; skipping MCP tool.");
return;
}
var mcp = reg.getProvider();
var def = new McpToolDefinition() {
@Override
public @NotNull String id() {
return "myplugin_greeting";
}
@Override
public @NotNull org.bukkit.plugin.Plugin owningPlugin() {
return MyPlugin.this;
}
@Override
public @NotNull McpSchema.Tool tool() {
return McpSchema.Tool.builder()
.name("myplugin_greeting")
.description("Returns a short greeting")
.inputSchema(json, """
{"type":"object","properties":{"name":{"type":"string"}},"required":[]}
""")
.build();
}
@Override
public @NotNull Mono<McpSchema.CallToolResult> call(@NotNull McpToolInvocation invocation) {
return Mono.just(ToolResults.ok(Map.of("greeting", "Hello from MyPlugin")));
}
};
mcp.registerTool(def).subscribe();
}
}McpToolInvocation runs off the server thread; use Bukkit.getScheduler() or patterns from eu.mrapik.minecraftservermcp.builtin for main-thread work.
chmod +x gradlew # Unix if needed
./gradlew build- Server:
build/libs/MinecraftServerMCP-*.jarwithout-plainin the name. - API only:
*-plain.jarfor local compile tests.
Treat tokens as root-access token/password. Never commit or share your tokens. Be careful!