CLI
The keyboard path — every move an agent makes over MCP you can make by hand with the openislands command, with the same validation.
openislands is the keyboard path. Every move the agent makes over MCP you can make
by hand — same validation, same files, same refusal to write something that won't render.
Reach for it to bootstrap a project, drive a quick change without an agent in the loop, or wire
a connector into cron.
Tip
No global install needed: npx openislands <command> runs the latest published CLI. Install it
(npm i -g openislands) once you're using it daily.
The everyday loop
Most sessions are the same rhythm: infer fast, formalize immediately. See a file's shape with no commitment, then bind it, add an island, and validate.
Infer a file's shape
Drop a CSV (or JSON / JSONL / Parquet) into data/ and ask what's in it:
npx openislands infer data/spending.csvYou get the inferred columns and types, a proposed dataset contract, and a few suggested islands. Nothing is written — it's a read-only look.
Bind it, then add an island
Re-run with --bind to write the dataset into the manifest, then scaffold an island:
npx openislands infer data/spending.csv --bind
npx openislands add category.bar--bind re-validates before saving, so a bad write is refused, not persisted. Then open the app's
apps/<id>/manifest.json, set the island's dataset, and fill in its required fields from the
columns infer showed you.
Validate
npx openislands validateGreen means the dashboard renders. A binding to a field that doesn't exist fails here and
names the island — the same check the agent's oi.app().replaceManifest runs.
Commands
Every project is a workspace: apps live under apps/<id>/, and the commands below run at the
project root and fan out across them. --app <id> narrows a command to one app; validate and
sync default to all apps.
init
openislands init [project] --app <id> --template <empty|finance|health|operations>Scaffolds a complete, working project workspace into project (default: openislands/), with the
first app at apps/<id>/. --template (-t) picks the starting point and defaults to finance —
net worth, allocation, holdings, and transactions as typed islands over CSVs you own; health,
operations, and the blank empty are the others. --app names that first app id; it defaults to
the template name (finance, health, …), or main for empty.
serve
openislands serve [project] [--port 4321] [--host 127.0.0.1] [--mcp] [--mcp-token <token>]Boots your dashboard as a long-running local app (the TanStack Start SSR runtime), querying your
files on every request. It scans apps/*, compiles each, and serves them all in one process on one
port — the UI gets a left rail to switch apps (hidden when there's only one). It binds to loopback
by default — this is your data. Leave it running: edit a file and the page live-updates over SSE.
--mcp also mounts the MCP server over Streamable HTTP on the same port at a single /mcp
endpoint — one server for the whole workspace, with the app chosen by the app tool param — so a
remote agent can drive the dashboard. --mcp-token
sets the bearer token it requires — mandatory when binding off loopback, since MCP is a write
surface. Every flag has an environment equivalent (OPENISLANDS_PORT, OPENISLANDS_HOST,
OPENISLANDS_MCP, OPENISLANDS_MCP_TOKEN), which is how the Docker image is
configured; flags win when both are set. When you bind off loopback, OPENISLANDS_ALLOWED_IPS
restricts which client IPs may reach the port (comma-separated IPs and IPv4 CIDRs; default allow-all).
See Self-hosting for the container story.
infer
openislands infer <file> [project] [--app <id>] [--bind]Reads a data file through the same DuckDB core the runtime uses and prints its columns, a
proposed dataset contract, and suggested islands. Read-only by default; --bind writes the
dataset into the target app's apps/<id>/manifest.json (re-validating first). The file must
live inside the app — move it under its data/ before binding. Operates on one app: it defaults
to the sole app and errors if the project has several, so pass --app.
add
openislands add <island-type> [project] [--app <id>]Appends an island skeleton to the first page, then validates before writing — if the skeleton
wouldn't render, nothing is saved. Fill in its dataset and fields afterward. Targets one app
(defaults to the sole app; --app when there's more than one). Any built-in type works; see the
island registry.
add-app
openislands add-app <id> [project] --template <empty|finance|health|operations>Adds another app to an existing workspace, scaffolding a full template into apps/<id>/ — the CLI
parity for the MCP oi.createApp method. The id must be a single safe path segment and not already
exist.
validate
openislands validate [project] [--app <id>]Compiles every app's manifest and checks each island's binding against the live data; --app
narrows it to one. On failure it names the app, page, island, and missing field, and exits
non-zero if any app fails — the safety net behind every edit, agent or human.
sync
openislands sync [project] [connector] [--app <id>]Pulls configured connectors once across all apps and writes their datasets;
name a connector and/or pass --app to narrow. Cron this for headless refresh; an OAuth connector
needs to be authorized once from the running dashboard first.
Next
- MCP Server: the same surface, driven by an agent.
- The Manifest: the JSON these commands read and write.
- Islands: the building blocks
addscaffolds.
Agent Setup
Any agent that speaks the Model Context Protocol can drive OpenIslands — this page covers wiring up Hermes, OpenClaw, Claude Code, and Codex CLI.
Self-hosting
Run OpenIslands as an always-on Docker container on a server or home NAS — the dashboard and the MCP server over HTTP in one process, from the ghcr image, with a token guarding the write surface off loopback.