OptionalData ScienceVersion 1.0.0

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 documentation

This 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:

  1. uv must be installed. Check with which uv.
  2. JupyterLab must be installed: uv tool install jupyterlab.
  3. 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.

  1. First execution after server start may time out. The kernel needs a moment to initialize. Retry if you hit a timeout.
  2. 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.
  3. --compact saves significant tokens. Always use it; JSON output can be very verbose without it.
  4. For pure REPL use, create a scratch.ipynb and skip cell editing. Just call execute repeatedly.
  5. Argument order matters. Subcommand flags like --path go before the sub-subcommand. For example, variables --path nb.ipynb list, not variables list --path nb.ipynb.
  6. 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.
  7. Errors come back as JSON with a traceback. Read the ename and evalue fields to understand what went wrong.
  8. Occasional websocket timeouts. Some operations may time out on the first try, especially after a kernel restart. Retry once before escalating.
  9. 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 shows execution_state=idle and execution_count increments); only the websocket reply channel is broken. zmq transport uses jupyter_client directly and sidesteps the issue.
  10. When starting a fresh server for REST-only use, add --ServerApp.disable_check_xsrf=True. Otherwise POST /api/sessions returns "'_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.