# Four tools. Read, search, propose. Nothing writes directly.

MCP reference. 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.

- Transport: Streamable HTTP at /mcp
- Auth: scoped bearer token (guided), OAuth 2.1 (in build)
- Compatible with Claude, ChatGPT / Codex, Cursor and Grok Bot

Canonical: https://unforgetter.com/docs/mcp

## Authority comes from the token, never from the request.

- **Workspace · Bound to the token**: Every call is evaluated inside one workspace. There is no parameter to name another.
- **Actor and trust zone · Bound to the token**: 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.
- **Project catalogue · Filtered by the token**: list_projects returns only allowed projects. Selecting one in later calls cannot widen authority; an unlisted project id is refused.
- **Data class · Enforced on the server**: Restricted material is absent from the catalogue and refused on direct retrieval, search, cursor replay and as a proposal target.

## The tool surface.

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 · returns the projects this token may select (Working now)

```text
list_projects()
→ { projects: [ { id, name, data_class, updated_at } ] }

Only allowed projects are returned. Restricted projects are not listed.
```

get_context · returns the compiled bundle for a task in one project (Working now)

```text
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 · scoped deeper retrieval (Working now)

```text
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 · the only write path (Working now)

```text
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.
```

## What happens after propose_memory returns.

1. **Queued or quarantined.** 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.
2. **Delivered once.** The consolidator pulls pending proposals from a watermark. Retries are safe; a replayed request id is acknowledged without creating a duplicate.
3. **Validated.** Schema, scope, protected targets, conflicts with newer decisions and provenance are checked. Protected targets and concrete injection signals are rejected.
4. **Published or 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 are specific so agents can recover without guessing.

| 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. |

## Every bundle says how current it is and where each line came from.

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.

## Status of the controls that end access.

- **Token revocation** (Working now): Current tokens are revocable and time-limited by the owner. A revoked token receives unauthorized on every tool.
- **Self-service revoke** (In build): A connections view where the owner revokes an agent and confirms the old token no longer works.
- **Workspace export** (In build): Readable Markdown plus a provenance manifest; must open without Unforgetter.
- **Workspace deletion** (In build): Removal from all serving paths, indexes and, after the documented retention window, backups.

## Per-client notes.

| 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 |

> In build. Copy-as-Markdown and copy-to-agent actions for these docs, and per-client quickstarts, are added as each client is verified end to end.

## Connect a supported client.

Tokens and endpoints are issued during private-alpha onboarding. Apply and mention which clients you use.

- [Join the private alpha](https://unforgetter.com/private-alpha)

## Next

- [Documentation](https://unforgetter.com/docs)
- [Security and ownership](https://unforgetter.com/security)
- [How it works](https://unforgetter.com/how-it-works)

---
Status: Private alpha · guided setup. Contact: hello@unforgetter.com.
