marimo-pair

Collaboration protocol for pairing with a user through a running marimo notebook via bundled scripts or MCP. Use when the user asks you to work in, build, explore, or modify a marimo notebook — or when you detect a running marimo session by listing sessions. Do NOT use for general Python scripting outside of marimo or for marimo plugin/package development.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "marimo-pair" with this command: npx skills add marimo-team/marimo-pair/marimo-team-marimo-pair-marimo-pair

Notebook metaprogramming lives in marimo._code_mode. You MUST use async with — without it, operations silently do nothing.

import marimo._code_mode as cm

async with cm.get_context() as ctx:
    for c in ctx.cells:
        print(c.cell_id, c.code[:80])

Explore the API with dir(ctx) and help() at the start of each session.

marimo Pair Programming Protocol

You can interact with a running marimo notebook via bundled scripts or MCP. Bundled scripts are the default — they work everywhere with no extra setup. The workflow is identical either way; only the execution method differs.

Prerequisites

The marimo server must be running with token and skew protection disabled:

marimo edit notebook.py --no-token --no-skew-protection

Figure out the best way to invoke marimo from the current directory — if it's a project with a managed environment, use that (e.g., uv run marimo).

Do NOT use --headless unless the user asks for it. Omitting it lets marimo auto-open the browser, which is the expected pairing experience. If the user explicitly requests headless, offer to open it with open http://localhost:<port>.

If no servers are found, offer to start marimo as a background task. Be eager — suggest it proactively. The user may also prefer to start it themselves.

Always discover servers before starting a new one. Background task "completed" notifications do not mean the server died — check the output or run discover before starting another.

How to Discover Servers and Execute Code

Two operations: discover servers and execute code.

OperationScriptMCP
Discover serversbash scripts/discover-servers.shlist_sessions() tool
Execute codebash scripts/execute-code.sh -c "code"execute_code(code=..., session_id=...) tool
Execute code (complex)bash scripts/execute-code.sh /tmp/code.pysame

Scripts auto-discover sessions from the registry on disk. Use --port to target a specific server when multiple are running. If the server was started with --mcp, you'll have MCP tools available as an alternative.

Use a file for complex code. When code contains quotes, backticks, ${} template literals, or multiline strings (common with anywidget ESM modules), write the code to a temp file with the Write tool first, then pass the file path as a positional argument. This avoids shell escaping issues entirely.

Inline ESM in cell code. Temp files are for execute-code.sh transport only — never for runtime. Use """ for ESM inside ''' for the cell code.

First Step: Explore the code_mode Context

The code_mode API can change between marimo versions. Your first execute-code call should discover what the running server actually provides:

Never guess method signatures. Always help(ctx.method_name) before calling a method for the first time — parameter names and defaults change across versions.

import marimo._code_mode as cm

async with cm.get_context() as ctx:
    print(dir(ctx))
    help(ctx)

Two Modes of Working

Scratchpad (simple): Just Python — print(df.head()), check data shapes, test a snippet. Cell variables are already in scope. Results come back to you — the user doesn't see them. You can also read and set UI element state programmatically (see ui-state). The kernel preamble in execute-code.md has the correct entry point and imports.

Cell operations (complex): Creating, editing, moving, deleting cells. These require careful API orchestration — compile, register, notify the frontend, then execute. Get it wrong and the UI desyncs.

Decision Tree

SituationAction
Need to find running serversDiscover servers
Need to read data/stateUse scratchpad recipes in execute-code.md
Need to create/edit/move/delete cellsFollow the scratchpad-to-cell workflow below, then use execute-code.md
Need to install a packageUse the code_mode context — see Installing Packages
Unsure what API to useSee Discovering the API in execute-code.md
Import path failsSee Discovering the API in execute-code.md
Need a custom visualization or interactive widgetSee rich-representations.md (_display_() for display-only, anywidget for bidirectional)
Widget trait should drive downstream cellsmo.state() + .observe() — see Reactive anywidgets
Need to display a notification to the user (toast, banner, focus)See other operations
User asks to improve/optimize/clean up the notebookSee notebook-improvements.md

The Scratchpad-to-Cell Workflow

The cardinal rule: never show the user broken code. Runtime errors in cells are a bad experience. Runtime errors in the scratchpad are invisible learning.

Compile-check is not validation. It catches syntax errors, broken refs, and cycles — but not wrong arguments, missing methods, or type mismatches. Don't let a passing compile-check give you false confidence.

ALWAYS test in the scratchpad before creating or editing a cell. No exceptions unless the user explicitly says to skip testing. If the code is expensive, test on a subset — or if that's not possible, ask the user.

The async with context manager automatically compile-checks on exit — syntax errors, multiply-defined names, and cycles are caught before any graph mutation occurs. If the check fails, the operation is rejected and you get an error. You don't need to compile-check manually.

If testing passes, do the cell operation immediately — in the same execute-code call when possible. Never pause to ask; the only reason to pause is ambiguous intent.

Steps (same for add or edit)

  1. If editing, read the current cell code from the graph
  2. Test in scratchpad — run the code to validate at runtime
  3. Create or update the cell — the context manager auto-compile-checks. If it fails, fix the code and retry. See execute-code.md.

Keep cells small and focused — prefer splitting computation across cells and extracting helpers over large monolithic cells. Hide code by default so the notebook reads as a clean document.

Philosophy

You have full access to the running notebook. When the user's intent is clear, act on it. When it's ambiguous, clarify.

Before reaching for external tools or CLIs, explore what ctx and marimo already provide — marimo has integrations for many common operations (e.g., installing packages). Use dir(ctx) and help() to discover capabilities.

Before making choices, look for signal — notebook imports, pyproject.toml, sys.modules, existing cells, directory structure. Follow existing patterns. Take agency over things you're confident in. If you're not sure, ask.

Guard Rails

Skip these and the UI breaks:

  • Install packages via ctx.install_packages(), not uv add or pip. The code API handles kernel restarts and dependency resolution correctly. Only fall back to external CLIs if the API is unavailable or fails.
  • Custom widget = anywidget. When the user asks for a "custom widget", "custom view", or any bespoke visual component, build an anywidget with HTML/CSS/JS — do NOT compose mo.ui elements. Composed mo.ui is fine for simple forms and controls, but anywidget gives full layout control, avoids same-cell value constraints, and is what the user expects when they say "custom". See rich-representations.md.
  • Notify the frontend before executing cell operations — use _code_mode.
  • The async with context manager auto-compile-checks — if it rejects, fix and retry.
  • Clean up dry-run registrations — scratchpad side effects persist in the graph.
  • Don't write to the .py file directly — the kernel owns it.
  • No temp-file deps in cells. pathlib.Path("/tmp/...") in cell code is a bug.
  • No empty cells. Before creating a cell, check for existing empty cells and edit_cell into them instead. On startup, use the default empty cell rather than appending. Clean up any cells that end up empty after edits.

Confirm with the user before:

  • Installing packages — adds dependencies to their project.
  • Deleting cells — removes work that may not be recoverable.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

marimo-notebook

No summary provided by upstream source.

Repository SourceNeeds Review
General

marimo-batch

No summary provided by upstream source.

Repository SourceNeeds Review
General

anywidget-generator

No summary provided by upstream source.

Repository SourceNeeds Review