Repository navigation
fix(settings): the remote-session grants misspell the server they name - #488
Conversation
`.claude/settings.json` has granted `mcp__Claude_Code_Remote__*` — underscores — since the rules were written. The server registers as `Claude-Code-Remote`, with hyphens. Confirmed against the CLI's own log tree, which is keyed by the name the CLI used: `mcp-logs-Claude-Code- Remote`. So every one of those seven rules has matched nothing, in every episode, and `create_session` prompted on every dispatch. Both denials went with it. AGENTS.md bans babysitting timers and this file denies `send_later` and `create_trigger` — under a name that matches no tool, so the ban has been decorative for as long as the typo has. WHY IT SURVIVED TWO SESSIONS OF DIAGNOSIS, which is the part worth keeping. The evidence looked exactly like CLOUD-178's UUID name-flip: connectors were exposed as `mcp__<uuid>__*`, no committed rule spelled that, and remote-session calls were denied. 121de48 recorded that reading as fact. It is wrong, and one observation in the same sessions refuted it the whole time: Linear kept working under the same UUID exposure, which a missing allow rule cannot explain. The reason is that A CLAUDE.AI CONNECTOR IS NOT GOVERNED BY THIS ALLOWLIST. It is authorised at the connector layer — every connector reports `connected: true, enabledInChat: true` regardless of what this file says — so `mcp__Linear__*` is not what makes Linear work, and the name it spells never mattered. Claude-Code-Remote is not a connector; it is the harness's own toolbox server, absent from the connector list, and therefore the one server in this file whose rule name has to be right. Same observation, opposite causes, and only the log tree separates them. So the gate goes where the log tree already is. `mcp-attach-check` gains a second predicate: a permission rule whose server name differs from a registered one only in separators or case. Deny rules included, for the reason above. Narrowed deliberately, and the naive predicate is refused by a row. "A granted server that did not register" was written first and fired on `mcp__claude_ai_Linear__*`, the local-CLI spelling of a connector that legitimately registers nothing in a web session. A gate that reports the portable entries as defects on every run is a gate that gets switched off, so the finding is near-miss only. The predicate also sat behind an early return for "no enabled `.mcp.json` servers", so its first four rows passed green while it never executed — CLOUD-418's vacuity, reproduced by its own remedy. The two predicates are independent now, and the restored early exit is the declared mutation, with the task enrolled in `MUTANT_GATES`. Not claimed: that this unblocks `create_session`. The session is currently in the UUID-exposure phase, where no readable-name rule matches either, so the fix cannot be exercised here. Refs: CLOUD-178, CLOUD-191
`mise run mutant` resolves a declaration's third field as a bats `--filter` regex, which is case-sensitive. The row named "…the stamp is newer"; the case is "CLOUD-615 REPLAY: … the stamp is NEWER". So the filter matched nothing, the task reported `names-no-case`, and that gate had no proof it discriminates for as long as the declaration has existed. Caught by enrolling a new gate in `MUTANT_GATES` and running the sweep, which is the sweep working rather than a chore: an unenforced declaration is exactly the "reads as coverage" failure CLOUD-418 was filed about. Refs: CLOUD-418
CLOUD-665 The remote-session grants misspell the server they name, so every rule has matched nothing since it was written
Seven rules matching nothing, in every episode, since the rules were written. Why it survived two sessions of diagnosisThe evidence mimicked CLOUD-178's UUID name-flip: connectors were exposed as One observation in those same sessions refuted it throughout: Linear kept working under the same UUID exposure, which a missing allow rule cannot explain. The reason is that a claude.ai connector is not governed by this allowlist at all — Same observation, opposite causes, and nothing in the repo could separate them: Refinement — Ready Refinement gate: Definition of Ready & Done. This body carries only specializations.
The narrowing is load-bearing, not a detailThe obvious predicate — "a granted server that did not register" — is wrong and was written first. It fires on The defect is a near miss: a rule naming a server that is here, under a spelling differing only in separators or case. That is never intentional, and it cannot be confused with a cross-host entry — fold out separators and case, and Done
CLOUD-178 claude.ai connector tools flip between readable and UUID names, silently breaking the permission allowlist
Split out of CLOUD-177, which is Done on its own scope. Evidence thread: the comments on CLOUD-177, in particular the final correction establishing verdict (a). ReadyA claude.ai connector MCP server is exposed to a session under two different names over its lifetime, and a permission allow rule can only name one of them. At session start the Linear connector appears as This hits the board-in-lockstep rule directly: moving a Measured, across two containers
Verdict: the UUID is stable and equal across containers. It is a durable identity for the connector; what varies is which of the two names is live at a given moment. This was initially mis-called as "unstable per boot" from a session that had not yet reconnected — absence of a reconnect is not evidence of naming stability, and any future measurement here needs to span at least one reconnect to say anything. Confirmed load-bearing, not cosmetic: a Note this is a second, independent defect from the one that originally masked it: an org-level Ready predicateA session survives a connector reconnect with Linear calls still auto-approved — no prompt on a Done (proposed)
"mcp__Linear",
"mcp__4db58e41-cd4e-4818-8922-46cf616593f4"
Open questions
Why the repo cannot fix this itselfIn Claude Code on the web, connectors are "provisioned by the remote host and arrive as explicit Generated by Claude Code CLOUD-191 Self-heal the connector allowlist at session start from the host-injected MCP config
ReadyThere is no durable machine. Every session runs in a container that is reclaimed, so a fix written to The connector's exposed name is chosen per registration episode, and observation now shows the flip is bidirectional — a single session went readable → UUID → readable. So the failure has no monotone boundary: an allowlist naming only the readable form is correct during whichever episodes land on it, and silently denies every Linear call during the others. The identity is discoverable, so no identifier needs committingThe host writes its injected MCP configuration to That makes a self-healing repair possible with no account-specific identifier in git: read the injected config, derive the server keys present, emit allow rules for them. The mechanism is portable to any account and any fork, and it repairs whichever name the current episode chose. Ready predicateA session that receives connectors under a name no committed allow rule matches reaches a state where Linear calls are permitted, without a human editing settings and without any UUID appearing in a tracked file. Blockers (§8)None. Open questions, in the order they gate the work
RETRACTED — nothing shipped, and the section below described a fix for a defect that was not happeningEverything from here to the end of the "Done" list is withdrawn. It was written, committed, and reset out of the branch before merging; no The defect that was actually denying Why this issue's premise does not survive. It reasons that the flip "silently denies every Linear call" during UUID episodes. It does not, and never did: a claude.ai connector is not governed by So the Ready predicate is unreachable as written — "a session that receives connectors under a name no committed allow rule matches reaches a state where Linear calls are permitted" describes a state that already holds for reasons unrelated to any allow rule. Back to Todo, unassigned, and it needs re-refining against what is now known before anyone builds again. What may remain: whether the toolbox server's rule matching survives a mid-session flip once the spelling is right. That is a real question and it is much smaller than this issue. RETRACTED: What shipped, and where it differs from the specification aboveThe premise held and the mechanism changed. The Ready block reasoned that the live identity is discoverable, so nothing account-specific needs committing — correct, and the missing half was which discoverable thing to anchor on. Not the key: each entry's
It is a name translation, not a grant. Done
RETRACTED — this residue was residue of the retracted mechanism. CLOUD-663 is being closed as not-a-finding: the memory text it carries describes the same wrong model. The underlying observation it made — that the protected-path gate's redirect names a surface which may not have attached — is real and stands on its own, and is recorded there. Original text follows.
Watch: what a later session needs to pick this upThe trigger is not observable to an agent as a prompt — an agent cannot see its own approval prompts. What it can observe:
So the observation to record, each time it is taken: the session's live connector keys, which form the tool names took, and whether a call succeeded or was denied. Appended to CLOUD-178, which is the evidence thread and is durable when the container is not. That series is what open question 2 needs. It cannot be answered from a single session, and no session so far has recorded it in a form the next one can read. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan includes up to 3 reviews per rolling hour; 1 remains after this review. 📝 WalkthroughWalkthroughThe change aligns Claude Remote MCP permission identifiers and extends ChangesMCP permission validation
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The permission-name correction and associated checks introduce no actionable merge-blocking risk at the current head; the PR is merge-ready after normal checks and review. Sequence Diagram(s)sequenceDiagram
participant mcp-attach-check
participant PermissionSettings
participant SessionLogTree
mcp-attach-check->>PermissionSettings: Read allow and deny MCP rules
mcp-attach-check->>SessionLogTree: Read registered server names
SessionLogTree-->>mcp-attach-check: Return server directories
mcp-attach-check->>mcp-attach-check: Normalize and compare names
mcp-attach-check-->>PermissionSettings: Report near-miss names
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsThese MCP integrations need to be re-authenticated in the Integrations settings: Linear Comment |
|
|
/fast-forward |



.claude/settings.jsonhas grantedmcp__Claude_Code_Remote__*— underscores — since the rules were written. The server registers asClaude-Code-Remote, with hyphens. Confirmed against the CLI's own log tree, which is keyed by the name the CLI used:Seven rules, matching nothing, in every episode.
create_sessionprompted on every dispatch.Both denials went with it. AGENTS.md bans babysitting timers and this file denies
send_laterandcreate_trigger— under a name that matches no tool, so the ban has been decorative for as long as the typo has.Why it survived two sessions of diagnosis
The evidence looked exactly like CLOUD-178's UUID name-flip: connectors were exposed as
mcp__<uuid>__*, no committed rule spelled that, and remote-session calls were denied. Commit 121de48 recorded that reading as fact. It is wrong, and one observation in those same sessions refuted it the whole time — Linear kept working under the same UUID exposure, which a missing allow rule cannot explain.The reason is that a claude.ai connector is not governed by this allowlist. It is authorised at the connector layer;
ListConnectorsreports every connectorconnected: true, enabledInChat: trueregardless of what this file says. Somcp__Linear__*is not what makes Linear work, and the name it spells never mattered.Claude-Code-Remote is not a connector — it is absent from the connector list entirely, being the harness's own toolbox server. It is therefore the one server in this file whose rule name has to be right, and it was the only casualty. Same observation, opposite causes, and only the log tree separates them.
The gate
mcp-attach-checkalready reads both the settings file and the log tree, so the predicate goes there: a permission rule whose server name differs from a registered one only in separators or case. Deny rules included, for the reason above.Narrowed deliberately, and the naive predicate is refused by a row. "A granted server that did not register" was written first and fired on
mcp__claude_ai_Linear__*— the local-CLI spelling of a connector, legitimately absent in a web session. A gate that reports the portable entries as defects on every run is a gate that gets switched off, so the finding is near-miss only.Eight new rows. One of them exists because the predicate initially sat behind an early return for "no enabled
.mcp.jsonservers", so its first four rows passed green while it never executed — CLOUD-418's vacuity, reproduced by its own remedy. The two predicates are independent now, the restored early exit is the declared#MUTANT, andmcp-attach-checkis enrolled inMUTANT_GATES.Also repairs
claim-check's own#MUTANTdeclaration, whose case-name substring did not match the case it names (newervsNEWER), somise run mutantreportednames-no-caseand could not prove that gate discriminates.What this does not claim
That the corrected spelling unblocks
create_session. This session is in the UUID-exposure phase, where no readable-name rule matches either, so the fix cannot be exercised here. The next session that starts in the readable phase is the test.History
Its predecessor (#487) opened carrying a ~600-line
PreToolUsename-translation mechanism built on the wrong diagnosis above. That was reset out of the branch before merging and is not in this diff. CLOUD-191, which specified it, is back in Todo and needs re-refining: its Ready predicate describes a state that already holds for reasons unrelated to any allow rule.Closes CLOUD-665
Refs: CLOUD-178, CLOUD-191
Summary by CodeRabbit
Bug Fixes
Tests
Chores