Summary
A deployment using Claude mode still logs into GitHub Copilot at startup, stores and refreshes a Copilot token, and runs a background thread that polls api.github.com for the life of the server. Nothing in Claude mode will ever use that token. Reported by a user running Claude only:
WARNING - Storing the GitHub Copilot token under the default NBI_GH_ACCESS_TOKEN_PASSWORD. Set a per-user password in multi-tenant deployments.
INFO - Using existing GitHub access token
INFO - Refreshing GitHub token
Cause
The Copilot login is gated on using_github_copilot_service (notebook_intelligence/config.py:143-145):
def using_github_copilot_service(self) -> bool:
return self.chat_model.get("provider") == 'github-copilot' or \
self.inline_completion_model.get("provider") == 'github-copilot'
That reads the configured provider fields and nothing else. It does not ask whether an agent mode is active, and in Claude mode neither field necessarily describes what serves a request:
- Chat is served by the Claude participant, not
chat_model (ai_service_manager.py:199-204).
- Inline completion is served by Claude unless
claude_settings.inline_completion_model is inherit (ai_service_manager.py:202-233). When it is none or a Claude model id, the general inline model is discarded outright.
Both fields default to github-copilot (config.py:219, config.py:226), so a user who enables Claude mode without touching the General tab, which is the ordinary path, satisfies the gate on defaults alone.
The ordering makes it unconditional: update_models_from_config calls login_with_existing_credentials at ai_service_manager.py:169-171, before the is_claude_code_mode branch at :202 decides the Copilot inline model is not wanted.
Impact
More than log noise:
wait_for_tokens starts get_token_thread_func (github_copilot.py:630-645), a loop that lives for the server's lifetime and refreshes the token via GET {GH_REST_API_BASE_URL}/copilot_internal/v2/token (github_copilot.py:446) every TOKEN_FETCH_INTERVAL.
- An egress-restricted or air-gapped deployment running Claude only makes recurring calls to GitHub that its policy forbids and its operators did not ask for.
PRIVACY.md lists api.github.com under Copilot, which reads as conditional on using Copilot.
- The multi-tenant warning about
NBI_GH_ACCESS_TOKEN_PASSWORD fires in deployments that never use Copilot, so an admin is told to set a password for a service they do not run.
- A stored Copilot token is read and kept refreshed rather than left alone.
Suggested fix
Decide the gate from what will actually serve a request, not from the configured provider fields alone. In Claude mode that means Copilot is needed only when claude_settings.inline_completion_model == 'inherit' and the general inline model is Copilot; in ACP mode the same question applies to whatever that mode leaves to the native path.
The check lives on NBIConfig, which knows claude_settings and acp_settings already, so it can answer this without new plumbing. Worth confirming against the inline-completion inherit path rather than special-casing "an agent mode is on", which would break the user who deliberately runs Claude chat with Copilot autocomplete.
Also in the report, separately
The same log carries a CanUseToolShadowedWarning from claude_agent_sdk about can_use_tool not being invoked for the twelve mcp__nbi__ tools. That one is not a bug: NBI allowlists its own Jupyter UI tools on purpose (claude.py:2976-2978) so Claude does not ask permission for each cell edit, and the permission handler has no branch for those tools that the allowlist makes unreachable. It is new warning output from an unpinned claude-agent-sdk (pyproject.toml:67), and it will confuse anyone reading these logs. Worth suppressing that specific warning, or narrowing the entries, so it does not read as a permission bypass.
Environment
NBI main at 6333cd6. Reported against a Claude-only deployment on Windows.
Summary
A deployment using Claude mode still logs into GitHub Copilot at startup, stores and refreshes a Copilot token, and runs a background thread that polls
api.github.comfor the life of the server. Nothing in Claude mode will ever use that token. Reported by a user running Claude only:Cause
The Copilot login is gated on
using_github_copilot_service(notebook_intelligence/config.py:143-145):That reads the configured provider fields and nothing else. It does not ask whether an agent mode is active, and in Claude mode neither field necessarily describes what serves a request:
chat_model(ai_service_manager.py:199-204).claude_settings.inline_completion_modelisinherit(ai_service_manager.py:202-233). When it isnoneor a Claude model id, the general inline model is discarded outright.Both fields default to
github-copilot(config.py:219,config.py:226), so a user who enables Claude mode without touching the General tab, which is the ordinary path, satisfies the gate on defaults alone.The ordering makes it unconditional:
update_models_from_configcallslogin_with_existing_credentialsatai_service_manager.py:169-171, before theis_claude_code_modebranch at:202decides the Copilot inline model is not wanted.Impact
More than log noise:
wait_for_tokensstartsget_token_thread_func(github_copilot.py:630-645), a loop that lives for the server's lifetime and refreshes the token viaGET {GH_REST_API_BASE_URL}/copilot_internal/v2/token(github_copilot.py:446) everyTOKEN_FETCH_INTERVAL.PRIVACY.mdlistsapi.github.comunder Copilot, which reads as conditional on using Copilot.NBI_GH_ACCESS_TOKEN_PASSWORDfires in deployments that never use Copilot, so an admin is told to set a password for a service they do not run.Suggested fix
Decide the gate from what will actually serve a request, not from the configured provider fields alone. In Claude mode that means Copilot is needed only when
claude_settings.inline_completion_model == 'inherit'and the general inline model is Copilot; in ACP mode the same question applies to whatever that mode leaves to the native path.The check lives on
NBIConfig, which knowsclaude_settingsandacp_settingsalready, so it can answer this without new plumbing. Worth confirming against the inline-completion inherit path rather than special-casing "an agent mode is on", which would break the user who deliberately runs Claude chat with Copilot autocomplete.Also in the report, separately
The same log carries a
CanUseToolShadowedWarningfromclaude_agent_sdkaboutcan_use_toolnot being invoked for the twelvemcp__nbi__tools. That one is not a bug: NBI allowlists its own Jupyter UI tools on purpose (claude.py:2976-2978) so Claude does not ask permission for each cell edit, and the permission handler has no branch for those tools that the allowlist makes unreachable. It is new warning output from an unpinnedclaude-agent-sdk(pyproject.toml:67), and it will confuse anyone reading these logs. Worth suppressing that specific warning, or narrowing the entries, so it does not read as a permission bypass.Environment
NBI
mainat6333cd6. Reported against a Claude-only deployment on Windows.