EMET
EMET is a persistent MCP memory server: one verified, MongoDB-backed shared memory for AI agents, bots and chats across every device, with layered memory, a word-for-word transcript tier, and versioned document control.
Start and end sessions —
emet_initialize(paged startup withload_id),emet_session_open,emet_transcript_appendafter each reply, andemet_session_closeto verify the work actually saved.Remember and recall in six layers — identity, semantic, episodic, procedural, meta, working:
save_to_layer, keywordrecall, filteredquery_layer, meaning-basedsemantic_recall, andrevise_memory(supersede, never edit or delete).Keep the verbatim record —
save_transcript,revise_transcript,query_transcriptsstore the conversation word for word, separate from the curated layers.Control documents —
write_doc,read_doc(paged),read_doc_history,list_docs,patch_doc(exact string edits),retire_doc,rename_doc,restore_doc; nothing is ever deleted, prior versions are archived.Validate against templates —
validate_docandaccept_nonconformancecheck form and continuity, marking rather than rejecting failures.Trust the store — every write is read back and compared (
aleph), rows carry sha256 digests, an optional HMAC and a hash chain, andemet_gapscounts holes, mismatches and broken links.Manage access —
emet_invite_create(read-only guest),emet_member_access_create(read/write member),emet_client_approve(approve or deny new clients); owner-controlled, off by default.Diagnose and configure —
emet_statuswithprobe: trueproves the write path,emet_setup_completeruns first-time setup,emet_floorrereads the standing floor or a GEM.Share across hosts — stdio locally and remote HTTP with OAuth from one source tree, so desktop, CLI, browser, phone and other vendors' agents reach the same store; source tags record who wrote what.
Stores agent memory in MongoDB (Atlas or self-hosted) as the server's persistent backing store, persisting curated claims, layered memory records, and the separate verbatim transcript tier across sessions and hosts, with every write read back for verification.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@EMETremember we chose MongoDB Atlas, then read it back to verify"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
EMET
EMET is an MCP (Model Context Protocol) memory server, persistent shared memory for AI agents, bot teams and chats.
אמת — Hebrew for truth. Also Enduring Memory & Epistemic Tiers.
Website: https://dreamforgestudiollc.github.io/EMET/
See also: CHANGELOG · CONTRIBUTING · SECURITY · Charter · Feature map · Development practices
What it does
Layered memory. Six layers — identity, semantic, episodic, procedural, meta, and working — hold curated claims, split by what kind of thing an entry is.
Verified writes. Every write is read back and compared before it is reported as saved.
Word-for-word transcript tier. A separate tier stores the conversation as it was written, so a summary cannot overwrite the record it came from.
stdio and remote HTTP with OAuth. The same server runs locally over stdio, and remotely over HTTP with OAuth. The transport changes. The store does not.
MongoDB. Atlas, or MongoDB you run yourself. A one-machine trial can use MongoDB 6 or newer. Only
semantic_recallneeds Atlas.Golem Expertise Module (GEM) socket. Add-on behavior ships as GEMs, seated at startup and checked by
gem-checkagainst the GEM standard ingem-library/. A GEM ranks below the core floor and below the user's current word, and cannot remove a protected floor line. Two work-in-progress examples ship bundled and stay off unless you ask: Neurendipity (neurodivergent engagement and autistic burnout support; support only, not counseling or treatment;EMET_ACTIVE_GEMS=neurendipity) and Lean Six Sigma (DMAIC and DMADV guidance, templates, and client-side calculators;EMET_ACTIVE_GEMS=lean-six-sigma). Fieldnote stays in the tree as a developer case-study of a runed GEM. It is not a user-facing third GEM, and it is off by default.Bot-team backup (optional). Off unless the first-run interview records yes. Yes is the recommended choice. The recipe and the recovery template ship as the bundled bot-team-backup GEM. EMET does not copy prompts or settings itself, and secret values are never stored.
Client-side GEM execution. A GEM may declare a tool. EMET does not run that computation. EMET verifies the record, not the computation. The client runs the tool, and what is written back is checked the same way as any other write.
Released flag. A GEM is seated only when its released flag is the boolean true. Any other value is refused. A missing flag is refused except for a legacy manifest pinned by id, version, and file digests.
Per-client attribution and approvals. Every write is attributed to the client that made it. A new client is read-limited until it is approved. The approval is recorded.
Error codes and problem reports. Every error carries a stable
EMET-<AREA>-<NNN>code. An operational error log keeps 10,000 entries or 90 days, plus daily summaries. The owner-onlyemet_error_reportreturns the report and an email draft that you send yourself. See Reporting a problem.verify-emet and the feature map.
npm run verifyrunsscripts/verify-emet.mjsfrom a checkout. It starts EMET in a temporary directory on the in-memory test database, with no.env, and drives every registered tool. A tool that needs a service a plain install does not have is reported as a gap. A run whose only problems are gaps exits 3. A failed tool exits 1. The map is docs/features/README.md.MIT. Version 0.3.1. EMET is still 0.x: stored document shapes may change before 1.0. Pin a version if you depend on them.
The store has one owner, and other people can join only through the optional Guest (read-only) and Member (read and write) access, both off by default.
Related MCP server: io.github.admiralpunk/provena-memory
Document control (ISO 9001 7.5.3 / ISO 13485 4.2.5)
EMET is built for document control in environments that work under ISO 9001 and ISO 13485. It supports that work; it holds no certification and claims no conformity. An organization that uses it stays responsible for its own quality system, including validating EMET for its intended use.
No delete, on any store, by design. No tool removes a memory entry, a document or a transcript. The database role EMET runs under is not granted remove on the record either.
Corrections are made by adding. A memory entry is superseded by a revision that stands alone. A document is revised or retired. A transcript part is revised. Events are never rewritten.
Retired means obsolete, not gone.
retire_docmarks a document obsolete and keeps its id, content, version and digest.read_docstill returns it, flagged retired, with the reason.Prior versions are retained. Every document write archives the copy it replaces, and
read_doc_historyreads any retained revision back, checked against its stored digest.Records are superseded whole, never patched. The handoff and any other record document cannot be patched. A new revision is written in full and names the version it supersedes.
Templates are controlled documents. Base templates ship with the release, and the shipped copy governs. Every templated write is checked against its template, and a write that fails is kept and marked for review, never silently accepted or discarded.
A test holds the line.
write-path.test.jsfails if anything insrc/removes from a layer, document or transcript collection, andatlas-role.test.jsfails if the database role grants remove.
30-second start
Guided setup: docs/GUIDED-SETUP.md — never deployed anything. Your assistant installs, configures, deploys, and verifies. You sign in when asked and paste the connector URL.
Setup: docs/SETUP.md — the manual walkthrough: MongoDB, both transports, and the first-run interview.
Deploy: docs/DEPLOY.md — remote HTTP with OAuth, so a phone, a browser, or another vendor's agent reaches the same store.
Status: 0.x. The stored document shapes are not frozen yet. Breaking changes to the schema may land before 1.0. Pin a version if you depend on it.
Set
EMET_SOURCE_TAGSbefore you let more than one host or bot write. The source-tag guards (unregistered tags refused on the main records, and on another bot'sbots/folder) are off unlessEMET_SOURCE_TAGSis set. With it unset, any caller can write under any tag. See.env.exampleand docs/DEPLOY.md.
Install as a Cursor / Grok Bot plugin
EMET ships a plugin for Cursor and Grok Bot (this repo's .cursor-plugin/plugin.json and
mcp.json). The Cursor Marketplace listing is submitted and pending review. The plugin does not host memory for you: it connects to your own EMET
server, so your records stay in your MongoDB.
Deploy EMET remotely: docs/DEPLOY.md. Set
PUBLIC_URLand a strongEMET_OAUTH_PASSPHRASE. Checkhttps://<your-host>/healthreturns{"ok":true,...}.Install the EMET plugin and set the variable
EMET_URLtohttps://<host>/mcp.Connect. The OAuth sign-in opens EMET's passphrase page; enter your passphrase once per device.
Verify: call
emet_statuswith{"probe": true}(it writes, reads back and removes a row from_emet_verifyonly), then save withsave_to_layerandrecallit from another client.
The plugin also ships an emet-memory skill that tells the agent when to recall, save and close
a session.
Shared memory across hosts
EMET stores agent memory in MongoDB (Atlas or self-hosted) across six layers, keeps a separate word-for-word
transcript tier, and verifies every write by reading it back. It runs as a local stdio server
and as a remote HTTP server from the same source, so it works as a bridge: AI models on several
hosts — desktop, CLI, browser, phone, another vendor's agent — read and write one shared
memory, and the work, checks, and decisions one leaves behind are there for the next. That
includes Grok Bot teams and single-session Grok chats connected to the same server: what the
bots save is there for a fresh Grok chat to find once it initializes, and the reverse. To
connect a single-session chat (a Grok project, or any chat host that takes a remote MCP
connector and supports its OAuth sign-in), add your deployed server's URL,
https://<your-host>/mcp, as a remote MCP connector there and complete the sign-in with the
passphrase you set (EMET_OAUTH_PASSPHRASE; see
Connecting clients). A connector that only takes a fixed
header or key will not work. Then put the instruction block from
docs/BOOTSTRAP.md in the project instructions: the chat reaches for that
memory most reliably, unprompted from the first turn, with the block there, or, as a fallback
where there is no such field, with it pasted as the first message. The store has one
owner, and other people can join only through the optional Guest (read-only) and
Member (read and write) access, both off by default.
Two ways in
A session that has not initialized can read everything and can write only a new drop (drops/<date>-<slug>.md): its items, wrap-up and handoff. A session with a registered source and the full startup has full access. Every initialized session sees unhandled drops first. A board edit is refused only for a drop that was listed at that session's boot. New drops are capped at 20 per New York day, in one bucket: the whole local process on stdio, one shared visitor bucket for HTTP with no signed-in client, the source tag bound at approval when there is one, or that OAuth client otherwise. A source the caller types does not move the bucket. Drops are never deleted.
Editing working state
The server provisions a blank board at setup. No session can write the board. It is edited with patch_doc and a reason; an added row names its origin. Once any working document exists, it is edited, not rewritten. A change that leaves a document under half the size it had when this session first touched it, or under half of its largest size in the last 24 hours, is refused. retire_doc refuses a working document. rename_doc issues a new id and marks the old id retired; the text stays readable and stays in the list. restore_doc is the one whole-document recovery, for an owner-listed tag, and only a stored conforming revision whose digest matches.
The name
EMET is אמת — Hebrew for truth. It is also an acronym for what the server does: Enduring Memory & Epistemic Tiers.
The choice is not decoration. In the golem legend the word emet is inscribed to animate the body, and erasing its first letter leaves מת — met, dead. One character is the whole difference.
That is why the verification layer is called aleph. Every write in EMET is read back and
compared before it is reported as saved. That read-back is the aleph: a memory system without
it still returns answers, still looks healthy, and is quietly a corpse — confidently serving
things it never actually stored. Aleph is also the first letter, which matches the order the
protocol insists on: verify before retrying, verify the premise before applying the remedy,
read back before trusting.
The same image explains what setup is for. A golem is inert until the word is written on it;
a fresh model instance is unformed until the record is written into it. emet_initialize is
that inscription — it hands the model an identity, the person it serves, where things stood,
and the disciplines it is bound by. Nothing mystical is claimed here. It is a literal
description of what the first tool call does.
Both halves of the acronym are load-bearing.
Enduring is not a modest claim, and it is not a feature competing with other memory products. It is measured against what a language model is by default: an etch-a-sketch. Every session starts from nothing, and ends by forgetting everything. You explain yourself again, re-establish what you are working on again, re-derive decisions you already made. Enduring names the difference between an assistant that knows you and one meeting you for the first time, again. That is the character of the thing, not a marketing adjective attached to it.
Epistemic Tiers is how that endurance stays trustworthy. Tier 1 holds curated claims; tier 2 holds the verbatim record that justifies them — justified belief and its evidence, kept structurally separate so neither can quietly overwrite the other. Memory that persists but cannot be checked is just confident noise that survives longer.
One memory, every device
This is the part worth understanding before anything else.
Most MCP memory servers run as a local process: your desktop launches it over stdio, and the memory lives wherever that process can reach. That works until you pick up your phone.
EMET ships two entry points over one source tree:
| stdio | desktop and CLI, launched by the client |
| streamable HTTP + OAuth | phone, tablet, browser, another vendor's agent |
They are not two projects. Both call the same dispatchTool in dispatch.js, which calls
tools.js and database.js. src/index.js also imports validation.js. src/http-remote.js
does not import validation.js or content_analyzer.js; tools.js imports the analyser. The
transport is the only thing that differs. There is no fork to keep in sync, no second copy
drifting out of date, no "mobile version" lagging the real one. A fix lands once and both
transports have it.
Deploy the HTTP entry point to any host that can run a container — the Dockerfile is
included, and docs/DEPLOY.md walks through Railway and Fly — and every device
you own reaches the same store. Write a decision from your laptop, ask about it from your
phone an hour later, and it is simply there. Add a third host and it joins the same memory
rather than starting its own.
The remote endpoint is OAuth-protected: a device is authorized once with a passphrase you set,
and the token persists across restarts and redeploys. Leave EMET_SOURCE_TAG unset on a
server several hosts share. Over HTTP, document, transcript, and memory writes use the source
tag bound at approval. A local stdio host still passes an exact tag from EMET_SOURCE_TAGS
as source on its writes (see .env.example). Every entry records which machine produced
it — with several hosts writing to one store, provenance stops being optional bookkeeping and becomes the thing that lets you trust
what you read.
Running cost for a single user is a small MongoDB cluster plus a hobby-tier container host. Both have free tiers adequate to start.
Best practices
Split the work by what each host does best. A single-session chat (for example, a Grok project chat, connected as described at the top) only works when you send it a message. It can't watch EMET, and bots can't message it. So when a bot team needs the chat's help:
The bots leave a task in EMET with everything needed to do it, in a document whose id they give you.
You send the chat one line that names that document, for example: "Initialize EMET, read threads/open-tasks.md, do the task, and save the result to EMET."
The chat saves its draft to EMET.
The bots check it.
Good fits for the chat: long drafting and review done in one sitting. Good fits for the bots: work in the repository, on the server, on a schedule, or in accounts. If the chat runs on a separate plan, its work draws on that plan's allowance, not the bots'.
Fetch every startup page, then open the session with that startup's load_id. Call
emet_initialize {} first and fetch every page it names; ask for {"page": "all"} only on a
host known to read long results whole. Pass the load_id from those pages to
emet_session_open. An open with no load_id, with a page missing, or with a load_id the
server has no record of (one from before a restart, or 60 minutes past its last page) is
refused, and the reply says what to call. The page check runs on your own startup, and a
skipped page is refused before anything is written. See docs/BOOTSTRAP.md
and docs/BOTS.md.
Set the source-tag list before more than one host or bot writes. The guards that refuse
an unregistered tag are off unless EMET_SOURCE_TAGS is set; with it unset, a stdio caller can
write under any tag. Over HTTP the tag is the one bound when that host was approved. Add each
host's tag to the list before that host's setup interview. With several hosts writing one store,
the tag is what lets you trust who wrote what. See docs/SETUP.md.
Put the instruction block in the project's instructions. Connecting the server gives the assistant the ability to remember; it does not make it use that ability. A host with an instructions field is the strongest integration: paste the block from docs/BOOTSTRAP.md there. Where there is no such field, paste it as the first message of a session. With the block in place, the assistant reaches for memory most reliably from the first turn, instead of answering from what it can infer and saving nothing.
Approve reads standing, and keep an eye on the three writes that replace a document. Reads cost nothing and cannot be wrong destructively, so a reasonable default for most users is standing approval for reads. Additive writes are recoverable, because a correction supersedes what it corrects rather than overwriting it. The three replacing writes archive the prior version, but a wrong one is silently a different document rather than an error. Do not carry this posture to a filesystem or shell server. See docs/PERMISSIONS.md.
Reconnect each host after an upgrade that changes the tool list. Hosts cache the tool list: after a deploy that adds or renames tools, each host keeps offering the old set until you reconnect the connector there. New tools stay invisible until the host sees the new list, and the host may ask again for approvals you had already given. See docs/DEPLOY.md.
Register a bot before its first write, and set both lists in one change. One bot, one tag.
In the same deployment, add the tag to EMET_SOURCE_TAGS and append bots/<tag>/HANDOFF.md
to EMET_RECORD_DOCS. A registered tag that is not listed as a bot can still write the main
records. The hub's tag is registered as a host and is never listed as a bot. See
docs/BOTS.md.
Prove the write path before you trust a new deploy. Run emet_status with probe: true.
It writes a test document, reads the same value back and removes it; a read alone cannot show
a broken write path. A role missing a permission fails there, loudly, instead of in the middle
of a session. Then save something from one device and recall it from another. See
docs/SETUP.md.
Keep house style in the store, not pasted into every project. A document written with
write_doc is read by every host and changed in one place; text pasted into three projects is
three copies that will disagree. Delivery preferences belong in bootstrap/USER.md, not in one
vendor's own memory, which does not travel to the next host. See
docs/BOOTSTRAP.md.
Attribution
EMET is a derivative of CASCADE Enterprise RAM by Jason Glass / C.I.P.S. LLC, published
at github.com/For-Sunny/cascade-memory-enterprise, used and redistributed under the MIT
license the project adopted at commit acdb5c8c99cdfc29fb44db2cd828d7752be744db (v2.2.2,
2026-02-19). Both copyright lines are retained in LICENSE.
Link unavailable: as of 2026-10-03 that repository no longer resolves (GitHub answers 404), and no moved copy, public mirror or archive of it was found. The address and commit are kept here as the record of where this code came from.
How much is upstream, measured rather than estimated. Line-level comparison of this tree against upstream v2.2.2 at the commit named above.
Method, stated so it can be reproduced: exact, case-sensitive comparison of non-blank
lines; an upstream line counts as retained if the same line is present in this tree's file.
"How much of upstream survives here" and "how much of this file is upstream's" are different
questions with different answers, so both are given. scripts/measure-upstream.mjs
regenerates this table against a clone of the commit above (while the upstream repository is unavailable, that needs a copy kept from before) — run it after any change to
src/, because a figure nobody can cheaply re-derive is a figure that goes quietly stale.
File | Lines here | Upstream lines retained | of upstream | of this file |
| 450 | not recomputed | — | — |
| 1163 | not recomputed | — | — |
| 390 | not recomputed | — | — |
| 4273 | not recomputed | — | — |
| 811 | not recomputed | — | — |
| 269 | — | — | no upstream counterpart |
| 702 | — | — | no upstream counterpart |
| 206 | — | — | no upstream counterpart |
| 26 | — | — | no upstream counterpart |
| 4803 | — | — | no upstream counterpart |
Lines here were counted on 2026-10-07 (every line, including blanks, with one trailing empty line dropped). The retained percentages were not recomputed: the upstream repository no longer resolves, and no local copy of that upstream commit was available. The last measurement, against an earlier tree, was content analyser 361 of 394 (91.6% of upstream, 93.5% of that file), validation 765 of 798 (95.9%, 78.2%), index 541 of 631 (85.7%, 74.0%), tools 798 of 1007 (79.2%, 33.2%), and database 353 of 651 (54.2%, 40.2%). Other files under src/ also have no upstream counterpart and are not in this table.
Upstream's server/decay.js and server/healthcheck.js have no counterpart here and are not
carried at all. Of the five files that do have one, the content analyser and the validation
layer are substantially Jason's work, and most of his tools.js and index.js survives here
as well — the routing patterns, the validators, and the error classes are his. What changed is
described below; what did not change is his.
What diverged from upstream
Storage: SQLite on a RAM disk → MongoDB. Upstream's
ram_disk_managerandserver/decay.jstemporal-decay engine are not carried forward, and the SQLite/RAM code paths have been removed rather than left dormant.Transport: stdio only → stdio and streamable HTTP with OAuth, from one source tree.
Tools: 7 → 29. Three are carried from upstream (
recall,query_layer,save_to_layer);remember,get_stats,get_echo_statsandget_statusare dropped as duplicates or folded intoemet_status. Twenty-six have no upstream equivalent — the document store (read_doc/read_doc_history/write_doc/list_docs/patch_doc/retire_doc/rename_doc/restore_doc/validate_doc/accept_nonconformance), the transcript tier (save_transcript/revise_transcript/query_transcripts— which also reads the pre-2026-07 exchange archive),semantic_recall,revise_memory,corpus_recall,emet_gaps,emet_floor, the setup and session family (emet_initialize,emet_status,emet_setup_complete,emet_session_open,emet_transcript_append,emet_session_close), and the two access doors (emet_invite_create,emet_member_access_create).The write path: upstream validates and inserts. Here every layer write, transcript write and document write passes through one door that scrubs secrets, decides the source tag, fills missing accountability fields with visible markers, digests content and metadata into their own columns, reads the row back, and returns one receipt shape. A test fails the build on any second door. See The door below.
Metadata vocabulary: upstream's whitelist relocated any field it did not know under
custom. EMET's own fields —supersedes,superseded_by,superseded_at,corrects,corrected_by,valid_until,valid_from,routing,assertion_origin, and the marker booleans — are first-class, and anything that is still relocated is named in the receipt.Charter as data:
layers.jscarries, beside each layer's prose, its numeric importance band, whether it may be revised, whether provenance is required, its aliases and its expected share, and the write path reads those rather than hardcoding them.
No claim is made that layered memory, entry supersession, or a split curated/verbatim store originate here. Comparable ideas ship in other projects. EMET's aim is that the substrate is not something you have to think about.
Install
Never deployed anything? Let your AI do it: docs/GUIDED-SETUP.md. You sign in to two services when asked and paste one URL; the assistant installs, configures, deploys and verifies the rest. It needs a shell — Claude Code and Cowork have one; Claude Desktop gets one from an MCP server such as Desktop Commander (one command, in the guide).
Full walkthrough: docs/SETUP.md — Atlas, both transports, and the first-run interview. Deploying remotely: docs/DEPLOY.md. Making your assistant actually use it: docs/BOOTSTRAP.md. Running bots alongside your assistant — registering their tags, their own handoffs, the session loop: docs/BOTS.md. For designing a bot team, see docs/SUBKEYS.md.
Continuous chats. A bot in a long-running chat saves its transcripts to EMET
word for word at least nightly (a checkpoint), under the open session or a daily
YYYY-MM-DD_chat-daily session. Prefer event triggers over time-based polling.
When polling is unavoidable, use the longest interval the job allows. Never
reconstruct turns. Mark gaps as gaps. Session close cites those saved parts.
Context compression erases older turns, so a close days later cannot invent the
transcript.
Short version:
npm install
cp .env.example .env # fill in MONGODB_URI and MONGODB_DB
npm startRequires Node 22+ and a MongoDB connection. .env is gitignored — never commit it.
Local (stdio) — as an MCP server entry:
{ "mcpServers": { "emet": { "command": "node", "args": ["/path/to/emet/src/index.js"] } } }Remote (HTTP) — npm run start:http, or deploy the included Dockerfile to any container
host. Set PORT, PUBLIC_URL (the OAuth issuer — it must match the deployed URL exactly) and
EMET_OAUTH_PASSPHRASE. Then add https://<your-host>/mcp as a custom connector in each
client. See docs/DEPLOY.md.
Give every host its own source tag. Over HTTP, document, transcript, and memory writes use the
tag bound at approval; the caller does not pass source on those writes. A local stdio host
still passes an exact registered tag as source. EMET_SOURCE_TAGS registers the tags. Leave
EMET_SOURCE_TAG unset on a server several hosts share, since a default tag makes every
host look the same; set it only where one host uses the server (a local stdio entry, for
example). Every write records the host that produced it; a shared default tag is ambiguity
rather than attribution.
First run
Say "initialize". That is the whole interface.
emet_initialize is the front door and the only thing anyone needs to know about. On a new
store it returns a short interview — what the assistant should be called, how it should carry
itself, your name, what you are using this for, and whether more than one bot or AI will use
this memory — which your assistant runs conversationally,
then records with emet_setup_complete, which also creates the owner person. Whether more than one bot or AI will use this memory is optional; leaving it out leaves delegated client sub-keys off. A yes writes them only when this server reads .env, and they take effect on restart. A hosted server refuses and tells you the three variables to set, then redeploy. See docs/SUBKEYS.md. On an established store the same call returns the
assistant's identity, your profile, and where things were left, so the session opens already
knowing you. A read-scoped emet_initialize writes nothing; a write-scoped one may provision a missing board. First-time setup is the owner's:
emet_setup_complete is refused under a bot tag, and on a store that is already set up it is
refused unless reissue: true is passed on the owner's explicit word.
Requiring a setup command would have been backwards: the one time you need it is the first session, before you have read anything. So the branch happens inside the tool, and you never have to know which case you were in.
Connecting the server gives your assistant the ability to remember; it does not make it use that ability unprompted. docs/BOOTSTRAP.md has a short block to paste into your project instructions, and explains what still works on hosts that have no instructions field at all.
Two behaviours worth knowing. Setup refuses to finish on partial answers rather than inventing an identity for itself. And if the store cannot be reached, initialize says so plainly instead of proceeding as though memory were available — fabricated continuity is worse than an honest gap.
Every session after that: how to start, how to end
Memory is only as good as the two moments at the edges of a conversation. Nothing your assistant learned survives unless it was written to the store, and the moment to write it arrives without warning — a closed tab, a context limit, a phone call. So every session has one word at the start and one at the end.
Start: say "initialize". Your assistant calls emet_initialize, which returns who it is,
who you are, where things were left and the operating disciplines, and picks up from there.
Before it asks you anything, it names your items from the handoff and state documents: anything
left half-finished, what is dated in the next seven days, each open item you own with its next
action, and whatever was left undecided. An item not named at the start of a session is easy
to lose, so a count or a bare "where do you want to start?" is not treated as a startup.
Do this in every new conversation, on every device — the first message, before the question
you actually came to ask. A session that skips it is a session that answers from guesswork
and saves nothing.
End: say "wrap up" (or goodbye, or that you are opening a new session). Your assistant
then writes four things — the handoff, the state document, an episodic entry for the session,
and the verbatim transcript — and calls emet_session_close, which checks that each one
actually landed and returns state: "complete". Wait for that word before you close the
window. If it comes back anything else, something did not save, and the assistant will say
which.
Why both, every time: the layers are curated meaning and the transcript is the word-for-word
record; the next session reads the first and can reach into the second by timestamp. Close
without the ritual and the next session inherits a hole — it cannot see what happened, only
that something did, and emet_gaps will count it. Start without it and the assistant is a
stranger with your tools. The instruction block in docs/BOOTSTRAP.md
makes both of these automatic on hosts that take standing instructions; on hosts that do not,
the two words are what you supply.
Design notes
The six layers
Memory is split by what kind of thing an entry is, not by topic. Each entry carries an importance score and optional temporal validity.
Layer | Holds | Importance | Written |
identity | Who the assistant is and who it serves. Durable self-definition and the user's standing requirements. | 0.9 – 1.0 | Rarely, by design |
semantic | Facts that stay true regardless of when they were learned. People, systems, definitions, established conclusions. | 0.6 – 0.9 | When a durable fact is established or corrected |
episodic | What happened, and when. Session records, problems solved, progress through long work. | 0.4 – 0.7 | At session close and at milestones |
procedural | How something is done, and decisions that govern future action — with the reasoning. | 0.5 – 0.8 | When a decision constrains later work |
meta | Everything visible only from outside the exchange: how the memory system behaves, and how the assistant behaves toward the user. | 0.6 – 0.9 | On retrieval failures and wins, protocol drift, and corrections to how the user is worked with |
working | Short-lived context, in-flight state, notes for whoever picks the work up. | 0.3 – 0.6 | When parking state between sessions |
The distinction that does most of the work: episodic, semantic and procedural are about the world — what happened, what is true, how something is done. Meta is about the system — looking back at the exchange from outside it. Material belonging in meta feels like nothing else fits it, which is precisely why it gets discarded when meta has no definition.
Every layer works from the first install
Creating six collections is not the same as having six layers. A layer with no stated charter has no answer to "does this belong here?", so nothing routes to it — and an empty collection throws no error and fails no test, so it can stay empty indefinitely without anyone noticing.
In the deployment this design came from, the meta layer sat nearly unused for about a year. The collection existed from day one and the tools accepted writes to it; it was simply never the obvious place to put anything. When it was finally given a definition it filled immediately — which is the proof that the material had been there all along and was being thrown away.
Two things in this repo exist because of that:
src/layers.jsships the charter for all six layers in code, andemet_initializereturns it — on a brand-new store as well as an established one. Same reasoning as the operating disciplines: documentation is read by people, and this has to reach the model at the start of every session. What belongs, what does not belong, when to write, when to read, and what the layer's absence looks like.assessLayerHealth()reports a layer that has gone dark. A store cannot know what should have been written, but it can see that one layer took almost nothing while the others filled, and say so in the first payload of the next session. Run against the real history above, it flags meta at 10 entries / 1.8% of the store — and goes quiet once the layer is in genuine use. It reports; it never blocks, and it stays silent on a store too new to have a pattern.
Two tiers, deliberately independent. Layer entries are curated meaning; a separate transcript store holds the word-for-word conversation. When a layer entry is relevant but thin, the transcript holds the depth that was compressed out of it. Summarising at transcript-write time collapses the two tiers into one and destroys the second line of inquiry — so the transcript path stores exactly what it is given. If a payload will not transmit, split it across parts; do not compress it.
How tier 1 reaches tier 2 — two links, one precedence rule.
An entry records the transcript span it came from: session_id plus a range of exchange indices,
written at save time as derived_from. Entries consolidated from other entries carry
{entries: [id, ...]} instead, because an entry with no transcript origin must be able to say so
rather than carry a nearest-timestamp guess dressed as a citation. Both are stored as top-level
indexed columns rather than inside the metadata subdocument, so the reverse question — every
entry drawn from this transcript — is entirely a query.
Every entry also has a timestamp, so a window around it can always be searched. The two are not redundant copies: the pointer is exact but conditional, the timestamp universal but approximate — it depends on choosing the window, degrades when sessions overlap, and when it is wrong it returns the neighbouring span rather than failing.
Precedence is what keeps them from conflicting: the pointer wins whenever present, the
timestamp is the fallback when it is null. Never averaged, never combined, and the timestamp is
never preferred merely because it returned something. resolveProvenance() applies the rule so no
caller has to decide it.
Keeping both buys something neither gives alone — they can check each other. If an entry's
pointer names a session whose span sits nowhere near that entry's own timestamp, something is
wrong: a mis-written pointer, a field copied from another entry, an entry attributed to the wrong
session. crossCheckProvenance() reports the disagreement. Two independently derived answers that
can disagree is redundancy in the engineering sense; one link that can only be trusted is not.
The reverse direction — what did we take from this session? — is not a second structure.
source_session_id is indexed, so it is the same data read the other way. A stored reverse index
would be a mirror of data that already exists, and mirrors are how forks are born.
Entries written before pointers existed keep a null pointer and resolve by timestamp, and that remains correct for them — superseded, not wrong. They are not backfilled: the only way to invent a pointer for them is to infer it from the timestamp, which is exactly the weaker method the pointer replaces. Old records are not rewritten to look like new ones. Note also that an empty timestamp-window search is not proof that nothing was recorded — a period may hold no transcript at all.
Supersession, not mutation. revise_memory writes a new entry carrying supersedes and
marks the parent with superseded_by and superseded_at. Nothing is edited or deleted, and the
parent is not hidden. Recall ranks by how many of the query's words match, then by importance.
Current entries come first in every order, including time order. The store applies that same order before it keeps the window, so a stronger match is not dropped for a lower importance or an older time. A word matches as written, so $gt is that text, and capital and lower case match, including École and école. İ matches only itself. Full-width letters fold only with their full-width counterparts. ẞ and ß fold together. The store and the page use that same match. Time order is what you get
from order: 'latest'. A standalone "latest", "newest", or "most recent", together with other
words, reorders those matches and does not change which rows match. A hyphenated word such as
latest-budget, "not the latest", and a bare "latest" are ordinary keywords.
A superseded or expired entry stays in the same result, marked
with its end date and the id of what replaced it. Pass history: true when current matches fill
the limit and you still need the ended ones. Episodic entries
are immutable — events do not get revised.
Corrections, not supersession, for partial fixes. When a later entry corrects only part of an
earlier one (one item of an episode, say), superseding puts an end date on the whole parent,
including the parts that still stand. The parent stays readable, marked ended. Instead the new entry carries metadata.corrects: <parent id> (same layer; a missing
parent is refused before anything is written), and the parent is marked: its corrected_by list
gains the new id, its content, digest and liveness are untouched, and its metadata digest is
re-attested in the same write. Reads return corrected_by, so a reader of the parent sees that a
correction exists. corrected_by is a door marker; a caller cannot set it. Entries corrected
before this existed (e.g. episodic ref 604 correcting ref 603) carry no link; the fix is a new correction
entry naming corrects: 603, not an edit or a backfill.
Verified writes - src/aleph.js. Every write is read back and compared before it is
reported as saved. Document writes archive the prior version, then confirm both the sha256 and
the version; layer writes confirm the stored content at the id that was allocated. The id is
checked by content rather than existence on purpose: ids come from a counters collection, and a
counter read from the wrong collection restarts at 1 and silently overwrites real entries -
which looks exactly like a successful save.
A verification failure raises AlephError, deliberately distinct from a database error. A
database error means the store could not be reached and the write probably did not happen. An
AlephError means the call succeeded and the store does not contain what it should. That is
the more serious of the two and the one that must never be retried blindly, so it is never
flattened into "the save failed".
The aleph answers two questions, at two moments. At the write: was it recorded accurately?
The read-back above, before any receipt. At every later read: is it still what was
recorded? Each row carries its content and metadata digests, an HMAC signature over both
(when EMET_SIGNING_KEY is set; the key stays on the service), and a link in its layer's hash chain (each entry's chain
digest covers its own content digest and its predecessor's). Every read recomputes the digests
and reports integrity; emet_gaps walks the whole chain for any host and counts a row altered
after writing (broken_chain) or removed from mid-chain (chain_discontinuities). Expected
count for both: zero.
The door
Truth integrity, accountability and validation are meant to be a guaranteed property of the
store, not an option a model can decline to use. A file a model can be told about is a file it
can ignore. So the guarantee comes from a property, not a module: exactly one way to write
each store, with the checks inside it. saveMemory for the six layers, writeDoc for
documents (patch_doc delegates to it), saveTranscript
and reviseTranscript for the verbatim tier. tests/write-path.test.js scans src/ for every
MongoDB write verb and fails the build on any that sits outside those functions — it found and
removed a second document door in setup.js.
Inside the door, in order:
Secrets are masked before anything is stored or hashed (
src/redact.js): the literal values of secret-bearing environment variables (includingEMET_SUBKEY_PEPPER), credentials inside URIs, bearer tokens, JWTs, andkey: valuepairs whose key names a secret become*****. The mask is visible on purpose, the receipt'sredactedreports the number of spans this write changed (text already carrying the mask is left alone and not counted, so a read-modify-write of a document reports zero for the masks it already held), and the same scrubber wraps every rendered log line.The source tag is decided here, not by the caller over HTTP. Document, transcript, and memory writes use the tag bound at approval. On stdio it is the caller's exact registered tag, else the environment default marked
source_defaulted: true, elsenullmarkedsource_missing: true.Every missing accountability field becomes a visible marker rather than an absence —
provenance_missing,assertion_origin_missing,importance_defaulted,source_unregistered— because absence cannot be queried and "missing" can. A missing field is flagged, not refused, with one exception (2026-09-29): a semantic or procedural entry with no source is refused, because a durable claim that cannot point to where it was said is exactly what the record exists to prevent. The door stamps the source from the session's transcript position when the caller names its session. Importance defaults to the layer's charter midpoint; a value outside the band is kept and warned about. The door never infers an assertion's origin.The routing decision is kept in
metadata.routing— whether the layer was chosen by the caller or fell back, and what both routers thought — so routing can be measured later instead of remembered. An unlabelled write is never placed by a router: it lands inworking, flaggedrouting.method: 'fallback', with a receipt warning. Naming the layer is the norm.Content and metadata are digested into their own columns (
sha256,metadata_sha256, the latter over canonical JSON), read back and compared before the write is reported. Every read path —recall,query_layer,semantic_recall— recomputes both on the way out and attachesintegrityandmetadata_integrity:true,false(the row is returned and flagged, never dropped), or'unattested'for rows written before the column existed. Supersession and the correction mark are the only sanctioned mutations of stored metadata, and both re-attest the parent.One receipt shape on both transports:
layer, id, timestamp, verified, sha256, digest_stored, metadata_sha256, metadata_digest_stored, redacted, provenance, source, assertion_origin, importance, routing, warnings[]plus whichever markers apply.
And the record's honesty about its own holes is a query: emet_gaps counts, with the ids
behind each count, unsourced assertions, unattributed inferences, ambiguous attribution by tag
and by session, self-routed writes, unattested rows, digest mismatches, pointer disagreements
against the transcript store, broken supersession, broken correction links, expired-but-live rows, consolidation
candidates, and redacted spans by source. It identifies and never fills — where a source is
knowable the fix is revise_memory with a real pointer; where it is not, the gap stands as a
fact and the count stops growing from that date. emet_initialize carries the counts beside
layer_health.
Topic pages are views, not records. An area is a controlled document, areas/<slug>.md,
checked against templates/AREA.md. It names layers, tags, keywords, document prefixes, an
optional client list, a staleness window (default 30 days), and a cap. Only the owner creates
or edits an area. emet_page computes the page on every request from current records. Every
displayed line cites [mem:<id> · <layer> · <client> · <YYYY-MM-DD>] or [doc:<doc_id>@v<version>].
A line with no source is dropped. Superseded and retired records stay out of the current
sections unless include_history is set, and then only under History. The page is not stored,
not written back, and not returned by recall. emet_page_lint flags contradictions,
superseded-but-current claims, unsourced lines, duplicates, broken links, and long-unconfirmed
statuses. It writes nothing. A Monday emet_initialize carries the weekly counts. STATE.md
stays the hand-kept board; areas/state.md is the selector for a generated view beside it.
The shipped examples are EMET, Health, Learning, Home, and Garden. The owner
writes those files into the store with write_doc. Pages are visible to the owner only.
What can be guaranteed this way: every write attested, attributed, scrubbed, and every absence visible. What cannot be guaranteed without inviting fabrication: every write complete. The store does not promise the latter.
Guardrails & Monorails
The door guards each write. Guardrails & Monorails guards the order of a session's writes.
Guardrails (the floor, the character, the served instructions) bound a field where the AI
moves freely and does its judgment work. The monorail (the session graph,
src/session-graph.js) carries it from one field to the next without asking it to choose:
open, work, record, board, handoff, close. The car can't derail or leave the beam. Where the
flow must fork, a switch at the junction (a small logic unit in EMET's code, tripped only by
its condition, like a transistor; no hosted model, no API) routes the session to the right
guardrail space. The track throws the switch, never the AI. The stations are where the
processing happens, and each station's door opens only when every stop before it has been made.
Switches are designed, not yet built. The server checks the track
on every governed write, so it is the same for any AI on any device. No host hook is needed.
Every skipped step is refused, and the refusal says how to pass. The full practice, the gates and the honest limit
are in docs/GUARDRAILS-AND-MONORAILS.md.
Tools
Thirty-four tools are registered with sub-keys off. With EMET_SUBKEYS=1, four more (emet_subkey_request, emet_subkey_claim, emet_subkey_revoke, emet_subkey_end_session) and the subkey and session_handle arguments. The names below are every tool the registry registers, both sets together.
accept_nonconformance
corpus_recall
emet_client_approve
emet_error_report
emet_floor
emet_gaps
emet_initialize
emet_invite_create
emet_member_access_create
emet_page
emet_page_lint
emet_pages
emet_session_close
emet_session_open
emet_setup_complete
emet_status
emet_subkey_claim
emet_subkey_end_session
emet_subkey_request
emet_subkey_revoke
emet_transcript_append
list_docs
patch_doc
query_layer
query_transcripts
read_doc
read_doc_history
recall
rename_doc
restore_doc
retire_doc
revise_memory
revise_transcript
save_to_layer
save_transcript
semantic_recall
validate_doc
write_docRetained revisions — every document write archives the prior copy; read_doc_history lists
what is retained for a document and returns any retained revision in full, re-hashed against its
stored digest. Retention that nothing could read back was a promise without a witness.
One reader for the second tier — query_transcripts reads the live transcript store and the
legacy exchange archive (rows written by earlier hosts, before the transcript store existed)
together, in one session shape, each result naming the collection it came from. Nothing is
migrated between them.
Document control — validate_doc checks a controlled document against the template that
governs it, and the same check runs inside the write path on every document write, where no
caller can skip it. A record that fails is marked, never rejected: it lands, carries a
nonconformance marker naming what failed, and stays counted at session start until it is
corrected by a later revision or dispositioned with accept_nonconformance. The checks are form
and continuity — sections present, in order and filled; no placeholder left unfilled; every item
on the previous revision still present or carrying a disposition. Whether an answer is true is
not something a machine can check, and this one does not pretend to.
Session start — emet_initialize (start here): fetch every page it names, or pass page: "all"
on a host with no size limit; then emet_session_open with that startup's load_id. Session ids are
dated in the local time zone (EMET_TIMEZONE, default America/New_York); a closed session is
never reopened, so a slug whose id is already closed opens as <id>-2, -3, and the result says so.
After a complete close a session takes exactly one more append, its closing exchange, within
EMET_CLOSE_GRACE_MINUTES (default 5, at most 60) of the close, written into the session's last
part, and is then locked; a retry of it writes nothing. Corrections with revise_transcript stay
allowed but must keep exactly the same exchange numbers.
During the session — emet_transcript_append after every reply, one exchange at a time.
Every write tool also takes session_id and exchange, and returns session_graph.next, the
next stop on the monorail (Guardrails & Monorails).
Session end — emet_session_close. Checks whether the session's work actually reached the
store — transcript saved and intact, continuity documents updated, an episodic entry written —
and reports exactly what is missing. It does not write the artifacts: only the model holds the
session's content, and a tool that generated a plausible-looking summary would be manufacturing
the false continuity the disciplines exist to prevent. emet_initialize returns the same protocol
at session start, which is the only moment early enough to matter.
Setup internals and diagnostics — emet_status (environment, layers, health; probe: true writes and reads back a probe) · emet_setup_complete
Approvals
Every tool declares MCP annotations — readOnlyHint, destructiveHint, idempotentHint,
openWorldHint — in one table in src/annotations.js, which is also the complete statement of
what EMET does to a store. 15 of the 34 tools cannot modify anything; four can replace the
visible content of a document; openWorldHint is false everywhere. The tools reach this
server's own database and nothing else: no filesystem and no shell. The sign-in server fetches
a client's metadata document, and only from a public address it has already checked.
A server that declares nothing is treated as if every call might do anything, so a host that asks before acting asks before every call, including the reads that make up most of a session. These hints let a host stop asking about reads without being told to trust writes. They are hints, not enforcement: what is actually auto-approved is a host-side setting. See docs/PERMISSIONS.md.
Reporting a problem
emet_error_report is owner-only and read-only. It reads the operational error log and returns the full report, a mailto: draft, and a structured email {to, subject, body_text, short_id, version}. EMET does not send mail and does not open a network connection for this tool.
Read the report first. The draft is addressed to EMET_REPORT_EMAIL. When that variable is unset, the address is dreamforgestudio@mail.grokbot.com, the project's report inbox. Set it to your own address to keep reports in house. Set it to an empty string, or to anything other than one plain address, and the tool returns the report and no email.
A calling assistant that has its own email address may pass that structured email to its own email tool. The calling assistant may send this report only after showing the report to its user and getting their OK. EMET will not send it. The code table is docs/ERROR-CODES.md, and the behavior is written up in docs/ERROR-CODES-SPEC.md.
Tests
npm test1,852 checks across 64 suites (the PASS lines npm test prints, counted 2026-10-07). No framework, no database, no
credentials — each suite is a plain node script on one shared harness (tests/harness.js), and
the runner discovers every *.test.js, so npm test runs on a fresh clone. Per-suite counts
are not kept here because they go stale; npm test prints them.
validation.test.js and content_analyzer.test.js began as upstream's own
suites, ported unchanged apart from the import path; every one of upstream's assertions still
stands, and the additions cover EMET's metadata vocabulary and the shared layer list. They are
the original author's assertions about the original author's code, and they are kept passing.
The rest cover this tree. Some are fixtures rather than unit tests — they read the source and
fail on a class of drift: write-path.test.js allows no MongoDB write outside the
sanctioned doors, requires each door to scrub before it hashes, and requires that nothing in
the tree removes from a layer, document or transcript collection; dispatch.test.js
requires the declared tool list and both transports' dispatch switches to be the same set, and
that a list response can carry a caller-facing warning; annotations.test.js requires
every tool to declare its approval hints; atlas-role.test.js requires the database role to
grant no remove on the record. The others test behaviour — among them aleph.test.js
(read-back, digests, signatures, the chain walk), session-graph.test.js (the monorail's
gates and lab results), receipt.test.js, provenance-stamp.test.js, session.test.js,
gaps.test.js and redact.test.js. Their failure
cases matter more than their success cases — a verifier that passes something it should have
caught converts an undetected problem into a confident assurance. layers.test.js asserts
against the store shape that produced the year-long dormant layer, so the check is tested
against the failure it was written for rather than a hypothetical one.
Upstream's index.test.js, decay.test.js and integration.test.js are not carried: they
target the SQLite backend and the temporal decay engine, neither of which exists here. Running
them would produce failures that say nothing about this code.
Benchmarks
EMET's implementation language was evaluated rather than assumed. The same specification was implemented in JavaScript, Python, Go and Rust, deployed identically, and run against live Atlas — with a digest equivalence check proving all four did the same work before any timing was read. JavaScript won, on serial in-process cost, on throughput under concurrency, and on standards provenance as the only candidate carrying an international language standard (ECMA-262 and ISO/IEC 16262).
The exercise's most useful output was not a language choice but a function to optimise: the write-path scrubber was paying nearly full cost on documents containing no secrets, and fixing that saved roughly thirty times what the best available language change could have.
Method, full results, and an explicit account of what was not measured:
docs/BENCHMARKS.md. scripts/redact-bench.mjs measures the scrubber
alone — run it before and after any change to src/redact.js.
Known gaps
Honest list, because the point of this project is that it behaves predictably:
Rows written before 2026-09-05 carry no content digest and rows written before 2026-09-07 carry no metadata digest; reads report them as
'unattested', and they are not backfilled — a digest computed today would attest to today's copy, not to the write.emet_gapscounts the boundary.Entries written before provenance existed keep a null pointer and resolve by timestamp window;
emet_gapscounts them as unsourced. They are not backfilled, for the reason given under Provenance.Two content routers are carried from upstream (a substring keyword router and a content analyser). Measured against a 1,175-row corpus both agree with the stored layer well under half the time (41% and 36%), so neither places entries: an unlabelled write lands in
working, flagged, with both opinions recorded inrouting(keyword_layer,analyzer_layer). Retuning them into something fit to route is possible with those numbers as the baseline; it is not scheduled.consolidation_candidatesinemet_gapsis tag overlap, not semantic similarity.The monorail (session graph) refuses every skipped step (enforce only since 2026-10-03; there is no report mode or off switch). Its turn coverage is only as strong as the exchange numbers hosts report, and it never sees the reply text. See Guardrails & Monorails.
The scheduled weekly intake writes without opening a session, so the session-order check refuses those writes (gate
no_session) until the routine opens a session with its startup'sload_idand passes thesession_id. Its old source tag,cloud-scheduled-intake, was retired on 2026-10-02. Acloud-scheduled-prefix is not a registered tag, and those writes are refused. The weekly intake runs underintake-bot.emet_gapskeeps counting the rows already written under the old tag as ambiguous attribution instead of rewriting history.save_transcript's description does not state that it stores content verbatim; the implementation does.No export / import / restore-and-compare yet.
The document store is not prefixed, so one database holds one instance's identity. Run separate databases rather than separate prefixes if you want separate memories.
Dynamic Client Registration is still served beside Client ID Metadata Documents; the MCP specification's deprecation window closes around 2027-07. It is scheduled to come off on 2027-06-01.
Delegated client sub-keys stay off unless setup is told that more than one bot or AI will use the memory. A hosted server does not write a pepper into its container. A stamp shows that an approved key was presented, not which program held it. See docs/SUBKEYS.md.
Origin
EMET was built by Dreamforge Studio LLC from a neurodivergent user’s own need for an AI assistant that remembers. The word coined for that is neurendipity: neurodivergent and serendipity.
It is designed first for neurodivergent users, and it works for anyone.
License and access
EMET is free and open source under the MIT License (see LICENSE). No account, key or payment is required to run it.
Authorship of the commit history, including a correction recorded as an addition
rather than a rewrite: docs/AUTHORSHIP.md.
MIT. See LICENSE — both the original C.I.P.S. LLC notice and the Dreamforge Studio LLC notice
must travel with any copy.
Available Tools
34 toolsaccept_nonconformanceA
Record the written disposition for a nonconformance that will not be corrected. A marker is never cleared silently: it leaves either because a later revision passes, or because someone accepts it in writing here. The marker is RETAINED with the acceptance beside it, and the acceptance covers only the revision it names - the next write is validated fresh, and accepted markers stay counted at session start, separately from open ones. The reason must be at least 10 characters: it is what an audit reads later.
| Name | Required | Description | Default |
|---|---|---|---|
| doc_id | Yes | Document carrying the open marker. | |
| reason | Yes | Why this nonconformance is accepted rather than corrected. At least 10 characters. | |
| source | No | Source tag - which host produced this write. One name on every write since 2026-09-27. | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| session_id | No | Transcript session id. | |
| accepted_by | No | Alias of `source` (the older name, kept one release). Prefer `source`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare write/non-idempotent/non-destructive; the description adds far more: the marker is RETAINED rather than cleared, the acceptance covers only the named revision so the next write is validated fresh, and accepted markers are counted separately from open ones at session start. It also states the audit-facing durability of the reason.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then layered with genuinely useful lifecycle behavior. Every sentence carries weight, though the closing constraint note slightly overlaps the schema and keeps it from being maximally tight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-param write tool with full schema coverage and no output schema, the description covers purpose, lifecycle, persistence, and validation semantics well. It stops short of naming sibling alternatives, but an agent has what it needs to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so doc_id, reason, source, exchange, etc. are already documented. The description reinforces the ≥10-character reason constraint and its audit purpose, but adds no syntax or format detail beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Record the written disposition for a nonconformance that will not be corrected.' It makes the scope unmistakable (only for nonconformances deliberately left uncorrected) and clearly separates this action from the alternative path where a revision later passes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear selection context: use it when a marker will not be corrected, since it otherwise leaves only via a passing revision. However, it never names the sibling tools (e.g. validate_doc or write_doc) that represent the other path, so the routing is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
corpus_recallARead-onlyIdempotent
Semantic search over an optional supplementary knowledge corpus that the user has loaded separately from memory. Returns nothing unless a corpus has been populated; this is not where session memory lives.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max results (default 5, max 25) | |
| query | Yes | Natural-language query (raw text; concept match, phrased however the experience is phrased) | |
| corpus | No | Optional: restrict to one corpus name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnly, idempotent, and non-destructive behavior, so the description's job is to add context. It does so by disclosing the empty-result condition ('returns nothing unless a corpus has been populated') and the separate-corpus scope, both of which are useful traits not visible in the annotations or schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with the primary action front-loaded and the scoping caveat immediately after. No filler, though the second sentence's second clause slightly overlaps the first sentence's 'loaded separately from memory' idea.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter, no-output-schema read tool with full schema coverage, the description supplies the key precondition and scope boundary. The only real gap is the absence of explicit routing guidance relative to recall/semantic_recall.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds no syntax, format, or default details for query, limit, or corpus beyond what the schema already documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: semantic search over a supplementary knowledge corpus. It also explicitly rules out session memory, which helps separate it from the recall/semantic_recall siblings, but it never names those siblings directly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a precondition (a corpus must have been populated) and implicitly signals usage via the contrast with session memory, but there is no explicit statement of when to choose this over recall, semantic_recall, or query_layer.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_client_approveADestructive
Approve or deny a new client (a bot, or a single-instance AI such as Claude or ChatGPT) that EMET flagged on its first write. A new client is read-limited until approved. Only the owner, or the chief-of-staff bot named by EMET_COS_TAG, may decide. Pending clients are listed in emet_initialize and emet_status as client_approvals.
| Name | Required | Description | Default |
|---|---|---|---|
| as | No | Required when the client_id already has approved tags: add keeps them all (several bots on one shared client); rename retires one | |
| tag | Yes | The pending client's tag, as listed in client_approvals.pending | |
| from | No | With as=rename on a client that has several approved tags: the tag being replaced | |
| reason | No | Optional note kept with the decision | |
| decision | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag this as destructive and non-idempotent, and the description adds real context beyond them: the read-limited state of unapproved clients and the authorization constraint (only the owner or the EMET_COS_TAG chief-of-staff bot may decide). It does not explain consequences of a deny (e.g., whether the client is blocked permanently), which is the main remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action and resource, then state model, then authorization, then discovery. Four sentences that each carry information; the parenthetical definition of a client is slightly verbose but aids disambiguation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, non-idempotent mutation with no output schema, the description covers the state transition, the authority requirement, and where to find pending items. The main omission is the outcome/aftermath of a deny decision.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 80%, so the schema already documents tag, as, from, reason, and decision with meaningful descriptions. The description adds no syntax or usage detail for these parameters beyond pointing at client_approvals.pending, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States specific verbs (approve or deny) and a specific resource (a new client EMET flagged on first write), and defines what a 'client' is. This is clearly distinguishable from siblings like emet_invite_create or emet_member_access_create.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the trigger context (clients pending after being flagged on first write) and points to emet_initialize/emet_status as where pending clients are listed. It doesn't explicitly name a sibling alternative to use instead, but the authorization condition and discovery path give clear when-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_error_reportRead-onlyIdempotent
Read the operational error log and show a report the owner can send on. Owner only. Read-only: it writes nothing and sends nothing. EMET has no mail sender and opens no network connection for this tool. The result is the full report, then a mailto link, then a structured email {to, subject, body_text, short_id, version} a calling assistant can pass to its own email tool. The calling assistant may send this report only after showing the report to its user and getting their OK.
| Name | Required | Description | Default |
|---|---|---|---|
| codes | No | Optional list of EMET codes to include. | |
| limit | No | How many of the most recent entries to list. Default 50, maximum 200. | |
| since | No | ISO date (YYYY-MM-DD) or UTC timestamp. Default is 7 days ago. Cannot be older than 90 days. | |
| correlation_id | No | 16 hex characters from an error response. When set, since is ignored. |
emet_floorARead-onlyIdempotent
Read the floor: the standing description of how this assistant engages with the person using it. It ships in the server code, so setup cannot overwrite it and no install can lose it - the character document holds the user's modifiers on top of it. emet_initialize already returns the whole floor at session start; call this to read it again without re-running initialize. Use compact: true for the short form partway through a long session, when the start of the conversation is far behind. Pass gem to read one installed GEM's floor; reading does not turn that GEM on. Read-only, and re-reading is not the same as being bound by it.
| Name | Required | Description | Default |
|---|---|---|---|
| gem | No | Read one installed GEM's floor by id, for example 'neurendipity'. Reading does not turn the GEM on. Omit it to read the core floor. | |
| compact | No | Return the short re-assertion form instead of the whole floor. | |
| section | No | Return one section only, e.g. 'voice'. Call with no arguments to see the section names. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the full safety profile (readOnlyHint, idempotentHint, destructiveHint=false), so the bar is lower. The description still adds non-obvious context: the floor ships in server code so setup cannot overwrite it, and 're-reading is not the same as being bound by it' clarifies a semantic effect no annotation covers.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose before any caveats, and no sentence is filler. It is somewhat dense and long for a read-only getter, but each clause (immutability, initialize overlap, compact, gem, read-only framing) carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must convey what is returned, and it does: the whole floor, the compact short re-assertion, a single section, or one GEM's floor. It also explains how to discover valid section names (call with no arguments), leaving nothing important unstated for a 3-param, all-optional tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3 and the schema carries the syntax. The description nonetheless adds usage meaning for two parameters: compact is framed as the short form for late in a long session, and gem is framed as reading one GEM without enabling it. The section parameter is left to the schema, which documents it adequately.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read the floor') and immediately defines the domain-specific noun as 'the standing description of how this assistant engages with the person using it.' It also distinguishes itself from the closest sibling by noting emet_initialize already returns the floor at session start and this re-reads it without re-running initialize.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit alternatives and conditions: use this instead of re-running emet_initialize, use compact:true when 'the start of the conversation is far behind,' pass gem to read a specific GEM's floor. It also states a boundary ('reading does not turn that GEM on'), so the agent knows what this tool does not do.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_gapsARead-onlyIdempotent
The record's honesty about its own holes, as a query. Read-only; identifies and never fills. Counts, with the ids behind them, for each gap class: unsourced assertions, unattributed inferences, ambiguous attribution (by tag and by session), uncertain routing, unattestable rows (pre-2026-09-05 boundary), digest mismatches (the alarm - expect zero), pointer disagreements against the transcript store, broken supersession, expired-but-live rows, consolidation candidates (tag families; a list for a person), and redacted spans by source. Call it when asked how trustworthy the record is, before a consolidation pass, or after any migration. Closing a gap is a person's decision: revise_memory with a real derived_from where the source is knowable; otherwise the gap stands as a fact.
| Name | Required | Description | Default |
|---|---|---|---|
| layer | No | Restrict to one layer. Default: all six. | |
| limit | No | Max ids returned per class (1-500, default 50). Counts are always complete. | |
| since | No | Unix seconds; only entries with timestamp >= since. Default: the whole store. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, but the description adds value beyond them: "identifies and never fills," the return expectation "digest mismatches (the alarm - expect zero)," and the note that counts are complete while ids are capped. It stops short of describing the response shape in full.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but front-loaded: the operation and its read-only nature come first, then the enumerated classes, then usage. The list of gap classes is long but each item is distinct and informative. The opening metaphor is a minor flourish that costs a few words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only diagnostic query with no output schema, the description is largely sufficient: it names every gap class, states counts are complete, and explains what closing a gap entails. Only the exact response structure and id-format remain unspecified, which is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents layer, limit, and since in detail. The description adds no parameter-level syntax or defaults; its references to tags, sessions, and the 2026-09-05 boundary describe gap classes rather than inputs. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
After a metaphorical opening ("the record's honesty about its own holes"), it states a concrete verb and resource: "Counts, with the ids behind them, for each gap class," and enumerates those classes (unsourced assertions, digest mismatches, broken supersession, etc.). This distinguishes it from siblings like emet_status or query_layer, though the poetic framing costs some immediate clarity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit triggers are given: "Call it when asked how trustworthy the record is, before a consolidation pass, or after any migration." It also names the follow-up path, routing remediation to revise_memory "with a real derived_from where the source is knowable," and clarifies the tool itself never fills gaps.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_initializeAIdempotent
START HERE. Call this FIRST in every session, before answering anything that might have prior context, and always when the user opens with 'initialize', 'start', 'begin', 'hello' or similar. It is the only setup step a user should ever have to know about. A large startup comes in pages: read page_notice first and call again with the page it names until the last page, before replying. If this memory store is new it returns a short interview to run conversationally; if it is established it returns who you are, how you speak (character, when the store keeps one - authoritative on voice for the whole session), who you are serving, and where things were left. The user never needs to know which case applied.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | The startup is served in pages when it is larger than one result (each page fits under 20,000 bytes). Omit for page 1; every page but the last says which page to ask for next - call again with that page, before replying, until a page says it is the last. "all" returns the whole startup in one result, for a host with no size limit. | |
| source | No | Your own source tag (the bot or host you are). The startup then reports the tag your writes will carry and its approval state. | |
| load_id | No | The load_id your startup returned in `startup_page.load_id`: every emet_initialize result carries one (each page of a paged startup, the whole startup with page "all" or when it fits one result, and a store that is not set up yet). Pass it with every later page so all pages come from the same startup, and pass the same load_id to emet_session_open so its startup page check runs on this startup. | |
| recent_limit | No | How many recent context entries to return (default 5). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare idempotentHint=true and readOnlyHint=false; the description is consistent, explaining that a load_id and session state are produced (the non-read-only behavior) and that paged results are safe to re-request. It discloses the paged-startup loop, the new-store interview branch, and the authoritative 'character' voice, though pagination mechanics are also restated in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the imperative 'START HERE' and organized as one dense but purposeful paragraph; every sentence carries information (ordering, paging, branch outcomes). Slightly long, but not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description must describe returns, and it does: interview for a new store, identity/voice/principal/last-state for an established one. It stops short of naming concrete return fields (e.g. startup_page), which are only discoverable in the load_id parameter text, leaving a small gap for a tool whose output is the whole point.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it explains why load_id must be carried across pages and into emet_session_open, and frames 'source' as reporting the tag the agent's writes will carry. That is interpretive context beyond the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action (session initialization) with an imperative scope ('START HERE. Call this FIRST in every session') and explicitly distinguishes the setup role from other memory tools. An agent can tell at a glance that this is the entry point versus siblings like emet_session_open or emet_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit conditions for invocation ('first in every session', 'before answering anything that might have prior context', plus trigger words like 'initialize', 'start', 'hello'), and coordinates with emet_session_open via the load_id parameter. The only missing element is a when-not-to-use clause, which the 'always first' framing makes largely moot.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_invite_createADestructive
EMET Guest Access: create a one-time code for a guest. They paste it on the connect screen instead of the passphrase. Default access is read-only. Only the owner can create a guest code. EMET Member Access is a different door.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Days until the code expires. Default 14. | |
| scopes | No | Guest invites accept only emet.read. | |
| source | No | Source tag bound to this guest at approval. Exact membership of EMET_SOURCE_TAGS when a registry is set. | |
| display_name | Yes | Name the guest will be known by in this store |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the mutation profile (readOnlyHint false, destructiveHint true, idempotentHint false). The description adds meaningful behavioral context beyond annotations: owner-only authorization, default read-only access, and that the code is one-time. It does not explain the destructiveHint or the return value, but the added auth and default-access details are valuable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Five short sentences, front-loaded with the tool name and core action. Each sentence carries distinct information: purpose, usage mechanics, access default, authorization rule, and sibling differentiation. There is no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a creation tool with no output schema, the description covers purpose, usage context, authorization, and alternative routing. However, it does not state what the tool returns (presumably the one-time code) or how expiration defaults work beyond the schema's 'days' parameter. These are minor gaps for an otherwise complete definition.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters are already documented in the schema. The description reinforces that guest invites accept only 'emet.read', which maps to the scopes parameter, but adds no new syntax or format details. This is the baseline case where the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'create a one-time code for a guest'. It immediately scopes the tool to EMET Guest Access and explicitly distinguishes it from 'EMET Member Access', which is named as a different door. An agent can identify exactly what this tool does without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides clear usage context: guests paste the code on the connect screen instead of the passphrase, only the owner can create a guest code, and member access is a separate path. The alternative (EMET Member Access) is explicitly named and visually separated as 'a different door'. This leaves little ambiguity about when to use this tool versus its sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_member_access_createADestructive
EMET Member Access: create a one-time code for a new person with read and write access. They paste it on the connect screen instead of the passphrase. This is not EMET Guest Access, and it never makes them the owner. Only the owner can create it. Off unless EMET_MEMBER_ACCESS=1.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Days until the code expires. Default 14. | |
| source | No | Source tag bound to this member at approval. Exact membership of EMET_SOURCE_TAGS when a registry is set. Required for this member to write documents over HTTP while a registry is set. | |
| display_name | Yes | Name this person will be known by in this store. A name never selects an existing person. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, idempotentHint=false and readOnlyHint=false, so the mutation profile is covered. The description adds real value beyond that: it is a one-time code granting read/write access, it never confers ownership, it is owner-only, and it is gated behind an environment flag.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence, and the follow-on sentences carry distinct, non-redundant information (connect-screen usage, non-ownership, owner-only, env gate). Slightly long at five sentences but each earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a credential-creation tool with no output schema, the description covers who may invoke it, what it grants, and its activation prerequisite. It does not describe what is returned (the code itself) or how approval/expiry interacts, but annotations and schema carry most of the remaining load.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents display_name, days and source. The description adds no syntax or format detail beyond the schema (only that the code is one-time and expiring), so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (create) and resource (a one-time code for a new person with read/write access), and clarifies it is not Guest Access nor ownership. However it never distinguishes itself from the sibling emet_invite_create, leaving the agent to infer which credential-creation tool to pick.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear preconditions: only the owner can create it, the recipient pastes it on the connect screen instead of the passphrase, and it is off unless EMET_MEMBER_ACCESS=1. It excludes Guest Access but does not explicitly compare against the sibling emet_invite_create.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_pageRead-onlyIdempotent
Read one compiled topic page for an area slug (areas/.md). A page is a view of current records. It is not a record: this tool writes nothing, stores nothing, and the result is never written back and is not returned by recall. Every displayed line cites its source as [mem: · · · ] or [doc:<doc_id>@v]. A line with no source is dropped. Superseded and retired records stay out of the current sections; pass include_history true to show them under History only. A line is marked stale when its source has not been confirmed or revised within the area's staleness window (default 30 days; the area document may set its own). Owner only. Computed on every call, so cache_hit is false.
| Name | Required | Description | Default |
|---|---|---|---|
| area | Yes | Area slug, for example health. The selector is the current areas/<slug>.md document. | |
| as_of | No | Optional ISO date or unix seconds. The page shows records that were current at that moment. A document line cites the revision in force then, from documents_history. A revision that was never archived is absent. A superseded or retired record with no end date is still shown in this past view, labelled "end date unknown", and is not a current line. The area selector is the current area document. | |
| include_history | No | When true, superseded and retired records appear under History, labelled as not current. Default false. |
emet_page_lintRead-onlyIdempotent
Flag problems in topic pages and the records they read. Read-only: it never edits a record. Findings cover contradictions (two current records that share a subject and disagree), superseded or retired records still claimed as current, lines with no source, duplicates, broken links, and statuses unconfirmed longer than the area's staleness window. It builds on the emet_gaps classifiers. A fix is a person's revise_memory, retire_doc, or accept_nonconformance. Owner only. Omit area to lint every area.
| Name | Required | Description | Default |
|---|---|---|---|
| area | No | Optional area slug. Omit to lint every current area. |
emet_pagesRead-onlyIdempotent
List current area documents. For each area: line count, stale count, and the time of the newest source the page read. Read-only. Owner only. Computed on every call. The page body is not stored.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
emet_session_closeA
Call this when the session is ending - the user says goodbye, asks you to wrap up, mentions restarting or opening a new session, or you are about to run out of room. An acknowledgement of the turn just completed - thanks, great, perfect - is not a session end; if it is ambiguous, ask rather than close. Checks whether this session's work has actually been written to the store and reports exactly what is missing. It does not write anything itself. RECEIPTS ARE REQUIRED: pass receipts naming what you wrote and read back for each close step - working documents, the episodic session record id, the board and its version, the transcript part ids and highest exchange index as query_transcripts returned them, and the handoff version (written last). Each receipt is compared with the store; omitted or mismatched receipts, a handoff that was not the last write, or a transcript no layer entry cites (derived_from) return state 'blocked'. Each missing line carries a scope: touch only what it names. THREE blocked closes on one session return state 'escalate': stop retrying, say the speak lines, and hand the user the list - the counter does not reset on a changed receipt, only with a new session. Say every line of the returned speak to the user, in order - a wrap that leaves it out is a failed close. Never tell the user their session is saved unless this returns state 'complete'.
| Name | Required | Description | Default |
|---|---|---|---|
| source | No | Your host's source tag: the registered tag this session was written under. The close is never refused for it; an unregistered tag, one that did not write this session, or none (with no server default) gets a warning that the close is not attributed. | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| receipts | No | Required for state 'complete'. One entry per close step, taken from what the tools returned this session - not from recall. | |
| session_id | No | The session_id used (or to be used) for this session's transcript, e.g. '2026-09-03_short-description'. Without it the transcript cannot be checked; receipts.transcript_session_id is used when this is omitted. | |
| thread_docs | No | Additional document ids this session should have updated, e.g. ['threads/some-project.md']. | |
| session_start | No | Unix seconds when the session began. Defaults to 12 hours ago, which makes the answer approximate. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
It discloses behavior far beyond the annotations: the receipt-comparison protocol, that omitted/mismatched receipts, a non-last handoff, or an uncited transcript yield state 'blocked'; that three blocked closes yield 'escalate' and that the counter persists across receipt changes; and that every 'speak' line must be relayed. One mild tension: readOnlyHint=false implies side effects while the text says it 'does not write anything itself', though the persistent blocked-close counter plausibly accounts for the non-idempotent annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The text is long but dense, and the most consequential rules are front-loaded (when to call, then what it does, then RECEIPTS ARE REQUIRED) with emphasis markers for the blocked/escalate branches. A few clauses, such as the repeated warnings about relaying speak lines, could be tightened, but almost every sentence carries an operative rule.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite a complex 6-parameter schema with a nested receipts object and no output schema, the description covers the return states ('complete', 'blocked', 'escalate'), the per-line 'scope' field, and the required 'speak' output, so an agent knows both how to call it and how to act on the result. Nothing essential is left unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real semantics the schema cannot: receipts are required for state 'complete', each receipt is compared against the store, and each missing line carries a 'scope' telling the agent to touch only what it names. It also stresses receipts must come from tool returns, not recall, which is meaningful usage guidance for the nested object.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise verb+resource: it 'Checks whether this session's work has actually been written to the store and reports exactly what is missing' and explicitly disclaims writing ('It does not write anything itself'). This distinguishes it cleanly from sibling write tools like save_to_layer, write_doc, and emet_transcript_append, and from the sibling emet_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit triggering conditions ('user says goodbye, asks you to wrap up, mentions restarting or opening a new session, or you are about to run out of room') and an explicit exclusion ('An acknowledgement of the turn just completed - thanks, great, perfect - is not a session end'), plus a tie-breaker ('if it is ambiguous, ask rather than close'). This is exactly the when/when-not guidance the dimension rewards.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_session_openA
Open a transcript session. Returns session_id, dated in the configured local time zone (EMET_TIMEZONE, default America/New_York). A slug whose id is already CLOSED is never reopened: the session opens as -2, -3, ... and the result's notice says so. Pass load_id from your startup pages (startup_page.load_id on every emet_initialize page) so the startup page check runs on your own startup; an open without load_id, or with a page of that startup never fetched, is refused (the refusal says how to pass). After every reply, call emet_transcript_append with that id and the one exchange that just happened. Do not dump the whole chat at close.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Short name for the session, e.g. grok-init | |
| load_id | No | Required for the startup page check (by policy; the schema leaves it optional so older hosts can still open): the load_id from `startup_page` on your emet_initialize pages - page 1 returns it, every page carries it, and the last page names it. An open without load_id, or with a page of that load known not fetched, is refused before anything is written (gate startup_pages); the refusal names the exact calls that pass. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: discloses the timezone (EMET_TIMEZONE, default America/New_York), the CLOSED-slug collision rule (session opens as <id>-2, -3 with a `notice`), and the refusal path for a missing/unmatched load_id. These are non-obvious behavioral traits that the readOnlyHint/not-destructiveHint/not-idempotentHint annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and return values, then the load_id gate and workflow. Dense with information, though a couple of sentences are long and slightly clause-heavy; little is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description covers the return (session_id, timezone, `notice`) plus the prerequisite gate and the required follow-up call. An agent has everything needed to invoke it correctly and continue the session workflow.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: it explains why load_id is optional in the schema yet required by policy, and clarifies slug collision semantics. It gives more semantic context than the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Open a transcript session") and immediately specifies what it returns (session_id) and the scoping behavior. An agent can distinguish it from emet_session_close and emet_transcript_append without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to pass load_id from startup_page and states the refusal condition when it is missing or unfetched, naming the gate. It also prescribes the follow-up workflow ("After every reply, call emet_transcript_append with that id") and warns against dumping the whole chat, which routes the agent correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_setup_completeDestructive
First-time setup, the owner's only: record the user's interview answers as bootstrap/IDENTITY.md, bootstrap/CHARACTER.md and bootstrap/USER.md and as an identity-layer entry, and create the owner person. Every write is read-back verified. Refused under a bot tag. On a store that is already set up it is refused unless reissue is true (the owner's explicit word): a re-run writes new versions of the user's identity documents; prior versions are archived, never destroyed. answers.more_than_one_bot is optional. Absent or no leaves sub-keys off. yes writes EMET_SUBKEYS=1, a generated pepper and EMET_SUBKEY_PEPPER_VERSION=1 into .env only when that file is the config source, after the store writes succeed, and does not turn the flag on in this process. A hosted server refuses yes and returns the three variables to set, then redeploy. See docs/SUBKEYS.md. answers.bot_team_backup is optional. Yes or no, in the user's words, the same way as more_than_one_bot. Yes and Yes. record the flag. yes is the recommended choice. Absent or no leaves bot-team backup off. Anything else, including a non-string, is refused before a write. Setup does not copy prompts or settings.
| Name | Required | Description | Default |
|---|---|---|---|
| source | No | Your source tag. Setup is refused under a bot tag. | |
| answers | Yes | Answers keyed exactly as the interview questions returned by emet_initialize. | |
| reissue | No | Only on the owner's explicit word: re-run setup on a store that is already set up. Without it, a re-run is refused and nothing is written. |
emet_statusAIdempotent
Diagnostic in one call (replaces emet_setup_status, get_status and emet_verify_connection, 2026-09-27). Reports environment, database, collections, vector index, whether identity and user documents exist, and per-layer counts and health. Pass probe: true to also prove the write path - connect, write a probe document, read the same value back, remove it; a read alone cannot reveal a broken write path. Prefer emet_initialize for a normal session start.
| Name | Required | Description | Default |
|---|---|---|---|
| probe | No | Also write and read back a probe document (default false). Use after a deploy or a configuration change. | |
| source | No | Your own source tag. The status then reports the tag your writes will carry and its approval state. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false, idempotentHint=true and destructiveHint=false, but the description goes further by disclosing the exact write-path sequence (connect, write, read back, remove) and why it matters ('a read alone cannot reveal a broken write path'). It still doesn't say what permissions the probe requires or whether it leaves residue on failure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the purpose and the tool-replacement note, then the probe semantics, then the routing hint. Slightly dense – the '(replaces ... 2026-09-27)' parenthetical is historical trivia – but every other clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by enumerating the reported sections, which is enough for an agent to know what to expect. It stops short of describing the response shape or pagination/format details, but for a diagnostic tool that is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine meaning: it explains the probe's rationale (proving the write path) and that 'source' causes the report to echo the tag your writes will carry plus its approval state. That is more than the schema's terse field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb+resource (diagnostic status of the store) and enumerates exactly what is reported: environment, database, collections, vector index, identity/user documents, per-layer counts and health. It also names the three tools it replaces, so an agent can confidently distinguish it from siblings like emet_setup_complete or emet_initialize.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly routes the agent: 'Prefer emet_initialize for a normal session start,' and the probe flag is scoped to 'after a deploy or a configuration change.' Both the when and the when-not are stated, with the alternative named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
emet_transcript_appendA
Append ONE exchange to the session transcript. Call after every reply. Idempotent on exchange_index. Rolls to a new part after 8 exchanges. After a complete close the session takes exactly ONE more append - its closing exchange, numbered next, within EMET_CLOSE_GRACE_MINUTES (default 5) of the close; it is written into the session's last part whatever session_id it names, and the result's closing_exchange says it was accepted and that the session is now locked. A retry of that exchange number is answered as already saved and writes nothing. exchange_index, when given, must be a whole number of 1 or more (a number or its text). Any other append to a closed session is refused and nothing is written; that exchange belongs in the next session you open the usual way. THE FIRST APPEND OF A SESSION IS CHECKED: its assistant_message must carry the measured machine line - a machine name from the install notes plus the clock reading, as the shell returned them - or the words no local shell MCP. Without one of those the append is refused and the transcript does not start.
| Name | Required | Description | Default |
|---|---|---|---|
| source | No | Source tag - which host produced this write. One name on every write since 2026-09-27. | |
| channel | No | Alias of `source` (the older name, kept one release). Prefer `source`. | |
| session_id | Yes | ||
| user_message | Yes | ||
| session_start | No | ||
| exchange_index | No | ||
| assistant_message | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes far beyond the annotations: part rolling after 8 exchanges, the single closing-exchange exception after close, the grace window, the lock state, refusal semantics, and the first-append machine-line gate. The only blemish is the claim "Idempotent on exchange_index" sitting against idempotentHint=false; it is defensible as scoped idempotency (a repeated index writes nothing) rather than a safety-relevant contradiction, but the two signals are not obviously reconciled for the agent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose and routing rule in the first two sentences, then layers edge cases in descending priority. It is long and occasionally dense (the closing-exchange paragraph is a single run-on chain), and "whatever session_id it names" is a minor detour, but almost every sentence carries operational weight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, stateful, multi-constraint write tool with no output schema, the description covers the full lifecycle: normal append, part rollover, close grace append, retry resolution, and refusal. It even describes the observable result field (`closing_exchange`) that confirms acceptance, so an agent can act and verify without further documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 29% across 7 parameters, so the description must compensate. It does add real meaning for exchange_index (whole number of 1 or more, number or its textual form) and for assistant_message on the first append (must carry the measured machine line or "no local shell MCP"), but source, channel, session_start, user_message and the closing-exchange assistant_message content are left to the schema or undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource ("Append ONE exchange to the session transcript") and even emphasizes the unit of work with capitalised "ONE", which separates it from bulk siblings like save_transcript and revise_transcript. An agent knows exactly what this writes and at what granularity without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says "Call after every reply", then spells out the when-not cases: appends to a closed session are refused and "that exchange belongs in the next session you open the usual way", with a named exception window (EMET_CLOSE_GRACE_MINUTES). This is textbook when/when-not guidance with the alternative route named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_docsARead-onlyIdempotent
List continuity documents (metadata only; optional doc_id prefix filter, e.g. 'threads/'). Current documents come first. Retired documents stay in the list, marked with retired_at and, when a rename replaced them, what replaced them.
| Name | Required | Description | Default |
|---|---|---|---|
| prefix | No | ||
| include_retired | No | Accepted. Retired documents are always listed, after the current ones. This flag does not hide them. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered; the description adds real behavioral context beyond that: results are metadata only, current documents sort first, retired documents are always present and annotated with retired_at plus rename lineage. It stops short of disclosing pagination or result-size behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three compact sentences, front-loaded with purpose and the prefix example, then ordering and retired-document behavior. The parenthetical is dense but every clause carries information; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only list tool with no output schema, the description covers scope, ordering, and the retired/renamed edge cases an agent would otherwise be surprised by. Pagination and the shape of returned metadata are left unstated, which is the main remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50% and the 'prefix' parameter has no schema description, so the description's 'doc_id prefix filter, e.g. threads/' carries the load for that parameter. It also clarifies include_retired's semantics ('always listed... this flag does not hide them'), reinforcing the schema note rather than contradicting it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List continuity documents') and narrows the scope with 'metadata only', which implicitly separates it from read_doc/read_doc_history. It never names a sibling tool explicitly, so it falls short of full differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies enumeration use via the prefix filter and ordering rules, but never states when to choose this over read_doc, read_doc_history, or read_doc_history/restore_doc. No prerequisites, no exclusions, no named alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
patch_docADestructive
Replace one exact substring in a document (str_replace semantics) without resending the whole file. THE PREFERRED WAY TO MAKE SMALL EDITS to large docs like STATE.md. old_str must match EXACTLY ONCE - zero or multiple matches are rejected rather than guessed. Pass new_str as an empty string to delete. Same versioning, archiving, and sha256 read-back as write_doc.
| Name | Required | Description | Default |
|---|---|---|---|
| edits | No | Several replacements in one revision. Each old_str must match the current text exactly once. Applied together, so a failed edit writes nothing. | |
| doc_id | Yes | ||
| origin | No | Required when a board edit adds a row id. Names the session, chat or drop the row came from. | |
| reason | Yes | Required, at least 10 characters. A removed or closed board row says why. | |
| source | No | Source tag - which host produced this write. One name on every write since 2026-09-27. | |
| new_str | No | Replacement text. Empty string deletes the matched text. | |
| old_str | No | Exact text to replace. Must occur exactly once in the current text. Omit when `edits` is set. | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| session_id | No | Transcript session id. The floor is measured from the size when this session first touched the document. | |
| updated_by | No | Alias of `source` (the older name, kept one release). Prefer `source`. | |
| expected_version | No | Optional optimistic-concurrency guard. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and non-idempotent, so the mutation profile is known. The description adds genuinely useful behavior beyond that: exact-once match enforcement with ambiguous matches rejected rather than guessed, empty new_str as delete, and parity with write_doc's versioning/archiving/sha256 read-back. It does not discuss permissions or what a failed multi-edit leaves behind (that detail lives in the schema).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tight sentences, front-loaded with the operation and its mechanism, then preference guidance, then the critical exact-match constraint, then delete behavior and parity guarantees. No filler and each sentence carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter mutation tool with no output schema, the description covers the core mental model (substring replacement, ambiguity rejection, delete, versioning parity) and the schema carries the remaining parameters at 91% coverage. It omits any narrative about the session/exchange graph constraints and batch-edit atomicity, which are only in schema text, but nothing essential to invoking it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 91%, so the schema already documents nearly every parameter including the edits array, origin, reason, exchange and expected_version. The description only restates old_str/new_str semantics already present in the schema, adding no syntax, format, or constraint detail beyond it. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb+resource with its mechanism ('Replace one exact substring ... str_replace semantics') and explicitly distinguishes itself from the full-file alternative ('without resending the whole file'). An agent can tell this apart from write_doc and revise_memory from the first sentence alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear selection context: 'THE PREFERRED WAY TO MAKE SMALL EDITS to large docs like STATE.md' implicitly routes full rewrites to write_doc. It stops short of naming write_doc explicitly or stating when-not to use patching (e.g. multi-region rewrites), so it is clear but not fully exclusionary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_layerCRead-onlyIdempotent
Query a specific memory layer with structured filters
| Name | Required | Description | Default |
|---|---|---|---|
| layer | Yes | Memory layer to query | |
| options | No | Query options with structured filters |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, closed-world, so the safety profile is covered. The description adds nothing beyond that: no mention of result ordering, superseded/expired entry handling, or pagination semantics — all of which are behavioral facts an agent should not have to dig out of the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no wasted words. It is efficient, though its brevity shades into under-specification rather than crispness given the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has a rich nested schema at full coverage, but no output schema and no sibling differentiation, so the agent lacks guidance on return shape and on choosing this over recall/semantic_recall. Adequate but with clear gaps for a query tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents layer, filters, ordering, history, and include_superseded thoroughly. The description adds no syntax or format detail beyond the schema, which is the expected baseline 3 when structured fields do the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Query a specific memory layer') and adds scope ('with structured filters'), so the agent knows the operation. However, it does nothing to differentiate from closely related siblings such as recall, semantic_recall, or corpus_recall, which an agent must distinguish.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use guidance and no mention of alternatives, despite overlapping siblings (recall, semantic_recall, query_transcripts) that clearly invite confusion. The agent is left to infer that this tool is the layer-scoped filtered variant.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_transcriptsARead-onlyIdempotent
Retrieve session transcripts - the second tier, word for word. Reads the live store written by save_transcript AND the legacy archive (exchanges written by earlier hosts, June 2025 to June 2026), grouped into the same session shape; every result carries source naming the collection it came from. Nothing is migrated between them. Use it when a layer entry is relevant but thin: the transcript around its timestamp holds the depth the layer compressed out.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of sessions to return (default: 50, max: 500) | |
| session_id | No | Optional: Filter by specific session ID (e.g. '2026-04-16_001') | |
| timestamp_end | Yes | Unix timestamp - end of time range (filters by session_start) | |
| timestamp_start | Yes | Unix timestamp - start of time range (filters by session_start) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, and closed-world, so the safety profile is covered. The description adds real value beyond them: it discloses that reads span two collections (live store + legacy archive June 2025–June 2026), that nothing is migrated between them, and that each result carries a `source` field naming its origin.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first clause, followed by provenance detail and the usage trigger. Four sentences that each carry information; only the 'second tier, word for word' phrasing is slightly cryptic, but it earns its place by contrasting with the compressed layer.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully explains the return shape (sessions grouped, each carrying `source`) and the two backing stores. Annotations handle safety; parameters are fully covered by the schema. Adequate for a read-only query tool, though it could note ordering or pagination behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters (limit, session_id, timestamp_start/end) are already documented in the schema. The description adds no syntax or filtering nuance beyond that, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Retrieve session transcripts') and clarifies it reads both the live store and the legacy archive. This distinguishes it from query_layer/recall by framing it as the 'second tier' depth source, though the tier jargon is not self-evident without sibling context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete when-to-use condition ('when a layer entry is relevant but thin: the transcript around its timestamp holds the depth the layer compressed out'), implicitly positioning it against query_layer. No explicit exclusions or negative guidance, but the selection condition is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_docARead-onlyIdempotent
Read a continuity document from the versioned document store. A document longer than one page (18,000 UTF-8 bytes by default) comes back in pages: the result then carries page with next_offset - call again with that offset (and expected_version set to the first page's version) until next_offset is null, and do not act on a document you have only part of. If the document changes between pages the read is refused: restart from offset 0. sha256 and size (characters; size_bytes in UTF-8 bytes) always describe the WHOLE document.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional: page size in bytes as the host receives it (default 18,000): a page ends before its JSON-escaped text plus 1,500 bytes kept for the result envelope would pass it, and never splits a character. 0 returns the whole document in one result. | |
| doc_id | Yes | Document id, e.g. 'STATE.md', 'HANDOFF.md', 'threads/pcb-laser-board.md', 'bootstrap/IDENTITY.md' | |
| offset | No | Optional: UTF-8 byte offset to start from (default 0). Use the previous page's `page.next_offset`. | |
| expected_sha256 | No | Optional: the `sha256` your first page returned - the same pin as expected_version, and the only pin for a shipped guide (guides have no version). | |
| expected_version | No | Optional: the `version` your first page returned. Pass it with every later page; if the document changed in between, the read is refused (restart from offset 0) so pages of two versions are never joined. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, but the description adds substantial behavior beyond them: paging rules, refusal-and-restart semantics when a document changes between pages, and the guarantee that `sha256`/`size` describe the whole document rather than the page. These are exactly the traits an agent needs to avoid joining pages of two versions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action, then layers paging, pinning, and invariants in dense sentences with no filler. Every sentence carries operational information an agent must act on.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description must describe return values, and it does: `page` with `next_offset`, `version`, `sha256`, `size`, and `size_bytes`. Combined with the paging protocol, an agent can call and consume the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter meaning the schema does not: `expected_version` must be the first page's version, and `expected_sha256` is the same pin and the only pin for shipped guides. That interaction logic goes beyond per-field schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read a continuity document from the versioned document store'), which clearly separates it from write_doc, list_docs, and read_doc_history. The pagination framing further distinguishes it from sibling read tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit operational guidance: call again with `next_offset` until it is null, pin `expected_version` to the first page's version, and do not act on a partial document. It does not, however, say when to prefer this over siblings like corpus_recall, semantic_recall, or read_doc_history, so routing among read tools is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_doc_historyARead-onlyIdempotent
Read a RETAINED revision of a document, or list the revisions retained for it. Every write archives the prior copy (nothing is deleted); this is the reader for that archive. Without a version: the list of retained revisions with their digests and dates. With a version: that revision's full content, re-hashed against its stored digest. Read-only. Use it for an audit question - what did this document say at version N - or to compare a document with what it replaced.
| Name | Required | Description | Default |
|---|---|---|---|
| doc_id | Yes | Document id, e.g. 'CHARTER.md', 'STATE.md' | |
| version | No | Optional: the revision to read. Omit to list what is retained. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only, idempotent, and non-destructive, yet the description adds substantial context: every write archives the prior copy, nothing is deleted, and a returned revision is re-hashed against its stored digest. This is genuine behavioral disclosure beyond the structured hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then the archive rationale, then the two parameter behaviors and use cases. Information-dense, though the middle clauses are slightly long and could be trimmed without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so: it describes both the list form (digests and dates) and the single-revision form (full content, re-hashed). Combined with the archive semantics, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds return-shape meaning per parameter value: omitting version yields the list of revisions with digests and dates, supplying version yields that revision's full re-hashed content. That is more than the schema's terse per-field notes.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (read/list) plus resource (retained revision) and scope ('Every write archives the prior copy... this is the reader for that archive'), clearly separating it from the sibling read_doc which reads the current copy. An agent can distinguish it without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit use cases are given: 'audit question - what did this document say at version N' and 'compare a document with what it replaced.' This is clear context for when to reach for it. It does not name read_doc explicitly as the alternative for current content, so it stops short of a full when/when-not statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recallARead-onlyIdempotent
Search memories by keyword. Ranked by how many of your words match, then by importance. Current entries come first in every order. Pass order: 'latest' for time order. A standalone latest, newest, or most recent, together with other words, also asks for time order: it reorders those matches and does not change which rows match. A hyphenated word such as latest-budget, a negated 'not the latest', and a bare 'latest' are ordinary keywords. Expired and superseded entries stay in the result, marked with their end date and what replaced them. Pass history: true to read ended entries when current ones fill the limit.
| Name | Required | Description | Default |
|---|---|---|---|
| layer | No | Optional: Search only in specific layer | |
| limit | No | Maximum number of results to return (default: 10) | |
| order | No | Pass 'latest' for time order: current matches first, then newer ones. This does not change which rows match the query. | |
| query | Yes | Search query to match against memory content. order: 'latest' is how you ask for time order. A standalone latest, newest, or most recent reorders the other words' matches. | |
| history | No | Return ended entries (superseded or expired), marked, up to limit. Default false: ended entries are included after current ones until limit. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish read-only, idempotent, non-destructive behavior, so the lower bar applies; the description still adds meaningful result-composition behavior: ranking is by word-match count then importance, current entries sort first in every order, and expired/superseded entries remain in results marked with their end date and replacement. It does not describe pagination beyond the limit, but that is minor against the annotation coverage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core ('Search memories by keyword', ranking, ordering) is front-loaded, which is good. But the middle is dense and repetitive, dwelling on edge cases like hyphenated 'latest-budget' and 'not the latest', and restating the 'latest' reordering rule several times; those sentences do not clearly earn their place for an agent's decision-making.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description appropriately carries the burden of describing what comes back: ranked matches, current-first ordering, and marked ended entries with end dates and replacements. Layer filtering and limit defaults are only covered by the schema, but the description covers the return semantics an agent needs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3, and the description goes beyond the schema by explaining the special keyword semantics of 'order' and 'query' (bare/hyphenated/negated 'latest', standalone 'newest'/'most recent' reordering matches without changing the match set) and the interaction between history and the limit. This is genuine added meaning, though some of it restates the 'order' schema description almost verbatim.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search memories by keyword') and even specifies the matching mechanism (keyword vs. presumably semantic siblings). It does not name any sibling such as semantic_recall or corpus_recall, so an agent must infer which search tool to pick, but the purpose itself is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear trigger conditions for two parameters ('Pass order: latest for time order', 'Pass history: true to read ended entries when current ones fill the limit'), which is real usage guidance. However it offers no guidance on when to choose this tool over the many sibling search tools (semantic_recall, corpus_recall, query_layer), so selection is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rename_docA
Reissue a continuity document under a new doc_id. The destination is written first through the normal write path (same redaction, digest, versioning and sha256 read-back), and only then is the old id marked obsolete with retire_doc. NOTHING IS DELETED: the old id keeps its content and stays readable at its own id, flagged retired with the reason 'Reissued as <new_id>'. list_docs keeps it after the current documents and names the new id in replaced_by. No tombstone document is needed, because the retired original IS the tombstone and it still carries the real content. Refuses if the destination already exists, because a rename onto an occupied id is a merge decision and merges are not guessed.
| Name | Required | Description | Default |
|---|---|---|---|
| new_id | Yes | Destination document id. Must not already exist. | |
| old_id | Yes | Existing document id to reissue from. It is retired, not removed. | |
| reason | Yes | Why the document is reissued. Required, at least 10 characters. | |
| source | No | Source tag - which host produced this write. One name on every write since 2026-09-27. | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| session_id | No | Transcript session id. | |
| updated_by | No | Alias of `source` (the older name, kept one release). Prefer `source`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false and destructiveHint=false; the description goes well beyond them by stating nothing is deleted, the old id stays readable, it is flagged retired with reason 'Reissued as <new_id>', and list_docs shows replaced_by. It also discloses the refusal semantics on an occupied destination, which is behavior no structured field carries.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and mostly disciplined, but the 'No tombstone document is needed' sentence and the closing rationale about merges are discursive justification rather than call-critical instruction. Still, each sentence maps to a distinct behavior an agent should know.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter mutation tool with no output schema, it covers destructive-vs-retain semantics, ordering, refusal, and downstream list_docs effects well. What remains unstated is the response shape and whether the destination write is transactional if the retire step fails.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all seven parameters including old_id/new_id semantics and the exchange/session graph rule. The description reinforces old_id/new_id but adds no new syntax or format detail for the four context parameters (source, exchange, session_id, updated_by). Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Reissue a continuity document under a new doc_id') and immediately distinguishes the operation from a plain rename or delete. It names the collaborating siblings (retire_doc, list_docs) so the agent can place it in the corpus without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear when-not condition ('Refuses if the destination already exists') and explains the internal ordering (destination written first, then retire_doc marks the old id). It references sibling behavior but does not explicitly route to an alternative tool for the merge case, only saying merges are not guessed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
restore_docADestructive
Write a retained revision back as the current document. The retained row is copied through the normal write path, with a reason. Nothing is deleted.
| Name | Required | Description | Default |
|---|---|---|---|
| doc_id | Yes | Document id to restore. | |
| reason | Yes | Why this revision is restored. At least 10 characters. | |
| source | Yes | Source tag. Must be in EMET_RESTORE_TAGS. A missing source is refused. | |
| version | Yes | Retained revision number to write back. | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| session_id | No | Transcript session id. Required on Path 2. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and idempotentHint=false, so the safety profile is known. The description adds real context beyond that: the retained row goes through the normal write path and no rows are deleted, which usefully qualifies what 'destructive' means here. It still doesn't state permission requirements or failure behavior beyond what the schema carries.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the action, and the clarifying 'Nothing is deleted' earns its place by tempering the destructive hint. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with annotations and a fully documented six-parameter schema, the description covers the essential mechanism. It lacks any note on permissions, partial-failure behavior, or what the caller gets back, but with no output schema and rich annotations those gaps are minor.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter (including the source-tag allowlist and exchange ordering rule) is already documented in the schema. The description alludes to 'a reason' and 'retained revision' but adds no format or constraint detail beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: writing a retained revision back as the current document. An agent understands the operation immediately, but the description never distinguishes it from siblings like patch_doc, write_doc, or read_doc_history.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the mechanics described (restoring a retained revision), but there is no explicit when-to-use guidance, no exclusions, and no mention of the sibling tools that also write document content. The agent must infer that this is the revert path rather than a normal write.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
retire_docAIdempotent
Mark a continuity document OBSOLETE. Nothing is deleted: the document keeps its id, its full content, its version and its digest, and read_doc still returns it, flagged retired, with the reason. list_docs and the startup scan keep it too, after the current documents, with retired_at and what replaced it. A retired drop is not an unhandled drop. This is document control as ISO 7.5.3 describes it - issue the revision, mark the prior copy obsolete, retain it - and it is the document-store counterpart of revise_memory superseding a layer entry. THE STORE HAS NO DELETE, BY DESIGN. Use for genuine litter: tombstones, merged staging documents, test scratch. Reversible: write_doc on the same id brings it back into the working view.
| Name | Required | Description | Default |
|---|---|---|---|
| doc_id | Yes | Document id to mark obsolete. | |
| reason | Yes | Why it is obsolete. Required, at least 10 characters. Drops only. | |
| source | No | Source tag - which host produced this write. One name on every write since 2026-09-27. | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| retired_by | No | Alias of `source` (the older name, kept one release). Prefer `source`. | |
| session_id | No | Transcript session id. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag idempotentHint and destructiveHint=false, but the description goes far beyond: it enumerates exactly what is preserved (id, content, version, digest), how read_doc and list_docs surface the retired state (retired_at, replacement), and that the operation is reversible via write_doc on the same id. This is exactly the added context the bar asks for.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core action and its non-destructive nature are front-loaded, and most sentences carry real routing or behavioral information. The ISO 7.5.3 framing and 'a retired drop is not an unhandled drop' add flavor that is less essential, keeping it just below top marks.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by describing the resulting state (flagged retired, retained in listings, reversible). Combined with annotations that align with the described behavior, an agent has everything needed to call this correctly and predict the outcome.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents doc_id, reason, source, exchange, and the retired_by alias. The description only echoes the reason ('with the reason') without adding format or constraint detail, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (mark OBSOLETE) and resource (continuity document), and explicitly distinguishes itself from siblings: read_doc still returns it, list_docs keeps it, write_doc restores it, and revise_memory is named as the analogous layer operation. An agent can route to it without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to-use cases ('genuine litter: tombstones, merged staging documents, test scratch') and rules out the delete mental model with 'THE STORE HAS NO DELETE, BY DESIGN'. It stops short of explicitly contrasting against a near-alternative tool for the same state change, so it is strong context rather than full when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
revise_memoryA
Supersede an existing entry with a revised one (supersession, not mutation). Creates a NEW entry carrying metadata.supersedes=<target_id> and marks the target with superseded_by and superseded_at. The old entry stays readable: it is not hidden and is_live is not cleared. Recall, query_layer and semantic_recall return it marked with that end date and the id of the new entry. The current entry is listed first. Episodic revision is prohibited - events are immutable.
| Name | Required | Description | Default |
|---|---|---|---|
| layer | Yes | Layer of the entry being revised (episodic is prohibited) | |
| content | Yes | Full revised content - must stand alone without the superseded entry | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| metadata | No | Metadata for the new entry (supersedes is set automatically; include source tag) | |
| target_id | Yes | ID of the entry this revision supersedes | |
| session_id | No | Your transcript session id from emet_session_open. The session graph reads this session's state before the write and returns `session_graph.next` - the next step. A governed write with no session is refused (open one with emet_session_open and load_id). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing the exact side effects: a NEW entry is created rather than the target being mutated, the target is marked with superseded_by/superseded_at, and crucially the old entry stays readable (not hidden, is_live not cleared). It also explains how recall/query_layer/semantic_recall surface the superseded entry, which is non-obvious behavior an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the most decision-relevant fact (supersession, not mutation) and each following sentence carries distinct behavioral information. It is dense rather than padded, though the trailing sentence about ordering ('The current entry is listed first') sits slightly oddly at the end.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter, nested-object write tool with no output schema, the description covers the mutation model, the after-state of both entries, downstream read behavior, and the layer restriction. Nothing essential for correct invocation appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 and the schema already documents layer, content, exchange, metadata, target_id and session_id semantics. The description restates that metadata.supersedes is set automatically, which largely duplicates the schema text rather than adding format or value guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Supersede an existing entry with a revised one') and immediately draws the key distinction 'supersession, not mutation', which separates it from the mutation-style siblings. It then specifies the concrete mechanism (new entry with metadata.supersedes=<target_id>, target marked with superseded_by/superseded_at).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear negative constraint ('Episodic revision is prohibited - events are immutable') which routes the agent away from episodic-layer targets. However, it never explicitly contrasts with siblings like save_to_layer or revise_transcript, so the agent must infer when supersession is preferred over a plain write.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
revise_transcriptA
Supersede a stored transcript part with a corrected one. Nothing is edited or deleted: a NEW document is written under the same session_id with an incremented revision and a pointer to its parent, and the parent is marked superseded. Use when a saved part has a mispaired, missing or wrong exchange. The reason parameter is required - an unexplained correction is a silent rewrite. To ADD a new part, use save_transcript instead. Allowed on a closed session (a correction deletes nothing), but there a revision may only correct the exchanges the part already holds - never add new ones - and must carry exactly the same exchange numbers (none dropped or repeated; numbers sent as text are stored as numbers).
| Name | Required | Description | Default |
|---|---|---|---|
| reason | Yes | What was wrong and how this differs. Required, minimum 10 characters. This is the audit record of the correction. | |
| source | No | Source tag - which host produced this write. Defaults to the parent's. | |
| channel | No | Alias of `source` (the older name, kept one release). Prefer `source`. | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| exchanges | Yes | The COMPLETE corrected exchange list for this part, verbatim. A revision replaces the whole part. | |
| session_id | Yes | The session_id of the part to correct, e.g. '2026-09-04_x_verbatim_part14'. Must already exist. | |
| session_end | No | Optional. Defaults to the parent's. | |
| session_start | No | Optional. Defaults to the parent's. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare non-read-only, non-destructive, non-idempotent, and the description explains why: nothing is edited or deleted, a new document is written and the parent marked superseded. It adds behavioral facts beyond the annotations, including the mandatory reason ('an unexplained correction is a silent rewrite') and the closed-session exchange-number rules. No contradiction with the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but well front-loaded: the mechanism comes first, then when-to-use, then the closed-session caveat. Each sentence carries distinct information, though the final clause is long and could be split for readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter mutation tool with no output schema, the description covers replacement semantics, the required audit reason, sibling routing, and edge-case behavior on closed sessions. An agent has everything needed to invoke it correctly; return values are not its burden.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description still adds meaning by requiring the reason and explaining the closed-session constraint that exchanges must match exactly and text numbers are stored as numbers. It does not add syntax detail for source/channel/session_start/session_end beyond the schema, keeping it at 4 rather than 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the specific verb and resource ('Supersede a stored transcript part with a corrected one') and immediately clarifies the mechanism (new document, incremented revision, parent superseded). It distinguishes itself from the sibling save_transcript by contrast. An agent can identify the operation without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the trigger ('when a saved part has a mispaired, missing or wrong exchange') and the alternative ('To ADD a new part, use save_transcript instead'). It further qualifies usage on a closed session with concrete constraints, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_to_layerA
Save memory to a specific layer with full control over metadata. PASS session_id - your transcript session from emet_session_open - on every save made inside a session: the server stamps derived_from with the exchange in progress. Semantic and procedural entries are REFUSED without a source (session_id, or metadata.derived_from); nothing is written when refused.
| Name | Required | Description | Default |
|---|---|---|---|
| layer | Yes | Target memory layer | |
| content | Yes | Memory content to save | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| metadata | No | Full metadata control | |
| session_id | No | Your transcript session id (from emet_session_open). The server reads that session's counter and stamps derived_from {session_id, exchange_start, exchange_end} with the exchange in progress. Ignored when metadata.derived_from is given. Required in practice for semantic and procedural saves made in a session. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish non-readOnly, non-destructive and non-idempotent. The description adds genuinely new behavior: the server auto-stamps derived_from from the in-progress exchange, semantic/procedural writes are refused without a source, and nothing is written on refusal. It omits what a successful write returns, but for a write tool with no output schema this is solid added context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, purpose front-loaded before the operational caveats, with no filler. The all-caps emphasis (PASS, REFUSED) is slightly heavy but each sentence carries distinct, load-bearing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-parameter, nested-object write tool this covers the main agent-facing risks: source attribution requirements, refusal behavior, and session stamping. Return values are undescribed and there is no output schema, but since the tool's observable outcome is whether the write succeeded or was refused, the description is effectively complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description goes beyond the schema by explaining the runtime effect of session_id (reads the session counter and stamps derived_from) and its precedence over metadata.derived_from, plus the practical requirement that overrides the schema's optional-looking status.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (save) and resource (memory layer) plus the distinguishing capability of full metadata control. It separates itself from query/recall siblings but does not explicitly contrast with the closely-named save_transcript, leaving that distinction to be inferred.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit operational rule: pass session_id on every save made inside a session, and that semantic/procedural saves are refused without a reachable source. This is strong when-to-use guidance, though it never names an alternative tool (e.g. save_transcript) or states when not to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_transcriptA
FALLBACK. The primary path is emet_session_open then emet_transcript_append after every reply; use this only where a host cannot append per reply. Saves the full word-for-word dialog of a session - full transcripts only, no summaries. Pass exchanges in the call. The server does not read a file. Refused for a session that was closed complete.
| Name | Required | Description | Default |
|---|---|---|---|
| source | No | Source tag - which host produced this write. One name on every write since 2026-09-27. | |
| channel | No | Alias of `source` (the older name, kept one release). Prefer `source`. | |
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| exchanges | Yes | Claude.ai Project mode: full array of exchange objects for this session | |
| session_id | Yes | Unique session identifier, e.g. 'claude_project_2026-05-23_001' | |
| session_end | No | Unix timestamp — session end (optional, defaults to now) | |
| session_start | Yes | Unix timestamp — session start |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the safety profile (non-read-only, non-destructive, non-idempotent), so the description carries real extra weight: it discloses that exchanges must be passed inline, that the server does not read a file, and that the call is refused for a session already closed complete. It does not address duplicate-write behavior despite idempotentHint=false, which is the one remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads 'FALLBACK.' so the routing decision is the first thing read, and every clause carries a distinct constraint (primary path, per-reply alternative, inline payload, no file read, refusal condition). The dense second sentence is slightly packed but nothing is wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a non-idempotent write with no output schema, the description covers the key operational facts: inline data, no server-side file read, whole-transcript requirement, and the closed-complete refusal. Only success/response behavior and duplicate-write handling are unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, including ordering rules for exchange and naming/aliasing notes for source vs channel, so the schema does the heavy lifting. The description only adds the generic 'pass exchanges in the call' instruction, which is the baseline 3 for a fully documented schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (saves a full word-for-word session transcript) and explicitly positions itself as the fallback against named siblings emet_session_open and emet_transcript_append. An agent can distinguish it from the primary append path without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-to-use condition ('use this only where a host cannot append per reply') plus the preferred alternative path, and adds an exclusion ('full transcripts only, no summaries'). Nothing about selection is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
semantic_recallARead-onlyIdempotent
Semantic (meaning-based) memory search across all six memory layers via Atlas Vector Search autoEmbed. Finds conceptually related memories even without keyword overlap. Complements keyword-based recall/query_layer.
| Name | Required | Description | Default |
|---|---|---|---|
| layer | No | Optional: restrict to one layer | |
| limit | No | Max results (default 10) | |
| query | Yes | Natural-language query - embedded automatically server-side (voyage-4) | |
| history | No | Return ended entries only, up to limit. Default false: current matches come first and ended matches follow until limit, marked with their end date. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already carry the safety profile (readOnly, idempotent, closed-world, non-destructive), so the bar is lower. The description adds real context beyond them: the search is vector/embedding based, embedding happens server-side automatically, and results can be conceptually related without lexical overlap. It does not mention pagination or result scoring, but the substance added is meaningful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences that are front-loaded with the primary capability and end with the routing hint. Every clause carries information: what it does, how, and what it complements.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter, all-optional-but-one search tool with 100% schema coverage and a full annotation set, the description covers purpose and differentiation adequately. The only gap is return-shape detail, but the history parameter's schema text and the absence of an output schema make this a minor omission.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% — every parameter including the layer enum, limit default, and the history semantics is documented in the schema itself. The description adds only the broad 'across all six memory layers' framing, which loosely relates to the layer parameter, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Semantic memory search across all six memory layers') plus the mechanism (Atlas Vector Search autoEmbed) and the distinguishing property ('finds conceptually related memories even without keyword overlap'). It also names the siblings it is not — keyword-based recall/query_layer — so an agent can pick it apart from them without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly frames itself as a complement to keyword-based recall/query_layer, which gives the agent a clear selection rule: use this for meaning-based matching, those for keyword matching. It stops short of spelling out an exclusion (e.g. when keyword search is strictly preferable) or cost/latency tradeoffs, so it is clear context rather than full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_docARead-onlyIdempotent
Check a controlled document against the template that governs it, WITHOUT writing anything. The same check the write path runs on every document write - this is the freestanding form, for auditing records written before validation existed, for counting what adoption will mark before it marks anything, and for checking new content before it is stored. Pass doc_id to check a stored document (the previous revision is pulled from the archive so the carry-forward check runs). Pass content, with template_id or a doc_id the map covers, to check text that is not stored. Checks FORM and CONTINUITY only: sections present, in order and filled; no placeholder left unfilled; NOT COMPLETED carrying a reason; a board's counts matching its rows; every row on the previous revision still present or dispositioned. It cannot check whether an answer is true, or whether a 'none' states its scope honestly - those stay with the author and the reader.
| Name | Required | Description | Default |
|---|---|---|---|
| doc_id | No | Stored document to check. Alone, it is read from the store and compared against its own previous revision. | |
| content | No | Text to check instead of a stored document. Requires template_id, or a doc_id the map covers. | |
| template_id | No | Template to check against, e.g. 'templates/BOARD.md'. Read from the copy shipped with this release, never the store copy. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, yet the description adds substantial behavior: no writes occur, the previous revision is pulled from the archive so the carry-forward check runs, and the check scope is bounded to FORM and CONTINUITY. It additionally discloses what the tool cannot do (verify truth or honest scope of a 'none'), which is exactly the kind of limit an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the no-write guarantee, then use cases, then parameter behavior, then scope limits. Dense and mostly waste-free, though the middle sentence packs three use cases and the boundary paragraph is long relative to the rest.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-optional-param, no-output-schema tool, the description is nearly sufficient: it explains inputs, the archive comparison, and the check's boundaries. It does not describe the shape of the result (e.g., that a report/count of findings is returned), leaving the response format to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: doc_id pulls the prior revision from the archive for carry-forward comparison, content requires template_id or a covered doc_id, and template_id is read from the shipped copy rather than the store copy. These interaction rules go beyond the field-level schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb ('Check') plus resource ('controlled document against the template that governs it') and an explicit scoping qualifier ('WITHOUT writing anything'). It distinguishes itself from the write path it mirrors and from doc-mutating siblings, so an agent can identify it without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Enumerates three concrete use cases (auditing pre-validation records, counting adoption impact, pre-write checks of unstored content) and notes it is the freestanding counterpart of the write-path check, which implicitly routes agents away from write_doc/patch_doc. It lacks an explicit 'do not use this for X' exclusion or a named alternative tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
write_docADestructive
Write or update a continuity document in the document store (versioned, prior copy archived to documents_history and marked obsolete, sha256 read-back verified). RECORDS - the handoff (HANDOFF.md) and any id in EMET_RECORD_DOCS - are superseded, never amended: patch_doc is refused on them, and a new revision is written here WHOLE with supersedes set to the version currently in force (ISO 9001 7.5.3 / 13485 4.2.5).
| Name | Required | Description | Default |
|---|---|---|---|
| doc_id | Yes | ||
| reason | No | Required on a record revision (at least 10 characters). Stored as edit_reason. | |
| source | No | Source tag - which host produced this write. One name on every write since 2026-09-27. | |
| content | Yes | ||
| exchange | No | The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended. | |
| session_id | No | Transcript session id. The floor is measured from the size when this session first touched the document. | |
| supersedes | No | Required when writing a new revision of a RECORD (the handoff): the version currently in force, which this revision supersedes. Ignored for ordinary documents. | |
| updated_by | No | Alias of `source` (the older name, kept one release). Prefer `source`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare destructiveHint=true and idempotentHint=false; the description adds substantial behavioral detail beyond them - the prior copy is archived to documents_history and marked obsolete, the write is sha256 read-back verified, and record writes must carry supersedes pointing at the version in force. That is exactly the kind of consequence information an agent needs before a mutation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first clause, followed by behavioral guarantees and the record rule. The parentheticals (documents_history, sha256, ISO references) are dense but each carries real information; the single long sentence is slightly heavy but not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with annotations present and no output schema, the description covers the versioning, archival and record-supersession workflow well. Remaining gaps (reason minimum length, session_id floor semantics, exchange ordering) are left to the schema, which is acceptable given 75% coverage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 75% and the schema already documents reason, source, exchange, session_id and supersedes in detail. The description restates the supersedes requirement rather than adding format or syntax beyond the schema, so with the schema doing the heavy lifting this sits at the baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (write or update) and resource (continuity document in the document store), and immediately distinguishes itself from patch_doc by naming the record case where patching is refused. An agent can tell it apart from read_doc, retire_doc, rename_doc and patch_doc without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit routing for RECORDS: those are superseded, never amended, and patch_doc is refused on them, so write_doc is the required path. However, it never states the inverse condition for ordinary documents (i.e. when a small edit should go through patch_doc instead), so the alternative-usage guidance is one-sided rather than complete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.3.1- Added
emet_error_report - Added
emet_page - Added
emet_page_lint - Added
emet_pages - Changed
emet_setup_complete2 fields changed- added
Input schema / properties / answers / properties / bot_team_backupAdded value: +{ + "description": "Optional. Yes or no, in the user's words. Yes and Yes. count as yes. yes is the recommended choice and records bot-team backup as on. Absent or no leaves it off. A non-string is refused. Do not invent this answer.", + "type": "string" +} - added
Input schema / properties / answers / properties / more_than_one_botAdded value: +{ + "description": "Optional. yes or no, in the user's words. Absent or no leaves sub-keys off. yes turns them on only when this server reads .env. Do not invent this answer.", + "type": "string" +}
12 tool updates
v0.2.5- Added
emet_client_approve - Changed
emet_floor2 fields changed- added
Input schema / properties / gemAdded value: +{ + "description": "Read one installed GEM's floor by id, for example 'neurendipity'. Reading does not turn the GEM on. Omit it to read the core floor.", + "type": "string" +} - changed
Input schema / properties / section / descriptionPrevious value: -"Return one section only, e.g. 'neurodivergent_support'. Call with no arguments to see the section names."New value: +"Return one section only, e.g. 'voice'. Call with no arguments to see the section names."
- Changed
emet_initialize1 field changed- added
Input schema / properties / sourceAdded value: +{ + "description": "Your own source tag (the bot or host you are). The startup then reports the tag your writes will carry and its approval state.", + "type": "string" +}
- Changed
emet_invite_create3 fields changed- changed
Input schema / properties / scopes / descriptionPrevious value: -"Default: emet.read only"New value: +"Guest invites accept only emet.read." - changed
Input schema / properties / scopes / items / enumPrevious value: -[ - "emet.read", - "emet.write" -]New value: +[ + "emet.read" +] - added
Input schema / properties / sourceAdded value: +{ + "description": "Source tag bound to this guest at approval. Exact membership of EMET_SOURCE_TAGS when a registry is set.", + "type": "string" +}
- Changed
emet_member_access_create1 field changed- added
Input schema / properties / sourceAdded value: +{ + "description": "Source tag bound to this member at approval. Exact membership of EMET_SOURCE_TAGS when a registry is set. Required for this member to write documents over HTTP while a registry is set.", + "type": "string" +}
- Changed
emet_status1 field changed- added
Input schema / properties / sourceAdded value: +{ + "description": "Your own source tag. The status then reports the tag your writes will carry and its approval state.", + "type": "string" +}
- Changed
list_docs1 field changed- changed
Input schema / properties / include_retired / descriptionPrevious value: -"Include documents marked obsolete. Default false."New value: +"Accepted. Retired documents are always listed, after the current ones. This flag does not hide them."
- Changed
query_layer4 fields changed- added
Input schema / properties / options / properties / historyAdded value: +{ + "description": "Return ended entries only, up to limit, so a page full of current entries cannot leave them out. Default false: ended entries follow the current ones until limit.", + "type": "boolean" +} - changed
Input schema / properties / options / properties / include_superseded / descriptionPrevious value: -"Whether entries replaced by a later revision are returned. DEFAULT TRUE since 2026-09-13: a superseded entry surfaces carrying superseded_by and is_superseded, so a revision is read in the context of what it replaced. Pass false for the live-only view. Expired entries are filtered either way - expiry and supersession are separate."New value: +"Accepted, and it does not hide anything. Superseded and expired entries are always returned, marked with their end date and what replaced them. Current entries are listed first." - changed
Input schema / properties / options / properties / limit / descriptionPrevious value: -"Max results (1-1000, default: 20)"New value: +"Max results (1-1000, default: 10)" - changed
Input schema / properties / options / properties / order_by / descriptionPrevious value: -"Sort field and direction, e.g. 'timestamp DESC' or 'importance ASC'. Fields: id, timestamp, importance, emotional_intensity, content, context. Anything else falls back to 'timestamp DESC'."New value: +"Sort field and direction, e.g. 'importance DESC' or 'timestamp DESC'. Fields: id, timestamp, importance, emotional_intensity, content, context. Default is importance descending. Current entries come first in every order, including timestamp. Anything else falls back to importance descending."
- Changed
recall3 fields changed- added
Input schema / properties / historyAdded value: +{ + "description": "Return ended entries (superseded or expired), marked, up to limit. Default false: ended entries are included after current ones until limit.", + "type": "boolean" +} - added
Input schema / properties / orderAdded value: +{ + "description": "Pass 'latest' for time order: current matches first, then newer ones. This does not change which rows match the query.", + "enum": [ + "latest" + ], + "type": "string" +} - changed
Input schema / properties / query / descriptionPrevious value: -"Search query to match against memory content"New value: +"Search query to match against memory content. order: 'latest' is how you ask for time order. A standalone latest, newest, or most recent reorders the other words' matches."
- Changed
save_to_layer1 field changed- changed
Input schema / properties / metadata / properties / valid_until / descriptionPrevious value: -"Unix seconds. After this the entry stops surfacing in recall and queries (it is retained, never deleted). Meant for the working layer, whose entries are supposed to age out."New value: +"Unix seconds. After this the entry is marked ended and stays in recall and queries, with this date as its end date. It is not hidden and it is not deleted."
- Changed
save_transcript2 fields changed- removed
Input schema / properties / jsonl_pathRemoved value: -{ - "description": "CLI mode: absolute path to Claude Code session JSONL file. Tool reads and parses it automatically.", - "type": "string" -} - changed
Input schema / requiredPrevious value: -[ - "session_id", - "session_start" -]New value: +[ + "session_id", + "session_start", + "exchanges" +]
- Changed
semantic_recall1 field changed- added
Input schema / properties / historyAdded value: +{ + "description": "Return ended entries only, up to limit. Default false: current matches come first and ended matches follow until limit, marked with their end date.", + "type": "boolean" +}
14 tool updates
v0.2.2- Changed
accept_nonconformance2 fields changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended." - changed
Input schema / properties / session_id / descriptionPrevious value: -"Your transcript session id from emet_session_open. The session graph reads this session's state before the write and returns `session_graph.next` - the next step."New value: +"Transcript session id."
- Changed
emet_initialize2 fields changed- added
Input schema / properties / load_idAdded value: +{ + "description": "The load_id your startup returned in `startup_page.load_id`: every emet_initialize result carries one (each page of a paged startup, the whole startup with page \"all\" or when it fits one result, and a store that is not set up yet). Pass it with every later page so all pages come from the same startup, and pass the same load_id to emet_session_open so its startup page check runs on this startup.", + "type": "string" +} - added
Input schema / properties / pageAdded value: +{ + "description": "The startup is served in pages when it is larger than one result (each page fits under 20,000 bytes). Omit for page 1; every page but the last says which page to ask for next - call again with that page, before replying, until a page says it is the last. \"all\" returns the whole startup in one result, for a host with no size limit." +}
- Changed
emet_session_close2 fields changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended." - added
Input schema / properties / sourceAdded value: +{ + "description": "Your host's source tag: the registered tag this session was written under. The close is never refused for it; an unregistered tag, one that did not write this session, or none (with no server default) gets a warning that the close is not attributed.", + "type": "string" +}
- Changed
emet_session_open1 field changed- added
Input schema / properties / load_idAdded value: +{ + "description": "Required for the startup page check (by policy; the schema leaves it optional so older hosts can still open): the load_id from `startup_page` on your emet_initialize pages - page 1 returns it, every page carries it, and the last page names it. An open without load_id, or with a page of that load known not fetched, is refused before anything is written (gate startup_pages); the refusal names the exact calls that pass.", + "type": "string" +}
- Changed
patch_doc7 fields changed- added
Input schema / properties / editsAdded value: +{ + "description": "Several replacements in one revision. Each old_str must match the current text exactly once. Applied together, so a failed edit writes nothing.", + "items": { + "type": "object" + }, + "type": "array" +} - changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended." - changed
Input schema / properties / old_str / descriptionPrevious value: -"Exact text to replace. Must occur exactly once - include surrounding context to disambiguate."New value: +"Exact text to replace. Must occur exactly once in the current text. Omit when `edits` is set." - added
Input schema / properties / originAdded value: +{ + "description": "Required when a board edit adds a row id. Names the session, chat or drop the row came from.", + "type": "string" +} - added
Input schema / properties / reasonAdded value: +{ + "description": "Required, at least 10 characters. A removed or closed board row says why.", + "type": "string" +} - changed
Input schema / properties / session_id / descriptionPrevious value: -"Your transcript session id from emet_session_open. The session graph reads this session's state before the write and returns `session_graph.next` - the next step."New value: +"Transcript session id. The floor is measured from the size when this session first touched the document." - changed
Input schema / requiredPrevious value: -[ - "doc_id", - "old_str", - "new_str" -]New value: +[ + "doc_id", + "reason" +]
- Changed
read_doc4 fields changed- added
Input schema / properties / expected_sha256Added value: +{ + "description": "Optional: the `sha256` your first page returned - the same pin as expected_version, and the only pin for a shipped guide (guides have no version).", + "type": "string" +} - added
Input schema / properties / expected_versionAdded value: +{ + "description": "Optional: the `version` your first page returned. Pass it with every later page; if the document changed in between, the read is refused (restart from offset 0) so pages of two versions are never joined.", + "type": "number" +} - changed
Input schema / properties / limit / descriptionPrevious value: -"Optional: page size in characters (default 40,000). 0 returns the whole document in one result."New value: +"Optional: page size in bytes as the host receives it (default 18,000): a page ends before its JSON-escaped text plus 1,500 bytes kept for the result envelope would pass it, and never splits a character. 0 returns the whole document in one result." - changed
Input schema / properties / offset / descriptionPrevious value: -"Optional: character offset to start from (default 0). Use the previous page's `page.next_offset`."New value: +"Optional: UTF-8 byte offset to start from (default 0). Use the previous page's `page.next_offset`."
- Changed
rename_doc4 fields changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended." - added
Input schema / properties / reasonAdded value: +{ + "description": "Why the document is reissued. Required, at least 10 characters.", + "type": "string" +} - changed
Input schema / properties / session_id / descriptionPrevious value: -"Your transcript session id from emet_session_open. The session graph reads this session's state before the write and returns `session_graph.next` - the next step."New value: +"Transcript session id." - changed
Input schema / requiredPrevious value: -[ - "old_id", - "new_id" -]New value: +[ + "old_id", + "new_id", + "reason" +]
- Added
restore_doc - Changed
retire_doc4 fields changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended." - changed
Input schema / properties / reason / descriptionPrevious value: -"Why it is obsolete, e.g. 'merged into threads/income-transition.md'. Recorded on the document and shown by read_doc."New value: +"Why it is obsolete. Required, at least 10 characters. Drops only." - changed
Input schema / properties / session_id / descriptionPrevious value: -"Your transcript session id from emet_session_open. The session graph reads this session's state before the write and returns `session_graph.next` - the next step."New value: +"Transcript session id." - changed
Input schema / requiredPrevious value: -[ - "doc_id" -]New value: +[ + "doc_id", + "reason" +]
- Changed
revise_memory2 fields changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended." - changed
Input schema / properties / session_id / descriptionPrevious value: -"Your transcript session id from emet_session_open. The session graph reads this session's state before the write and returns `session_graph.next` - the next step."New value: +"Your transcript session id from emet_session_open. The session graph reads this session's state before the write and returns `session_graph.next` - the next step. A governed write with no session is refused (open one with emet_session_open and load_id)."
- Changed
revise_transcript1 field changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended."
- Changed
save_to_layer1 field changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended."
- Changed
save_transcript1 field changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended."
- Changed
write_doc3 fields changed- changed
Input schema / properties / exchange / descriptionPrevious value: -"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is flagged by the session graph."New value: +"The exchange (turn) this write belongs to, counting from 1. A write for exchange n while exchange n-1 was never appended is refused by the session graph until n-1 is appended." - added
Input schema / properties / reasonAdded value: +{ + "description": "Required on a record revision (at least 10 characters). Stored as edit_reason.", + "type": "string" +} - changed
Input schema / properties / session_id / descriptionPrevious value: -"Your transcript session id from emet_session_open. The session graph reads this session's state before the write and returns `session_graph.next` - the next step."New value: +"Transcript session id. The floor is measured from the size when this session first touched the document."
28 tool updates
v0.1.0- First observed
accept_nonconformance - First observed
corpus_recall - First observed
emet_floor - First observed
emet_gaps - First observed
emet_initialize - First observed
emet_invite_create - First observed
emet_member_access_create - First observed
emet_session_close - First observed
emet_session_open - First observed
emet_setup_complete - First observed
emet_status - First observed
emet_transcript_append - First observed
list_docs - First observed
patch_doc - First observed
query_layer - First observed
query_transcripts - First observed
read_doc - First observed
read_doc_history - First observed
recall - First observed
rename_doc - First observed
retire_doc - First observed
revise_memory - First observed
revise_transcript - First observed
save_to_layer - First observed
save_transcript - First observed
semantic_recall - First observed
validate_doc - First observed
write_doc
TDQS
Scored across 30 tools
Several clusters overlap in purpose: recall/semantic_recall/query_layer/corpus_recall all search memory; write_doc vs patch_doc both edit documents; retire_doc/rename_doc/restore_doc all change document status. The descriptions are unusually detailed and explicitly route usage, but the agent must read carefully to avoid misselection.
Snake_case is used throughout, but the surface mixes an emet_ prefix on roughly a third of tools with unprefixed names, and mixes verb_noun forms (read_doc, save_transcript) with noun and noun_verb forms (emet_status, emet_session_open, emet_floor). The pattern is not predictable.
30 tools is above the 25-tool threshold for 'too many'. The domain is broad—memory, transcripts, documents, identity, access control—but the count still feels heavy and several closely related operations could have been consolidated.
The surface covers setup, session lifecycle, memory write/recall/revise, full document versioning (write/read/patch/list/history/restore/rename/retire/validate), transcripts, access control, and gap reporting. No obvious dead end is apparent for the stated purpose; the lack of delete is intentional by design.
Maintenance
Related MCP Connectors
- memnodeOAuthdev.memnode
Persistent, inspectable memory for AI agents with lineage, correction, and a hosted MCP endpoint.
Verified memory for AI agents. Signed assertions, billing attestation, session continuity.
Governed local-first memory and continuity for AI agents — signed receipts, optional hosted lane.
Hosted agent memory with provenance, contradiction surfacing, and snapshot rollback.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables agents to read and write a durable, append-only memory with verified provenance, supporting search, record capture, and context preparation across sessions and machines.MIT
- AlicenseNot gradedqualityAmaintenanceProvides AI agents a shared long-term memory layer with evidence-backed, auditable claims, enabling persistent, explainable, and conflict-aware recall across sessions.212 PyPI7MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to store and recall an append-only, hash-chained memory with citation-backed semantic search, fact verification, contradiction detection, and replay/audit capabilities through local MCP tools.1AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to maintain persistent memory across sessions with verifiable source receipts, supporting explicit remember, recall, inspect, correct, and forget operations.1 npm31Apache 2.0