Bound to the token
Every call is evaluated inside one workspace. There is no parameter to name another.
Unforgetter exposes a stateless MCP server over Streamable HTTP. The tool surface is deliberately small: an agent can list the projects its token allows, get a compiled context bundle, search deeper, and propose a change. It cannot write canonical memory.
Identity and scope
Every call is evaluated inside one workspace. There is no parameter to name another.
Claude, Codex and the Grok Bot zone each hold their own token. The Grok Bot zone is treated as one shared trust zone with a shorter lifetime.
list_projects returns only allowed projects. Selecting one in later calls cannot widen authority; an unlisted project id is refused.
Restricted material is absent from the catalogue and refused on direct retrieval, search, cursor replay and as a proposal target.
Tools
The shapes below represent the current contract and are labelled Working now. Client-facing schemas are versioned as the alpha evolves; behaviour and security boundaries will not loosen silently.
list_projects()
→ { projects: [ { id, name, data_class, updated_at } ] }
Only allowed projects are returned. Restricted projects are not listed.get_context({ project_id, task })
→ {
context_id, // request id, used for dedupe and audit
freshness: { compiled_at, replica_at },
identity: { actor, trust_zone, role },
policies: [ ... ], // protected, always included where relevant
facts: [ { state, kind, text, provenance: { source, date, actor } } ],
open_threads: [ ... ],
methods: [ ... ], // instruction-only skills relevant to the task
pointers: [ { ref, why } ] // deeper retrieval, not the vault
}
The bundle is bounded. The whole workspace is never returned.search_memory({ project_id, query, cursor? })
→ { results: [ { ref, excerpt, provenance, data_class } ], cursor? }
Results are limited to the selected project and the token's data classes.
A cursor from one project is invalid in another.propose_memory({
project_id,
request_id, // client-supplied; replays are acknowledged, not duplicated
change: { op: "add_fact" | "update_fact" | "open_thread" | "close_thread" | "reject",
target?, text, supersedes? },
evidence: [ { ref | text, trust } ]
})
→ { proposal_id, status: "queued" | "quarantined", taint }
Proposals are append-only and attested. They are delivered to the
consolidator exactly once. Nothing is published until it is reviewed.Proposal flow
A proposal whose evidence is trusted is queued. A proposal carrying untrusted evidence, or coming from a lower-trust zone, is quarantined and marked for stricter review. Taint never decreases.
The consolidator pulls pending proposals from a watermark. Retries are safe; a replayed request id is acknowledged without creating a duplicate.
Schema, scope, protected targets, conflicts with newer decisions and provenance are checked. Protected targets and concrete injection signals are rejected.
Accepted changes become part of the working truth and are served from the next get_context. Rejected proposals are kept as rejected so they are not re-proposed.
Errors and recovery
| Error | When | What the agent should do |
|---|---|---|
| forbidden_scope | The project id is not in the token's catalogue, or the data class is not allowed | Call list_projects and select an allowed project. Do not retry with the same id. |
| unknown_project | The project does not exist in this workspace | Call list_projects. The catalogue is authoritative. |
| invalid_cursor | A search cursor from another project or an expired page | Restart the search without a cursor. |
| protected_target | A proposal targets goals, policies, permissions or identities | Do not retry. Surface the change to the workspace owner instead. |
| duplicate_request | A replayed request id | Treat as success; the original result is returned. |
| stale_replica | The gateway replica is older than the freshness threshold | Proceed with caution and cite freshness, or wait for the next publish cycle. |
| unauthorized | The token is expired or revoked | Stop. A new token must be issued by the owner; there is no self-service refresh in the current setup. |
Provenance and freshness
freshness.compiled_at is when the consolidator last published the working truth. freshness.replica_at is when the gateway received it. If the two differ by more than the publish cadence, the bundle is marked stale rather than served as current.
Each fact carries provenance: the source reference, the date and the actor. Agents are expected to cite it when a decision depends on it, and reviewers use it to trace a served fact to its origin.
Revoke and deletion
Current tokens are revocable and time-limited by the owner. A revoked token receives unauthorized on every tool.
A connections view where the owner revokes an agent and confirms the old token no longer works.
Readable Markdown plus a provenance manifest; must open without Unforgetter.
Removal from all serving paths, indexes and, after the documented retention window, backups.
Clients
| Client | Current transport | Status |
|---|---|---|
| Claude | Remote MCP in web and Desktop, or local stdio proxy in Claude Code | Compatible |
| ChatGPT web | Custom remote MCP connection | Compatible |
| Codex | Local stdio proxy or remote MCP | Compatible |
| Cursor, VS Code and other MCP clients | Streamable HTTP or stdio proxy | Compatible |
| Grok Bot | Custom remote MCP connection over Streamable HTTP | Compatible |
| Codex Cloud session hook | Prefetch a scoped snapshot in environment setup; open evaluation task | In build |
Tokens and endpoints are issued during private-alpha onboarding. Apply and mention which clients you use.