servers / openislands
openislands MCP server
communitystdiolocalwrite capablehealthy
Local-first dashboards your AI agent builds and keeps maintaining, over data that never leaves your machine.
01Tools · 1
How to read this: tool names here are observed from a live tools/list handshake. The Risk label is a heuristic inferred from the tool name (write/destructive verbs), not from executing the tool — a conservative guess, not a verified capability. We never escalate risk from a description. Found one that's wrong? Tell us — we fix on report.
| Tool | Risk | Side effects | Approval |
|---|---|---|---|
| execute Run the OpenIslands API by writing a small async JavaScript program (Code Mode). One tool replaces the per-operation tools: write JS that calls the `oi` API, compose many steps in a single call (loops, conditionals, chaining), and `return` a value and/or `console.log` what you want back. It runs in a sandbox — no require / process / network, only `oi`. The result is { ok, result, logs, checkpoints_created? }; on a thrown error it's { ok:false, error, logs }.
Read-many / write-one: read freely; every manifest change goes stage → apply (oi.app().patchManifest/replaceManifest returns a proposal_id, then oi.app().applyEdit(proposal_id) writes it and snapshots the prior state for rollback). If a stage returns ok:false, each error names the page/island/field — fix it and retry.
type Verbosity = "concise" | "detailed";
declare const oi: {
// workspace
listApps(): Promise<{ ok: boolean; apps: { id: string; title: string; dir: string }[] }>;
createApp(input: { id: string; title?: string }): Promise<{ ok: boolean; id?: string; dir?: string; error?: string }>;
deleteApp(input: { id: string }): Promise<{ ok: boolean; archivedTo?: string; error?: string }>;
// app-scoped API; omit `id` when the workspace has exactly one app, else pass it (see oi.listApps()).
app(id?: string): AppApi;
};
interface AppApi {
// orient
getOverview(opts?: { verbosity?: Verbosity }): Promise<Overview>; // START HERE: manifest + every dataset's live columns + actions/queries/connectors + checkpoint count
getManifest(): Promise<Manifest>; // the raw manifest object
listIslands(): Promise<{ ok; islands }>; // built-in island types + required fields + span range
getIslandSchema(type: string): Promise<{ ok; schema; layout; notes }>; // JSON Schema + layout guidance for one island type (e.g. "metric.kpi", or "layout.row")
getDataSchema(dataset: string): Promise<{ ok; dataset; columns }>; // columns + inferred types from the live data
// read data (read-only, row-capped; pass verbosity:"detailed" for a bigger pull)
runSql(input: { sql?: string; dataset?: string; limit?: number; verbosity?: Verbosity }): Promise<{ ok; rows; rowCount; truncated? }>; // one read-only SELECT over the dataset views, or a whole dataset
previewDataset(dataset: string, opts?: { limit?: number; verbosity?: Verbosity }): Promise<{ ok; rows; rowCount; truncated? }>; // read a dataset/transform's rows back — alias for runSql({ dataset }); the findable way to verify computed values
validateSql(sql: string): Promise<{ ok; columns?; error? }>; // dry-run a SELECT (catalog/parse/type errors) without running it
validateManifest(manifest?: Manifest): Promise<{ ok; errors; warnings }>; // validate + check bindings vs data (current manifest if omitted)
// edit the manifest — read-many / write-one. Nothing is written until applyEdit.
patchManifest(patch: ManifestPatch): Promise<Proposal>; // PREFERRED: send only the sections that change
replaceManifest(manifest: Manifest): Promise<Proposal>; // full rewrite
applyEdit(proposalId: string): Promise<{ ok; checkpoint_id? }>; // write a staged proposal; snapshots prior state for rollback
rollback(checkpointId?: string): Promise<{ ok; restored? }>; // restore a checkpoint byte-for-byte (latest if omitted)
listCheckpoints(): Promise<{ ok; checkpoints: string[] }>;
pruneCheckpoints(keep?: number): Promise<{ ok; kept; removed }>;
// data actions (typed appends) + queries (typed reads)
listActions(): Promise<{ ok; actions }>;
// typed writes, dispatched by each action's declared mode (listActions/getOverview show it): insert/replace take rows, delete takes match, update takes match+set. Atomic by default: validates all calls first, rolls back every write if any fails.
runActions(calls: { action: string; rows?: object[]; match?: object; set?: object }[], opts?: { atomic?: boolean }): Promise<{ ok; results?; checkpoint_ids?; failures? }>;
listQueries(): Promise<{ ok; queries }>;
runQuery(name: string, params?: object, opts?: { limit?: number; verbosity?: Verbosity }): Promise<{ ok; rows; columns }>;
// connectors — provider sync. Keyless connectors (auth:"none", canSyncDirectly:true) need NO authorization: sync them directly with runSync. oauth2/bearer connectors need a human to connect via the dashboard Connect button first.
listConnectors(): Promise<{ ok; connectors }>;
runSync(name: string): Promise<{ ok; error? }>;
}
// Proposal: { ok; proposal_id?; diff; warnings?; errors? } — errors name the page/island/field on ok:false
// ManifestPatch: { title?; icon?; datasets?: Record<name, spec|null>; actions?; queries?; connectors?; pages?: Page[]; remove_pages?: string[] } // null deletes a key; pages upsert by id
Example — orient, add a KPI, apply:
const app = oi.app();
const ov = await app.getOverview();
const page = ov.pages[0];
page.islands.push({ type: "metric.kpi", title: "Net worth", dataset: "net_worth_monthly", value: "net_worth_eur", format: "eur", span: 4 });
const staged = await app.patchManifest({ pages: [page] });
if (!staged.ok) return staged.errors; // each error names the page/island/field
return await app.applyEdit(staged.proposal_id);
Example — sanity-check every dataset in one call:
const app = oi.app();
const ov = await app.getOverview();
const counts = {};
for (const name of Object.keys(ov.datasets)) counts[name] = (await app.runSql({ dataset: name, limit: 1 })).rowCount;
return counts; | write | true | unknown |
02Install & source
npx -y @openislands/mcp
npx- repohttps://github.com/lukaisailovic/openislands
- homepagehttps://openislands.sh/
- licenseMIT
- adoption21 stars · 2 forks
03Access granted
Query a database · destructive
The access this server can exercise, inferred from its verified tools — not a declared OAuth scope.
05Provenance & freshness
sourcesGitHub repo search [p4]
last_checked2026-08-16 20:13Z
next_check2026-08-16 23:10Z
cadenceevery 3h
verifiedtools_list:passed handshake:passed metadata:passed tools_list:passed handshake:passed metadata:passed tools_list:passed handshake:passed metadata:passed tools_list:passed
index_statusindex — 9 unique facts >= 5
06Badge
Add the “as seen on MCPExplorer” badge to your README.
[](https://mcpexplorer.com/servers/openislands)
Next step
This is one server. A loadout combines the right servers, governance, and proven plays for a whole job — assembled deliberately, not tool-dumped.
Explore loadouts →