OpenIslands

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.csv

You 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 validate

Green 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 add scaffolds.

On this page