Repository navigation
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>
- 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>
Maselkov
added a commit
that referenced
this pull request
Nov 4, 2026
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.
Stacked on #199, which is stacked on #198. Merge #198, then #199, then this. Until they're in, this diff also shows their commits. The base is
mainon purpose, so this can't end up merged into a stacked branch.claude.ai, the Claude apps and Claude Code can now connect to
/mcpby URL and sign in with OAuth, without anyone handing them a token. The server is its own authorization server, built on the access tokens users already have.The flow
/mcpwithout a token gets a 401 withWWW-Authenticate: Bearer resource_metadata=".../.well-known/oauth-protected-resource". claude.ai only starts signing in on a 401./.well-known/oauth-protected-resource/mcp) names this server, and/.well-known/oauth-authorization-server(RFC 8414) lists the endpoints. These and the token and registration endpoints sendAccess-Control-Allow-Origin: *for browser-based clients./oauth/register, RFC 7591): public clients only. Redirect URIs must be https, or http to a loopback address. Claude registers on every new connection, so registrations never used are dropped after a day, with at most 200 an hour. CIMD isn't supported; the metadata doesn't advertise it, so claude.ai falls back to registering./oauth/authorize): a plain HTML page in the site's style.form-actionmust include the app's callback origin, because Chrome appliesform-actionto the redirect after a form POST./oauth/token, form-encoded):gcmat_…) last 1 h; refresh tokens (gcmrt_…) last 30 days.invalid_grant, as claude.ai expects.iss(RFC 9207).Loopback redirect URIs match on any port, which Claude Code needs.
Revocation
A grant remembers the login token the user consented with. Revoking that token, New token, or deleting the user disconnects the app. Admins see each user's apps in Manage users ("App: Claude · connected … · used …") with a Disconnect button. The new route is
POST /api/admin/apps/<id>/revoke.Storage
A new
app.dbmigration addsoauth_clients,oauth_codes,oauth_grantsandoauth_tokens. Codes and tokens are stored as SHA-256 hashes. Grants and codes cascade when their user is deleted.Consent page
Dark and light at phone width, checked in headless Chrome:
Testing
tests/test_oauth.py(40 tests): discovery andPUBLIC_URL; registration, including bad redirect URIs, confidential clients, the hourly cap and dropping unused clients; consent (signing in, allow, deny, a forged form, a cross-origin POST, unknown client or redirect, the four errors sent back to the client, loopback any-port); tokens (the full flow onto/mcp, a code used once, wrong verifier, wrong redirect or client, expiry, refresh rotation and replay revoking the grant); revocation by login token, New token, user deletion and admin Disconnect./mcp, see the app in Manage users, Disconnect it, and check the token stops working.node --test, e2e (55),node --check, Lua (on 5.4), ruff, luacheck and shellcheck.🤖 Generated with Claude Code