Tools
What a tool is, what it owns, and the two places people meet it.
Say your table keeps rolling dice in a chat window and losing the results. You want a dice tray that lives in the world, remembers every roll, and shows the same numbers to everyone at the table. In vvd, that's a tool.
A tool is a capability that edits one kind of document. You declare which kind, you write a React component, and the platform handles the rest: connecting, syncing, merging two people's edits, undo, presence, permissions. Here's the whole thing — the code on the left is live, so change a number and watch the tray change.
You will learn
- What makes a tool a tool, and what a single
documentTypesline dispatches - Why one component renders both the panel and the embedded block
- Which two files carry a tool, and what
vvd.jsondeclares about it - Where to go next for blocks, host capabilities, and server endpoints
Edit and the example re-runs. Tab indents; press Escape to leave the editor.
That example is the whole surface area of a working tool. Everything else in this section is a thing you can add to it.
A tool claims a document type
documentTypes is the only registration a tool needs. It's a list of slugs, and it means
"when the world opens a document of this type, mount me."
documentTypes: ["dice-tray"]Nothing else routes to your tool. There is no registry to edit, no route to add, no place
where the platform learns your name twice. A world that installs your tool can create
dice-tray documents; opening one mounts your render. That's the whole dispatch.
Most tools claim exactly one type. Claim more when they genuinely share an editor — a
tool that renders both stat-block and stat-block-npc, say. Two types that need two
different components are two tools.
One document at a time
Your render is handed one document and only ever sees that one. A tool that wants to show a list of other documents reads the world instead — see world blocks — but it still owns exactly one document of its own, or none.
One render, two faces
Here's the part that surprises people. You wrote one component, and the platform mounts it in more than one place:
- As a document. Someone opens the dice tray from the sidebar and gets the panel — the full working view.
- As a block. Someone drops the tray into a card body, a project page, or a wiki page, and the same component renders there, block-sized.
You don't write the second one. Your render reads context.view to find out which of the
three views it's in — "editor", "embed", or "fullscreen" — and lays itself out
accordingly.
This matters more than it sounds like it does, because the embed is where most people will meet your tool. A session-prep card with a dice tray in it gets opened far more often than the tray does on its own. Blocks is the page about designing for that first.
Where the code lives
A tool is a normal npm project the vvd CLI scaffolds and bundles for you. Two files
carry everything this section talks about:
dice-tray/
├── vvd.json the manifest — id, documentTypes, capabilities, needs
├── src/
│ ├── codec.ts the document's shape (the ONE definition)
│ └── tool.tsx defineTool + your component
└── api/ optional — server endpoints (see Server endpoints)vvd.json for the tray above:
{
"id": "dice-tray",
"name": "Dice tray",
"kind": "tool",
"version": "0.1.0",
"documentTypes": ["dice-tray"],
"entryModule": "@/tool",
"codecModule": "@/codec",
"needs": [],
"capabilities": { "readsWorld": false, "writesWorld": false, "sharing": false }
}codecModule points at the one place your data model is written down. It's how
vvd save derives what an agent can do with your documents and what's queryable about
them, with nothing declared twice — see Making it agentable.
This section assumes Getting Started
Codecs, useDocument, reading the world, presence, and the CLI loop
(vvd create → run → save → publish) are all in
Getting Started. Everything here is what's specific to
tools.
Recap
- A tool is a capability that edits one kind of document;
documentTypesis the only registration it needs. - One render is mounted in three views —
"editor","embed","fullscreen"— and readscontext.viewto lay itself out. src/codec.tsandsrc/tool.tsxcarry a tool;codecModuleinvvd.jsonpoints at the one place your data model is written down.