Skip to main content
Glama
newgepard

opensheet

by newgepard

OpenSheet

A database UI without the database.

Your tables are plain .jsonl files on disk. Edit one cell — git diff shows one line. Close OpenSheet, and jq, DuckDB, Excel, pandas and your AI agents can still read everything.

License: MIT Runtime dependencies: 0 Tests: 329 Network calls: 0

中文文档


What is this

Spreadsheet-database tools have to put your rows somewhere. The usual answers are a database you also have to run, a storage engine that ships with the app, or a single binary file in the app's own format. All three work. All three mean the tool sits between you and your data.

OpenSheet's source of truth is a text file you already know how to read. One folder is a library, each .jsonl inside it is a table, one line is a record. There is no database, no account, no server, no sync. Delete OpenSheet and your data doesn't move an inch.

$ cat my-library/campaigns.jsonl
{"id":"KS-001","project":"Modular Mechanical Keyboard","raised_usd":412870,"rating":5}
{"id":"KS-002","project":"Solar Camp Lantern","raised_usd":88250,"rating":4}

$ jq -r 'select(.rating == 5) | .project' my-library/campaigns.jsonl
Modular Mechanical Keyboard

That's the whole storage format. The spreadsheet UI is a view onto it, not a wrapper around it.

Related MCP server: Universal JSON Agent MCP

Quick start

Requires Python 3.9+. No pip install, no Docker, no build step.

git clone https://github.com/newgepard/opensheet
cd opensheet
PYTHONPATH=src python3 -m opensheet ~/my-library/

It opens http://127.0.0.1:<free port> in your browser. Point it at a folder of .jsonl files, at a single .jsonl file, or at an empty folder to start from scratch.

macOS desktop app — an 860 KB native shell (Swift + WKWebView, uses the system WebKit):

bash shell/build.sh
open shell/build/OpenSheet.app

The fidelity promise

This is what everything else is built around, and it is tested rather than asserted.

1. Untouched rows are written back byte-for-byte. Edit one cell in a 10,000-row table and git diff shows exactly one changed line. Rows you didn't touch are never re-serialized — not reordered, not reformatted, not re-escaped.

2. Non-ASCII text is never escaped. {"名前":"甲"} stays exactly that. It never turns into {"\u540d\u524d":"\u7532"}, which is what json.dumps does by default and what makes a file unreadable in every tool that isn't a JSON parser.

3. Sparse files stay sparse. If a row doesn't have a key, OpenSheet will not invent one for it. Adding a field to a table with 50,000 existing rows rewrites zero of them.

Undo is built as inverse commands, not snapshot rollback, specifically so promise 1 survives it: edit a cell, press ⌘Z, save — git diff is empty. A snapshot-restore implementation would look correct on screen while quietly re-serializing every row it touched.

Editing one cell; git diff shows a single changed line

Backing this up: a fingerprint check refuses to overwrite changes made behind OpenSheet's back, writes go through a temp file plus os.replace(), and every save keeps a .bak alongside an open-time snapshot.

Features

  • Your data is a file you own — plain .jsonl, readable by jq, DuckDB, pandas, Excel and git

  • Undo everything⌘Z / ⇧⌘Z; a batch delete collapses into a single undo step

  • 13 field types — text, number, select, multi-select, date, checkbox, URL, attachment, formula, cross-table lookup, created/updated time, auto-number. Numbers render as progress bars, currency, ratings or percentages without changing the stored value

  • 4 view types — grid, kanban, gallery, plus per-view filters, sorts, grouping, statistics, row height, frozen columns and column order

  • 10,000 rows stay smooth — virtual scrolling; measured at 57 ms to load and 59 ms to save a 7.5 MB table

  • Import and export — in from CSV, XLSX, JSON, SQLite and .grist; out to CSV, XLSX, JSON, JSONL, Markdown and Parquet

  • Attachments stay local — files land in <library>/assets/, the cell holds a relative path, nothing is uploaded anywhere

Built for agents, not decorated with AI

Adding an AI button to a spreadsheet is a feature. Choosing a storage format an agent can read and write without an adapter is an architecture. OpenSheet did the second one, and everything else follows from it: the file is line-oriented so appends are >>, the format is text so grep works, and the writer is single so byte fidelity survives concurrent access.

An MCP server runs in the same process as the HTTP server, sharing one Store instance:

{
  "mcpServers": {
    "opensheet": {
      "command": "python3",
      "args": ["-m", "opensheet", "/path/to/libraries", "--mcp", "--mcp-root", "/path/to/libraries"],
      "env": { "PYTHONPATH": "/path/to/opensheet/src" }
    }
  }
}

Tools: list_libraries, list_tables, get_schema, query, upsert_rows. upsert_rows returns a receipt — inserted / updated / unchanged counts plus the primary keys for each — and supports dry_run. Rows identical to what's already on disk count as unchanged, and the file is not touched.

Same process is not an implementation detail. Byte fidelity depends on in-process state: which bytes each row arrived as. Two processes each holding their own copy would each conclude the other's writes were "changes" and re-serialize the whole file on the next save. A second process detects the lock file and forwards to the first, so there is always exactly one writer.

Agents can also skip OpenSheet and append to the file directly. The rules that keep fidelity intact when they do are documented in the direct-write guide.

Architecture

graph TD
    A["my-library/"] --> B["campaigns.jsonl<br/>source of truth"]
    A --> C["suppliers.jsonl"]
    A --> D[".opensheet/<br/>schema · views · links"]
    B --> E["store.py<br/>row passthrough · atomic write"]
    C --> E
    E --> F["single process"]
    F --> G["HTTP + browser UI"]
    F --> H["MCP over stdio"]
    G --> I["native macOS shell"]

.opensheet/ holds field types, column widths, view definitions and cross-table links. Delete it and you lose formatting, not data — field types fall back to inference.

Why it stays small

The backend is Python standard library only. The frontend is plain JavaScript — no framework, no build step. The macOS shell is 184 KB of Swift on the system WebKit, not a bundled browser.

DuckDB is optional and read-only, serving the SQL panel and Parquet export. It never participates in filtering, sorting or grouping, and it never writes. Every write goes through store.py.

No network, by construction

No telemetry, no update checks, no CDN assets, no online demo. The browser only requests same-origin /api/* paths, the server binds to the IPv4 loopback only, and the desktop shell holds zero TCP connections of its own. tests/test_offline.py enforces all of this.

Verification

bash tests/opensheet-全量验收-20260815.sh

329 tests: 188 JavaScript unit, 83 Python, 42 browser end-to-end (Playwright), 8 fidelity round-trip, 8 library round-trip. The fidelity tests compare files byte-for-byte rather than field-by-field — a test that only checked values would pass while promise 1 was silently broken.

Browser tests need npm install inside web/ for Playwright's Chromium.

What OpenSheet will not do

Comments, permissions, real-time collaboration, automations, a hosted version, an online demo. Each of them needs an account system or a public address, which contradicts the first design constraint: data never leaves your machine.

This is a deliberate boundary, not a roadmap gap.

Status

Working and used daily, with two rough edges worth knowing before you try it:

  • The UI is currently Chinese-only. The code, comments and interface strings are all in Chinese. English localisation hasn't been done yet.

  • macOS only for the desktop shell. The web version runs anywhere Python does.

Contributing

Issues and PRs welcome. Two things to know before touching the storage layer:

  1. The row-passthrough logic in store.py is the product. Changing it means keeping the fidelity tests green, and those compare bytes, not fields.

  2. Code comments are currently in Chinese. Translation help is very welcome.

License

MIT — see LICENSE.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

0Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    Provides AI agents with database-like operations over LanceDB with automatic BGE-M3 multilingual embedding generation, enabling semantic search, CRUD operations, and safe schema migrations across structured data.
  • A
    license
    A
    quality
    B
    maintenance
    Turns a folder of CSV, Parquet, and JSON files into a single SQL-queryable source for AI agents, supporting JOINs across files with read-only sandboxed access.
    6
    2
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to read, write, query, and manage JSON data files with automatic ID and timestamp generation.
    6
    20
    1
    MIT

View all related MCP servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/newgepard/opensheet'

If you have feedback or need assistance with the MCP directory API, please join our Discord server