fix(binjovi-mcp): the OAuth service must know every MCP scope #122

Merged
binjovi-bot merged 1 commit from fix/mcp-oauth-image-carries-repo-scope into trunk 2026-09-13 15:29:01 +00:00
Owner

create_repository could not be used even after #120 put
binjovi.repository.create on the client and the control plane policy granted
it. Every authorize request naming the scope answered
{"error":"invalid_request"}.

The cause

Binjovi.MCPOAuth.Protocol builds @scopes at compile time from
Binjovi.Domain.MCPPermissions.scopes(), and the authorize endpoint refuses any
request naming a scope outside it. This image decides which scopes can ever be
granted.
The control plane at binjovi.sean.farm decides which tools exist.
They are pinned in different repositories, and nothing keeps them in step.

This Deployment ran sha-833fc189660f, which predates create_repository:

$ git show 833fc189660f:apps/binjovi_domain/lib/binjovi/domain/mcp_permissions.ex | grep -c create_repository
0

So the MCP tool was advertised by a current control plane, while the OAuth
service would never grant its scope. The symptom read as a stale token or a wrong
client scope. It was neither.

Measured, not guessed

The same authorize request was replayed against the live endpoint, varying only
the scope:

scope set HTTP
20 scopes + offline_access 302
the same + binjovi.repository.create 400 {"error":"invalid_request"}

The fix

Bumped to sha-57d0ab2ab4be, the revision the control plane runs.

The structural hazard

Two Deployments of one codebase, pinned independently, where one validates the
vocabulary the other advertises. A new MCP tool ships in sean/binjovi, and this
pin silently makes it unusable — with an error that names nothing. The comment on
the pin records this and says to bump it when a tool ships, but a comment is the
weakest possible guard. Worth a follow-up: either derive the scope list at
runtime, or gate a tool's release on the OAuth service advertising its scope.

tests/check.sh green, tests/binjovi-mcp-oauth-service.sh included.

https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3

`create_repository` could not be used even after #120 put `binjovi.repository.create` on the client and the control plane policy granted it. Every authorize request naming the scope answered `{"error":"invalid_request"}`. ## The cause `Binjovi.MCPOAuth.Protocol` builds `@scopes` at **compile time** from `Binjovi.Domain.MCPPermissions.scopes()`, and the authorize endpoint refuses any request naming a scope outside it. **This image decides which scopes can ever be granted.** The control plane at `binjovi.sean.farm` decides which tools exist. They are pinned in different repositories, and nothing keeps them in step. This Deployment ran `sha-833fc189660f`, which predates `create_repository`: ``` $ git show 833fc189660f:apps/binjovi_domain/lib/binjovi/domain/mcp_permissions.ex | grep -c create_repository 0 ``` So the MCP tool was advertised by a current control plane, while the OAuth service would never grant its scope. The symptom read as a stale token or a wrong client scope. It was neither. ## Measured, not guessed The same authorize request was replayed against the live endpoint, varying only the scope: | scope set | HTTP | |---|---| | 20 scopes + `offline_access` | **302** | | the same + `binjovi.repository.create` | **400** `{"error":"invalid_request"}` | ## The fix Bumped to `sha-57d0ab2ab4be`, the revision the control plane runs. ## The structural hazard Two Deployments of one codebase, pinned independently, where one validates the vocabulary the other advertises. A new MCP tool ships in `sean/binjovi`, and this pin silently makes it unusable — with an error that names nothing. The comment on the pin records this and says to bump it when a tool ships, but a comment is the weakest possible guard. Worth a follow-up: either derive the scope list at runtime, or gate a tool's release on the OAuth service advertising its scope. `tests/check.sh` green, `tests/binjovi-mcp-oauth-service.sh` included. https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3
fix(binjovi-mcp): the OAuth service must know every MCP scope
All checks were successful
binjovi/ci Binjovi completed the frozen plan
01c6bdeb9d
Every authorize request naming binjovi.repository.create answered
{"error":"invalid_request"}, so create_repository could never be used
even with the scope on the client and the permission in the control
plane policy.

Binjovi.MCPOAuth.Protocol builds @scopes at COMPILE time from
Binjovi.Domain.MCPPermissions.scopes(), and refuses any request naming a
scope outside it. This Deployment was pinned to sha-833fc189660f, which
predates create_repository: that revision has no occurrence of the tool
at all. So the control plane advertised a tool whose scope this service
would never grant.

Measured, not guessed. The same authorize request was replayed against
the live endpoint twice: without the scope it answers 302, with it 400.

Bumped to sha-57d0ab2ab4be, the revision the control plane runs.

The two images are pinned in different repositories and nothing keeps
them in step; the comment says so, and says to bump this when a new MCP
tool ships.

Claude-Session: https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3
binjovi-bot deleted branch fix/mcp-oauth-image-carries-repo-scope 2026-09-13 15:29:01 +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!122
No description provided.