jupyter-mynerva
A JupyterLab extension that provides an LLM-powered assistant with deep understanding of notebook structure.
Why "Mynerva"?
The name derives from Minerva, the Roman goddess of wisdom. The spelling reflects "My + Minerva"—a personalized, notebook-centric intelligence.
As Jupyter reinterprets Jupiter, Mynerva reinterprets Minerva as an AI companion for computational notebooks.
Concept: The Story Travels with the Code
A notebook is a computational narrative. Headings, code, and outputs form a coherent story. Existing AI assistants see only isolated snippets—the surrounding context gets lost.
jupyter-mynerva keeps the story intact. Section structure, explanatory markdown, and outputs travel together as logical units.
The LLM actively explores—requesting the table of contents, navigating sections, examining outputs. It pulls the context it needs, rather than waiting for users to push fragments.
Demo
Exploring a Notebook
Adding Code
Architecture
┌─────────────────────────────────────────────────────────┐
│ JupyterLab │
│ ┌─────────────────────┐ ┌──────────────────────────┐ │
│ │ Active Notebook │ │ Mynerva Panel (React) │ │
│ │ │ │ - Chat UI │ │
│ │ │ │ - Action confirmation │ │
│ └─────────────────────┘ └────────────┬─────────────┘ │
│ │ │
│ ┌─────────────────────────────────────▼─────────────┐ │
│ │ Context Engine (TypeScript) │ │
│ │ - Notebook mutation handlers │ │
│ │ - nblibram client (query delegation) │ │
│ └─────────────────────────────────────┬─────────────┘ │
└────────────────────────────────────────┼────────────────┘
│ REST
┌────────────────────────────────────────▼────────────────┐
│ Server Extension (Python) │
│ - LLM proxy (OpenAI / Anthropic / Bedrock / Enki Gate) │
│ - nblibram CLI proxy (query execution) │
│ - Session storage (.mynerva files) │
│ - Enki Gate device flow authentication │
└─────────────────┬───────────────────────────────────────┘
│ subprocess
┌─────────────────▼───────────────────────────────────────┐
│ nblibram (Go binary) │
│ - ToC / Section / Cell / Output extraction │
│ - Privacy filter (gitleaks-based) │
└─────────────────────────────────────────────────────────┘
Mynerva Panel: Chat interface in the right sidebar. Operates on the currently open notebook.
Context Engine: Handles notebook mutations (insert, update, delete cells) and delegates read queries (ToC, sections, cells, outputs) to nblibram via the server extension.
Server Extension: Proxies LLM API (no streaming), invokes nblibram CLI for notebook queries, and handles Enki Gate device flow authentication. Sessions persisted in .mynerva/ directory.
nblibram: External Go binary that extracts notebook structure—ToC from headings, sections as markdown+code units, outputs with type awareness. Also applies privacy filters based on gitleaks rules (configured via NBLIBRAM_GITLEAKS_CONFIG environment variable).
Design Decisions
| Topic | Decision |
|---|---|
| Context extraction | Delegated to nblibram CLI (Go binary, unifies nbq + nbfilter) |
| Privacy filter | Handled by nblibram, gitleaks-based rules (NBLIBRAM_GITLEAKS_CONFIG env) |
| Session storage | .mynerva/sessions/*.mnchat files, auto-named (timestamp + ID) |
| Session lifecycle | Independent of active notebook; explicit switch only |
| Streaming | Not supported |
| File access | Jupyter root directory only |
| Action confirmation | Batch confirmation supported; per-notebook auto-approval available |
| Mutation validation | Optimistic locking via _hash; must read before write |
| Error handling | API failure: retry with limit (3). Hash mismatch / user rejection: feedback to LLM, no retry count |
| LLM providers | OpenAI (+ compatible endpoints), Anthropic, Amazon Bedrock (Converse API), Enki Gate (device flow) |
UI
Panel (right sidebar):
- Session selector (dropdown + new)
- Chat messages
- Query preview (inline, with filter option)
- Mutate preview (modal, diff view)
- Trust mode toggle
Settings (gear icon in panel header):
- Provider selection
- Model selection
- API key (Fernet-encrypted if secret key present)
- Enki Gate connection (device flow authentication)
Actions
LLM communicates through structured actions. All actions require user confirmation.
Query actions show a preview before sending to LLM. Users can choose to apply privacy filters (nbfilter-style masking of IPs, domains, etc.) or send raw content.
Query: Active Notebook
| Action | Parameters | Description |
|---|---|---|
getToc |
— | Heading structure of current notebook |
getSection |
query |
Cells under matched heading |
getCells |
query, count? |
Cell range from matched position |
getOutput |
query |
Output of matched cell |
Query: Other Files
| Action | Parameters | Description |
|---|---|---|
listNotebookFiles |
path? |
List notebooks in directory |
getTocFromFile |
path |
Heading structure of specified notebook |
getSectionFromFile |
path, query |
Cells under matched heading |
getCellsFromFile |
path, query, count? |
Cell range from matched position |
getOutputFromFile |
path, query |
Output of matched cell |
Query: Indexed Notebooks
When nbsearch is configured for the Jupyter server, Mynerva can search the nbsearch jupyter-notebook index and then read matched notebook payloads through nblibram. The panel's Privacy filter checkbox controls whether those cells are filtered before sharing; filtering is enabled by default.
| Action | Parameters | Description |
|---|---|---|
searchNotebooks |
query, focus, sort?, limit? |
Search indexed notebooks and return summaries |
summaryCellsFromSearch |
referenceId, focus |
Summarize cells and identify cell ranges |
getCellsFromSearch |
referenceId, start?, limit? |
Read cells from a search result |
searchNotebooks.query is a Solr/Lucene query string sent to nbsearch. For example, use filename:*Search* to find notebooks whose filename contains Search, or *:* with sort: "mtime desc" to fetch recently modified notebooks. Search results return metadata such as filename, timestamps, summaries, and references to summaryCellsFromSearch. Use summaryCellsFromSearch to locate relevant cell ranges in a large notebook, then use getCellsFromSearch to fetch the necessary raw cells under the approved Privacy filter setting.
Query Syntax
{ "match": "## Data" } // regex against heading/content
{ "contains": "pandas" } // substring match
{ "start": 5 } // cell index
{ "id": "abc123" } // cell ID
Mutate: Active Notebook
| Action | Parameters | Description |
|---|---|---|
insertCell |
position, cellType, source |
Insert new cell |
updateCell |
query, source, _hash |
Update cell content |
deleteCell |
query, _hash |
Delete cell |
runCell |
query |
Execute cell |
Help (no confirmation required)
| Action | Parameters | Description |
|---|---|---|
listHelp |
— | Show available actions (re-display system prompt) |
help |
action |
Show details for specific action |
Action Auto-Approval
Users can choose "Always allow for this notebook" when approving an action. Auto-approval is scoped to:
- Same notebook (the currently open tab)
- Same action type (or broader for Query actions)
Query action hierarchy:
Query actions have a granularity hierarchy. Approving finer-grained actions automatically approves coarser-grained ones:
| Approved Action | Also Approved |
|---|---|
getOutput |
getCells, getSection, getToc |
getCells |
getSection, getToc |
getSection |
getToc |
getToc |
(none) |
Rationale: If the user trusts output-level access, code and structure access (which may expose less sensitive detail) is implicitly trusted.
Mutate actions: Each type (insertCell, updateCell, deleteCell, runCell) requires separate approval. No hierarchy applies.
Auto-approval is cleared when switching notebooks or closing the panel.
System Prompt
LLM receives the following initial instruction:
You are Mynerva, a Jupyter notebook assistant.
- Always respond with JSON only. No text before or after.
- JSON structure:
{
"messages": [{ "role": "assistant", "content": "explanation" }],
"actions": [{ "type": "...", "query": {...}, ... }]
}
- "messages": natural language responses to user
- "actions": structured operations (can be empty array)
Available actions:
Query (active notebook):
- getToc: {}
- getSection: { query }
- getCells: { query, count? }
- getOutput: { query }
Query (other files):
- listNotebookFiles: { path? }
- getTocFromFile: { path }
- getSectionFromFile: { path, query }
- getCellsFromFile: { path, query, count? }
- getOutputFromFile: { path, query }
Mutate (requires _hash from prior read):
- insertCell: { position, cellType, content }
- replaceCell: { query, content, _hash }
- deleteCell: { query, _hash }
- runCell: { query? }
Query syntax: { match: "regex" } | { contains: "text" } | { start: N } | { id: "cellId" }
Help:
- listHelp: {} - show this prompt again
- help: { action } - show details for specific action
Configuration
Secret Key
MYNERVA_SECRET_KEY (env) or c.Mynerva.secret_key (traitlets)
Fernet key for encrypting API keys. If absent, Settings UI shows warning; keys stored unencrypted (not recommended).
Default Configuration (Environment Variables)
Administrators can provide default LLM settings via environment variables. Users can choose to use these defaults or configure their own.
| Variable | Description |
|---|---|
MYNERVA_OPENAI_API_KEY |
Default OpenAI API key |
MYNERVA_OPENAI_BASE_URL |
Default OpenAI-compatible endpoint (e.g. vLLM, Ollama) |
MYNERVA_ANTHROPIC_API_KEY |
Default Anthropic API key |
MYNERVA_BEDROCK_API_KEY |
Default Amazon Bedrock short-term or long-term API key (bearer) |
MYNERVA_BEDROCK_REGION |
AWS region for Bedrock requests |
MYNERVA_DEFAULT_PROVIDER |
Default provider (openai, anthropic, or bedrock) |
MYNERVA_DEFAULT_MODEL |
Default model name (optional, fetched from endpoint if not set) |
MYNERVA_DEFAULTS_ONLY |
Lock LLM settings to admin defaults (hides settings UI) |
Provider auto-detection:
- If only one API key (or base URL) is set, that provider is automatically selected
- If multiple keys are set,
MYNERVA_DEFAULT_PROVIDERis required - If
MYNERVA_OPENAI_BASE_URLis set without an API key, theopenaiprovider is enabled (for endpoints that don't require authentication) - When
MYNERVA_OPENAI_BASE_URLis set andMYNERVA_DEFAULT_MODELis not, the model list is fetched from the endpoint's/v1/models - When
MYNERVA_BEDROCK_API_KEYis set andMYNERVA_DEFAULT_MODELis not, the model list is fetched from Bedrock's/inference-profilesin the configured region
Auto-initialization: If ~/.mynerva/config.json doesn't exist and defaults are available, it's automatically created with useDefault: true.
Security note: API key environment variables are deleted after loading to prevent exposure in notebook cells.
Amazon Bedrock (Converse API)
The bedrock provider streams responses from Bedrock's Converse Stream API at https://bedrock-runtime.{region}.amazonaws.com using bearer-token authentication.
To use it:
- Select "Amazon Bedrock (Converse)" in Settings
- Choose the AWS region (default
us-east-1) - Paste your Bedrock API key
- Click the refresh icon next to the model dropdown. The list is populated from Bedrock's
/inference-profilesAPI, filtered by the bedrock entry injupyter_mynerva/models.json
Extended thinking (a.k.a reasoning) is enabled automatically for Anthropic-family model IDs (those whose ID contains claude or anthropic), giving the same UX as the dedicated Anthropic provider.
Enki Gate
Enki Gate is an LLM gateway that provides shared access to AI models. To connect:
- Select "Enki Gate" as the provider in Settings
- Enter the Enki Gate URL (e.g.
https://enki-gate.web.app) - Click "Connect" to start the device flow
- Open the verification URL in your browser and enter the displayed code
- Once authorized, the token is saved automatically
The token has an expiration time; the UI will prompt re-authentication when needed.
User Configuration
Users can configure their own settings via the panel settings UI:
- Provider selection
- Model selection
- API key (encrypted if
MYNERVA_SECRET_KEYis set) - Base URL for OpenAI-compatible endpoints (optional)
- Enki Gate connection (device flow)
If default configuration is available, users can choose "Use default settings" instead of providing their own API key.
nbsearch
If nbsearch is enabled in the same Jupyter server configuration, Mynerva exposes indexed notebook search actions. Mynerva reads the existing nbsearch traitlets configuration (c.NBSearchDB.*) to talk to Solr and object storage; it does not maintain a separate nbsearch connection setting.
Requirements
- JupyterLab >= 4.0.0 or Notebook >= 7.0.0
- nblibram (installed automatically when installing Mynerva via pip)
Install
pip install jupyter_mynerva
Contributing
Development install
# Clone the repo to your local environment
# Change directory to the jupyter_mynerva directory
# Set up a virtual environment and install package in development mode
python -m venv .venv
source .venv/bin/activate
pip install --editable "."
# Link your development version of the extension with JupyterLab
jupyter labextension develop . --overwrite
# Server extension must be manually installed in develop mode
jupyter server extension enable jupyter_mynerva
# Rebuild extension Typescript source after making changes
jlpm build
You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.
# Watch the source directory in one terminal, automatically rebuilding when needed
jlpm watch
# Run JupyterLab in another terminal
jupyter lab
Running with encryption
Generate a Fernet key for API key encryption:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Run JupyterLab with the secret key:
MYNERVA_SECRET_KEY=your-generated-key jupyter lab
Without MYNERVA_SECRET_KEY, API keys are stored unencrypted in ~/.mynerva/config.json.
Running with default API key
Provide a default API key for users:
MYNERVA_OPENAI_API_KEY=sk-... MYNERVA_DEFAULT_PROVIDER=openai MYNERVA_DEFAULT_MODEL=gpt-6-luna jupyter lab
Users will see a "Use default settings" option in the settings UI.
Running with an OpenAI-compatible endpoint
Point to a custom endpoint (e.g. vLLM, Ollama):
MYNERVA_OPENAI_BASE_URL=http://localhost:8000/v1 jupyter lab
The model list is fetched from the endpoint automatically. Add MYNERVA_OPENAI_API_KEY if the endpoint requires authentication.
Packaging the extension
See RELEASE
References
Metadata
Release files for jupyter-mynerva 2026.10.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| jupyter_mynerva-2026.10.1.tar.gz | 1.5 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jupyter_mynerva-2026.10.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.7 MB
Release files / jupyter_mynerva-2026.10.1.tar.gz
| Download URL | jupyter_mynerva-2026.10.1.tar.gz |
|---|---|
| Size | 1.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
68fa8c03c9d18c6ae5719a121cea0de748dd0c03f79a6616961ab48f8e867186
|
|
BLAKE2b-256 checksum How to use checksums |
0fb577354f015d7bf7d413d6171980dc7d30a6f57e3e1c46437f20d953932b21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / jupyter_mynerva-2026.10.1-py3-none-any.whl
| Download URL | jupyter_mynerva-2026.10.1-py3-none-any.whl |
|---|---|
| Size | 221.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f236ad39f57488f85fd16deb9acc60e0b43e347af193a90b8352d18f5950f376
|
|
BLAKE2b-256 checksum How to use checksums |
d083db4341c16902b92311214cd6bce46204ec8db7aa5cb01c1f18a8caa0a7c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for jupyter-mynerva 2026.10.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for jupyter_mynerva-2026.10.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.