opensheet
OpenSheet
没有数据库的数据库 UI。
你的表格就是磁盘上的普通 .jsonl 文件。编辑一个单元格——git diff 只显示一行变化。
关闭 OpenSheet 后,jq、DuckDB、Excel、pandas 和你的 AI 代理仍然可以读取所有内容。
这是什么
电子表格数据库工具必须把你的行数据放在某个地方。常见的答案是:一个你还得自己运行的数据库、 一个随应用附带的存储引擎,或者一个应用自有格式的单一二进制文件。这三种方案都能用, 但都意味着这个工具横亘在你和数据之间。
OpenSheet 的数据源是一个你已经知道怎么读的文本文件。一个文件夹就是一个库,
里面的每个 .jsonl 就是一张表,一行就是一条记录。没有数据库、没有账号、没有服务器、
没有同步。删掉 OpenSheet,你的数据纹丝不动。
$ 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这就是全部存储格式。电子表格 UI 只是它之上的一个视图,而不是包裹它的外壳。
Related MCP server: jsony-mcp
快速开始
需要 Python 3.9+。无需 pip install、无需 Docker、无需构建步骤。
git clone https://github.com/newgepard/opensheet
cd opensheet
PYTHONPATH=src python3 -m opensheet ~/my-library/它会在浏览器中打开 http://127.0.0.1:<free port>。把它指向一个包含 .jsonl 文件的文件夹、
指向单个 .jsonl 文件,或者指向一个空文件夹从零开始。
macOS 桌面应用 —— 一个 860 KB 的原生外壳(Swift + WKWebView,使用系统 WebKit):
bash shell/build.sh
open shell/build/OpenSheet.app保真承诺
其他一切都围绕这一点构建,而且它是经过测试验证的,不是嘴上说说。
1. 未触碰的行按字节原样写回。 在一个 10,000 行的表中编辑一个单元格,
git diff 恰好显示一行变化。你没有碰过的行绝不会被重新序列化——不会重排、不会重格式化、
不会重新转义。
2. 非 ASCII 文本绝不转义。 {"名前":"甲"} 保持原样。它绝不会变成
{"\u540d\u524d":"\u7532"}——那是 json.dumps 的默认行为,会让文件在除 JSON 解析器之外的
所有工具里都不可读。
3. 稀疏文件保持稀疏。 如果一行没有某个键,OpenSheet 不会为它凭空造一个。 给一个有 50,000 行现有数据的表添加字段,不会重写其中任何一行。
撤销是作为逆命令实现的,而不是快照回滚,正是为了让承诺 1 在撤销后依然成立:
编辑一个单元格,按 ⌘Z,保存——git diff 是空的。快照恢复实现看起来在屏幕上是对的,
却会悄悄重新序列化它碰过的每一行。

支撑这一点的是:指纹检查拒绝覆盖在 OpenSheet 背后发生的更改,写入通过临时文件加
os.replace() 完成,每次保存都会在打开时的快照旁边保留一个 .bak。
功能特性
你的数据是你拥有的文件 —— 纯
.jsonl,jq、DuckDB、pandas、Excel 和 git 都能读一切皆可撤销 ——
⌘Z/⇧⌘Z;批量删除会折叠成一步撤销13 种字段类型 —— 文本、数字、单选、多选、日期、复选框、URL、附件、公式、 跨表查找、创建/更新时间、自动编号。数字可以渲染为进度条、货币、评分或百分比 而不改变存储的值
4 种视图类型 —— 网格、看板、画廊,外加每个视图的筛选、排序、分组、统计、 行高、冻结列和列顺序
10,000 行依然流畅 —— 虚拟滚动;实测加载 7.5 MB 的表耗时 57 ms,保存耗时 59 ms
导入和导出 —— 从 CSV、XLSX、JSON、SQLite 和
.grist导入;导出为 CSV、XLSX、JSON、 JSONL、Markdown 和 Parquet附件保持本地 —— 文件存放在
<library>/assets/,单元格保存相对路径, 不会上传到任何地方
为代理而生,而不是用 AI 装饰
给电子表格加一个 AI 按钮是一个功能。选择一种代理无需适配器就能读写存储格式则是一种架构。
OpenSheet 选择了后者,其他一切都由此而来:文件是面向行的,所以追加就是 >>;
格式是文本,所以 grep 能用;写入者是单一的,所以字节保真在并发访问下依然成立。
一个 MCP 服务器与 HTTP 服务器运行在同一个进程中,共享同一个 Store 实例:
{
"mcpServers": {
"opensheet": {
"command": "python3",
"args": ["-m", "opensheet", "/path/to/libraries", "--mcp", "--mcp-root", "/path/to/libraries"],
"env": { "PYTHONPATH": "/path/to/opensheet/src" }
}
}
}工具:list_libraries、list_tables、get_schema、query、upsert_rows。
upsert_rows 返回一张回执——inserted / updated / unchanged 计数以及每个的主键——
并支持 dry_run。与磁盘上已有内容完全相同的行计为 unchanged,文件不会被触碰。
同一进程不是实现细节。 字节保真依赖于进程内状态:每一行到达时的字节是什么。 两个各自持有自己副本的进程会各自认为对方的写入是"更改",并在下次保存时重新序列化整个文件。 第二个进程会检测到锁文件并转发给第一个进程,所以始终只有一个写入者。
代理也可以绕过 OpenSheet 直接追加到文件。它们这样做时保持保真完整的规则记录在 直接写入指南 中。
架构
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/ 存放字段类型、列宽、视图定义和跨表链接。删掉它,你失去的是格式,不是数据——
字段类型会回退到推断。
为什么它保持小巧
后端只用 Python 标准库。前端是纯 JavaScript——没有框架、没有构建步骤。 macOS 外壳是 184 KB 的 Swift,跑在系统 WebKit 上,而不是打包一个浏览器。
DuckDB 是可选的,而且是只读的,服务于 SQL 面板和 Parquet 导出。它从不参与筛选、
排序或分组,也从不写入。所有写入都经过 store.py。
按构造无网络
没有遥测、没有更新检查、没有 CDN 资源、没有在线演示。浏览器只请求同源的 /api/* 路径,
服务器只绑定 IPv4 回环地址,桌面外壳自身不持有任何 TCP 连接。
tests/test_offline.py 强制执行所有这些。
验证
bash tests/opensheet-全量验收-20260815.sh329 个测试:188 个 JavaScript 单元测试、83 个 Python 测试、42 个浏览器端到端测试(Playwright)、 8 个保真往返测试、8 个库往返测试。 保真测试是逐字节比较文件,而不是逐字段比较——一个只检查值的测试会在承诺 1 被悄悄破坏时 依然通过。
浏览器测试需要在 web/ 内执行 npm install 来安装 Playwright 的 Chromium。
OpenSheet 不会做什么
评论、权限、实时协作、自动化、托管版本、在线演示。 每一项都需要账号系统或公网地址,这与第一条设计约束相矛盾:数据绝不离开你的机器。
这是一个刻意的边界,而不是路线图的缺口。
状态
已在日常使用中正常工作,但有两个粗糙之处值得你在尝试前了解:
UI 目前只有中文。 代码、注释和界面字符串都是中文的。英文本地化还没做。
桌面外壳仅限 macOS。 Web 版可以在任何能运行 Python 的地方运行。
贡献
欢迎提交 Issue 和 PR。在触碰存储层之前,有两点需要了解:
store.py中的行透传逻辑就是产品本身。修改它意味着要保持保真测试通过, 而那些测试比较的是字节,不是字段。代码注释目前是中文的。非常欢迎翻译帮助。
许可证
MIT —— 见 LICENSE。
Available Tools
5 toolsget_schemaC
读取表字段 schema
| Name | Required | Description | Default |
|---|---|---|---|
| table | Yes | ||
| library | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The verb '读取' indicates a read-only, non-mutating operation, which is useful since siblings include upsert_rows. However, with no annotations, the description carries the full burden and does not disclose return format, permissions, or potential errors, so transparency is only partial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single short sentence with no filler, and the core action is front-loaded. It is concise, though slightly under-specified, which is more a completeness issue than a structure problem.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no annotations, no output schema, and 0% parameter description coverage, the definition leaves the agent without enough context about how to supply library/table values or what the returned schema will look like. Sibling names hint at a workflow, but the description itself does not establish it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%: the input schema only gives parameter names and types. The description mentions '表字段' (table fields) but leaves 'library' unexplained, and provides no value formats or how to obtain valid inputs, so it does not compensate for the missing schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description '读取表字段 schema' clearly states a read operation on a table's field schema, with a specific resource and scope. It is distinguishable from siblings like query (data retrieval) and list_tables (table enumeration), though it does not name them explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool instead of list_tables or query, and no exclusions or prerequisite steps are mentioned. The intended workflow (first list libraries/tables, then fetch schema) is only inferable from sibling names, not stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_librariesA
列出根目录内的 OpenSheet 库
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It clearly indicates a read-only listing operation scoped to the root directory, but it does not disclose details such as pagination, sorting, whether nested libraries are included, or what the response shape is. For a no-parameter list operation, this is adequate but minimal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence that immediately communicates the action and scope. There is no filler or redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the zero-parameter schema and no output schema, the description is nearly sufficient: it names the action, resource, and scope. Minor gaps include no statement about non-recursive behavior or return format, but the tool is simple enough that these are not critical.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is no schema burden to compensate for. The description appropriately focuses on output scope rather than parameter details, matching the baseline for parameterless tools.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('列出') and a specific resource ('根目录内的 OpenSheet 库'), clearly distinguishing this from siblings like list_tables, which lists tables rather than libraries. The scope 'root directory' adds useful precision.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description states what the tool does but gives no guidance on when to choose it over alternatives, nor any exclusions or prerequisites. An agent must infer from sibling names and the description that this is the tool for listing libraries, not tables or schemas.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tablesB
列出库里的 JSONL 表
| Name | Required | Description | Default |
|---|---|---|---|
| library | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description bears the full burden. The verb '列出' implies a read-only listing operation, but the description does not disclose output format, pagination, error behavior, or whether any library validation occurs. It is minimally transparent but not richly so.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single short sentence with no redundant words. The operation and scope are front-loaded, making it easy to parse quickly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one required parameter and no output schema, the description gives only minimal context. It does not explain what a 'JSONL 表' is exactly, what the agent should do with the returned list, or how to obtain a valid library value. The ambiguity around 'JSONL' and library selection leaves meaningful gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. The phrase '库里的' clarifies that the 'library' parameter identifies which library's tables are listed. However, it does not explain valid library identifier formats, whether values come from list_libraries, or how missing/invalid libraries are handled.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the operation ('列出' / list) and the resource ('JSONL 表' / tables) scoped to a library. It is easily distinguished from siblings like list_libraries, though it does not explicitly name the sibling it differs from.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is given on when to use this tool versus siblings such as get_schema, query, or list_libraries. The description only states what it does, not when it should be chosen or what prerequisite steps (like selecting a library) are needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
queryB
用只读 DuckDB SQL 查询表
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | ||
| table | Yes | ||
| library | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It does disclose the critical read-only trait, which is essential for an agent to know this tool will not mutate data. It says nothing about return shape, SQL restrictions beyond read-only, error behavior, or permission requirements, so transparency is partial rather than complete.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence with no filler. The key behavioral qualifier 'read-only' is front-loaded, and every word contributes meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with three required parameters, zero schema descriptions, and no annotations, the description is too sparse. An agent still lacks clarity about the 'library' parameter, whether results are returned in a specific format, and what SQL constructs are allowed beyond the read-only restriction.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the description adds little to parameter meaning beyond what the parameter names already imply. 'sql' and 'table' are self-explanatory, but 'library' remains ambiguous and there is no guidance on how the three parameters relate or what format the SQL should follow.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action—querying tables via DuckDB SQL—and explicitly marks it as read-only, which distinguishes it from upsert_rows. It does not explicitly name sibling tools or contrast its scope with list/get_schema tools, so it is clear but not maximally differentiated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The read-only qualifier implies this tool is for SELECT-style data retrieval and not for modifications, giving some usage direction. However, it does not explicitly state when to choose this over list_libraries, list_tables, or get_schema, leaving the routing mostly to inference from the tool name and siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
upsert_rowsA
按指定主键批量新增或更新,走与 HTTP 相同的 Store 并发保护。返回回执:inserted / updated / unchanged 计数与各自的主键值列表。dry_run=true 只算回执不落盘。
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes | ||
| rows | Yes | ||
| table | Yes | ||
| dry_run | No | 只返回将发生的变更,不写文件 | |
| library | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full responsibility for behavior disclosure, and it delivers: it mentions Store concurrency protection, the exact receipt format (inserted/updated/unchanged counts and key lists), and the no-persistence behavior of dry_run=true. It does not cover error or validation behavior, but the most decision-relevant side effects are explicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three compact sentences cover the core operation, concurrency protection, return receipt, and dry-run behavior. The most essential verb and scope are front-loaded with no redundant filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description compensates for the missing output schema by stating the receipt shape, and it covers the operation's key nuances. It lacks row-structure details and failure semantics, which would help for a 5-parameter mutation tool, but it is sufficiently complete for a competent agent to call the tool correctly in the common case.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 20%, with only dry_run documented. The description adds meaning by identifying key as the primary key, rows as the batch payload, and clarifying dry_run semantics. However, it does not explain how rows should encode the key or what values library/table expect, leaving part of parameter construction to inference.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with '按指定主键批量新增或更新', a specific verb (upsert) and a clear resource (rows keyed by primary key). This immediately distinguishes it from the read-only and metadata sibling tools like query and list_tables, so an agent can tell what it does without needing more context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description conveys that the tool is for batch insert-or-update operations by primary key and even highlights the dry_run inspection mode. However, it never explicitly contrasts it with alternatives such as query for reads or states when not to use it. The usage context is clear but the tool-selection guidance is only implied, not spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
get_schema - First observed
list_libraries - First observed
list_tables - First observed
query - First observed
upsert_rows
TDQS
Scored across 5 tools
Each tool maps to a distinct action and resource level: libraries, tables, schema, query, and upsert. There is no meaningful overlap, and the descriptions reinforce the boundaries clearly.
Most tool names follow a clear verb_noun snake_case pattern: list_libraries, list_tables, get_schema, upsert_rows. The single-word 'query' is a minor deviation, but the overall naming style remains uniform and predictable.
Five tools is well-scoped for an OpenSheet data access server. Each tool covers a necessary step in the workflow without redundancy or bloat.
The set covers the core workflow well: discovering libraries, listing tables, inspecting schema, querying data, and upserting rows. The main gap is the lack of any delete or schema-mutation capability, but that may be intentionally out of scope for this server.
Maintenance
Related MCP Connectors
Every spreadsheet is a table: Excel, CSV, Parquet and JSON as SQL tables for AI agents.
The all-in-one data stack for agents. Upload files, run SQL, evolve tables, and render charts.
Store, query, and update structured data from any AI agent
Push CSV, JSON, JSONL or Parquet from your agent and get a link a human can explore.
Related MCP Servers
- AlicenseAqualityCmaintenanceTurns 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.622 PyPI2MIT
- AlicenseAqualityDmaintenanceEnables AI assistants to read, write, query, and manage JSON data files with automatic ID and timestamp generation.614 npm1MIT
- AlicenseBqualityDmaintenanceEnables AI agents to interact with local SQLite databases with full CRUD, schema introspection, foreign key relations, generated columns, and multi-format import/export (CSV, JSON, XLSX) through natural language.269 npmMIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to keep structured, typed records in SQLite sheets with stable row ids, versions, and per-write audit history, so data survives across sessions and multiple agents. Lookups are fuzzy with an explicit match status, writes are version-checked and dry-run by default, and sheets can be created, queried, searched, and analyzed through MCP tools.-