JupyterLab extension that opens an OpenAI Codex chat in the left sidebar.
Project description
jupyterlab-codex
A JupyterLab 4 extension that adds an OpenAI Codex chat panel to the left sidebar. The OpenAI mark serves as the sidebar tab icon.
Scope: notebook copilot, not project agent
This is a deliberately small tool. The agent can only:
- list variables in your notebook kernel
- describe a pandas DataFrame
- run short Python snippets in the kernel
- read the source / outputs of a selected cell or a cell by execution count
- insert (and optionally run) a new code cell below the active one
That's the complete tool surface. No filesystem read/write. No shell. No MCP. No project-wide context. The agent sees what's in your kernel and your active notebook — nothing else.
This is a feature, not a limitation. Claude Code, Cursor, and the Codex
CLI itself are excellent project agents — they edit files, run tests,
manage git. Use them for that. jupyterlab-codex is for the inner
loop inside a notebook: explain this cell, evaluate this model,
describe this DataFrame, write the next cell. It runs where your
exploratory work already lives, with zero context-switch and a live
view of kernel state.
If you find yourself wishing it could grep your repo, that's a sign you want a different tool — and you almost certainly already have one open in another window.
Authentication uses the user's ChatGPT subscription via OAuth (the same
flow the Codex CLI uses). Tokens live in localStorage and are
forwarded to a small proxy that talks to the Codex Responses API.
Tested in JupyterLab 4 and JupyterLite. Notebook 7 should work — file an issue if it doesn't.
Two deployment modes
The extension needs a proxy because both
chatgpt.com/backend-api/codex/responses and auth.openai.com reject
browser fetches from arbitrary origins. Where the proxy lives depends on
how you're running JupyterLab.
Classic JupyterLab — bundled, no external infra
pip install jupyter-codex ships a Tornado server extension that
proxies to OpenAI from same-origin. The browser fetches /codex and
/codex/device/... on whichever host the Jupyter server is exposed on
— no CORS, no third-party services.
pip install jupyter-codex # (or pip install -e . for dev)
jupyter lab
Open the OpenAI mark in the left sidebar and click Sign in with ChatGPT. That's it.
| Setting | Default | Notes |
|---|---|---|
codexProxyUrl |
codex |
Resolved against the Jupyter base URL → <base>/codex |
codexModel |
gpt-5.3-codex |
Must be a model your ChatGPT account is entitled to |
The bundled handlers live under the Jupyter base_url:
POST <base>/codex → forwards Responses API call upstream
POST <base>/codex/device/start → request a device code
POST <base>/codex/device/poll → poll for completion + token exchange
POST <base>/codex/refresh → rotate the refresh_token
JupyterLite — external proxy required
In JupyterLite there is no Python server (the kernel runs in
Pyodide, the page is static). The Python package isn't installed, so
the bundled proxy can't run. Host a proxy out-of-band and override
codexProxyUrl to point at it.
The bundled jupyter_codex/handlers.py is itself a reference
implementation — port it to whichever runtime you prefer (Netlify
Function, Cloudflare Worker, AWS Lambda, a small FastAPI service). Any
proxy works as long as it exposes the same four routes:
POST <proxy> → forwards Responses API call upstream
POST <proxy>/device/start
POST <proxy>/device/poll
POST <proxy>/refresh
Then in Settings → jupyterlab-codex set
codexProxyUrl to the proxy URL (e.g. /.netlify/functions/codex if
the proxy is co-hosted on the lite site, or
https://my-proxy.example.com/codex if it's elsewhere).
URL resolution
codexProxyUrl is interpreted as follows:
| Value | Resolved to |
|---|---|
"codex" (default) |
<jupyter-base-url>/codex |
"/foo/bar" |
/foo/bar (absolute path on current origin) |
"https://x.com/y" |
used as-is |
"" |
<jupyter-base-url>/codex |
This lets the same default work in classic Lab (hits the bundled server
extension), single-user JupyterHub (<base> becomes /user/<name>/),
and JupyterLite (user picks an absolute path or a full URL).
Install for development
pip install -e ".[dev]"
jupyter labextension develop . --overwrite
jlpm build
jlpm watch rebuilds the labextension on change; refresh Lab to pick up
TypeScript changes.
Project layout
src/
index.ts # plugin entry point — registers the sidebar
widget.ts # Lumino widget — chat UI + sign-in flow
icon.ts # OpenAI mark for the sidebar tab
kernelRpc.ts # run-Python + sentinel-parsing primitive
agent/
client.ts # tool-use loop driver
codexAuth.ts # localStorage auth + device-code login
codexProvider.ts # Codex Responses API client (Anthropic-shape adapter)
proxyUrl.ts # codexProxyUrl resolution helper
tools.ts # kernel-side tool schemas + Python prelude
toolRunner.ts # injects prelude, parses sentinel-wrapped JSON
frontendTools.ts # browser-side tools (insert_cell, get_cell_context, …)
transcriptStore.ts # persists chat to .jupyterlab-codex-chat.json
markdown.ts # tiny markdown → HTML renderer
schema/plugin.json # ISettingRegistry schema
style/ # sidebar + chat CSS, OpenAI SVG mark
jupyter_codex/ # Python distribution name on PyPI: jupyter-codex
__init__.py # labextension paths + server-extension entry points
handlers.py # Tornado proxy: /codex, /codex/device/*, /codex/refresh
jupyter-config/ # auto-enables the server extension on install
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jupyter_codex-0.1.1.tar.gz.
File metadata
- Download URL: jupyter_codex-0.1.1.tar.gz
- Upload date:
- Size: 113.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8156b0771f471bf2bc715d010a9f5d336109e7c8662c8f095b9d68c368fd2666
|
|
| MD5 |
76ec625b89b6c2df3e95daa8078b2b31
|
|
| BLAKE2b-256 |
f2a33fb5f4d1f160faab1322b6e1166134a62cecc8fb99f7b552c1bf1465b4f0
|
File details
Details for the file jupyter_codex-0.1.1-py3-none-any.whl.
File metadata
- Download URL: jupyter_codex-0.1.1-py3-none-any.whl
- Upload date:
- Size: 35.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
461130b822e85b72b99b888bc15577548e99d3487089a42dbb87c2bdea9e633c
|
|
| MD5 |
87f7a9afd239f3c9e7b4764a3ff80d4d
|
|
| BLAKE2b-256 |
84a162924a5b09ccba2c81b306b0f9c24f00cafa87a1148069fb9ea4135b4932
|