Skip to content

feat(ai): keep MCP tools and chat turns read-only for observers - #2547

Open
Makisuo wants to merge 1 commit into
observer/enforcementfrom
observer/agents
Open

Makisuo wants to merge 1 commit into
observer/enforcementfrom
observer/agents

Conversation

@Makisuo

@Makisuo Makisuo commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

Third layer of the Observer role, on top of #2546. MCP tools and the AI chat do not go through the HTTP auth middlewares, so this layer makes them read-only by role.

What changed

  • An observer is refused every MCP tool that changes data, with the same message as the API, and is not shown those tools in tools/list. This reuses the path a read-only API key already takes.
  • The check reads the role, not a flag set where the tenant is assembled, so it holds for an API key, an MCP sign-in, a session and a chat turn alike.
  • A chat turn run for an observer is offered no approval-gated tool. An observer could not approve the proposal: the approval endpoint is refused by the layer below.
  • Observers can still ask questions. The read tools and the chat message routes are unchanged.

Verified

  • apps/ai: read-only-key.test.ts, agents.test.ts, permissions.test.ts, registry.test.ts, apply-proposal.test.ts, 55 passed. New cases: an observer runs a read tool, is refused a write tool by name with the role message, and their chat turn is offered search_traces but no mutating tool while a member's turn still proposes them.
  • @maple/ai typecheck and the CI lint config on the changed files.

Not in this layer

  • On a chat connector that cannot say who clicked an approval, the approved tool still runs with the org's authority. That predates this work and is tracked separately.
  • Whether observers should spend AI credits at all is a product call. They can today, like members.

Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

An observer is refused every MCP tool that changes data and is not shown one in tools/list, the same as a read-only API key. The check reads the role, so it holds on every way a tenant reaches the registry: an API key, an MCP sign-in, a session and a chat turn.

A chat turn run for an observer is offered no approval-gated tool, since an observer could not approve the proposal. They can still ask questions: the read tools are unchanged.
@maple-review-bot

maple-review-bot Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 8/10 · likely safe to merge. Both refusals are pinned by new tests; only the observer case of the tools/list hiding in server.ts is untested.
1 note · tests partial −1 · risk high −1

Applies the observer role to the two surfaces that skip the HTTP middlewares: MCP refuses a mutating tool by role and hides it from tools/list, and a chat turn drops its approval-gated tools. Correct on both layers and safe to merge; the McpQueryError issues in registry.ts come from the query path this diff does not touch.

  • isReadOnlyTenant refuses an observer every MCP tool that changes data
  • Observers are no longer shown write tools in tools/list
  • profileForRoles withholds an observer chat turn's approval-gated tools

Findings

🔵 Note · F1 · The observer's tools/list hiding has no test

tests · apps/ai/src/mcp/server.ts:78

app.test.ts:738 pins per-credential visibility for a read-only key, and this line extends the same gate to a role, but nothing asserts an observer's tools/list omits create_*. A revert to tenant?.readOnly !== true would pass every test in the change.

Add an observer case beside "hides write tools from a read-only key" in `apps/ai/src/mcp/app.test.ts`.
🤖 Prompt to fix this finding with an AI agent
Findings from an automated review of commit 7ea2550b6c6d403cf550cf93190df1af835cf9c5. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F1 · Note · tests · apps/ai/src/mcp/server.ts:78
The observer's `tools/list` hiding has no test
`app.test.ts:738` pins per-credential visibility for a read-only key, and this line extends the same gate to a role, but nothing asserts an observer's `tools/list` omits `create_*`. A revert to `tenant?.readOnly !== true` would pass every test in the change.
Suggested fix: Add an observer case beside "hides write tools from a read-only key" in `apps/ai/src/mcp/app.test.ts`.

Production impact

Open errors in the changed files (2)
Issue Service Occurrences File
@maple/mcp/errors/McpQueryError (3 issues) maple-chat 15 apps/ai/src/mcp/tools/registry.ts
@maple/mcp/errors/McpQueryError (7 issues) maple-ai 30 apps/ai/src/mcp/tools/registry.ts

After this merges, Maple checks whether they stop.

7ea2550 · What was checked, coverage and telemetry details are on the Maple / review check. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@coderabbitai

coderabbitai Bot commented Nov 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e3f3197f-78ff-41d6-b05e-6ca4c550d708

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

This branch has not been deployed

No deployments
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.

1 participant