[BUG] Cloud routines / Claude Code on the web have no non-interactive way to trust a project-scoped `.mcp.json` MCP server - daily routine broke after the untrusted-workspace gate
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What happened
A scheduled Routine running on Claude Code on the web (cloud sandbox) calls tools from a project-scoped MCP server declared in the repo's .mcp.json, auto-approved via the repo's committed .claude/settings.json (permissions.allow: ["mcp__<server>__*"]).
This worked until ~2.1.196. Now every run, the MCP server sits at ⏸ Pending approval and/or its tools are denied, because the committed project config is treated as an untrusted workspace and ignored:
Ignoring N permissions.allow entries from .claude/settings.json: this workspace has not been trusted.
Run Claude Code interactively here once and accept the trust dialog, or set
projects["/path/to/repo"].hasTrustDialogAccepted: true in ~/.claude.json.
The relevant hardening:
2.1.196 — Security:claude mcp list/getno longer spawn.mcp.jsonservers that a repo self-approved via a committed.claude/settings.json; untrusted workspaces show⏸ Pending approval
(The project-scoped ⏸ Pending approval gate itself landed earlier, ~2.1.154.)
Why this is unfixable for a routine
Every documented remedy is interactive, and a cloud routine has no human and no TUI:
- "Accept the workspace trust dialog" — no dialog in a non-interactive cloud routine.
- "Approve the server in the
/mcppanel" — no/mcppanel in a routine. claude mcp reset-project-choices— interactive re-prompt; nothing to re-prompt to.hasTrustDialogAccepted: truein~/.claude.json— the cloud sandbox is re-cloned per run; there is no persistent~/.claude.jsonto set.
There is also no web-UI affordance to trust/approve a repo-scoped (.mcp.json) server — the connector UI only manages account-level connectors, so a server that exists only in the repo has nowhere to be approved from.
Net: the project-MCP approval model assumes an interactive session, but routines are non-interactive by definition. The two are mutually exclusive, with no escape hatch.
Repro
- Repo with
.mcp.jsondeclaring a project-scoped (e.g. HTTP) MCP server and.claude/settings.jsonwithpermissions.allow: ["mcp__<server>__*"]. - Configure a Routine on Claude Code web that calls a tool from that server.
- Run it (or wait for schedule).
- Server shows
⏸ Pending approval/ tool calls are denied; routine fails. No way to approve from within the non-interactive run.
Confirming the rule syntax is NOT the problem
The same glob, supplied from a trusted source (CLI flag, which bypasses the untrusted-workspace gate), auto-approves with zero denials — proving the permission rule is valid and the issue is purely trust-source, not syntax:
claude -p "call <tool>" --permission-mode default --allowedTools 'mcp__<server>__*'
→ result returned, permission_denials: []
Whereas relying on the committed permissions.allow from .claude/settings.json in the same (untrusted) workspace → ignored.
Expected
A non-interactive way for a cloud routine / web session to trust a project-scoped .mcp.json server + its tools, e.g. any of:
- A per-routine "trust this repo's MCP servers / approve these tools" setting in the web UI.
- Honor
enabledMcpjsonServers+permissions.allowfrom a committed.claude/settings.jsonwhen the routine owner is the repo owner (or behind an explicit opt-in flag). - A documented, supported headless approval (e.g.
claude mcp approve <server> --scope projectusable from a setup script).
Current workaround (for others hitting this)
Register the server from a trusted source the gate doesn't touch — user scope — at session start, e.g. in the cloud environment setup script (runs before the session) or a committed SessionStart hook:
claude mcp add -s user -t http <server> <url> -H "Authorization: Bearer $TOKEN"
with the secret in a cloud environment variable. Pair with a non-prompting permission mode. This sidesteps the repo-self-approval gate but defeats the point of repo-scoped config.
Related
- #69960 — Cloud routines (CCR) cannot use Slack MCP write tools, "Always allow" unavailable (closed, same class, routine case unsolved)
- #69055 — Routine inside Claude Code being skipped
- #9189 — Project-scoped MCP servers don't trigger approval prompt
- #63308 — docs omit
claude mcp list/getpending-approval output for unapproved project servers - #12227 — Workspace trust not persisting
- #67319 — Trust dialog never fires on a non-CLI surface → project
.claude/settings.jsonsilently skipped - #69953 — 3P mode: custom MCP servers blocked,
allowedMcpServershardcoded empty
Environment
- Claude Code 2.1.196
- Surface: Claude Code on the web (cloud routine / scheduled session)
- MCP: project-scoped HTTP server in
.mcp.json, bearer-auth
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
I too have ran into this with previously working projects
I believe I have this issue too. I have a local mcp and approvals in the settings, but this is all ignored during cloud routines and the routine sits waiting for approvals for hours
This looks like the same authority boundary in a more concrete routine shape.
I would separate three records before the run starts:
The critical test is not "does settings.json contain an allow rule?" It is whether that rule can be promoted into an effective non-interactive pre-grant for this cloud routine.
The fail-closed path should be equally explicit:
That gives maintainers a clean regression boundary: a scheduled routine should either apply a typed pre-run authority record or fail before side effects. It should not park for hours on an approval surface that cannot be resolved inside the run.