Repository navigation
Conversation
This was referenced Oct 13, 2026
bzoltan1
approved these changes
Oct 13, 2026
plusky
force-pushed
the
mcp-writes
branch
4 times, most recently
from
October 16, 2026 13:44
dbdaf3f to
1b717df
Compare
The official Ruby SDK for the Model Context Protocol powers the new machine interface. Only the server half is used, over the per-request controller transport, so no Rails version pin is needed. Assisted-by: opencode/GLM-5.3-Flash
POST /mcp speaks Streamable HTTP through a per-request server, so multiple Puma workers need no shared session state. The caller authenticates with the normal login identity; Bearer tokens arrive with the token stack. Tools receive the user as server context and answer guests without leaking email. Assisted-by: opencode/GLM-5.3-Flash
The episode listing tool authorizes through the same Ability as the HTML pages, and without a read rule every non-admin is denied. This grants Episode read alongside the other public listings, which also makes the episode index and show pages - already authentication-skipped in the controller - actually readable. Split out from the endpoint commit on review feedback. Assisted-by: opencode/GLM-5.3-Flash
List, fetch, LIKE-search and episode listing go through the same Ability as the HTML pages, with a uniform denial that reveals nothing about missing versus forbidden records. Search stays clear of Sphinx so the default suite needs no searchd. Assisted-by: opencode/GLM-5.3-Flash
The search tool runs a full-table LIKE per request, so unbounded clients can saturate the database. Throttle to 300 requests per 5 minutes per IP; production shares the file cache across Puma workers, the spec swaps in a memory store because test runs null_store. Assisted-by: opencode/GLM-5.3-Flash
The only tool without a bound; episodes are few today but the endpoint should not serialize unbounded relations. Assisted-by: opencode/GLM-5.3-Flash
Satisfies Style/ClassAndModuleChildren; no behavior change. Assisted-by: opencode/GLM-5.3-Flash
Headless MCP clients cannot complete the browser login, so users mint per-client Bearer secrets in the web UI. Only a SHA256 digest hits the database; the raw secret renders once at mint time and is never stored or logged. Scopes separate read clients from write clients; revocation is an instant timestamp. Assisted-by: opencode/GLM-5.3-Flash
Token management lives in profile settings and binds strictly to the requester, so even admins cannot mint credentials for somebody else. Creation caps at 50 tokens per user and the minted secret page carries Cache-Control: no-store. Assisted-by: opencode/GLM-5.3-Flash
A present Bearer header owns the request: the session is ignored, so a junk secret degrades to guest instead of inheriting the cookie identity. Forgery protection is skipped only for authenticated tokens, authentication runs once per request, and last_used_at refreshes at most once a minute. Scope travels in the server context for the write tools of the next stack. Assisted-by: opencode/GLM-5.3-Flash
A stolen bearer was valid until manual revoke. New tokens expire a year after mint; nil means never for tokens minted before this change. The UI shows the date; authentication rejects the expired. Assisted-by: opencode/GLM-5.3-Flash
With forgery protection on, a junk bearer plus session cookie raised an HTML error instead of the uniform guest denial, fingerprinting sessions. Re-run the dispatch with a guest identity so the bytes match. Also drops two dead McpAuth wrappers nobody calls. Assisted-by: opencode/GLM-5.3-Flash
Left over from extracting the dispatch helper; no behavior change. Assisted-by: opencode/GLM-5.3-Flash
Posting updates and joining projects are the state-changing calls, so both enforce the write scope inside the tool on top of the Ability check, denying uniformly like the read tools. my_projects answers which projects the caller originates or belongs to, proving the authenticated user is carried end to end. Assisted-by: opencode/GLM-5.3-Flash
Gives operators the endpoint URL, the token minting flow and a client configuration pointer, so the feature is discoverable without reading the code. Assisted-by: opencode/GLM-5.3-Flash
Write calls accept unbounded TEXT, inviting table bloat through the API. Model-side bound covers the MCP tools and every other writer; existing copy stays orders of magnitude below it. Assisted-by: opencode/GLM-5.3-Flash
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.
State-changing MCP tools on top of #1943: posting project updates and joining projects (both enforce the write scope inside the tool plus the Ability check, denying uniformly), and my_projects, which proves the authenticated user is carried end to end. Includes the README section documenting the endpoint and token flow. Split from #1939, where this code was already reviewed.
To verify: