feat(jupyter): forward extra HTTP headers and honor an explicit token - #13
Merged
echarles merged 1 commit intoJul 29, 2026
Merged
Conversation
JupyterSandbox could only authenticate to an external Jupyter Server with a token. Deployments that authenticate with a password have no token: the credentials are a session Cookie plus a matching X-XSRFToken header, and there was no way to pass those through the sandbox. Two things blocked it: - Headers could not reach the requests. `client_kwargs` only reaches the kernel client's websocket, while the kernel manager sends its own HTTP requests (kernel GET/POST/DELETE, interrupt) using its `headers` argument, which nothing populated. `**kwargs` were collected into `_extra_kwargs` and never forwarded. - `token=None` was replaced by `uuid.uuid4().hex`, putting a credential the server never issued on every request. A generated token is only meaningful for a server this sandbox starts itself, where it becomes --ServerApp.token. So add a `headers` argument, forwarded to the kernel client, to the readiness and interrupt requests, and to the JupyterServerClient used for kernel reuse. Only pass it on when the caller supplied headers, so the common token/anonymous path is unchanged. And keep an explicit token as given — including None — when talking to a server the sandbox does not own. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 tasks
echarles
approved these changes
Jul 29, 2026
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
JupyterSandboxcan only authenticate to an external Jupyter Server with a token. Deployments that authenticate with a password have no token — the credentials are a sessionCookieplus a matchingX-XSRFTokenheader — and there is currently no way to pass those through the sandbox.This came up while routing jupyter-mcp-server's password-authentication support (#250) through
code-sandboxesafter theenh: code sandboxesrefactor. It is the same gap that jupyter-nbmodel-client#61 closed for the notebook websocket (additional_headers), now on the kernel side.The two blockers
client_kwargsonly reaches the kernel client's websocket. The kernel manager makes its own HTTP requests (kernelGET/POST/DELETE, interrupt) using itsheadersargument, which nothing populated — and**kwargswere collected into_extra_kwargsand never forwarded to the client.token=Nonewas overwritten.self._token = token or uuid.uuid4().hexput a credential the server never issued on every request, so cookie-authenticated requests were rejected. A generated token is only meaningful for a server the sandbox starts itself, where it becomes--ServerApp.token.Changes
headersargument toJupyterSandbox, forwarded to:/api/statusreadiness poll,/api/kernels/{id}/interruptrequest,JupyterServerClientused to find a reusable kernel.headerson when the caller supplied some, so the call is unchanged for the common token/anonymous path.tokenas given — includingNone— when talking to a server the sandbox does not own; still generate one for a server it starts itself.Tests
Four tests added to
tests/test_jupyter.py, in the existing stub style: headers reach the kernel client; noheaderskwarg when none supplied (and the token is passed through); an external server keepstoken=None; a sandbox-owned server still generates a token.pytest -q→ 108 passed, 2 skipped.ruff checkon the changed files is clean (the 8 pre-existingA003findings elsewhere are untouched).Compatibility
Backwards compatible:
headersdefaults toNoneand is omitted from the client call when unset. The token change only affects the case where a caller explicitly passed no token and supplied aserver_url— previously that silently sent a random token, which could not have authenticated anywhere.🤖 Generated with Claude Code