feat(mcp): expose --hub-scope and default Hub scope to include Hub identity #101

Merged
David merged 1 commit from feat/YT-44-hub-scope-install into main 2026-07-05 19:35:41 +02:00
Owner

What

Fixes the Hub OAuth scope so a stock yt mcp serve install can actually complete login and call YouTrack, and exposes the scope through the installer. Implements YT-44.

Previously the server requested only offline_access from Hub. The callback's Hub identity fetch (<issuer>/api/rest/users/me) then failed with 401 invalid_token: The operation requires the "0-0-0-0-0" scope, but the token is granted for [youtrack-mcp], and the per-user YouTrack REST calls (which reuse the Hub token, web/mod.rs) would 401 the same way for lack of the instance's YouTrack service id. yt mcp service install had no --hub-scope, so the only fix was hand-editing mcp.env.

Changes

  • New youtrack_mcp::DEFAULT_HUB_SCOPE = "0-0-0-0-0 offline_access" (Hub's universal identity service id + refresh). Both the runtime HubOAuthConfig::from_env fallback and the installer default read this one constant so they cannot drift.
  • yt mcp service install gains --hub-scope <SCOPE> (space-separated), always baked into the env file as YOUTRACK_MCP_HUB_SCOPE.
  • Because the YouTrack service id is instance-specific and cannot be defaulted, install prints a warning when the resolved scope has no YouTrack service id, pointing at Hub -> Services -> YouTrack (<instance>/hub/services/<id>).
  • .env.example updated to the new default and the service-id guidance.
  • Install subcommand variant boxed to satisfy clippy::large_enum_variant after InstallArgs grew (clap accepts Box<InstallArgs>; the call site relies on deref coercion).

Invariant sweep (default Hub scope must include Hub identity + be single-sourced)

Site Classification
oauth_hub.rs DEFAULT_HUB_SCOPE (runtime default) fixed: now 0-0-0-0-0 offline_access
oauth_hub.rs from_env fallback fixed: reads DEFAULT_HUB_SCOPE
service.rs resolve_hub_scope / render_env fixed: added, reads the shared constant
.env.example scope entry fixed: updated default + guidance
oauth_as.rs ADVERTISED_SCOPE N/A: downstream MCP-resource scope advertised to Claude (app-issued bearer), a different OAuth relationship from upstream Hub service-id scopes
oauth_hub.rs test token bodies echoing offline_access N/A: mock Hub responses (what Hub granted), not the default request
docs/deployment.md scope rows tracked in YT-45 (linked docs rewrite), not edited here to avoid overlapping that PR

Tests

  • default_hub_scope_covers_identity_and_refresh (oauth_hub)
  • hub_scope_defaults_to_shared_constant_and_overrides, youtrack_service_id_detection_drives_the_warning, and a YOUTRACK_MCP_HUB_SCOPE= assertion in the env render test (installer)
  • Full just pre-commit green (fmt, clippy -D warnings, build, 82 tests).

Related: YT-42 (DB dir, already merged), YT-45 (docs rewrite).

## What Fixes the Hub OAuth scope so a stock `yt mcp serve` install can actually complete login and call YouTrack, and exposes the scope through the installer. Implements YT-44. Previously the server requested only `offline_access` from Hub. The callback's Hub identity fetch (`<issuer>/api/rest/users/me`) then failed with `401 invalid_token: The operation requires the "0-0-0-0-0" scope, but the token is granted for [youtrack-mcp]`, and the per-user YouTrack REST calls (which reuse the Hub token, `web/mod.rs`) would 401 the same way for lack of the instance's YouTrack service id. `yt mcp service install` had no `--hub-scope`, so the only fix was hand-editing `mcp.env`. ## Changes - New `youtrack_mcp::DEFAULT_HUB_SCOPE = "0-0-0-0-0 offline_access"` (Hub's universal identity service id + refresh). Both the runtime `HubOAuthConfig::from_env` fallback and the installer default read this one constant so they cannot drift. - `yt mcp service install` gains `--hub-scope <SCOPE>` (space-separated), always baked into the env file as `YOUTRACK_MCP_HUB_SCOPE`. - Because the YouTrack service id is instance-specific and cannot be defaulted, install prints a warning when the resolved scope has no YouTrack service id, pointing at Hub -> Services -> YouTrack (`<instance>/hub/services/<id>`). - `.env.example` updated to the new default and the service-id guidance. - `Install` subcommand variant boxed to satisfy `clippy::large_enum_variant` after `InstallArgs` grew (clap accepts `Box<InstallArgs>`; the call site relies on deref coercion). ## Invariant sweep (default Hub scope must include Hub identity + be single-sourced) | Site | Classification | | --- | --- | | `oauth_hub.rs` `DEFAULT_HUB_SCOPE` (runtime default) | fixed: now `0-0-0-0-0 offline_access` | | `oauth_hub.rs` `from_env` fallback | fixed: reads `DEFAULT_HUB_SCOPE` | | `service.rs` `resolve_hub_scope` / `render_env` | fixed: added, reads the shared constant | | `.env.example` scope entry | fixed: updated default + guidance | | `oauth_as.rs` `ADVERTISED_SCOPE` | N/A: downstream MCP-resource scope advertised to Claude (app-issued bearer), a different OAuth relationship from upstream Hub service-id scopes | | `oauth_hub.rs` test token bodies echoing `offline_access` | N/A: mock Hub responses (what Hub granted), not the default request | | `docs/deployment.md` scope rows | tracked in YT-45 (linked docs rewrite), not edited here to avoid overlapping that PR | ## Tests - `default_hub_scope_covers_identity_and_refresh` (oauth_hub) - `hub_scope_defaults_to_shared_constant_and_overrides`, `youtrack_service_id_detection_drives_the_warning`, and a `YOUTRACK_MCP_HUB_SCOPE=` assertion in the env render test (installer) - Full `just pre-commit` green (fmt, clippy `-D warnings`, build, 82 tests). Related: YT-42 (DB dir, already merged), YT-45 (docs rewrite).
feat(mcp): expose --hub-scope and default Hub scope to include identity
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 38s
Create release / Create release from merged PR (pull_request) Has been skipped
90dfa18ccf
The Hub OAuth flow requested only `offline_access`, so the callback's Hub identity fetch (`users/me`) failed with a 401 requiring the `0-0-0-0-0` scope, and `yt mcp service install` gave no supported way to set the scope short of hand-editing `mcp.env`. A stock install therefore authenticated against Hub but could not read the user identity or call YouTrack.

Change the shared default to `0-0-0-0-0 offline_access` (Hub's universal identity service id plus refresh) via a new `youtrack_mcp::DEFAULT_HUB_SCOPE`, used both by the runtime `HubOAuthConfig::from_env` fallback and by the installer so the two never drift. Add `--hub-scope` to `yt mcp service install`, always baking `YOUTRACK_MCP_HUB_SCOPE` into the env file. Because the YouTrack service id is instance-specific and cannot be defaulted, install warns when the resolved scope carries no YouTrack service id, pointing at Hub Services to find it.

Box the `Install` subcommand variant so the enlarged `InstallArgs` does not trip `clippy::large_enum_variant`.

Docs updates (deployment.md rewrite) are tracked in YT-45; the DB-dir fix this builds on is YT-42.

#YT-44

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David scheduled this pull request to auto merge when all checks succeed 2026-07-05 19:35:37 +02:00
David merged commit 47a6700935 into main 2026-07-05 19:35:41 +02:00
David deleted branch feat/YT-44-hub-scope-install 2026-07-05 19:35:42 +02: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
pandoras-box/youtrack-cli!101
No description provided.