Jupyter Notebook Skill: Stateful Python REPL for Hermes Agent
Iterative Python via live Jupyter kernel (hamelnb).
Written by Neura Market from the official Hermes Agent documentation for Jupyter Notebook. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationThis skill gives Hermes Agent a live, stateful Python REPL backed by a real Jupyter kernel. Variables, imports, and objects survive between calls, so you can build up a session step by step instead of rerunning a whole script. Reach for it when the task is exploratory: poking at an API, inspecting a DataFrame, or trying a change and checking the result before committing to it.
What it does
The skill wraps a Jupyter kernel in a command-line tool called jupyter_live_kernel.py, shipped as part of the hamelnb project. Through it, the agent can list running servers and notebooks, execute Python against a chosen notebook, inspect the variables currently in memory, edit notebook cells, and restart the kernel to run everything top to bottom. Every command returns structured JSON, and the --compact flag trims that output to save tokens.
The important difference from a one-shot script runner is persistence. When you execute a snippet, the kernel keeps the resulting state. The next execution sees the same names, the same imported modules, the same loaded data. That is what makes iterative work practical: you can define a helper, test it, adjust it, and keep going without paying the setup cost each time.
Before you start
Three things must be in place before any of this works:
- uv must be installed. Check with
which uv. - JupyterLab must be installed:
uv tool install jupyterlab. - A Jupyter server must be running. The setup section below covers starting one.
The skill is optional and installed on demand. Its path is optional-skills/data-science/jupyter-notebook, version 1.0.0, authored by Hermes Agent under the MIT licence. It runs on linux, macos, and windows.
Setup
The hamelnb script lives at a fixed path:
SCRIPT="$HOME/.agent-skills/hamelnb/skills/jupyter-live-kernel/scripts/jupyter_live_kernel.py"
If the repository has not been cloned yet:
git clone https://github.com/hamelsmu/hamelnb.git ~/.agent-skills/hamelnb
Starting JupyterLab
First check whether a server is already running:
uv run "$SCRIPT" servers
If none is found, start one:
jupyter-lab --no-browser --port=8888 --notebook-dir=$HOME/notebooks \
--IdentityProvider.token='' --ServerApp.password='' > ~/.hermes/cache/scratch/jupyter.log 2>&1 &
sleep 3
Token and password are disabled here so the local agent can reach the server without credentials. The server runs headless, with no browser window.
Creating a Notebook for REPL Use
If you only need a REPL and have no existing notebook, create a minimal one. Start with the directory:
mkdir -p ~/notebooks
Then write a minimal .ipynb JSON file containing one empty code cell, and start a kernel session through the Jupyter REST API:
curl -s -X POST http://127.0.0.1:8888/api/sessions \
-H "Content-Type: application/json" \
-d '{"path":"scratch.ipynb","type":"notebook","name":"scratch.ipynb","kernel":{"name":"python"}}'
Core workflow
All commands return structured JSON. Always pass --compact to keep the output small.
1. Discover servers and notebooks
uv run "$SCRIPT" servers --compact
uv run "$SCRIPT" notebooks --compact
Use these first to confirm what is running and which notebooks the server can see.
2. Execute code
This is the primary operation:
uv run "$SCRIPT" execute --path <notebook.ipynb> --code '<python code>' --compact
State persists across execute calls. Variables, imports, and objects all survive.
Multi-line code works with $'...' quoting:
uv run "$SCRIPT" execute --path scratch.ipynb --code $'import os\nfiles = os.listdir(".")\nprint(f"Found {len(files)} files")' --compact
3. Inspect live variables
uv run "$SCRIPT" variables --path <notebook.ipynb> list --compact
uv run "$SCRIPT" variables --path <notebook.ipynb> preview --name <varname> --compact
Listing shows what is currently bound in the kernel; preview shows the contents of a named variable. This is how you check state without re-executing anything.
4. Edit notebook cells
# View current cells
uv run "$SCRIPT" contents --path <notebook.ipynb> --compact
# Insert a new cell
uv run "$SCRIPT" edit --path <notebook.ipynb> insert \
--at-index <N> --cell-type code --source '<code>' --compact
# Replace cell source (use cell-id from contents output)
uv run "$SCRIPT" edit --path <notebook.ipynb> replace-source \
--cell-id <id> --source '<new code>' --compact
# Delete a cell
uv run "$SCRIPT" edit --path <notebook.ipynb> delete --cell-id <id> --compact
contents gives you the current cells, including the cell IDs you need for the replace and delete operations.
5. Verification (restart + run all)
Use this only when the user asks for a clean verification or you need to confirm the notebook runs top to bottom:
uv run "$SCRIPT" restart-run-all --path <notebook.ipynb> --save-outputs --compact
When not to use it
The source draws a clear line between this skill and two alternatives.
Use execute_code for one-shot scripts that need Hermes tool access such as web_search or file operations. That path is stateless, which is fine when there is nothing to carry forward.
Use terminal for shell commands, builds, installs, git, and process management.
The rule of thumb the source gives: if you would want a Jupyter notebook for the task, use this skill. If you would not, one of the other two is the better fit.
Limits and gotchas
The source lists ten practical points worth reading before your first run.
- First execution after server start may time out. The kernel needs a moment to initialize. Retry if you hit a timeout.
- The kernel Python is JupyterLab's Python. Packages must be installed in that environment. If you need additional packages, install them into the JupyterLab tool environment first.
--compactsaves significant tokens. Always use it; JSON output can be very verbose without it.- For pure REPL use, create a
scratch.ipynband skip cell editing. Just callexecuterepeatedly. - Argument order matters. Subcommand flags like
--pathgo before the sub-subcommand. For example,variables --path nb.ipynb list, notvariables list --path nb.ipynb. - If a session does not exist yet, start one through the REST API as shown in Setup. The tool cannot execute without a live kernel session.
- Errors come back as JSON with a traceback. Read the
enameandevaluefields to understand what went wrong. - Occasional websocket timeouts. Some operations may time out on the first try, especially after a kernel restart. Retry once before escalating.
- If websocket consistently times out on this host, force zmq transport:
uv run "$SCRIPT" execute --transport zmq .... The symptom is every execute returning "Websocket execution may already have reached the kernel, so auto fallback was skipped". The kernel actually ran fine (REST showsexecution_state=idleandexecution_countincrements); only the websocket reply channel is broken. zmq transport usesjupyter_clientdirectly and sidesteps the issue. - When starting a fresh server for REST-only use, add
--ServerApp.disable_check_xsrf=True. OtherwisePOST /api/sessionsreturns"'_xsrf' argument missing from POST"and kernel session creation fails.
Timeout defaults
The script has a 30-second default timeout per execution. For long-running operations, pass --timeout 120. Use generous timeouts (60+) for initial setup or heavy computation.
Related
This skill sits in the Data Science category alongside the other optional skills. If your task needs Hermes tool access inside the code, pair it with execute_code; if it needs shell work, pair it with terminal. The upstream project is hamelnb, cloned from https://github.com/hamelsmu/hamelnb.git.