bilig
proompteng/bilig/docs/llms-full.txt
Full host context for Bilig, a WorkPaper formula readback runtime for Node services, MCP clients, and tool integrations. Repository: https://github.com/proompteng/bilig Site: https://proompteng.github.io/bilig/ npm: https://www.npmjs.com/package/@bilig/workpaper npm workbook: https://www.npmjs.com/package/@bilig/workbook Agent start: https://proompteng.github.io/bilig/agent-start.txt Agent instructions: https://proompteng.github.io/bilig/AGENTS.md Agent install context: https://proompteng.github.io/bilig/llms-install.html Skill manifest: https://bilig.proompteng.ai/.well-known/agent-skills/bilig-workpaper/SKILL.txt Compact index: https://proompteng.github.io/bilig/llms.txt Do not use npm create @bilig/workpaper@latest while @bilig/create-workpaper@latest resolves to 0.164.11. Its generated smoke reports formulasPersisted: false because that release checks escaped JSON text instead of restored formula state. The source fix must ship in a newer…
- Reads credentials
- Installs packages
# Bilig llms-full
> Full host context for Bilig, a WorkPaper formula readback runtime for Node services, MCP clients, and tool integrations.
Repository: https://github.com/proompteng/bilig
Site: https://proompteng.github.io/bilig/
npm: https://www.npmjs.com/package/@bilig/workpaper
npm workbook: https://www.npmjs.com/package/@bilig/workbook
Agent start: https://proompteng.github.io/bilig/agent-start.txt
Agent instructions: https://proompteng.github.io/bilig/AGENTS.md
Agent install context: https://proompteng.github.io/bilig/llms-install.html
Skill manifest: https://bilig.proompteng.ai/.well-known/agent-skills/bilig-workpaper/SKILL.txt
Compact index: https://proompteng.github.io/bilig/llms.txt
## Published Starter Status
Do not use `npm create @bilig/workpaper@latest` while `@bilig/create-workpaper@latest` resolves to `0.164.11`. Its generated smoke reports `formulasPersisted: false` because that release checks escaped JSON text instead of restored formula state. The source fix must ship in a newer release and pass a fresh consumer smoke before this path is restored.
## Generated Agent Instructions
# Bilig Agent Instructions
Use this file when an AI coding agent, MCP client, or tool host needs workbook formulas without opening Excel, LibreOffice, Google Sheets, or a screenshot grid.
## Discovery Order
1. Read `https://proompteng.github.io/bilig/agent-start.txt` for the one-command WorkPaper readback contract.
2. Read `https://proompteng.github.io/bilig/llms.txt` for the compact map.
3. Read `https://proompteng.github.io/bilig/llms-full.txt` when you need enough context to implement a workflow without searching the whole site.
4. Use `https://proompteng.github.io/bilig/agent-adoption-kit.html` when you need the shortest no-key install, MCP proof, and workbook task.
5. If your host can install skills from a well-known endpoint, run
`npx --yes skills@latest add https://bilig.proompteng.ai --list`.
6. Read `https://bilig.proompteng.ai/.well-known/agent-skills/bilig-workpaper/SKILL.txt` when your agent supports skill manifests.
7. If your host can install skills from GitHub, run
`npx --yes skills@latest add proompteng/bilig --skill bilig-workpaper --list`.
8. If you are using GitHub Copilot or VS Code agent mode in a cloned checkout,
use `.github/copilot-instructions.md`,
`.github/instructions/bilig-workpaper.instructions.md`,
`.github/prompts/bilig-workpaper-proof.prompt.md`, and
`.vscode/mcp.json`.
9. If you are inside a cloned Bilig repository with Claude Code, read
`CLAUDE.md` first, then use the project skill at
`.claude/skills/bilig-workpaper/SKILL.md` or invoke the explicit
formula-proof command from
`.claude/commands/bilig-workpaper-proof.md`.
10. If you are using OpenCode, use `opencode.jsonc` and
`.opencode/agents/bilig-workpaper.md`.
11. If you are using Aider, load `CONVENTIONS.md` through
`.aider.conf.yml`.
12. If you are using OpenHands, read `AGENTS.md`, then the project skill at
`.agents/skills/bilig-workpaper/SKILL.md`; add the local MCP server with
`https://proompteng.github.io/bilig/openhands-workpaper-mcp.html`.
13. If you are using Cursor, Kiro, Trae, Qodo IDE, Zed, Windsurf/Cascade, Cline, or Continue, use
`.cursor/rules/bilig-workpaper.mdc` or
`.kiro/steering/bilig-workpaper.md`,
`.trae/rules/bilig-workpaper.md` with `.trae/mcp.json`,
`https://proompteng.github.io/bilig/qodo-workpaper-mcp.html` for Qodo Agentic Tools MCP JSON,
`.zed/settings.json`,
`.devin/rules/bilig-workpaper.md`,
`.windsurf/rules/bilig-workpaper.md`,
`.clinerules/bilig-workpaper.md`, or
`.continue/rules/bilig-workpaper.md`. Continue Agent mode can also use
`.continue/mcpServers/bilig-workpaper.yaml` for the project-local
file-backed WorkPaper MCP server.
14. Start the MCP server or import `@bilig/workpaper` directly.
Remote MCP clients that support Streamable HTTP can smoke-test the stateless
demo endpoint at `https://bilig.proompteng.ai/mcp`. Directory scanners that inspect the
hosted endpoint origin can read the same-origin server card at
`https://bilig.proompteng.ai/.well-known/mcp/server-card.json`. Local agents that need writable project files should
still prefer the file-backed stdio command below.
## Default Decision
Use Bilig when the task is workbook-shaped business logic: pricing, budget checks, payout models, import validation, forecasts, tool integrations, or route handlers that need formula readback.
Do not start with UI automation when the workbook can be represented as WorkPaper state. Screenshots are useful for human review; they are not formula truth.
## Required Proof
Before reporting success, return a compact proof object with:
- `editedCell`
- `before`
- `after`
- `afterRestore`
- `persistedDocumentBytes`
- `verified`
- `limitations`
Do not claim success from a write call alone. The proof is computed readback plus persisted state.
## Fast Commands
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-agent-start --json
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario provider-backed --json
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- bilig-evaluate --door workbook-compatibility --json
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- bilig-evaluate --door xlsx-cache --json
npm exec --package @bilig/workpaper@latest -- bilig-agent-challenge --json
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --from-xlsx ./pricing.xlsx
pnpm --dir examples/headless-workpaper run agent:mcp-xlsx-risk-preflight
npm exec --package @bilig/workpaper@latest -- bilig-formula-clinic ./reduced.xlsx --cells "Summary!B7,Inputs!B2"
```
Claude Desktop users can install the released MCPB bundle from:
- https://github.com/proompteng/bilig/releases/latest/download/bilig-workpaper.mcpb
- https://github.com/proompteng/bilig/releases/latest/download/bilig-workpaper.mcpb.sha256
## Direct TypeScript
Use `buildA1WorkPaper()` for hand-authored models. Prefer
`book.set("Inputs!B2", value)`, `book.display("Summary!B2")`, and
`book.editAndReadback("Inputs!B2", value, { readbackRange: "Summary!B2" })`
before reaching for lower-level sheet ids or zero-based `{ row, col }`
addresses.
## Boundaries
Keep Excel, LibreOffice, Microsoft Graph, or an oracle harness in the loop when the workbook depends on macros, pivots, charts, external links, unsupported functions, locale-specific Excel behavior, or exact desktop UI behavior.
## Generated Skill Manifest
---
name: bilig-workpaper
version: 0.1.0
description: Use @bilig/workpaper WorkPaper state for workbook formulas, MCP editing, and tool integrations without driving spreadsheet UI.
tags:
- agents
- workbook-runtime
- formulas
- workpaper
- mcp
- typescript
---
# Bilig WorkPaper Agent Skill
Use this skill when an agent needs spreadsheet-style formulas but the work should run through files, terminal commands, TypeScript, HTTP routes, or MCP tools instead of Excel UI automation.
## When To Trigger
Trigger this skill for tasks involving:
- workbook-shaped business logic in Node.js services;
- formula readback after writing cells;
- quote, budget, payout, pricing, import-validation, or forecast models;
- agent spreadsheet tools that need deterministic cell addresses;
- MCP clients that can run a stdio server or call a Streamable HTTP endpoint;
- reduced formula/import bugs that need a local report.
Do not trigger it for manual spreadsheet editing, Office macros, VBA, pivots, charts, COM automation, or exact Excel desktop behavior unless the user explicitly asks to compare Bilig against an Excel oracle.
## Command Safety
Do not build shell commands by concatenating user text. Treat the commands below as literal templates, validate workbook paths before use, and reject values containing newlines, backticks, `$(`, `;`, `&`, `|`, `<`, or `>`. Prefer MCP client `command` plus `args` arrays or direct TypeScript calls when inserting user-provided paths or cell references.
## First Check: Agent Triage
Before wiring a client or opening a spreadsheet UI, print the compact decision
card:
```json
{
"command": "npm",
"args": ["exec", "--yes", "--package", "@bilig/workpaper@latest", "--", "bilig-agent-start", "--json"]
}
```
## First Check: Agent Evaluator
Before wiring a client, prove the published agent door with the package-owned evaluator.
It exercises MCP discovery, cell mutation, formula readback, JSON export, restart restore, and returns `verified: true`:
```json
{
"command": "npm",
"args": ["exec", "--yes", "--package", "@bilig/workpaper@latest", "--", "bilig-evaluate", "--door", "agent-mcp", "--json"]
}
```
For service-owned WorkPaper logic without MCP, run `bilig-evaluate --door workpaper-service --json`.
Use the lower-level challenge commands only when debugging the direct API loop or file-backed MCP JSON-RPC transcript:
```json
[
{ "command": "npm", "args": ["exec", "--package", "@bilig/workpaper@latest", "--", "bilig-agent-challenge", "--json"] },
{ "command": "npm", "args": ["exec", "--package", "@bilig/workpaper@latest", "--", "bilig-mcp-challenge", "--json"] }
]
```
## First Choice: MCP
Use MCP when the host can run a stdio server or call a Streamable HTTP server.
Configure stdio as an argument array, not a shell-concatenated string:
If the host supports installable skills, first check that the public skill
package is discoverable:
```sh
npx --yes skills@latest add https://bilig.proompteng.ai --list
npx --yes skills@latest add proompteng/bilig --skill bilig-workpaper --list
```
```json
{
"command": "npm",
"args": [
"exec",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"./pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
]
}
```
Run `bilig-evaluate --door agent-mcp --json` first. If the workbook contains
provider-backed formulas such as `IMPORTRANGE`, run
`bilig-evaluate --door agent-mcp --scenario provider-backed --json` to confirm
the adapter boundary. If the evaluator fails, run `bilig-mcp-challenge` and
treat its returned `tools` array as the source of truth for the currently published package. The core file-backed tools are:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
When the server is started through `@bilig/workpaper@latest` with
`--from-xlsx ./pricing.xlsx`, `tools/list` also includes
`analyze_workbook_risk`. That tool is fixed to the source XLSX passed at
startup and reports workbook risk indicators before a workflow trusts the imported
WorkPaper. Without `--workpaper --writable`, edits stay in memory; add a
WorkPaper JSON path only when the task needs persisted file state. It does not
certify Excel compatibility.
For a maintained XLSX preflight transcript, run
`pnpm --dir examples/headless-workpaper run agent:mcp-xlsx-risk-preflight`.
It requires `analyze_workbook_risk`, `set_cell_contents_and_readback`,
`export_workpaper_document`, `Inputs!B3`, `Summary!B3`, `60000 -> 96000`,
and `verified: true`.
After a write, always read the dependent output cell and export the WorkPaper
document. If the listed tool set includes `set_cell_contents_and_readback`,
prefer it for stateless clients because the edit and dependent readback happen
in one tool call. If it is absent, call `set_cell_contents`, then `read_cell`
or `read_range`, then `export_workpaper_document`.
For remote MCP clients, use the stateless demo endpoint when the client supports
Streamable HTTP:
```text
https://bilig.proompteng.ai/mcp
https://bilig.proompteng.ai/mcp/workpaper
```
The remote endpoint is request-local and does not write user files. Use it for
connector smoke tests, tool discovery, and agent onboarding; use the file-backed
stdio command when the workflow must persist a project WorkPaper JSON file.
## Second Choice: Direct TypeScript
Use `@bilig/workpaper` directly when workbook logic belongs in a service, queue worker, test, or route:
```ts
import { buildA1WorkPaper } from '@bilig/workpaper'
const book = buildA1WorkPaper({
Inputs: [
['Metric', 'Value'],
['Customers', 20],
['Average revenue', 1200],
],
Summary: [
['Metric', 'Value'],
['Revenue', '=Inputs!B2*Inputs!B3'],
],
})
const proof = book.editAndReadback('Inputs!B2', 32, {
readbackRange: 'Summary!B2',
})
console.log({
editedCell: proof.editedCell,
after: proof.afterReadback.displayValues,
afterRestore: proof.restoredReadback.displayValues,
persistedDocumentBytes: proof.persistedDocumentBytes,
verified: proof.verified,
})
book.dispose()
```
## Formula Clinic
When the user has a reduced workbook formula/import bug, generate a local report through an argument array:
```json
{
"command": "npm",
"args": [
"exec",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-formula-clinic",
"./reduced.xlsx",
"--cells",
"Summary!B7,Inputs!B2"
]
}
```
The report is local. It does not upload workbook contents. Ask for a reduced public fixture rather than private customer spreadsheets.
## Required Verification
Return readback, not a write-only claim. A successful agent response should include:
- the exact edited sheet and A1 cell;
- before values for relevant inputs and dependent outputs;
- after values read from the recalculated workbook;
- persistence evidence from serialized or exported WorkPaper state;
- restore or reimport checks when file boundaries matter;
- limitations for unsupported formulas or Excel-only features.
If any readback step fails, report the blocker instead of claiming the workbook was updated.
## Reference URLs
- Compact docs map: https://proompteng.github.io/bilig/llms.txt
- Full host context: https://proompteng.github.io/bilig/llms-full.txt
- Host handbook: https://proompteng.github.io/bilig/headless-workpaper-agent-handbook.html
- Agent workbook challenge: https://proompteng.github.io/bilig/agent-workbook-challenge.html
- MCP server guide: https://proompteng.github.io/bilig/mcp-workpaper-tool-server.html
- OpenHands MCP setup: https://proompteng.github.io/bilig/openhands-workpaper-mcp.html
- OpenCode MCP setup: https://proompteng.github.io/bilig/opencode-workpaper-mcp.html
- Open WebUI tool setup: https://proompteng.github.io/bilig/open-webui-workpaper-mcp.html
- LobeHub MCP setup: https://proompteng.github.io/bilig/lobehub-workpaper-mcp.html
- AnythingLLM MCP setup: https://proompteng.github.io/bilig/anythingllm-workpaper-mcp.html
- Sim MCP setup: https://proompteng.github.io/bilig/sim-workpaper-mcp.html
- Formula clinic: https://proompteng.github.io/bilig/formula-bug-clinic.html
- Compatibility limits: https://proompteng.github.io/bilig/where-bilig-is-not-excel-compatible-yet.html
- Repository: https://github.com/proompteng/bilig
---
## Repository README
Source: https://github.com/proompteng/bilig/blob/main/README.md
# Bilig
[](https://github.com/proompteng/bilig/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@bilig/workpaper)
[](packages/workpaper/package.json)
[](https://scorecard.dev/viewer/?uri=github.com/proompteng/bilig)
[](LICENSE)
**Keep the workbook model. Run the rule in Node.**
Bilig is a TypeScript-native, headless WorkPaper runtime for Node.js services,
tests, and AI agents. Set inputs, recalculate formulas, read computed outputs,
persist WorkPaper JSON, restore it, and verify the result—without driving Excel
or a browser grid.
[Docs](https://proompteng.github.io/bilig/) ·
[Quick start](#quick-start) ·
[TypeScript API](#use-it-from-typescript) ·
[MCP](#agents-and-mcp) ·
[Examples](#examples-and-deeper-guides) ·
[Discussions](https://github.com/proompteng/bilig/discussions)
<p align="center">
<img src="docs/assets/bilig-hero-workbook-api.png" alt="A WorkPaper input edit recalculating a formula, then surviving JSON restore" />
</p>
> [!NOTE]
> Bilig is a headless workbook runtime, not a visual spreadsheet app or a claim
> of full Excel compatibility. If an `.xlsx` file is your contract, start with
> the [compatibility report](docs/workbook-compatibility-report.md).
## Quick Start
Prove the published package before installing it:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json
```
The evaluator edits `Inputs!B2`, recalculates `Summary!B2`, saves the WorkPaper,
restores it, and compares the restored value:
```json
{
"schemaVersion": "bilig-evaluator.v1",
"door": "workpaper-service",
"evidence": {
"editedCell": "Inputs!B2",
"dependentCell": "Summary!B2",
"before": 24000,
"after": 38400,
"afterRestore": 38400
},
"verified": true
}
```
`verified: true` means the write, formula readback, JSON export, and restored
readback all passed. It is stronger evidence than a successful write call.
## Use It From TypeScript
```sh
npm install @bilig/workpaper
```
```ts
import { buildA1WorkPaper } from "@bilig/workpaper";
const pricing = buildA1WorkPaper({
Inputs: [
["Metric", "Value"],
["Units", 20],
["Price", 1200],
],
Summary: [
["Metric", "Value"],
["Revenue", "=Inputs!B2*Inputs!B3"],
],
});
const proof = pricing.editAndReadback("Inputs!B2", 32, {
readbackRange: "Summary!B2",
});
console.log(proof.afterReadback.displayValues[0]?.[0]); // 38400
console.log(proof.verified); // true
pricing.dispose();
```
For ordinary operations, use `set()`, `setMany()`, `readMany()`, `display()`,
and `saveJson()`. Use `editManyAndReadback()` when multiple inputs must be
committed and verified as one edit. The complete public API is documented in
[`packages/workpaper/README.md`](packages/workpaper/README.md).
The lifecycle is deliberately small:
`inputs → formula recalculation → typed readback → JSON persistence → restore verification`
## Why Bilig
| Capability | What it gives you |
| --- | --- |
| Workbook-shaped models | Sheets, A1 addresses, formulas, ranges, and named expressions without a spreadsheet UI. |
| Verified mutations | Before/after computed values plus persistence and restore checks. |
| Service-owned state | Portable WorkPaper JSON for routes, queues, tests, tools, and audit trails. |
| Agent-safe tools | Narrow read/write tools with exact cells, computed readback, and writable-sheet boundaries. |
| Explicit file boundaries | Separate XLSX import, export, risk inspection, and Excel-oracle workflows. |
Use Bilig for pricing, quote approval, payouts, forecasts, validation rules,
formula-backed workflows, and tests where a service or tool should own the
model. Choose a spreadsheet application or hosted spreadsheet API when you
need visual editing, collaboration, macros, interactive pivots or charts, or
desktop fidelity.
## Agents And MCP
Agents should first ask which system owns state, then run the smallest matching
proof. For a tool host or MCP client:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-agent-start --json
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
The MCP evaluator proves tool discovery, mutation, recalculated readback, JSON
export, disk persistence, process restart, and restored readback. For a local,
writable WorkPaper:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
```
Use that local stdio path for private or persistent project state. The hosted
`https://bilig.proompteng.ai/mcp` endpoint is request-local and only intended
for stateless connector discovery and smoke tests; do not send private workbook
data to it.
The server exposes `list_sheets`, `read_range`, `read_cell`,
`set_cell_contents`, `set_cell_contents_and_readback`,
`get_cell_display_value`, `export_workpaper_document`, and `validate_formula`.
It also publishes MCP resources and prompts so capable hosts can discover the
workflow before editing cells.
Machine-readable entry points:
| Need | Entry point |
| --- | --- |
| A compact routing card | [`docs/agent-start.txt`](docs/agent-start.txt) |
| A concise model index | [`docs/llms.txt`](docs/llms.txt) |
| Full agent documentation | [`docs/llms-full.txt`](docs/llms-full.txt) |
| Installation context | [`docs/llms-install.md`](docs/llms-install.md) |
| Structured capabilities | [`docs/agent.json`](docs/agent.json) |
| Reusable skill | [`skills/bilig-workpaper/SKILL.md`](skills/bilig-workpaper/SKILL.md) |
| Proof and host matrix | [`docs/agent-adoption-kit.md`](docs/agent-adoption-kit.md) |
The published package also carries `AGENTS.md` and `SKILL.md`, so an agent can
discover the same proof contract from `node_modules`. Install or inspect the
public skill with either source:
```sh
npx --yes skills@latest add https://bilig.proompteng.ai --list
npx --yes skills@latest add proompteng/bilig --skill bilig-workpaper --list
```
<details>
<summary>Host-specific project files</summary>
Use the [agent rule chooser](docs/agent-rule-chooser.md) or the
[host handoff prompt](docs/headless-workpaper-agent-handbook.md#copy-paste-prompt-for-another-agent).
The repository includes `CLAUDE.md`,
`.claude/skills/bilig-workpaper/SKILL.md`,
`.claude/commands/bilig-workpaper-proof.md`,
`.cursor/rules/bilig-workpaper.mdc`, `.devin/rules/bilig-workpaper.md`,
`.windsurf/rules/bilig-workpaper.md`, `.clinerules/bilig-workpaper.md`,
`.continue/rules/bilig-workpaper.md`, `.zed/settings.json`, `opencode.jsonc`,
and `.opencode/agents/bilig-workpaper.md`.
</details>
## Integration Recipes After The Proof
Run an evaluator first, then use the recipe owned by your host:
- [OpenAI Agents SDK](https://proompteng.github.io/bilig/openai-agents-sdk-workpaper-tool.html): direct tools, `MCPServerStdio`, and `MCPServerStreamableHttp`.
- [OpenAI Responses API](https://proompteng.github.io/bilig/openai-responses-workpaper-tool-call.html): function-call readback with explicit before/after evidence.
- [Vercel AI SDK](https://proompteng.github.io/bilig/vercel-ai-sdk-langchain-spreadsheet-tool.html): `generateText()` and `streamText()` tool loops.
- [Open WebUI](https://proompteng.github.io/bilig/open-webui-workpaper-mcp.html): local or hosted MCP discovery.
- [n8n](https://proompteng.github.io/bilig/n8n-workpaper-formula-readback.html): the `@bilig/n8n-nodes-workpaper` community node.
## Choose An Evaluation Path
| Your state owner | Start here | Evidence to require |
| --- | --- | --- |
| TypeScript application | `npm install @bilig/workpaper` | direct A1 API and focused application tests |
| Node service, route, queue, or test | `bilig-evaluate --door workpaper-service --json` | edit, recalculation, JSON export, restore, `verified: true` |
| MCP client or tool host | `bilig-evaluate --door agent-mcp --json` | discovery, readback, disk persistence, restart |
| Imported `.xlsx` is the contract | `workbook-compatibility-report workbook.xlsx --json` | unsupported formulas and workbook risk reasons for that file |
| Cached `.xlsx` values look stale | `xlsx-cache-doctor workbook.xlsx --json` | stale-cache diagnosis, recalculation, and readback for that file |
The `workbook-compatibility` and `xlsx-cache` evaluator doors use bundled demo
workbooks to smoke-test the published package; they do not inspect your file.
Do not treat any evaluator as proof of desktop Excel parity.
## Examples And Deeper Guides
Start with one maintained example, not the whole monorepo:
- [`examples/headless-workpaper`](examples/headless-workpaper): pricing,
invoice, budget, fulfillment, subscription, persistence, and agent examples.
- [`examples/serverless-workpaper-api`](examples/serverless-workpaper-api):
quote approval through Hono, Next.js, and persistence adapters.
- [`examples/xlsx-recalculation-node`](examples/xlsx-recalculation-node): import,
recalculate, export, reimport, and verify an XLSX workbook.
- [`examples/recalc-bridge-workflows`](examples/recalc-bridge-workflows): focused
bridges for existing SheetJS, xlsx-populate, and ExcelJS workflows.
Useful decision guides:
- [Formula workbooks proof page](docs/formula-workbooks-node-services-agent-tools.md)
- [Agent evaluator matrix](docs/agent-proof-matrix.md)
- [MCP spreadsheet server for coding agents](docs/mcp-spreadsheet-formula-server-for-coding-agents.md)
- [Vercel AI SDK formula readback](docs/vercel-ai-sdk-spreadsheet-tool-formula-readback.md)
- [OpenAI Responses tool calls](docs/openai-responses-workpaper-tool-call.md)
- [ExcelJS formula result boundary](docs/exceljs-formula-result-not-updating-after-node-edits.md)
- [Google Sheets `QUERY` and `SORTN`](docs/google-sheets-query-sortn-node-workpaper.md)
- [Microsoft Graph Excel boundary](docs/microsoft-graph-excel-recalculation-node.md)
- [XLSX formula support answers](docs/xlsx-formula-support-answers.md)
- [Production adoption checklist](docs/production-adoption-checklist-headless-workpaper.md)
<details>
<summary>Runnable integration and diagnostic commands</summary>
```sh
pnpm --dir examples/headless-workpaper run agent:ai-sdk-generate-text
pnpm --dir examples/headless-workpaper run agent:ai-sdk-stream-text
pnpm --dir examples/headless-workpaper run agent:openai-responses
pnpm --dir examples/headless-workpaper run agent:mcp-xlsx-risk-preflight
pnpm --dir examples/serverless-workpaper-api run hono-route
pnpm --dir examples/serverless-workpaper-api run next-server-action
pnpm --dir examples/serverless-workpaper-api run next-server-action-formdata
```
The AI SDK `generateText()` smoke lives at
[`ai-sdk-generate-text-tool-smoke.ts`](examples/headless-workpaper/ai-sdk-generate-text-tool-smoke.ts).
The OpenAI example is documented in
[`openai-responses-workpaper-tool-call`](docs/openai-responses-workpaper-tool-call.md).
For a reduced formula or import bug:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-formula-clinic ./reduced.xlsx --cells "Summary!B7,Inputs!B2"
```
</details>
## XLSX And Excel Compatibility
Bilig can import and export workbook files, but cached formula values inside an
`.xlsx` are diagnostics—not an accuracy oracle. Inspect the file before trusting
it:
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- bilig-evaluate --door workbook-compatibility --json
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- workbook-compatibility-report workbook.xlsx --json
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- xlsx-cache-doctor workbook.xlsx --json
```
The first command is a package smoke test over a bundled demo. The next two
inspect the named file. The compatibility report identifies unsupported
functions, external links, macros, pivots, volatile formulas, and other risks;
it does not certify Excel compatibility. When correctness matters, compare
against a workbook freshly recalculated by Excel. See the
[compatibility limits](docs/where-bilig-is-not-excel-compatible-yet.md) and
[Excel oracle walkthrough](docs/xlsx-corpus-verifier-walkthrough.md).
## Packages And Repository Map
| Path | Role |
| --- | --- |
| [`packages/workpaper`](packages/workpaper) | Recommended `@bilig/workpaper` API, evaluators, AI SDK adapter, MCP server, and XLSX boundary. |
| [`packages/headless`](packages/headless) | Lower-level WorkPaper runtime and integration primitives. |
| [`packages/xlsx-formula-recalc`](packages/xlsx-formula-recalc) | Real-file compatibility and stale-cache diagnostics. |
| [`packages/formula`](packages/formula) | Formula parser, binder, compiler, and evaluator. |
| [`packages/core`](packages/core) | Workbook state, mutations, snapshots, and scheduling. |
| [`apps/web`](apps/web) | Browser spreadsheet shell. |
| [`apps/bilig`](apps/bilig) | Full-stack runtime, APIs, and static site host. |
The public package requires Node.js `>=22`. Local monorepo development uses
Node.js 24+, Bun, and `pnpm@10.32.1`.
Published releases include npm registry signatures and provenance attestations:
```sh
npm view @bilig/workpaper version dist.attestations dist.signatures --json
npm audit signatures
```
## Development
Choose one long-running development server:
```sh
pnpm dev:web
pnpm dev:web-local
```
Install and validate the repository with:
```sh
pnpm install
pnpm build
pnpm lint
pnpm typecheck
pnpm test
pnpm run ci
```
Architecture lives in [`docs/architecture.md`](docs/architecture.md). Read
[`CONTRIBUTING.md`](CONTRIBUTING.md) before opening a pull request; first-time
contributors can start with the [new contributor guide](docs/new-contributor-guide.md)
and [starter issues](docs/starter-issues.md). All participation follows the
[`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).
## Support And Security
- Ask adoption and design questions in
[Discussions](https://github.com/proompteng/bilig/discussions).
- Follow versioned changes through
[GitHub Releases](https://github.com/proompteng/bilig/releases/latest).
- Report reproducible bugs through
[Issues](https://github.com/proompteng/bilig/issues); reduced workbooks can use
the [formula bug clinic](docs/formula-bug-clinic.md) and
[fixture form](docs/submit-workbook-fixture.md).
- Read [`SUPPORT.md`](SUPPORT.md) for the evidence that makes a report actionable.
- Follow [`SECURITY.md`](SECURITY.md) for private vulnerability reporting. Never
attach private workbook data, credentials, or tokens to a public issue.
If Bilig fits one of your services or agent workflows,
[star the repository](https://github.com/proompteng/bilig) to follow releases
and help other Node developers find it. Tell us what proof or formula is still
missing.
## License
[MIT](LICENSE)
---
## Agent Install Context
Source: https://github.com/proompteng/bilig/blob/main/llms-install.md
# Bilig WorkPaper install context
Bilig gives tool hosts a workbook API instead of a spreadsheet screen.
Use it when a task needs to edit workbook inputs, recalculate formulas, read the
computed value back, and persist the workbook state. Do not use Excel,
LibreOffice, Google Sheets, browser grids, or screenshots as the first tool when
the workbook can run as WorkPaper JSON.
## Fast check
Run this before wiring any host integration:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
A useful result has `schemaVersion: "bilig-evaluator.v1"`, `door:
"agent-mcp"`, `verified: true`, a changed input cell, a changed dependent
formula value, exported WorkPaper JSON, and restart readback that matches the
post-edit value.
Use the service path when the integration will call TypeScript directly instead of
MCP:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json
```
## Add host files to an existing repo
Do not use `npm create @bilig/workpaper@latest` while
`@bilig/create-workpaper@latest` resolves to `0.164.11`. Its generated smoke
reports `formulasPersisted: false` because that release checks escaped JSON
text instead of restored formula state. The source fix must ship in a newer
release and pass a fresh consumer smoke before this path is restored.
Until then, use the evaluator commands above, install the reusable skill below,
or copy the matching host files from a cloned checkout. The repository carries
`AGENTS.md`, `CONVENTIONS.md`, `.aider.conf.yml`, `CLAUDE.md`, `GEMINI.md`,
Copilot/VS Code instructions, Cursor, Kiro, Roo Code, Trae, Qodo, Zed, Junie,
OpenCode, Aider, Cline, Continue, Windsurf rules, and MCP client configs.
## Install the reusable skill
```sh
npx --yes skills@latest add https://bilig.proompteng.ai --list
npx --yes skills@latest add proompteng/bilig --skill bilig-workpaper --list
```
Use the hosted skill URL first. Keep the GitHub skill command for hosts that
only support repository-backed skills.
## MCP server config
Use file-backed stdio for private workbook state:
```json
{
"mcpServers": {
"bilig-workpaper": {
"command": "npm",
"args": [
"exec",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"./.bilig/pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
]
}
}
}
```
If the project already has an `.xlsx` file and the agent needs triage before
trusting the import, start with the direct XLSX mode:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --from-xlsx ./pricing.xlsx
```
That imports the workbook into an in-memory WorkPaper server. In this mode,
`tools/list` also includes `analyze_workbook_risk`, a fixed-source diagnostic
for unsupported functions, external links, macro payloads, pivots, volatile
formulas, stored formula results, and concrete risk reasons. It does not certify
Excel compatibility.
Persist the imported WorkPaper only when the workflow needs a durable sidecar:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --from-xlsx ./pricing.xlsx --workpaper ./.bilig/pricing.workpaper.json --writable
```
Expected tools:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
Use the hosted endpoint only for stateless smoke tests and tool discovery:
```text
https://bilig.proompteng.ai/mcp
```
Do not put private workbook data in the hosted demo endpoint.
## IDE and agent rule files
Use the repo-local files when the agent is already inside a checkout:
- Claude Code: `.claude/skills/bilig-workpaper/SKILL.md`
- Claude Code command: `.claude/commands/bilig-workpaper-proof.md`
- GitHub Copilot: `.github/copilot-instructions.md`
- GitHub Copilot custom instructions: `.github/instructions/bilig-workpaper.instructions.md`
- GitHub Copilot prompt: `.github/prompts/bilig-workpaper-proof.prompt.md`
- VS Code MCP: `.vscode/mcp.json`
- Cursor: `.cursor/rules/bilig-workpaper.mdc`
- Kiro: `.kiro/steering/bilig-workpaper.md` and `.kiro/settings/mcp.json`
- Roo Code: `.roo/rules/bilig-workpaper.md` and `.roo/mcp.json`
- Trae: `.trae/rules/bilig-workpaper.md` and `.trae/mcp.json`
- Qodo IDE: use [Qodo WorkPaper MCP setup](https://proompteng.github.io/bilig/qodo-workpaper-mcp.html) and paste
the `bilig-workpaper` server JSON into Qodo Agentic Tools MCP settings
- Zed: `.zed/settings.json`
- Junie: `.junie/mcp/mcp.json`
- OpenCode: `opencode.jsonc` and `.opencode/agents/bilig-workpaper.md`
- Aider: `CONVENTIONS.md` loaded by `.aider.conf.yml`
- Cascade/Devin: `.devin/rules/bilig-workpaper.md`
- Cline: `.clinerules/bilig-workpaper.md`
- Continue: `.continue/rules/bilig-workpaper.md`
- Windsurf/Cascade: `.windsurf/rules/bilig-workpaper.md`
Rule chooser: <https://proompteng.github.io/bilig/agent-rule-chooser.html>
## Direct TypeScript path
Use direct imports for services, queues, route handlers, and tests:
```sh
npm install @bilig/workpaper
```
```ts
import {
WorkPaper,
exportWorkPaperDocument,
serializeWorkPaperDocument,
} from "@bilig/workpaper";
const workbook = WorkPaper.buildFromSheets({
Inputs: [
["Metric", "Value"],
["Customers", 20],
["Average revenue", 1200],
],
Summary: [
["Metric", "Value"],
["Revenue", "=Inputs!B2*Inputs!B3"],
],
});
const inputs = workbook.getSheetId("Inputs");
const summary = workbook.getSheetId("Summary");
if (inputs === undefined || summary === undefined) {
throw new Error("missing sheets");
}
workbook.setCellContents({ sheet: inputs, row: 2, col: 1 }, 4800);
const after = workbook.getCellDisplayValue({ sheet: summary, row: 1, col: 1 });
const saved = serializeWorkPaperDocument(
exportWorkPaperDocument(workbook, { includeConfig: true }),
);
console.log({ after, persistedDocumentBytes: saved.length });
```
## Agent task contract
Ask the agent for workbook evidence, not a write status:
```text
Use Bilig WorkPaper tools instead of spreadsheet UI automation. Read the input
and output cells first, set one input cell, read the dependent formula cell
after recalculation, export or serialize the WorkPaper document, restore it when
a file boundary matters, and return editedCell, before, after, afterRestore,
persistedDocumentBytes, verified, and limitations.
```
Reject answers that only say a cell was written.
## More context
- Compact map: <https://proompteng.github.io/bilig/llms.txt>
- Full context: <https://proompteng.github.io/bilig/llms-full.txt>
- Agent start: <https://proompteng.github.io/bilig/agent-start.txt>
- Agent manifest: <https://proompteng.github.io/bilig/.well-known/agent.json>
- Agent handoff checklist: <https://proompteng.github.io/bilig/agent-adoption-kit.html>
- MCP server guide: <https://proompteng.github.io/bilig/mcp-workpaper-tool-server.html>
- Repository: <https://github.com/proompteng/bilig>
---
## Workbook Compatibility Report
Source: https://github.com/proompteng/bilig/blob/main/docs/workbook-compatibility-report.md
# Workbook Compatibility Report
Use this before wiring a real `.xlsx` workbook into a Node service or coding
agent. The report answers one question:
> If I point an agent or Node service at this workbook, what known risks should
> I investigate before I trust the outputs?
It is an inspector, not a grader. It does not say that a workbook is Excel
compatible, and it does not print a compatibility percentage.
## One command
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- \
workbook-compatibility-report workbook.xlsx --json
```
For a no-project proof:
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- \
workbook-compatibility-report --demo --json
```
The same proof is available through the evaluator door:
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- \
bilig-evaluate --door workbook-compatibility --json
```
For an agent that will keep working through MCP after the risk report, use the
XLSX preflight transcript:
```sh
pnpm --dir examples/headless-workpaper run agent:mcp-xlsx-risk-preflight
```
That path starts `bilig-workpaper-mcp --from-xlsx`, calls
`analyze_workbook_risk`, edits one input through
`set_cell_contents_and_readback`, verifies dependent formula readback, and
exports the WorkPaper document.
## Fields to trust
The v1 report stays small on purpose:
- `verified: true`: the report completed locally.
- `workbook.formulaCellCount`: how much formula surface was inspected.
- `findings.unsupportedFunctions`: formulas that need review before readback is
trusted.
- `findings.externalLinks`: linked workbook references and unresolved count.
- `findings.macroModules`: preserved VBA payloads that Bilig does not execute.
- `findings.volatileFunctions`: functions like `NOW` and `OFFSET`.
- `findings.pivotTables`: pivots and unsupported pivot surfaces.
- `findings.staleCachedFormulas`: cached formula values that changed when
recalculated.
- `cacheInspection.uninspectedFormulaCellCount`: formulas left unchecked when a
caller deliberately sets `--inspect-limit`.
- `risk.level` and `risk.reasons`: low, medium, or high with concrete reasons.
- `excelParity: "not_proven"`: the report does not certify desktop Excel
behavior.
## Demo output
The checked-in proof artifact is
[`workbook-compatibility-report.json`](workbook-compatibility-report.json).
The important shape is:
```json
{
"schemaVersion": "bilig-workbook-compatibility-report.v1",
"verified": true,
"workbook": {
"sheetCount": 2,
"formulaCellCount": 3
},
"findings": {
"unsupportedFunctions": [{ "name": "CUBEVALUE", "count": 1 }],
"externalLinks": { "count": 0, "unresolvedCount": 0, "refreshedCount": 0 },
"macroModules": { "count": 0, "byteLength": 0 },
"volatileFunctions": [{ "name": "NOW", "count": 1 }],
"pivotTables": { "count": 0, "unsupportedCount": 0, "cacheOnlyCount": 0 },
"staleCachedFormulas": { "count": 2 },
"missingCachedFormulaValues": { "count": 1 }
},
"risk": {
"level": "high",
"reasons": ["unsupported functions: CUBEVALUE (1)"]
},
"excelParity": "not_proven"
}
```
The demo workbook is intentionally imperfect. A perfect workbook proves little;
this one proves the report can call out a known unsupported function, a volatile
function, and cache states without pretending to know full Excel parity.
## Human output
Without `--json`, the CLI prints a compact readout:
```text
Workbook analyzed. Risk level: HIGH
Findings:
- Unsupported functions: CUBEVALUE (1)
- External links: 0
- Macro modules: 0
- Pivot tables: 0
- Volatile functions: NOW (1)
- Formula cells: 3
- Stale cached formulas: 2
- Missing cached formula values: 1
This report identifies workbook features that may require investigation before using Bilig in a service or agent workflow. It is not an Excel compatibility certification.
```
## What this proves
- the workbook can be imported locally without a spreadsheet UI
- formula cells can be counted and inspected
- unsupported function, external-link, macro, volatile, pivot, and cache signals
are machine-readable
- limited inspection is visible and raises risk when formula cells remain
unchecked
- the report completed without uploading the workbook
## What this does not prove
This is not an Excel compatibility certification. It does not execute VBA,
refresh pivots, refresh external data sources, certify chart behavior, or prove
desktop Excel UI behavior. Do not add `compatibilityScore`,
`excelCompatibilityPercent`, or similar score fields around this report.
## Related
- [Agent XLSX risk preflight](agent-xlsx-risk-preflight.md)
- [Workbook Compatibility Report transcript](workbook-compatibility-report-transcript.md)
- [Where Bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
---
## Workbook Compatibility Report Transcript
Source: https://github.com/proompteng/bilig/blob/main/docs/workbook-compatibility-report-transcript.md
# Workbook Compatibility Report Transcript
This is the local terminal proof for the workbook compatibility report. It is
not a screenshot, not a hosted upload, and not spreadsheet UI automation.
## Report command
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- \
workbook-compatibility-report --demo --json
```
Observed shape from `@bilig/xlsx-formula-recalc` `0.157.0`:
```json
{
"schemaVersion": "bilig-workbook-compatibility-report.v1",
"verified": true,
"input": {
"fileName": "bilig-workbook-compatibility-demo.xlsx",
"externalWorkbookCount": 0,
"inspectLimit": "all"
},
"workbook": {
"sheetCount": 2,
"sheetNames": ["Inputs", "Summary"],
"nonEmptyCellCount": 14,
"formulaCellCount": 3,
"definedNameCount": 0,
"tableCount": 0,
"pivotTableCount": 0,
"chartCount": 0,
"macroModuleCount": 0
},
"findings": {
"unsupportedFunctions": [{ "name": "CUBEVALUE", "count": 1 }],
"externalLinks": { "count": 0, "unresolvedCount": 0, "refreshedCount": 0 },
"macroModules": { "count": 0, "byteLength": 0 },
"volatileFunctions": [{ "name": "NOW", "count": 1 }],
"pivotTables": { "count": 0, "unsupportedCount": 0, "cacheOnlyCount": 0 },
"staleCachedFormulas": { "count": 2 },
"missingCachedFormulaValues": { "count": 1 },
"unsupportedRecalculations": { "count": 0 },
"warnings": [
"Volatile formulas were preserved during XLSX import; cached formula values may depend on workbook calculation time."
]
},
"cacheInspection": {
"inspectedFormulaCellCount": 3,
"uninspectedFormulaCellCount": 0,
"inspectionLimit": "all",
"suggestedReads": ["Summary!B2", "Summary!B3", "Summary!B4"]
},
"commandSucceeded": true,
"inspectionCompleted": true,
"recalculationCompleted": true,
"excelParity": "not_proven",
"risk": {
"level": "high",
"reasons": ["unsupported functions: CUBEVALUE (1)"]
}
}
```
## Evaluator command
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- \
bilig-evaluate --door workbook-compatibility --json
```
The evaluator wraps the same report in `bilig-evaluator.v1`:
```json
{
"schemaVersion": "bilig-evaluator.v1",
"door": "workbook-compatibility",
"doorName": "Workbook compatibility risk report",
"verified": true,
"evidence": {
"riskLevel": "high",
"unsupportedFunctions": [{ "name": "CUBEVALUE", "count": 1 }],
"volatileFunctions": [{ "name": "NOW", "count": 1 }],
"formulaCellCount": 3,
"staleCachedFormulaCount": 2,
"checks": {
"commandSucceeded": true,
"inspectionCompleted": true,
"recalculationCompleted": true,
"riskReasonsExplainFindings": true,
"noCompatibilityScore": true,
"unsupportedFunctionsReported": true
}
}
}
```
The important evaluator check is `noCompatibilityScore: true`. This proof path
must remain a risk inspector. It must not grow a `compatibilityScore`,
`excelCompatibilityPercent`, or similar field that implies a defensible Excel
parity percentage.
## Related
- [Workbook Compatibility Report](workbook-compatibility-report.md)
---
## Agent XLSX Risk Preflight
Source: https://github.com/proompteng/bilig/blob/main/docs/agent-xlsx-risk-preflight.md
# Agent XLSX Risk Preflight
Use this when an agent already has a real `.xlsx` file and is about to automate
spreadsheet edits. The first useful question is not whether the write call
succeeded. It is whether the imported workbook has risk indicators that should
change the plan before any edit is trusted.
This path starts the Bilig WorkPaper MCP server from the XLSX, calls the
read-only `analyze_workbook_risk` tool, then proves a formula edit through
`set_cell_contents_and_readback` and `export_workpaper_document`.
## Run It
From a cloned checkout:
```sh
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:mcp-xlsx-risk-preflight
```
The example builds a small `pricing-risk-preflight.xlsx`, starts the published
MCP binary, imports the XLSX into a persisted WorkPaper JSON file, analyzes
workbook risk, edits `Inputs!B3`, reads back `Summary!B3`, and exports the
WorkPaper document.
The underlying server command is:
```sh
npm exec --package @bilig/workpaper@latest -- \
bilig-workpaper-mcp \
--from-xlsx pricing-risk-preflight.xlsx \
--workpaper pricing-risk-preflight.workpaper.json \
--writable
```
## Required Tool Order
An agent should use this order for a private workbook:
1. `tools/list`
2. `tools/call` `analyze_workbook_risk` with `inspectLimit: "all"`
3. Decide whether the workbook can continue through WorkPaper or needs Excel,
LibreOffice, Microsoft Graph, or a human/oracle path.
4. `tools/call` `set_cell_contents_and_readback` for one small input edit.
5. `tools/call` `export_workpaper_document` for handoff or persistence proof.
The risk step is fixed to the XLSX file passed at server startup. It is
read-only, local, and does not upload the workbook.
## Expected Proof
A passing run prints a compact JSON object:
```json
{
"schemaVersion": "bilig-agent-xlsx-risk-preflight.v1",
"transport": "stdio",
"risk": {
"schemaVersion": "bilig-workbook-compatibility-report.v1",
"verified": true,
"fileName": "pricing-risk-preflight.xlsx",
"formulaCellCount": 3,
"excelParity": "not_proven"
},
"readback": {
"editedCell": "Inputs!B3",
"beforeExpectedArr": 60000,
"afterExpectedArr": 96000,
"restoredExpectedArr": 96000,
"persisted": true,
"restoredReadbackMatchesAfter": true
},
"verified": true
}
```
Those fields matter more than the exact serialized byte count. The result is
usable only when the risk diagnostic is `verified: true`, `excelParity` remains
`"not_proven"`, the dependent formula readback changed from `60000` to `96000`,
and restored state still reads `96000`.
## What It Proves
- the agent used a real XLSX file, not a hand-waved workbook description
- the local MCP tool surface exposed `analyze_workbook_risk`
- workbook risk indicators were inspected before edits
- a dependent formula was recalculated after `Inputs!B3` changed
- the edited WorkPaper was exported or persisted for another process to check
## What It Does Not Prove
This is not an Excel compatibility certification. It does not execute VBA,
refresh pivots, refresh external data, prove chart layout, or certify desktop
Excel UI behavior. If the risk report flags unsupported functions, macros,
pivots, external links, or workbook features that Bilig does not cover, keep
Excel, LibreOffice, Microsoft Graph, or a spreadsheet-specific oracle in the
loop.
## Related
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [Workbook Compatibility Report](workbook-compatibility-report.md)
- [Agent WorkPaper evaluator matrix](agent-proof-matrix.md)
- [Stale formula readback chooser](stale-formula-readback-chooser.md)
- [Where Bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
---
## Evaluate XLSX Formula Recalculation
Source: https://github.com/proompteng/bilig/blob/main/docs/eval-xlsx-recalc.md
# Evaluate XLSX formula recalculation
Use this when you have an `.xlsx` file. The check is whether Node can edit known
input cells, recalculate formulas, write a new XLSX, and return proof without
opening Excel, LibreOffice, or a browser UI.
## One command
```sh
npm exec --package @bilig/xlsx-formula-recalc@latest -- xlsx-recalc --demo --json
```
## Expected proof
The current demo prints this shape:
```json
{
"mode": "demo",
"input": "generated demo workbook",
"output": "bilig-formula-recalc-demo.xlsx",
"edits": 2,
"externalWorkbooks": 0,
"reads": {
"Summary!B2": {
"tag": 1,
"value": 72000
}
},
"warnings": [],
"commandSucceeded": true,
"recalculationCompleted": true,
"excelParity": "not_proven",
"expectedReadback": {
"Summary!B2": 72000
},
"expectedValueMatched": true
}
```
The exact output file name can change if you pass your own `--out` path. The
important checks are `commandSucceeded: true`, `recalculationCompleted: true`,
an empty or understood `warnings` array, and the recalculated cell value under
`reads`. `expectedValueMatched: true` is only a demo-fixture check. It is not an
Excel parity claim; real workbooks still report `excelParity: "not_proven"`
unless you compare against your own Excel, LibreOffice, or Graph oracle.
The JSON contains proof fields only. Discussion, release-watch, and follow-up
links stay in prose so machine output stays usable in CI and agents.
## Inspect your workbook first
If you already have the workbook but do not know the right output cells yet,
start with inspection:
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- workbook-compatibility-report pricing.xlsx --json
```
That command does not write `pricing.recalculated.xlsx`. It inspects the
workbook for unsupported functions, external links, macros, pivots, volatile
formulas, stored formula results, and risk reasons before you pick the exact
input and output cells for the proof command. If you intentionally pass
`--inspect-limit`, require `uninspectedFormulaCellCount: 0` before treating the
report as complete coverage.
Expected shape:
```json
{
"input": "pricing.xlsx",
"output": "pricing.recalculated.xlsx",
"sets": [{ "cell": "Inputs!B2", "value": 48 }],
"reads": [{ "cell": "Summary!B7", "displayValue": "72000" }],
"commandSucceeded": true,
"recalculationCompleted": true,
"verified": true,
"excelParity": "not_proven"
}
```
## Try your workbook
```sh
npm exec --package @bilig/xlsx-formula-recalc@latest -- xlsx-recalc pricing.xlsx \
--set Inputs!B2=48 \
--set Inputs!B3=1500 \
--read Summary!B7 \
--out pricing.recalculated.xlsx \
--json
```
Use sheet-qualified A1 references. Keep your adapter strict: known input cells,
known output cells, and tests around the exported workbook.
## Put it in CI
Use the compatibility report in CI when workbook risk should block a pull
request:
```yaml
- run: npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- workbook-compatibility-report fixtures/pricing.xlsx --json
```
Keep the recalculation proof separate from the compatibility report: the report
decides whether the file is safe to trust, and `xlsx-recalc` proves the exact
input edit and output readback you intend to automate.
## What this proves
- the package can import an XLSX workbook in Node
- known input cells can be edited from a command
- dependent formulas can be recalculated and read back
- the edited workbook can be written back to XLSX bytes
- warnings are visible instead of hidden behind a "success" message
## What this does not prove
This is not a claim of complete Excel parity. It does not prove macros, pivots,
charts, unsupported formulas, locale-specific Excel behavior, external-link
freshness, or exact desktop Excel UI behavior. Keep a golden workbook fixture
and an Excel or LibreOffice oracle test for customer-critical file flows.
## After the proof
- Repository:
<https://github.com/proompteng/bilig>
- Watch releases if you want compatibility and formula updates:
<https://github.com/proompteng/bilig/subscription>
- Report the exact implementation gap:
<https://github.com/proompteng/bilig/discussions/new?category=general>
## Related
- [XLSX formula recalculation in Node.js](xlsx-formula-recalculation-node.md)
- [Curlable XLSX recalculation proof](xlsx-recalculation-proof.md)
- [External workbook recalculation proof](external-workbook-recalc-proof.md)
- [Agent XLSX recalculation without LibreOffice](agent-xlsx-formula-recalculation-without-libreoffice.md)
- [Where Bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
---
## External Workbook Recalculation Proof
Source: https://github.com/proompteng/bilig/blob/main/docs/external-workbook-recalc-proof.md
# External workbook recalculation proof in Node.js
Use this when an `.xlsx` model references another workbook and the saved
external-link cache is stale. The proof builds a model workbook with cached
external values, builds a companion rates workbook with newer values, binds the
companion to the exact Excel link target, recalculates formulas, and writes a
new XLSX without opening Excel, LibreOffice, or a browser.
## Run it in a blank folder
```sh
mkdir bilig-external-workbook-proof
cd bilig-external-workbook-proof
npm init -y >/dev/null
npm pkg set type=module
npm install @bilig/xlsx-formula-recalc tsx
curl -fsSLO https://proompteng.github.io/bilig/external-workbook-recalc-proof.ts
npx tsx external-workbook-recalc-proof.ts
```
Expected output includes:
```json
{
"proof": "Bilig refreshed an XLSX external-link cache from a companion workbook, recalculated formulas, and wrote a new XLSX without Excel.",
"verified": true,
"sum": 180,
"lookup": 60,
"externalTarget": "file:///bilig-proof/rates.xlsx",
"reads": {
"Model!C1": {
"value": 180
},
"Model!C2": {
"value": 60
}
},
"checks": {
"externalWorkbookMatched": true,
"refreshedExternalCells": true,
"recalculatedExternalSum": true,
"recalculatedExternalLookup": true,
"outputXlsxWritten": true,
"verified": true
}
}
```
The script writes inspectable files to
`bilig-external-workbook-proof-output/`:
- `model-with-stale-external-cache.xlsx`
- `rates-current.xlsx`
- `model-recalculated.xlsx`
## What this proves
- a companion XLSX can be supplied to `@bilig/xlsx-formula-recalc`;
- the companion can be matched to an exact Excel external-link target;
- stale external cache cells can be refreshed before formula recalculation;
- formulas that read the external cache can return fresh values in Node;
- the recalculated workbook can be written as a new XLSX file;
- hydration diagnostics are visible in JSON instead of hidden behind success.
## What this does not prove
This is not full Excel parity. It does not prove every external-link layout,
network path, password-protected workbook, volatile formula, data connection,
pivot cache, macro, or desktop Excel UI behavior. For customer-critical models,
keep a golden workbook fixture and an Excel, LibreOffice, or Microsoft Graph
oracle test around the exact files you accept.
## Source
- [downloadable external-workbook proof script](external-workbook-recalc-proof.ts)
- [package README](https://github.com/proompteng/bilig/tree/main/packages/bilig-xlsx-formula-recalc#readme)
- [XLSX formula recalculation in Node.js](xlsx-formula-recalculation-node.md)
## After the proof
- Repository:
<https://github.com/proompteng/bilig>
- Watch releases if you need formula and workbook compatibility updates:
<https://github.com/proompteng/bilig/subscription>
- Report the exact workbook-link blocker if it almost worked:
<https://github.com/proompteng/bilig/discussions/new?category=general>
---
## Evaluate WorkPaper In A Node Service
Source: https://github.com/proompteng/bilig/blob/main/docs/eval-workpaper-service.md
# Evaluate WorkPaper in a Node service
Use this when the calculation model belongs in code, not in a user-edited Excel
file. The evaluator starts from an empty directory, creates a small WorkPaper
service, writes one input, reads a dependent formula, serializes the WorkPaper
document, restores it, and verifies the same result.
## One command
Run the published package directly:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json
```
The generated starter is release-pending while
`@bilig/create-workpaper@latest` resolves to `0.164.11`; that release's smoke
reports `formulasPersisted: false`. Use this evaluator until a newer generator
release passes a fresh consumer smoke.
## Current evaluator transcript
This transcript was captured on June 25, 2026 against
`@bilig/workpaper@0.164.11`. It is the shortest current proof for service-owned
WorkPaper state:
```json
{
"schemaVersion": "bilig-evaluator.v1",
"door": "workpaper-service",
"doorName": "WorkPaper service proof",
"packageVersions": {
"@bilig/workpaper": "0.164.11"
},
"evidence": {
"editedCell": "Inputs!B2",
"dependentCell": "Summary!B2",
"before": 24000,
"after": 38400,
"afterRestore": 38400,
"persistedDocumentBytes": 999,
"checks": {
"formulaReadbackChanged": true,
"exportedWorkPaperDocument": true,
"restoredMatchesAfter": true
}
},
"verified": true
}
```
The full command also returns `limitations`, `next`, `sourceProof`, and
`durationMs`. Treat `durationMs` as runtime noise; the proof invariants are the
edited cell, dependent cell, changed value, restore readback, persisted document
bytes, and `verified: true`.
## Expected proof
The starter smoke prints this shape:
```json
{
"before": {
"summary": {
"decision": "review"
},
"inputCells": {
"units": "Inputs!B2",
"listPrice": "Inputs!B3",
"discount": "Inputs!B4"
}
},
"edit": {
"input": {
"units": 40,
"discount": 0.05
},
"before": {
"decision": "review"
},
"after": {
"decision": "approved"
},
"restored": {
"decision": "approved"
},
"checks": {
"decisionChanged": true,
"formulasPersisted": true,
"restoredMatchesAfter": true,
"serializedBytes": 1242
}
},
"verified": true
}
```
The byte count can change by package version. The invariants are
`decisionChanged`, `formulasPersisted`, `restoredMatchesAfter`, and
`verified: true`.
## What this proves
- a service can own workbook-shaped business logic as WorkPaper JSON
- input cells can be changed through an API instead of a UI
- dependent formulas recalculate before the service responds
- exported WorkPaper state can be restored and re-read
- the proof object is small enough for tests, logs, or agent handoff
## Recompute And Output Boundaries
WorkPaper edits are isolated at the request and state boundary, not as
independent single-cell mini-runs. A write mutates the WorkPaper model,
recalculates dependent formulas, and returns a coherent post-edit readback.
Ordinary input edits use tracked dependency paths where possible, but the public
contract is the final WorkPaper state plus proof fields such as `after`,
`afterRestore`, and `checks.restoredMatchesAfter`.
Public headless WorkPaper execution is structured and batch-oriented today. The
service evaluator, Node helpers, and MCP tools return after write, recalculation,
readback, JSON export, and restore verification. They do not expose formula
evaluation as line-by-line or cell-by-cell progressive streaming. For partial
dashboard rendering, split the dashboard into explicit update steps and read the
needed cells after each committed edit batch.
## What this does not prove
This does not prove desktop spreadsheet compatibility, database durability, or a
visual editor. Use this path when the service owns the formulas and JSON state.
Use the saved-file compatibility evaluator only when a saved workbook file is
the source of truth.
## After the proof
- Repository:
<https://github.com/proompteng/bilig>
- Watch releases for API and formula runtime updates:
<https://github.com/proompteng/bilig/subscription>
- Report the exact implementation gap:
<https://github.com/proompteng/bilig/discussions/new?category=general>
## Related
- [Try Bilig WorkPaper in Node](try-bilig-headless-in-node.md)
- [Create a Bilig WorkPaper starter](create-bilig-workpaper.md)
- [WorkPaper service recipe](node-service-workpaper-recipe.md)
- [Quote approval WorkPaper API](quote-approval-workpaper-api.md)
---
## WorkPaper Host Handoff
Source: https://github.com/proompteng/bilig/blob/main/docs/agent-adoption-kit.md
# Tool Host WorkPaper Handoff
Use this page when you are deciding whether Bilig should be the workbook tool
inside a coding agent, MCP client, or agent framework. The path is deliberately
short: install the instructions, run one no-key evaluator, paste one workbook
task, and require formula readback before calling the job done.
## Choose Bilig When Readback Matters
Choose Bilig instead of Excel or Sheets UI automation when the agent needs a
machine-checkable workbook proof, not a visual session transcript.
| Agent need | Use Bilig WorkPaper | Keep Excel, Sheets, or browser UI automation |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Cell-addressed automation | The task names exact sheets, ranges, or cells such as `Inputs!B3` and `Summary!B3`. | The task depends on a person inspecting or editing the live grid. |
| Formula proof after writes | Success requires formula readback after writes through `set_cell_contents_and_readback`, not a write-call status. | The spreadsheet product owns recalculation and the agent only needs to click through the product UI. |
| Durable agent state | The agent must export, persist, restore, or restart from persisted WorkPaper JSON and prove the same calculated value comes back. | The durable artifact is an existing Excel or Sheets file with macros, charts, pivots, add-ins, or visual layout. |
| CI and tool-call evidence | The result must fit logs, MCP transcripts, CI checks, or a compact proof object with `verified: true`. | The output is a screenshot, screen recording, manual review note, or collaboration comment in a spreadsheet app. |
Run the no-key proof before wiring the path:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
For the browser/agent boundary, pair this page with the
[Browser Use WorkPaper formula tool](browser-use-workpaper-formula-tool.md), the
[headless WorkPaper agent handbook](headless-workpaper-agent-handbook.md), and
the [agent framework map](agent-framework-workbook-tools.md).
## Avoid Stored-Result False Negatives
Agents often test spreadsheet code by writing an `.xlsx` file with ExcelJS,
SheetJS, openpyxl, pandas, or another file library, then reading the formula
cell back in the same process. That is a false-negative trap: many file
libraries can preserve or write formula records without recalculating the stored
result. The agent may blame its own code, or claim a workbook failed, when it
only read unchecked file metadata.
Use Bilig at the decision boundary when the agent, CI job, route handler, or
queue worker must trust the value before Excel or Sheets opens the file:
- If the workbook can stay headless, use `@bilig/workpaper` and require
`set_cell_contents_and_readback`, export or restore evidence, and
`verified: true`.
- If the workflow must keep an `.xlsx` file, run
`@bilig/xlsx-formula-recalc` or `@bilig/exceljs-formula-recalc` at the
recalculation boundary, then read the calculated cells.
- If the task is a visual layout, chart, pivot, macro, add-in, or human review
problem, keep Excel, Sheets, or browser UI automation in the loop and do not
overclaim Bilig compatibility.
This is the shortest rule to paste into another agent:
```text
Do not grade workbook automation from unchecked stored formula results. After a formula or
input edit, prove the dependent calculated cell changed through WorkPaper
readback or an XLSX recalculation bridge before reporting success or failure.
```
## Install The Agent Instructions
If your agent supports installable skills, start here:
```sh
npx --yes skills@latest add https://bilig.proompteng.ai --list
npx --yes skills@latest add proompteng/bilig --skill bilig-workpaper --list
```
Use the app-host discovery URL first. Keep the GitHub repo skill command as a
fallback for hosts that only support GitHub skill sources.
If the agent is already inside a cloned Bilig checkout, do not copy a host list
from this page. Use the [coding agent rule chooser](agent-rule-chooser.md); it
maps each host to the current repo-local rule and MCP config. Common anchors are
`CLAUDE.md`, `.claude/skills/bilig-workpaper/SKILL.md`,
`.claude/commands/bilig-workpaper-proof.md`,
`.github/instructions/bilig-workpaper.instructions.md`,
`.github/prompts/bilig-workpaper-proof.prompt.md`, `.vscode/mcp.json`,
`.cursor/rules/bilig-workpaper.mdc`, `.kiro/steering/bilig-workpaper.md`,
`.trae/mcp.json`, `.trae/rules/bilig-workpaper.md`, `.zed/settings.json`,
`opencode.jsonc`, and `.continue/mcpServers/bilig-workpaper.yaml`.
The published starter overlay is release-pending. Do not use
`npm create @bilig/workpaper@latest` while `@bilig/create-workpaper@latest`
resolves to `0.164.11`; that release's generated smoke reports
`formulasPersisted: false`. Until a newer release passes a fresh consumer
smoke, use the rule chooser to copy only the host files you need and run the
direct `npm exec` MCP commands above.
For web fetch, give the agent the compact map first:
```text
https://proompteng.github.io/bilig/llms.txt
```
When a reviewer wants to see a successful run before adopting the path, require
fresh evaluator JSON from `bilig-evaluate --door agent-mcp --json`.
## Agent Manifest Gate
When an agent host, directory scanner, or internal platform wants machine-readable
entrypoints, start with:
```text
https://proompteng.github.io/bilig/.well-known/agent.json
```
Accept the integration only if the manifest exposes `public_entrypoints`,
`evaluator_doors`, `proof_contract`, and the `mcp` server block. Those fields
let the host find the compact start file, choose the right evaluator door, and
verify that success means computed readback plus persisted state.
For a human-readable decision object, run:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-agent-start --json
```
Then validate the same boundary with the agent MCP evaluator:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
Do not treat a manifest link, installed rule file, or MCP server listing as
success by itself. The gate passes only when the evaluator returns the proof
fields in `proof_contract`, including `editedCell`, `before`, `after`,
`afterRestore`, `persistedDocumentBytes`, and `verified`.
## Run The No-Key Check
This checks the published package and the file-backed MCP tool path without
cloning the repo or using an API key:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
For a richer workbook check, use the revenue-plan scenario:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario revenue-plan --json
```
That scenario proves `SUM`, `SUMIF`, `XLOOKUP`, `FILTER`, a named expression,
JSON persistence, and restart readback through the same MCP door.
If the workbook includes provider-backed formulas such as `IMPORTRANGE`, run the
adapter-boundary check:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario provider-backed --json
```
That check should show `#BLOCKED!` and `provider-backed-adapter-missing` before
a local synthetic adapter is installed, then a fresh `96000` readback with
diagnostics cleared after the adapter path runs. It does not call Google Sheets.
A passing run must return `schemaVersion: "bilig-evaluator.v1"`,
`door: "agent-mcp"`, `verified: true`, and these checks:
- tools, resources, and prompts were discovered;
- one input cell changed;
- a dependent formula cell changed after recalculation;
- WorkPaper JSON was exported and persisted;
- restart readback matched the post-edit value.
Use the raw MCP challenge only when you need the lower-level JSON-RPC proof:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
```
Use the service evaluator when the agent will import `@bilig/workpaper` instead
of using MCP:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json
```
## Wire The Local MCP Server
Use file-backed stdio for private project state:
```json
{
"mcpServers": {
"bilig-workpaper": {
"command": "npm",
"args": [
"exec",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"./.bilig/pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
]
}
}
}
```
If the repository already has a workbook, create the file-backed WorkPaper from
that XLSX first:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --from-xlsx ./pricing.xlsx --workpaper ./.bilig/pricing.workpaper.json --writable
```
That command imports the XLSX once, refuses to replace an existing WorkPaper
JSON unless `--overwrite-workpaper` is present, and then exposes the same
`read_cell`, `set_cell_contents_and_readback`, `export_workpaper_document`, and
read-only `analyze_workbook_risk` tools.
Use the hosted endpoint only for smoke tests and tool discovery:
```text
https://bilig.proompteng.ai/mcp
```
The hosted endpoint is stateless. It is not where private workbook files live.
## Paste This Task Into An Agent
```text
Use Bilig WorkPaper tools instead of spreadsheet UI automation. Build or load a
small workbook with Inputs!B2 as customers, Inputs!B3 as average revenue, and
Summary!B3 as the revenue formula. First read the relevant input and summary
range. Then set Inputs!B3 to 4800, read Summary!B3 after recalculation, export
or serialize the WorkPaper document, restore it, and return editedCell, before,
after, afterRestore, persistedDocumentBytes, verified, and limitations.
Do not claim success from a write call alone. Success requires computed
readback plus persisted or restored state.
```
## Expected Result
The exact values can change, but the evaluator result should look like this:
```json
{
"schemaVersion": "bilig-evaluator.v1",
"door": "agent-mcp",
"verified": true,
"packageVersions": {
"@bilig/workpaper": "0.164.11",
"xlsx-formula-recalc": "0.164.11"
},
"evidence": {
"editedCell": "Inputs!B3",
"dependentCell": "Summary!B3",
"before": 60000,
"after": 96000,
"afterRestore": 96000,
"afterRestart": 96000,
"checks": {
"listedFileBackedTools": true,
"listedResourcesAndPrompts": true,
"formulaValidationPassed": true,
"dependentCellChanged": true,
"persistedToDisk": true,
"exportContainsWorkPaperDocument": true,
"restartReadbackMatchesAfter": true
}
}
}
```
Reject answers that only say a cell was written. The point of Bilig is that the
agent returns the calculated result and saved state from the workbook itself.
## Upstream Maintainer Notes
When you want a third-party agent host, MCP client, framework, or docs site to
add Bilig, use the same proof bar and avoid duplicate threads.
Before opening anything upstream:
- search that project for existing Bilig issues, PRs, examples, and docs links;
- run the no-key `agent-mcp` evaluator against the currently published package;
- decide whether the host needs a local file-backed MCP config, a hosted
stateless smoke endpoint, an installable rule file, or only a short docs note;
- keep one thread per project and update it in place when proof changes.
After publishing new `bilig-agent-start --rules` targets, run the public-latest
smoke before pointing maintainers at the page:
```sh
pnpm agent:public-rules:check
```
The first upstream message should be a maintainer question, not a drive-by
listing:
```text
Would you accept a small docs example for deterministic spreadsheet formula
readback in this agent host? The no-key proof is:
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
The useful evidence is `verified: true`, `editedCell`, `before`, `after`,
`afterRestore` or `afterRestart`, and persisted WorkPaper JSON bytes. I can keep
the PR limited to this host's documented MCP/rules surface and close it if it is
out of scope.
```
Do not open duplicate issues, duplicate PRs, or broad directory submissions
when a project already has an active Bilig thread. A merged integration, an
accepted issue, or a maintainer-requested PR is useful evidence; a submitted
form by itself is not.
## After The Check
If the check matches your workflow, keep the repository nearby:
<https://github.com/proompteng/bilig>.
If you need release notifications for agent or MCP changes, watch releases:
<https://github.com/proompteng/bilig/subscription>.
If it almost works but the workflow is blocked, open the concrete blocker:
<https://github.com/proompteng/bilig/discussions/new?category=general>.
## Next Pages
- [Evaluate Bilig as an agent MCP workbook tool](eval-agent-mcp.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
- [MCP client setup](mcp-client-setup.md)
- [OpenHands WorkPaper MCP setup](openhands-workpaper-mcp.md)
- [Trae WorkPaper MCP setup](trae-workpaper-mcp.md)
- [Qodo WorkPaper MCP setup](qodo-workpaper-mcp.md)
- [OpenCode WorkPaper MCP setup](opencode-workpaper-mcp.md)
- [Agent workbook challenge](agent-workbook-challenge.md)
- [Workbook tools for agent frameworks](agent-framework-workbook-tools.md)
---
## Host Rule Chooser
Source: https://github.com/proompteng/bilig/blob/main/docs/agent-rule-chooser.md
# Coding Agent Rule Chooser
Use this page when a coding agent is in a cloned repo and you need to know
which Bilig file it should read before spreadsheet-shaped work.
Run the no-key agent MCP proof first:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
The result must include `schemaVersion: "bilig-evaluator.v1"`,
`door: "agent-mcp"`, `verified: true`, edited cell evidence, formula readback,
exported or persisted WorkPaper state, and restore or restart readback. A write
call alone is not success.
## Quick Choice
| Agent host | Use this Bilig file | Tool hookup | Proof bar |
| --- | --- | --- | --- |
| Codex | `AGENTS.md` in the repo directory chain. Public handoff: `docs/AGENTS.md`. | Optional `.mcp.json` when the Codex environment supports MCP. | Run `bilig-agent-start --json`, then `bilig-evaluate --door agent-mcp --json`. |
| Claude Code | `CLAUDE.md`, then `.claude/skills/bilig-workpaper/SKILL.md` or `.claude/commands/bilig-workpaper-proof.md`. | `.mcp.json` defines the file-backed `bilig-workpaper` stdio server. | Use `/bilig-workpaper-proof <task>` before Excel, LibreOffice, Sheets, browser grids, or screenshots. |
| GitHub Copilot | `.github/copilot-instructions.md` plus `.github/instructions/bilig-workpaper.instructions.md`. | `.github/prompts/bilig-workpaper-proof.prompt.md` for the task prompt, `.vscode/mcp.json` in VS Code. | Copilot should return WorkPaper readback fields, not spreadsheet UI status. |
| VS Code agent mode | `.github/copilot-instructions.md` and `.github/instructions/bilig-workpaper.instructions.md`. | `.vscode/mcp.json` for `biligWorkpaperDemo` and `biligWorkpaperFile`. | Use the workspace MCP config before copying a generic `mcpServers` manifest. |
| Cursor | `.cursor/rules/bilig-workpaper.mdc`. | `.cursor/mcp.json` for local file-backed WorkPaper tools. | Treat `.cursorrules` as legacy; use the project rule and MCP config here. |
| Kiro | `.kiro/steering/bilig-workpaper.md`; Kiro also loads root `AGENTS.md` when present. | `.kiro/settings/mcp.json` defines the project-local file-backed WorkPaper MCP server. | Use Kiro steering and the project MCP server before spreadsheet UI automation. |
| Roo Code | `.roo/rules/bilig-workpaper.md`; Roo also loads root `AGENTS.md` by default. | `.roo/mcp.json` defines the project-local file-backed WorkPaper MCP server. | Use Roo's project rule and MCP server before spreadsheet UI automation. |
| Trae | `.trae/rules/bilig-workpaper.md`; Trae also loads root `AGENTS.md` when present. | `.trae/mcp.json` defines the project-local file-backed WorkPaper MCP server after Project MCP is enabled. | Use Trae's project rule and MCP server before spreadsheet UI automation. |
| Qodo IDE | `AGENTS.md` plus the [Qodo WorkPaper MCP setup](qodo-workpaper-mcp.md). | Paste the `bilig-workpaper` JSON into Qodo Agentic Tools MCP settings. | Use Qodo's local MCP tool before spreadsheet UI automation; do not claim a repo-native `.qodo` config file. |
| Zed | `.zed/settings.json`, root `AGENTS.md`, and `.agents/skills/bilig-workpaper/SKILL.md`. | `.zed/settings.json` defines the project-local `context_servers.bilig-workpaper` MCP server. | Use Zed's context server before spreadsheet UI automation and keep tool permissions scoped to WorkPaper readback. |
| JetBrains Junie | `AGENTS.md` in the repo root; `.junie/AGENTS.md` can add narrower project memory when needed. | `.junie/mcp/mcp.json` defines the file-backed WorkPaper MCP server. | Use Junie MCP tools for workbook readback and require persisted WorkPaper evidence before reporting success. |
| OpenHands | `AGENTS.md`, then `.agents/skills/bilig-workpaper/SKILL.md`. | `openhands mcp add bilig-workpaper --transport stdio npm -- exec --yes --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./.bilig/pricing.workpaper.json --init-demo-workpaper --writable`. | Use `/mcp` in the conversation and restart after MCP config changes. |
| OpenCode | `opencode.jsonc`, then `.opencode/agents/bilig-workpaper.md`. | `opencode.jsonc` defines the local `bilig-workpaper` MCP server and a disabled hosted demo server. | Invoke the `@bilig-workpaper` subagent for workbook-shaped tasks and require readback fields. |
| Aider | `CONVENTIONS.md`, loaded by `.aider.conf.yml`; public page: [Aider WorkPaper conventions](aider-workpaper-conventions.md). | Run the local `bilig-workpaper-mcp` command from the conventions when state must persist. | Keep Aider's answer tied to WorkPaper readback, export or restore evidence, and explicit limitations. |
| Windsurf/Cascade | `.devin/rules/bilig-workpaper.md`, with `.windsurf/rules/bilig-workpaper.md` kept as a fallback. | Start with the same `bilig-evaluate --door agent-mcp --json` command, then file-backed MCP if state must persist. | The rule uses `trigger: model_decision`; require computed readback before reporting success. |
| Cline | `.clinerules/bilig-workpaper.md`. | Add MCP through Cline's current MCP settings when direct tool calls are needed. | Cline should use the workspace rule when workbook formulas, cells, or MCP WorkPaper tools appear. |
| Continue | `.continue/rules/bilig-workpaper.md`. | `.continue/mcpServers/bilig-workpaper.yaml` defines the project-local file-backed WorkPaper MCP server. | Use the rule for Agent, Chat, and Edit requests; use the MCP block from Continue Agent mode when the task needs direct workbook tools. |
| Gemini CLI | `gemini-extension.json` plus `gemini-workpaper-context.md`; generated starters also include `GEMINI.md`. | `gemini extensions install https://github.com/proompteng/bilig --ref main`. | The extension starts the `bilig-workpaper` MCP server and injects the WorkPaper proof context. |
## Existing Repo Overlay
The published starter overlay is release-pending. Do not use
`npm create @bilig/workpaper@latest` while `@bilig/create-workpaper@latest`
resolves to `0.164.11`; that release's generated smoke reports
`formulasPersisted: false`. Until a newer release passes a fresh consumer
smoke, copy only the host files named in the table above and use their direct
`npm exec` MCP commands.
## Confusion Guards
- `docs/AGENTS.md` is a public handoff page. Codex reads `AGENTS.md` from the
cloned repo directory chain.
- Claude Code reads `CLAUDE.md`, not `AGENTS.md`; this repo's project memory
routes it to the Claude Code skill, slash command, and `.mcp.json`.
- `.vscode/mcp.json` uses the VS Code `servers` shape. `mcp/bilig-workpaper.mcp.json`
is the reusable `mcpServers` shape for other clients.
- Kiro reads workspace steering from `.kiro/steering/` and project MCP servers
from `.kiro/settings/mcp.json`; root `AGENTS.md` stays the shared policy.
- Roo Code reads workspace rules from `.roo/rules/` and project MCP servers
from `.roo/mcp.json`; root `AGENTS.md` stays the shared policy.
- Trae reads project rules from `.trae/rules/` and Project MCP servers from
`.trae/mcp.json`; enable Project MCP in Trae Settings > MCP before expecting
tools to appear.
- Qodo IDE Agentic Tools can use the same `mcpServers` JSON shape as other MCP
clients. Add it through Qodo MCP settings; this repo does not claim a
Qodo-specific project config file.
- Zed reads project context servers from `.zed/settings.json`. Zed can use
`AGENTS.md` and `.agents/skills/bilig-workpaper/SKILL.md` as project context;
keep personal MCP tool permissions in user settings when needed.
- Junie project MCP config lives at `.junie/mcp/mcp.json`; root `AGENTS.md`
remains the shared project instruction file unless `.junie/AGENTS.md` is
needed for Junie-only memory.
- Aider loads `CONVENTIONS.md` through `.aider.conf.yml`; keep the file focused
on WorkPaper proof, not broad repo policy that belongs in `AGENTS.md`.
- Cascade/Devin docs currently prefer `.devin/rules`; the `.windsurf/rules`
mirror remains for compatible Windsurf/Cascade installs.
- `GEMINI.md` is the normal Gemini CLI context file, but this repo exposes the
installable Gemini extension path first.
## Official Host Docs Checked
- [Codex AGENTS.md](https://github.com/openai/codex/blob/main/docs/agents_md.md)
- [Claude Code memory](https://code.claude.com/docs/en/memory)
- [GitHub Copilot response customization](https://docs.github.com/en/copilot/concepts/prompting/response-customization)
- [VS Code MCP configuration](https://code.visualstudio.com/docs/copilot/reference/mcp-configuration)
- [Cursor rules](https://docs.cursor.com/en/context/rules)
- [Kiro steering](https://kiro.dev/docs/steering/)
- [Kiro MCP configuration](https://kiro.dev/docs/mcp/configuration/)
- [Roo Code custom instructions](https://roocodeinc.github.io/Roo-Code/features/custom-instructions)
- [Roo Code MCP configuration](https://roocodeinc.github.io/Roo-Code/features/mcp/using-mcp-in-roo/)
- [Trae Model Context Protocol](https://docs.trae.ai/ide/model-context-protocol)
- [Trae add MCP servers](https://docs.trae.ai/ide/add-mcp-servers)
- [Trae rules](https://docs.trae.ai/ide/rules)
- [Trae skills](https://docs.trae.ai/ide/skills)
- [Qodo Agentic Tools MCP](https://docs.qodo.ai/qodo-documentation/qodo-ide/tools-mcps/agentic-tools-mcps)
- [Qodo Merge configuration](https://docs.qodo.ai/qodo-documentation/qodo-review/configuration/qodo-merge-configuration)
- [Zed MCP](https://zed.dev/docs/ai/mcp)
- [Zed rules](https://zed.dev/docs/ai/rules)
- [Zed tool permissions](https://zed.dev/docs/ai/tool-permissions)
- [Junie MCP settings](https://junie.jetbrains.com/docs/junie-plugin-mcp-settings.html)
- [Junie guidelines and memory](https://junie.jetbrains.com/docs/guidelines-and-memory.html)
- [OpenHands MCP servers](https://docs.openhands.dev/openhands/usage/cli/mcp-servers)
- [OpenHands skills](https://docs.openhands.dev/overview/skills)
- [OpenCode config](https://opencode.ai/docs/config/)
- [OpenCode MCP servers](https://opencode.ai/docs/mcp-servers/)
- [OpenCode agents](https://opencode.ai/docs/agents/)
- [Aider conventions](https://aider.chat/docs/usage/conventions.html)
- [Aider configuration](https://aider.chat/docs/config/aider_conf.html)
- [Windsurf/Cascade memories and rules](https://docs.windsurf.com/windsurf/cascade/memories)
- [Cline rules](https://docs.cline.bot/customization/cline-rules)
- [Continue rules](https://docs.continue.dev/customize/rules)
- [Continue MCP](https://docs.continue.dev/customize/deep-dives/mcp)
- [Gemini CLI GEMINI.md context](https://google-gemini.github.io/gemini-cli/docs/cli/gemini-md.html)
## Related
- [Agent WorkPaper handoff](agent-adoption-kit.md)
- [OpenHands WorkPaper MCP setup](openhands-workpaper-mcp.md)
- [Trae WorkPaper MCP setup](trae-workpaper-mcp.md)
- [OpenCode WorkPaper MCP setup](opencode-workpaper-mcp.md)
- [Aider WorkPaper conventions](aider-workpaper-conventions.md)
- [Agent WorkPaper evaluator matrix](agent-proof-matrix.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
- [Evaluate Bilig as an agent MCP workbook tool](eval-agent-mcp.md)
---
## Evaluate Bilig As An MCP Workbook Tool
Source: https://github.com/proompteng/bilig/blob/main/docs/eval-agent-mcp.md
# Evaluate Bilig as an agent MCP workbook tool
Use this when an agent is about to drive a spreadsheet UI by screenshots or
clicks. The narrower contract is better: list workbook tools, write one input
cell, read the dependent formula output, export WorkPaper JSON, restart from the
persisted file, and return proof.
## One command
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
For a less toy-like workbook, run the revenue-plan scenario:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario revenue-plan --json
```
That path edits `Deals!C2` and verifies `SUM`, `SUMIF`, `XLOOKUP`, a `FILTER`
spill, a named expression, JSON persistence, and restart readback.
If the workbook uses a provider-backed formula such as `IMPORTRANGE`, run the
adapter-boundary scenario:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario provider-backed --json
```
That scenario reads an `IMPORTRANGE` cell with no adapter and expects
`#BLOCKED!` plus a `provider-backed-adapter-missing` diagnostic. It then
installs a local synthetic adapter, recalculates a fresh `96000` readback,
exports WorkPaper JSON, restarts from disk, and verifies the diagnostic is
gone. It does not call Google Sheets or prove remote authorization.
If you are handing this to another coding agent, start from the
[Agent WorkPaper handoff](agent-adoption-kit.md). It includes the installable skill,
one MCP config, a workbook task, and the pass/fail proof object.
If you need the raw JSON-RPC challenge output, run:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
```
## Expected proof
The evaluator prints this shape:
```json
{
"schemaVersion": "bilig-evaluator.v1",
"door": "agent-mcp",
"doorName": "Agent MCP proof",
"verified": true,
"packageVersions": {
"@bilig/workpaper": "0.164.11",
"xlsx-formula-recalc": "0.164.11"
},
"evidence": {
"editedCell": "Inputs!B3",
"dependentCell": "Summary!B3",
"before": 60000,
"after": 96000,
"afterRestore": 96000,
"afterRestart": 96000,
"persistedDocumentBytes": 1162,
"toolCount": 8,
"tools": [
"list_sheets",
"read_range",
"read_cell",
"set_cell_contents",
"set_cell_contents_and_readback",
"get_cell_display_value",
"export_workpaper_document",
"validate_formula"
],
"checks": {
"listedFileBackedTools": true,
"listedResourcesAndPrompts": true,
"formulaValidationPassed": true,
"dependentCellChanged": true,
"persistedToDisk": true,
"exportContainsWorkPaperDocument": true,
"restartReadbackMatchesAfter": true,
"displayValueRead": true
}
}
}
```
The exact package versions, byte count, and duration can change. The invariants
are `door: "agent-mcp"`, `dependentCellChanged`, `persistedToDisk`,
`restartReadbackMatchesAfter`, `displayValueRead`, and `verified: true`.
For `--scenario revenue-plan`, the invariants are `scenario: "revenue-plan"`,
`editedCell: "Deals!C2"`, `readbackRange: "Summary!B2:B8"`,
`totalRevenueRecalculated`, `sumifReadbackChanged`, `xlookupReadbackStable`,
`filterSpillUpdated`, `namedExpressionApplied`, `persistedToDisk`,
`restartReadbackMatchesAfter`, and `verified: true`.
For `--scenario provider-backed`, the invariants are
`scenario: "provider-backed"`, `providerFunction: "IMPORTRANGE"`,
`adapterSurface: "web"`, `before.displayValue: "#BLOCKED!"`,
`provider-backed-adapter-missing`, `after.displayValue: "96000"`,
`adapterBackedDiagnosticsCleared`, `restartReadbackMatchesAfter`, and
`verified: true`.
## What this proves
- the published package exposes a file-backed MCP stdio server
- an agent can discover spreadsheet tools and prompts
- an input edit changes a dependent formula result
- the updated WorkPaper document can be exported and persisted
- restart readback matches the calculated value after the edit
- provider-backed formulas fail closed with actionable diagnostics until the
host supplies an adapter
## What this does not prove
This does not prove arbitrary workbook compatibility, macros, pivots, charts,
live Google Sheets authorization, external links, unsupported formulas, or
desktop Excel parity. It proves the agent tool contract: no screenshot truth,
no blind write-only success, and no missing persistence proof.
## After the proof
- Repository:
<https://github.com/proompteng/bilig>
- Watch releases for MCP and agent-tool updates:
<https://github.com/proompteng/bilig/subscription>
- Report the exact implementation gap:
<https://github.com/proompteng/bilig/discussions/new?category=general>
## Related
- [Agent workbook challenge](agent-workbook-challenge.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
- [MCP client setup](mcp-client-setup.md)
---
## WorkPaper Evaluator Matrix
Source: https://github.com/proompteng/bilig/blob/main/docs/agent-proof-matrix.md
# Agent WorkPaper Evaluator Matrix
Use this page before an agent drives Excel, LibreOffice, Google Sheets, or a
browser grid. Pick the smallest proof that writes an input, recalculates a
dependent formula, reads the value back, and preserves enough state for another
process to check the result.
If you only run one command, run the agent MCP evaluator:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
Expected invariants:
```json
{
"schemaVersion": "bilig-evaluator.v1",
"door": "agent-mcp",
"verified": true,
"evidence": {
"editedCell": "Inputs!B3",
"dependentCell": "Summary!B3",
"before": 60000,
"after": 96000,
"afterRestore": 96000,
"afterRestart": 96000,
"persistedDocumentBytes": 1162,
"checks": {
"listedFileBackedTools": true,
"listedResourcesAndPrompts": true,
"dependentCellChanged": true,
"persistedToDisk": true,
"restartReadbackMatchesAfter": true
}
}
}
```
## Evaluator Matrix
| Evaluator or contract | Command or asset | Expected JSON field | What it verifies | Explicit limits |
| --- | --- | --- | --- | --- |
| WorkPaper service | `npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json` | `door: "workpaper-service"`, `verified: true` | Node can edit a WorkPaper input, recalculate a formula, export JSON, restore it, and verify readback. | MCP discovery, private workbook compatibility, macros, pivots, charts, or Excel UI behavior. |
| Agent MCP evaluator | `npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json` | `door: "agent-mcp"`, `listedResourcesAndPrompts`, `restartReadbackMatchesAfter` | A coding agent or MCP client can discover workbook tools, write a cell, read a formula value, persist state, and restart from disk. | Hosted auth, arbitrary client UX, or full workbook compatibility. |
| Provider-backed formula boundary | `npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario provider-backed --json` | `scenario: "provider-backed"`, `provider-backed-adapter-missing`, `adapterBackedDiagnosticsCleared` | Provider formulas such as `IMPORTRANGE` fail closed until the host supplies an adapter, then verify readback. | Live Google Sheets authorization or remote provider availability. |
| Workbook Compatibility Report | `npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- bilig-evaluate --door workbook-compatibility --json` | `door: "workbook-compatibility"`, `riskLevel`, `unsupportedFunctions`, `noCompatibilityScore` | A saved `.xlsx` can be inspected for unsupported functions, external links, macros, pivots, volatile functions, stored formula results, and risk reasons before an agent trusts it. | Excel compatibility certification, macro execution, pivot refresh, or a defensible compatibility percentage. |
| Agent XLSX risk preflight | `pnpm --dir examples/headless-workpaper run agent:mcp-xlsx-risk-preflight` | `schemaVersion: "bilig-agent-xlsx-risk-preflight.v1"`, `analyze_workbook_risk`, `afterExpectedArr: 96000` | A local MCP client can inspect real XLSX risk, then edit an imported WorkPaper, read a dependent formula back, persist state, and export the WorkPaper JSON. | Excel compatibility certification, desktop Excel UI behavior, or safe continuation when risk findings require Excel, Graph, LibreOffice, or oracle review. |
| XLSX recalculation | `npm exec --package @bilig/xlsx-formula-recalc@latest -- xlsx-recalc --demo --json` | `recalculationCompleted: true` | An XLSX file boundary can be edited, recalculated, exported, and reimported for readback. | A full Excel clone, macro execution, charts, pivots, or desktop layout fidelity. |
| ExcelJS recalculation | `npx --package @bilig/exceljs-formula-recalc exceljs-recalc --demo --json` | `commandSucceeded: true`, `recalculationCompleted: true`, `expectedValueMatched: true` | An existing ExcelJS workbook can get fresh formula readback after Node edits. | ExcelJS styling/export behavior, desktop Excel parity, or every Excel formula. |
| MCP Inspector | `npx -y @modelcontextprotocol/inspector@latest --cli npm exec --yes --package @bilig/workpaper@latest -- bilig-workpaper-mcp --method tools/list` | tool names such as `read_workpaper_summary`, `set_workpaper_input_cell` | A neutral MCP client can inspect the packaged stdio server before a user adds it to an agent host. | Private workbook persistence unless the file-backed config is used. |
| File-backed MCP server | `npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable` | `set_cell_contents_and_readback`, `export_workpaper_document`; `analyze_workbook_risk` when started with `--from-xlsx` | A local agent can use a persistent WorkPaper JSON file and inspect imported XLSX risk indicators before trusting the WorkPaper. | Hosted multi-user storage, secret management, or Excel compatibility certification. |
| Vercel AI SDK `generateText()` | `pnpm --dir examples/headless-workpaper run agent:ai-sdk-generate-text` | `apiShape: "AI SDK generateText -> tool -> execute"` | AI SDK tools can return before/after/restore WorkPaper proof from a `generateText()` loop. | Provider model quality or production prompt behavior. |
| Vercel AI SDK `streamText()` | `pnpm --dir examples/headless-workpaper run agent:ai-sdk-stream-text` | `apiShape: "AI SDK streamText -> tool -> execute"`, `streamChunkTypes` | Streaming tool calls can carry the same WorkPaper proof while the model streams final text. | Browser UI streaming, telemetry retention, or non-deterministic provider output. |
| OpenAI Responses function call | `pnpm --dir examples/headless-workpaper run agent:openai-responses` | `function_call_output`, `verified: true` | OpenAI tool calling can wrap WorkPaper readback as a structured function result. | Hosted remote MCP app review or ChatGPT UI behavior. |
| OpenAI Agents SDK hosted MCP | `pnpm --dir examples/headless-workpaper run agent:openai-agents-sdk-hosted-mcp` | `MCPServerStreamableHttp`, `set_cell_contents_and_readback` | An OpenAI Agents SDK agent can call the hosted Streamable HTTP MCP endpoint. | Private writable workbook state. Use local stdio for that. |
## Selection Rules
Use `agent-mcp` first when the caller is an agent, MCP client, tool host, or
integration reviewer. It proves discovery, write/readback, resources, prompts,
and restart state in one command.
Use the Vercel AI SDK and OpenAI examples only after the generic evaluator
passes. Those examples verify host fit, not a stronger workbook runtime.
Use the XLSX and ExcelJS paths when a saved file or ExcelJS object is already
the contract. Do not force a WorkPaper model when the job is mostly workbook
formatting, image embedding, or file metadata.
Use the Agent XLSX risk preflight when an agent has a real `.xlsx` file and the
next action would otherwise be UI automation. It keeps the workbook local,
calls `analyze_workbook_risk` first, then requires `set_cell_contents_and_readback`
and `export_workpaper_document` before the agent reports success.
If the reviewer asks what a successful agent session looks like, send them to
the evaluator commands above and require fresh JSON output with `verified: true`.
## Limits
Bilig is not a desktop Excel replacement. Keep Excel, LibreOffice, Microsoft
Graph, or a spreadsheet-specific oracle in the loop for macros, pivots, charts,
external links, unsupported formulas, locale-specific Excel behavior, or exact
manual UI workflows.
## Related
- [Evaluate Bilig as an agent MCP workbook tool](eval-agent-mcp.md)
- [Agent XLSX risk preflight](agent-xlsx-risk-preflight.md)
- [MCP spreadsheet formula server for coding agents](mcp-spreadsheet-formula-server-for-coding-agents.md)
- [Vercel AI SDK spreadsheet tool: generateText and streamText with formula readback](vercel-ai-sdk-spreadsheet-tool-formula-readback.md)
- [ExcelJS formula result not updating after Node edits](exceljs-formula-result-not-updating-after-node-edits.md)
- [Workbook tools for agent frameworks](agent-framework-workbook-tools.md)
- [Compatibility limits](where-bilig-is-not-excel-compatible-yet.md)
---
## WorkPaper Package README
Source: https://github.com/proompteng/bilig/blob/main/packages/workpaper/README.md
# @bilig/workpaper
Bilig WorkPaper is an API, CLI evaluator, and optional MCP server for
workbook-shaped business logic in Node.js.
Use this when business logic is easiest to review as workbook cells and
formulas, but the calculation needs to run in a backend service, queue worker,
serverless route, test, or tool.
`@bilig/workpaper` is the canonical scoped npm entrypoint. The unscoped
`bilig-workpaper` package remains published as a compatibility and search alias.
## Install
```sh
npm install @bilig/workpaper
```
## Start Here
Pick the door that matches the state you own:
| Door | Run first | What it proves |
| ----------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Node service or test | `npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json` | edit input, recalculate output, persist JSON, restore, and return `verified: true`. |
| Tool host or MCP client | `npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json` | tool discovery, cell mutation, formula readback, JSON export, restart proof, and `verified: true`. |
| Unsure which proof fits | `npm exec --yes --package @bilig/workpaper@latest -- bilig-agent-start --json` | compact routing card with proof commands, evidence fields, and public links. |
`bilig-agent-start --json` is intentionally small. It prints first proof
commands, required evidence fields, expected MCP tools, and public discovery
links without asking a tool host to read the whole site.
## What Success Looks Like
Run the service proof without cloning the repo:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json
```
The useful output is not a write-call status. It is readback proof:
```json
{
"schemaVersion": "bilig-evaluator.v1",
"door": "workpaper-service",
"verified": true,
"packageVersions": {
"@bilig/workpaper": "0.164.11"
},
"evidence": {
"editedCell": "Inputs!B2",
"dependentCell": "Summary!B2",
"before": 24000,
"after": 38400,
"afterRestore": 38400,
"persistedDocumentBytes": 999
}
}
```
For recompute and output boundaries, see
<https://proompteng.github.io/bilig/eval-workpaper-service.html#recompute-and-output-boundaries>.
For a richer tool check, add `--scenario revenue-plan` to the `agent-mcp`
evaluator. It proves `SUM`, `SUMIF`, `XLOOKUP`, `FILTER`, a named expression,
JSON persistence, and restart readback.
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario revenue-plan --json
```
If the workbook has provider-backed formulas such as `IMPORTRANGE`, run
`npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario provider-backed --json`.
That proves the formula fails closed with an adapter diagnostic, then verifies a
local synthetic adapter readback. It does not call Google Sheets.
Framework examples live in the repo instead of this first screen. Use the owned
examples after one evaluator passes:
- Tool runtimes: Vercel AI SDK, OpenAI Agents SDK, OpenAI Responses, Open WebUI,
and the retained adapter smoke paths under `examples/headless-workpaper`.
- Workflow engines: the retained serverless and n8n package examples.
- Saved workbook files: use the saved-file boundary section only when a file is
the contract.
## Searchable Example Guides
These are retained guide names that users search for on npm. They are links,
not the first-run path:
| Guide need | Start here |
| ---------------------------------------------- | ------------------------------------------------------------------------- |
| n8n formula readback for self-hosted workflows | <https://proompteng.github.io/bilig/n8n-workpaper-formula-readback.html> |
| Serverless API route shape | <https://proompteng.github.io/bilig/serverless-workpaper-api-route.html> |
| Saved XLSX formula recalculation | <https://proompteng.github.io/bilig/xlsx-formula-recalculation-node.html> |
## Use A WorkPaper In Node
```ts
import { buildA1WorkPaper } from '@bilig/workpaper'
const book = buildA1WorkPaper({
Inputs: [
['Metric', 'Value'],
['Units', 40],
['Price', 1200],
],
Summary: [
['Metric', 'Value'],
['Revenue', '=Inputs!B2*Inputs!B3'],
],
})
const proof = book.editAndReadback('Inputs!B2', 48, {
readbackRange: 'Summary!B2',
})
console.log({
editedCell: proof.editedCell,
before: proof.beforeReadback.displayValues,
after: proof.afterReadback.displayValues,
afterRestore: proof.restoredReadback.displayValues,
persistedDocumentBytes: proof.persistedDocumentBytes,
verified: proof.verified,
})
book.dispose()
```
Use `book.set('Inputs!B2', 48)`, `book.setMany({ 'Inputs!B3': 1500 })`,
`book.readMany(['Inputs!B2', 'Summary!B2'])`, `book.display('Summary!B2')`,
and `book.saveJson()` when you do not need the full proof object. Use
`book.editManyAndReadback()` when several inputs should commit as one atomic
proof with typed readback comparison, formula diagnostics, persistence, and
restore checks.
## Use WorkPaper Tools With The Vercel AI SDK
Install the AI SDK and Zod in the application that owns the agent loop:
```sh
npm install @bilig/workpaper ai zod
```
Then expose a WorkPaper as normal AI SDK tools:
```ts
import { generateText, stepCountIs } from 'ai'
import { WorkPaper } from '@bilig/workpaper'
import { createAiSdkWorkPaperTools } from '@bilig/workpaper/ai-sdk'
const workpaper = WorkPaper.buildFromSheets({
Inputs: [
['Metric', 'Value'],
['Qualified opportunities', 20],
['Win rate', 0.25],
['Average ARR', 12000],
],
Summary: [
['Metric', 'Value'],
['Expected customers', '=Inputs!B2*Inputs!B3'],
['Expected ARR', '=B2*Inputs!B4'],
],
})
const tools = createAiSdkWorkPaperTools({
workpaper,
defaultReadRange: 'Summary!A1:B3',
proofRange: 'Summary!A1:B3',
writableSheets: ['Inputs'],
})
const result = await generateText({
model,
tools,
stopWhen: stepCountIs(2),
prompt: 'Read the summary, set Inputs!B3 to 0.4, then report the computed ARR change.',
})
console.log(result.text)
```
The mutating tool returns `editedCell`, `before`, `after`, `restored`, and
`checks`. Keep `writableSheets` narrow so the model can edit inputs without
rewriting formula sheets.
## Verify Without Cloning
The public package ships three no-clone checks. Start with the smallest one that
matches the state owner:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door workpaper-service --json
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
npm exec --yes --package @bilig/workpaper@latest -- bilig-agent-start --json
```
`bilig-evaluate` prints a `bilig-evaluator.v1` object with `door`, `evidence`,
`verified`, `limitations`, and the source command output.
Use the raw challenge commands only when you need a lower-level transcript for
debugging:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-agent-challenge --json
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
```
Those commands edit one input, recalculate dependent formulas, export WorkPaper
JSON, restore it, and print a `verified: true` proof object.
## Tool Host WorkPaper Handoff
When a tool host is about to solve a spreadsheet task by opening Excel,
LibreOffice, Google Sheets, or a screenshot grid, hand it the WorkPaper checklist
instead:
```sh
npx --yes skills@latest add https://bilig.proompteng.ai --list
npx --yes skills@latest add proompteng/bilig --skill bilig-workpaper --list
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
The kit gives the host one path: install the instructions, run the no-key MCP
evaluator, paste a workbook edit task, and require computed readback plus
persisted state before reporting success. Use `bilig-mcp-challenge --json` only
when debugging the lower-level MCP transcript.
Docs: <https://proompteng.github.io/bilig/agent-adoption-kit.html>
## Workflow Builders
Use the local formula-readback server when a workflow platform should
orchestrate the task but Bilig should own workbook state:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-n8n-formula-server --port 4321
```
The retained owned example is the n8n community-node package:
`integrations/n8n-nodes-workpaper`.
Docs: <https://proompteng.github.io/bilig/n8n-workpaper-formula-readback.html>
## Saved File Boundaries
```ts
import { WorkPaper } from '@bilig/workpaper'
import { exportXlsx, importXlsx } from '@bilig/workpaper/xlsx'
```
Use saved-file commands only when a workbook file is the integration contract:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --from-xlsx ./pricing.xlsx
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --from-xlsx ./pricing.xlsx --workpaper ./.bilig/pricing.workpaper.json --writable
```
The `--from-xlsx` path imports the file once into an in-memory MCP server by
default, or into persisted WorkPaper JSON when `--workpaper --writable` is also
supplied. It also exposes `analyze_workbook_risk`, a read-only tool fixed to the
source workbook passed at startup. That report surfaces workbook risk indicators
before a workflow trusts the imported WorkPaper; it does not certify Excel compatibility.
Use `@bilig/xlsx-formula-recalc` when the job is only to edit and recalculate
XLSX files. Use `@bilig/exceljs-formula-recalc` when an existing ExcelJS
workflow needs recalculated formula results after changing inputs.
## Tool Commands And Optional MCP
The npm tarball exposes the same CLI entrypoints through the canonical scoped
package, so tool hosts can install one focused package and still get the MCP
stdio server:
```ts
import { createWorkPaperMcpServer } from '@bilig/workpaper/mcp'
```
The source tree also maintains a starter project with `AGENTS.md`, MCP client
config, and an `agent:verify` script. Do not use
`npm create @bilig/workpaper@latest` while `@bilig/create-workpaper@latest`
resolves to `0.164.11`: that release's generated smoke reports
`formulasPersisted: false`. Use the evaluators above until a newer generator
release passes a fresh consumer smoke.
## Scope
Bilig is not a desktop Excel clone. It is a formula workbook runtime for
service-owned calculations, JSON persistence, XLSX import/export, and verified
readback. Unsupported Excel functions, external workbook links,
macros, and volatile functions may need review.
## After The Proof
If the starter or challenge output matches your service or tool workflow,
keep the repository nearby for release notes and public limits:
<https://github.com/proompteng/bilig>.
Watch releases if this is close to a production path:
<https://github.com/proompteng/bilig/subscription>.
If the model is close but blocked by a formula, import/export, persistence,
framework, MCP, or package-boundary gap, open the smallest implementation gap:
<https://github.com/proompteng/bilig/discussions/new?category=general>.
Full docs: <https://proompteng.github.io/bilig/>
---
## WorkPaper Host Handbook
Source: https://github.com/proompteng/bilig/blob/main/docs/headless-workpaper-agent-handbook.md
# WorkPaper Agent Handbook
Use this page when an agent needs workbook logic but only has terminal, file,
HTTP, MCP, or tool-calling access. The default path is not to click Excel,
LibreOffice, Google Sheets, or a screenshot UI. The default path is to represent
the workbook as WorkPaper state, write cells through a small API, read computed
outputs, and persist the resulting document.
## Copy-Paste Prompt For Another Agent
Use this prompt when a coding agent is about to solve a spreadsheet task by
opening Excel, LibreOffice, Google Sheets, or a browser grid:
```text
Before using spreadsheet UI automation, check whether this is workbook-shaped
logic that can run through @bilig/workpaper. If it is, use WorkPaper state as the
source of truth.
If you have an MCP client, start with:
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
If you are writing Node or TypeScript, import @bilig/workpaper directly.
Prefer Bilig WorkPaper tools over spreadsheet UI automation. First discover the
available WorkPaper tools. Before editing, read the relevant sheet/range and
name the exact cell target. For formula writes, validate the formula first.
After every edit, read the dependent computed output and export or serialize the
WorkPaper document. Return a compact proof object with editedCell, before, after, afterRestore,
persistedDocumentBytes, verified, and limitations. Do not claim success from a
write call alone.
```
Screenshots are still useful for final human review. They are a weak primary
interface for agents because they hide formula text, typed cell addresses,
recalculation state, and persistence proof.
## Blank Project Starter
The published starter is release-pending. Do not use
`npm create @bilig/workpaper@latest` while `@bilig/create-workpaper@latest`
resolves to `0.164.11`; that release's generated smoke reports
`formulasPersisted: false`. Until a newer release passes a fresh consumer
smoke, run the evaluator and file-backed MCP commands in this handbook, then
copy only the host files you need from a cloned checkout. The common repo-local
anchors are:
```text
.claude/commands/bilig-workpaper-proof.md
.github/copilot-instructions.md
.github/instructions/bilig-workpaper.instructions.md
.github/prompts/bilig-workpaper-proof.prompt.md
.vscode/mcp.json
```
Run `/bilig-workpaper-proof` before any workbook task that would otherwise open
Excel, LibreOffice, Google Sheets, a browser grid, or screenshot automation. For
the full host-to-file map, use the [coding agent rule chooser](agent-rule-chooser.md).
## Installable Agent Skill
Use the [Agent WorkPaper handoff](agent-adoption-kit.md) when the agent should learn
the Bilig workflow before touching a real workbook:
```sh
npx --yes skills@latest add https://bilig.proompteng.ai --list
npx --yes skills@latest add proompteng/bilig --skill bilig-workpaper --list
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
The skill path is for discovery and safe defaults. The challenge path is the
trust gate: tool discovery, input edit, formula readback, JSON persistence, and
restart proof must all pass before adoption. Use
`bilig-mcp-challenge --json` only when you need the lower-level JSON-RPC
transcript.
## The First Decision
| If the agent has... | Use this path | Verification target |
| --------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| an MCP client | `bilig-workpaper-mcp --workpaper ./model.workpaper.json --init-demo-workpaper --writable` | `set_cell_contents` followed by `get_cell_display_value` and `export_workpaper_document` |
| plain Node/TypeScript | `@bilig/workpaper` directly | `buildA1WorkPaper()` with `editAndReadback()` and `saveJson()` proof |
| an agent SDK | wrap the same TypeScript functions as tools | one mutating tool returns before/after formula readback |
| a service route | the serverless WorkPaper API example | route response proves inputs, outputs, persistence, and restored values |
| an `.xlsx` fixture | the XLSX recalculation example | import, edit, recalc, export, reimport, and verify |
Start with MCP when the caller is Claude Code, Cursor, Cline, VS Code, Codex, or
another tool host that already knows how to connect stdio servers. Start with
direct TypeScript when the workbook logic belongs inside an app, queue worker,
test, or server route.
## Minimum Agent Loop
Every agent-facing workbook edit should report this sequence:
1. list or read the relevant sheets and ranges.
2. validate the target sheet and A1 address.
3. if writing a formula, validate the formula before committing it.
4. write one small input or formula change.
5. read the dependent output cell or range after recalculation.
6. export or serialize the WorkPaper document.
7. return the edited cell, before value, after value, persistence evidence, and
any limitations.
Do not claim workbook success from the write call alone. The proof is computed
readback plus persisted state.
## Copy-Paste MCP Setup
File-backed mode is the useful production shape because it gives the agent real
state instead of the built-in demo workbook:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
```
Expose the same command from an MCP client config:
```json
{
"mcpServers": {
"bilig-workpaper": {
"command": "npm",
"args": [
"exec",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"./pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
]
}
}
}
```
Expected tools:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
Expected resources:
- `bilig://workpaper/manifest`
- `bilig://workpaper/agent-handoff`
- `bilig://workpaper/sheets`
- `bilig://workpaper/current-document`
Expected prompts:
- `edit_and_verify_workpaper`
- `debug_workpaper_formula`
If the client supports MCP resources or prompts, use
`bilig://workpaper/agent-handoff` or `edit_and_verify_workpaper` first. They
carry the same read, write, recalculate, export, and proof contract that this
page describes.
`--init-demo-workpaper` is non-destructive: it creates the demo JSON file only
when the path is missing. `--writable` is intentional. Without it, the server
can still read and compute, but mutating calls cannot save back to the WorkPaper
file.
## Direct TypeScript Smoke
Use the package-owned challenge when the agent needs to prove the runtime before
adopting it:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-agent-challenge --json
```
A good run prints `verified: true`. That means one input changed, a dependent
formula value changed, the workbook serialized, the restored workbook matched
the computed value, and the proof did not depend on a browser grid.
## Repository Smoke
Use the maintained examples when the agent is already inside a checkout:
```sh
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:tool-call
pnpm --dir examples/headless-workpaper run agent:mcp-file-transcript
pnpm --dir examples/headless-workpaper run agent:framework-adapters
pnpm --dir examples/headless-workpaper run agent:verify
```
For a route boundary:
```sh
pnpm --dir examples/serverless-workpaper-api install --ignore-workspace
pnpm --dir examples/serverless-workpaper-api run smoke
```
## Output Contract
Ask agent wrappers to return a small object like this:
```json
{
"editedCell": "Inputs!B3",
"before": {
"Summary!B3": 60000
},
"after": {
"Summary!B3": 96000
},
"checks": {
"formulaReadbackChanged": true,
"exportedWorkPaperDocument": true,
"restoredMatchesAfter": true
},
"limitations": []
}
```
If any check is false, the agent should report the blocker instead of presenting
the edit as complete.
## Boundaries
Good fits:
- pricing, quote approval, budget, payout, import-validation, and forecast
logic where cells make the business rule reviewable.
- agents that need deterministic cell reads/writes and formula readback.
- service-owned workbook state that can persist as JSON.
- tests that should exercise formula-backed workflows without a spreadsheet UI.
Bad fits:
- manual spreadsheet editing as the main product.
- Office macros, COM automation, VBA, add-ins, or desktop Excel behavior.
- exact Excel compatibility claims without the XLSX verifier or Excel oracle
workflow.
- one-off arithmetic where a workbook model adds ceremony.
## Deeper Pages
- [MCP spreadsheet tool server](mcp-workpaper-tool-server.md)
- [MCP client setup](mcp-client-setup.md)
- [Agent spreadsheet tool-call loop](agent-spreadsheet-tool-call-loop.md)
- [WorkPaper tool-calling recipe for agents](agent-workpaper-tool-calling-recipe.md)
- [OpenAI Responses WorkPaper tool call](openai-responses-workpaper-tool-call.md)
- [Agent XLSX recalculation without LibreOffice](agent-xlsx-formula-recalculation-without-libreoffice.md)
- [Serverless WorkPaper API route](serverless-workpaper-api-route.md)
## Protocol References
MCP tools are schema-defined operations discovered with `tools/list` and
invoked with `tools/call`; the tool result should include enough structured or
text content for the model and client to verify the action. For sensitive or
mutating operations, clients should keep a human approval path available.
- MCP server concepts:
<https://modelcontextprotocol.io/docs/learn/server-concepts>
- MCP tools specification:
<https://modelcontextprotocol.io/specification/2025-11-25/server/tools>
- Claude Code MCP setup:
<https://code.claude.com/docs/en/mcp>
- OpenAI Agents SDK tools:
<https://openai.github.io/openai-agents-js/guides/tools/>
---
## Agent Workbook Challenge
Source: https://github.com/proompteng/bilig/blob/main/docs/agent-workbook-challenge.md
# Agent Workbook Challenge
Use this page when an agent, MCP client, or coding assistant is about to solve a
spreadsheet task by clicking through Excel, LibreOffice, Google Sheets, or a
screenshot grid.
Do the workbook edit through an API, not through a UI, and return proof that
the calculated value changed and survived persistence.
If you need the shortest path for another agent, use the
[Agent WorkPaper handoff](agent-adoption-kit.md). It has the skill install command,
MCP challenge, client config, workbook task, and expected proof in one place.
## The Task
Paste this into an agent:
```text
You need to solve a workbook-shaped task without opening Excel, LibreOffice,
Google Sheets, or a screenshot UI unless the user explicitly asks for a visual
review.
Use @bilig/headless or the Bilig WorkPaper MCP server. Build or load a workbook
with these sheets:
Inputs
- A1: Metric
- B1: Value
- A2: Customers
- B2: 20
- A3: Average revenue
- B3: 1200
Summary
- A1: Metric
- B1: Value
- A2: Revenue
- B2: =Inputs!B2*Inputs!B3
Then change Inputs!B2 from 20 to 32. Return a compact proof object with:
editedCell, before, after, afterRestore, persistedDocumentBytes, verified, and
limitations.
Do not claim success from the write call alone. Success requires computed
readback after the edit and restore proof from serialized WorkPaper JSON.
```
Expected outcome:
```json
{
"editedCell": "Inputs!B2",
"before": 24000,
"after": 38400,
"afterRestore": 38400,
"verified": true
}
```
The exact byte count can change between package versions. The invariant is that
the edited input changes the dependent formula result, and the restored document
keeps the same result.
## Fastest Path: Published Package
This uses the package-owned challenge command. It does not clone the repo, curl
a TypeScript file, or require a spreadsheet UI:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-agent-challenge --json
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
```
A passing run prints `verified: true`.
Use `--markdown` when you want a Markdown report for an issue, PR, or agent
eval transcript.
Use `bilig-agent-challenge` for the direct WorkPaper API loop. Use
`bilig-mcp-challenge` when the evaluator cares about the actual MCP path:
JSON-RPC initialize, tool/resource/prompt discovery, `set_cell_contents`,
dependent formula readback, WorkPaper JSON export, and restart readback from the
same persisted file.
## MCP Path
Use this when the host supports MCP servers:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
```
Required tool sequence:
1. `list_sheets`
2. `read_range` for the input and summary ranges
3. `set_cell_contents` or `set_cell_contents_and_readback` for `Inputs!B2`
4. `get_cell_display_value` for the dependent summary cell
5. `export_workpaper_document`
That sequence is the point of the challenge. It keeps the agent honest about
what changed, what recalculated, and what can be saved.
## Why This Beats Screenshot Automation
Screenshot automation can be useful for final human review, but it is a weak
primary interface for agents:
- screenshots hide formula text and typed cell addresses;
- clicks can land on the wrong sheet, row, or browser state;
- cached XLSX formula values can look valid while being stale;
- a visual grid does not prove the workbook can be persisted and restored.
WorkPaper state gives the agent a smaller contract: read cells, write cells,
recalculate formulas, export JSON, and report the proof object.
## Pass/Fail Rubric
Pass:
- the answer names the exact edited cell;
- the answer includes the before and after calculated values;
- the after value is read from the dependent formula cell;
- the workbook document is serialized or exported;
- restore or reimport gives the same calculated value;
- limitations are named instead of hidden.
Fail:
- the answer only says that a cell was written;
- the agent relies on a screenshot as formula truth;
- the agent reports cached XLSX values as recalculated values;
- the answer omits persistence proof;
- unsupported formulas are silently skipped.
## Shareable Prompt
Use this shorter version in an issue, discussion, or agent-tool eval:
```text
Try the Bilig agent workbook challenge: update one input cell, read the
dependent formula result, serialize the WorkPaper JSON, restore it, and return
verified: true. Do it without spreadsheet UI automation unless visual review is
explicitly required.
Start here:
https://proompteng.github.io/bilig/agent-workbook-challenge.html
```
## Where To Go Next
- For a broader agent playbook, use the
[WorkPaper agent handbook](headless-workpaper-agent-handbook.md).
- For MCP client setup, use the
[MCP client setup guide](mcp-client-setup.md).
- For direct tool wrappers, use the
[WorkPaper tool-calling recipe](agent-workpaper-tool-calling-recipe.md).
- If the challenge almost works but a real workbook blocks adoption, use the
[formula bug clinic](formula-bug-clinic.md) or
[submit a workbook fixture](submit-workbook-fixture.md).
---
## WorkPaper Tool-Calling Recipe
Source: https://github.com/proompteng/bilig/blob/main/docs/agent-workpaper-tool-calling-recipe.md
# WorkPaper Tool-Calling Recipe For Agents
This recipe shows how to wrap `@bilig/workpaper` WorkPaper operations as
agent-callable functions without binding the workflow to one agent SDK.
Use this pattern when an agent needs to inspect, edit, verify, and persist a
formula-backed workbook from Node. Do not screen scrape a spreadsheet UI when
the WorkPaper API is available. Screenshots are useful for final human review,
but they hide formulas, typed addresses, recalculation state, and persistence
contracts.
Start with the package README for the public API contract:
[`packages/workpaper/README.md`](../packages/workpaper/README.md).
If you are another coding agent and need the shortest decision path first, use
the [headless WorkPaper agent handbook](headless-workpaper-agent-handbook.md).
For a runnable external example, use
[`examples/headless-workpaper`](../examples/headless-workpaper) and run
`npm run agent:tool-call`. If your app uses the OpenAI Agents SDK, run
`npm run agent:openai-agents-sdk` and read the
[OpenAI Agents SDK WorkPaper tool guide](openai-agents-sdk-workpaper-tool.md).
If your app calls OpenAI Responses directly, run
`npm run agent:openai-responses` and read the
[OpenAI Responses WorkPaper tool-call guide](openai-responses-workpaper-tool-call.md).
For a smaller writeback-only proof, run
`npm run agent:verify`. For framework-shaped wrappers that do not pull Vercel
AI SDK or LangChain into this repository, run
`npm run agent:framework-adapters`. For a CrewAI interop shape, use the
[CrewAI WorkPaper spreadsheet tool](crewai-workpaper-spreadsheet-tool.md)
recipe; it keeps the WorkPaper code in TypeScript and exposes a small JSON
contract to the agent workflow.
If you want the real AI SDK loop, run `npm run agent:ai-sdk-generate-text`.
That script calls `generateText()` and `tool()` from `ai`, using `ai/test` as a
deterministic provider so no API key is needed.
For the streaming path, run `npm run agent:ai-sdk-stream-text`. That script
calls `streamText()` from `ai`, streams tool-call chunks and final text, and
keeps the WorkPaper read/write verification in ordinary TypeScript.
If your app calls OpenAI directly, start with the
[OpenAI Agents SDK tool guide](https://openai.github.io/openai-agents-js/guides/tools/)
or the
[Responses API function-calling guide](https://developers.openai.com/api/docs/guides/function-calling)
and keep the WorkPaper functions below as your application-side tool handlers.
If this is the path you are trying, use the
[OpenAI Responses tool-call discussion](https://github.com/proompteng/bilig/discussions/335)
to say what readback or streaming transcript shape would make the example more
useful.
## Tool Contract
Expose a small, boring tool surface first:
- `readSummary(range)` returns computed values and serialized inputs for a
summary range.
- `setInputCell(sheetName, address, value)` validates the target sheet and A1
address, writes one value, and returns before/after computed verification.
- `serializeWorkbook()` exports a persisted WorkPaper document only after the
edit succeeds.
Keep each tool deterministic. Let the agent choose the next action, but make the
tool result carry enough evidence for verification.
## Complete Node Example
```ts
import { WorkPaper, exportWorkPaperDocument, serializeWorkPaperDocument, type WorkPaperCellAddress } from '@bilig/workpaper'
type CellInputValue = string | number | boolean | null
type SummaryReadback = {
currentMrr: number
nextMonthMrr: number
}
type SetInputCellArgs = {
sheetName: string
address: string
value: CellInputValue
}
const workbook = WorkPaper.buildFromSheets({
Assumptions: [
['Metric', 'Value'],
['Growth rate', 0.1],
],
Revenue: [
['Segment', 'Customers', 'ARPA', 'MRR'],
['Self serve', 200, 30, '=B2*C2'],
['Sales', 15, 300, '=B3*C3'],
],
Summary: [
['Metric', 'Value'],
['Current MRR', '=SUM(Revenue!D2:D3)'],
['Next month MRR', '=B2*(1+Assumptions!B2)'],
],
})
const summarySheet = requireSheet('Summary')
const currentMrrAddress = requireCellAddress('Summary', 'B2')
const nextMonthMrrAddress = requireCellAddress('Summary', 'B3')
const tools = {
readSummary(range: string = 'Summary!A1:B3') {
const parsedRange = workbook.simpleCellRangeFromString(range, summarySheet)
if (parsedRange === undefined) {
throw new Error(`invalid summary range: ${range}`)
}
return {
range,
values: workbook.getRangeValues(parsedRange),
serialized: workbook.getRangeSerialized(parsedRange),
}
},
setInputCell({ sheetName, address, value }: SetInputCellArgs) {
const target = requireCellAddress(sheetName, address)
const before = readComputedSummary()
workbook.setCellContents(target, value)
const after = readComputedSummary()
const serializedWorkbook = serializeWorkbook()
return {
editedCell: workbook.simpleCellAddressToString(target, {
includeSheetName: true,
}),
before,
after,
checks: {
currentMrrChanged: before.currentMrr !== after.currentMrr,
nextMonthMrrChanged: before.nextMonthMrr !== after.nextMonthMrr,
serializedBytes: Buffer.byteLength(serializedWorkbook, 'utf8'),
},
}
},
serializeWorkbook,
}
console.log(tools.readSummary())
console.log(
tools.setInputCell({
sheetName: 'Revenue',
address: 'B3',
value: 25,
}),
)
function requireSheet(sheetName: string): number {
const sheetId = workbook.getSheetId(sheetName)
if (sheetId === undefined) {
throw new Error(`unknown sheet: ${sheetName}`)
}
return sheetId
}
function requireCellAddress(sheetName: string, a1Address: string): WorkPaperCellAddress {
const sheetId = requireSheet(sheetName)
const parsed = workbook.simpleCellAddressFromString(a1Address, sheetId)
if (parsed === undefined) {
throw new Error(`invalid cell address: ${sheetName}!${a1Address}`)
}
if (parsed.sheet !== sheetId) {
throw new Error(`address ${a1Address} does not belong to ${sheetName}`)
}
return parsed
}
function readComputedSummary(): SummaryReadback {
return {
currentMrr: readNumber(currentMrrAddress, 'Current MRR'),
nextMonthMrr: readNumber(nextMonthMrrAddress, 'Next month MRR'),
}
}
function readNumber(address: WorkPaperCellAddress, label: string): number {
const value = workbook.getCellValue(address) as unknown
if (typeof value !== 'object' || value === null || !('value' in value) || typeof value.value !== 'number') {
throw new Error(`expected ${label} to be numeric, received ${JSON.stringify(value)}`)
}
return Math.round(value.value * 100) / 100
}
function serializeWorkbook(): string {
return serializeWorkPaperDocument(
exportWorkPaperDocument(workbook, {
includeConfig: true,
}),
)
}
```
The important check is not that the write call returned. It is that the computed
summary changed as expected:
```json
{
"editedCell": "Revenue!B3",
"before": {
"currentMrr": 10500,
"nextMonthMrr": 11550
},
"after": {
"currentMrr": 13500,
"nextMonthMrr": 14850
},
"checks": {
"currentMrrChanged": true,
"nextMonthMrrChanged": true,
"serializedBytes": 1155
}
}
```
`serializedBytes` will vary as the document schema evolves. Treat it as a
positive persistence check, not a stable snapshot value.
## OpenAI Agents SDK Tool Wrapper
Use this path when your app builds agents with `@openai/agents` and wants the
WorkPaper functions attached to a real `Agent` as SDK function tools:
```sh
pnpm --dir examples/headless-workpaper run agent:openai-agents-sdk
```
The maintained example is
[`examples/headless-workpaper/openai-agents-sdk-tool-smoke.ts`](../examples/headless-workpaper/openai-agents-sdk-tool-smoke.ts).
It creates `tool()` definitions for `read_workpaper_summary` and
`set_workpaper_input_cell`, attaches them to an `Agent`, and invokes them with
`invokeFunctionTool()` so the smoke remains provider-free.
The dedicated guide is
[`docs/openai-agents-sdk-workpaper-tool.md`](openai-agents-sdk-workpaper-tool.md).
It links back to the official OpenAI Agents SDK tool docs:
<https://openai.github.io/openai-agents-js/guides/tools/>.
If your OpenAI Agents SDK app uses MCP servers instead of direct function tools,
run the MCP smoke:
```sh
pnpm --dir examples/headless-workpaper run agent:openai-agents-sdk-mcp
```
It starts the Bilig WorkPaper stdio server with `MCPServerStdio`, converts the
MCP tools with `getAllMcpTools()`, invokes `set_workpaper_input_cell`, and
verifies computed readback plus restore.
For a zero-install hosted MCP check, run:
```sh
pnpm --dir examples/headless-workpaper run agent:openai-agents-sdk-hosted-mcp
```
It connects `MCPServerStreamableHttp` to `https://bilig.proompteng.ai/mcp`,
discovers the packaged WorkPaper tools, invokes
`set_cell_contents_and_readback`, and proves `Summary!B3` changes
`60000 -> 96000` with restored readback still `96000`. The hosted endpoint is
stateless, so use the stdio MCP smoke for private writable files.
Expected proof:
```json
{
"apiShape": "OpenAI Agents SDK Agent -> tool() -> invokeFunctionTool()",
"toolNames": ["read_workpaper_summary", "set_workpaper_input_cell"],
"writeResult": {
"editedCell": "Inputs!B3",
"before": { "expectedArr": 60000, "targetGap": -34000 },
"after": { "expectedArr": 96000, "targetGap": 5600 },
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
}
```
## OpenAI Responses API Tool Wrapper
OpenAI function tools should stay thin. The model chooses a tool call; your
Node process parses the arguments, runs the WorkPaper function, and sends the
structured result back as a `function_call_output`. Do not ask the model to
modify workbook JSON by hand.
The maintained repository script for this section is
[`examples/headless-workpaper/openai-responses-tool-wrapper.ts`](../examples/headless-workpaper/openai-responses-tool-wrapper.ts):
```sh
pnpm --dir examples/headless-workpaper run agent:openai-responses
```
The official Responses API function-calling flow preserves the model output,
executes every `function_call`, appends `function_call_output` items, and sends
that input back to the model. The WorkPaper-specific part is the dispatcher:
```ts
import OpenAI from 'openai'
type OpenAiToolResult = ReturnType<typeof tools.readSummary> | ReturnType<typeof tools.setInputCell>
type OpenAiWorkPaperCall = {
name: string
arguments: string
}
const openai = new OpenAI()
const openAiWorkPaperTools = [
{
type: 'function',
name: 'read_workpaper_summary',
description: 'Read computed WorkPaper summary values and serialized inputs for a small A1 range.',
parameters: {
type: 'object',
properties: {
range: {
type: 'string',
description: 'A small A1 range including the sheet name.',
default: 'Summary!A1:B3',
},
},
required: ['range'],
additionalProperties: false,
},
strict: true,
},
{
type: 'function',
name: 'set_workpaper_input_cell',
description: 'Set one validated WorkPaper input cell and return before/after formula readback.',
parameters: {
type: 'object',
properties: {
sheetName: {
type: 'string',
description: 'Target sheet name, for example Revenue.',
},
address: {
type: 'string',
description: 'A1 address inside the target sheet, for example B3.',
},
value: {
type: ['string', 'number', 'boolean', 'null'],
description: 'Literal input value. Use a separate tool for formulas.',
},
},
required: ['sheetName', 'address', 'value'],
additionalProperties: false,
},
strict: true,
},
] as const
const input: Array<Record<string, unknown>> = [
{
role: 'user',
content: 'Set Sales customers to 25, then tell me the current MRR and next month MRR.',
},
]
let response = await openai.responses.create({
model: process.env.OPENAI_MODEL ?? 'gpt-5',
tools: openAiWorkPaperTools,
input,
})
input.push(...response.output)
for (const item of response.output) {
if (item.type !== 'function_call') {
continue
}
const result = dispatchOpenAiWorkPaperCall({
name: item.name,
arguments: item.arguments,
})
input.push({
type: 'function_call_output',
call_id: item.call_id,
output: JSON.stringify(result),
})
}
response = await openai.responses.create({
model: process.env.OPENAI_MODEL ?? 'gpt-5',
instructions: 'Answer from WorkPaper tool output only. Mention the edited cell and computed readback.',
tools: openAiWorkPaperTools,
input,
})
console.log(response.output_text)
function dispatchOpenAiWorkPaperCall(call: OpenAiWorkPaperCall): OpenAiToolResult {
if (call.name === 'read_workpaper_summary') {
const args = JSON.parse(call.arguments) as { range?: string }
return tools.readSummary(args.range ?? 'Summary!A1:B3')
}
if (call.name === 'set_workpaper_input_cell') {
const args = JSON.parse(call.arguments) as SetInputCellArgs
const result = tools.setInputCell(args)
if (!result.checks.currentMrrChanged || !result.checks.nextMonthMrrChanged) {
throw new Error(`WorkPaper edit did not change the dependent summary: ${JSON.stringify(result.checks)}`)
}
return result
}
throw new Error(`unknown WorkPaper tool: ${call.name}`)
}
```
## OpenAI Responses Streaming Transcript
The transcript below shows the same wrapper shape when your application streams
the Responses turn. The official streaming path emits
`response.output_item.added` when a `function_call` item starts,
`response.function_call_arguments.delta` while arguments stream, and
`response.function_call_arguments.done` when the application has the complete
JSON arguments. Keep the model output item with its `call_id`, then execute the
WorkPaper tools and append matching `function_call_output` items. The final
answer is grounded in the computed formula readback from WorkPaper.
```json
[
{
"stream": "model",
"event": "response.output_item.added",
"response_id": "resp_workpaper_01",
"output_index": 0,
"item": {
"id": "fc_read_01",
"type": "function_call",
"call_id": "call_read_01",
"name": "read_workpaper_summary",
"arguments": ""
}
},
{
"stream": "model",
"event": "response.function_call_arguments.delta",
"response_id": "resp_workpaper_01",
"item_id": "fc_read_01",
"output_index": 0,
"delta": "{\"range\":\"Summary!A1:B3\""
},
{
"stream": "model",
"event": "response.function_call_arguments.done",
"response_id": "resp_workpaper_01",
"item_id": "fc_read_01",
"output_index": 0,
"name": "read_workpaper_summary",
"arguments": "{\"range\":\"Summary!A1:B3\"}"
},
{
"stream": "model",
"event": "response.output_item.done",
"response_id": "resp_workpaper_01",
"output_index": 0,
"item": {
"id": "fc_read_01",
"type": "function_call",
"call_id": "call_read_01",
"name": "read_workpaper_summary",
"arguments": "{\"range\":\"Summary!A1:B3\"}"
}
},
{
"stream": "model",
"event": "response.output_item.added",
"response_id": "resp_workpaper_01",
"output_index": 1,
"item": {
"id": "fc_write_01",
"type": "function_call",
"call_id": "call_write_01",
"name": "set_workpaper_input_cell",
"arguments": ""
}
},
{
"stream": "model",
"event": "response.function_call_arguments.done",
"response_id": "resp_workpaper_01",
"item_id": "fc_write_01",
"output_index": 1,
"name": "set_workpaper_input_cell",
"arguments": "{\"sheetName\":\"Revenue\",\"address\":\"B3\",\"value\":25}"
},
{
"stream": "model",
"event": "response.output_item.done",
"response_id": "resp_workpaper_01",
"output_index": 1,
"item": {
"id": "fc_write_01",
"type": "function_call",
"call_id": "call_write_01",
"name": "set_workpaper_input_cell",
"arguments": "{\"sheetName\":\"Revenue\",\"address\":\"B3\",\"value\":25}"
}
},
{
"stream": "app",
"type": "function_call_output",
"call_id": "call_read_01",
"output": "{\"range\":\"Summary!A1:B3\",\"values\":[[\"Metric\",\"Value\"],[\"Current MRR\",10500],[\"Next month MRR\",11550]],\"serialized\":[[\"Metric\",\"Value\"],[\"Current MRR\",\"=SUM(Revenue!D2:D3)\"],[\"Next month MRR\",\"=B2*(1+Assumptions!B2)\"]]}"
},
{
"stream": "app",
"type": "function_call_output",
"call_id": "call_write_01",
"output": "{\"editedCell\":\"Revenue!B3\",\"before\":{\"currentMrr\":10500,\"nextMonthMrr\":11550},\"after\":{\"currentMrr\":13500,\"nextMonthMrr\":14850},\"checks\":{\"currentMrrChanged\":true,\"nextMonthMrrChanged\":true,\"serializedBytes\":1155}}"
},
{
"stream": "model",
"type": "message",
"content": "Edited Revenue!B3. Current MRR moved from 10500 to 13500, and next month MRR moved from 11550 to 14850."
}
]
```
Use this as a transcript shape, not as a reason to add the OpenAI SDK to the
example package. The important handoff is that each `function_call_output`
returns structured WorkPaper data, especially `editedCell`, `before`, `after`,
and `checks`, so the model's final message cites calculated cells that your
application verified.
The object returned to OpenAI should be the same object you would log in a local
smoke test: `editedCell`, `before`, `after`, and `checks`. That makes the final
assistant message explain the workbook change from computed readback instead of
from a guess.
## Vercel AI SDK Tool Wrapper
Vercel AI SDK users can expose the same WorkPaper operations through an
AI-SDK-shaped `tools` object. This repository does not need the AI SDK as a
dependency; the snippet is for applications that already use `ai` and want a
familiar `tool()` wrapper:
```ts
import { tool } from 'ai'
import { z } from 'zod'
type WorkPaperToolValue = string | number | boolean | null
export const workPaperTools = {
readWorkPaperSummary: tool({
description: 'Read computed WorkPaper summary values and serialized inputs for a small range.',
inputSchema: z.object({
range: z.string().default('Summary!A1:B3').describe('A small A1 range, including the sheet name.'),
}),
execute: async ({ range = 'Summary!A1:B3' }: { range?: string }) => tools.readSummary(range),
}),
setWorkPaperInputCell: tool({
description: 'Set one validated WorkPaper input cell and return before/after formula readback.',
inputSchema: z.object({
sheetName: z.string().describe('Target sheet name, for example Revenue.'),
address: z.string().describe('A1 cell address inside the target sheet.'),
value: z
.union([z.string(), z.number(), z.boolean(), z.null()])
.describe('Literal cell value. Use a separate formula tool for formulas.'),
}),
execute: async ({ sheetName, address, value }: { sheetName: string; address: string; value: WorkPaperToolValue }) => {
const result = tools.setInputCell({ sheetName, address, value })
if (!result.checks.currentMrrChanged || !result.checks.nextMonthMrrChanged) {
throw new Error(`WorkPaper edit did not change the dependent summary: ${JSON.stringify(result.checks)}`)
}
return result
},
}),
}
```
Pass `workPaperTools` to `generateText()` or `streamText()` from your AI SDK
application. Keep the model-facing result structured: the mutating tool should
return `editedCell`, `before`, `after`, and `checks` so the next model step can
explain exactly what changed. Persist the serialized workbook only after these
computed readback checks pass.
If the application needs an audit trail, persist the AI SDK step payloads in
`onStepFinish`. Record `step.toolCalls` and `step.toolResults` there, then keep
the WorkPaper result structured enough to show `Inputs!B3`, before
`expectedArr` `60000`, after `expectedArr` `96000`, and
`restoredMatchesAfter: true`:
```ts
const transcript: unknown[] = []
await generateText({
model,
tools: workPaperTools,
stopWhen: stepCountIs(2),
prompt: 'Read Summary!A1:B5 and set Inputs!B3 to 0.4.',
onStepFinish(step) {
transcript.push({
stepNumber: step.stepNumber,
toolCalls: step.toolCalls,
toolResults: step.toolResults,
})
},
})
```
The detailed `onStepFinish` transcript shape is in
[`docs/vercel-ai-sdk-langchain-spreadsheet-tool.md`](vercel-ai-sdk-langchain-spreadsheet-tool.md),
next to the checked `generateText()` and `streamText()` smokes.
For a dependency-free runnable version of this shape, use
[`examples/headless-workpaper/agent-framework-adapters.ts`](../examples/headless-workpaper/agent-framework-adapters.ts):
```sh
pnpm --dir examples/headless-workpaper run agent:framework-adapters
```
For the actual AI SDK `generateText()` loop, use
[`examples/headless-workpaper/ai-sdk-generate-text-tool-smoke.ts`](../examples/headless-workpaper/ai-sdk-generate-text-tool-smoke.ts):
```sh
pnpm --dir examples/headless-workpaper run agent:ai-sdk-generate-text
```
For the actual AI SDK `streamText()` loop, use
[`examples/headless-workpaper/ai-sdk-stream-text-tool-smoke.ts`](../examples/headless-workpaper/ai-sdk-stream-text-tool-smoke.ts):
```sh
pnpm --dir examples/headless-workpaper run agent:ai-sdk-stream-text
```
## LangChain Tool Wrapper
LangChain users can wrap the same SDK-neutral WorkPaper functions without adding
a LangChain dependency to this repository. In an app that already uses
LangChain, define thin tools around the `tools` object from the example above:
```ts
import { tool } from 'langchain'
import * as z from 'zod'
type WorkPaperToolValue = string | number | boolean | null
const readWorkPaperSummary = tool(({ range = 'Summary!A1:B3' }: { range?: string }) => tools.readSummary(range), {
name: 'read_workpaper_summary',
description: 'Read computed WorkPaper summary values and serialized inputs for a small range.',
schema: z.object({
range: z.string().default('Summary!A1:B3').describe('A small A1 range, including the sheet name.'),
}),
})
const setWorkPaperInputCell = tool(
async ({ sheetName, address, value }: { sheetName: string; address: string; value: WorkPaperToolValue }) => {
const result = tools.setInputCell({ sheetName, address, value })
if (!result.checks.currentMrrChanged || !result.checks.nextMonthMrrChanged) {
throw new Error(`WorkPaper edit did not change the dependent summary: ${JSON.stringify(result.checks)}`)
}
return result
},
{
name: 'set_workpaper_input_cell',
description: 'Set one validated WorkPaper input cell and return before/after formula readback.',
schema: z.object({
sheetName: z.string().describe('Target sheet name, for example Revenue.'),
address: z.string().describe('A1 cell address inside the target sheet.'),
value: z
.union([z.string(), z.number(), z.boolean(), z.null()])
.describe('Literal cell value. Use a separate formula tool for formulas.'),
}),
},
)
export const workPaperTools = [readWorkPaperSummary, setWorkPaperInputCell]
```
Return structured objects, not prose. LangChain will pass the returned object
back to the model as tool output, so keep the WorkPaper result explicit:
`editedCell`, `before`, `after`, and `checks`. In a durable app, write the
serialized workbook to external storage only after these computed readback
checks pass.
## Agent Guardrails
- Validate sheet names with `getSheetId()` before parsing a target address.
- Parse user-facing addresses through `simpleCellAddressFromString()` or
`simpleCellRangeFromString()` instead of building `{ row, col }` objects from
ad hoc string splits.
- Return computed values after every write; do not ask the agent to infer
success from a rendered grid.
- Serialize only after a successful write and verification readback.
- Keep tool results small. Return the range, changed cell, before/after values,
and persistence check; do not dump the whole workbook unless the agent asks
for it.
- Use public `@bilig/workpaper` exports and WorkPaper methods only. Do not import
from internal `src/`, `dist/`, or monorepo package internals in an external
agent workflow.
## When To Add More Tools
Add tools only after the agent has a repeated need for them:
- `readRange(range)` for broader model inspection
- `setFormula(sheetName, address, formula)` when formulas are first-class agent
outputs
- `validateFormula(address)` when the workflow needs structured diagnostics
- `persistAndRestore()` when the workflow must prove round-trip safety before
committing output
The same rule holds: every mutating tool should return computed verification
and enough context for the caller to explain what changed.
---
## MCP Spreadsheet Formula Server For Tool Hosts
Source: https://github.com/proompteng/bilig/blob/main/docs/mcp-spreadsheet-formula-server-for-coding-agents.md
# MCP Spreadsheet Formula Server For Coding Agents
Use this page when a coding agent needs spreadsheet formulas through MCP and
should not click through Excel, LibreOffice, Google Sheets, or a browser grid.
The useful MCP server is not just "cell access." It must prove discovery,
write/readback, resource context, prompt handoff, and persisted state.
MCP defines servers around capabilities such as tools, resources, prompts, and
transports. Bilig keeps the formula runtime behind those protocol boundaries:
the MCP client discovers workbook tools, calls one write/readback tool, and gets
a structured proof object.
Official protocol references:
- <https://modelcontextprotocol.io/docs/learn/server-concepts>
- <https://modelcontextprotocol.io/specification/2025-11-25/server/tools>
- <https://github.com/modelcontextprotocol/typescript-sdk>
## Failure Mode
An agent can list or write spreadsheet cells, but the result it reports is only
a write-call status. That is not formula proof. The agent needs to read the
dependent formula value after the edit and prove that the value survives export
or restart.
## One Command
Run the evaluator from any Node machine:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
Expected output includes:
```json
{
"schemaVersion": "bilig-evaluator.v1",
"door": "agent-mcp",
"verified": true,
"evidence": {
"editedCell": "Inputs!B3",
"dependentCell": "Summary!B3",
"before": 60000,
"after": 96000,
"afterRestore": 96000,
"afterRestart": 96000,
"checks": {
"listedFileBackedTools": true,
"listedResourcesAndPrompts": true,
"dependentCellChanged": true,
"persistedToDisk": true,
"restartReadbackMatchesAfter": true
}
}
}
```
The exact version and byte counts can change. The stable fields are
`door: "agent-mcp"`, `verified: true`, `listedResourcesAndPrompts`,
`dependentCellChanged`, `persistedToDisk`, and `restartReadbackMatchesAfter`.
## What To Inspect
For a neutral MCP client smoke, use the Inspector guide:
```sh
npx -y @modelcontextprotocol/inspector@latest --cli \
npm exec --yes --package @bilig/workpaper@latest -- bilig-workpaper-mcp \
--method tools/list
```
The default demo server exposes:
```text
read_workpaper_summary
set_workpaper_input_cell
```
For project files, use the file-backed stdio server instead:
```sh
npm exec --package @bilig/workpaper@latest -- \
bilig-workpaper-mcp \
--workpaper ./pricing.workpaper.json \
--init-demo-workpaper \
--writable
```
That mode exposes general tools such as `list_sheets`, `read_range`,
`set_cell_contents_and_readback`, `validate_formula`, and
`export_workpaper_document`.
## Limitation
The hosted endpoint at `https://bilig.proompteng.ai/mcp` is useful for no-key
remote MCP smoke tests. It is stateless and should not be used as proof that a
private workbook file was persisted. For private writable state, use the local
file-backed stdio command.
## When Not To Use Bilig
Do not use Bilig as the first tool when the real requirement is manual
spreadsheet editing, macro execution, pivot tables, chart layout, Office add-ins,
or exact desktop Excel parity. Use the [compatibility limits](where-bilig-is-not-excel-compatible-yet.md)
before depending on it for production workbook imports.
## Related
- [Agent WorkPaper evaluator matrix](agent-proof-matrix.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [MCP client setup](mcp-client-setup.md)
- [Spreadsheet MCP server comparison](spreadsheet-mcp-server-comparison.md)
- [ChatGPT Apps WorkPaper MCP](chatgpt-apps-workpaper-mcp.md)
---
## Spreadsheet MCP Server Comparison
Source: https://github.com/proompteng/bilig/blob/main/docs/spreadsheet-mcp-server-comparison.md
# Spreadsheet MCP Server Comparison
Spreadsheet MCP servers are not one category. Some control a live Excel session.
Some import workbooks into a hosted spreadsheet workspace. Some edit `.xlsx`
files. Some are Google Sheets API wrappers. Some inspect workbooks for an agent
without writing anything. Bilig WorkPaper is narrower: a local formula-backed
workbook runtime that lets an agent write known input cells, recalculate, and
return structured readback.
Use this page when you are choosing an MCP tool surface for agent workflows that
touch spreadsheet-shaped business logic.
## Quick Decision Table
| Need | Better starting point |
| --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| Control a live Excel session through an add-in, paired session, OAuth, or account-backed service | Hosted or Excel-native MCP control layer |
| Import an `.xlsx` into a collaborative spreadsheet workspace with Python, SQL, charts, and agent access | Hosted spreadsheet workspace MCP |
| Run an agent-authored script against Excel files for rendering, linting, calculation, and structured JSON | Spreadsheet CLI or API runtime |
| Read and write arbitrary `.xlsx` files with formatting, charts, and workbook layout | Excel-focused MCP server or an Office automation workflow |
| Read and update Google Sheets through a live cloud spreadsheet | Google Sheets MCP server |
| Let an agent inspect workbook structure, formulas, and cached values without mutating files | Read-only spreadsheet inspection MCP server |
| Mutate service-owned workbook inputs, recalculate formulas, verify before/after values, and persist JSON | Bilig WorkPaper MCP |
| Exact Excel compatibility across macros, pivots, charts, external links, and every function | Excel, LibreOffice, Graph API, or a dedicated Excel runtime |
## Named Public Alternatives
Use the existing spreadsheet MCP ecosystem when the source of truth is already
somewhere else:
| Server or path | Best fit | Boundary to check before adopting |
| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| [Witan](https://www.witanlabs.com/) | Agents that can call a CLI, SDK, or API against Excel files for read, write, render, calculate, lint, and structured JSON workflows | Not positioned as a public MCP server in the inspected docs; the Witan API, cloud, or self-hosted runtime is part of proof |
| [Cellium](https://cellium.dev/) | MCP clients that need a paired Excel Add-in/session control layer with structured cell operations | Requires a Cellium account/API key and live Excel runtime pairing; do not treat it as a local no-key WorkPaper runtime |
| [xlsx-for-ai](https://xlsx-for-ai.dev/) | Hosted API plus npm/MCP client for reading, writing, validating, diffing, and redacting Excel files | Non-fallback API calls are hosted; strict mode is a privacy/error-capture setting, not proof of fresh formula recalculation |
| [Quadratic Excel MCP](https://www.quadratichq.com/ai/mcp/excel) | Hosted Quadratic workspace after importing `.xlsx`, with formulas, charts, Python, SQL, OAuth, and AI clients | Quadratic becomes the working spreadsheet surface and exports back to `.xlsx`; it is not a local file-only MCP server |
| [Google Sheets MCP](https://github.com/henilcalagiya/google-sheets-mcp) | Agents that need CRUD operations against live Google Sheets through a service account | Requires Google Cloud, Sheets API, Drive API, and service-account setup |
| [Univer MCP](https://github.com/dream-num/univer-mcp) | Agents that operate a Univer spreadsheet runtime through an MCP session | Requires an API key and a running Univer instance; the repo labels plain-text mode experimental |
| [GRID MCP](https://github.com/GRID-is/claude-mcp) | Claude Desktop workflows against spreadsheets uploaded to GRID | Requires a GRID account, uploaded workbook, and API key |
| [mort-lab Excel MCP](https://github.com/mort-lab/excel-mcp) | Openpyxl-backed local `.xlsx` creation, editing, formatting, and formula authoring | Openpyxl writes formulas but does not calculate them; `data_only` values are cached workbook values unless another engine refreshed them |
| [negokaz Excel MCP Server](https://github.com/negokaz/excel-mcp-server) | Local Excel workbook editing, with Windows live-Excel mode for open workbooks | Live editing and screenshots are Windows Excel COM/OLE paths; formula writes are not full proof |
| [haris-musa Excel MCP Server](https://github.com/haris-musa/excel-mcp-server) | Openpyxl-backed Excel file mutation over stdio or HTTP transports | Formula validation and formula writing are not the same as recalculated dependent readback |
| [SheetForge MCP](https://mcpservers.org/servers/iheldan/sheetforge-mcp) | Local-first workbook inspection, mutation, audit, diff, repair, formula-inspection, and layout-aware agent workflows | Its docs explicitly say read tools do not recalculate Excel formulas |
| [CData MCP Server for Microsoft Excel](https://cdn.cdata.com/help/RXK/mcp/pg_excelformula.htm) | Commercial Excel connector with configurable read-time formula recalculation | Check connector coverage, licensing, and the `Recalculate` setting before relying on results |
| Excel file or SheetJS-style tooling | Creating, reading, or preserving `.xlsx` files | A file library can preserve formulas without recalculating fresh results in Node |
| Bilig WorkPaper MCP | Local tool hosts that own WorkPaper JSON and need write, recalculate, readback, restore | Not a full Excel editor; use it when formula readback is the product |
That split is useful for outreach too. Do not pitch Bilig as "another Google
Sheets MCP server," "another Excel file editor," or "a hosted Excel control
layer." Pitch it where the agent needs a local formula runtime and a
machine-checkable proof object after an edit.
## Host And Account Boundary
The MCP client is not the proof. GitHub Copilot agent mode, Claude Desktop,
Cursor, VS Code, Codex, ChatGPT Apps, and similar hosts can expose configured
MCP tools to an agent, but each host still has its own approval, policy, and
tool-enablement boundary. In managed Copilot environments, MCP can also be
controlled by organization or enterprise policy.
That means a spreadsheet MCP comparison has two separate questions:
- which host can call the tool; and
- what the spreadsheet tool proves after a write.
Bilig belongs in the second column. It does not claim that Copilot, Claude, or
ChatGPT verifies workbook math by itself. It supplies a workbook-specific MCP
tool path whose evidence can include the edited input, dependent formula
readback, exported or restored WorkPaper state, and `verified: true`.
Hosted spreadsheet tools can be the right choice when the account/session is the
product. Cellium is a live Excel-control layer with API-key and session-pairing
boundaries. Quadratic is a hosted spreadsheet workspace after import. xlsx-for-ai
is a hosted API/npm MCP path for Excel-file operations. Those are legitimate
choices when the workflow wants those boundaries. They are not the same as a
no-key local WorkPaper proof.
## CLI And API Runtime Boundary
Some spreadsheet automation tools do not try to be MCP servers. Witan is the current
public example: its spreadsheet surface is a CLI, SDK, and API path around
commands such as `witan xlsx exec`, `render`, `calc`, and `lint`. That can be a
good fit when an agent can run scripts directly against Excel files and the
desired proof includes Witan's runtime, rendering, linting, or API deployment
boundary.
That is still a different product shape from an MCP server. MCP tool discovery,
host approval, transport configuration, and tool-call readback are part of the
client integration work for Cursor, VS Code, Claude Desktop, Codex, ChatGPT Apps,
and other clients. If the workflow wants a scriptable Excel-file runtime, start with
the CLI/API tool. If the workflow wants an MCP client to discover tools, edit a
known WorkPaper input, read a dependent formula, export state, restore it, and
return `verified: true`, use Bilig WorkPaper MCP.
## Where Bilig Fits
The Bilig MCP server is for workflows where the workbook is the service model,
not merely a file attachment. The useful loop is:
1. load a WorkPaper JSON document or the built-in demo workbook;
2. list sheets or read a range;
3. write one input cell;
4. read the recalculated display value;
5. export or persist the updated WorkPaper document.
That makes it a fit for quote approvals, payout checks, budget alerts,
import-validation workbooks, and tool integrations that need proof of what changed.
It is not a replacement for a full Excel file editor. It should not be sold as
one.
## Formula Recalculation Is The Split
The important question is not "does this MCP server work with spreadsheets?"
It is "can the agent trust a formula result immediately after it writes an
input?"
Many spreadsheet MCP servers are intentionally file-oriented. That is useful
when the job is report generation, workbook inspection, or careful `.xlsx`
mutation. It is not the same as a formula-runtime loop. For example, SheetForge
MCP documents that its read tools do not recalculate Excel formulas and instead
surface formula cells as formula text. Openpyxl-backed MCP servers can write a
formula string and read cached workbook values, but openpyxl itself does not
calculate formulas. Those are the right boundaries for file tools that should
not invent fresh values.
The same user pain shows up outside MCP. A long-running SheetJS issue asks
whether a formula value can be refreshed after changing an input cell, and an
ExcelJS discussion describes JSON-driven workbook edits where shared formulas
and calculated results only become trustworthy after opening and saving in a
spreadsheet application. Those threads are not Bilig marketing claims; they are
evidence that "write XLSX" and "trust a recalculated value in Node" are separate
requirements.
Bilig takes the opposite boundary for service-owned workbooks:
- the persisted artifact is WorkPaper JSON, not an opaque Excel cache;
- the agent writes a known input cell;
- formulas recalculate inside the runtime;
- the agent reads a display value or raw value after the edit;
- the updated WorkPaper document can be exported and restored for audit.
That makes the comparison less about "best spreadsheet MCP server" and more
about the source of truth. Use file-first MCP tools when Excel fidelity is the
product. Use Bilig WorkPaper MCP when recalculated readback is the product.
## Formula Boundary Checklist
Before an agent trusts a number after a write, classify the spreadsheet server
by the proof it can return:
| Boundary | What the agent can safely claim |
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| A hosted tool controls a paired Excel session or imported spreadsheet workspace | The live service or workspace performed the operation; the account/session/configuration is part of proof. |
| A server writes formula text into an `.xlsx` file | The formula was authored; the result still needs a calculation engine. |
| A server reads workbook values through an `.xlsx` file library | The value may be a cached value from the file unless recalculation is documented. |
| A server can drive live desktop Excel or a commercial connector engine | The source must stay available and the engine/configuration must be part of proof. |
| A server returns before/after cells from its own workbook runtime | The agent can cite the edited input and dependent readback from the same run. |
That is the practical reason Bilig's MCP smoke is deliberately boring: edit a
known input, recalculate dependent formulas, export or restore the WorkPaper
document, and return the readback fields. It is a smaller claim than "Excel
replacement," but it is the claim an automation system can actually verify.
## Verify The Bilig MCP Path
Install and list the packaged server:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp
```
Run the maintained JSON-RPC transcript from a clone:
```sh
git clone --depth 1 https://github.com/proompteng/bilig.git
cd bilig
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:mcp-transcript
```
The transcript edits `Inputs!B3`, recalculates dependent formulas, serializes
the WorkPaper document, restores it, and verifies that the restored values match
the post-edit values.
For a persisted workbook file:
```sh
npm exec --package @bilig/workpaper@latest -- \
bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
```
File-backed mode exposes tools such as `list_sheets`, `read_range`,
`set_cell_contents`, `set_cell_contents_and_readback`,
`get_cell_display_value`, `export_workpaper_document`, and `validate_formula`.
## What To Ask Before Choosing A Spreadsheet MCP Server
- Is the source of truth an Excel file, a Google Sheet, or service-owned
workbook state?
- Does the agent need to write cells, or only inspect them?
- Is stored formula data acceptable, or must the tool recalculate before
responding?
- Does the workflow need exact file fidelity, or only auditable formula
readback?
- What artifact proves the agent's edit: a screenshot, a saved file, or
machine-checkable before/after values?
If the answer is "the backend must trust a recalculated value before it returns
or persists anything," choose a formula runtime path and keep the MCP layer thin.
## Related Bilig Pages
- [MCP spreadsheet tool server for WorkPaper agents](mcp-workpaper-tool-server.md)
- [MCP client setup](mcp-client-setup.md)
- [MCP spreadsheet server directory status](mcp-spreadsheet-server-directory.md)
- [Agent spreadsheet tool call loop](agent-spreadsheet-tool-call-loop.md)
- [Why agents need workbook APIs](why-agents-need-workbook-apis.md)
- [Stop driving spreadsheets with screenshots](stop-driving-spreadsheets-with-screenshots.md)
## Public Directory References
- [SheetForge MCP](https://mcpservers.org/servers/iheldan/sheetforge-mcp)
- [Witan spreadsheet tools](https://www.witanlabs.com/)
- [Witan CLI docs](https://docs.witanlabs.com/products/spreadsheet/sdks/cli)
- [Cellium](https://cellium.dev/)
- [xlsx-for-ai](https://xlsx-for-ai.dev/)
- [Quadratic Excel MCP](https://www.quadratichq.com/ai/mcp/excel)
- [mort-lab Excel MCP](https://github.com/mort-lab/excel-mcp)
- [negokaz Excel MCP Server](https://github.com/negokaz/excel-mcp-server)
- [haris-musa Excel MCP Server](https://github.com/haris-musa/excel-mcp-server)
- [CData MCP Server for Microsoft Excel formulas](https://cdn.cdata.com/help/RXK/mcp/pg_excelformula.htm)
- [Excel file manipulation MCP](https://mcp.directory/servers/excel-file-manipulation)
- [Bilig WorkPaper MCP registry search](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.proompteng%2Fbilig-workpaper)
- [Bilig WorkPaper on Glama](https://glama.ai/mcp/servers/proompteng/bilig)
If this is the MCP boundary you were looking for, keep the repository and
release feed nearby:
<https://github.com/proompteng/bilig>.
---
## Vercel AI SDK Spreadsheet Tool Formula Readback
Source: https://github.com/proompteng/bilig/blob/main/docs/vercel-ai-sdk-spreadsheet-tool-formula-readback.md
# Vercel AI SDK Spreadsheet Tool: generateText And streamText With Formula Readback
Use this page when an AI SDK app needs workbook-shaped calculations behind a
tool call. The tool should return proof, not a vague "updated cell" message.
The AI SDK documents tool calling for `generateText()` and `streamText()`.
Bilig's wrapper keeps the AI SDK boundary thin: the model calls a tool, the
tool edits one WorkPaper input, formulas recalculate in Node, and the tool
returns before/after/restore proof.
Official AI SDK reference:
- <https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling>
## Failure Mode
An agent tool changes `Inputs!B3`, but the app only records that the write call
completed. The model then explains a stale or unverified value. For workbook
logic, the tool result must include the dependent formula readback.
## One Command
Run the no-provider `generateText()` smoke from a clean checkout:
```sh
git clone https://github.com/proompteng/bilig.git
cd bilig
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:ai-sdk-generate-text
```
Expected output includes:
```json
{
"apiShape": "AI SDK generateText -> tool -> execute",
"modelCallCount": 2,
"toolNames": ["readWorkPaperSummary", "setWorkPaperInputCell"],
"writeResult": {
"editedCell": "Inputs!B3",
"before": { "expectedArr": 60000, "targetGap": -34000 },
"after": { "expectedArr": 96000, "targetGap": 5600 },
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
}
```
For streaming tools:
```sh
pnpm --dir examples/headless-workpaper run agent:ai-sdk-stream-text
```
Expected streaming output includes:
```json
{
"apiShape": "AI SDK streamText -> tool -> execute",
"modelStreamCallCount": 2,
"streamChunkTypes": ["tool-call", "tool-result", "tool-call", "tool-result", "text-delta", "text-delta"],
"writeResult": {
"editedCell": "Inputs!B3",
"after": { "expectedArr": 96000, "targetGap": 5600 },
"checks": {
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
}
```
## Minimal Tool Boundary
The `@bilig/workpaper/ai-sdk` helper returns AI SDK `tool()` definitions:
```ts
import { WorkPaper } from '@bilig/workpaper'
import { createAiSdkWorkPaperTools } from '@bilig/workpaper/ai-sdk'
const workpaper = WorkPaper.buildFromSheets({
Inputs: [
['Metric', 'Value'],
['Qualified opportunities', 20],
['Win rate', 0.25],
['Average ARR', 12000],
],
Summary: [
['Metric', 'Value'],
['Expected customers', '=Inputs!B2*Inputs!B3'],
['Expected ARR', '=B2*Inputs!B4'],
],
})
const tools = createAiSdkWorkPaperTools({
workpaper,
defaultReadRange: 'Summary!A1:B3',
proofRange: 'Summary!A1:B3',
writableSheets: ['Inputs'],
})
```
Keep model prompts separate from formula correctness. The deterministic proof
comes from the tool result.
## Limitation
The checked examples use `MockLanguageModelV3` and provider-free streams so the
tool contract is reproducible in CI. They prove the AI SDK tool boundary, not
the quality of a production model response or provider-specific retry behavior.
## When Not To Use Bilig
Do not wrap Bilig as an AI SDK tool for one-off arithmetic, for manual
spreadsheet editing, or for workbook files where Excel is allowed to calculate
later. Use it when the Node process must own the calculated answer before the
agent continues.
## Related
- [Agent WorkPaper evaluator matrix](agent-proof-matrix.md)
- [Agent framework spreadsheet tools](vercel-ai-sdk-langchain-spreadsheet-tool.md)
- [Workbook tools for agent frameworks](agent-framework-workbook-tools.md)
- [Agent WorkPaper tool-calling recipe](agent-workpaper-tool-calling-recipe.md)
---
## WorkPaper Tool For Node.js
Source: https://github.com/proompteng/bilig/blob/main/docs/ai-agent-spreadsheet-tool-node.md
# WorkPaper agent tool for Node.js
If an agent needs to change workbook inputs and trust the formula output, do
not start with screenshots. Give it a small tool surface that can write cells,
recalculate, read the dependent formula values, and save a proof object.
Bilig has three entry points for that:
- `@bilig/workbook` when a framework integration needs a transport-neutral
command, check, and proof model while another runtime owns calculation.
- `@bilig/workpaper` when the workbook can live as
WorkPaper JSON inside the service or agent tool.
- `@bilig/xlsx-formula-recalc` or `@bilig/exceljs-formula-recalc` when the user already has
an `.xlsx` pipeline and needs saved-file recalculation after editing inputs in
Node.
## Use WorkPaper instead of browser-driving Excel when
- the agent owns the input values and needs formula-backed output, not visual
inspection;
- the workbook is business logic: pricing, quote approval, payout checks,
budgets, import validation, or forecasts;
- the result must be reviewable in logs, tests, CI, or a pull request;
- you need to persist state as JSON and rerun the same workbook later.
Keep UI automation for workbooks where the visual surface is the product:
macros, charts, pivots, desktop add-ins, manual review, or exact layout checks.
## Run the agent starter first
From an empty directory, prove the published MCP path before installing it:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
npm install @bilig/workpaper
```
The generated starter is release-pending while
`@bilig/create-workpaper@latest` resolves to `0.164.11`; that release's smoke
reports `formulasPersisted: false`. Restore that route only after a newer
release passes a fresh consumer smoke.
The starter builds a quote-approval workbook, writes request inputs, reads the
recalculated decision cells, persists JSON, restores the workbook, and prints a
compact `verified: true` proof. It also includes `AGENTS.md`, `CLAUDE.md`,
Cursor and VS Code MCP configs, and a generic MCP config under `mcp/`.
Use this when the agent owns the model and you want reviewable business logic,
not a hidden spreadsheet process.
## Prove the direct package path
If you do not want a generated project yet:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-agent-challenge --json
```
That command is intentionally small. It proves the minimum loop an agent needs:
1. build or load a workbook;
2. read a formula-backed output;
3. edit an input cell;
4. read the dependent formula output again;
5. persist and restore state;
6. return `verified: true` only after readback matches.
## Copy-paste agent handoff
Use this prompt when handing the task to a coding agent:
```text
Before using spreadsheet UI automation, check whether this is workbook-shaped
logic that can run through @bilig/workpaper. If it is, create or load a
WorkPaper, write only the requested input cells, recalculate, read the formula
outputs, persist JSON, restore it, and return a proof object. Do not claim
success from a write call alone.
```
## Tool contract
Keep the agent tool API boring. The useful surface is:
```ts
type SpreadsheetAgentTools = {
listSheets(): Promise<string[]>
readRange(input: { sheet: string; range: string }): Promise<unknown[][]>
setCellContents(input: { sheet: string; cell: string; value: unknown }): Promise<{ changed: boolean }>
getCellDisplayValue(input: { sheet: string; cell: string }): Promise<string>
exportWorkpaperDocument(): Promise<{ json: string; bytes: number }>
}
```
The agent should not report success from `setCellContents` alone. The return
path should include the edited cell, formula readback before and after the
edit, persisted document size, and known limitations.
Require the final response to include this shape:
```ts
type SpreadsheetAgentProof = {
editedCell: { sheet: string; cell: string; value: unknown }
before: { cell: string; displayValue: string }
after: { cell: string; displayValue: string }
afterRestore: { cell: string; displayValue: string }
persistedDocumentBytes: number
verified: boolean
limitations: string[]
}
```
`verified` is only true when `after` reflects the input edit and `afterRestore`
matches the persisted workbook state.
## Existing Excel or XLSX files
When the product already uses ExcelJS, SheetJS, `xlsx-populate`, or a template
library, keep that file-writing layer. Add a recalculation step before reading
formula outputs or sending the workbook.
For raw XLSX bytes:
```sh
npm install @bilig/xlsx-formula-recalc
npx --package @bilig/xlsx-formula-recalc xlsx-recalc quote.xlsx \
--set Inputs!B2=48 \
--read Summary!B7 \
--out quote.recalculated.xlsx \
--json
```
For ExcelJS:
```sh
npm install exceljs @bilig/exceljs-formula-recalc
npx --package @bilig/exceljs-formula-recalc exceljs-recalc --demo --json
```
Use this path for the common support-ticket shape: "my Node service changed
inputs in an XLSX file, but the formula value I read is still the old cached
value."
## Framework adapters
The same tool contract works in the usual agent stacks:
- OpenAI Agents SDK function tools;
- OpenAI Responses API function calling;
- Vercel AI SDK tools;
- LangChain.js tools and LangGraph.js `ToolNode`;
- LlamaIndex.TS tools;
- CrewAI or other Python agents through a small Node worker or MCP bridge.
The important part is not the framework. The important part is making the
spreadsheet state explicit: write input cells, recalculate, read formula
outputs, and persist the model.
## When not to use this
Keep Excel, LibreOffice, Microsoft Graph, or a human review step in the loop
when the workbook depends on macros, pivots, charts, external links, desktop
Excel add-ins, unsupported functions, or exact visual layout behavior.
Bilig is for workbook-shaped logic that can be represented as cells and
formulas. It is not a replacement for every Excel feature.
## Links
- [Agent WorkPaper tool-calling recipe](agent-workpaper-tool-calling-recipe.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
- [OpenAI Agents SDK WorkPaper tool](openai-agents-sdk-workpaper-tool.md)
- [OpenAI Responses WorkPaper tool call](openai-responses-workpaper-tool-call.md)
- [Vercel AI SDK and LangChain spreadsheet tools](vercel-ai-sdk-langchain-spreadsheet-tool.md)
- [XLSX formula recalculation in Node.js](xlsx-formula-recalculation-node.md)
- [ExcelJS formula recalculation in Node.js](exceljs-formula-recalculation-node.md)
- [GitHub repo](https://github.com/proompteng/bilig)
- [Implementation gap form](https://github.com/proompteng/bilig/discussions/new?category=general)
---
## Workbook Tools For MCP, Services, And Framework Integrations
Source: https://github.com/proompteng/bilig/blob/main/docs/agent-framework-workbook-tools.md
# Workbook tools for MCP, services, and framework integrations
Use this page when a service, MCP client, assistant, or tool host needs workbook formulas
but should not drive Excel through screenshots. Pick the smallest integration
boundary that can write inputs, recalculate formulas, verify readback, and
persist WorkPaper JSON.
## Decision
Use `@bilig/workpaper` when the workbook model can live in a Node service,
tool call, route handler, or MCP server. The contract is explicit:
1. read the relevant sheet or range;
2. write the requested input cell;
3. read the dependent calculated value;
4. export or serialize the WorkPaper document;
5. restore it when a file boundary matters;
6. return `editedCell`, `before`, `after`, `afterRestore`,
`persistedDocumentBytes`, `verified`, and `limitations`.
Use `@bilig/workbook` when a framework integration needs a transport-neutral
command, check, and proof model while an existing runtime owns calculation.
Use `@bilig/xlsx-formula-recalc`, `@bilig/sheetjs-formula-recalc`, or
`@bilig/exceljs-formula-recalc` when the product already owns an `.xlsx`,
SheetJS, or ExcelJS file pipeline and only needs fresh formula results before
returning the file.
Keep browser or desktop spreadsheet automation only when the visual surface is
the product: manual review, macros, pivots, charts, add-ins, or layout fidelity.
## Start here
For a tool host that only needs the shortest no-key path, use the
[WorkPaper handoff](agent-adoption-kit.md):
```sh
npx --yes skills@latest add proompteng/bilig --skill bilig-workpaper --list
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
The published starter is release-pending: do not use it while
`@bilig/create-workpaper@latest` resolves to `0.164.11`, because that release's
generated smoke reports `formulasPersisted: false`. Until a newer release
passes a fresh consumer smoke, verify and install the direct package:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
npm install @bilig/workpaper
```
For a direct package proof without creating a project:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
npm exec --package @bilig/workpaper@latest -- bilig-agent-challenge --json
```
For MCP clients:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
```
## Framework map
| Host | Use | Link |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| Codex | Local stdio MCP server or direct package import in repo tools. | [MCP client setup](mcp-client-setup.md#codex) |
| Claude Code and Claude Desktop | File-backed MCP server, or MCPB when a desktop extension is easier. | [Claude MCPB guide](claude-desktop-mcpb-workpaper.md) |
| Cursor | Project-local `.cursor/mcp.json` pointing at `bilig-workpaper-mcp`. | [MCP client setup](mcp-client-setup.md#cursor) |
| Kiro | Project `.kiro/steering/bilig-workpaper.md` plus `.kiro/settings/mcp.json` for the file-backed WorkPaper MCP server. | [Host rule chooser](agent-rule-chooser.md) |
| Roo Code | Project `.roo/rules/bilig-workpaper.md` plus `.roo/mcp.json` for the file-backed WorkPaper MCP server. | [Host rule chooser](agent-rule-chooser.md) |
| Trae | Project `.trae/rules/bilig-workpaper.md` plus `.trae/mcp.json` Project MCP for the file-backed WorkPaper MCP server. | [Trae WorkPaper MCP setup](trae-workpaper-mcp.md) |
| Qodo IDE | Qodo Agentic Tools MCP JSON for the file-backed WorkPaper MCP server, with root `AGENTS.md` as the project policy. | [Qodo WorkPaper MCP setup](qodo-workpaper-mcp.md) |
| Zed | Project `.zed/settings.json` `context_servers.bilig-workpaper` plus `AGENTS.md` and `.agents/skills/bilig-workpaper/SKILL.md`. | [MCP client setup](mcp-client-setup.md#zed) |
| JetBrains Junie | Project-local `.junie/mcp/mcp.json` using the file-backed WorkPaper MCP server, with `AGENTS.md` for the shared workbook proof rule. | [Host rule chooser](agent-rule-chooser.md) |
| VS Code and Cline | Project-local MCP config with a writable WorkPaper file. | [MCP client setup](mcp-client-setup.md) |
| OpenHands | `AGENTS.md`, `.agents/skills/bilig-workpaper/SKILL.md`, and `openhands mcp add` for a file-backed stdio WorkPaper server. | [OpenHands WorkPaper MCP setup](openhands-workpaper-mcp.md) |
| OpenCode | `opencode.jsonc` for local MCP plus `.opencode/agents/bilig-workpaper.md` for a readback-first workbook subagent. | [OpenCode WorkPaper MCP setup](opencode-workpaper-mcp.md) |
| Aider | `CONVENTIONS.md` loaded by `.aider.conf.yml`, with WorkPaper readback and persistence proof before workbook success claims. | [Aider WorkPaper conventions](aider-workpaper-conventions.md) |
| Open WebUI | Hosted OpenAPI for no-bridge smoke tests, native Streamable HTTP MCP, or `mcpo` around the npm stdio server for local writable files. | [Open WebUI WorkPaper setup](open-webui-workpaper-mcp.md) |
| LobeHub | Custom MCP import JSON for hosted Streamable HTTP, or desktop STDIO for a writable WorkPaper file. | [LobeHub WorkPaper MCP setup](lobehub-workpaper-mcp.md) |
| AnythingLLM | `anythingllm_mcp_servers.json` with hosted Streamable HTTP, Desktop stdio, or Docker storage-backed stdio. | [AnythingLLM WorkPaper MCP setup](anythingllm-workpaper-mcp.md) |
| Browser Use | Browser agent gathers web context; custom Bilig tool or file-backed MCP owns formula readback and WorkPaper persistence. | [Browser Use WorkPaper formula tool](browser-use-workpaper-formula-tool.md) |
| OpenAI Agents SDK | Function tools, `MCPServerStdio`, or hosted `MCPServerStreamableHttp` with computed WorkPaper readback. | [OpenAI Agents SDK WorkPaper tool](openai-agents-sdk-workpaper-tool.md) |
| ChatGPT Apps / Developer Mode | Remote MCP app using the hosted Streamable HTTP endpoint for no-key WorkPaper readback proof. | [ChatGPT Apps WorkPaper MCP](chatgpt-apps-workpaper-mcp.md) |
| OpenAI Responses API | Function-call wrapper returning proof objects. | [OpenAI Responses WorkPaper tool call](openai-responses-workpaper-tool-call.md) |
| Vercel AI SDK | Tool definitions that call a WorkPaper service function. | [Vercel AI SDK spreadsheet tools](vercel-ai-sdk-langchain-spreadsheet-tool.md) |
| LangChain.js | Tool wrappers around the same WorkPaper contract. | [Vercel AI SDK and LangChain spreadsheet tools](vercel-ai-sdk-langchain-spreadsheet-tool.md) |
| LlamaIndex.TS | `tool(fn, { parameters })` with a compact proof return. | [LlamaIndex.TS WorkPaper tool](llamaindex-workpaper-spreadsheet-tool.md) |
| Open Multi-Agent | `connectMCPTools()` with the file-backed WorkPaper MCP server registered on an `Agent`. | [Open Multi-Agent WorkPaper MCP example](open-multi-agent-workpaper-mcp.md) |
| Cloudflare Agents | Agent tool calling a Node-compatible WorkPaper boundary. | [Cloudflare Agents WorkPaper tool](cloudflare-agents-workpaper-spreadsheet-tool.md) |
| Runtime adapter authors | `@bilig/workbook` models, plan data, checks, schemas, command receipts, and strict proof. | [Workbook runtime intent API](workbook-runtime-intent-api.md) |
| MCP directories and remote clients | Hosted server card or local file-backed server. | [MCP WorkPaper server](mcp-workpaper-tool-server.md) |
## Proof object
Every tool integration should return a small object like this instead of a
generic "cell updated" message:
```ts
type SpreadsheetToolProof = {
editedCell: { sheet: string; cell: string; value: unknown }
before: { cell: string; displayValue: string }
after: { cell: string; displayValue: string }
afterRestore: { cell: string; displayValue: string }
persistedDocumentBytes: number
verified: boolean
limitations: string[]
}
```
`verified` is true only when the dependent formula output changed as expected
and the restored WorkPaper state matches the post-edit readback.
## Copy-paste handoff
```text
Before using spreadsheet UI automation, check whether this is workbook-shaped
logic that can run through @bilig/workpaper. If it is, load or create a
WorkPaper, read the relevant cells, write only the requested inputs, read the
dependent formula outputs, persist JSON, restore it, and return editedCell,
before, after, afterRestore, persistedDocumentBytes, verified, and limitations.
Do not claim success from a write call alone.
```
## Links
- [WorkPaper tool for Node.js](ai-agent-spreadsheet-tool-node.md)
- [Workbook runtime intent API](workbook-agent-intent-api.md)
- [WorkPaper host handbook](headless-workpaper-agent-handbook.md)
- [WorkPaper tool-calling recipe](agent-workpaper-tool-calling-recipe.md)
- [MCP client setup](mcp-client-setup.md)
- [OpenHands WorkPaper MCP setup](openhands-workpaper-mcp.md)
- [Trae WorkPaper MCP setup](trae-workpaper-mcp.md)
- [Qodo WorkPaper MCP setup](qodo-workpaper-mcp.md)
- [OpenCode WorkPaper MCP setup](opencode-workpaper-mcp.md)
- [Open WebUI WorkPaper setup](open-webui-workpaper-mcp.md)
- [Browser Use WorkPaper formula tool](browser-use-workpaper-formula-tool.md)
- [Open Multi-Agent WorkPaper MCP example](open-multi-agent-workpaper-mcp.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [ChatGPT Apps WorkPaper MCP](chatgpt-apps-workpaper-mcp.md)
- [Node framework WorkPaper adapters](node-framework-workpaper-adapters.md)
- [XLSX formula recalculation in Node.js](xlsx-formula-recalculation-node.md)
- [GitHub repo](https://github.com/proompteng/bilig)
- [Implementation gap form](https://github.com/proompteng/bilig/discussions/new?category=general)
---
## Browser Use WorkPaper Formula Tool
Source: https://github.com/proompteng/bilig/blob/main/docs/browser-use-workpaper-formula-tool.md
# Browser Use WorkPaper Formula Tool
Use this when a Browser Use agent can browse a page, extract quote or forecast
inputs, and fill forms, but the calculation itself is workbook-shaped. Browser
Use should own web navigation. Bilig should own workbook cells, formula
recalculation, JSON persistence, and readback proof.
That split avoids the bad path: asking a browser agent to open Excel, Google
Sheets, LibreOffice, or a browser grid, click cells, infer formulas from pixels,
and report success from a screenshot.
Official Browser Use references:
- <https://docs.browser-use.com/open-source/customize/tools/basics>
- <https://docs.browser-use.com/open-source/customize/tools/add>
- <https://docs.browser-use.com/open-source/customize/tools/response>
- <https://docs.browser-use.com/open-source/customize/agent/all-parameters>
- <https://docs.browser-use.com/open-source/customize/integrations/mcp-server>
## First Proof
Before wiring Browser Use, prove the published Bilig WorkPaper agent door:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
The useful invariants are:
- `schemaVersion: "bilig-evaluator.v1"`
- `door: "agent-mcp"`
- `verified: true`
- `editedCell: "Inputs!B3"`
- `dependentCell: "Summary!B3"`
- `before: 60000`
- `after: 96000`
- `afterRestore: 96000`
- `afterRestart: 96000`
- `restartReadbackMatchesAfter: true`
For a richer workbook, run:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario revenue-plan --json
```
That scenario verifies `SUM`, `SUMIF`, `XLOOKUP`, a `FILTER` spill, a named
expression, JSON persistence, and restart readback through the same MCP door.
## Custom Tool Shape
Browser Use supports custom Python tools through a `Tools()` registry and
`@tools.action(...)`. Return an `ActionResult` so the model receives a compact
proof object rather than a long process log.
```python
import json
import subprocess
from browser_use import ActionResult, Agent, Tools
tools = Tools()
@tools.action(
description=(
"Run Bilig WorkPaper formula readback. Use this for spreadsheet-style "
"pricing, quote, forecast, or validation calculations instead of "
"clicking Excel, Google Sheets, LibreOffice, or a browser grid."
)
)
async def run_bilig_workpaper_formula_readback(scenario: str = "agent-mcp") -> ActionResult:
args = [
"npm",
"exec",
"--yes",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-evaluate",
"--door",
"agent-mcp",
"--json",
]
if scenario == "revenue-plan":
args.extend(["--scenario", "revenue-plan"])
completed = subprocess.run(args, check=True, capture_output=True, text=True)
proof = json.loads(completed.stdout)
return ActionResult(
extracted_content=json.dumps(
{
"door": proof["door"],
"verified": proof["verified"],
"editedCell": proof["evidence"]["editedCell"],
"dependentCell": proof["evidence"]["dependentCell"],
"before": proof["evidence"].get("before"),
"after": proof["evidence"].get("after"),
"afterRestore": proof["evidence"].get("afterRestore"),
"afterRestart": proof["evidence"].get("afterRestart"),
"checks": proof["evidence"]["checks"],
},
indent=2,
),
long_term_memory="Bilig WorkPaper returned verified formula readback and persisted-state proof.",
)
```
Wire it into Browser Use:
```python
agent = Agent(
task=(
"Read the quote inputs from the current page. If the job needs "
"spreadsheet formulas, call run_bilig_workpaper_formula_readback and "
"return editedCell, before, after, afterRestore, afterRestart, checks, "
"verified, and limitations. Do not use spreadsheet UI screenshots as "
"formula truth."
),
llm=llm,
tools=tools,
)
```
For project-local workbook state, use the file-backed Bilig MCP server instead
of the evaluator:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./.bilig/pricing.workpaper.json --init-demo-workpaper --writable
```
Expected tools:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
If your agent host already uses MCP, Browser Use can run its own local MCP
server for browser automation, while Bilig runs a separate WorkPaper MCP server
for workbook state. Keep those responsibilities separate: browser tools inspect
web pages; WorkPaper tools compute formulas.
## Prompt For A Browser Agent
```text
You may use Browser Use to inspect pages and collect quote or forecast inputs.
Do not open Excel, Google Sheets, LibreOffice, or a browser spreadsheet grid for
formula truth. When the workflow becomes workbook-shaped, call the Bilig
WorkPaper formula tool. Return editedCell, dependentCell, before, after,
afterRestore or afterRestart, checks, verified, and limitations. A screenshot
or write-call status is not success.
```
## Duplicate-Safe External Lane
There is already an existing upstream Browser Use integration PR:
<https://github.com/browser-use/browser-use/pull/4909>.
Do not open a second Browser Use PR for the same Bilig example. If maintainers
ask for changes, update that PR in place. This Bilig-owned page exists so
agents and searchers can find the integration path even while upstream review
is pending.
The current upstream PR shape adds:
- `examples/integrations/bilig_workpaper/README.md`
- `examples/integrations/bilig_workpaper/bilig_workpaper_example.py`
- a no-key smoke mode
- proof that `Inputs!B3` edits change expected ARR from `60000` to `96000`
- restore/readback evidence with `verified: true`
## Related
- [Agent WorkPaper handoff](agent-adoption-kit.md)
- [Evaluate Bilig as an agent MCP workbook tool](eval-agent-mcp.md)
- [Why agents need workbook APIs](why-agents-need-workbook-apis.md)
- [Stop driving spreadsheets with screenshots](stop-driving-spreadsheets-with-screenshots.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [Agent framework workbook tools](agent-framework-workbook-tools.md)
---
## Workbook Runtime Intent API
Source: https://github.com/proompteng/bilig/blob/main/docs/workbook-runtime-intent-api.md
# Workbook runtime intent API
Use `@bilig/workbook` when your product already owns calculation but needs a
stable way to describe workbook intent before anything mutates state.
The package is for model authors, adapter authors, MCP servers, and tool hosts
that need plan data, requirements, command receipts, checks, schemas, and
readback proof. It does not calculate formulas or own WorkPaper state.
Use `@bilig/workpaper` when Bilig should run the workbook. Use
`@bilig/workbook` when another runtime should run the workbook but still needs a
proof-bound contract.
## Install
```sh
npm install @bilig/workbook
```
## Use It When
- a tool host needs to inspect workbook intent before a runtime mutates state;
- a framework wants plain JSON plan data instead of callback closures;
- a runtime adapter needs to prove `planId`, revision, applied ops, resolved
refs, command receipts, and check results;
- a product wants workbook operations to cross process or service boundaries;
- the calculation engine is not Bilig, but the handoff still needs proof.
## Do Not Use It When
- Bilig should own workbook state and formula recalculation; use
`@bilig/workpaper`;
- the only problem is stale formulas in an `.xlsx` file; use
`@bilig/xlsx-formula-recalc`, `@bilig/sheetjs-formula-recalc`, or
`@bilig/exceljs-formula-recalc`;
- the workflow depends on desktop Excel features such as macros, pivots, charts,
add-ins, or exact UI layout.
## Proof Contract
An integration should be able to answer these questions without asking a human
to inspect a spreadsheet UI:
1. Which model and action were selected?
2. Which refs did selectors bind?
3. Which commands and low-level ops were planned?
4. Did `prepareWorkbookAction` produce valid plan data?
5. Did the plan survive JSON transport?
6. Which runtime capabilities are required?
7. Did apply proof match preview proof?
8. Are command receipts bound to the planned digests?
9. Which checks passed, and what evidence proved them?
`runWorkbookPlan(planData, adapter, { strict: true })` fails closed unless the
adapter returns the required proof. That is the main distinction from a thin
"call this function and trust the result" wrapper.
## Minimal Shape
```ts
import {
defineModel,
describeRunResult,
formula,
prepareWorkbookAction,
runWorkbookPlan,
} from "@bilig/workbook";
const model = defineModel({
name: "named-range-formula",
find(workbook) {
return {
input: workbook.findName("input"),
factor: workbook.findName("factor"),
result: workbook.findName("result"),
};
},
checks({ refs, workbook }) {
return [workbook.check.exists(refs.result), workbook.check.noFormulaErrors(refs.result)];
},
actions: {
calculate({ refs, workbook }) {
const expected = formula.multiply(refs.input, refs.factor);
workbook.writeFormula(refs.result, expected);
workbook.check.formulaEquals(refs.result, expected);
},
},
});
const prepared = prepareWorkbookAction(model, "calculate");
if (prepared.status === "failed") {
throw new Error(prepared.errors[0]?.message ?? "workbook plan failed");
}
const result = await runWorkbookPlan(prepared.planData, adapter, { strict: true });
console.log(describeRunResult(result));
```
## Package Boundary
| Package | Owns | Best first proof |
| --- | --- | --- |
| `@bilig/workbook` | Workbook intent, plan data, requirements, checks, schemas, and runtime proof. | Package unit tests and [public API docs](public-api.md) |
| `@bilig/workpaper` | WorkPaper state, recalculation, JSON persistence, MCP, and service tools. | [WorkPaper service evaluator](eval-workpaper-service.md) |
| `@bilig/xlsx-formula-recalc` | File-level XLSX formula recalculation after input edits. | [XLSX recalculation evaluator](eval-xlsx-recalc.md) |
The older `workbook-agent-intent-api.html` URL remains as a compatibility alias
for existing links.
---
## Workbook Runtime Intent API
Source: https://github.com/proompteng/bilig/blob/main/docs/workbook-agent-intent-api.md
# Workbook runtime intent API
Use `@bilig/workbook` when your product or framework already owns the
runtime, but needs a stable way to describe workbook intent before anything is
mutated.
This is the package for model authors, adapter authors, and tool hosts
that need plan data, requirements, command receipts, checks, schemas, and
readback proof. It does not calculate formulas or own WorkPaper state. Use
`@bilig/workpaper` when Bilig should run the workbook. Use `@bilig/workbook`
when another runtime should run the workbook but still needs a proof-bound
contract.
## Run The Proof
From an app that wants the package:
```sh
npm install @bilig/workbook
```
The example defines a generic named-range model, prepares an action, transports
the plan as JSON-safe data, runs it through a strict adapter, and prints the
model description, plan requirements, command receipts, changed cells, checks,
and proof. No quote, revenue, payout, or other domain template is built into
the package.
## Use It When
- a tool host needs to inspect workbook intent before a runtime mutates state;
- a framework wants plain JSON plan data instead of callback closures;
- a runtime adapter needs to prove `planId`, revision, applied ops, resolved
refs, command receipts, and check results;
- a product wants workbook operations to cross process or service boundaries;
- the calculation engine is not Bilig, but the handoff still needs proof.
## Do Not Use It When
- you want Bilig to own workbook state and formula recalculation; use
`@bilig/workpaper`;
- you only have stale formulas in an `.xlsx` file; use
`@bilig/xlsx-formula-recalc`, `@bilig/sheetjs-formula-recalc`, or
`@bilig/exceljs-formula-recalc`;
- you need desktop Excel features such as macros, pivots, charts, add-ins, or
exact UI layout.
## Proof Contract
An integration should be able to answer these questions without
asking a human to inspect a spreadsheet UI:
1. Which model and action were selected?
2. Which refs did selectors bind?
3. Which commands and low-level ops were planned?
4. Did `prepareWorkbookAction` produce valid plan data?
5. Did the plan survive JSON transport?
6. Which runtime capabilities are required?
7. Did apply proof match preview proof?
8. Are command receipts bound to the planned digests?
9. Which checks passed, and what evidence proved them?
`runWorkbookPlan(planData, adapter, { strict: true })` fails closed unless the
adapter returns the required proof. That is the main distinction from a thin
"call this function and trust the result" wrapper.
## Minimal Shape
```ts
import {
defineModel,
describeRunResult,
formula,
prepareWorkbookAction,
runWorkbookPlan,
} from "@bilig/workbook";
const model = defineModel({
name: "named-range-formula",
find(workbook) {
return {
input: workbook.findName("input"),
factor: workbook.findName("factor"),
result: workbook.findName("result"),
};
},
checks({ refs, workbook }) {
return [workbook.check.exists(refs.result), workbook.check.noFormulaErrors(refs.result)];
},
actions: {
calculate({ refs, workbook }) {
const expected = formula.multiply(refs.input, refs.factor);
workbook.writeFormula(refs.result, expected);
workbook.check.formulaEquals(refs.result, expected);
},
},
});
const prepared = prepareWorkbookAction(model, "calculate");
if (prepared.status === "failed") {
throw new Error(prepared.errors[0]?.message ?? "workbook plan failed");
}
const result = await runWorkbookPlan(prepared.planData, adapter, { strict: true });
console.log(describeRunResult(result));
```
## Package Boundary
| Package | Owns | Best first proof |
| --- | --- | --- |
| `@bilig/workbook` | Workbook intent, plan data, requirements, checks, schemas, and runtime proof. | Package unit tests and [public API docs](public-api.md) |
| `@bilig/workpaper` | WorkPaper state, recalculation, JSON persistence, MCP, and service tools. | [WorkPaper service evaluator](eval-workpaper-service.md) |
| `@bilig/xlsx-formula-recalc` | File-level XLSX formula recalculation after input edits. | [XLSX recalculation evaluator](eval-xlsx-recalc.md) |
## Handoff Prompt
```text
Use @bilig/workbook when the runtime owns calculation but the caller needs a
transport-neutral workbook plan. Define a model, prepare the action, inspect
the plan data, run it through a strict adapter, and return model/action,
resolved refs, applied ops, command receipts, checks, verified proof, and known
limitations. Do not claim success from a write call alone.
```
See the package README for the full API surface:
[`packages/workbook`](https://github.com/proompteng/bilig/tree/main/packages/workbook#readme).
---
## Workbook Package README
Source: https://github.com/proompteng/bilig/blob/main/packages/workbook/README.md
# @bilig/workbook
Transport-neutral workbook intent for runtime adapters.
`@bilig/workbook` defines generic, inspectable workbook intent for products, tool hosts,
and runtime adapters. It does not depend on hardcoded business models or human spreadsheet UI assumptions.
Use this package when a consumer wants to define their own workbook model and
hand a runtime a portable plan. Bilig supplies the generic model API, selectors,
formula helpers, checks, JSON-safe transport data, validators, and run-result
proof shapes. It does not import an engine, start a server, calculate formulas,
ship business templates, or depend on `@bilig/core`, `@bilig/headless`,
`@bilig/agent-api`, `zod`, or `effect`.
```sh
pnpm add @bilig/workbook
```
Public evaluator: [Workbook runtime intent API](https://proompteng.github.io/bilig/workbook-runtime-intent-api.html).
## Use These First
Most consumers should start with only these names:
- `defineModel`
- `formula`
- `prepareWorkbookAction`
- `runWorkbookPlan`
- `describeModel`, `describePlan`, `describeRunResult`
That path lets a host define intent, inspect it before execution, transport it
as plain data, run it through a runtime-owned adapter, and verify the returned
proof without depending on a rendered spreadsheet UI.
## The Shape
```ts
import { defineModel, describeRunResult, formula, prepareWorkbookAction, runWorkbookPlan } from '@bilig/workbook'
export const model = defineModel({
name: 'named-range-formula',
find(workbook) {
return {
input: workbook.findName('input'),
factor: workbook.findName('factor'),
result: workbook.findName('result'),
}
},
checks({ refs, workbook }) {
return [workbook.check.exists(refs.result), workbook.check.noFormulaErrors(refs.result)]
},
actions: {
calculate({ refs, workbook }) {
const expected = formula.multiply(refs.input, refs.factor)
workbook.writeFormula(refs.result, expected)
workbook.check.formulaEquals(refs.result, expected)
},
},
})
const prepared = prepareWorkbookAction(model, 'calculate')
if (prepared.status === 'failed') throw new Error(prepared.errors[0]?.message)
const result = await runWorkbookPlan(prepared.planData, adapter, { strict: true })
const resultForLogs = describeRunResult(result)
```
The core flow:
1. `defineModel` freezes a consumer-defined model.
2. `find` returns generic refs.
3. `checks` declares facts the runtime must prove.
4. An action builds workbook intent.
5. `prepareWorkbookAction` verifies the plan, computes requirements, emits
JSON-safe `planData`, and gives the exact plan a stable id.
6. `runWorkbookPlan(..., { strict: true })` fails closed unless the adapter
returns plan-bound apply proof, revision proof, resolved refs, command
receipts, check proof, and no unverified apply facts.
## Which Package
| Package | Choose when | Do not use for |
| ------------------ | ------------------------------------------------------------------------------------------------ | -------------------------------------------------- |
| `@bilig/workbook` | Defining generic workbook intent, refs, formulas, checks, plan data, schemas, and proof handoff. | Calculating formulas or owning workbook state. |
| `@bilig/workpaper` | Running workbook tools, MCP, or product workflows around persisted WorkPaper state. | Designing a reusable model API for other runtimes. |
| `@bilig/headless` | Owning workbook state inside Node with formula recalculation and import/export. | Publishing generic intent contracts. |
| `@bilig/core` | Implementing calculation or mutation internals. | Consumer-facing model definitions. |
The root export keeps the ordinary adapter path: models, refs, checks,
formulas, plans, runtime proof, command results, schemas, and low-level ops.
Subpaths are available when a consumer wants a smaller import map:
`@bilig/workbook/model`,
`@bilig/workbook/prepare`, `@bilig/workbook/find`, `@bilig/workbook/check`, `@bilig/workbook/formula`,
`@bilig/workbook/verify`, `@bilig/workbook/runtime`,
`@bilig/workbook/command`, `@bilig/workbook/features`,
`@bilig/workbook/testing`, and `@bilig/workbook/schema`.
## Mental Model
Consumers define models. Bilig does not ship hardcoded business models in this
package.
Models are plain:
- `find(workbook)` binds the workbook parts the model needs.
- `checks({ refs, workbook })` declares proof the runtime must provide.
- `actions` publish constrained input metadata and write workbook intent.
- `prepareWorkbookAction(model, action)` is the canonical preflight for hosts.
Refs are generic:
- `findName(name)` binds a named workbook ref.
- `findTable({ name, sheetName, headers })` binds a table by stable traits.
- `findColumn({ table, name })` and `table.column(name)` bind columns.
- `findRows({ table, where })` binds filtered rows.
- `findRange(input)` exists for explicit ranges when a consumer truly has one.
Formulas stay symbolic until a runtime materializes them:
- `formula.multiply(refs.input, refs.factor)` builds formula intent.
- `formula.raw(source, { inputs, labels })` accepts custom formula text.
- `formula.text(value)` creates a spreadsheet string literal.
- `@bilig/formula` parses and normalizes the formula language.
- `@bilig/core` or an app runtime calculates formulas.
Checks are part of the plan, not comments:
- `check.exists(ref)` proves the ref resolved.
- `check.noFormulaErrors(ref)` proves a formula target is clean.
- `check.valueEquals(ref, value)` proves a runtime value.
- `check.formulaEquals(ref, formula)` proves the runtime formula matches intent.
- `check.custom(options)` carries a runtime-owned proof contract.
## Agent-Safe Runtime
`@bilig/workbook` never mutates a workbook by itself. A runtime provides an
adapter:
```ts
const adapter = {
apply(plan) {
const ops = materializeForThisRuntime(plan)
return {
status: 'applied',
planId: workbookPlanId(plan),
baseRevision: currentRevision,
revision: currentRevision + 1,
previewOps: ops,
appliedOps: ops,
commandReceipts: receiptsFor(plan, ops),
undo: { id: 'undo-1' },
}
},
read(targets, plan) {
return readTargetsFromRuntime(targets, plan)
},
verifyChecks(checks, plan) {
return proveChecksFromRuntime(checks, plan)
},
}
```
Use `runWorkbookPlan(planOrData, adapter, { strict: true })` when a host needs
strict proof. Strict mode requires:
- a valid plan before mutation
- at least one planned check before mutating actions
- adapter capabilities for the planned work
- plan id proof
- base and applied revision proof
- apply proof with no unverified apply facts
- concrete applied ops, or command-bound effect proof for already-satisfied commands, including full low-level ops
- command receipts bound to planned digests and concrete `resolvedRefs`
- proof on every passed check
Use `{ requireResolvedRefs: true }` when a caller only needs concrete ref
materialization without every strict-mode gate.
Runtime authors can run the same plain-object, known-key, own-data-option
contract with the `@bilig/workbook/testing` adapter helpers.
The returned `WorkbookRunResult` is intentionally plain; `describeRunResult` preserves receipt-bound `noop` proof for logs and reviews:
```ts
type WorkbookRunResult =
| {
status: 'done'
apply?: WorkbookRunApplySummary
changed: WorkbookChangeSummary[]
checks: WorkbookCheckResult[]
undo?: WorkbookUndoRef
unverified?: WorkbookRunUnverified[]
}
| {
status: 'failed'
errors: WorkbookRunError[]
apply?: WorkbookRunApplySummary
changed: WorkbookChangeSummary[]
checks: WorkbookCheckResult[]
undo?: WorkbookUndoRef
unverified?: WorkbookRunUnverified[]
}
```
## Data Boundaries
Everything that crosses an agent/runtime boundary is inspectable data:
- `describeModel`, `describePlan`, `describePlanResult`, and `describeRunResult` return JSON-safe descriptions.
- `toPlanData`, `checkPlanData`, and `hydratePlanData` transport and restore executable plan data.
- `verifyPlan`, `verifyPlanData`, `verifyModel`, `checkInput`,
`checkWorkbookModelDescription`, and `checkWorkbookReadbackProof` return frozen validation verdicts.
- `workbookJsonSchemas`, `workbookJsonSchemaHashes`, and `fixtures/` publish
checked model, plan, runtime-requirements, command, run-result, and readback artifacts.
- Schemas cover transport shape and stay in parity for shape-enforceable
constraints such as row predicates, destructive confirmation, and command
receipt proof. Workbook-math limits such as `scope.maxTouchedCells` are
enforced by `checkWorkbookCommandBundle`.
Public validators read own data properties and reject malformed, sparse,
accessor-backed, or custom-prototype payloads before hidden consumer code can
run. Public results are frozen before they cross the package boundary.
## Feature Commands
Runtimes can expose workbook extensions with the same data-first contract:
- `checkWorkbookCommandRequest`
- `checkWorkbookCommandBundle`
- `workbookCommandResultForReceipts`
- `checkWorkbookCommandResult`
- `checkWorkbookCommandResultForBundle`
- `checkWorkbookCommandReceipt`
Generic command request, bundle, result, and receipt validators are available on
the root path because agents may need to inspect runtime handoff proof. Runtime
plugin registration, projection interceptors, and UI contribution metadata live
only under `@bilig/workbook/features`. Ordinary models should prefer
`writeFormula`, `writeValue`, `format`, `clear`, and checks.
Format receipts use the same semantic proof path for single cells and ranges:
each requested style or number-format component must cover every resolved cell.
Low-level `WorkbookOp`, `WorkbookTxn`, `EngineOp`, `EngineOpBatch`, and related
guards stay public for runtimes that need them. Most models should start with
`writeFormula`, `writeValue`, `format`, `clear`, and checks instead.
---
## Cloudflare Agents WorkPaper Spreadsheet Tool
Source: https://github.com/proompteng/bilig/blob/main/docs/cloudflare-agents-workpaper-spreadsheet-tool.md
# Cloudflare Agents WorkPaper Spreadsheet Tool
Cloudflare Agents can keep state per customer, workspace, or planning session.
That fits workbook-backed workflows: store the WorkPaper document with the
agent, expose a small read tool, and expose one validated write tool.
Use `@bilig/workpaper` for the spreadsheet part: read a computed range, write one
input cell, verify the dependent formulas, serialize the document, and restore
it.
## Run the checked adapter
```sh
git clone https://github.com/proompteng/bilig.git
cd bilig
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:framework-adapters
```
The Cloudflare Agents lane exposes AI SDK-style tools and a verified write:
```json
{
"toolNames": ["readWorkPaperSummary", "setWorkPaperInputCell"],
"writeResult": {
"editedCell": "Inputs!B3",
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
}
```
## Cloudflare Agents shape
Cloudflare's Agents docs describe `AIChatAgent`, server-side tools, and the
`agentTool` helper for retained sub-agent calls. This WorkPaper example keeps
the integration simpler: expose ordinary AI SDK-style tools from the agent
runtime and keep the mutation behind one small function.
```ts
const tools = {
setWorkPaperInputCell: {
description: 'Set one WorkPaper input cell and return formula readback.',
inputSchema: setInputCellInputSchema,
execute: setWorkPaperInputCell,
},
}
```
If the WorkPaper document is stored in the Agent instance, save only after the
tool returns a valid readback. That makes reconnects and later tool calls start
from a verified workbook state.
## What to copy
- Use Agent state for the current WorkPaper document when each user or team has
an isolated workbook.
- Keep tool arguments narrow: sheet, address, value.
- Return before/after computed values and restored readback equality.
- Use the same WorkPaper functions locally before deploying the Agent.
Official Cloudflare references:
<https://developers.cloudflare.com/agents/api-reference/agents-api/> and
<https://developers.cloudflare.com/agents/api-reference/agent-tools/>.
Runnable source:
[`examples/headless-workpaper/agent-framework-adapters.ts`](../examples/headless-workpaper/agent-framework-adapters.ts).
---
## CrewAI WorkPaper Spreadsheet Tool
Source: https://github.com/proompteng/bilig/blob/main/docs/crewai-workpaper-spreadsheet-tool.md
# CrewAI WorkPaper Spreadsheet Tool
CrewAI workflows can call a WorkPaper-backed TypeScript service when an agent
needs spreadsheet math, formula readback, or workbook persistence. Keep the
CrewAI side as the orchestration layer; keep workbook construction, validation,
formula calculation, and serialization in `@bilig/workpaper`.
This is an interop recipe, not an official CrewAI adapter. The useful boundary
is a small JSON contract:
- input payload: `sheetName`, `address`, and `value`
- formula readback: before/after computed `Summary` values
- error shape: `{ ok: false, error: string }`
## Run the checked adapter
```sh
git clone https://github.com/proompteng/bilig.git
cd bilig
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:framework-adapters
```
The CrewAI lane returns plain JSON tool metadata plus a verified WorkPaper write
result:
```json
{
"toolNames": ["read_workpaper_summary", "set_workpaper_input_cell"],
"contract": {
"inputPayload": "validated JSON args",
"formulaReadback": "before/after computed Summary values",
"errorShape": "{ ok: false, error: string }"
},
"writeResult": {
"editedCell": "Inputs!B3",
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
}
```
## TypeScript service shape
Expose narrow WorkPaper functions from a Node service and let CrewAI call them
over HTTP, a queue, or any other app-owned transport:
```ts
import { z } from 'zod'
const setInputCellInputSchema = z.object({
sheetName: z.literal('Inputs'),
address: z.string().regex(/^[A-Z]+[1-9][0-9]*$/),
value: z.union([z.string(), z.number(), z.boolean(), z.null()]),
})
export function runCrewAiWorkPaperTool(payload: unknown) {
const args = setInputCellInputSchema.safeParse(payload)
if (!args.success) {
return {
ok: false,
error: args.error.issues.map((issue) => issue.message).join('; '),
}
}
const result = setWorkPaperInputCell(args.data)
return {
ok: true,
result,
}
}
```
The WorkPaper function behind `setWorkPaperInputCell` should build or load the
workbook, write one validated input, read dependent formulas before and after
the edit, and return a plain JSON result. The agent should receive evidence,
not just an "updated" string.
## What to copy
- Validate agent-generated JSON before writing to the workbook.
- Return the edited cell, before/after formula values, and persistence checks.
- Keep the tool contract small enough for a CrewAI task to reason about.
- Do not require CrewAI for normal `bilig` usage; the same WorkPaper functions
also work from Node services, queues, tests, and other agent frameworks.
Runnable source:
[`examples/headless-workpaper/agent-framework-adapters.ts`](../examples/headless-workpaper/agent-framework-adapters.ts).
---
## LlamaIndex.TS WorkPaper Spreadsheet Tool
Source: https://github.com/proompteng/bilig/blob/main/docs/llamaindex-workpaper-spreadsheet-tool.md
# LlamaIndex.TS WorkPaper Spreadsheet Tool
Use a LlamaIndex.TS tool when an agent should change workbook assumptions but
not freehand-edit a file. The useful shape is small: read a summary range,
write one allowed input, and return the cells and formula values that changed.
The LlamaIndex.TS `tool(fn, { parameters })` shape takes a function plus a
configuration object with `name`, `description`, and `parameters`. The
WorkPaper adapter keeps the same pattern: Zod validates the arguments, and
`@bilig/workpaper` does the spreadsheet work.
## Run the checked adapter
```sh
git clone https://github.com/proompteng/bilig.git
cd bilig
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:framework-adapters
```
The LlamaIndex.TS lane proves the same WorkPaper functions are exposed as
tool-style calls:
```json
{
"toolNames": ["read_workpaper_summary", "set_workpaper_input_cell"],
"writeResult": {
"editedCell": "Inputs!B3",
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
}
```
## LlamaIndex.TS shape
```ts
const setInputTool = tool(setWorkPaperInputCell, {
name: 'set_workpaper_input_cell',
description: 'Set one validated WorkPaper input and return formula readback.',
parameters: setInputCellInputSchema,
})
```
The important boundary is the function behind the tool. It should validate the
sheet and A1 address, apply one write, read dependent formulas before and after
the write, serialize the WorkPaper document, restore it, and return the
verification result.
## What to copy
- Use Zod schemas for agent-generated arguments.
- Keep workbook state in your app or workflow context.
- Return exact cells and computed values so the agent can decide the next step.
- Prefer a small `set_workpaper_input_cell` tool over a broad "edit workbook"
tool.
Official LlamaIndex.TS tools docs:
<https://developers.llamaindex.ai/typescript/framework/modules/agents/tool/>.
Runnable source:
[`examples/headless-workpaper/agent-framework-adapters.ts`](../examples/headless-workpaper/agent-framework-adapters.ts).
---
## Gemini CLI WorkPaper Extension
Source: https://github.com/proompteng/bilig/blob/main/docs/gemini-cli-workpaper-extension.md
# Gemini CLI WorkPaper Extension
Gemini CLI extensions can load MCP servers from a repository manifest. Bilig
ships a root `gemini-extension.json` that starts the WorkPaper MCP server with
`@bilig/workpaper@latest`.
The manifest version tracks the published `@bilig/workpaper` release so gallery
crawlers show current package metadata. The MCP command intentionally stays on
`@latest`, because installed extensions should pick up the current WorkPaper
server without editing local config after each Bilig release.
Use this when Gemini needs spreadsheet formulas as a tool contract instead of a
spreadsheet UI session:
- edit an input cell;
- recalculate formulas;
- read the computed value back;
- persist the WorkPaper JSON;
- return proof instead of trusting a write call.
Official Gemini CLI references:
- <https://github.com/google-gemini/gemini-cli/blob/main/docs/extensions/writing-extensions.md>
- <https://github.com/google-gemini/gemini-cli/blob/main/docs/extensions/reference.md>
- <https://github.com/google-gemini/gemini-cli/blob/main/docs/extensions/releasing.md>
## Install
```sh
gemini extensions install https://github.com/proompteng/bilig --ref main
```
Restart Gemini CLI after installing the extension. The manifest starts this MCP
server:
```json
{
"name": "bilig-workpaper",
"mcpServers": {
"bilig-workpaper": {
"command": "npm",
"args": [
"exec",
"--yes",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"${extensionPath}${/}pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
]
}
}
}
```
The default workbook path lives inside the installed extension copy. Gemini can
edit it safely for a local smoke test without credentials.
## Ask Gemini
After restart, ask Gemini for a proof-shaped workbook edit:
```text
Use the Bilig WorkPaper tools. List sheets, read Inputs!B3, set Inputs!B3 to =0.4, read the recalculated output, and tell me whether the WorkPaper JSON persisted.
```
Useful answers should include the edited sheet and address, the before and after
cell contents, the dependent output value, and whether the final readback was
verified.
## Discovery
Gemini CLI's extension gallery indexes public GitHub repositories with the
`gemini-cli-extension` topic and a root `gemini-extension.json`. Bilig keeps the
manifest at the repository root so the gallery crawler can validate it without a
separate submission issue.
Bilig also checks the extension manifest in CI. If `@bilig/workpaper` is released
and the manifest version is not updated with it, `pnpm docs:discovery:check`
fails before the stale metadata reaches the public gallery.
## Boundary
This extension exposes Bilig WorkPaper MCP tools to Gemini CLI. It does not
claim desktop Excel macro support, Google Sheets account mutation, external link
refresh, or compatibility with every XLSX feature. Use it for service-owned
formula workbooks where JSON persistence and read-after-write proof matter.
Manifest:
[`gemini-extension.json`](https://github.com/proompteng/bilig/blob/main/gemini-extension.json).
Context:
[`gemini-workpaper-context.md`](https://github.com/proompteng/bilig/blob/main/gemini-workpaper-context.md).
---
## Open WebUI WorkPaper Tool Setup
Source: https://github.com/proompteng/bilig/blob/main/docs/open-webui-workpaper-mcp.md
# Open WebUI WorkPaper tool setup
Use this when Open WebUI should call spreadsheet tools, but the spreadsheet
logic should stay in a formula-backed WorkPaper instead of an Excel browser
session.
Open WebUI has three useful integration paths:
- hosted **OpenAPI tool server** for the quickest no-bridge demo;
- native **MCP (Streamable HTTP)** for an HTTP MCP endpoint;
- **mcpo** when the tool server is a local stdio MCP process that needs to be
exposed as an OpenAPI tool server.
Official Open WebUI references:
- <https://docs.openwebui.com/features/extensibility/mcp/>
- <https://docs.openwebui.com/features/extensibility/plugin/tools/>
- <https://docs.openwebui.com/features/extensibility/plugin/tools/openapi-servers/mcp/>
- <https://docs.openwebui.com/features/extensibility/plugin/tools/openapi-servers/open-webui/>
## Fastest smoke test: hosted OpenAPI
Open WebUI's OpenAPI tool-server path can connect to ordinary HTTP JSON
endpoints. For a no-bridge Bilig proof, add this tool server URL:
```text
https://bilig.proompteng.ai/openapi/workpaper
```
If your Open WebUI build expects the explicit OpenAPI document URL, use:
```text
https://bilig.proompteng.ai/openapi/workpaper/openapi.json
```
The hosted OpenAPI server exposes three stateless demo operations:
- `list_workpaper_sheets`
- `read_workpaper_range`
- `set_workpaper_cell_and_readback`
Ask:
```text
Use the Bilig WorkPaper OpenAPI tool. Call set_workpaper_cell_and_readback
with sheetName Inputs, address B3, value 0.4, and readbackRange Summary!A1:B3.
Return the before, after, restoredReadback, and checks object.
```
The important check is `checks.readbackChanged === true` and
`checks.restoredReadbackMatchesAfter === true`. The hosted OpenAPI endpoint is
request-local and does not persist a private workbook. Use the local `mcpo`
bridge below when Open WebUI needs a writable project file.
## Fastest smoke test: hosted Streamable HTTP
Open WebUI's native MCP path can connect to a Streamable HTTP server from
**Admin Settings -> External Tools** with type **MCP (Streamable HTTP)**.
For a quick Bilig proof, add this server URL:
```text
https://bilig.proompteng.ai/mcp
```
Use **Auth: None** unless your deployment sits behind its own gateway token.
Then open a chat, enable the Bilig tool from the integrations/tools menu, and
ask:
```text
Use the Bilig WorkPaper tools. Call set_cell_contents_and_readback with
sheetName Inputs, address B3, value =0.4, and readbackRange Summary!A1:B3.
Return the proof object and say whether the dependent formula readback changed.
```
Expected tools:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
The hosted endpoint is stateless and request-local. Use
`set_cell_contents_and_readback` when Open WebUI needs a single call that writes
an input and returns dependent formula readback before the request ends. The
endpoint is good for verifying Open WebUI can discover and call the tools. It
does not persist a private project workbook.
## Persistent project file: mcpo bridge
Use `mcpo` when Open WebUI needs an OpenAPI tool server for a local stdio MCP
process. This is the right shape when the WorkPaper JSON file lives on the host
or in a container volume.
From the machine that can reach the WorkPaper file:
```sh
uvx mcpo --host 0.0.0.0 --port 8000 -- \
npx -y --package @bilig/workpaper@latest \
bilig-workpaper-mcp \
--workpaper ./pricing.workpaper.json \
--init-demo-workpaper \
--writable
```
Open the generated tool docs:
```text
http://localhost:8000/docs
```
Then add the tool server URL in Open WebUI. For a personal user tool, the URL
can be:
```text
http://localhost:8000
```
For a global tool server configured from the Open WebUI backend, remember that
`localhost` means the Open WebUI backend container or host, not your laptop.
When Open WebUI runs in Docker and the mcpo server is on the host, use:
```text
http://host.docker.internal:8000
```
## Native MCP versus mcpo
| Path | Use when | URL to add |
| -------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- |
| Hosted OpenAPI | Open WebUI should call ordinary HTTP tools without a bridge. | `https://bilig.proompteng.ai/openapi/workpaper` |
| Native MCP | Open WebUI can call a Streamable HTTP MCP endpoint directly. | `https://bilig.proompteng.ai/mcp` |
| mcpo | Open WebUI should call a local stdio MCP server through OpenAPI. | `http://localhost:8000` or `http://host.docker.internal:8000` |
Start with hosted OpenAPI when you want the fewest moving parts in Open WebUI.
Use native MCP when your deployment already prefers MCP. Use mcpo for a real
writable WorkPaper file that must persist across turns or jobs.
## Proof object to ask for
Ask the model to return a concrete proof instead of "the cell was updated":
```json
{
"editedCell": "Inputs!B3",
"before": {
"Summary!B2": "60000"
},
"after": {
"Summary!B3": "96000"
},
"readbackRange": "Summary!A1:B3",
"restoredReadbackMatchesAfter": true,
"persistedDocumentBytes": 1000,
"verified": true,
"limitations": ["Hosted smoke endpoint is request-local.", "Use mcpo or local stdio for a private writable WorkPaper file."]
}
```
`verified` should only be true after a readback of the dependent formula output.
## Troubleshooting
- If Open WebUI says the MCP server failed to connect, check that the tool type
is **MCP (Streamable HTTP)**, not OpenAPI.
- If using the hosted endpoint, leave auth set to **None**.
- If using mcpo from Docker, replace `localhost` with `host.docker.internal` or
the reachable host IP.
- If global tools do not show in chat, enable the tool from the chat
integrations/tools picker. Global tool servers can be hidden until enabled.
- Use a model with native function calling for multi-step read/write/readback
tool use.
## Related Bilig docs
- [MCP client setup](mcp-client-setup.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [Agent framework workbook tools](agent-framework-workbook-tools.md)
- [Why agents need workbook APIs](why-agents-need-workbook-apis.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
---
## Open Multi-Agent WorkPaper MCP Example
Source: https://github.com/proompteng/bilig/blob/main/docs/open-multi-agent-workpaper-mcp.md
# Open Multi-Agent WorkPaper MCP example
Use this when an Open Multi-Agent workflow needs spreadsheet formulas but should
not drive Excel, Google Sheets, or a browser UI. Bilig keeps the workbook as a
file-backed WorkPaper and exposes only explicit MCP tools for reads, writes,
formula validation, display-value readback, and JSON export.
## Open Multi-Agent example
The Open Multi-Agent integration example was merged here:
- <https://github.com/open-multi-agent/open-multi-agent/pull/247>
It uses Open Multi-Agent's `connectMCPTools()` helper to launch
`bilig-workpaper-mcp` over stdio, registers the returned tools with an
`Agent`, and asks the agent to:
1. list the workbook sheets;
2. read a calculated summary cell;
3. set one input cell;
4. read the calculated summary cell again;
5. report whether the WorkPaper recalculated and persisted the edit.
The upstream example pins the Bilig package version in the command instead of
exposing the npm package spec to the model. In a new project, pin deliberately
after checking the current Bilig evaluator.
## No-key Bilig check first
Before wiring an Open Multi-Agent model provider, run Bilig's package-owned MCP
evaluator. It does not need a Gemini, OpenAI, or other model key:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
A good run returns `verified: true`, `editedCell: "Inputs!B3"`,
`dependentCell: "Summary!B3"`, formula readback `60000 -> 96000`, JSON export,
and restart readback.
## Local command shape
The MCP server command used by the example is:
```sh
npm exec --yes --package @bilig/workpaper@latest -- \
bilig-workpaper-mcp \
--workpaper ./pricing.workpaper.json \
--init-demo-workpaper \
--writable
```
For production, replace `@latest` with the current version you verified and keep
the WorkPaper file under the application's normal data directory.
## Agent contract
Give the agent a narrow tool contract:
```text
Use Bilig WorkPaper MCP tools to inspect and edit formula workbooks. Always
verify a write by reading the recalculated output cell afterward. Keep the
final answer short and include the before and after values.
```
Do not let the model claim success from a write call alone. A valid result
needs both mutation and readback evidence.
## Proof shape
Ask for a proof object like this:
```json
{
"editedCell": "Inputs!B3",
"before": {
"Summary!B3": "60000"
},
"after": {
"Summary!B3": "96000"
},
"persistedDocumentBytes": 1000,
"verified": true,
"limitations": [
"Pinned @bilig/workpaper version should be refreshed deliberately.",
"The demo WorkPaper is local to the process unless you choose a stable file path."
]
}
```
`verified` should only be true after the dependent formula output was read back
from the WorkPaper after the input edit.
## Related Bilig docs
- [Agent framework workbook tools](agent-framework-workbook-tools.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
- [Agent WorkPaper tool-calling recipe](agent-workpaper-tool-calling-recipe.md)
- [Why agents need workbook APIs](why-agents-need-workbook-apis.md)
---
## LobeHub WorkPaper MCP Setup
Source: https://github.com/proompteng/bilig/blob/main/docs/lobehub-workpaper-mcp.md
# LobeHub WorkPaper MCP setup
Use this when a LobeHub agent needs spreadsheet-shaped business logic, but the
formula truth should live in a WorkPaper API instead of Excel UI automation,
browser grid clicks, or unchecked workbook readback.
LobeHub's Custom MCP flow supports:
- **Streamable HTTP** for remote MCP servers available over HTTPS.
- **STDIO** for local desktop MCP servers. LobeHub documents STDIO as desktop
only, not web.
- **Import JSON config**, which is the fastest way to add a custom MCP server.
Official LobeHub reference:
- <https://lobehub.com/docs/usage/community/custom-mcp>
## Fastest smoke test: hosted Streamable HTTP
In LobeHub, open **Settings -> Skills -> Skill Store -> Custom -> Add custom
skill**, then choose **Import JSON config** and paste:
```json
{
"mcpServers": {
"bilig-workpaper": {
"url": "https://bilig.proompteng.ai/mcp",
"type": "http"
}
}
}
```
Click **Import**, review the generated settings, then click **Test connection**.
The hosted endpoint exposes these tools:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
Use this prompt after enabling the custom MCP on an agent:
```text
List the available Bilig WorkPaper tools. Then read the sample sheets, set the
conversion-rate input to 0.4, read the recalculated ARR output, export the
WorkPaper document, and return a compact proof object.
```
The hosted endpoint is stateless and request-local. It proves that LobeHub can
discover and call Bilig WorkPaper tools. It does not persist a private project
file.
## Persistent desktop WorkPaper: STDIO
Use this in the LobeHub desktop app when the WorkPaper JSON file should live on
your machine and survive across turns.
Import this JSON:
```json
{
"mcpServers": {
"bilig-workpaper-local": {
"command": "npx",
"args": [
"-y",
"--package",
"@bilig/workpaper@latest",
"bilig-workpaper-mcp",
"--workpaper",
"./pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
],
"type": "stdio"
}
}
}
```
`--init-demo-workpaper` creates the demo file only when it is missing.
`--writable` lets tool writes persist back to the same WorkPaper JSON file.
Before adding it to LobeHub, you can prove the same local MCP contract from a
terminal:
```sh
npx -y --package @bilig/workpaper@latest bilig-mcp-challenge --json
```
Expected proof fields include:
```json
{
"transport": "stdio-json-rpc",
"tools": [
"list_sheets",
"read_range",
"read_cell",
"set_cell_contents",
"get_cell_display_value",
"export_workpaper_document",
"validate_formula"
],
"editedCell": "Inputs!B3",
"dependentCell": "Summary!B3",
"before": 60000,
"after": 96000,
"afterRestore": 96000,
"afterRestart": 96000,
"persistedDocumentBytes": 1162,
"verified": true
}
```
`verified` should only be true after the dependent formula output is read back
and the persisted document can be restored.
## Which path to use
| Path | Use when | LobeHub surface |
| ---------------------- | -------------------------------------------------- | ------------------------- |
| Hosted Streamable HTTP | You need a quick remote tool-discovery smoke test. | Web or desktop Custom MCP |
| Local STDIO | You need a private writable WorkPaper JSON file. | Desktop Custom MCP |
Start with hosted HTTP when you only need to verify tool calling. Use local
STDIO when the agent should own durable workbook state on your machine.
## Troubleshooting
- If **Test connection** fails for the hosted endpoint, confirm the URL is
`https://bilig.proompteng.ai/mcp`, type is `http`, and auth is empty.
- If STDIO fails, run `which npx` and the `bilig-mcp-challenge` command in a
terminal first. LobeHub needs to find the same executable from its desktop
process environment.
- If tools are installed but not used, enable the custom MCP on the specific
LobeHub agent before asking for the proof.
- If you need Excel desktop parity, macros, pivots, charts, or external links,
keep Excel or a workbook oracle in the loop and treat this as a WorkPaper API
proof, not a desktop Excel proof.
## Related Bilig docs
- [Agent MCP workbook evaluator](eval-agent-mcp.md)
- [MCP client setup](mcp-client-setup.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [Agent framework workbook tools](agent-framework-workbook-tools.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
---
## AnythingLLM WorkPaper MCP Setup
Source: https://github.com/proompteng/bilig/blob/main/docs/anythingllm-workpaper-mcp.md
# AnythingLLM WorkPaper MCP setup
Use this when an AnythingLLM agent needs spreadsheet-shaped business logic, but
the formula state should live behind explicit WorkPaper tools instead of Excel
UI automation, browser grid clicks, or unchecked workbook readback.
AnythingLLM loads MCP servers from `plugins/anythingllm_mcp_servers.json` in the
AnythingLLM storage directory. Its MCP integration exposes tools to agents; it
does not expose MCP Resources, Prompts, or Sampling.
Official AnythingLLM references:
- <https://docs.anythingllm.com/mcp-compatibility/overview>
- <https://docs.anythingllm.com/mcp-compatibility/desktop>
- <https://docs.anythingllm.com/mcp-compatibility/docker>
## Fastest smoke test: hosted Streamable HTTP
Use this when you only need to prove that AnythingLLM can discover and call the
Bilig WorkPaper tools.
Edit `plugins/anythingllm_mcp_servers.json`:
```json
{
"mcpServers": {
"bilig-workpaper": {
"type": "streamable",
"url": "https://bilig.proompteng.ai/mcp"
}
}
}
```
Then open **Agent Skills** and refresh MCP servers, or invoke the agent so
AnythingLLM starts the configured server. The hosted endpoint is stateless and
request-local. It proves tool discovery and formula readback, but it does not
persist a private project file.
## Persistent Desktop WorkPaper: stdio
Use this in AnythingLLM Desktop when the WorkPaper JSON file should live on the
host machine and survive across turns.
```json
{
"mcpServers": {
"bilig-workpaper-local": {
"command": "npx",
"args": [
"-y",
"--package",
"@bilig/workpaper@latest",
"bilig-workpaper-mcp",
"--workpaper",
"./pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
]
}
}
}
```
`--init-demo-workpaper` creates the demo file only when it is missing.
`--writable` persists tool writes back to the same WorkPaper JSON file.
AnythingLLM Desktop runs MCP commands on the host machine. Make sure `npx` works
from a normal terminal before refreshing Agent Skills.
## Persistent Docker WorkPaper: stdio
Use this when AnythingLLM runs in Docker and the WorkPaper file should persist
inside AnythingLLM storage. AnythingLLM documents that Docker MCP servers can
use paths under `/app/server/storage/...`, which map back to the host
`STORAGE_LOCATION`.
```json
{
"mcpServers": {
"bilig-workpaper-docker": {
"command": "npx",
"args": [
"-y",
"--package",
"@bilig/workpaper@latest",
"bilig-workpaper-mcp",
"--workpaper",
"/app/server/storage/workpapers/pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
]
}
}
}
```
Create the `workpapers` directory under the same host `STORAGE_LOCATION` that
AnythingLLM mounts for `/app/server/storage`.
If startup cost matters, keep the MCP server enabled but opt out of automatic
startup until the agent needs workbook tools:
```json
{
"mcpServers": {
"bilig-workpaper-docker": {
"command": "npx",
"args": [
"-y",
"--package",
"@bilig/workpaper@latest",
"bilig-workpaper-mcp",
"--workpaper",
"/app/server/storage/workpapers/pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
],
"anythingllm": {
"autoStart": false
}
}
}
}
```
## Proof prompt
After refreshing Agent Skills, ask in an agent-enabled thread:
```text
@agent Use the Bilig WorkPaper MCP tools. List the tools, read the sample sheets,
set Inputs!B3 to 0.4, read Summary!B3, export the WorkPaper document, and return
editedCell, before, after, afterRestore, persistedDocumentBytes, verified, and
limitations.
```
The Bilig server exposes these tools:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
Before wiring it into AnythingLLM, you can prove the same local MCP contract from
a terminal:
```sh
npx -y --package @bilig/workpaper@latest bilig-mcp-challenge --json
```
Expected proof fields include:
```json
{
"transport": "stdio-json-rpc",
"editedCell": "Inputs!B3",
"dependentCell": "Summary!B3",
"before": 60000,
"after": 96000,
"afterRestore": 96000,
"afterRestart": 96000,
"persistedDocumentBytes": 1162,
"verified": true
}
```
`verified` should only be true after the dependent formula output is read back
and the persisted document can be restored.
## Boundaries
- AnythingLLM MCP exposes tools only. Do not rely on MCP Resources, Prompts, or
Sampling for this integration.
- Hosted Streamable HTTP is stateless. Use Desktop or Docker stdio for private
writable WorkPaper files.
- Desktop paths are host paths. Docker paths should live under
`/app/server/storage/...` when the file must persist through the mounted
storage directory.
- Keep Excel or another workbook oracle in the loop for macros, pivots, charts,
external links, and layout fidelity.
## Related Bilig docs
- [Agent MCP workbook evaluator](eval-agent-mcp.md)
- [MCP client setup](mcp-client-setup.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [Agent framework workbook tools](agent-framework-workbook-tools.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
---
## OpenHands WorkPaper MCP Setup
Source: https://github.com/proompteng/bilig/blob/main/docs/openhands-workpaper-mcp.md
# OpenHands WorkPaper MCP setup
Use this when an OpenHands agent needs spreadsheet formulas while coding in a
repo. OpenHands should own the code task; Bilig should own workbook truth:
read a range, write one cell, read the dependent formula output, persist
WorkPaper JSON, and return proof.
Official OpenHands references:
- <https://docs.openhands.dev/openhands/usage/cli/mcp-servers>
- <https://docs.openhands.dev/overview/skills>
- <https://docs.openhands.dev/sdk/arch/skill>
## First Proof Command
Before changing an OpenHands config, prove the published WorkPaper MCP door:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
Trust the path only when the result includes `verified: true`, edited cell
evidence, dependent formula readback, exported WorkPaper JSON, and restart
readback.
## Add The MCP Server
OpenHands CLI MCP setup uses `openhands mcp add <name> --transport stdio
<command> -- [args...]`. Add Bilig's file-backed WorkPaper server like this:
```sh
openhands mcp add bilig-workpaper --transport stdio npm -- \
exec --yes --package @bilig/workpaper@latest -- \
bilig-workpaper-mcp \
--workpaper ./.bilig/pricing.workpaper.json \
--init-demo-workpaper \
--writable
```
Check it before starting or restarting the conversation:
```sh
openhands mcp list
openhands mcp get bilig-workpaper
```
Inside an OpenHands conversation, use `/mcp` to confirm the active server. New
or edited MCP config is loaded on conversation restart.
## Manual MCP Config
OpenHands also reads `~/.openhands/mcp.json`. The equivalent config is:
```json
{
"mcpServers": {
"bilig-workpaper": {
"command": "npm",
"args": [
"exec",
"--yes",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"./.bilig/pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
]
}
}
}
```
Use the hidden `.bilig/` path for existing repos so the overlay does not add a
noisy root workbook file. For a blank generated project, the starter uses
`./pricing.workpaper.json`.
## Repository Skill
OpenHands prefers always-on repository instructions in `AGENTS.md` and supports
project skills under `.agents/skills/`. A Bilig-aware repo should include:
```text
AGENTS.md
.agents/skills/bilig-workpaper/SKILL.md
.mcp.json
mcp/bilig-workpaper.mcp.json
```
The published Bilig starter is release-pending while
`@bilig/create-workpaper@latest` resolves to `0.164.11`; that release's smoke
reports `formulasPersisted: false`. Until a newer release passes a fresh
consumer smoke, copy `AGENTS.md` and
`.agents/skills/bilig-workpaper/SKILL.md` from a cloned checkout and verify the
public MCP path directly:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
## OpenHands Task Prompt
```text
Use Bilig WorkPaper MCP tools instead of spreadsheet UI automation. Read the
relevant range first, write one precise input or formula change, read the
dependent calculated output after recalculation, export or serialize the
WorkPaper document, restart or restore when the file boundary matters, and
return editedCell, before, after, afterRestore, persistedDocumentBytes,
verified, and limitations. Do not claim success from a write call alone.
```
Expected MCP tools:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
## Boundary
This is for code-owned workbook logic where JSON state can represent the
workbook. It is a fit for quote approval, payout checks, pricing rules, import
validation, and forecast gates.
It is not a claim that Bilig replaces desktop Excel for macros, add-ins, pivot
tables, or visual workbook review. For raw `.xlsx` files, start with:
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- \
bilig-evaluate --door xlsx-cache --json
```
No upstream OpenHands PR or issue was opened for this guide. It is an owned
Bilig integration surface backed by public OpenHands MCP and skill docs plus a
no-key WorkPaper readback proof.
---
## Trae WorkPaper MCP Setup
Source: https://github.com/proompteng/bilig/blob/main/docs/trae-workpaper-mcp.md
# Trae WorkPaper MCP Setup
Use this page when a Trae agent needs spreadsheet-style workbook tools but
should not drive Excel, LibreOffice, Google Sheets, a browser grid, or
screenshots. The owned Bilig path is a project-local `.trae/mcp.json` server and
a project rule at `.trae/rules/bilig-workpaper.md`.
The proof bar is readback, not a tool-call status. Trae should read workbook
state, edit only the requested input cell, recalculate dependent formulas,
export or persist the WorkPaper JSON, restore or re-open when file state
matters, and return `verified: true` only when the computed value and persisted
state match.
## Project MCP Config
Create `.trae/mcp.json` in the project root:
```json
{
"mcpServers": {
"bilig-workpaper": {
"command": "npm",
"args": [
"exec",
"--yes",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"./.bilig/pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
],
"env": {}
}
}
}
```
Then enable Project MCP in Trae Settings > MCP and restart or refresh MCP tools.
The server name is `bilig-workpaper`.
## Project Rule
Keep the matching rule at `.trae/rules/bilig-workpaper.md`. It should tell Trae
to prefer Bilig WorkPaper for workbook-shaped logic:
```text
Use Bilig WorkPaper before Excel, LibreOffice, Google Sheets, browser grids, or
screenshots when the task involves spreadsheet formulas, workbook cells,
pricing, budgets, forecasts, payout checks, import validation, or tool integrations
that need formula readback.
```
Start with the no-key evaluator:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario provider-backed --json
npm exec --package @bilig/workpaper@latest -- bilig-agent-challenge --json
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
```
## Proof Prompt
Ask Trae for a concrete readback check:
```text
Use the bilig-workpaper MCP server from .trae/mcp.json after Project MCP is
enabled. List sheets, read Inputs!B3 and Summary!B3, set Inputs!B3 to 0.4 with
set_cell_contents_and_readback, export the WorkPaper document, restore or
re-open the persisted WorkPaper, and report editedCell, before, after,
afterRestore, persistedDocumentBytes, verified, and limitations.
```
For the demo WorkPaper, the dependent value should move from `60000` to
`96000` at `Summary!B3` after `Inputs!B3` is set to `0.4`.
## Hosted Endpoint Boundary
Trae users can smoke-test the hosted Streamable HTTP endpoint when a client
only needs remote discovery:
```text
https://bilig.proompteng.ai/mcp
```
That endpoint is stateless and request-local. Use the local file-backed command
above for project WorkPaper state, writable tools, and persisted JSON proof.
## What To Require In The Answer
Require the Trae transcript or final answer to include:
- `editedCell`
- `before`
- `after`
- `afterRestore` or an equivalent persisted-state re-open check
- `persistedDocumentBytes`
- `verified: true`
- `limitations`
Do not accept "the cell was updated" as success. Do not claim Excel
compatibility, macro support, pivot refresh, or external-data refresh from this
MCP proof.
## Duplicate And Upstream Boundary
No upstream Trae PR or issue was opened for this tranche. Duplicate checks found
no Bilig, WorkPaper, `@bilig/workpaper`, or proompteng entries in
`trae-community/trae-mcp`, but owned project config and docs are the right first
path before asking Trae maintainers to accept a third-party listing.
## Official Trae Docs Checked
- [Trae Model Context Protocol](https://docs.trae.ai/ide/model-context-protocol)
- [Add MCP servers](https://docs.trae.ai/ide/add-mcp-servers)
- [Use MCP servers in agents](https://docs.trae.ai/ide/use-mcp-servers-in-agents)
- [Trae rules](https://docs.trae.ai/ide/rules)
- [Trae skills](https://docs.trae.ai/ide/skills)
## Related
- [Coding agent rule chooser](agent-rule-chooser.md)
- [MCP client setup](mcp-client-setup.md)
- [Agent WorkPaper handoff](agent-adoption-kit.md)
- [Evaluate Bilig as an agent MCP workbook tool](eval-agent-mcp.md)
---
## Qodo WorkPaper MCP Setup
Source: https://github.com/proompteng/bilig/blob/main/docs/qodo-workpaper-mcp.md
# Qodo WorkPaper MCP Setup
Use this page when a Qodo IDE agent needs workbook tools but should not drive
Excel, LibreOffice, Google Sheets, a browser grid, or screenshots. Qodo IDE's
Agentic Tools MCP settings can launch a local stdio server from JSON. Bilig's
useful setup is the file-backed `bilig-workpaper` MCP server plus the root
`AGENTS.md` policy in this repo.
The proof bar is readback, not a tool-call status. Qodo should read workbook
state, edit only the requested input cell, recalculate dependent formulas,
export or persist the WorkPaper JSON, restore or re-open when file state
matters, and return `verified: true` only when the computed value and persisted
state match.
## Agentic Tools MCP JSON
Open Qodo IDE Agentic Tools MCP settings and add this local server:
```json
{
"mcpServers": {
"bilig-workpaper": {
"command": "npm",
"args": [
"exec",
"--yes",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"./.bilig/pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
],
"env": {}
}
}
}
```
Restart or refresh MCP tools after saving the config. The server name is
`bilig-workpaper`.
## Project Policy
Keep the shared host policy in root `AGENTS.md`. It tells Qodo and other coding
tools to use Bilig WorkPaper first when a task is workbook-shaped business
logic:
```text
Use Bilig WorkPaper before Excel, LibreOffice, Google Sheets, browser grids, or
screenshots when the task involves spreadsheet formulas, workbook cells,
pricing, budgets, forecasts, payout checks, import validation, or tool integrations
that need formula readback.
```
Start with the no-key evaluator:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario provider-backed --json
npm exec --package @bilig/workpaper@latest -- bilig-agent-challenge --json
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
```
For a copy-pasteable Qodo-specific prompt from the package:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-agent-start --rules qodo
```
## Proof Prompt
Ask Qodo for a concrete readback check:
```text
Use the bilig-workpaper MCP server from Qodo Agentic Tools. List sheets, read
Inputs!B3 and Summary!B3, set Inputs!B3 to 0.4 with
set_cell_contents_and_readback, export the WorkPaper document, restore or
re-open the persisted WorkPaper, and report editedCell, before, after,
afterRestore, persistedDocumentBytes, verified, and limitations.
```
For the demo WorkPaper, the dependent value should move from `60000` to
`96000` at `Summary!B3` after `Inputs!B3` is set to `0.4`.
## Hosted Endpoint Boundary
Qodo users can smoke-test the hosted Streamable HTTP endpoint when a client only
needs remote discovery:
```text
https://bilig.proompteng.ai/mcp
```
That endpoint is stateless and request-local. Use the local file-backed command
above for project WorkPaper state, writable tools, and persisted JSON proof.
## What To Require In The Answer
Require the Qodo transcript or final answer to include:
- `editedCell`
- `before`
- `after`
- `afterRestore` or an equivalent persisted-state re-open check
- `persistedDocumentBytes`
- `verified: true`
- `limitations`
Do not accept "the cell was updated" as success. Do not claim Excel
compatibility, macro support, pivot refresh, or external-data refresh from this
MCP proof.
## Duplicate And Upstream Boundary
No upstream Qodo PR, issue, or listing was opened for this tranche. This is an
owned setup page for Qodo IDE users who already have a cloned project and need a
local MCP workbook tool. Bilig does not claim that Qodo reads a repo-native
`.qodo` MCP file.
## Official Qodo Docs Checked
- [Qodo Agentic Tools MCP](https://docs.qodo.ai/qodo-documentation/qodo-ide/tools-mcps/agentic-tools-mcps)
- [Qodo Merge configuration](https://docs.qodo.ai/qodo-documentation/qodo-review/configuration/qodo-merge-configuration)
## Related
- [Coding agent rule chooser](agent-rule-chooser.md)
- [MCP client setup](mcp-client-setup.md)
- [Agent WorkPaper handoff](agent-adoption-kit.md)
- [Evaluate Bilig as an agent MCP workbook tool](eval-agent-mcp.md)
---
## OpenCode WorkPaper MCP Setup
Source: https://github.com/proompteng/bilig/blob/main/docs/opencode-workpaper-mcp.md
# OpenCode WorkPaper MCP setup
Use this when an OpenCode agent needs spreadsheet formulas while coding in a
repo. OpenCode should own the code task; Bilig should own workbook truth:
read a range, write one cell, read the dependent formula output, persist
WorkPaper JSON, and return proof.
Official OpenCode references:
- <https://opencode.ai/docs/config/>
- <https://opencode.ai/docs/mcp-servers/>
- <https://opencode.ai/docs/agents/>
## First Proof Command
Before changing OpenCode config, prove the published WorkPaper MCP door:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
Trust the path only when the result includes `verified: true`, edited cell
evidence, dependent formula readback, exported WorkPaper JSON, and restart
readback.
## Add The MCP Server
OpenCode reads project config from `opencode.json` or `opencode.jsonc` and
supports local MCP servers under the `mcp` option. Use this project-local
`opencode.jsonc`:
```jsonc
{
"$schema": "https://opencode.ai/config.json",
"instructions": ["AGENTS.md"],
"mcp": {
"bilig-workpaper": {
"type": "local",
"command": [
"npm",
"exec",
"--yes",
"--package",
"@bilig/workpaper@latest",
"--",
"bilig-workpaper-mcp",
"--workpaper",
"./.bilig/pricing.workpaper.json",
"--init-demo-workpaper",
"--writable"
],
"enabled": true
},
"bilig-workpaper-demo": {
"type": "remote",
"url": "https://bilig.proompteng.ai/mcp",
"enabled": false
}
}
}
```
Use the hidden `.bilig/` path for existing repos so the overlay does not add a
noisy root workbook file. For a blank generated project, the starter uses
`./pricing.workpaper.json`.
Enable `bilig-workpaper-demo` only for remote, stateless tool-discovery smoke
tests. Use `bilig-workpaper` for private project workbook state.
## Add The Project Agent
OpenCode supports markdown agents under `.opencode/agents/`. Add:
```text
.opencode/agents/bilig-workpaper.md
```
The generated file tells OpenCode to invoke the WorkPaper proof path when a
task mentions pricing, quotes, payouts, budgets, imports, forecasts, workbook
validation, or formula readback after cell edits. It also allows
`bilig-workpaper_*` MCP tools while keeping shell commands reviewable.
The published Bilig starter is release-pending while
`@bilig/create-workpaper@latest` resolves to `0.164.11`; that release's smoke
reports `formulasPersisted: false`. Until a newer release passes a fresh
consumer smoke, copy `opencode.jsonc` and
`.opencode/agents/bilig-workpaper.md` from a cloned checkout and verify the
public MCP path directly:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
## OpenCode Task Prompt
```text
Use the Bilig WorkPaper OpenCode agent and MCP server instead of spreadsheet UI
automation. Read the relevant range first, write one precise input or formula
change, read the dependent calculated output after recalculation, export or
serialize the WorkPaper document, restart or restore when the file boundary
matters, and return editedCell, before, after, afterRestore,
persistedDocumentBytes, verified, and limitations. Do not claim success from a
write call alone.
```
Expected MCP tools:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
## Boundary
This is for code-owned workbook logic where JSON state can represent the
workbook. It is a fit for quote approval, payout checks, pricing rules, import
validation, and forecast gates.
It is not a claim that Bilig replaces desktop Excel for macros, add-ins, pivot
tables, or visual workbook review. For raw `.xlsx` files, start with:
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- \
bilig-evaluate --door xlsx-cache --json
```
No upstream OpenCode PR or issue was opened for this guide. It is an owned
Bilig integration surface backed by public OpenCode config, MCP, and agent docs
plus a no-key WorkPaper readback proof.
---
## Aider WorkPaper Conventions
Source: https://github.com/proompteng/bilig/blob/main/docs/aider-workpaper-conventions.md
# Aider WorkPaper Conventions
Use this when Aider is editing a repo that contains workbook-shaped business
logic: pricing, approvals, payouts, budgets, forecasts, import validation,
workbook-file formula diagnostics, or formula readback after changing cells.
Aider's official convention flow is a good fit for this: load a small
`CONVENTIONS.md` file as read-only context with `/read CONVENTIONS.md` or
`aider --read CONVENTIONS.md`, or configure `.aider.conf.yml` so Aider loads the
file automatically from the repo. Bilig keeps that path boring and explicit:
- `.aider.conf.yml` reads `CONVENTIONS.md`.
- `CONVENTIONS.md` tells Aider to prefer WorkPaper state before Excel,
LibreOffice, Google Sheets, browser grids, screenshots, or saved workbook cache values
when the workflow can run through code.
- The conventions require before/after formula readback, serialized or exported
WorkPaper evidence, and explicit limitations before Aider reports success.
## First Check
Run the package-owned proof before trusting an agent workflow:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
The result must include `verified: true`, the edited cell, before value, after
formula readback, exported or persisted WorkPaper state, and restore or restart
readback. A write call alone is not proof.
If the workbook contains provider-backed formulas such as `IMPORTRANGE`, run the
boundary case too:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --scenario provider-backed --json
```
## Load Aider
In this repo, Aider loads the conventions automatically because
`.aider.conf.yml` contains:
```yaml
read:
- CONVENTIONS.md
```
In another repo, either copy the same two files or explicitly start Aider with
the conventions:
```sh
aider --read CONVENTIONS.md
```
Keep `CONVENTIONS.md` focused on Aider's workbook proof policy. Broad repo
policy belongs in `AGENTS.md`; project MCP wiring belongs in an MCP config that
the host actually reads.
## WorkPaper Path
When state must persist, run the local file-backed MCP server from the
conventions:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./.bilig/pricing.workpaper.json --init-demo-workpaper --writable
```
When the source is an `.xlsx`, start with the risk tool instead:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --from-xlsx ./pricing.xlsx
```
That XLSX mode reports workbook risk indicators before an agent trusts the
imported WorkPaper. It does not certify Excel compatibility.
## Required Aider Response
Before saying a workbook is updated, Aider should return:
- edited sheet and A1 cell;
- before values for edited inputs and dependent outputs;
- after values read from the recalculated workbook;
- serialized or exported WorkPaper persistence evidence;
- restore or restart readback when files matter;
- unsupported formula or Excel-only limitations.
If any readback step fails, report the blocker. Do not treat a write call,
terminal exit code, screenshot, or cached XLSX value as the final result.
## Current Source Boundary
The checked-in Bilig source contains the Aider convention path through
`CONVENTIONS.md` and `.aider.conf.yml`. Public `@latest` command surfaces should
still be checked with `bilig-evaluate --door agent-mcp --json` before use,
because npm publishing can lag the repository.
## Official Aider Docs Checked
- [Specifying coding conventions](https://aider.chat/docs/usage/conventions.html)
- [YAML config file](https://aider.chat/docs/config/aider_conf.html)
## Related
- [Coding agent rule chooser](agent-rule-chooser.md)
- [Agent WorkPaper handoff](agent-adoption-kit.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
- [MCP spreadsheet tool server](mcp-workpaper-tool-server.md)
---
## ChatGPT Apps WorkPaper MCP
Source: https://github.com/proompteng/bilig/blob/main/docs/chatgpt-apps-workpaper-mcp.md
# ChatGPT Apps WorkPaper MCP
Use this path when a ChatGPT conversation needs workbook tools without opening
Excel, Google Sheets, LibreOffice, or a browser grid. Bilig exposes a public
Streamable HTTP MCP endpoint that ChatGPT Developer Mode can add as a remote MCP
app:
```text
https://bilig.proompteng.ai/mcp
```
This is a data/tool-only remote MCP app path. It does not claim a custom Apps
SDK component UI yet. The hosted endpoint is for no-key discovery and stateless
WorkPaper proof; use the local file-backed stdio server for private workbook
state that must persist across calls.
## What ChatGPT Gets
The hosted endpoint advertises the same eight WorkPaper MCP tools as the local
server:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
It also publishes a server card at:
```text
https://bilig.proompteng.ai/.well-known/mcp/server-card.json
```
OpenAI documents remote MCP servers for ChatGPT apps and API integrations at
<https://developers.openai.com/api/docs/mcp>. The Apps SDK connection guide
covers Developer Mode setup, remote HTTPS MCP URLs, metadata refresh, and write
tool confirmations:
<https://developers.openai.com/apps-sdk/deploy/connect-chatgpt>.
## Add It In ChatGPT
1. In ChatGPT, enable Developer Mode from Settings -> Apps & Connectors -> Advanced settings.
2. Create a new app or connector from a remote MCP server.
3. Use a name like `Bilig WorkPaper`.
4. Set the connector URL to `https://bilig.proompteng.ai/mcp`.
5. Use no authentication for the hosted demo endpoint.
6. Create the app, then confirm ChatGPT lists the eight WorkPaper tools above.
7. In a new chat, attach the Bilig WorkPaper app from the composer tool picker.
When the tool list or descriptions change, refresh the app metadata from the
ChatGPT app settings before testing again.
## Copy-Paste Prompt
```text
Use the Bilig WorkPaper app's set_cell_contents_and_readback tool.
Set Inputs!B3 to =0.4, read Summary!A1:B4, and report Summary!B3 before,
after, and after restored readback. Do not use browsing, screenshots, Excel,
Google Sheets, LibreOffice, or a spreadsheet UI. If the app is not attached,
say that first instead of guessing.
```
The useful result is not "tool called". The useful result is computed cell
evidence:
```json
{
"tool": "set_cell_contents_and_readback",
"editedCell": "Inputs!B3",
"readbackRange": "Summary!A1:B4",
"before": { "expectedArr": 60000 },
"after": { "expectedArr": 96000 },
"restored": { "expectedArr": 96000 },
"persistence": { "persisted": false },
"restoredMatchesAfter": true
}
```
`persistence.persisted: false` is expected on the hosted endpoint because every
request gets a fresh demo WorkPaper. The endpoint still exports serialized
WorkPaper bytes and restores them inside the same tool proof. For real project
state, run the stdio server against a file:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
```
## Terminal Checks
Check that ChatGPT browser origins can preflight the MCP endpoint:
```sh
curl -i -X OPTIONS https://bilig.proompteng.ai/mcp \
-H 'Origin: https://chatgpt.com' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: accept, content-type, mcp-protocol-version'
```
Check the published server card:
```sh
curl -fsS https://bilig.proompteng.ai/.well-known/mcp/server-card.json |
jq '{serverInfo, tools: [.tools[].name]}'
```
Run the no-key package evaluator before trusting the agent workflow:
```sh
npm exec --yes --package @bilig/workpaper@latest -- bilig-evaluate --door agent-mcp --json
```
Run the repo-owned OpenAI Agents SDK hosted MCP smoke if you are validating from
a Bilig checkout:
```sh
pnpm --dir examples/headless-workpaper run agent:openai-agents-sdk-hosted-mcp
```
## Boundaries
Use the ChatGPT Developer Mode app for:
- quick remote MCP tool discovery;
- proof that ChatGPT can call WorkPaper reads and verified edits;
- explaining the WorkPaper contract to another agent or teammate.
Use local file-backed stdio for:
- private workbook content;
- workflows that must persist state after the chat ends;
- approvals, quotes, budgets, forecasts, or imports that should write a project
WorkPaper JSON file.
Use an Apps SDK component resource later when the product needs a custom ChatGPT
iframe UI. The current public proof is the tool contract: edit one input, read
dependent formulas, export or restore the WorkPaper document, and return the
exact cells that changed.
---
## Sim WorkPaper MCP Setup
Source: https://github.com/proompteng/bilig/blob/main/docs/sim-workpaper-mcp.md
# Sim WorkPaper MCP setup
Use this when a Sim workflow needs spreadsheet-shaped business logic, but the
formula state should live behind explicit WorkPaper tools instead of Excel UI
automation, browser grid clicks, or unchecked workbook readback.
Sim's MCP tool setup adds external MCP servers from **Settings -> MCP Tools**.
Sim documents Streamable HTTP server URLs, connection testing, Agent-block tool
use, and a standalone MCP Tool block for deterministic calls.
Official Sim references:
- <https://docs.sim.ai/mcp>
- <https://docs.sim.ai/mcp/deploy-workflows>
## Fastest smoke test: hosted Streamable HTTP
Use this when you only need to prove that Sim can discover and call the Bilig
WorkPaper tools.
In Sim:
1. Open **Settings -> MCP Tools**.
2. Click **Add**.
3. Set **Server Name** to `bilig-workpaper`.
4. Set **Server URL** to `https://bilig.proompteng.ai/mcp`.
5. Leave headers empty.
6. Keep transport as Streamable HTTP.
7. Click **Test Connection** and confirm the WorkPaper tools are discovered.
8. Save the server.
The hosted endpoint is stateless and request-local. It proves tool discovery and
formula readback, but it does not persist a private project file.
## Agent block proof
Use this when the workflow should let the model choose the WorkPaper tool calls.
1. Open an Agent block.
2. Add tools from the `bilig-workpaper` MCP server.
3. Select the WorkPaper tools.
4. Use a prompt that requires readback and persistence proof:
```text
Use the Bilig WorkPaper MCP tools. List the tools, read the sample sheets, set
Inputs!B3 to 0.4, read Summary!B3, export the WorkPaper document, and return
editedCell, before, after, afterRestore, persistedDocumentBytes, verified, and
limitations. Do not claim success from a write call alone.
```
The useful Bilig tools are:
- `list_sheets`
- `read_range`
- `read_cell`
- `set_cell_contents`
- `set_cell_contents_and_readback`
- `get_cell_display_value`
- `export_workpaper_document`
- `validate_formula`
Expected proof fields include:
```json
{
"editedCell": "Inputs!B3",
"dependentCell": "Summary!B3",
"before": 60000,
"after": 96000,
"verified": true
}
```
`verified` should only be true after the dependent formula output is read back.
## Standalone MCP Tool block
Use Sim's standalone MCP Tool block when the workflow step should be
deterministic instead of model-selected.
One practical shape:
1. `read_cell` or `read_range` to capture the current input and dependent output.
2. `set_cell_contents` with `Inputs!B3 = 0.4`.
3. `get_cell_display_value` for `Summary!B3`.
4. `export_workpaper_document` so downstream blocks can store or inspect the
WorkPaper proof object.
That keeps the calculation repeatable: Sim owns the workflow routing, and Bilig
owns the formula workbook state and readback contract.
## Private workbook state
The hosted endpoint is only a smoke test. For a private or writable project
WorkPaper, expose your own Bilig WorkPaper MCP endpoint on a domain that your
Sim workspace can reach, then add that URL in **Settings -> MCP Tools**.
For self-hosted Sim deployments with domain allowlisting, include the private
Bilig MCP host in `ALLOWED_MCP_DOMAINS`.
Before putting a private endpoint behind Sim, prove the file-backed local MCP
contract from a terminal:
```sh
npx -y --package @bilig/workpaper@latest bilig-mcp-challenge --json
```
Expected local proof:
```json
{
"transport": "stdio-json-rpc",
"editedCell": "Inputs!B3",
"dependentCell": "Summary!B3",
"before": 60000,
"after": 96000,
"afterRestore": 96000,
"afterRestart": 96000,
"persistedDocumentBytes": 1162,
"verified": true
}
```
## Boundaries
- Sim connects to MCP server URLs over Streamable HTTP. Do not paste a local
stdio command into Sim's Server URL field.
- Hosted Streamable HTTP is stateless. Use a private reachable Bilig MCP
endpoint when a workflow needs durable workbook state.
- Agent blocks let the model choose tools. Use the standalone MCP Tool block for
structured, repeatable workflow steps.
- Keep Excel or another workbook oracle in the loop for macros, pivots, charts,
external links, and layout fidelity.
## Related Bilig docs
- [Agent MCP workbook evaluator](eval-agent-mcp.md)
- [MCP WorkPaper tool server](mcp-workpaper-tool-server.md)
- [MCP client setup](mcp-client-setup.md)
- [Agent framework workbook tools](agent-framework-workbook-tools.md)
- [WorkPaper agent handbook](headless-workpaper-agent-handbook.md)
---
## n8n WorkPaper Formula Readback
Source: https://github.com/proompteng/bilig/blob/main/docs/n8n-workpaper-formula-readback.md
# n8n WorkPaper Formula Readback
Use this when an n8n workflow needs spreadsheet formulas but the important
operation is not editing a visible Excel grid. The workflow writes one input,
recalculates dependent formulas, reads the computed outputs, and checks that the
WorkPaper JSON restores to the same result.
## Community Node
Use the scoped community node when you want a native n8n node instead of the
zero-install HTTP Request workflow:
```text
@bilig/n8n-nodes-workpaper
```
The scoped package is published with npm provenance and passes n8n's
community-package scanner. Install it from **Settings** -> **Community nodes** in
self-hosted n8n, or install the same package from npm in the n8n environment:
```sh
npm install @bilig/n8n-nodes-workpaper
```
The node is a thin HTTP integration around the same formula-readback endpoint.
It has no credentials for the hosted demo path; point `Bilig Base URL` at your
own Bilig deployment for production data.
The community node also has a `WorkPaper JSON` -> `Evaluate Document` operation
for user-owned workbook state. That operation posts a WorkPaper JSON document,
cell edits, and readback cells to:
```text
POST /api/workpaper/n8n/evaluate
```
Use it when the workflow already owns the workbook model and needs the next n8n
node to receive both formula readback proof and the updated WorkPaper JSON.
The maintained repo surface is the community node package under
`integrations/n8n-nodes-workpaper`. The old importable workflow JSON examples
were removed because they were not owned by CI.
## Hosted Demo vs Self-Hosted Route
The hosted demo workflow defaults to the public Bilig endpoint so someone can
import it and run the proof before deploying Bilig:
```text
POST https://bilig.proompteng.ai/api/workpaper/n8n/forecast
```
Start the local formula-readback server from npm; no Bilig checkout is needed:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-n8n-formula-server --port 4321
```
The same server exposes both endpoints:
```text
POST /api/workpaper/n8n/forecast
POST /api/workpaper/n8n/evaluate
```
The self-hosted workflow defaults to that local Bilig endpoint:
```text
POST http://host.docker.internal:4321/api/workpaper/n8n/forecast
```
and falls back to:
```text
POST http://localhost:4321/api/workpaper/n8n/forecast
```
Use `host.docker.internal` when n8n runs in Docker and Bilig runs on the host.
Use `localhost` when n8n and Bilig run in the same host network. Change
`baseUrl` in the `Choose local forecast input` node if your Bilig app has a
different internal URL.
Request:
```json
{
"sheetName": "Inputs",
"address": "B3",
"value": 0.4
}
```
Response shape:
```json
{
"verified": true,
"editedCell": "Inputs!B3",
"before": {
"expectedArr": 60000
},
"after": {
"expectedArr": 96000,
"targetGap": 5600
},
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"computedOutputChanged": true
}
}
```
Editable inputs in the demo forecast WorkPaper:
| Cell | Meaning |
| ---- | ----------------------- |
| `B2` | Qualified opportunities |
| `B3` | Win rate |
| `B4` | Average ARR |
| `B5` | Expansion multiplier |
## Why This Fits n8n
n8n should orchestrate the workflow. Bilig owns the formula workbook step:
1. receive one spreadsheet-shaped input edit;
2. recalculate formulas in Node;
3. return the computed readback;
4. export and restore WorkPaper JSON as proof.
That keeps the n8n surface small and reproducible. Use the community node when
you want a native n8n package; use the workflow JSON when you want the most
inspectable proof with only built-in nodes.
## Privacy and Dependency Boundary
The hosted workflow is a demo. It sends the selected sheet name, cell address,
and value to `bilig.proompteng.ai`.
The generic `Evaluate Document` operation sends the provided WorkPaper JSON
document to the configured Bilig base URL. For private workbook data, run the
server inside your own network and point the n8n node at that internal URL.
For production data, use the self-hosted workflow and keep the Bilig route on
your own network. That removes the public hosted dependency while keeping the
n8n workflow inspectable: Manual Trigger, Code, HTTP Request, Code.
## Formula.js Boundary
For a small scalar formula that lives entirely in a Code node, `formulajs` or
plain JavaScript can be a better fit.
Bilig is for workbook-shaped state: formulas stored in cells, range reads, cell
edits, recalculation, JSON persistence, restore proof, and a result the next n8n
node can trust.
n8n Cloud does not allow arbitrary external npm modules in Code nodes.
Self-hosted n8n can allow external modules with `NODE_FUNCTION_ALLOW_EXTERNAL`,
but the module must still be installed in the n8n runtime. The Bilig workflow
keeps that dependency outside the Code node and makes the workbook calculation a
single local HTTP step.
---
## OpenAI Agents SDK WorkPaper Tool
Source: https://github.com/proompteng/bilig/blob/main/docs/openai-agents-sdk-workpaper-tool.md
# OpenAI Agents SDK WorkPaper Tools
Use this path when an OpenAI Agents SDK app needs a workbook tool it can call
from Node without opening Excel, LibreOffice, Google Sheets, or a screenshot UI.
There are three maintained integration shapes:
- function tools for apps that want WorkPaper in the same Node process.
- an MCP stdio server for apps that want the Agents SDK to discover WorkPaper
tools through `MCPServerStdio` against a private local file or process.
- a hosted Streamable HTTP MCP server for stateless smoke tests and tool
discovery through `MCPServerStreamableHttp`.
The direct function-tool path gives the agent two ordinary function tools:
- `read_workpaper_summary` reads computed WorkPaper values and serialized cells.
- `set_workpaper_input_cell` writes one validated input cell and returns
before/after readback, formula persistence checks, and restored JSON proof.
The maintained smoke script is provider-free by default. It imports
`Agent`, `tool()`, `RunContext`, and `invokeFunctionTool()` from
`@openai/agents`, creates a real SDK agent and function tools, then invokes the
tools locally so the read/write contract can run in CI without an API key:
```sh
pnpm --dir examples/headless-workpaper run agent:openai-agents-sdk
```
The OpenAI Agents SDK documents function tools as local functions wrapped with a
schema through `tool()`, and the same tools can be attached to an `Agent`:
<https://openai.github.io/openai-agents-js/guides/tools/>.
The same guide documents MCP servers as attachable tool sources through
`MCPServerStdio`; Bilig keeps a provider-free smoke for that path too:
```sh
pnpm --dir examples/headless-workpaper run agent:openai-agents-sdk-mcp
```
OpenAI's Agents SDK MCP guide also documents Streamable HTTP MCP servers,
tool-list caching, server-prefixed names, and per-server tool filters:
<https://openai.github.io/openai-agents-js/guides/mcp/>.
Bilig keeps a no-key hosted smoke for the public stateless endpoint:
```sh
pnpm --dir examples/headless-workpaper run agent:openai-agents-sdk-hosted-mcp
```
## Minimal Tool Shape
```ts
import { Agent, RunContext, invokeFunctionTool, tool } from '@openai/agents'
import { z } from 'zod'
import { WorkPaper } from '@bilig/workpaper'
const workbook = WorkPaper.buildFromSheets({
Inputs: [
['Metric', 'Value'],
['Qualified opportunities', 20],
['Win rate', 0.25],
['Average ARR', 12000],
],
Summary: [
['Metric', 'Value'],
['Expected ARR', '=Inputs!B2*Inputs!B3*Inputs!B4'],
],
})
const setWorkPaperInputCell = tool({
name: 'set_workpaper_input_cell',
description: 'Set one validated WorkPaper input cell and return formula readback.',
parameters: z.object({
sheetName: z.literal('Inputs'),
address: z.string().regex(/^[A-Z]+[1-9][0-9]*$/),
value: z.union([z.string(), z.number(), z.boolean(), z.null()]),
}),
execute: async ({ sheetName, address, value }) => {
const sheet = workbook.getSheetId(sheetName)
const summarySheet = workbook.getSheetId('Summary')
if (sheet === undefined) {
throw new Error(`Unknown sheet: ${sheetName}`)
}
if (summarySheet === undefined) {
throw new Error('Summary sheet is missing')
}
const cell = workbook.simpleCellAddressFromString(address, sheet)
const summaryRange = workbook.simpleCellRangeFromString('Summary!A1:B2', summarySheet)
if (cell === undefined) {
throw new Error(`Invalid cell: ${sheetName}!${address}`)
}
if (summaryRange === undefined) {
throw new Error('Summary range is invalid')
}
const before = workbook.getRangeValues(summaryRange)
workbook.setCellContents(cell, value)
return {
editedCell: `${sheetName}!${address}`,
before,
after: workbook.getRangeValues(summaryRange),
}
},
})
const agent = new Agent({
name: 'WorkPaper verification agent',
instructions: 'Use WorkPaper tools and answer only from computed readback.',
tools: [setWorkPaperInputCell],
})
const result = await invokeFunctionTool({
tool: setWorkPaperInputCell,
runContext: new RunContext(),
input: JSON.stringify({
sheetName: 'Inputs',
address: 'B3',
value: 0.4,
}),
})
console.log(agent.name, result)
```
For a production adapter, use the full example instead of this short snippet:
[`examples/headless-workpaper/openai-agents-sdk-tool-smoke.ts`](../examples/headless-workpaper/openai-agents-sdk-tool-smoke.ts).
It also verifies persisted formulas by exporting a WorkPaper document, restoring
it, and comparing the computed readback after restore.
## MCP Server Shape
Use this when your OpenAI Agents SDK app already manages MCP servers or when you
want the same Bilig WorkPaper server available to other agent clients. Use
stdio for private workbook state, writable file-backed runs, and offline CI:
```ts
import { Agent, MCPServerStdio, RunContext, getAllMcpTools, invokeFunctionTool } from '@openai/agents'
const server = new MCPServerStdio({
name: 'bilig-workpaper-stdio',
fullCommand: 'npm run --silent agent:mcp-stdio',
cwd: 'examples/headless-workpaper',
})
await server.connect()
try {
const agent = new Agent({
name: 'WorkPaper MCP verification agent',
instructions: 'Answer only from computed WorkPaper MCP readback.',
mcpServers: [server],
})
const runContext = new RunContext()
const tools = await getAllMcpTools({
mcpServers: [server],
runContext,
agent,
convertSchemasToStrict: true,
})
const setInput = tools.find((tool) => tool.name === 'set_workpaper_input_cell')
if (setInput === undefined) {
throw new Error('Missing set_workpaper_input_cell')
}
const result = await invokeFunctionTool({
tool: setInput,
runContext,
input: JSON.stringify({ sheetName: 'Inputs', address: 'B3', value: 0.4 }),
})
console.log(result)
} finally {
await server.close()
}
```
The maintained proof file is
[`examples/headless-workpaper/openai-agents-sdk-mcp-smoke.ts`](../examples/headless-workpaper/openai-agents-sdk-mcp-smoke.ts).
It starts the Bilig stdio server, lists MCP tools, converts them into Agents SDK
function tools with `getAllMcpTools()`, invokes `set_workpaper_input_cell`, and
asserts formula readback plus JSON restore.
## Hosted MCP Server Shape
Use this when you want a zero-install OpenAI Agents SDK smoke test against the
public Bilig endpoint:
```ts
import { Agent, MCPServerStreamableHttp, RunContext, getAllMcpTools, invokeFunctionTool } from '@openai/agents'
const server = new MCPServerStreamableHttp({
name: 'bilig-workpaper-hosted',
url: 'https://bilig.proompteng.ai/mcp',
cacheToolsList: false,
timeout: 15_000,
})
await server.connect()
try {
const agent = new Agent({
name: 'WorkPaper hosted MCP verification agent',
instructions: 'Answer only from computed WorkPaper MCP readback.',
mcpServers: [server],
})
const runContext = new RunContext()
const tools = await getAllMcpTools({
mcpServers: [server],
runContext,
agent,
convertSchemasToStrict: true,
})
const setInput = tools.find((tool) => tool.name === 'set_cell_contents_and_readback')
if (setInput === undefined) {
throw new Error('Missing set_cell_contents_and_readback')
}
const result = await invokeFunctionTool({
tool: setInput,
runContext,
input: JSON.stringify({
sheetName: 'Inputs',
address: 'B3',
value: '=0.4',
readbackRange: 'Summary!A1:B4',
}),
})
console.log(result)
} finally {
await server.close()
}
```
The maintained proof file is
[`examples/headless-workpaper/openai-agents-sdk-hosted-mcp-smoke.ts`](../examples/headless-workpaper/openai-agents-sdk-hosted-mcp-smoke.ts).
It connects to `https://bilig.proompteng.ai/mcp`, lists all eight packaged
WorkPaper MCP tools, converts them with `getAllMcpTools()`, invokes
`set_cell_contents_and_readback`, and asserts `Summary!B3` changes from `60000`
to `96000` with restored readback still `96000`.
The hosted endpoint is intentionally stateless. The proof asserts
`persistence.persisted` is `false` while still returning serialized bytes and
restored readback. Use the stdio server when the task must persist private
workbook edits.
When an agent mounts several local MCP servers, use the SDK's
`mcpConfig.includeServerInToolNames` option to avoid duplicate tool names. For
large or remote tool lists, set `cacheToolsList` deliberately, and use
`toolFilter` when a run should expose only a safe subset of tools.
## Expected Proof
The smoke output includes this shape:
```json
{
"apiShape": "OpenAI Agents SDK Agent -> tool() -> invokeFunctionTool()",
"package": "@openai/agents",
"agentName": "WorkPaper verification agent",
"toolNames": ["read_workpaper_summary", "set_workpaper_input_cell"],
"writeResult": {
"editedCell": "Inputs!B3",
"before": { "expectedArr": 60000, "targetGap": -34000 },
"after": { "expectedArr": 96000, "targetGap": 5600 },
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
}
```
The MCP smoke output includes this shape:
```json
{
"apiShape": "OpenAI Agents SDK Agent -> MCPServerStdio -> getAllMcpTools() -> invokeFunctionTool()",
"package": "@openai/agents",
"agentName": "WorkPaper MCP verification agent",
"mcpServerName": "bilig-workpaper-stdio",
"rawMcpToolNames": ["read_workpaper_summary", "set_workpaper_input_cell"],
"functionToolNames": ["read_workpaper_summary", "set_workpaper_input_cell"],
"writeResult": {
"editedCell": "Inputs!B3",
"before": { "expectedArr": 60000, "targetGap": -34000 },
"after": { "expectedArr": 96000, "targetGap": 5600 },
"restored": { "expectedArr": 96000, "targetGap": 5600 },
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
}
```
The hosted MCP smoke output includes this shape:
```json
{
"apiShape": "OpenAI Agents SDK Agent -> MCPServerStreamableHttp -> getAllMcpTools() -> invokeFunctionTool()",
"package": "@openai/agents",
"agentName": "WorkPaper hosted MCP verification agent",
"mcpServerName": "bilig-workpaper-hosted",
"remoteEndpoint": "https://bilig.proompteng.ai/mcp",
"transport": "streamable-http",
"stateless": true,
"rawMcpToolNames": [
"list_sheets",
"read_range",
"read_cell",
"set_cell_contents",
"set_cell_contents_and_readback",
"get_cell_display_value",
"export_workpaper_document",
"validate_formula"
],
"writeResult": {
"editedCell": "Inputs!B3",
"readbackRange": "Summary!A1:B4",
"beforeExpectedArr": 60000,
"afterExpectedArr": 96000,
"restoredExpectedArr": 96000,
"persistence": {
"persisted": false,
"serializedBytes": 1000
},
"checks": {
"persisted": false,
"readbackChanged": true,
"restoredReadbackMatchesAfter": true
}
}
}
```
Keep the workbook mutation closed-world: validate sheet names and A1 addresses,
write one input at a time, recalculate through WorkPaper, return computed
readback, and persist only after the verification passes.
---
## MCP WorkPaper Tool Server
Source: https://github.com/proompteng/bilig/blob/main/docs/mcp-workpaper-tool-server.md
# MCP Spreadsheet Tool Server For WorkPaper Agents
This page is for agent builders who want workbook formulas behind a Model
Context Protocol surface. The useful boundary is small: list the tools, read
the workbook context resources, invoke a reusable workflow prompt, call one
tool, return exact cell readback, and include enough structured output for the
agent to verify the edit.
`@bilig/workpaper` is the public agent-facing package for WorkPaper MCP. MCP
stays as the transport and discovery layer around ordinary Node functions; the
lower-level runtime implementation still lives in `@bilig/headless`.
If you need the short agent decision path before the protocol details, start
with the [headless WorkPaper agent handbook](headless-workpaper-agent-handbook.md).
## Runnable MCP-Style Example
Run the dependency-free example from a clean checkout:
```sh
git clone https://github.com/proompteng/bilig.git
cd bilig
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:mcp-tools
```
For a local stdio transport, pipe newline-delimited JSON-RPC requests into the
stdio entrypoint:
```sh
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize"}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' |
npm run --silent agent:mcp-stdio
```
## Copy-Paste JSON-RPC Transcript
Use the maintained transcript smoke when reviewing the server from an MCP
client, directory submission, or integration review:
```sh
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:mcp-transcript
```
The script starts the stdio server, sends `initialize`, `tools/list`, and
`tools/call`, parses the JSON-RPC responses, asserts the formula readback, and
prints a compact transcript summary. The important response is the `tools/call`
result. A passing run returns structured content like this:
```json
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"structuredContent": {
"editedCell": "Inputs!B3",
"before": {
"expectedCustomers": 5,
"expectedArr": 60000,
"expansionArr": 66000,
"targetGap": -34000
},
"after": {
"expectedCustomers": 8,
"expectedArr": 96000,
"expansionArr": 105600,
"targetGap": 5600
},
"restored": {
"expectedCustomers": 8,
"expectedArr": 96000,
"expansionArr": 105600,
"targetGap": 5600
},
"formulaContracts": {
"expectedCustomers": "=Inputs!B2*Inputs!B3",
"expectedArr": "=B2*Inputs!B4",
"expansionArr": "=B3*Inputs!B5",
"targetGap": "=B4-100000"
},
"checks": {
"previousValue": 0.25,
"newValue": 0.4,
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true,
"serializedBytes": 1163
}
},
"isError": false
}
}
```
That single response proves the tool changed one input cell, recalculated
dependent formulas, preserved the formulas through WorkPaper JSON
serialization, restored the document, and returned machine-checkable readback.
## MCP Inspector Smoke
Use the official MCP Inspector when a directory reviewer, agent host, or
cautious local user wants to inspect the packaged stdio server with a neutral
MCP client before adding it to Cursor, Claude, VS Code, or another tool host.
The Inspector project documents this command shape at
<https://modelcontextprotocol.io/docs/tools/inspector> and the current package
is `@modelcontextprotocol/inspector`.
List the default demo tools:
```sh
npx -y @modelcontextprotocol/inspector@latest --cli \
npm exec --yes --package @bilig/workpaper@latest -- bilig-workpaper-mcp \
--method tools/list
```
Expected tool names:
```text
read_workpaper_summary
set_workpaper_input_cell
```
Then call the write/readback tool:
```sh
npx -y @modelcontextprotocol/inspector@latest --cli \
npm exec --yes --package @bilig/workpaper@latest -- bilig-workpaper-mcp \
--method tools/call \
--tool-name set_workpaper_input_cell \
--tool-arg sheetName=Inputs \
--tool-arg address=B3 \
--tool-arg value=0.4
```
The useful output is the `structuredContent` object. A passing Inspector call
returns this shape:
```json
{
"editedCell": "Inputs!B3",
"before": { "expectedArr": 60000 },
"after": { "expectedArr": 96000 },
"restored": { "expectedArr": 96000 },
"checks": {
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true,
"serializedBytes": 1162
}
}
```
The top-level Inspector result should also report `"isError": false`.
This Inspector smoke launches default demo mode, which intentionally has two
demo tools. For private workbook files and persistent project state, use the
file-backed config in `.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, or
`mcp/bilig-workpaper.mcp.json`; that mode exposes the eight general WorkPaper
tools listed below.
Keep the Inspector proxy on localhost. It can spawn local processes, so do not
expose it to untrusted networks or disable its auth token for ordinary
inspection.
If you want the raw newline-delimited JSON-RPC request stream instead of the
maintained transcript wrapper, use:
```sh
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize"}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"set_workpaper_input_cell","arguments":{"sheetName":"Inputs","address":"B3","value":0.4}}}' |
NODE_NO_WARNINGS=1 npm run --silent agent:mcp-stdio
```
The npm package exposes the demo server as `bilig-workpaper-mcp` by default:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp
```
### Cursor demo server config
For a Cursor smoke that matches the lower-level runtime package, add this
server to `.cursor/mcp.json`:
```json
{
"mcpServers": {
"bilig-workpaper": {
"command": "npm",
"args": ["exec", "--yes", "--package", "@bilig/headless@latest", "--", "bilig-workpaper-mcp"]
}
}
}
```
That default demo mode exposes two tools: `read_workpaper_summary` and
`set_workpaper_input_cell`.
Ask Cursor for a concrete write/readback proof:
```text
Use the `bilig-workpaper` MCP server. Read `Summary!A1:B5`, then set
`Inputs!B3` to `0.4` with `set_workpaper_input_cell`, and report
`expectedArr` before and after.
```
A passing result reports `expectedArr` `60000` before the edit and `96000`
after the edit. Use the file-backed config below when Cursor needs persistent
project WorkPaper state instead of the packaged demo workbook.
For Cursor, use the project-local `.cursor/mcp.json` shape in the
[MCP client setup guide](mcp-client-setup.md#cursor). That setup uses
`@bilig/workpaper@latest` in file-backed mode, so Cursor sees the general
WorkPaper tools such as `list_sheets`, `read_range`,
`set_cell_contents_and_readback`, and `export_workpaper_document`.
## Remote Stateless Endpoint
The hosted app runtime also exposes a JSON-only Streamable HTTP MCP endpoint for
clients that cannot launch a local stdio process:
```text
https://bilig.proompteng.ai/mcp
```
There is also a compatibility alias:
```text
https://bilig.proompteng.ai/mcp/workpaper
```
The endpoint is stateless and request-local. It loads the packaged demo
WorkPaper for each JSON-RPC request, exposes the same file-backed tool catalog,
resources, and prompts, and returns write/readback proof without writing user
files or issuing an MCP session id. Use `set_cell_contents_and_readback` when a
hosted client needs to write one input and read dependent formula output in the
same request. Use local file-backed stdio when an agent needs to persist a real
project WorkPaper JSON file.
Protocol smoke:
```sh
curl -fsS https://bilig.proompteng.ai/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H 'mcp-protocol-version: 2025-11-25' \
--data '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[].name'
```
For server-to-server clients, omit `Origin`. Browser-based clients must send an
allowed `Origin`; Claude and ChatGPT origins such as `https://chatgpt.com` are
allowed by default. For ChatGPT Developer Mode setup, use the
[ChatGPT Apps WorkPaper MCP](chatgpt-apps-workpaper-mcp.md) page.
For a real agent workflow, point the same binary at a persisted WorkPaper JSON
document:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-mcp-challenge --json
npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
```
`bilig-mcp-challenge` is the one-command evaluator path. It initializes the
file-backed MCP server, lists tools/resources/prompts, edits `Inputs!B3`, reads
recalculated `Summary!B3`, exports WorkPaper JSON, restarts from disk, and
prints `verified: true`.
File-backed mode loads `./pricing.workpaper.json`, exposes `list_sheets`,
`read_range`, `read_cell`, `set_cell_contents`,
`set_cell_contents_and_readback`, `get_cell_display_value`,
`export_workpaper_document`, and `validate_formula`, then writes the updated
WorkPaper JSON back to the same file after `set_cell_contents` or
`set_cell_contents_and_readback` when `--writable` is present. It also exposes
`resources/list`, `resources/read`,
`prompts/list`, and `prompts/get` so clients can discover the live workbook
manifest, agent handoff instructions, current document JSON, and reusable edit
or formula-debug prompts. Omit `--writable` for read-only inspection.
The high-signal runtime resources are:
- `bilig://workpaper/manifest`
- `bilig://workpaper/agent-handoff`
- `bilig://workpaper/sheets`
- `bilig://workpaper/current-document`
The reusable prompts are:
- `edit_and_verify_workpaper`
- `debug_workpaper_formula`
Every file-backed tool includes an MCP `outputSchema`, parameter descriptions,
and safety annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`,
and `openWorldHint`). That is deliberate: directory scanners and coding agents
should be able to pick the workbook read, write, display, export, or formula
validation tool without treating the description as a vague demo.
Use the maintained file-backed transcript when a directory reviewer or agent
builder needs proof that the packaged binary mutates a real WorkPaper JSON file:
```sh
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:mcp-file-transcript
```
A passing run starts `npm exec --package @bilig/workpaper@latest --
bilig-workpaper-mcp --workpaper pricing.workpaper.json --init-demo-workpaper --writable`, lists the
file-backed tool surface, writes `Inputs!B3`, persists the JSON file, reads
`Summary!B3`, and asserts that the recalculated value is `96000`.
## XLSX Risk Preflight Before Edits
When the agent starts from a real `.xlsx`, run the file-backed XLSX path before
opening Excel, LibreOffice, Google Sheets, or a browser grid:
```sh
pnpm --dir examples/headless-workpaper install --ignore-workspace
pnpm --dir examples/headless-workpaper run agent:mcp-xlsx-risk-preflight
```
The example starts the published binary this way:
```sh
npm exec --package @bilig/workpaper@latest -- \
bilig-workpaper-mcp \
--from-xlsx pricing-risk-preflight.xlsx \
--workpaper pricing-risk-preflight.workpaper.json \
--writable
```
For an agent or MCP client, the required JSON-RPC sequence is:
1. `initialize`
2. `tools/list`
3. `tools/call` `analyze_workbook_risk` with `inspectLimit: "all"`
4. `tools/call` `set_cell_contents_and_readback` for one small input edit
5. `tools/call` `export_workpaper_document`
The maintained transcript proves the sequence with
`schemaVersion: "bilig-agent-xlsx-risk-preflight.v1"`,
`analyze_workbook_risk`, `Inputs!B3`, `Summary!B3`, `60000 -> 96000`,
`restoredReadbackMatchesAfter: true`, exported WorkPaper JSON, and
`verified: true`.
This preflight is local and read-only until a write tool is called. It reports
workbook risk indicators and keeps `excelParity: "not_proven"`; it does not
certify Excel compatibility.
## Docker Target For Directory Introspection
MCP directories such as Glama need to start the server and run `tools/list`
without cloning the monorepo or building the web app. The root Dockerfile keeps
the production web image as `--target bilig-runtime` and adds a separate MCP
target for directory scanners:
```sh
docker build --target bilig-workpaper-mcp -t bilig-workpaper-mcp:local .
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize"}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' |
docker run --rm -i bilig-workpaper-mcp:local
```
The target installs `@bilig/workpaper` from npm, seeds
`/workpaper/pricing.workpaper.json`, and starts
`bilig-workpaper-mcp --workpaper /workpaper/pricing.workpaper.json --init-demo-workpaper --writable`
over stdio. That makes directory introspection see the general WorkPaper tools:
`list_sheets`, `read_range`, `read_cell`, `set_cell_contents`,
`set_cell_contents_and_readback`, `get_cell_display_value`,
`export_workpaper_document`, and `validate_formula`.
It also carries the OCI label
`io.modelcontextprotocol.server.name=io.github.proompteng/bilig-workpaper`, so
registry and directory tooling can match the container target to the official
MCP Registry name.
For crawlers that cannot run Docker or stdio, the docs site also publishes a
static MCP server card at
`https://proompteng.github.io/bilig/.well-known/mcp/server-card.json`. The card
lists the same `list_sheets`, `read_range`, `read_cell`, `set_cell_contents`,
`set_cell_contents_and_readback`, `get_cell_display_value`,
`export_workpaper_document`, and `validate_formula` tools, plus the WorkPaper
resources and prompts, without requiring account auth or a live server
connection.
The hosted endpoint origin serves the same crawler-friendly card at
`https://bilig.proompteng.ai/.well-known/mcp/server-card.json`, with
`streamable-http` transport metadata for `https://bilig.proompteng.ai/mcp`.
That gives Smithery-style scanners a same-origin metadata path when they start
from the remote MCP URL rather than the documentation site.
The `@bilig/workpaper` package carries
`mcpName: io.github.proompteng/bilig-workpaper` and a matching `server.json`.
It is the canonical package metadata for the official MCP Registry entry
`io.github.proompteng/bilig-workpaper`:
<https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.proompteng%2Fbilig-workpaper>.
If you already know which client you want to use, start with the
[MCP client setup guide](mcp-client-setup.md) for Claude, Cursor, Junie, VS Code,
and Codex config snippets.
If you are checking a directory listing or preparing one, use the
[MCP spreadsheet server directory status page](mcp-spreadsheet-server-directory.md)
for the canonical npm command, official Registry proof, Glama listing, and
pending directory-review status.
Before submitting the server to an MCP registry, verify this repo-specific
readiness checklist:
- `packages/workpaper/server.json` exists and describes the packaged stdio
server.
- `packages/workpaper/package.json` exposes `bilig-workpaper-mcp` in `bin`.
- `packages/workpaper/package.json` includes
`mcpName: io.github.proompteng/bilig-workpaper`.
- `pnpm publish:runtime:check` passes against the runtime packages.
- `pnpm --dir examples/headless-workpaper run agent:mcp-transcript` passes.
Passing the checklist means the repository metadata and smoke checks are ready
for registry submission; it does not mean the package has already been
published.
## Vercel AI SDK MCP Client Recipe
If your agent loop already uses the Vercel AI SDK, keep the MCP client thin and
let the WorkPaper server own the spreadsheet reads and writes:
```ts
import { createMCPClient } from '@ai-sdk/mcp'
import { Experimental_StdioMCPTransport } from '@ai-sdk/mcp/mcp-stdio'
import { generateText } from 'ai'
const client = await createMCPClient({
transport: new Experimental_StdioMCPTransport({
command: 'npm',
args: [
'exec',
'--package',
'@bilig/workpaper@latest',
'--',
'bilig-workpaper-mcp',
'--workpaper',
'./pricing.workpaper.json',
'--init-demo-workpaper',
'--writable',
],
}),
})
try {
const tools = await client.tools()
const { text } = await generateText({
model: 'your-model',
tools,
prompt: [
'Read Summary!A1:B5 with read_range.',
'Then set Inputs!B3 to =0.4 with set_cell_contents_and_readback.',
'Use readbackRange Summary!A1:B5 and export the document.',
'Return editedCell, beforeReadback, afterReadback, persisted, and restoredReadbackMatchesAfter.',
].join('\n'),
})
console.log(text)
} finally {
await client.close()
}
```
The server command is `bilig-workpaper-mcp`; the `npm exec --package
@bilig/workpaper -- bilig-workpaper-mcp` wrapper only resolves the published npm
package for a clean checkout. The stdio transport receives `npm` as the command
and the rest as `args`, so shell parsing does not sit between the AI SDK client
and the MCP server. The two tool calls prove the useful workflow: read a
formula-backed summary, set one input cell, and return computed before/after
readback.
Verify the docs links and discovery metadata after editing this page:
```sh
pnpm docs:discovery:check
```
The script implements the JSON-RPC methods needed for the file-backed WorkPaper
agent surface:
- `tools/list` returns `read_workpaper_summary` and
`set_workpaper_input_cell` with JSON Schema inputs and MCP tool annotations.
- `tools/call` invokes the requested WorkPaper tool and returns text content
plus structured formula readback.
- `resources/list` and `resources/read` expose the live WorkPaper manifest,
sheet summary, current document JSON, and compact agent handoff.
- `prompts/list` and `prompts/get` expose the edit-and-verify and formula-debug
workflows as reusable client prompts.
The packaged binary has two tool sets:
- default demo mode: `read_workpaper_summary` and `set_workpaper_input_cell`
- file-backed mode: `list_sheets`, `read_range`, `read_cell`,
`set_cell_contents`, `set_cell_contents_and_readback`,
`get_cell_display_value`, `export_workpaper_document`, and
`validate_formula`
The annotations are explicit for directory reviewers and cautious MCP clients:
`read_workpaper_summary` is read-only, idempotent, and closed-world.
`set_workpaper_input_cell` mutates the local WorkPaper state, is idempotent for
the same cell/value arguments, and is closed-world rather than a network or
filesystem tool.
In file-backed mode, `set_cell_contents` is annotated as destructive only when
the server starts with `--writable`.
### MCP Stdio Troubleshooting
| Symptom | What to check |
| ------------------------------ | ----------------------------------------------------------------------------------------------- |
| `Parse error` response | Make sure each stdin line is valid JSON before it reaches the server. |
| No response appears | End each JSON-RPC message with a newline; the server waits for newline-delimited input. |
| Notification has no output | `notifications/initialized` is intentionally one-way and does not produce a JSON-RPC response. |
| `Invalid params` or tool error | Check that `tools/call` includes a supported `name` and the required `arguments` for that tool. |
The example deliberately avoids an MCP SDK dependency so the workbook contract
is visible. Put the same handlers behind stdio, HTTP, or your MCP SDK adapter
when you wire it into a production agent host.
## What A Passing Run Proves
The write tool edits `Inputs!B3`, recalculates dependent formulas, serializes
the WorkPaper document, restores it, and checks that formulas and computed
values survived the round trip:
```json
{
"editedCell": "Inputs!B3",
"before": {
"expectedCustomers": 5,
"expectedArr": 60000,
"expansionArr": 66000,
"targetGap": -34000
},
"after": {
"expectedCustomers": 8,
"expectedArr": 96000,
"expansionArr": 105600,
"targetGap": 5600
},
"checks": {
"previousValue": 0.25,
"newValue": 0.4,
"formulasPersisted": true,
"restoredMatchesAfter": true,
"expectedArrChanged": true
}
}
```
That is the part spreadsheet agents need. A tool that only says "updated" is
not enough. Return the edited address, previous value, new value, before/after
computed values, formula contracts, and persistence proof.
## Tool Boundary
Expose only the minimum useful surface first:
1. `read_workpaper_summary` reads a bounded range and returns computed values
plus serialized cell contents.
2. `set_workpaper_input_cell` validates the sheet and A1 address before a
write, then returns formula readback and persistence checks.
3. Everything outside that boundary stays in your MCP host: auth, transport,
rate limits, logging, and user approval policy.
The official MCP specification describes tool discovery through `tools/list`,
tool invocation through `tools/call`, input schemas, and tool annotations:
<https://modelcontextprotocol.io/specification/2025-11-25/server/tools>.
It also defines server resources through `resources/list` and
`resources/read`, and reusable prompt templates through `prompts/list` and
`prompts/get`:
<https://modelcontextprotocol.io/specification/2025-11-25/server/resources>
and
<https://modelcontextprotocol.io/specification/2025-11-25/server/prompts>.
## Files To Inspect
- MCP-style adapter script:
[`examples/headless-workpaper/mcp-tool-server.ts`](https://github.com/proompteng/bilig/blob/main/examples/headless-workpaper/mcp-tool-server.ts)
- stdio adapter script:
[`examples/headless-workpaper/mcp-stdio-server.ts`](https://github.com/proompteng/bilig/blob/main/examples/headless-workpaper/mcp-stdio-server.ts)
- official MCP Registry entry:
[`io.github.proompteng/bilig-workpaper`](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.proompteng%2Fbilig-workpaper)
- example README:
[`examples/headless-workpaper/README.md#mcp-tool-server-shape`](https://github.com/proompteng/bilig/tree/main/examples/headless-workpaper#mcp-tool-server-shape)
- SDK-neutral tool-calling recipe:
[`docs/agent-workpaper-tool-calling-recipe.md`](agent-workpaper-tool-calling-recipe.md)
- Vercel AI SDK and LangChain wrappers:
[`docs/vercel-ai-sdk-langchain-spreadsheet-tool.md`](vercel-ai-sdk-langchain-spreadsheet-tool.md)
## Feedback Thread
Use the
[MCP spreadsheet tool server discussion](https://github.com/proompteng/bilig/discussions/230)
for adapter feedback. The open questions are deliberately concrete: stdio,
HTTP/SSE, or SDK adapter next; which spreadsheet workflow should be proven
next; and which structured fields every write tool should return.
## When This Is A Good Fit
Use this pattern when an agent needs to edit a forecast, pricing workbook,
quote approval rule, budget check, or service-side spreadsheet model and prove
the formulas reacted. Keep the MCP layer thin, keep the workbook logic
testable, and make every write return structured verification.
Start with the adapter command above. If it almost matches but a gap blocks
adoption, open an implementation gap discussion:
<https://github.com/proompteng/bilig/discussions/new?category=general>.
---
## Agent XLSX Formula Recalculation Without LibreOffice
Source: https://github.com/proompteng/bilig/blob/main/docs/agent-xlsx-formula-recalculation-without-libreoffice.md
# Agent XLSX formula recalculation without LibreOffice
If an agent edits an `.xlsx` file and then acts on a formula result, it needs a
fresh value before the next tool call. Returning the old cached value is worse
than an error because the agent thinks the workbook agreed with it.
Many spreadsheet automation recipes solve this by running Excel, LibreOffice,
Microsoft Graph, or a Python recalculation helper after every file write. That
is a reasonable choice when exact Excel behavior matters. It is also a heavy
boundary for a Node agent tool that only needs a supported formula workbook,
verified readback, and an exported `.xlsx` at the edge.
Bilig's narrower path is:
1. import the `.xlsx` into a WorkPaper;
2. write the agent's input cells;
3. recalculate in the Node process;
4. read the output cells;
5. export the edited `.xlsx`;
6. reimport it in a smoke test to prove the boundary still works.
## Run the proof
This is the smallest useful check. It starts from a blank directory, downloads
one TypeScript file, creates an XLSX quote workbook, edits inputs, reads the
calculated approval result, exports the edited XLSX, and reimports it.
```sh
mkdir bilig-agent-xlsx-proof
cd bilig-agent-xlsx-proof
curl -fsSLO https://proompteng.github.io/bilig/xlsx-recalculation-proof.ts
npm init -y >/dev/null
npm pkg set type=module
npm install @bilig/workpaper@latest tsx@4.21.0
npx --no-install tsx xlsx-recalculation-proof.ts
```
The run is useful only if it ends with:
```json
{
"checks": {
"decisionChanged": true,
"recalculatedMargin": true,
"exportedReimportMatchesAfter": true,
"formulasSurvivedXlsxRoundTrip": true,
"verified": true
}
}
```
## Tool contract
For an agent, keep the tool surface boring:
```ts
type WorkbookEditRequest = {
file: string
writes: Array<{ sheet: string; cell: string; value: string | number | boolean }>
reads: Array<{ sheet: string; cell: string }>
}
type WorkbookEditResult = {
values: Array<{ sheet: string; cell: string; value: unknown }>
exportedFile: string
verified: true
}
```
The tool should refuse to return `verified: true` unless all of these happened:
- the target sheets and cells existed;
- every requested write was applied;
- formula output cells were read after the writes;
- the edited workbook was exported;
- the exported workbook could be imported again;
- the reimported values matched the values returned to the agent.
That contract is more important than the model prompt. The agent needs a
tool-shaped invariant it cannot hand-wave past.
## When not to use this
Keep Excel, LibreOffice, or Microsoft Graph in the loop when the workbook
depends on macros, pivots, charts, external links, unsupported functions, or
exact Excel UI behavior.
Use Bilig when the formulas are in the supported runtime surface and the job is
a backend or agent workflow: pricing checks, payout approvals, import
validation, budget gates, quote models, or fixture-driven workbook tests.
## Where this fits
This page exists for the same class of problem documented by spreadsheet automation
tooling that shells out to a recalculation step after writing formulas. If your
agent already has LibreOffice available and the latency is acceptable, keep it.
If you want a TypeScript runtime that can be tested inside the agent tool loop,
run the proof above and inspect the emitted XLSX files.
Related:
- [curlable XLSX recalculation proof](xlsx-recalculation-proof.md)
- [XLSX formula recalculation in Node.js](xlsx-formula-recalculation-node.md)
- [agent spreadsheet tool-call loop](agent-spreadsheet-tool-call-loop.md)
- [MCP spreadsheet tool server](mcp-workpaper-tool-server.md)
- [compatibility limits](where-bilig-is-not-excel-compatible-yet.md)
If this is the exact agent spreadsheet loop you are trying to avoid rebuilding,
open one concrete blocker or adoption note so the next developer can evaluate it faster:
<https://github.com/proompteng/bilig/discussions/new?category=general>.
---
## ExcelJS Formula Result Not Updating After Node Edits
Source: https://github.com/proompteng/bilig/blob/main/docs/exceljs-formula-result-not-updating-after-node-edits.md
# ExcelJS Formula Result Not Updating After Node Edits
Use this page for the specific ExcelJS failure where a Node service edits input
cells, but formula cells still show old cached results in the same process.
ExcelJS is the right tool for many `.xlsx` file tasks. The boundary is
calculation: a file library can preserve formula records and cached results
without recalculating the dependency graph after your service changes an input.
ExcelJS documents formula cells as objects with `formula` and optional `result`
data. If your backend needs the fresh value before returning a response, add a
formula runtime at that boundary.
Official ExcelJS reference:
- <https://github.com/exceljs/exceljs#formula-value>
## Failure Mode
The workbook has a formula such as `Quote!B2 = Inputs!B2*Inputs!B3`, and ExcelJS
shows a cached formula result. Your service changes `Inputs!B3`, then reads
`Quote!B2` before Excel opens the file. The cached result can still be the old
number.
Setting `workbook.calcProperties.fullCalcOnLoad = true` is not enough for
in-process readback. It asks a spreadsheet app to recalculate later.
## One Command
Run the ExcelJS bridge demo:
```sh
npx --package @bilig/exceljs-formula-recalc exceljs-recalc --demo --json
```
Expected output includes:
```json
{
"commandSucceeded": true,
"recalculationCompleted": true,
"expectedValueMatched": true,
"reads": {
"Summary!B2": {
"value": 72000
}
}
}
```
For a source-level reproduction:
```sh
git clone https://github.com/proompteng/bilig.git
cd bilig
npm --prefix examples/recalc-bridge-workflows install
npm --prefix examples/recalc-bridge-workflows run so:exceljs-44199441
```
That script mirrors the stale-result pattern from "Get computed value of Excel
sheet cell in Node.js": an input changes, ExcelJS still has the old cached
formula result, then `@bilig/exceljs-formula-recalc` verifies and patches the
fresh result.
## Minimal Bridge
Use ExcelJS for workbook files and Bilig only at the recalculation boundary:
```ts
import ExcelJS from 'exceljs'
import { recalculateExceljsWorkbook } from '@bilig/exceljs-formula-recalc'
const workbook = new ExcelJS.Workbook()
// build or load sheets here
const result = await recalculateExceljsWorkbook(workbook, {
edits: [{ target: 'Inputs!B3', value: 0.4 }],
reads: ['Summary!B2'],
})
console.log({
readback: result.reads['Summary!B2'],
workbookMutated: result.workbookMutated,
warnings: result.warnings,
})
```
## Limitation
This bridge is for fresh formula readback after Node edits. It is not a
replacement for ExcelJS styling, workbook layout, images, tables, comments, or
file-generation features.
## When Not To Use Bilig
Do not use Bilig when Excel, LibreOffice, or another spreadsheet application
will open and calculate the workbook before any service decision depends on the
value. Do use it when the Node process must reject, persist, route, approve, or
answer based on the recalculated formula result.
## Related
- [ExcelJS formula recalculation in Node.js](exceljs-formula-recalculation-node.md)
- [XLSX formula recalculation in Node.js](xlsx-formula-recalculation-node.md)
- [SheetJS formula result not updating in Node.js](sheetjs-formula-result-not-updating-node.md)
- [Agent WorkPaper evaluator matrix](agent-proof-matrix.md)
---
## Formula Bug Clinic
Source: https://github.com/proompteng/bilig/blob/main/docs/formula-bug-clinic.md
# Bilig formula bug clinic
If a workbook formula bug is blocking your Node service, send the smallest
public case that proves it. The goal is not to collect private spreadsheets.
The goal is to turn real failures into public fixtures that future evaluators
can run.
Start with the WorkPaper clinic report when the blocker is an import, formula,
persistence, or agent readback gap:
```sh
npm exec --package @bilig/workpaper@latest -- bilig-formula-clinic ./reduced.xlsx \
--cells "Summary!B7,Inputs!B2"
```
That command runs locally, does not upload the workbook, imports the file into
WorkPaper, samples formulas, reads the requested cells, and prints a Markdown
report you can paste into the fixture form or discussion.
Good cases:
- an ExcelJS, SheetJS, or `xlsx-populate` pipeline writes inputs but cannot prove
formula readback;
- an ExcelJS workflow writes inputs but needs recalculated output evidence;
- an XLSX uses shared formulas and the imported formula text is wrong;
- a workbook works in Excel but fails in a local Node formula runtime;
- a WorkPaper JSON restore changes a calculated value;
- an agent or MCP tool writes a cell but cannot prove the recalculated output;
- a service route needs one missing formula family, import detail, or example.
Open the fixture form when the reduced public fixture is ready:
<https://github.com/proompteng/bilig/issues/new?template=workbook_fixture.yml>.
Discuss the shape first if you are still reducing the case:
<https://github.com/proompteng/bilig/discussions/414>.
## Generate a local report
Use the narrowest command that matches the blocker:
| Blocker | First local command | What to paste |
| --- | --- | --- |
| WorkPaper import, formula, or persistence mismatch | `npm exec --package @bilig/workpaper@latest -- bilig-formula-clinic ./reduced.xlsx --cells "Summary!B7,Inputs!B2"` | The Markdown clinic report with requested cells, formula samples, warnings, and actual readback. |
| Saved workbook compatibility question | `npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- workbook-compatibility-report ./reduced.xlsx --json` | The compatibility report with unsupported functions, external links, volatile formulas, and inspected formula counts. |
If the workbook is already reduced, run the clinic reporter locally and paste
the Markdown output into the fixture form. It reads the file on your machine and
does not upload workbook contents.
```sh
npm exec --package @bilig/workpaper@latest -- bilig-formula-clinic ./reduced.xlsx \
--cells "Summary!B7,Inputs!B2"
```
That is the lowest-friction path for package users. It imports the workbook,
samples formulas, reads the requested cells through WorkPaper, and prints a
Markdown report.
If you want to pin or edit the reporter script directly:
```sh
mkdir bilig-formula-clinic
cd bilig-formula-clinic
npm init -y
npm pkg set type=module
npm install @bilig/workpaper
npm install --save-dev tsx typescript @types/node
curl -fsSLo formula-clinic-report.ts \
https://proompteng.github.io/bilig/formula-clinic-report.ts
npx tsx formula-clinic-report.ts ./reduced.xlsx \
--cells "Summary!B7,Inputs!B2"
```
Use `--cells` for the output cells that prove the bug. The report includes
import warnings, formula samples, requested readback, and a fixture checklist.
## What to send
Send one reduced public fixture, not the whole production workbook.
Include:
- package version or commit tested;
- sheet names and exact cells or ranges;
- formulas involved;
- input values before and after the edit;
- expected output from Excel, LibreOffice, Graph, an existing service, or a
manual check;
- actual Bilig output, import error, unsupported function, or missing API;
- the shortest command or script that maintainers can run.
Do not attach confidential workbooks, customer data, financial models, or files
that cannot be redistributed in a public test corpus. Replace names and numbers
with neutral values while keeping the same formula shape.
Good discussion summary:
```text
Reduced XLSX fixture attached or linked. The clinic report reads Summary!B7
as "review" after the input edit, while Excel returns "approved" for the same
fixture. The service should return "approved".
```
Bad discussion summary:
```text
My spreadsheet is wrong. Can Bilig support it?
```
## Why this helps
A reduced workbook fixture is better than a broad bug report because it gives
maintainers something concrete to merge:
- a regression test;
- an XLSX import/export corpus case;
- a formula compatibility note;
- a WorkPaper JSON persistence fixture;
- a service-route example;
- an MCP or agent-tool transcript.
When a case lands, the issue can point to the commit, release, and docs page
that fixed it. That is the evidence a skeptical backend developer can inspect
before adopting the package.
## Fast local check
For saved workbook boundaries, first inspect compatibility risks:
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- \
workbook-compatibility-report ./reduced.xlsx --json
```
If the report shows unsupported formulas, external links, macros, pivots, or
volatile formulas, include that output with the reduced fixture.
For a pure WorkPaper case, reduce it to a script:
```sh
mkdir bilig-fixture-check
cd bilig-fixture-check
npm init -y
npm pkg set type=module
npm install @bilig/workpaper
npm install --save-dev tsx typescript @types/node
```
If the script is short enough to paste into an issue, it is probably a good
fixture.
## Useful references
- [Submit a workbook fixture](submit-workbook-fixture.md)
- [ExcelJS shared formulas and Node.js recalculation](exceljs-shared-formula-recalculation-node.md)
- [XLSX formula recalculation in Node.js](xlsx-formula-recalculation-node.md)
- [Where Bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
If this helped you reduce a workbook bug but a gap still blocks adoption,
open one concrete blocker or fixture note:
<https://github.com/proompteng/bilig/discussions/new?category=general>.
---
## Google Sheets QUERY and SORTN in Node.js
Source: https://github.com/proompteng/bilig/blob/main/docs/google-sheets-query-sortn-node-workpaper.md
# Google Sheets QUERY and SORTN formulas in Node.js
Use `@bilig/workpaper` when a Node service or coding agent needs Google
Sheets-style formula behavior over workbook state it already owns.
This is not a Google Sheets connector. It does not fetch a live spreadsheet,
read Drive permissions, or run a remote Visualization API query. It evaluates
local WorkPaper ranges, recalculates formulas, reads the result back, and can
persist the WorkPaper document as JSON.
## What works locally
Bilig supports the useful service-side subset:
- `QUERY(range, "select ... where ... order by ... limit ... offset ...", headers)`
- `QUERY` `group by` with `sum(column)` and `count(column)`
- `QUERY` `label` for selected columns and supported aggregate output headers
- `SORTN(range, n, tie_mode, sort_column_or_range, ascending, ...)`
- `COUNTUNIQUEIFS(...)`
- `ARRAYFORMULA(...)` spill evaluation
That covers the common backend job: take a small model, group or filter it,
sort the rows, read the calculated output, and save the state.
Unsupported `QUERY` clauses fail closed. Do not expect `pivot`, `having`,
`format`, `options`, arbitrary SQL, or live Google data fetching.
Provider-backed imports such as `IMPORTDATA`, `IMPORTRANGE`, `IMPORTHTML`,
`IMPORTXML`, `IMPORTFEED`, and `GOOGLEFINANCE` are a separate boundary. Without
a host adapter, they return a blocked result instead of pretending to have
network or account access.
## Run the proof
From an empty directory:
```sh
mkdir bilig-query-sortn
cd bilig-query-sortn
npm init -y
npm pkg set type=module
npm install @bilig/workpaper
npm install -D tsx typescript @types/node
cat > query-sortn.ts <<'EOF'
import {
WorkPaper,
createWorkPaperFromDocument,
exportWorkPaperDocument,
parseWorkPaperDocument,
serializeWorkPaperDocument,
} from "@bilig/workpaper";
type CellValue = {
value?: unknown;
};
function readNumber(cell: unknown, label: string): number {
if (typeof cell === "object" && cell !== null && typeof (cell as CellValue).value === "number") {
return (cell as CellValue).value;
}
throw new Error(`expected ${label} to be numeric, got ${JSON.stringify(cell)}`);
}
const workbook = WorkPaper.buildFromSheets({
Deals: [
["Region", "Segment", "Revenue"],
["West", "SMB", 60000],
["East", "Enterprise", 45000],
["West", "Enterprise", 140000],
["East", "SMB", 30000],
["West", "SMB", 36000],
],
Summary: [
["Metric", "Value"],
[
"Top region revenue",
'=INDEX(QUERY(Deals!A1:C6,"select A,sum(C) where C >= 30000 group by A order by sum(C) desc label A \'Region\', sum(C) \'Revenue\'",1),2,2)',
],
["Top deal", "=INDEX(SORTN(Deals!A2:C6,1,0,3,FALSE),1,3)"],
],
});
const summary = workbook.getSheetId("Summary");
const deals = workbook.getSheetId("Deals");
if (summary === undefined || deals === undefined) {
throw new Error("missing sheet");
}
const before = readNumber(workbook.getCellValue({ sheet: summary, row: 1, col: 1 }), "before top region");
workbook.setCellContents({ sheet: deals, row: 3, col: 2 }, 190000);
const after = readNumber(workbook.getCellValue({ sheet: summary, row: 1, col: 1 }), "after top region");
const topDeal = readNumber(workbook.getCellValue({ sheet: summary, row: 2, col: 1 }), "top deal");
const saved = serializeWorkPaperDocument(exportWorkPaperDocument(workbook, { includeConfig: true }));
const restored = createWorkPaperFromDocument(parseWorkPaperDocument(saved));
const restoredSummary = restored.getSheetId("Summary");
if (restoredSummary === undefined) {
throw new Error("missing restored Summary sheet");
}
const afterRestore = readNumber(
restored.getCellValue({ sheet: restoredSummary, row: 1, col: 1 }),
"restored top region",
);
console.log(
JSON.stringify(
{
before,
after,
afterRestore,
topDeal,
persistedDocumentBytes: saved.length,
verified: before === 236000 && after === 286000 && afterRestore === 286000 && topDeal === 190000,
},
null,
2,
),
);
workbook.dispose();
restored.dispose();
EOF
npx tsx query-sortn.ts
```
Expected output:
```json
{
"before": 236000,
"after": 286000,
"afterRestore": 286000,
"topDeal": 190000,
"persistedDocumentBytes": 1273,
"verified": true
}
```
The exact byte count can change with runtime metadata. The important proof is
that `QUERY` recalculated after the edit, `SORTN` found the new top deal, and
the restored WorkPaper still read the same calculated value.
## When to use this
Use this path for:
- pricing or quote models that group rows by region, tier, or customer segment
- import validation that filters suspicious rows before accepting a file
- tool integrations that need spreadsheet formulas without driving a browser grid
- backend tests where Google credentials would make the run flaky
Use Google Sheets or its API when the sheet is a shared hosted document,
permissions matter, or users expect to edit the source data in Google
Workspace.
## Related paths
- [Google Sheets API boundary](google-sheets-api-alternative-node-workpaper.md)
- [Formula language notes](formula-language.md)
- [Agent WorkPaper handoff](agent-adoption-kit.md)
- [Evaluate Bilig as an Agent MCP workbook tool](eval-agent-mcp.md)
- [Where Bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
If this matches the workflow, run the proof before adopting it. If the proof
passes, keep the repository and release feed nearby:
<https://github.com/proompteng/bilig>.
If a missing clause blocks adoption, open one concrete formula fixture:
<https://github.com/proompteng/bilig/discussions/new?category=general>.
## Sources
- Google Sheets QUERY function:
<https://support.google.com/docs/answer/3093343>
- Google Sheets SORTN function:
<https://support.google.com/docs/answer/7354624>
---
## Try Bilig WorkPaper In Node
Source: https://github.com/proompteng/bilig/blob/main/docs/try-bilig-headless-in-node.md
# Try Bilig WorkPaper in Node
> [!WARNING]
> Do not use the `npm create` path below while
> `@bilig/create-workpaper@latest` resolves to `0.164.11`. That release's
> generated smoke reports `formulasPersisted: false`. Use the direct evaluator
> path until the restored-formula fix ships in a newer release and passes a
> fresh consumer smoke.
This page is for people who want to try the package before reading the whole
repo. After the fixed generator release, it starts from an empty directory,
installs the published npm package, builds a tiny WorkPaper, edits an input
cell, reads the recalculated formula result, serializes the document, restores
it, and reads the result again.
No browser UI, account, server, or clone is required.
## Quickstart After The Fixed Release
```sh
npm create @bilig/workpaper@latest pricing-workpaper
cd pricing-workpaper
npm install
npm run smoke
```
Expected output includes this proof shape:
```json
{
"before": {
"summary": {
"decision": "review"
},
"inputCells": {
"units": "Inputs!B2",
"listPrice": "Inputs!B3"
}
},
"edit": {
"before": {
"decision": "review"
},
"after": {
"decision": "approved"
},
"restored": {
"decision": "approved"
},
"checks": {
"decisionChanged": true,
"formulasPersisted": true,
"restoredMatchesAfter": true,
"serializedBytes": 1242
}
},
"verified": true
}
```
The exact byte count can change between package versions. The important part is
that `verified` is `true`, `decisionChanged` is `true`, and
`restoredMatchesAfter` is `true`.
The generated starter uses the same maintained TypeScript proof shape as the
public mirror at <https://proompteng.github.io/bilig/npm-eval.ts> and
[`examples/headless-workpaper/npm-eval.ts`](https://github.com/proompteng/bilig/blob/main/examples/headless-workpaper/npm-eval.ts).
## Try it in Docker (optional)
> **Note:** pnpm is the primary recommended path. This section is for
> evaluators who prefer not to change their local Node version.
After completing the **Quickstart** step above you will have a generated
`pricing-workpaper/` project. Mount that directory into an official Node 24
container and run the same smoke script:
```sh
docker run --rm \
-v "$(pwd)":/eval \
-w /eval \
node:24-slim \
bash -c "npm install --silent && npm run smoke"
```
Expected output uses the same proof shape as above. The local run must set
`verified`, `decisionChanged`, `formulasPersisted`, and `restoredMatchesAfter`
to `true`:
```json
{
"edit": {
"after": {
"decision": "approved"
},
"restored": {
"decision": "approved"
},
"checks": {
"decisionChanged": true,
"formulasPersisted": true,
"restoredMatchesAfter": true,
"serializedBytes": 1242
}
},
"verified": true
}
```
No repo clone is needed. The container installs dependencies from npm and exits
cleanly after printing the result.
## What this proves
- multi-sheet workbook creation from plain arrays
- formula evaluation without a browser grid
- input edits through the workbook API
- computed value readback after the edit
- JSON document export, parse, restore, and readback
This is the core shape behind the larger examples for service routes, MCP tools,
agent writeback, and workbook automation.
## What this does not prove
`bilig` is not a finished Excel clone. It is useful when a TypeScript service or
agent needs a formula-backed workbook object it can mutate and persist. For full
Excel compatibility or XLSX layout fidelity, check the comparison and
compatibility pages before adopting it.
## Next paths
- [GitHub repository](https://github.com/proompteng/bilig)
- [@bilig/workpaper npm package](https://www.npmjs.com/package/@bilig/workpaper)
- [Five Node.js workbook automation examples](workbook-automation-examples-node.md)
- [Node.js spreadsheet formula engine guide](node-spreadsheet-formula-engine.md)
- [WorkPaper service recipe](node-service-workpaper-recipe.md)
- [MCP spreadsheet tool server](mcp-workpaper-tool-server.md)
- [Where bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
- [Where bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
If it almost matches but a gap blocks adoption, open an implementation gap discussion:
<https://github.com/proompteng/bilig/discussions/new?category=general>.
---
## Quote Approval WorkPaper API
Source: https://github.com/proompteng/bilig/blob/main/docs/quote-approval-workpaper-api.md
# Quote approval WorkPaper API in Node
Use this page when you want a quote-approval workflow instead of a tiny
arithmetic workbook.
The smoke runs a quote approval workflow:
1. Build a two-sheet WorkPaper with `Inputs` and `Summary`.
2. Write quote input cells: units, list price, discount, unit cost, and minimum
margin.
3. Recalculate formulas for net revenue, gross margin, and approval decision.
4. Serialize the WorkPaper document as JSON.
5. Restore that JSON and verify the restored workbook still matches the
recalculated result.
No browser grid, spreadsheet account, OAuth setup, or repo clone is required.
## Run It From An Empty Directory
```sh
mkdir bilig-quote-approval
cd bilig-quote-approval
npm init -y
npm pkg set type=module
npm install @bilig/workpaper
npm install -D tsx typescript @types/node
curl -fsSLo quote-approval-api.ts \
https://raw.githubusercontent.com/proompteng/bilig/main/examples/serverless-workpaper-api/quote-approval-api.ts
npx tsx quote-approval-api.ts
```
Expected shape:
```json
{
"route": "Quote approval WorkPaper API",
"inputCells": {
"units": "Inputs!B2",
"listPrice": "Inputs!B3",
"discount": "Inputs!B4",
"unitCost": "Inputs!B5",
"minimumMargin": "Inputs!B6"
},
"before": {
"netRevenue": 43200,
"grossMargin": 0.2963,
"decision": "review"
},
"edit": {
"input": {
"units": 40,
"listPrice": 1200,
"discount": 0.05,
"unitCost": 760,
"minimumMargin": 0.3
},
"after": {
"netRevenue": 45600,
"grossMargin": 0.3333,
"decision": "approved"
},
"checks": {
"decisionChanged": true,
"formulasPersisted": true,
"inputPersisted": true,
"restoredMatchesAfter": true
}
},
"verified": true
}
```
The exact serialized byte count can move between releases. The important parts
are:
- `decisionChanged: true`
- `formulasPersisted: true`
- `inputPersisted: true`
- `restoredMatchesAfter: true`
- `verified: true`
## What This Proves
This is the service boundary that matters for backend adoption:
- input JSON maps to known workbook cells
- formulas recalculate after the write
- the returned values come from formula readback, not a screenshot
- the persisted JSON still contains formulas
- a restored WorkPaper returns the same decision
That is the shape behind pricing rules, discount approval, payout checks,
budget guardrails, import validation, and tool integrations that need exact readback.
## What This Does Not Prove
It does not prove full Excel compatibility. It does not prove formatting,
charts, collaboration, or broad XLSX file fidelity. For those boundaries, read
the [compatibility limits](where-bilig-is-not-excel-compatible-yet.md) and the
[production adoption checklist](production-adoption-checklist-headless-workpaper.md).
It also does not prove every formula family you need is implemented. If this API
shape is right but a formula, persistence shape, or framework boundary blocks a
trial, open a concrete adoption note in the
[workflow feedback discussion](https://github.com/proompteng/bilig/discussions/157).
## Use It In A Service
The full example is
[`examples/serverless-workpaper-api`](https://github.com/proompteng/bilig/tree/main/examples/serverless-workpaper-api).
It includes:
- a web-standard `Request` / `Response` route handler
- a quote approval route
- a Vercel Function smoke
- a Next.js App Router smoke
- framework adapters
- persistence adapter examples
Run the wider proof from a repo checkout:
```sh
pnpm --dir examples/serverless-workpaper-api install --ignore-workspace
pnpm --dir examples/serverless-workpaper-api run test
pnpm --dir examples/serverless-workpaper-api run framework-adapters
pnpm --dir examples/serverless-workpaper-api run persistence-adapters
```
## Next Pages
- [Try `@bilig/workpaper` in Node](try-bilig-headless-in-node.md)
- [Serverless WorkPaper API route](serverless-workpaper-api-route.md)
- [Node service WorkPaper recipe](node-service-workpaper-recipe.md)
- [Five Node.js workbook automation examples](workbook-automation-examples-node.md)
- [Where bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
- [Where bilig is not Excel-compatible yet](where-bilig-is-not-excel-compatible-yet.md)
If it almost matches but a gap blocks adoption, open an implementation gap discussion:
<https://github.com/proompteng/bilig/discussions/new?category=general>.
---
## Compatibility Limits
Source: https://github.com/proompteng/bilig/blob/main/docs/where-bilig-is-not-excel-compatible-yet.md
# Where bilig Is Not Excel-Compatible Yet
Status: public compatibility boundary for `@bilig/headless`
`bilig` is not a complete Excel clone. The current adoption wedge is narrower:
`@bilig/workpaper` gives Node services and tool hosts a workbook API with formulas,
structural edits, persistence, validation, and auditable compatibility fixtures.
This page names the main compatibility boundaries so people can evaluate the
project without reading generated contract JSON first.
## Current Evidence Snapshot
The repository keeps compatibility claims tied to checked-in fixtures and
reports:
- formula inventory breadth is `100%` for the current office-listed and tracked
formula inventory in
[`packages/formula/src/__tests__/fixtures/formula-compatibility-snapshot.json`](../packages/formula/src/__tests__/fixtures/formula-compatibility-snapshot.json)
- formula semantics coverage has `431` canonical fixtures and `12` workbook
semantics fixtures, with no missing committed fixture ids in
[`packages/benchmarks/baselines/calculation-semantics-contract.json`](../packages/benchmarks/baselines/calculation-semantics-contract.json)
- import/export fidelity passes required CSV/XLSX cases, reports no unsupported
import/export features, and explicitly declines native macro execution in
[`packages/benchmarks/baselines/import-export-fidelity-contract.json`](../packages/benchmarks/baselines/import-export-fidelity-contract.json)
- the headless product claim is API behavior: WorkPaper edit/readback,
recalculation, persistence, restore, and deterministic import/export checks.
Those artifacts are useful evidence. They are not a blanket promise that every
Excel workbook, every formula argument shape, every UI interaction, or every
third-party file behaves exactly like desktop Excel.
If you need to preflight a specific workbook before integrating it, run the
[Workbook Compatibility Report](workbook-compatibility-report.md):
```sh
npm exec --yes --package @bilig/xlsx-formula-recalc@latest -- workbook-compatibility-report workbook.xlsx --json
```
That report lists unsupported functions, external links, macro payloads,
pivots, volatile formulas, stored formula results, and risk reasons. It is
not an Excel compatibility certification and does not include a compatibility
score.
## The Biggest Non-Goals
### Native macro execution
`bilig` does not execute VBA or spreadsheet macro code.
The XLSM path detects macro-enabled workbooks, preserves safe workbook cells,
preserves the original VBA payload and code names for round trips, and records a
non-execution warning. Native macro execution remains a deliberately declined
runtime feature: `xlsx.macros.execution`.
That boundary is security posture, not a missing convenience feature.
### Full Excel application parity
`@bilig/headless` is a workbook engine package, not a replacement for the full
Excel desktop application.
It does not claim complete parity for:
- ribbon behavior, dialog behavior, add-ins, and desktop automation surfaces
- arbitrary interactive chart editing
- arbitrary interactive PivotTable refresh behavior
- Excel's full UI collaboration surface
- every file produced by every Excel-compatible application
The current XLSX contract covers round trips for values, formulas, formats,
defined names, comments, styles, conditional formats, dimensions, merges,
freeze panes, filters, sorts, sheet protection, protected ranges, data
validations, tables, charts, pivots, multi-sheet workbooks, and macro payload
preservation. It does not turn charts and pivots into a promise of full desktop
Excel interactivity.
### Universal formula-behavior parity
The formula registry and fixture suite are broad, and the current tracked
Office formula inventory is production-routed. The formula-behavior claim is
still evidence-scoped.
The current formula semantics artifact covers the committed canonical fixtures
and workbook semantics fixtures. It should not be read as "every Excel formula
argument combination and locale/date edge case is already covered." New edge
cases should become fixtures, and unsupported deterministic formulas in an XLSX
corpus should show up as mismatches rather than being silently accepted.
### Cached XLSX result parity for arbitrary corpora
Cached-result parity is a corpus property, not a universal package guarantee.
Use:
```sh
pnpm workpaper:xlsx-corpus:check -- /path/to/xlsx-corpus
```
The verifier reads `.xlsx`, `.xlsm`, and `.xls` files and compares formula
cells against cached workbook results where that comparison is meaningful.
Missing cached results and volatile or environment-dependent formulas such as
`NOW()` and `CELL()` are counted as skipped, not as proof of parity.
For a concrete report walkthrough, see
[`docs/xlsx-corpus-verifier-walkthrough.md`](xlsx-corpus-verifier-walkthrough.md).
### Browser Claim Boundaries
The local browser grid and WorkPaper headless engine are different surfaces.
Browser performance gates in this repository are deterministic product budgets
for the local workbook shell, including large-workbook load and headed
Playwright scroll behavior. They are not cross-product benchmark claims against
Google Sheets, Microsoft Excel Web, or every real user workflow.
Do not use the headless WorkPaper benchmark to claim the browser grid is faster
than every spreadsheet UI. Keep those claims separated.
## When bilig Is A Good Fit Today
`@bilig/headless` is a good fit when you need:
- a Node workbook engine for formula-backed business workflows
- agent-controlled workbook edits with explicit readback
- structural edits without driving a browser UI
- JSON persistence and restore for workbook state
- compatibility reports and deterministic fixtures you can inspect and rerun
- import/export paths that surface compatibility warnings instead of hiding
them
Start with:
- [`docs/why-agents-need-workbook-apis.md`](why-agents-need-workbook-apis.md)
- [`docs/building-a-revenue-model-with-headless-workpaper.md`](building-a-revenue-model-with-headless-workpaper.md)
- [`examples/headless-workpaper`](../examples/headless-workpaper)
## How To Improve Compatibility
The right contribution is usually not a vague "support Excel better" issue.
Use one of these shapes:
- add a minimal workbook fixture that exposes a real mismatch
- add a canonical formula fixture for a missing semantic edge
- add an XLSX round-trip case with a specific expected metadata surface
- extend the corpus verifier report when a skipped or mismatched case needs a
clearer explanation
- add a focused public example that shows a supported workflow end to end
Small, reproducible compatibility reports are much more useful than screenshots
or broad parity claims.
---
## npm Provenance And Package Trust
Source: https://github.com/proompteng/bilig/blob/main/docs/npm-provenance-package-trust.md
# Verify npm Provenance For `@bilig/headless`
Production adoption starts before the first import. For a service runtime or
agent tool, the package needs to be traceable to source, release CI, and a
specific GitHub commit.
`@bilig/headless` is published with npm registry signatures and SLSA provenance
attestations. npm reports this for the latest published package:
```sh
npm view @bilig/headless@latest version dist.attestations dist.signatures --json
```
The important signal is that `dist.attestations.provenance.predicateType` is
`https://slsa.dev/provenance/v1` and that `dist.signatures` is non-empty.
## Verify After Install
From a clean project:
```sh
mkdir bilig-package-trust
cd bilig-package-trust
npm init -y
npm install @bilig/headless
npm audit signatures
```
Expected result for the current dependency tree:
```text
audited 31 packages in 0s
31 packages have verified registry signatures
10 packages have verified attestations
```
Use this as a package-integrity check, not as an application-security claim.
You still need workflow fixtures, rollback, and formula compatibility gates for
your own WorkPaper-backed service.
## Release Path
Runtime packages are released by `.github/workflows/headless-package.yml`.
The workflow:
- verifies the runtime package chain;
- checks publishable package metadata with `pnpm publish:runtime:check`;
- requires Forgejo and GitHub `main` to agree before publishing;
- uses `id-token: write` for GitHub Actions OIDC;
- publishes through `scripts/publish-runtime-package-set.ts` with
`npm publish ... --provenance`.
npm documents trusted publishing as an OIDC flow that avoids long-lived npm
tokens and can automatically generate provenance for public packages published
from public repositories:
- <https://docs.npmjs.com/trusted-publishers/>
- <https://docs.npmjs.com/viewing-package-provenance/>
OpenSSF Scorecard is another useful consumer-side signal for evaluating
dependency risk:
- <https://scorecard.dev/>
This repository runs the official OpenSSF Scorecard action on every `main`
update and on a weekly schedule. Results are published to the public Scorecard
API, exposed through the README badge, and uploaded as SARIF to GitHub code
scanning so dependency evaluators can inspect repository posture separately
from npm package provenance.
The GitHub trust surface also includes CodeQL analysis for the
JavaScript/TypeScript codebase and Dependabot version updates for npm, GitHub
Actions, and the root Dockerfile. Those checks do not replace package
provenance, but they make vulnerability discovery and dependency drift visible
before a production adopter has to ask for it.
## What This Does Not Prove
Package provenance does not prove that a workbook workflow is correct, complete,
or safe for every production domain.
Before adopting `@bilig/headless` for customer-critical work, also run:
- the [90-second npm eval](try-bilig-headless-in-node.md);
- the [quote approval WorkPaper API proof](quote-approval-workpaper-api.md);
- the [production adoption checklist](production-adoption-checklist-headless-workpaper.md);
- the [compatibility limits](where-bilig-is-not-excel-compatible-yet.md).
The package-trust question is: "Did this package come from the expected source
and release path?" The production-readiness question is: "Does this exact
workflow have fixtures, rollback, and compatibility evidence?"
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
No one has posted yet. Be the first.

