loading…
Search for a command to run...
loading…
Headless WorkPaper MCP server for spreadsheet formulas, workbook edits, JSON persistence, and verified readback from TypeScript services.
Headless WorkPaper MCP server for spreadsheet formulas, workbook edits, JSON persistence, and verified readback from TypeScript services.
CI GitHub Repo stars npm: @bilig/headless npm weekly downloads MCP server score License: MIT
bilig is a headless workbook runtime for TypeScript. Use it when the spreadsheet is the model, not the UI: pricing rules, finance checks, agent tools, import jobs, queue workers, and serverless routes that need formulas to recalculate the same way every run.
Project site: https://proompteng.github.io/bilig/
| Good fit | Use something else when |
|---|---|
| A Node service needs workbook formulas, named sheets, edits, and readback | You only need a visual grid component |
| An agent tool must write an input cell and return the calculated result | You need desktop Excel automation or Office macros |
| A backend job needs to save and restore formula-backed workbook state | Your workflow starts and ends with manual XLSX editing |
| Tests need a deterministic workbook model instead of screenshots | You want a general-purpose table library with no spreadsheet formula model |
24 GitHub stars, 12 forks, 15,592 npm downloads in the
last week, 23,240 npm downloads in the last 30 days, 77 open
good first issue tickets, 9 GitHub Discussions, and 455 recent
repository views.If the 90-second check below saves you a workbook automation spike, star the repo so you can find it again: https://github.com/proompteng/bilig/stargazers.
| Job | Start with |
|---|---|
| Prove the npm package | @bilig/headless in 90 seconds and the npm-only smoke test page |
| Build a Node workflow | server-side spreadsheet automation, five runnable workbook automation examples, framework adapters, evaluate Excel formulas in Node.js, and the Node service recipe |
| Wire an agent or MCP tool | agent tool-calling recipe, MCP spreadsheet tool server guide, MCP directory status, MCP client setup, and Claude Desktop MCPB bundle |
| Compare alternatives | JavaScript spreadsheet library guide, headless engine comparison, HyperFormula comparison |
| Contribute | starter issues, GitHub Discussions, and CONTRIBUTING.md |
Useful direct paths:
npm run agent:framework-adapters, npm run agent:mcp-tools,
npm run agent:mcp-stdio, or
pnpm mcpb:workpaper:build, or
npm exec --package @bilig/headless -- bilig-workpaper-mcp.The published MCP Registry entry is io.github.proompteng/bilig-workpaper.
@bilig/headless in 90 secondsThe fastest evaluation path uses the published npm package only. It builds a formula-backed workbook, applies an edit, persists the document, restores it, and throws if formula readback does not survive the round trip.
mkdir bilig-headless-eval
cd bilig-headless-eval
npm init -y
npm pkg set type=module
npm install @bilig/headless
npm install -D tsx typescript @types/node
Create eval.ts:
import {
WorkPaper,
createWorkPaperFromDocument,
exportWorkPaperDocument,
parseWorkPaperDocument,
serializeWorkPaperDocument,
} from '@bilig/headless'
type NumericCell = {
value: number
}
const workbook = WorkPaper.buildFromSheets({
Revenue: [
['Region', 'Customers', 'ARPA', 'Revenue'],
['West', 20, 1200, '=B2*C2'],
['East', 30, 250, '=B3*C3'],
['Central', 18, 300, '=B4*C4'],
],
Summary: [
['Metric', 'Value'],
['Total revenue', '=SUM(Revenue!D2:D4)'],
],
})
function numberValue(cell: unknown): number {
if (typeof cell === 'object' && cell !== null && typeof (cell as NumericCell).value === 'number') {
return (cell as NumericCell).value
}
throw new Error(`Expected numeric cell value, got ${JSON.stringify(cell)}`)
}
const revenue = workbook.getSheetId('Revenue')
const summary = workbook.getSheetId('Summary')
if (revenue === undefined || summary === undefined) {
throw new Error('Workbook sheets were not created')
}
const before = numberValue(workbook.getCellValue({ sheet: summary, row: 1, col: 1 }))
workbook.setCellContents({ sheet: revenue, row: 1, col: 1 }, 32)
const saved = serializeWorkPaperDocument(exportWorkPaperDocument(workbook, { includeConfig: true }))
const restored = createWorkPaperFromDocument(parseWorkPaperDocument(saved))
const restoredSummary = restored.getSheetId('Summary')
if (restoredSummary === undefined) {
throw new Error('Summary sheet was not restored')
}
const after = numberValue(restored.getCellValue({ sheet: restoredSummary, row: 1, col: 1 }))
const verified = before === 36900 && after === 51300 && saved.length > 0
if (!verified) {
throw new Error(`Unexpected formula readback: ${JSON.stringify({ before, after, bytes: saved.length })}`)
}
console.log({ before, after, sheets: restored.getSheetNames(), bytes: saved.length, verified })
Run it:
npx tsx eval.ts
Expected output:
{
"before": 36900,
"after": 51300,
"sheets": ["Revenue", "Summary"],
"bytes": 1064,
"verified": true
}
The maintained repository example adds agent-style writeback verification:
git clone https://github.com/proompteng/bilig.git
cd bilig/examples/headless-workpaper
npm install
npm start
npm run agent:tool-call
npm run agent:framework-adapters
npm run agent:verify
Expected proof from npm run agent:tool-call includes:
{
"toolCall": { "toolName": "setInputCell" },
"toolResult": {
"editedCell": "Inputs!B3",
"before": { "expectedArr": 60000, "targetGap": -34000 },
"after": { "expectedArr": 96000, "targetGap": 5600 },
"verified": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrImproved": true,
"targetGapClosed": true
}
}
}
Expected proof from npm run agent:verify includes:
{
"after": {
"customers": 65,
"grossMrr": 15600,
"expansionMrr": 18720,
"annualizedArr": 224640,
"arrTargetDelta": 74640
},
"verified": {
"formulasUnchanged": true,
"formulasPersisted": true,
"restoredMatchesAfter": true
}
}
The serverless route example gives HTTP and agent-tool evaluators a runnable JSON boundary:
git clone https://github.com/proompteng/bilig.git
cd bilig/examples/serverless-workpaper-api
npm install
npm run next-route-handler
npm run smoke
npm run persistence-adapters
Expected proof from npm run next-route-handler, npm run smoke, and
npm run persistence-adapters includes:
{
"edit": {
"records": 4,
"after": {
"totalRevenue": 48600,
"westCustomers": 20,
"largestDeal": 24000
},
"checks": {
"totalRevenueChanged": true,
"formulasPersisted": true
}
},
"verified": true
}
It is not a table widget. The repo contains a real workbook engine, formula parser/compiler, React workbook reconciler, reusable grid shell, binary sync protocol, agent API, browser/server persistence layers, and a conservative AssemblyScript/WASM fast path for formula families that have proven parity.
The long-term target is a spreadsheet platform that can be edited by people or agents, restored locally, synchronized through ordered mutation streams, and benchmarked against serious spreadsheet-engine workloads.
@bilig/core.@bilig/formula, with fixture-driven parity checks.apps/web and @bilig/grid.@bilig/renderer.apps/bilig monolith, which serves
the built web shell and backend APIs.packages/benchmarks,
scripts/, and e2e/tests.pnpm wasm:build.bilig is early, serious infrastructure. The architecture is broad and the correctness bar is intentionally high, but it is not a finished Excel clone.
Known open areas include:
Start here when you want to use the spreadsheet engine from Codex, Claude Code, a service, or a Node script without opening the browser UI.
@bilig/headless is production-ready for applications that call the documented
WorkPaper API directly. The package README is the contract for install, API
usage, persistence, validation, supported scope, and agent workflow:
packages/headless/README.md.
For the shortest public method map, start with the
WorkPaper read/write cheat sheet.
Install from npm:
pnpm add @bilig/headless
Try the package without cloning the monorepo:
mkdir bilig-headless-eval
cd bilig-headless-eval
npm init -y
npm pkg set type=module
npm install @bilig/headless
Create eval.ts with the quickstart below, then run npx tsx eval.ts. The
example builds a formula-backed workbook, edits source data, serializes the
document, restores it, and verifies that the recalculated value survives the
round trip.
For a runnable external-consumer example, start with
examples/headless-workpaper. The repository smoke
test executes that same example against packed local runtime packages with
pnpm workpaper:smoke:external.
For backend adoption, see docs/node-service-workpaper-recipe.md. It shows a minimal Node service boundary that reads computed summaries, applies one controlled edit, and persists the WorkPaper document.
For tabular service payloads, see docs/csv-shaped-workpaper-input-recipe.md. It normalizes a small CSV-shaped fixture into a WorkPaper workbook and reads formula-backed summary values.
For JSON service/API payloads, the runnable example includes
npm run json-records. It
maps an array of opportunity records into WorkPaper.buildFromSheets(), adds
formula-backed summary cells, and validates exact computed output before
printing JSON.
For billing-style service payloads, the runnable example includes npm run invoice-totals. It calculates line-item totals, subtotal, tax, and grand total formulas, then validates exact computed and serialized formula readback.
For reporting and finance automation, the runnable example includes npm run budget-variance. It compares budget and actual rows, calculates dollar and percent variance, and flags rows that need review with a formula-backed alert.
For subscription revenue forecasting, the runnable example includes npm run subscription-mrr. It models starting customers, churn, expansion, and new customers, then prints starting MRR, ending MRR, net expansion MRR, and verified formula readback.
For sales-ops quote workflows, the runnable example includes npm run quote-approval. It calculates list total, discount amount, quote total, max line discount, and an approval flag from formula-backed quote rows.
For operations planning, the runnable example includes npm run fulfillment-capacity. It compares forecast order volume with available labor hours, calculates required hours, capacity gap, short days, and a formula-backed status.
For formula errors, see
docs/unsupported-formula-troubleshooting-recipe.md.
It shows how to read #VALUE!/#NAME? display text together with structured
diagnostics so services and agents can reject or normalize unsupported inputs.
That example also includes npm run agent:verify, a small agent writeback demo
that records the exact assumption cells changed, verifies dependent formula
readback, persists the workbook, restores it, and proves the formulas and values
survived the round trip.
For a tool-calling shape closer to agent SDKs, run npm run agent:tool-call.
It returns a compact tool call, before/after computed values, formula
contracts, persistence proof, and round-trip verification.
For Vercel AI SDK and LangChain-shaped wrappers, run
npm run agent:framework-adapters. The example keeps the same validated
WorkPaper read/write functions and exposes thin framework adapter shapes
without adding either framework as a dependency.
For an MCP-style shape, run npm run agent:mcp-tools. It returns a
dependency-free tools/list response, a tools/call read, and a verified
input edit with structured computed readback. Run npm run agent:mcp-stdio
when you want the same tools over newline-delimited JSON-RPC stdio.
The package-level stdio binary is bilig-workpaper-mcp, runnable with
npm exec --package @bilig/headless -- bilig-workpaper-mcp.
It is published in the official MCP Registry as
io.github.proompteng/bilig-workpaper:
https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.proompteng%2Fbilig-workpaper.
For Claude Desktop bundle installs, build
build/mcpb/bilig-workpaper.mcpb with pnpm mcpb:workpaper:build; the
MCPB guide documents the manifest and
verification prompt.
Quickstart:
import {
WorkPaper,
createWorkPaperFromDocument,
exportWorkPaperDocument,
parseWorkPaperDocument,
serializeWorkPaperDocument,
type WorkPaperCellAddress,
} from '@bilig/headless'
type NumericCell = {
value: number
}
const workbook = WorkPaper.buildFromSheets(
{
Revenue: [
['Region', 'Customers', 'ARPA', 'Revenue'],
['West', 20, 1200, '=B2*C2'],
['East', 30, 250, '=B3*C3'],
['Central', 18, 300, '=B4*C4'],
],
Summary: [
['Metric', 'Value'],
['Total revenue', '=SUM(Revenue!D2:D4)'],
],
},
{ maxRows: 1_000, maxColumns: 100, useColumnIndex: true },
)
function numberValue(cell: unknown): number {
if (typeof cell === 'object' && cell !== null && typeof (cell as NumericCell).value === 'number') {
return (cell as NumericCell).value
}
throw new Error(`Expected numeric cell value, got ${JSON.stringify(cell)}`)
}
const revenue = workbook.getSheetId('Revenue')
const summary = workbook.getSheetId('Summary')
if (revenue === undefined || summary === undefined) {
throw new Error('Workbook sheets were not created')
}
const at = (row: number, col: number): WorkPaperCellAddress => ({
sheet: summary,
row,
col,
})
const before = numberValue(workbook.getCellValue(at(1, 1)))
workbook.setCellContents({ sheet: revenue, row: 1, col: 1 }, 32)
const saved = serializeWorkPaperDocument(exportWorkPaperDocument(workbook, { includeConfig: true }))
const restored = createWorkPaperFromDocument(parseWorkPaperDocument(saved))
const restoredSummary = restored.getSheetId('Summary')
if (restoredSummary === undefined) {
throw new Error('Summary sheet was not restored')
}
const after = numberValue(
restored.getCellValue({
sheet: restoredSummary,
row: 1,
col: 1,
}),
)
console.log({ before, after, sheets: restored.getSheetNames(), bytes: saved.length })
Rules for agents:
@bilig/headless; do not reach into src/ or
dist/ unless the task is to change the package itself.{ sheet, row, col }; resolve sheet ids with
getSheetId().exportWorkPaperDocument() and createWorkPaperFromDocument() for
persistence round trips.pnpm publish:runtime:check,
pnpm workpaper:bench:competitive:check, and pnpm run ci before publishing
or claiming production readiness.For workflow feedback from people building Node services or agent tools, use the GitHub discussion: https://github.com/proompteng/bilig/discussions/157.
For the five runnable service-workflow examples as one shareable thread, see: https://github.com/proompteng/bilig/discussions/213.
For the search-friendly version with commands and current output snippets, see docs/workbook-automation-examples-node.md.
For the first technical adoption article, see docs/why-agents-need-workbook-apis.md. It explains why agents should operate on workbook APIs instead of spreadsheet screenshots.
For a concrete framework-neutral agent tool loop, see docs/agent-workpaper-tool-calling-recipe.md. It wraps WorkPaper reads, validated writes, computed before/after checks, and persistence into a small tool surface.
For the persistence-focused follow-up article and runnable example, see docs/persisting-formula-backed-workpaper-documents-in-node.md and examples/headless-workpaper/persistence-roundtrip.ts. For service storage, run the typed Postgres JSONB, Redis/KV, and object-storage adapters in examples/serverless-workpaper-api/persistence-adapters.ts.
For the benchmark-focused explainer, see
docs/what-workpaper-benchmark-proves.md.
It states the 46/46 mean-win claim and the known p95 caveat without turning
the benchmark into a blanket performance claim.
For a local benchmark command walkthrough, see docs/local-workpaper-benchmark-walkthrough.md. It shows how to verify the checked-in artifact, run a reduced local smoke benchmark, and compare benchmark diffs.
For a concise HyperFormula comparison and evaluation path, see docs/hyperformula-alternative-headless-workpaper.md.
For a broader headless spreadsheet-engine comparison across @bilig/headless,
HyperFormula, IronCalc, ExcelJS, Formula.js, Hucre, Formualizer, and
JSpreadsheet Formula Pro, see
docs/headless-spreadsheet-engine-comparison.md.
If your search starts with "JavaScript spreadsheet library," use docs/javascript-spreadsheet-library-headless-node.md to separate browser grids, XLSX file tooling, formula engines, and backend WorkPaper runtimes before choosing a package.
For a runnable revenue-model walkthrough, see docs/building-a-revenue-model-with-headless-workpaper.md and examples/headless-workpaper/revenue-scenarios.ts.
For the current Excel-compatibility boundaries, see docs/where-bilig-is-not-excel-compatible-yet.md. It names the macro, formula, XLSX corpus, and UI-claim gaps without treating the project as a complete Excel clone.
For a short guide to interpreting XLSX cached-result corpus reports, see docs/xlsx-corpus-verifier-walkthrough.md.
For a formula-edge fixture walkthrough covering the exact-match XLOOKUP path,
see
docs/formula-edge-xlookup-exact-fixture.md.
For a formula-edge fixture walkthrough covering paired-criteria SUMIFS, see
docs/formula-edge-sumifs-paired-criteria-fixture.md.
For a formula-edge fixture walkthrough covering grouped dynamic-array GROUPBY
output, see
docs/formula-edge-groupby-spill-fixture.md.
For the published DEV article, see https://dev.to/gregkonush/why-agents-need-workbook-apis-instead-of-spreadsheet-screenshots-3d61. The source mirror with front matter is docs/dev-to-workbook-apis-post.md.
Use Node 24+, Bun, and [email protected].
pnpm install
pnpm wasm:build
pnpm typecheck
pnpm test
pnpm dev
The default dev command runs the local web shell and monolith together.
Useful alternatives:
pnpm dev:web
pnpm dev:web-local
pnpm dev:sync
docker compose up --build
This brings up:
http://localhost:3000 for the monolith web shell with /v2,
/api/zero/v2, and /zerohttp://localhost:4321/healthz for the monolith app runtimehttp://localhost:4848/keepalive for Zero cachepostgresql://bilig:bilig@localhost:5432/bilig for PostgresTo reset local state:
docker compose down -v
| Path | Role |
|---|---|
apps/web |
Vite/React browser source compiled into the monolith |
apps/bilig |
Fullstack monolith runtime, API surface, and static asset server |
packages/protocol |
Shared enums, opcodes, constants, and protocol types |
packages/formula |
A1 addressing, lexer, parser, binder, compiler, JS evaluator |
packages/core |
Workbook engine, scheduler, snapshots, selectors, sync ownership, WASM facade |
packages/headless |
Headless WorkPaper runtime surfaces |
packages/zero-sync |
Zero schema, workbook queries, projection, and event payload helpers |
packages/binary-protocol |
Wire format for sync frames |
packages/agent-api |
Agent request, response, event, and framing model |
packages/worker-transport |
Engine host/client bridge for worker execution |
packages/renderer |
Custom workbook reconciler and workbook DSL |
packages/grid |
Reusable React spreadsheet UI components and hooks |
packages/wasm-kernel |
AssemblyScript/WASM compute fast path |
packages/storage-browser |
Browser-side persistence |
packages/storage-server |
Server-side storage integration points |
packages/excel-fixtures |
Checked-in formula parity fixtures |
packages/benchmarks |
Benchmark harness and performance contracts |
The repo has a strict local preflight. For small changes, run the narrowest targeted command first; before publishing, use the full gate.
pnpm lint
pnpm typecheck
pnpm test
pnpm test:browser
pnpm bench:smoke
pnpm run ci
Generated sources are checked in and enforced:
pnpm protocol:check
pnpm formula-inventory:check
pnpm workspace-resolution:check
pnpm workpaper:bench:competitive:check
The WorkPaper track is the repo's performance-leadership program. It compares bilig's spreadsheet runtime against HyperFormula-style workloads and keeps the important claims tied to benchmark artifacts, counters, and docs.
Current public evidence:
packages/benchmarks/baselines/workpaper-vs-hyperformula.json, generated at
2026-05-08T15:00:27.603Z, records WorkPaper 46/46 mean wins on
scorecard-eligible comparable workloads: 38/38 public and 8/8 holdout.docs/assets/workpaper-benchmark-card.png is the shareable chart for the
current scorecard. It is generated from the checked-in artifact with
pnpm docs:benchmark-card:generate.docs/headless-workpaper-benchmark-evidence.md explains what is measured,
what is excluded, and why this is a mean-win claim rather than a blanket p95
guarantee.Start here:
docs/workpaper-engine-leadership-program.mddocs/headless-workpaper-benchmark-evidence.mddocs/what-workpaper-benchmark-proves.mddocs/local-workpaper-benchmark-walkthrough.mddocs/workpaper-oracle-sota-performance-design-2026-04-21.mddocs/workpaper-oracle-validated-performance-design-2026-04-26.mddocs/workpaper-oracle-benchmark-expansion-performance-plan-2026-04-28.mdRun the competitive benchmark with:
pnpm bench:workpaper:competitive
Good entry points:
docs/architecture.mddocs/public-api.mddocs/formula-language.mddocs/agent-api.mddocs/local-first-realtime-loop.mddocs/binary-protocol.mddocs/wasm-runtime-contract.mddocs/testing-and-benchmarks.mdRead CONTRIBUTING.md before opening a PR. If this is your first patch, start with the new contributor guide and then claim a scoped starter issue. The highest-value contributions are usually:
The shortest public on-ramp is the
starter issues queue. Current starter issues are
scoped around small runnable examples with explicit acceptance commands, so a
first contribution can improve the public WorkPaper evaluation path without
understanding the whole engine.
If this is your first contribution to bilig, start with the
first-timers-only
filter.
Please keep changes small, tested, and tied to the package that owns the behavior.
Forgejo Actions is the primary CI surface for this repo via
.forgejo/workflows/forgejo-ci.yml. GitHub Actions mirrors the verification
contract in .github/workflows/ci.yml.
The strict gate includes frozen lockfile install, full pnpm run ci, artifact
budget checks, browser smoke, and tracked-file cleanliness checks.
MIT.
Выполни в терминале:
claude mcp add bilig-workpaper -- npx PRs, issues, code search, CI status
автор: GitHubDatabase, auth and storage
автор: SupabaseSecure file operations with configurable access controls.
Reference / test server with prompts, resources, and tools.
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development