Bring a freshly cloned catalog to a running marimo kernel, then hand off to composition.
Steps
-
Read the catalog manifest. Read
catalog.tomlat the repo root forfirst_notebook,auth(any required env var), and the catalog name. If there is nocatalog.toml, fall back to the lowest-numbered notebook innotebooks/and checkREADME.mdfor auth. -
Check the runtime. Confirm
uvis installed (uv --version); if missing, install it:curl -LsSf https://astral.sh/uv/install.sh | sh -
Install the marimo-pair skill for the user's agent (it is how you drive a live kernel):
npx skills add marimo-team/marimo-pair --agent claude-code -y
-
Check auth if the manifest declares it. If
authnames an env var (e.g.FINNGENIE_TOKEN), confirm a.envexists and the var is set. If not, stop and tell the user how to get the token (point to the catalog's README); do not proceed. -
Launch the first notebook in a sandbox, in the background, and remember the port:
PORT=$(python -c "import socket; s=socket.socket(); s.bind(('127.0.0.1',0)); print(s.getsockname()[1])") echo "marimo port: $PORT" env -u PYTHONPATH uvx marimo edit --sandbox --no-token --port $PORT notebooks/<first_notebook>--sandboxis required so the notebook's PEP 723 dependencies are provisioned.env -u PYTHONPATHavoids a Nix-shell websockets shim that crashes startup. Run it in the background so it does not block, and keep$PORT- the next step needs it.Whether to pass
--headlessdepends on who is composing:- A human is pairing and a browser is available. Omit
--headless. marimo auto-opens the browser; that frontend connection both gives the user a live notebook to look at and registers the kernel session that step 6 needs. Skip to step 6. - You are an agent, or there is no browser (a
--headlessserver on a remote/SSH host, a CI box, an agent-driven run). Pass--headlessand register the session yourself in the next step - nothing connects to the websocket on its own, so the server comes up with no session and the firstexecute-code.shcall would fail withNo active sessions on the server. Make sure a notebook is open in the browser.
- A human is pairing and a browser is available. Omit
-
Ensure a kernel session exists (headless launch only - skip if a browser already opened). marimo creates a kernel session only when a frontend connects to its websocket. With no browser, register one yourself:
-
A browser exists on the host but did not auto-open (you launched
--headlesson a desktop): open the URL for the user and let their browser register the session -open "http://localhost:$PORT"on macOS,xdg-open "http://localhost:$PORT"on Linux. -
Truly headless (no browser at all): run the bundled stand-in, which connects to the websocket the way the frontend does, waits for the kernel to report ready, and prints the session id:
SESSION_ID=$(<getting-started-skill-dir>/scripts/register-session.py --port $PORT) echo "registered marimo session: $SESSION_ID"
In
marimo editmode the session persists after the helper exits, so this one-shot call is enough - keep$SESSION_IDand target it withexecute-code.sh --port $PORT(or--session $SESSION_ID). The helper takes--token/MARIMO_TOKENfor token-protected servers, and--holdfor the rare server that drops the session on disconnect (RUN mode or--session-ttl); plainmarimo editneeds neither. Confirm withcurl -s http://127.0.0.1:$PORT/api/sessionsthat a session is now listed. -
-
Run every cell, then confirm the kernel is populated. marimo only auto-runs cells when a browser frontend connects, so a freshly launched kernel can sit with all cells stale and every notebook variable undefined. Do not rely on the browser - run the cells explicitly via the marimo-pair scripts you installed in step 3 (
scripts/execute-code.sh, targeting--port $PORT):import marimo._code_mode as cm async with cm.get_context() as ctx: for c in ctx.cells: ctx.run_cell(c.id)Then spot-check that a key variable resolved (e.g. print the shape of the notebook's main table) before handing off. An empty, unexecuted kernel is the most common "it didn't work".
-
Hand off. Tell the user the kernel is running on
$PORT(and, if you registered one, that a session is live - the notebook is open in their browser when a browser was used) and that they can now ask a question - thecompose-notebookskill takes it from here.
Notes
- Public catalogs (e.g., jx, prx, dmx) need no auth, where some do, e.g., fgx needs
FINNGENIE_TOKEN. - Do not improvise alternative launch commands; the
--sandboxflag is what makes per-notebook dependencies work. - Do not improvise the headless session bootstrap either - the
scripts/register-session.pystand-in is the supported way to register a kernel session without a browser. It exists so each agent does not re-derive a websocket client from scratch. - If the repo gitignores its skill stores (tracks only
skills-lock.json) and the catalog skills look missing or stale,npx skills updatereconstitutes them from the lock. This is a refresh once the skills are already present - it is not the clone-time bootstrap. A cloner who has no catalog skills on disk cannot reach this skill to run that step; the post-clone restore therefore lives in the repo's trackedAGENTS.md/README.md, not here, since a skill cannot bootstrap its own install.