Repository navigation
MCP: AI assistants can request crafts and set stock rules - #201
Merged
Merged
Conversation
The MCP endpoint coming next must check what the page's routes check, so the logic moves into functions both can call: - commands.request_cancel(): the busy-CPU and same-job checks. The job identity helpers (output_identity, same_output) move with it from tracking, which imports commands, not the other way round. - commands.craft_request_error(): the craft request's input checks. - stock.item_from(), set_alert(), set_target() and rules(): stock rule validation, the craftable check and the rules listing. - planner.rules_for_user(): the stock rules a plan warns about. No change to what the routes accept or answer, except that a craft request's amount can no longer be true (which counted as 1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/mcp is a Model Context Protocol endpoint, so Claude Code or another MCP client can answer questions about the base. Eleven read-only tools: item search, item details and history, inventory changes, current crafts, a job's held-up steps, craft history and stats, the crafting plan, stock rules and power. - Hand-written JSON-RPC over Streamable HTTP (gcm/mcp/protocol.py): one JSON answer per POST, no sessions. It serves both protocol eras: the 2026-07-28 stateless requests (version in _meta, mirrored headers checked) and initialize-based 2025-03-26..2025-11-25 clients. - Signed in with the user's personal access token as a bearer token (auth.bearer_required), never a session cookie; another site's Origin is refused. - Tools take item keys from search_items, never names, and compact their output for a model's context. - docs: an AI assistants section in using.md, plus README, server.md, how-it-works.md and CONTEXT.md. dev.py prints the MCP address. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude.ai's connectors only sign in with OAuth, so the server is now its own authorization server for /mcp: - /mcp's 401 points at the protected resource metadata (RFC 9728), which names this server's authorization server metadata (RFC 8414). - Clients register themselves (RFC 7591, public clients only, unused ones dropped after a day, capped per hour). - /oauth/authorize is a consent page: sign in with your access token, see which app asks and where it sends you back, Allow or Cancel. Its own CSP lets the form's redirect reach the app's callback. - /oauth/token trades the code (PKCE S256 only) for an hour's access token and a 30-day refresh token. Refresh tokens rotate; one used twice revokes the grant. - Loopback redirect URIs match on any port, for Claude Code. - Manage users lists each user's connected apps with Disconnect. Revoking the login token they consented with, New token or deleting the user disconnects them too. - docs: using.md (claude.ai and Claude Code setup), server.md (PUBLIC_URL, proxy paths), CONTEXT.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven more tools, each running the page route's own code and role check: - set_stock_alert / remove_stock_alert: your own low-stock alerts. - set_stock_target / remove_stock_target (operators): keep-in-stock targets. - request_craft (operators): works out the crafting plan first and doesn't queue while ingredients are missing unless called again with force. No fixed amount cap: what's sensible depends on the item. It echoes back the name, amount, unit and stock. - cancel_craft (operators): takes the job_id current_crafts now gives each busy CPU, so a CPU that moved on to another job isn't cancelled. - request_status: whether a request or cancel went through. The tools that change the game carry destructiveHint, and they and the server's instructions tell the model to confirm with the user first. Viewers aren't offered operators' tools. Names lose Minecraft's colour codes. The consent page says what an operator's app can change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A tool that raises anything but ToolError answers as a tool error (and is logged) instead of a 500 the client can't read, and a KeyError inside a tool no longer reads as "Unknown tool". - Numbers in arguments must be finite: JSON's 1e400 is infinity to Python, which int() turned into a 500. - inventory_changes builds the snapshot's lookup once instead of scanning it for every changed item. - search_items ranks by the name without Minecraft's colour codes, and tool output drops them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The consent page only takes POSTs from its own page (Origin and Sec-Fetch-Site): another site could post a sign-in with its own token and sign the visitor in as someone else (login CSRF). - A refresh token used again within 30 s of its first use gets tokens instead of revoking the grant: that's two requests refreshing at once or a retry after a lost answer, not a copied token. - Registrations are capped per address (30 an hour) as well as in all (2000), so one script can't stop everyone else connecting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- request_status expires the queue before reading a request's row: a request the game never picks up is otherwise only marked failed when the game or the page next looks, and kept reading "waiting". - cancel_craft requires the job_id when the CPU reports what it's making: without it, a CPU that moved on to another job since the user agreed would have that one cancelled. - Stock rule amounts must be finite (on the page too): JSON's 1e400 is infinity, which a refill would then ask the game for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Last of the stack: merge #198 → #199 → #200 → this. Until those are in, this diff also shows their commits. The base is
mainon purpose.An AI assistant can now act on the base as well as read it. It can only do what the signed-in user's role allows, through the same code the page uses.
New tools
set_stock_alert,remove_stock_alertstock.set_alertset_stock_target,remove_stock_targetstock.set_target(craftable items only, refill above keep)request_craftcommands.queue_craftcancel_craftcommands.request_cancelrequest_statusGuards on
request_craftforce: truestarts it anyway; the description says to do that only when the user asks after seeing what's missing. With no pattern scan yet it queues the request and says the ingredients went unchecked, since AE2 still refuses what it can't cover.destructiveHint: true, so clients ask the user before calling them. Their descriptions and the server'sinstructionsalso tell the model to state the item, amount or CPU and get the user's OK.Also
current_craftsgives each busy CPU's job ajob_id.cancel_craftsends it back as the cancel's expected output, so a CPU that has moved on to another job isn't cancelled (the existing 409 check).tools/listdepends on the user: viewers aren't offered the operators' tools, and the tools also check the role when called.plan_craftandrequest_craftshare one plan helper.§a16k§r ME Storage Componentbecomes16k ME Storage Component), which showed up ondev.py.store.requests.find()looks up one of the user's own requests or cancels.request_craftguards a craft indocs/using.md, the README feature line, and a design note inCONTEXT.md.Testing
tests/test_mcp_write.py(15 tests): role filtering and refusals; annotations; alerts and targets with the page's own error messages;request_craftunchecked without patterns, held back when ingredients are missing, then forced; only craftable items; status through accepted and failed; cancel throughjob_id, refusing a CPU that moved on, and idle or unknown CPUs.dev.py:request_craftfor 4 Inconel-625 Gears was picked up by the fake game and accepted on CPU B01, andrequest_statusshowed it.node --test, e2e (55),node --check, Lua (on 5.4), ruff, luacheck and shellcheck.🤖 Generated with Claude Code