muninn-mcp

muninn-mcp

Read-only MCP server exposing one Muninn Minecraft backend's capabilities via safe, capability-aware tools. Broadly covers server status, CoreProtect lookups, Paper, CMI, and WorldGuard queries.

Category
访问服务器

README

muninn-mcp

Read-only Model Context Protocol server for one Muninn Minecraft backend.

One process always represents exactly one game backend. It has one Muninn base URL, one backend bearer token, and one discovered server_id. Run a separate process for each backend because installed plugins and capabilities can differ.

The default MCP transport is the official stateful Streamable HTTP transport from the MCP TypeScript SDK. Stdio remains available as a fallback.

Requirements

  • Node.js 20 or newer
  • a reachable Muninn plugin HTTP API
  • the backend bearer token from plugins/Muninn/config.yml

This MCP server requires Muninn plugin v0.3.0 or newer. Its versioned OpenAPI document defines CoreProtect block actions and summaries, exact next_offset pagination, and CMI player search/sort parameters. The OpenAPI document shipped by the running plugin remains the backend contract source of truth.

The native CoreProtect multi-player, exclusion, interaction, and entity filter fields documented below require a plugin build whose OpenAPI exposes those parameters. They are currently being developed after v0.3.0; do not assume an older backend applies unknown filters.

HTTP-first start

npm ci
npm run build

MUNINN_BASE_URL=http://127.0.0.1:8781 \
MUNINN_AUTH_TOKEN='<Muninn backend token>' \
MUNINN_EXPECTED_SERVER_ID=survival \
MUNINN_MCP_AUTH_TOKEN='<separate MCP client token>' \
npm start

Defaults:

  • transport: http
  • bind: 127.0.0.1
  • port: 3000
  • MCP endpoint: http://127.0.0.1:3000/mcp
  • readiness endpoint: http://127.0.0.1:3000/healthz

The incoming MUNINN_MCP_AUTH_TOKEN is deliberately separate from MUNINN_AUTH_TOKEN. The first protects MCP clients → this process; the second protects this process → the Minecraft backend. Never reuse them.

An HTTP MCP client connects to the URL and supplies the static token:

{
  "mcpServers": {
    "muninn-survival": {
      "url": "http://127.0.0.1:3000/mcp",
      "headers": {
        "Authorization": "Bearer <MUNINN_MCP_AUTH_TOKEN>"
      }
    }
  }
}

The exact client configuration envelope is client-specific; the URL, standard Authorization header, and Streamable HTTP protocol are not.

Readiness needs no token and exposes no configuration secrets:

curl http://127.0.0.1:3000/healthz

The process first discovers the backend through /health and /capabilities. It only starts listening after backend identity and capabilities have passed fail-fast validation.

Stdio fallback

MUNINN_TRANSPORT=stdio \
MUNINN_BASE_URL=http://127.0.0.1:8781 \
MUNINN_AUTH_TOKEN='<Muninn backend token>' \
MUNINN_EXPECTED_SERVER_ID=survival \
npm start

Example stdio client entry:

{
  "mcpServers": {
    "muninn-survival": {
      "command": "node",
      "args": ["/absolute/path/to/muninn-mcp/dist/index.js"],
      "env": {
        "MUNINN_TRANSPORT": "stdio",
        "MUNINN_BASE_URL": "http://127.0.0.1:8781",
        "MUNINN_AUTH_TOKEN": "<Muninn backend token>",
        "MUNINN_EXPECTED_SERVER_ID": "survival"
      }
    }
  }
}

Configuration

MCP transport

Variable Default Meaning
MUNINN_TRANSPORT http http or stdio.
MUNINN_MCP_BIND 127.0.0.1 HTTP listener hostname or IP.
MUNINN_MCP_PORT 3000 HTTP listener port, 1–65535.
MUNINN_MCP_PATH /mcp Exact Streamable HTTP endpoint path.
MUNINN_MCP_AUTH_TOKEN unset Incoming static bearer. Optional only on loopback; required for every non-loopback HTTP bind.
MUNINN_MCP_ALLOWED_HOSTS loopback hosts Comma-separated hostnames without ports. Required for wildcard binds such as 0.0.0.0.
MUNINN_MCP_ALLOWED_ORIGINS none Exact comma-separated browser origins allowed for CORS. Browser Origin requests are rejected by default.

For a private-network listener:

MUNINN_MCP_BIND=0.0.0.0 \
MUNINN_MCP_ALLOWED_HOSTS=minecraft-admin.internal,192.0.2.20 \
MUNINN_MCP_AUTH_TOKEN='<high-entropy token>' \
npm start

The built-in listener is plain HTTP. Do not expose it directly to the public internet. Keep it on loopback/private networking or place TLS and appropriate network controls in front of it.

Muninn backend

Variable Required Default Meaning
MUNINN_BASE_URL yes — Backend origin or API root. A bare origin gets /api/v1/ appended.
MUNINN_AUTH_TOKEN yes — Bearer token accepted by the Muninn plugin.
MUNINN_EXPECTED_SERVER_ID no — Fail-fast backend identity pin; strongly recommended.
MUNINN_TIMEOUT_MS no 15000 Per-request timeout, 100–120000 ms.
MUNINN_DEFAULT_PAGE_SIZE no 100 Explicit default for paginated tools.
MUNINN_MAX_PAGE_SIZE no 100 MCP-side page cap, maximum 1000.
MUNINN_MAX_LOOKUP_SECONDS no 2592000 MCP-side CoreProtect time-window cap.
MUNINN_MAX_RADIUS no 128 MCP-side CoreProtect radius cap.

Backend limits remain authoritative and may be stricter.

HTTP security and lifecycle

  • Host validation is port-independent and deny-by-default.
  • Browser requests with an Origin header are denied unless the exact origin is allowlisted. CORS never uses * and never enables credentials.
  • Incoming auth uses constant-time comparison of SHA-256 token digests.
  • Request bodies are parsed only after MCP authentication and are capped at 256 KiB.
  • Neither backend nor incoming bearer values are logged or returned in errors, including nested backend payloads.
  • Each initialize request gets a cryptographically random stateful MCP session, its own official StreamableHTTPServerTransport, and its own McpServer.
  • Subsequent POST/GET/DELETE requests require a valid Mcp-Session-Id.
  • HTTP DELETE terminates a session. SIGINT/SIGTERM stop accepting requests, close all active transports/SSE streams, and close the HTTP server.
  • Sessions are in memory and are not resumable across process restarts; clients initialize again after a restart.

Capability-aware tools

Only tools whose endpoint is present in an enabled module's capability report are registered. CoreProtect tools also require the corresponding feature flag. Restart the process after backend plugin/capability changes to refresh tools/list.

All tools are annotated read-only, non-destructive, and idempotent. The Paper batch endpoint uses HTTP POST but does not mutate game state.

Core and composite

  • server_status
  • investigate_block — bounded Paper/CoreProtect/WorldGuard context with independent probe results

CoreProtect primitives

  • coreprotect_block_lookup — bounded native CoreProtect lookup for block, interaction, and entity-kill events. Include/exclude arrays become single CSV query parameters and are applied by CoreProtect before pagination. The legacy scalar player remains supported.
  • coreprotect_block_summary — bounded count and optional grouping by PLAYER, MATERIAL, and/or ACTION; reports the scan limit and explicit complete/truncated metadata so a partial matched value is never mistaken for a total.
  • coreprotect_container_lookup
  • coreprotect_item_lookup
  • coreprotect_inventory_lookup
  • coreprotect_chat_lookup
  • coreprotect_command_lookup
  • coreprotect_session_lookup
  • coreprotect_sign_lookup
  • coreprotect_username_lookup

Paper primitives

  • paper_get_container
  • paper_batch_containers (1–64 locations)
  • paper_get_player_inventory
  • paper_get_player_ender_chest
  • paper_get_player_state — includes typed Unix-second first_played and ISO first_played_iso fields for both online and known offline players
  • paper_get_player_stats
  • paper_list_players
  • paper_get_server_info
  • paper_list_entities
  • paper_get_block

CMI primitives

  • cmi_list_players — optional case-insensitive query, sort (name/last_login/last_logoff), and order (asc/desc) are applied before pagination
  • cmi_get_player — includes the typed ban.active flag when CMI exposes a ban record, distinguishing an active ban from historical/raw metadata
  • cmi_get_player_homes
  • cmi_list_warps
  • cmi_list_jails

WorldGuard primitives

  • worldguard_list_regions
  • worldguard_get_region
  • worldguard_regions_at
  • worldguard_flags_at

ProtectionStones primitives

  • protectionstones_region_at — exact world/x/y/z lookup returning the nearest claim or region: null; claim data includes owners, members, protection-block, home, parent, hidden, and read-only commerce state
  • protectionstones_player_regions — bounded claims for an exact nickname or UUID, optionally restricted to one loaded world; include_member defaults to true
MCP tool Backend endpoint Required input Optional input
protectionstones_region_at GET /api/v1/protectionstones/region-at world, x, y, z —
protectionstones_player_regions GET /api/v1/protectionstones/player/{player}/regions exact nickname or UUID in player world, include_member (default true), offset, limit

Paginated tools always send explicit bounded offset/limit values. CoreProtect lookup responses return the backend's exact has_more marker; when has_more=true, pass next_offset unchanged to the follow-up call. It is absent on the final page. Other paginated endpoints retain their documented pagination shape.

CoreProtect block lookup filters

MCP input Backend query Semantics
player: string player Legacy scalar actor include filter. Mutually exclusive with players; the only actor filter allowed with at=true.
players: string[] players CSV 1–64 included actors. Mutually exclusive with player.
exclude_players: string[] exclude_players CSV 1–64 excluded actors. Does not provide the required player/area scope by itself.
materials: string[] materials CSV 1–64 included Bukkit block materials.
exclude_materials: string[] exclude_materials CSV 1–64 excluded Bukkit block materials. Does not provide the required player/area scope by itself.
entities: string[] entities CSV 1–64 included Bukkit entity types. Implies ENTITY_KILL when actions is omitted.
exclude_entities: string[] exclude_entities CSV 1–64 excluded Bukkit entity types. Does not provide the required player/area scope by itself.
actions actions CSV One or more of BLOCK_BREAK, BLOCK_PLACE, INTERACTION, ENTITY_KILL.
world, x, y, z, radius same names Existing bounded location filter; world and all coordinates are supplied together.
at: true at=true Exact-block history. Only scalar player, location, radius, and pagination inputs are allowed.
offset, limit same names Existing bounded pagination.

Without at=true, every request requires either concrete scalar player / players, or complete world/x/y/z coordinates with radius. Material, entity, action, and exclusion filters refine that player/area scope; they never enable an unrestricted global lookup because the public CoreProtect API v12 does not support one. The special #global actor also cannot replace the required scope. Array values must be unique, non-empty, comma-free, and contain at most 64 entries.

The response remains the existing BlockEvent page. entity_type can be absent for a generic filtered entity kill when the backend uses CoreProtect API v12.

For example, this finds selected ore events for one player while keeping the material restriction server-side:

{
  "seconds": 86400,
  "player": "Steve",
  "materials": ["DIAMOND_ORE", "DEEPSLATE_DIAMOND_ORE"],
  "actions": ["BLOCK_BREAK"],
  "offset": 0,
  "limit": 100
}

This example combines multiple actors with native exclusions and entity/action filters:

{
  "seconds": 86400,
  "players": ["Steve", "Alex"],
  "exclude_players": ["Automation"],
  "materials": ["DIAMOND_ORE"],
  "exclude_materials": ["STONE"],
  "entities": ["ZOMBIE", "SKELETON"],
  "exclude_entities": ["ARMOR_STAND"],
  "actions": ["BLOCK_BREAK", "ENTITY_KILL"],
  "offset": 0,
  "limit": 100
}

For a smaller answer when only totals are needed, use coreprotect_block_summary and inspect completeness before treating matched as a total:

{
  "seconds": 86400,
  "player": "Steve",
  "materials": ["DIAMOND_ORE", "DEEPSLATE_DIAMOND_ORE"],
  "actions": ["BLOCK_BREAK"],
  "group_by": ["MATERIAL", "ACTION"],
  "max_scan_events": 10000,
  "include_rolled_back": false
}

Errors

Muninn envelopes become readable MCP tool errors with stable codes, safe details, HTTP status, backend ID, guidance, and retryability where applicable: UNAUTHORIZED, NOT_FOUND, BAD_REQUEST, SYNC_TIMEOUT, MODULE_DISABLED, FEATURE_UNAVAILABLE, LIMIT_EXCEEDED, PLAYER_OFFLINE, and INTERNAL.

Transport-side errors include TIMEOUT, NETWORK_ERROR, CANCELLED, INVALID_RESPONSE, and SERVER_ID_MISMATCH.

HTTP routing/auth errors use bounded JSON/JSON-RPC bodies and never echo Host, Origin, authorization values, request bodies, or internal exceptions.

Verification

npm ci
npm run check
npm test

The suite covers:

  • HTTP-default and stdio process startup/shutdown;
  • official StreamableHTTPClientTransport initialize, initialized notification, tools/list, tools/call, SSE, and DELETE lifecycle;
  • incoming auth 401, Host/Origin/CORS policy, invalid methods and sessions;
  • non-zero invalid configuration;
  • recursive secret redaction;
  • all 29 atomic tool-to-endpoint mappings and capability filtering.

Real plugin Docker harness

The cross-project test starts the adjacent real Paper+CoreProtect+CMI+WorldGuard fixture, starts the built HTTP MCP process, connects through the official Streamable HTTP client, and removes all temporary processes/containers. Its field evaluation covers a server overview, the Paper first_played profile contract, CMI fuzzy search/sort, an action-filtered rare material summary, native CoreProtect actor/entity include-exclude filters, and a combined WorldGuard-region/CoreProtect-session scenario. The test prints MUNINN_FIELD_EVAL JSON with actual MCP tool call counts and UTF-8 output bytes per scenario and in total:

npm run test:e2e:harness

By default the plugin checkout is expected at ../muninn-plugin:

MUNINN_PLUGIN_DIR=/absolute/path/to/muninn-plugin npm run test:e2e:harness

An already-running backend can be tested directly:

MUNINN_E2E_BASE_URL=http://127.0.0.1:8781 \
MUNINN_E2E_AUTH_TOKEN='<backend token>' \
MUNINN_E2E_SERVER_ID=survival \
npm run test:e2e

The stdio fallback remains covered by the normal test suite.

Releases and npm publication

Release Please watches conventional commits on main, maintains a release PR, updates CHANGELOG.md, package.json, package-lock.json, and the release manifest, then creates a vX.Y.Z GitHub Release when that release PR is merged. The same workflow checks and tests the released commit before publishing @vanilla-game/muninn-mcp as a public npm package with provenance.

The package is not published yet, so the first release needs a short-lived npm granular access token with permission to create packages in the vanilla-game scope. Store it as the NPM_TOKEN GitHub Actions secret. After the bootstrap publish succeeds, configure npm Trusted Publishing for:

  • GitHub organization: Vanilla-Game
  • repository: muninn-mcp
  • workflow filename: release-please.yml
  • allowed action: npm publish

Then remove the NPM_TOKEN repository secret. Future releases use GitHub OIDC through the workflow's id-token: write permission instead of a long-lived npm credential. The npm package's repository URL must continue to match this GitHub repository exactly.

By default Release Please uses the workflow's GITHUB_TOKEN. An optional RELEASE_PLEASE_TOKEN GitHub secret can supply a GitHub App or fine-grained PAT when repository policy requires release PR events to trigger other workflows.

License

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选