fix(binjovi-mcp): grant the Claude client repository.create #120

Merged
binjovi-bot merged 1 commit from fix/claude-client-repository-scope into trunk 2026-09-13 14:45:26 +00:00
Owner

create_repository over MCP is refused with forbidden, even though the control
plane's BINJOVI_MCP_POLICY binding holds binjovi.repository.create and the
live Deployment carries it.

Why the binding was not enough

MCP authorization is the intersection of the policy binding and the token's
own scope claim (Binjovi.Api.MCP.Access.read/4 filters binding permissions by
identity.claims["scope"]). This client never carried the scope, so the binding
could grant nothing. get_permissions showed 20 grants with
binjovi.repository.create absent, which is the same filter reporting the same
gap.

Two writers, one scope apart

sean/seanfarm also registers binjovi-claude-code, with the scope present:

writer binjovi scopes repository.create
this repo 20 no
sean/seanfarm claude-code-oauth.yaml 21 yes

Last sync won, and the live client was diffed to confirm it: exactly one scope
missing, every other value identical.

Binjovi names this repo the owner — "Ory GitOps owns these public clients and
fixes their sole audience to MCP"
(Binjovi.MCPOAuth.Protocol, @managed_clients).
So the scope is added here, and the duplicate registrar is removed from
sean/seanfarm in a companion PR.

binjovi.repository.create is a declared MCP scope:
Binjovi.Domain.MCPPermissions maps create_repository to it, and
Protocol.@scopes advertises it in the OAuth metadata.

Guard

tests/binjovi-mcp-claude-client.sh moves length == 23 to 24 and now names
the scope, so dropping it again is a red test rather than a forbidden tool call
with no explanation. Confirmed red before the manifest change, green after.

tests/check.sh passes.

After this deploys

The client changes, not the token. Anyone already connected must reconnect the
MCP server to be issued a token carrying the new scope — a refresh does not pick
up scopes added after the grant.

https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3

`create_repository` over MCP is refused with `forbidden`, even though the control plane's `BINJOVI_MCP_POLICY` binding holds `binjovi.repository.create` and the live Deployment carries it. ## Why the binding was not enough MCP authorization is the **intersection** of the policy binding and the token's own `scope` claim (`Binjovi.Api.MCP.Access.read/4` filters binding permissions by `identity.claims["scope"]`). This client never carried the scope, so the binding could grant nothing. `get_permissions` showed 20 grants with `binjovi.repository.create` absent, which is the same filter reporting the same gap. ## Two writers, one scope apart `sean/seanfarm` also registers `binjovi-claude-code`, with the scope present: | writer | binjovi scopes | `repository.create` | |---|---|---| | this repo | 20 | no | | `sean/seanfarm` `claude-code-oauth.yaml` | 21 | yes | Last sync won, and the live client was diffed to confirm it: exactly one scope missing, every other value identical. Binjovi names this repo the owner — *"Ory GitOps owns these public clients and fixes their sole audience to MCP"* (`Binjovi.MCPOAuth.Protocol`, `@managed_clients`). So the scope is added **here**, and the duplicate registrar is removed from `sean/seanfarm` in a companion PR. `binjovi.repository.create` is a declared MCP scope: `Binjovi.Domain.MCPPermissions` maps `create_repository` to it, and `Protocol.@scopes` advertises it in the OAuth metadata. ## Guard `tests/binjovi-mcp-claude-client.sh` moves `length == 23` to `24` and now names the scope, so dropping it again is a red test rather than a `forbidden` tool call with no explanation. Confirmed red before the manifest change, green after. `tests/check.sh` passes. ## After this deploys The client changes, not the token. Anyone already connected must reconnect the MCP server to be issued a token carrying the new scope — a refresh does not pick up scopes added after the grant. https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3
fix(binjovi-mcp): grant the Claude client repository.create
All checks were successful
binjovi/ci Binjovi completed the frozen plan
e5ff383f3c
The MCP tool create_repository was refused with `forbidden` although the
control plane's BINJOVI_MCP_POLICY binding holds
binjovi.repository.create. MCP authorization is the INTERSECTION of that
binding and the token's own scope claim, and this client, which Ory
GitOps owns, never carried the scope. The binding alone can grant
nothing.

sean/seanfarm also registers this client_id, with the scope present, so
the two writers disagreed by exactly one scope and the last sync won.
That duplicate registrar is removed in sean/seanfarm; Binjovi names this
side the owner: "Ory GitOps owns these public clients and fixes their
sole audience to MCP" (Binjovi.MCPOAuth.Protocol).

binjovi.repository.create is a declared MCP scope:
Binjovi.Domain.MCPPermissions maps create_repository to it.

The guard moves 23 -> 24 and now names the scope, so a silent drop is a
red test rather than a forbidden tool call.

Claude-Session: https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3
binjovi-bot deleted branch fix/claude-client-repository-scope 2026-09-13 14:45:26 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sean/ory!120
No description provided.