# CLI (/cli)



`openislands` is the keyboard path. Every move the agent makes over [MCP](/mcp.md) 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.

<Callout type="info" title="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.
</Callout>

## The everyday loop [#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.

<Steps>
  <Step>
    ### Infer a file's shape [#infer-a-files-shape]

    Drop a CSV (or JSON / JSONL / Parquet) into `data/` and ask what's in it:

    ```bash
    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.
  </Step>

  <Step>
    ### Bind it, then add an island [#bind-it-then-add-an-island]

    Re-run with `--bind` to write the dataset into the manifest, then scaffold an island:

    ```bash
    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.
  </Step>

  <Step>
    ### Validate [#validate]

    ```bash
    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.
  </Step>
</Steps>

## Commands [#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` [#init]

```bash
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` [#serve]

```bash
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](/self-hosting.md) 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](/self-hosting.md) for the container story.

### `infer` [#infer]

```bash
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` [#add]

```bash
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](/islands/overview.md).

### `add-app` [#add-app]

```bash
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` [#validate-1]

```bash
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` [#sync]

```bash
openislands sync [project] [connector] [--app <id>]
```

Pulls configured [connectors](/data/connectors.md) 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 [#next]

* [MCP Server](/mcp.md): the same surface, driven by an agent.
* [The Manifest](/concepts/manifest.md): the JSON these commands read and write.
* [Islands](/islands/overview.md): the building blocks `add` scaffolds.


---

*This is one page of the OpenIslands docs. Every page in one file: [/llms-full.txt](/llms-full.txt). Page index: [/llms.txt](/llms.txt). Links above point to `.md` siblings — append `.md` to any page URL for its raw markdown.*
