session_id is special: it groups pushes into one source. It’s not a
filterable key — the ones you declare are.)
Now Sara messages again. You want her history — not Tom’s. Two steps.
Step 1: declare the key (once)
email field matters — index it.” Everything already
in the vault is indexed in the background (seconds for typical vaults), and
every future push indexes on arrival.
Where keys can live: the registry is an agent/team-vault feature. Agent
vaults declare via the SDK (above) or the vault’s Settings; team vaults via
Settings → Filterable metadata in the app. The personal vault has no
registry — metadata still rides on personal pushes (like session_id),
it just isn’t filterable there.
Declare exact-value keys you’ll filter by: email, customer_id,
ticket, order_id, repo. Don’t declare topical labels (topic: infra) — grouping by theme is what tags are for. Up to 16
keys per vault; values must be text/numbers/booleans (or arrays of them);
reserved keys like session_id are refused.
Step 2: filter your search
Call this rule match-or-absent: a source without the key is never
excluded. The policy note isn’t about Sara, but it isn’t about anyone
else either — so it stays available when answering her. Only data that
contradicts the filter is dropped. (Case doesn’t matter; lists work on
both sides; multiple keys combine.)
Adding metadata later
Metadata usually rides in at push time — but you can attach or fix it on an existing source too:Filtering examples
One person’s context (their data + everything unlabeled, never anyone else’s):Tags or metadata keys?
One question decides it: should a source that says nothing about this still show up? Yes → metadata key (forgiving, match-or-absent). No, only my curated set → tag (hard scope).Scoped pull_context
MCP agents get the same filter onpull_context — for team scopes,
since that’s where MCP-reachable declared keys live (teams declare them in
the team’s Settings; the personal vault has no registry):
- Single vault. Filters apply to one vault per call (a named team on
MCP, the addressed vault on the SDK); shared context isn’t searched while
a metadata filter is active — the
coverage_notesays so. - Typos fail loud. Filtering on an undeclared key is a 400 on the SDK
(listing the declared keys); on MCP it’s
items: []with the reason incoverage_note— read it before concluding “no memory”. - Combines with tags:
filters={"tags": ["launch"], "meta": {"email": ...}}= your curated set, minus anyone else’s data.
Best practices
- Canonicalize values before pushing. Matching is exact (trimmed,
case-insensitive) —
ORD-123never matches123. - Stamp the key consistently if exclusion matters: keyless sources
always stay in scope, which is a feature for shared notes and a foot-gun
for data you forgot to label.
update_source_metafixes stragglers. - Put metadata on the first push of a session. Later pushes in the
same
session_idextend the content, not the metadata. - Use arrays for multi-party sources —
"email": ["a@…", "b@…"]matches either. - Deleting a key is a light switch, not a shredder. Removing a declared key only drops the index; the metadata stays on every source, and re-declaring rebuilds it identically.
- Need only-matching results, keyless excluded too? That’s not a metadata filter — that’s a curated set. Use a tag.
- Storing names or emails in the content itself, not just metadata? See PII scrubbing to strip that at ingest.