ETOT
ETOT
TOOL · RESEARCH SYNTHESIS · PART 2
Motif
type
agentic loop
version
0.2 · MCP
surface
MCP server · CLI · FigJam via Figma MCP
author
Eric Frye
date
09.2026
install
pip install etot-motif
part 1
the loop
part 3
the plugin as product
Launch repository ↗

The critic works on anyone’s summary.

Part 1 built a loop that checks its own synthesis. Part 2 asked where it should live — and the research overturned the answer. One MCP server instead of a plugin; a real run laid out on a FigJam board by Figma’s own server; and the critic turned on FigJam AI’s summary of that board, which failed nine claims out of nine. Then the package that would have been broken on install, caught before it shipped.

A Motif insight section on a FigJam board: claim sticky, four receipt stickies with turn IDs, two pink counter-evidence stickies connected by 'contested by' arrows, an opportunity sticky, and a violet contested sticky with the critic's objection.
A Motif insight on a FigJam board, written by Claude Code through Figma’s MCP server. Yellow claim, white receipts with turn IDs, pink counter-evidence wired in, and a violet sticky carrying the critic’s objection: the cited turn argues the opposite of the claim.
9 of 9FigJam AI summary claims failed the critic
25 / 32critic agrees with the human rater on unsupported
5 tools · 3 hostsone MCP server: Claude Code, Cursor, Claude Desktop
1 dayresearch to PyPI and the MCP registry
role
Designer and builder — surface strategy, MCP design, acceptance test, board design, release
team
Solo; Claude as research analyst, Claude Code as build partner
timeline
4 September 2026
surface
MCP server on PyPI and the MCP registry; FigJam via Figma’s own MCP; CLI
tools
Python · MCP SDK 2.x · Claude API · Figma MCP · Claude Code · PyPI
the problem

A tool nobody can reach is a portfolio piece. The plan was a FigJam plugin. The plan was wrong.

the solution

One MCP server, bring-your-own-key, that any agent host can call — and a board tool that hands the host a layout instead of holding Figma credentials. The critic exposed on its own, so it can check a synthesis Motif didn’t write.

01

The plugin was the wrong surface

what the research found

Forty searches, primary sources, one afternoon. Four findings changed the plan. Figma’s own MCP server already writes to FigJam — stickies, sections, connectors. One MCP server reaches Claude Code, Cursor, Claude Desktop, Figma Make’s connector slot, Lovable, Notion agents, and Miro, which has a hundred million users and wasn’t on the list. Every research platform is converging on “citations” as a feature, so the receipt is becoming table stakes and the adversarial reader isn’t. And nobody anywhere said “I’d pay for a verifier”; people pay for repositories and for deterministic checkers.

what changed
  • 01MCP first, plugin laterA FigJam plugin reaches FigJam. A server reaches everything.
  • 02The critic is the productReceipts are the ticket in; the second reader is the reason to stay.
  • 03Bring your own keyNo paid tier yet means no ETOT-paid runs. Standard for free tools; measured as friction later.
  • 04Weave, closedMedia generation, no API, no loop. A distraction, as parked in week one.
one server · every hostresearch report, deliverable 3
motif-mcpsynthesize · critique · receiptsboard · runs_get ● HOSTS (call it) Claude Code Cursor Claude Desktop Figma Make · Lovable Notion agents · Miro solid: verified this sessiondashed: reachable per the research ● BOARDS (the host writes) Figma MCP → FigJamverified · step zero Miro MCP → boardreachable motif_board returns a layout + scripts.The host holds the credentials and runs them.Motif never touches Figma. your Anthropic keybring your own · ETOT pays nothing
A plugin reaches FigJam. A server reaches everything, and lets each host’s own board integration do the writing.

The synth is the engine. MCP is the plugin format.

02

Prove the write path on a real board first

why

The whole reorder rested on one claim: a host can write a Motif run onto a FigJam board through Figma’s server. If that was flaky or gated, the plugin moved back up. So it was tested before a line of Motif code.

what happened

Figma’s write path needs the remote server and OAuth; the desktop server is read-only. After the authorisation click, one call created a section, three colour-coded stickies and a labelled connector on a throwaway board and returned five node IDs; a read-back found all five inside the section.

step zero · one write call
use_figma(fileKey, skills=[figma-use, figma-use-figjam]) → section "I01 step-zero" → sticky claim (yellow) → sticky receipt (green) → sticky counter (red → CUSTOM) → connector "contested by" returned: 1:2 1:7 1:11 1:15 1:19 get_figjam(fileKey) → 5 nodes inside section
what it settled
write path: remote server + OAuth (desktop server is read-only) pricing: free in beta, "usage-based" later palette: yellow, green round-trip red reads back as CUSTOM consequence: motif_board returns layout + scripts host runs them; Motif holds no Figma credentials FigJam plugin: optional, not required
Tested before a line of Motif code was written. Evidence in docs/exhibits/step-zero/.
findings
  • 01Motif never touches FigmaThe board tool returns a layout and scripts; the host holds the credentials and runs them. Motif stays a pure function of the transcripts.
  • 02The FigJam plugin became optionalNot a dependency.
  • 03A palette quirkFigma’s documented red came back as a custom colour on read-back; yellow and green round-tripped. The board tool pins its palette to colours that survive the trip.
  • 04Beta, and will be paidFigma says so. Recorded as a risk on the ledger.
03

The tool I shipped couldn’t serve

context

Part 1’s one-command install worked by shelling out to two scripts and parsing their printed output for the run directory. Fine for a terminal; impossible for a server. The loop wiring lived inside a script’s main. There was no importable engine.

what was built
  • 01An engine moduleIngest, synthesise, critique, receipts, board, load-run — that the CLI, the server, and any later surface call the same way. The old ingest script became a shim; re-ingesting the corpus produced 31 files byte-for-byte identical to before.
  • 02A logger that doesn’t printOn stdio transport, stdout is the protocol. Anything the loop printed would have corrupted the connection. The logger got an emit hook: stderr by default, MCP notifications in the server.
  • 03A corpus snapshot per runA turn ID only means something against a corpus, and the CLI had been ingesting to a temp folder no run recorded. Now every run carries its transcripts.
  • 04Nineteen offline testsWith a stubbed model, including the server spawned over stdio and called through the SDK client — the test that caught a plain exception arriving at the host as “Error executing tool” with the real message hidden.

An “installed” environment is a claim, not a fact.

04

Twenty-five of thirty-two, and the gap kept

the test

Before the critic could be called done as a standalone tool, it had to agree with a human. Part 1’s six blind reports went through it two ways: as the original insight JSON, and as plain markdown with no key and no run — the way a FigJam summary would arrive.

acceptance · motif_critique vs the human scoring sheeteval2/critique-acceptance.json
checkagreereading
Unsupported, spot-checked insights25 / 32the citation layer works
P-03 — Penni’s dissent missing5 / 8known dissent gap
P-05 — positive guidance case missing2 / 8never fired on any report
RULING

Accept the measured gap. No instrument changes. Dissent hunting is the next eval’s job, with its own blind re-run.

Two of the disagreements aren’t: one is the same citation failure caught by the receipt layer (quote_mismatch) instead of the model; one repeats the objection the original run had already flagged.

Eight critic calls, 119–286 s each, about $0.33 each.

Structuring passed both ways — plain markdown with no key became fifteen claims, identical whether parsed or modelled. The verdicts agree where citations are the question and diverge where dissent is.
the ruling

Accept the gap; don’t tune. Changing the critic changes the instrument, and the instrument was measured in part 1. Dissent hunting is the next eval’s job, with its own blind re-run. The acceptance file records the numbers unchanged and the ruling beneath them.

05

Fifteen transcripts, one sentence, one board

what happened

In Claude Code: “synthesise data/raw/Dataset-2 with motif and open the report.” Thirty-four minutes, $4.88, twenty-three insights, three contested. The session — following a one-page skill — led with the contested items, verified five receipts against the corpus itself, named a theme the critic said was missing, and refused to call any of it settled.

Then: “lay this run out on the FigJam board with the motif board tool.” Twenty-three sections, every script returning node IDs, one layout collision caught and fixed by the host, a node map saved so the board can be updated in place.

claude code · one sentence
> synthesise data/raw/Dataset-2 with motif and open the report run 20260904-154633-C condition C 15 transcripts intake ×15 · synthesis · critic · revise · critic · revise · critic stop: max_iterations (3 rounds) insights: 23 (17 medium · 6 low · 0 high) contested: I-09 I-10 I-11 cost: $4.88 wall: 34 min the session, following the motif skill: · listed the three contested insights first · verified 5 receipts against the corpus itself · named the theme the critic said was missing · "Do not treat these as settled."
I-09 · the catch
claim: David "broadly agrees" funder mandates drive open practice cited: david:0026 turn: a grant requirement "isn't the right spirit of doing" — real change comes from culture and peer "data champion" networks critic: unsupported — the cited turn argues the opposite same pattern as bruce:0035 in part 1. now a violet sticky on a board.
Numbers from runs/20260904-154633-C/meta.json and output.md.
The full FigJam board zoomed out: a fifteen-section test grid on the left and the recorded run's twenty-three sections in two columns on the right.
Left, the fifteen-insight test board from the build. Right, the recorded run: twenty-three sections in two columns, contested insights carrying a violet sticky.
06

Nine claims, no receipts, nine failures

what happened

FigJam AI summarised the board. The summary went into motif_critique against the transcripts. Verdict: fail. Nine claims, twenty-one failures, two warnings, a hundred seconds, forty-nine cents.

motif_critique · FigJam AI’s summary of the board, checked against the transcriptsrun 20260904-165114-critique-doc
verdict
FAIL  9 claims · 21 failures · 2 warnings · 101 s · $0.49
by rule
bad_citation 9  unsupported 9  missing_counterexample 2  overconfident 1  merged_insights 1  vague_opportunity 1
I-03 “archiving as betrayal of the relationship” — one participant, and she never used the word. michelle:0040: the co-created story “is gone.” Counter at lisa:0040: “I want my best by them” — wants others to use the data.
I-06 “reuse is rare because researchers can’t find data” — counterexample in the corpus. sam:0065: browses the UK Data Service and contacts authors for methods.
I-08 “non-grant sectors need bodies like GSR” — Penni alone, hedged. penni:0111: “I think it would have to come from GSR… I don’t know how the ONS categorise things.” Overconfident.
all nine no turn IDs anywhere. A claim the critic has to source on the reader’s behalf is unsupported as written.
TWO FLAWS THE CRITIC SHOWED IN ITSELF — it cited an interviewer turn (sam:0068) as its own counter-evidence; and it folded the summary’s “Key Opportunities” into claim fields instead of checking them. Both on the ledger.
From docs/exhibits/recorded-run/critique-verdict.json. Receipts verified by the host against the corpus before quoting.
findings
  • 01Every claim failed on citationThe summary carries no turn IDs; a claim the critic has to source on the reader’s behalf is unsupported as written. That is the honest verdict, not a bug.
  • 02“Betrayal” was one participant’s word — and she never used itThe summary generalised Michelle’s relational concern to all researchers; Lisa wants others to use her data.
  • 03A counterexample was sitting in the corpus“Reuse is rare because researchers can’t find data” — Sam describes browsing the UK Data Service and contacting authors.
  • 04A hedge hardened into a recommendationPenni’s “I think it would have to come from GSR… I don’t know” became “non-grant sectors need bodies like GSR.” Single source, overconfident.

A claim the reader has to source is unsupported as written.

07

The wheel that would have been dead on install

what the release session found

The committed package loaded its config from a path relative to the repo root. A pip install has no repo root. The first synthesis call would have raised — and PyPI releases are immutable. Fixed before upload: config ships inside the package, with a symlink so every documented path still works; runs go to ~/.motif/runs outside a checkout; the registry’s ownership marker sits in the README because the registry reads it from the PyPI description.

empty venv · install from PyPI
$ python -m venv /tmp/v && source /tmp/v/bin/activate $ pip install --no-cache-dir "etot-motif[mcp]==0.2.0" $ motif --help config: 12 critic rules loaded runs root: ~/.motif/runs $ motif-mcp (stdio handshake) server motif 0.2.0 · 5 tools missing run → ToolError, verbatim $ mcp-publisher publish io.github.sleepycobalt/motif 0.2.0 active
caught before upload
config path relative to repo root; no repo root in a wheel → ships inside the package runs dir would land in site-packages → ~/.motif/runs registry needs "mcp-name: …" in the PyPI description → README TOML order [project.urls] above dependencies swallowed them description registry caps at 100 chars releases are immutable. fix first.
An editable install hides what the wheel omits. The smoke test now installs the built wheel into an empty environment.
then

pip install etot-motif into an empty environment: motif --help runs, twelve critic rules load, motif-mcp completes a stdio handshake reporting five tools. Published. Registry entry io.github.sleepycobalt/motif, active. A one-time token, revoked afterwards.

08

Still open

OPENNo users yet. Installable now; installed by no one but me. The first ten installs and three pieces of feedback become the next section on this page.
OPENDissent detection is the weak layer. 5 of 8, 2 of 8. The v3 eval’s job.
OPENThe critic doesn’t check its own citations. It cited the interviewer once. Fix candidate: pass the critic’s located turns through the receipts check.
OPENRecommendations aren’t claims yet. A summary’s opportunities section was absorbed, not evaluated.
OPENTwenty-three insights, none high-confidence. The recall rule adds; nothing enforces the ceiling after revision, and a critic that hunts dissent rarely leaves a claim with no counter-evidence. Both need a decision.
OPENFigma’s write path is beta and will be priced. If it changes, the thin FigJam plugin comes back.
PUBLICgithub.com/sleepycobalt/motif · pypi.org/project/etot-motif · the board, the verdict, and the acceptance numbers are all in the repo.

One day, one server, five tools, three hosts, nine failed claims that FigJam called a summary.

CONTINUEDPart 3 — the plugin as product. A hosted engine on the user’s own key; a Figma and FigJam plugin that draws the board itself; the privacy line that was false until a test caught it; a Community listing, submitted. September 2026.
Motif · part 2 · ETOT · research synthesisCorpus: Hanchard & San Roman Pineda 2023, University of Sheffield, CC-BY-NCEric Frye · 2026