Metadata-Version: 2.5
Name: agent-cabinet
Version: 0.1.0
Summary: Give your agent a filing cabinet.
License: Apache-2.0
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: jsonschema>=4.20
Requires-Dist: pyyaml>=6.0
Provides-Extra: cli
Requires-Dist: rich>=13.0; extra == 'cli'
Requires-Dist: typer>=0.12; extra == 'cli'
Provides-Extra: mcp
Requires-Dist: mcp>=2.0.0; extra == 'mcp'
Description-Content-Type: text/markdown

# agent-cabinet

<p>
  <a href="https://github.com/Intelligible/agent-cabinet/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Intelligible/agent-cabinet/actions/workflows/ci.yml/badge.svg"></a>
  <a href="https://pypi.org/project/agent-cabinet/"><img alt="PyPI" src="https://img.shields.io/pypi/v/agent-cabinet.svg"></a>
  <a href="https://pypi.org/project/agent-cabinet/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/agent-cabinet.svg"></a>
  <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-blue.svg"></a>
</p>

**Give your agent a filing cabinet.**

An agent's memory shouldn't be a black box. `agent-cabinet` stores what an
agent files away as plain Markdown files with a YAML frontmatter block —
readable, `grep`-able, git-diffable, editable by hand — and makes them
searchable by meaning, not just exact words.

Every entry is valid [OpenReasoningComponents](https://github.com/Intelligible/openreasoningcomponents)
(ORC) by default, with no ceremony required to get there: the cabinet fills
in the mechanical parts (a namespaced id, a default type, a scope) so a bare
note and a fully-evidenced, machine-verified claim are the same file format,
just with more filled in.

## 60 seconds

```bash
pip install agent-cabinet[cli]
agent-cabinet init
agent-cabinet remember "The user prefers dark mode." --kind preference --basis stated
agent-cabinet recall "theme preference"
```

Or from Python:

```python
from agent_cabinet import Cabinet

cabinet = Cabinet("./.cabinet", namespace="acme-project")
entry_id = cabinet.remember(
    "User said the DB is Postgres, not MySQL.",
    basis="stated",  # honest and cheap: where this came from, not a confidence score
)
cabinet.recall("database")[0].body
# 'User said the DB is Postgres, not MySQL.'
```

Serve it over MCP:

```bash
pip install agent-cabinet[mcp]
agent-cabinet mcp
```

That prints `agent-cabinet MCP server ready at http://localhost:7879/mcp`.
Point your agent at it (exact steps for each are just below), and it now has
somewhere to file things away that you can read, too.

## Connecting Clients

<details>
<summary><strong>Claude Code</strong></summary>

```bash
claude mcp add --transport http agent-cabinet http://localhost:7879/mcp
claude mcp list
```

</details>

<details>
<summary><strong>Cursor</strong></summary>

Add a project file at `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "agent-cabinet": {
      "url": "http://localhost:7879/mcp"
    }
  }
}
```

</details>

<details>
<summary><strong>Codex CLI</strong></summary>

```bash
codex mcp add agent-cabinet --url http://localhost:7879/mcp
```

</details>

## A note can become a verified fact

`remember()` writes a plain note. `file()` writes an already-verified ORC
component — typically from a pipeline, not a person. Both land in the same
file format, so a note can be promoted in place later, same id, once
something actually checks it.

<details>
<summary>See a note get promoted, one field at a time</summary>

```yaml
# remember("The DB is Postgres, not MySQL.")
---
id: acme-project/the_db_is_postgres_not_mysql
type: domain_knowledge
scope: { source: acme-project }
---
The DB is Postgres, not MySQL.
```

```yaml
# remember(..., basis="stated", links=["db-migration-plan"])
---
id: acme-project/the_db_is_postgres_not_mysql
type: domain_knowledge
scope: { source: acme-project }
basis: stated
links: [db-migration-plan]
---
The DB is Postgres, not MySQL.
```

```yaml
# file(component, overwrite=True) -- promoted later, same id, by
# something that actually verified it
---
id: acme-project/the_db_is_postgres_not_mysql
type: domain_knowledge
scope: { source: warehouse-connection-check }
evidence: { confirmed_by: "SELECT version() -> PostgreSQL 16.2" }
provenance: { derivation: check_db_dialect, derivation_version: a1b2c3 }
---
The DB is Postgres 16.2, confirmed by direct connection.
```

</details>

<details>
<summary><strong>How search works</strong></summary>

`recall()` is a rebuild-every-call BM25 pass over the cabinet — the right
default for the dozens-to-low-hundreds of entries a typical cabinet holds.
No index file, no server, nothing that can drift out of sync with the
Markdown files, which stay the sole source of truth.

A persisted, incremental index — dense/semantic retrieval alongside the
lexical half — is a real upgrade once a cabinet grows into the thousands
of entries. It isn't built yet; when it lands, it'll be a disposable cache
derived from these same files, never a second copy of them.

</details>

<details>
<summary><strong>What this doesn't do</strong></summary>

- **No multi-user access control.** One writer, one directory — a person,
  or a small team sharing a git repo. Grants, SSO, and per-entry sharing
  are an enterprise concern for a separate product to layer on top.
- **No confidence scores.** `basis` (`stated` / `inferred` / `observed` /
  `assumed`) asks for something an agent actually knows — where a belief
  came from — not a calibrated probability it would have to guess at.

</details>

## License

Apache-2.0.
