Skip to content

Add stateless MCP endpoint with read-only tools - #1942

Open
plusky wants to merge 7 commits into
SUSE:masterfrom
plusky:mcp-reads
Open

plusky wants to merge 7 commits into
SUSE:masterfrom
plusky:mcp-reads

Conversation

@plusky

@plusky plusky commented Oct 13, 2026 •

Copy link
Copy Markdown

Exposes the app over Model Context Protocol at POST /mcp (stateless Streamable HTTP via the official mcp gem, so multiple Puma workers need no shared state). This first stack serves server info plus project/episode search and reads; every call runs as the login user through the existing Ability, with a uniform denial for missing versus forbidden records. /mcp is throttled with rack-attack (300 requests per 5 minutes per IP). Split from #1939, where this code was already reviewed; Bearer tokens and write tools follow in the stacked PRs.

To verify:

  • bundle exec rspec and bundle exec rubocop
  • Boot the app, POST initialize and tools/list to /mcp (see README in the last stack for the endpoint shape), or point MCP Inspector at it
  • No database migration in this stack.

@plusky

plusky commented Oct 13, 2026

Copy link
Copy Markdown
Author

@pdostal @bzoltan1 — this supersedes #1939, split into a stack per CONTRIBUTING: #1942 (here: endpoint + reads), #1943 (tokens + bearer auth), #1944 (write tools + docs). All commits SSH-signed with Assisted-by trailers, CI running on each. Re-review appreciated when you have a moment.

Comment thread app/models/ability.rb
@plusky
plusky force-pushed the mcp-reads branch 3 times, most recently from bcb7ed5 to c487bab Compare October 16, 2026 13:44
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants