marimo-serve
Serve every marimo notebook in a project directory under one ASGI host with auto-discovery. Use when the user wants to browse multiple marimo apps at once (localhost, LAN, or tailnet) instead of running `marimo run` per file. Handles expose-on-tailnet via `tailscale serve`.
Best use case
marimo-serve is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Serve every marimo notebook in a project directory under one ASGI host with auto-discovery. Use when the user wants to browse multiple marimo apps at once (localhost, LAN, or tailnet) instead of running `marimo run` per file. Handles expose-on-tailnet via `tailscale serve`.
Teams using marimo-serve should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/marimo-serve/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How marimo-serve Compares
| Feature / Agent | marimo-serve | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Serve every marimo notebook in a project directory under one ASGI host with auto-discovery. Use when the user wants to browse multiple marimo apps at once (localhost, LAN, or tailnet) instead of running `marimo run` per file. Handles expose-on-tailnet via `tailscale serve`.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# marimo-serve
Runs one uvicorn process that auto-mounts every `*.py` notebook in a directory under `http://host:port/<mount>/<stem>`. Add or remove files — the URL list updates without restart. This is marimo's closest equivalent to "JupyterLab for many notebooks."
**Default is read-only Run mode.** Pass `--edit` to launch `marimo edit DIRECTORY --watch` instead (full editor, saves on disk; picks up external `.py` edits without restart).
## Quick usage
```bash
# Read-only Run mode (default)
pixi run python ${CLAUDE_SKILL_DIR}/scripts/serve.py [DIRECTORY] \
[--host 127.0.0.1] [--port 2718] [--mount /<project>] [--include-code]
# Edit mode — launches marimo's built-in multi-session editor
pixi run python ${CLAUDE_SKILL_DIR}/scripts/serve.py [DIRECTORY] --edit \
[--host 127.0.0.1] [--port 2718]
```
Defaults:
- `DIRECTORY` = `./notebooks`
- Bind = `127.0.0.1:2718`
- Mode = **read-only Run mode**. Users interact with UI widgets and see outputs but cannot edit cells. `marimo.create_asgi_app()` only supports Run mode.
- Mount = `/<project>` (the parent directory's name — e.g. running from `~/projects/mirror` with `./notebooks` → `/mirror/<stem>`). marimo requires a non-empty prefix; pass `--mount` to override. **Run mode only** — edit mode serves at `/`.
- Source code hidden (`--include-code` to reveal; read-only regardless)
## Expose on tailnet
Keep the server on `127.0.0.1` and let Tailscale Serve handle the tailnet + HTTPS. Because marimo requires a non-empty mount prefix (`/apps`), the upstream URL **must include the same prefix** or tailscale will strip it and marimo returns 404:
```bash
# mounts /<project> on the tailnet AND forwards /<project> upstream (path preserved)
tailscale serve --bg --https=443 --set-path=/<project> http://127.0.0.1:2718/<project>
# browse: https://<machine>.<tailnet>.ts.net/<project>/<notebook_stem>
tailscale serve status # see active config
tailscale serve --https=443 --set-path=/<project> off # remove just this path
tailscale serve reset # tear down all paths
```
Use `--set-path` if `/` is already mapped to another service — tailscale serve supports multiple path prefixes on the same hostname.
### Service-worker conflict (separate port)
If an app already mapped at `/` (e.g. a PWA) registers a service worker with root scope, that SW will intercept requests to `/<project>/*` on the **same** origin and return its own cached shell — marimo never gets the request. Fix: put marimo on a different HTTPS port so it's a separate origin (SWs cannot cross origins):
```bash
tailscale serve --bg --https=8443 --set-path=/<project> http://127.0.0.1:2718/<project>
# browse: https://<machine>.<tailnet>.ts.net:8443/<project>/<notebook_stem>
```
`tailscale serve --https=443 off` removes ALL paths on :443 — use `--set-path=/<path> off` to remove a single prefix.
## Persistence (macOS launchd)
For always-on serving, wrap in `~/Library/LaunchAgents/com.user.marimo-serve.plist` pointing at the `serve.py` command with `KeepAlive=true`. Tailscale Serve config survives reboots automatically.
## Dependencies
Project must have `marimo` and `uvicorn` installed. With pixi:
```bash
pixi add marimo uvicorn
```
## When to use another approach
- **Editing, not serving**: pass `--edit` (this wraps `marimo edit <dir>`) or call `marimo edit <dir>` directly.
- **Fully static hosting (no Python)**: `marimo export html-wasm` → drop on any static host.
- **Single notebook, one-off**: `marimo run notebook.py` is simpler.
## Implementation notes
Run mode uses `marimo.create_asgi_app().with_dynamic_directory()` — the officially documented pattern for multi-notebook ASGI serving. Filenames starting with `_` are skipped (treat as private/helper modules). `create_asgi_app()`'s docstring states it "only works for application that are in Run mode" — that's why edit mode delegates to the `marimo edit` CLI (`os.execv`) instead.
Edit mode passes `--watch` by default so that edits made to the `.py` file from outside the browser (e.g. by an agent using Edit/Write) are picked up by the running session without a manual reload. This pairs with the marimo-pair skill: one agent edits the file on disk, the user (or another agent) runs cells in the browser.Related Skills
marimo
Use when working with marimo notebooks — creating, editing, debugging, converting from Jupyter, or pairing with a running marimo server.
writing
This skill should be used when the user asks to 'write a paper', 'start a writing project', 'draft an article', 'write about', 'brainstorm writing topics', 'gather sources for a paper', 'what should I write about', or needs the writing workflow entry point for any writing task.
writing-validate
Validate draft sections cover all PRECIS claims before review.
writing-setup
Internal skill for creating PRECIS.md, OUTLINE.md, and ACTIVE_WORKFLOW.md. Called after brainstorm sources are gathered.
writing-revise
This skill should be used when the user asks to 'revise writing', 'fix review issues', 'polish draft', 'apply review feedback', 'complete writing workflow', or after /writing-review produces REVIEW.md with issues to fix.
writing-review
Internal skill for hierarchical document review. Called by writing-validate after claim validation passes.
writing-precis-reviewer
Internal skill used by writing-setup at exit gate. Dispatches a reviewer subagent to verify PRECIS.md quality before outlining. NOT user-facing.
writing-outline
Internal skill for creating detailed section outlines. Called by /writing workflow after PRECIS and master OUTLINE are complete.
writing-outline-reviewer
Internal skill used by writing-outline at exit gate. Dispatches a reviewer subagent to verify OUTLINE.md quality before drafting. NOT user-facing.
writing-lit-review
Internal skill for literature review and source materialization. Called after brainstorm, before setup. NOT user-facing.
writing-legal
Internal skill for academic legal writing. Loaded by /writing when style=legal. Based on Volokh's "Academic Legal Writing".
writing-handoff
Create structured handoff document for writing workflow session pause/resume.