Skip to main content
Glama
README.md
# The Workbook standard

A **workbook** is a zip file that answers a machine-learning question in a form another agent can
execute. It is three things at once, and the combination is the point:

- a **notebook** — cell-delimited source in whatever language the answer needs;
- a **skill** — `SKILL.md`, so an agent knows what this is and when to reach for it;
- an **MCP server** — dependency-free, stdio, installs the environment and runs the cells.

The hand-off it exists for: someone asks a question, gets an answer with citations, downloads a
workbook, drops the folder in front of a coding agent, and says "set this up and run it". The agent
reads `SKILL.md`, registers `mcp/server.py`, then calls `workbook_check`, `workbook_setup`,
`workbook_run`. Nothing in that sequence asks it to guess a dependency, a model id, or a command.

Read [SPEC.md](./SPEC.md) for the standard. It is the normative document; everything else here is
an implementation of it.

## Why not just a notebook

A notebook alone loses what made the answer correct. It does not say which file to take out of a
repository that holds forty of them, what the download actually weighs, which packages the imports
imply, what hardware the code assumes, or which paper the method came from. A workbook keeps that in
`workbook.json`, and the runner reads it rather than inferring it.

The manifest is also what makes this framework-agnostic. Nothing in it is Python. A runtime is a
declaration — interpreter, dependency manager, setup commands, argv prefix — so a Rust workbook and
a browser/WebGL workbook are the same shape as a PyTorch one, and one server drives all of them.

## What is in this repository

| path | what it is |
|---|---|
| `SPEC.md` | the standard |
| `manifest.ts` | schema, types, cell-marker parsing, and the nine completeness rules |
| `runtimes.ts` | per-runtime defaults: setup, exec prefix, dependency file |
| `templates.ts` | `SKILL.md`, `README.md`, `.mcp.json`, links, notebook scaffolds |
| `pack.ts` | manifest plus notebook to zip |
| `form.ts` | the intake form: how a workbook gets scoped before it is built |
| `templates/server.py` | the MCP server that ships inside every workbook |
| `conformance/` | a language-neutral validator and a live MCP handshake test |

## Conformance

The validator works on an unpacked workbook directory, so it is not tied to this implementation.

```bash
python3 conformance/run.py                        # the whole suite
python3 conformance/validate.py path/to/workbook  # validate any workbook, from any implementation
python3 conformance/handshake.py path/to/workbook # drive its MCP server the way a client would
python3 path/to/workbook/mcp/server.py --selfcheck
```

`run.py` closes the loop twice. It assembles the hand-written example in `conformance/example/`,
validates it, and drives its server through seventeen protocol checks; then it packs a workbook with
the TypeScript implementation and puts that one through the same three stages. It also asserts that
each of the nine completeness rules actually rejects a workbook that breaks it, because a validator
nobody has seen fail is not evidence of anything.

The validator and the handshake need no packages installed, and the handshake speaks raw JSON-RPC
rather than using a client library. That is deliberate: the tests have to run on the same bare
machine the server is designed to start on.

## Using the TypeScript implementation

```ts
import { normalize, pack, validate } from "workbook-standard";

const manifest = normalize(draftFromYourGenerator, notebookSource);
const { zip, issues, files } = pack({ manifest, notebook: notebookSource });
```

`pack` renders the environment file from the declared packages, the skill and readme from the
manifest, and copies in the MCP server and the spec, then validates the result. A generator only has
to supply what needs judgement: what to run, what it needs, and why.

There is no build step and no bundler requirement. `templates/server.py` and `SPEC.md` are inlined
into `generated/assets.ts` by `node tools/gen-assets.mjs`, which `--check` verifies has not drifted,
so the packer works in a Worker, in Node, and under any bundler. Relative imports carry explicit
`.ts` extensions so the sources run unbuilt under Node's type stripping; a TypeScript consumer needs
`allowImportingTsExtensions`.

## Status

Version 1. `workbook: "1"` in a manifest refers to `SPEC.md` in this repository. A reader that does
not recognise the version must refuse rather than guess.