Give your agent a memory of its own
A hands-on ENGRAM walkthrough · August 8, 2026
1. Why this tutorial exists
An assistant connected to ENGRAM with your access token remembers things into your memory. That is fine
for a while. Then it starts filing its own working notes — half-finished findings, environment quirks,
"the build needs --no-cache on this machine" — alongside the decisions you meant to
keep, and your memory stops being yours.
Giving the agent its own identity fixes that, and buys something better: two memories that share deliberately instead of one that blurs. The agent accumulates what it learns, you keep what you decided, and anything crossing between them is an explicit act you can see and revoke.
There is also a practical reason to do this carefully. An agent writing to memory faces three questions a person never encounters, and each fails quietly — the write succeeds, returns an id, and the memory is simply never seen again:
- Whose memory am I writing to?
- Which owner ring does this belong in?
- Did the round trip actually work?
This tutorial answers all three, in the order they bite.
Prior tutorials: Tutorial 2 (sub-agents and PATs), Tutorial 4 (a harness and its team). This one stands alone, but assumes you know what an MCP server entry is.
2. What you'll build
you (human) your memory — decisions, preferences, articles
└── your-agent (agent identity) its own memory — findings, conventions, environment
│
└── one deliberate share, agent → you
Two identities, two memories, one explicit grant between them.
3. Prerequisites
| Requirement | Notes |
|---|---|
| ENGRAM v1.14.0+ | earlier builds accept a mis-scoped memory silently instead of rejecting it — you would hit Part 3's trap without the error that explains it |
| Admin or AgentManager role | to create the agent identity and issue its token |
| An MCP client | Claude Desktop, Claude Code, or any MCP host |
| Sharing enabled | for Part 5 only |
Part 1 — Give the agent an identity
In Administration → Users → Agents, create the agent. Give it a name you will
recognise in a memory list months from now — code-prod, research-assistant — not
agent1.
Then issue it a personal access token. The raw key is shown once; copy it straight into the agent's configuration.
Finally, add a second MCP server entry in your client, pointing at the same ENGRAM instance but carrying the agent's token. You now have two entries that look identical apart from the credential — and that credential is the whole difference.
Verify before you write
Do not assume the new entry is the agent. Ask it something own-scoped:
list_projects()
Through the agent's entry this returns the agent's own inventory — empty, on a new identity. Through yours it returns your projects. If you see your work through what you believe is the agent's connection, the token is not the one you think it is, and every memory it writes will land in your store.
Part 2 — Prove the loop before you trust it
The most valuable thing you can do on a new connection is prove the round trip once. It costs one extra call and catches every failure in this tutorial while they are cheap to fix, rather than a session later when the memory is needed and missing.
remember(
content="On this project the integration suite must run against production-mirror; "
"staging masks the timing bug we spent a week on.",
owner_scope="user",
)
recall(query="which environment should the integration suite run against?")
You are looking for one specific thing in the result: via: "own".
via | meaning |
|---|---|
own | your own memory — what you just wrote |
shared | a teammate granted it to you |
public | shared knowledge in the instance |
If the memory you just wrote does not come back as own, it is not missing — it is
unreachable, which is Part 3.
Part 3 — The ring you have to choose
Every memory sits in an owner ring: how widely its author can reach it later. The rings nest — task ⊂ context ⊂ project ⊂ user.
A narrower ring is not more private. It is more situational: a
context-scoped memory is recallable only while you are working in that same context.
| Ring | Recallable | Use it for |
|---|---|---|
user | anywhere you work | durable knowledge you want in a later session |
project | while you declare that project | a finding that belongs to one piece of work |
context | while you declare that conversation or session | working notes tied to one session |
task | while you declare that task | scratch for a single unit of work |
Here is the part that surprises people. An agent's long-term memory defaults to
context, not user. That is deliberate — many agent instances run at once, and
their scratch should not all pile into one shared ring. But it means a bare remember is
session-local by default, which is rarely what you want from an agent you are teaching.
Try it and watch the guard fire:
remember(content="anything")
# → 400: cannot imprint a 'context'-scoped memory without context_id …
# Written without one it would be unreachable by every caller including you.
# Either pass the anchor, or pass owner_scope='user' for knowledge you want
# to recall anywhere.
That error exists because the failure it replaces was silent. A
context-scoped memory with no context has no context for anyone to declare — including its
author — so it was written, reported as saved, and never seen again. ENGRAM now refuses rather than storing
something unreachable.
So say what you mean:
remember(content="...", owner_scope="user") # recall in any future session
remember(content="...", context_id="<session>") # bound to this session
user.
Part 4 — What is actually worth remembering
An agent with memory tends to over-record. The useful test: would this otherwise be re-derived?
Worth keeping — a decision and why, a constraint that shaped the work, a measurement that overturned an assumption, a problem and its resolution.
Not worth keeping — anything recomputable from the code or git history, ephemeral state, unconfirmed speculation, and anything already saved as an article (articles generate their own memories; remembering their contents again just duplicates them).
Two habits that pay off later:
- One fact per memory. Retrieval surfaces memories individually; a paragraph holding four facts surfaces for the wrong one.
- Write so it reads cold. Months later there is no surrounding conversation. "Use production-mirror" is useless; "the integration suite must run against production-mirror — staging masks the timing bug" survives.
Long-form reference material belongs in the Knowledge Base as an article. Memory is for the atomic facts you want retrieval to surface.
Part 5 — Hand something back
The agent has its own memory now, which makes passing something to you a deliberate act:
# as the agent
share_memory(selector_kind="memory", selector_id="<memory id>",
grantee="you@example.com", bound_scope="user")
Recall it from your own identity and it arrives with via: "shared", attributed to the agent.
It stays the agent's memory — sharing passes recall, not authorship — and you can revoke it at any
time from Memory Explorer → Sharing.
For anything long-form, prefer an article: the agent writes it to the KB and the article's own memory becomes recallable to whoever can see it. That keeps reference material in one place instead of scattering it across memories.
6. What this gives you
Does: a memory per identity, so neither pollutes the other · attribution that survives sharing · deliberate, revocable hand-off · an agent whose knowledge persists across sessions.
Doesn't: make the agent's memory readable by you automatically. That is the point — crossing between identities is always an explicit grant.
Appendix — Gotchas, ranked by how much time they'll cost you
- Verify the identity before the first write. A wrong token means every memory lands in the wrong store, and it is tedious to unpick afterwards.
- An agent's default ring is
context. Passowner_scope="user"for anything you want in a later session. via: "own"is the check that matters. A write that returns an id has not proved it is reachable.- Don't duplicate articles into memories. Articles imprint their own.
- One fact per memory, written to read cold.
- Grantee identity is a
user_idor email, never a display name. - The raw token is shown once. If it is lost, rotate rather than hunt for it.