Conversation
Add TokenScopeDetector service that calls /api/v4/personal_access_tokens/self at startup to discover token scopes. Based on detected scopes: - Skip GraphQL introspection when token lacks api/read_api (no 401 stack traces) - Register only tools matching the token's permissions - Show clean INFO messages with actionable fix URLs - Warn when token expires within 7 days Also adds comprehensive authentication documentation (docs/guide/authentication.md) with PAT and OAuth walkthroughs, scope comparison table, and troubleshooting. Links added across all installation/deployment docs.
Test Coverage ReportOverall Coverage: 93.73%
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Adds startup-time GitLab token scope detection to avoid noisy GraphQL 401 stack traces and to register only tools permitted by the token, along with new/updated authentication documentation.
Changes:
- Introduce
TokenScopeDetectorto detect scopes via/api/v4/personal_access_tokens/self, compute capabilities, and emit user-friendly startup logs (incl. expiry warnings). - Update
ConnectionManagerto detect scopes before GraphQL introspection, skip introspection when GraphQL scopes are missing, and add a REST-based version fallback. - Filter tool registration in
RegistryManagerbased on detected token scopes; add/refresh docs linking to a new authentication guide.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/services/TokenScopeDetector.test.ts | Adds unit coverage for scope detection, tool filtering helpers, URL generation, and logging behavior. |
| tests/unit/RegistryManager.test.ts | Updates mocks to accommodate getTokenScopeInfo() and scope filtering. |
| src/services/TokenScopeDetector.ts | Implements token self-introspection, scope→tool mapping, and startup logging helpers. |
| src/services/ConnectionManager.ts | Runs scope detection before introspection; skips GraphQL when not allowed; adds REST version fallback and exposes token scope info. |
| src/registry-manager.ts | Applies scope-based filtering when building the available tool list. |
| docs/troubleshooting/connection.md | Links scope troubleshooting to the new authentication guide. |
| docs/index.md | Points token creation guidance to the authentication guide. |
| docs/guides/team-onboarding.md | Links team onboarding token requirements to the authentication guide. |
| docs/guide/quick-start.md | Adds a pointer to the authentication guide for first-time setup. |
| docs/guide/installation/npm.md | Links GITLAB_TOKEN requirements to the authentication guide. |
| docs/guide/installation/manual.md | Adds authentication guide link for token creation steps. |
| docs/guide/installation/index.md | Links prerequisite PAT guidance to the authentication guide. |
| docs/guide/installation/docker.md | Links PAT/scopes guidance to the authentication guide. |
| docs/guide/installation/claude-desktop.md | Links PAT requirement to the authentication guide. |
| docs/guide/configuration.md | Links GITLAB_TOKEN configuration details to the authentication guide. |
| docs/guide/authentication.md | Adds comprehensive PAT + OAuth authentication walkthrough and scope comparison/troubleshooting. |
| docs/deployment/local-stdio.md | Links PAT requirement to the authentication guide. |
| docs/deployment/docker-standalone.md | Links PAT requirement to the authentication guide. |
| docs/.vitepress/config.mts | Adds “Authentication” to the docs navigation. |
- Replace global fetch with enhancedFetch in detectTokenScopes and detectVersionViaREST (respects timeouts, proxy, TLS settings) - Parse expires_at as UTC date to avoid timezone off-by-one errors - Use URL/URLSearchParams for proper encoding in getTokenCreationUrl - Derive totalTools count dynamically from getToolScopeRequirements() - Use jest.useFakeTimers() for deterministic expiry tests - Test project/group token types, REST fallback, scope-gated paths
- Cache getTokenCreationUrl result in logTokenScopeInfo to avoid double URL construction - Replace `as any` with proper GitLabScope[] type in test fixtures
…xt from scope map Replace unsafe `as` type cast on /personal_access_tokens/self response with Zod schema validation via safeParse(). Returns null with a debug log if the response shape doesn't match expectations. Remove manage_context from TOOL_SCOPE_REQUIREMENTS since it manages local session state and never calls GitLab API — it's always available.
…oc counts - getToolScopeRequirements() now returns deep clone (arrays copied too) - getTokenCreationUrl() preserves subpath for self-hosted instances - Documentation updated: 43 scope-gated tools, read_user=2, read_api=23 - Example log output fixed to show URL-encoded comma (%2C)
… type heuristic - Replace unsafe `as GitLabScope[]` cast with z.enum validation that filters unknown scopes (future GitLab scopes are silently ignored) - Derive GitLabScope type from const array via z.infer - Remove token type detection by name prefix — token names are user-controlled and cannot reliably determine token type
…ope filter test - Log message now says "scope-gated tools" to clarify count excludes always-available tools like manage_context - tokenType defaults to "unknown" since type cannot be reliably inferred - Add RegistryManager unit test verifying scope-based tool filtering
…te docs - getTokenCreationUrl() now catches URL parse errors for schemeless baseUrl and falls back to string concatenation - daysUntilExpiry === 0 now logs "expires today" instead of "has expired" - Troubleshooting docs updated to match current log format
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
sw-release-bot bot
pushed a commit
that referenced
this pull request
Jan 25, 2026
## [6.42.0](v6.41.4...v6.42.0) (2026-01-25) ### Features * Token scope detection at startup with graceful degradation ([#190](#190)) ([28bab03](28bab03)), closes [#188](#188) ### Bug Fixes * **workitems:** use two-step approach for timeEstimate on create ([#195](#195)) ([98abf4e](98abf4e)), closes [#193](#193)
|
🎉 This PR is included in version 6.42.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
polaz
pushed a commit
that referenced
this pull request
Jan 25, 2026
## [6.42.0](v6.41.4...v6.42.0) (2026-01-25) ### Features * Token scope detection at startup with graceful degradation ([#190](#190)) ([28bab03](28bab03)), closes [#188](#188) ### Bug Fixes * **workitems:** use two-step approach for timeEstimate on create ([#195](#195)) ([98abf4e](98abf4e)), closes [#193](#193)
polaz
added a commit
that referenced
this pull request
Jan 25, 2026
…ck aggregation (#196) * feat(logging): Implement condensed access log format with request stack aggregation Implements single-line access log format inspired by nginx/envoy. Instead of multiple log lines per request, all events are aggregated into a single condensed entry when the request completes. Features: - Request stack aggregation: collect tool, action, GitLab response timing - Single-line access log format with timestamp, client, session, method, path, status, duration, tool/action, GitLab status/duration, and details - Connection tracking: log session stats (requests, tools, errors) on close - AsyncLocalStorage for transparent context propagation through async calls - LOG_FORMAT environment variable: "condensed" (default) or "verbose" Access log format: [timestamp] ip session method path status duration | tool action | gitlab_status gitlab_duration | details Connection close format: [timestamp] CONN_CLOSE ip session duration reason | reqs=N tools=N errs=N Closes #194 * chore(release): 6.42.0 [skip ci] ## [6.42.0](v6.41.4...v6.42.0) (2026-01-25) ### Features * Token scope detection at startup with graceful degradation ([#190](#190)) ([28bab03](28bab03)), closes [#188](#188) ### Bug Fixes * **workitems:** use two-step approach for timeEstimate on create ([#195](#195)) ([98abf4e](98abf4e)), closes [#193](#193) * fix(logging): nginx-style alignment, quote escaping, dedupe LogFormat type - All fields always present with "-" for missing values (nginx-style alignment) - Escape quotes and backslashes in log values for safe parsing - Import LogFormat from logging/types.ts to avoid type duplication - Add comments explaining close handler behavior for new sessions - Fix flaky test assertion using regex for duration matching * test(config): add LOG_FORMAT parsing tests Add unit tests for LOG_FORMAT environment variable parsing: - Default to 'condensed' when not set - Parse 'verbose' value - Case-insensitive parsing - Invalid values default to 'condensed' * feat(logging): add context and read-only mode to access logs - Add ctx (context path) and ro (read-only) fields to access log format - Capture session context and read-only state during tool execution - Fix SSE close handling: distinguish normal disconnect from abort - Fix session request counting: count initial request on session init - Update session ID on stack after session initialization - Escape newlines/tabs in log values to maintain single-line format - Add needsQuoting helper to detect control characters Access log format now: [ts] ip session ctx ro method path status dur | tool action | GL:status dur | details * test(logging): add addDetailsForCurrentRequest context-aware test * fix(logging): gate per-request INFO logs behind LOG_FORMAT=verbose In condensed mode, suppress individual "Tool called", "Connection verified", "Executing tool" INFO logs - these are aggregated into single access log line. Also fix ConnectionTracker.closeConnection to return early when disabled. * fix(logging): add request tracking to SSE /messages endpoint - Gate access log tracking in handlers.ts behind LOG_FORMAT=condensed - Add request tracking to SSE /messages handler: - Call connectionTracker.incrementRequests for each request - Update request stack with SSE session ID (query param vs header) - Wrap handlePostMessage in runWithRequestContextAsync * fix(logging): move access log middleware before rate limiter - Register access logging before rate limiter to capture 429 responses - Update docstring format to include ctx and ro fields - Correct comment about session ID availability in first request * fix(server): use optional chaining for res.locals in SSE handler Prevents TypeError when res.locals is undefined in test mocks. * fix(logging): cleanup connections on close even when tracking disabled Prevents memory leak when setEnabled(false) is called while connections are still open - connections are now always deleted from the map. * fix(logging): add session tracking for SSE /sse endpoint - Update request stack with SSE session ID for access log - Count initial GET /sse request in connection stats --------- Co-authored-by: semantic-release-bot <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TokenScopeDetectorservice that calls/api/v4/personal_access_tokens/selfat startup to discover token scopes before attempting GraphQL introspectionapi/read_apiscope — eliminates ugly 401 stack tracesRegistryManagerdocs/guide/authentication.mdwith PAT + OAuth walkthroughs, scope comparison table, and troubleshootingCloses #188
Test plan
TokenScopeDetector(detection, scope filtering, expiry, edge cases)RegistryManager.test.tsmock for newgetTokenScopeInfo()apiscope token (brief message, all tools)read_user-only token (limited message, 3 tools, no stack traces)