# Artifacts
Source: https://docs.klio.tech/concepts/artifacts
Files, documents, and JSON records shared across the org, discovered through the same recall an agent already calls.
Memory is good at facts, decisions, and plans — short, durable statements. It
is the wrong shape for a design doc, a dataset, or a PDF. Artifacts are Klio's
answer to that: a place to put the bytes, wired into the same discovery path
as everything else.
An artifact is a document, a JSON record, or a file, stored under the org and
made discoverable to every agent in it — the same tenancy boundary as memory,
described in [Scope](/concepts/scope).
## Discovery: a card, then the bytes
This is the part worth understanding before the tool parameters.
Storing an artifact does two things, not one. It stores the bytes, and it
writes a short **card** into the same shared memory that [`recall`](/tools/recall)
already searches — a sentence or two describing what the artifact is, tagged
`memory_type: "artifact"` with the artifact's id in `metadata.artifact_id`.
That means an agent that has never heard of the artifact finds it the
ordinary way: it calls `recall` about something it needs, the card surfaces
like any other memory, and the agent reads `metadata.artifact_id` off the
result and calls [`artifact_get`](/tools/artifact-get) to fetch the contents.
No separate artifact index, no second tool to remember to call.
```json theme={null}
{
"title": "Q3 pricing analysis",
"summary": "Competitor pricing comparison and margin model for the Q3 review",
"kind": "document",
"content": "# Q3 Pricing Analysis\n\n..."
}
```
Klio writes the artifact and, alongside it, a card:
```text theme={null}
[artifact] Q3 pricing analysis — Competitor pricing comparison and margin
model for the Q3 review (document, 4.1 KB)
```
```json theme={null}
{ "query": "did anyone look at competitor pricing for Q3?" }
```
`recall` returns the card like any other memory — same relevance ranking,
same scope rules — with `metadata.artifact_id` pointing at the full
document.
```json theme={null}
{ "artifact_id": "art_01J...", "mode": "text" }
```
Returns the text, paginated. See [`artifact_get`](/tools/artifact-get) for
paging and the binary path.
The card is deliberately terse — enough for recall's ranking and for a human
or agent to decide whether it is worth opening. The full content lives
behind `artifact_get`, not in the card itself.
## Storing one
[`artifact_put`](/tools/artifact-put) has three behaviours, selected by which
arguments you pass:
| Path | When | Calls |
| -------------------- | --------------------------------- | ------------------------------------------------------- |
| **Inline** | Text up to 64KB | One call, with `content` |
| **Presigned upload** | Larger or binary content | Two calls: request, then upload the bytes, then confirm |
| **Confirm** | Second half of the presigned path | One call, passing back the `artifact_id` |
Inline is the only write path available to clients that speak MCP but cannot
make an arbitrary HTTP request — ChatGPT connectors and Claude.ai custom
connectors both fall in this category. If your integration is one of those,
your artifacts go through `content`, up to 64KB.
Confirm is not a formality. The server re-measures the uploaded object
itself and enforces the plan's per-file and pool limits against that
measured size, not whatever the client declared when requesting the
upload URL.
## Reading one
[`artifact_get`](/tools/artifact-get) has two modes:
* **`mode: "text"`** *(default)* — the contents, one page at a time. Binary
artifacts return an error pointing at `mode: "url"` instead — Klio does no
text extraction from PDFs or Office documents in this version.
* **`mode: "url"`** — a presigned download link, valid for 15 minutes,
forced to download rather than render inline.
## Retracting one
[`forget`](/tools/forget) accepts an artifact id as well as a memory id. It
retracts the artifact and hides its card in the same call — recall stops
surfacing it immediately.
## Storage limits
Artifacts are available on every plan; what a plan buys is capacity, not
access. Limits are per file and for the org's total pool:
| Plan | Per file | Total pool |
| ------------------ | -------- | ---------- |
| Free | 10 MB | 100 MB |
| Starter, Pro, Team | 50 MB | 5 GB |
| Platform | 1 GB | 1 TB |
A single upload is capped at 100 MB regardless of plan — multipart upload is
not available yet. Talk to us if you need to store artifacts larger than
that.
Once the pool is full, writes are rejected until something is forgotten,
superseded, or the plan is upgraded. Reads always keep working — a full
pool never blocks `artifact_get` or `recall`.
## Retention
Ordinary memories age out under the plan's retention window. Artifacts do
not — an artifact lives until it is forgotten, superseded by a newer version,
or the org's storage pool fills. There is no time-based expiry on artifact
content.
## What this version does not do
Artifacts are managed through the MCP tools only. There is nowhere in
app.klio.tech to browse, upload, or delete one directly.
The `klio` CLI does not have an artifact command in this version.
Binary formats — PDF, Word, Excel, images — are stored and downloadable,
but nothing extracts their text for `mode: "text"` or for recall. Only the
card is searchable; the binary content itself is not.
# The handover loop
Source: https://docs.klio.tech/concepts/handover-loop
Six movements that turn raw session activity into something the next agent can act on.
A shared text store is not a workplace. The difference is what happens between
one agent writing and the next one reading — six movements, of which the last
three are the ones a plain database will not do for you.
An agent connects over MCP and is resolved to an org, an agent identity, and
optionally a space. No vendor owns the door — Claude Code, Cursor, Codex and
your own agents all arrive the same way.
Work is recorded as it happens through `remember`, `decide`, `plan`,
`observe`, and `note`. Secrets and personal data are stripped **before**
anything is persisted, and that step fails closed.
Every write lands under an org, and optionally a space. The job's knowledge
stays with the job rather than in one undifferentiated pile. See
[Scope and spaces](/concepts/scope).
Raw activity is condensed into the few durable facts worth keeping. The next
agent should inherit conclusions, not transcripts — a thousand observations
are worth less than the one decision they led to.
When a newer memory contradicts an older one, the old one is superseded
rather than left to compete with it. This is the step almost nothing else
does, and it is why recall stays sharp instead of degrading into a pile of
mutually contradictory notes.
The next agent calls `recall` and receives current state. One writes; the
next to look already sees it.
## Why distil and retire matter
Keeping text is easy. Two things make stored context stop being useful over
time, and both are the store's job to handle:
An agent that logs every step produces thousands of low-value entries. Recall
over that returns noise, and the model spends its context budget reading
things that did not matter. Distilling means a session's worth of activity
collapses into the handful of facts that survive it.
Six months in, the store holds "we use fixed backoff" *and* "we use jittered
backoff." Both surface. The agent picks one — sometimes the wrong one — and
quietly undoes work. Superseding means the retired statement stops being
returned, so nobody acts on stale truth.
## What the loop does not do
Klio does not notify running agents. A write is available to the next agent
that **reads**, immediately — but an agent already mid-session will not learn
about it until it calls `recall` again. If you need two agents to coordinate
within a single turn, that is a job for your orchestration layer, not for
memory.
Nor does it decide what is worth keeping on your behalf. Agents write what they
are told to write. The prompt guidance in the [Quickstart](/quickstart#make-it-a-habit)
exists because a workplace where nobody writes anything down is just an empty
room.
# Scope and spaces
Source: https://docs.klio.tech/concepts/scope
Strong scope is the feature. Random global memory everywhere is the failure mode.
The most common objection to shared agent memory is the right one: memory that
leaks everywhere is worse than no memory. An agent working on your billing
service should not surface a note from an unrelated prototype, and one team's
context should not appear in another's.
Klio answers that with three separate axes.
## The three axes
The tenant boundary, resolved from your API key. Nothing crosses it. Every
read and write is confined to the org the key belongs to.
Who wrote it, from the `X-Vex-Agent` header. Used for attribution, and for
private memory when you ask for it.
A named subdivision within the org — one per project, service, or workstream.
Managed with the [`space`](/tools/space) tool.
## The visibility ladder
Where a memory lands depends on **how it was written**, not on a parameter you
have to remember.
| `scope` | Who can recall it | What lands there |
| ---------------------- | ---------------------------------------- | ----------------------------------------------------------- |
| `private` | **Only you.** Not teammates. Not admins. | Everything your editor captures passively |
| `project` | Members of that project, and org admins | Explicit writes made while a project is set |
| `org` | Everyone in the org | Explicit writes — `remember`, `decide`, `plan`, `note` |
| `agent` *(deprecated)* | The agent identity that wrote it | Superseded by `private`. Still readable; no longer written. |
### Capture is private, sharing is deliberate
Passive capture — the hooks that record what your agent did — lands in **your**
private scope. Nobody else can read it, including an organisation administrator.
Calling a write tool is the act of sharing. `remember`, `decide`, `plan` and
`note` write to the team, or to a project when one is set.
```json theme={null}
{ "content": "The staging deploy needs the VPN on" }
```
That goes to the whole org, because you chose to write it.
### Recall spans everything you are entitled to
A normal `recall` searches your private memories, the projects you belong to,
and the org's shared brain — in one call. Ask for `scope: "private"` to narrow
to your own.
### Promoting something you captured
When a passive capture turns out to be worth sharing, promote it:
```json theme={null}
{ "memory_id": "60a0fa87-…", "to": "org" }
```
`share` moves it from your private scope to the team's, and keeps you recorded
as the author. Only the owner of a memory can share it.
**Private means private.** There is no administrator override, no support
path and no break-glass. An organisation administrator sees every org and
project memory and a single org-wide storage total — never the contents of
anyone's private scope, and never a per-person count of it.
**`agent` scope is not a privacy boundary** and never was. It resolves
against the `X-Vex-Agent` header, which the client sends and can set to any
value. It is deprecated in favour of `private`, which is enforced against
your verified credential. Existing `agent`-scoped memories remain readable;
nothing new is written there.
## Spaces
A space is a named store within the org. Use one per project so a question about
the billing service does not pull answers from the marketing site.
```json theme={null}
{ "action": "create", "name": "Billing service", "slug": "billing" }
```
Then pass the slug on reads and writes:
```json theme={null}
{ "content": "Invoices are generated nightly at 02:00 UTC", "space": "billing" }
```
```json theme={null}
{ "query": "when do invoices generate?", "space": "billing" }
```
Omit `space` and you are working against the org's default store. See the
[`space` tool](/tools/space) for `list`, `create`, `info`, and `switch`.
## Narrowing a recall
[`recall`](/tools/recall) also accepts a `project` filter, which narrows results
without needing a separate space:
```json theme={null}
{ "query": "auth error handling", "project": "api", "limit": 5 }
```
Clients can additionally declare a workspace-wide project through the
`X-Vex-Project` header. A project is identified by whichever of these it has:
a git remote URL when the workspace is a git repo, or the repository root
directory path when it is not.
Both resolve. Earlier, only a git remote or a project's display name looked
itself up correctly — a non-git project fell back silently to org-wide
recall, with nothing to indicate why its memories were not showing up
filtered. Directory-path projects now resolve the same way git-remote ones
always did.
Filtering by project is additive, not subtractive: recall fills the result set
with that project's memories first, then — if the limit has not been reached —
fills the remaining slots with unfiled memories (nothing was ever explicitly
attached to a project). Memories filed under a *different* project never come
back. There is no third pool for "everything, ranked, regardless of project."
Project resolution **fails open**. If the header is missing, the identifier is
unknown, or the lookup errors, the request falls back to org-wide rather than
returning nothing. That keeps a misconfigured client working, but it means the
header is a convenience — never a security boundary. The boundary is the org.
This is the same story as [spaces](/tools/space#spaces-are-not-a-tenancy-boundary):
narrowing by project is a relevance filter. Anyone holding the org's API key
can still recall any project's memories by asking for them, or by omitting
the filter entirely.
## Choosing a layout
One org, one space per repo. Set `X-Vex-Project` per workspace, or pass
`space` explicitly. Skip `agent` scope entirely — with one person, shared is
the point.
One org, spaces per service or bounded context. Set `X-Vex-Agent` per tool
so you can see whether a decision came from Claude Code or Cursor, and who
was driving.
One org per client, not one space per client. Spaces subdivide; they are not
a tenancy boundary. If two bodies of work must never see each other, they
need separate orgs and separate keys.
# What Klio keeps
Source: https://docs.klio.tech/concepts/what-klio-keeps
What belongs in a shared workplace, what is stripped before it is written, and what is retired once it stops being true.
A memory layer is defined as much by what it refuses to keep as by what it
stores. This page is the honest version of both.
## Keep vs. never keep
* **Decisions** — what was chosen, and why
* **Repo rules** — conventions the work must hold to
* **Plans** — the shape of work in flight
* **Durable facts** about the project or the people on it
* **Secrets** — keys, tokens, credentials
* **Personal data** — redacted before write
* **Transcripts, tool calls, file contents**
* **Anything a newer decision has retired**
The test for whether something belongs: *would the next agent be worse off
without it?* A decision passes. A log line of a file being read does not.
## Redaction fails closed
Secrets and personal data are stripped **before** anything is persisted — not
on the way out. If content cannot be made safe, the write is rejected rather
than stored in a degraded form.
This is a backstop, not a licence. Redaction is pattern-based, and no
pattern set catches everything. Do not deliberately route credentials or
customer PII through memory tools on the assumption that they will be caught.
Prompt your agents not to send them in the first place.
## Superseding
When a newer memory contradicts an older one, the older one is superseded and
stops surfacing in recall. The chain is retained — the history of what was
believed when is not deleted — but only current truth comes back from a query.
This is what keeps recall sharp over months. Without it, a store accumulates
contradictions and the agent picks between them arbitrarily.
To retract something explicitly rather than waiting for it to be contradicted,
use [`forget`](/tools/forget) with the memory id from a prior recall.
## Deduplication
Near-identical writes are collapsed rather than stored twice. Agents are
repetitive by nature — several of them working the same project will write the
same conclusion in slightly different words, and without dedup the store fills
with paraphrases of a single fact.
Dedup is similarity-based, so it is a judgement call rather than an exact
match. Two genuinely distinct facts phrased almost identically can be
collapsed. If you need both, make the wording meaningfully different or scope
them to different [spaces](/concepts/scope).
## Where it physically lives
Memory lives on Klio's infrastructure, encrypted in transit and at rest at
the infrastructure level, isolated per org. Secrets and PII are redacted
before storage.
The encryption keys are ours, and Cloud writes are not hash-chained. If your
threat model requires keys you hold, self-host.
Memory lives in your own Postgres, on your own hardware. It is encrypted at
rest under a key **you** own, and every write is chained with SHA-256 so the
history is tamper-evident and independently inspectable.
Nothing leaves your estate. See [Self-hosting](/self-host/install).
The user-held key and the hash chain are properties of the **self-hosted**
engine specifically. Anywhere you see those claims, they refer to self-hosting
— Klio Cloud makes the weaker guarantees described above.
# Any MCP client
Source: https://docs.klio.tech/connect/any-mcp-client
Klio is a standard Streamable HTTP MCP server — anything that speaks the protocol can join.
There is no Klio SDK to install. If your agent speaks MCP, it can use the
workplace, and that is deliberate: a workplace that only admitted one vendor's
agents would not be a workplace.
## Connection details
| | |
| ------------- | ------------------------------------------------------------------------------------------------------------ |
| **Endpoint** | `https://mcp.klio.tech/mcp` |
| **Transport** | Streamable HTTP |
| **Auth** | `X-Vex-Key` header, or an OAuth bearer token |
| **Identity** | `X-Vex-Agent` header (optional, recommended) |
| **Tools** | `recall`, `remember`, `observe`, `plan`, `decide`, `note`, `artifact_put`, `artifact_get`, `space`, `forget` |
## Generic configuration
Most clients accept a variant of this shape:
```json theme={null}
{
"mcpServers": {
"klio": {
"url": "https://mcp.klio.tech/mcp",
"headers": {
"X-Vex-Key": "YOUR_KEY",
"X-Vex-Agent": "your-agent-name"
}
}
}
}
```
Give every distinct client its own `X-Vex-Agent` value — `codex`, `ci-bot`,
`review-agent`. That string is what makes "which agent decided this?"
answerable later.
## Codex
Add to `~/.codex/config.toml`:
```toml theme={null}
[mcp_servers.klio]
url = "https://mcp.klio.tech/mcp"
[mcp_servers.klio.headers]
"X-Vex-Key" = "YOUR_KEY"
"X-Vex-Agent" = "codex"
```
## Building your own agent
Point any MCP client library at the endpoint and pass the headers. The tools
arrive through the standard `tools/list` handshake with full JSON schemas — you
do not need to hardcode their shapes.
```python theme={null}
# Pseudocode — use whichever MCP client library your stack provides.
client = MCPClient(
url="https://mcp.klio.tech/mcp",
headers={
"X-Vex-Key": os.environ["KLIO_API_KEY"],
"X-Vex-Agent": "my-research-agent",
},
)
hits = await client.call("recall", {"query": "what did we decide about retries?"})
await client.call("decide", {
"content": "Cache embeddings for 24h",
"rationale": "Re-embedding the same documents was 60% of our token spend",
})
```
## Verifying the endpoint
A bare request without a valid key returns `401`. That is the healthy response —
it confirms the endpoint is live and enforcing auth:
```bash theme={null}
curl -i https://mcp.klio.tech/mcp
```
## Self-hosted
Running your own engine? The endpoint is the same path on your own host, and
everything else is unchanged:
```
http://localhost:8080/mcp
```
See [Self-hosting](/self-host/install) for the exact port your install exposes.
## Notes
The hosted server accepts OAuth bearer tokens for clients that support the
MCP authorization flow. The agent identity then defaults to the OAuth
`client_id`, unless `X-Vex-Agent` overrides it.
`X-AgentGuard-Key` is still accepted as an alias for `X-Vex-Key`, so older
clients keep working. Do not use it in new integrations.
# Claude Code
Source: https://docs.klio.tech/connect/claude-code
Connect Claude Code to a shared Klio workplace.
## Add the server
```bash theme={null}
claude mcp add --transport http klio https://mcp.klio.tech/mcp \
--header "X-Klio-Key: YOUR_KEY" \
--header "X-Klio-Agent: claude-code"
```
Then confirm it registered:
```bash theme={null}
claude mcp list
```
You should see `klio` with ten tools: `recall`, `remember`, `observe`,
`plan`, `decide`, `note`, `artifact_put`, `artifact_get`, `space`, `forget`.
`X-Vex-Agent: claude-code` is what makes attribution work later — it is how
you tell which agent left a given decision. Pick a stable string and keep it
consistent across machines.
## Make recall reflexive
Claude Code will use the tools when it judges them relevant, but memory pays off
when reading is automatic. Add this to `CLAUDE.md` at your repo root:
```md theme={null}
## Shared memory (Klio)
Before answering questions about this project, call `recall` first — another
agent may have already decided it.
Persist durable knowledge as it arises, without being asked:
- a decision is made → `decide` with its rationale
- a plan is set → `plan`
- a durable fact or preference → `remember`
Do not persist secrets, tool call logs, or file contents.
```
Repo-level `CLAUDE.md` is the right home for this — it travels with the
project, so every teammate's Claude Code picks up the same habit.
## Scope it to the repo
If you run several projects, give each one its own
[space](/concepts/scope) so a question about one does not pull answers from
another:
```bash theme={null}
claude mcp add --transport http klio https://mcp.klio.tech/mcp \
--header "X-Klio-Key: YOUR_KEY" \
--header "X-Klio-Agent: claude-code" \
--header "X-Vex-Project: git@github.com:acme/billing.git"
```
`X-Vex-Project` accepts a git remote URL. If the repo has no remote configured,
pass the repository's root directory path instead — Klio resolves both the
same way.
Project resolution fails open — an unrecognised value falls back to org-wide
rather than erroring. It is a relevance filter, not a security boundary.
## Local install
If you are self-hosting, `klio init` wires Claude Code for you rather than you
running `claude mcp add` by hand:
```bash theme={null}
npx @klio-tech/klio init --local
```
See [Self-hosting](/self-host/install).
## Troubleshooting
Run `claude mcp list`. If `klio` is absent the server was never added; if it
is present but failing, the key is usually wrong — a bad key returns `401`.
Most often the write went somewhere else. Check that both agents use the
same key (same org), and that you are not writing with `space` set but
reading without it.
Without instruction, a model treats tools as optional. Add the `CLAUDE.md`
block above — an explicit "call recall first" changes behaviour immediately.
# Cursor
Source: https://docs.klio.tech/connect/cursor
Connect Cursor to the same shared workplace your other agents use.
## Add the server
Create `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` for every
project):
```json theme={null}
{
"mcpServers": {
"klio": {
"url": "https://mcp.klio.tech/mcp",
"headers": {
"X-Vex-Key": "YOUR_KEY",
"X-Vex-Agent": "cursor"
}
}
}
}
```
Reload Cursor, then check **Settings → MCP** — `klio` should be listed with
ten tools.
A project-level `.cursor/mcp.json` contains your API key. Add it to
`.gitignore`, or keep the config in `~/.cursor/mcp.json` instead so the key
never sits in the repo.
## Set `X-Vex-Agent` differently per tool
Use `cursor` here and `claude-code` in Claude Code. That difference is the whole
point of the header — it lets you see, later, which agent left which decision,
and it makes [`scope: "agent"`](/concepts/scope) meaningful.
## Make recall reflexive
Add a rule in `.cursor/rules/klio.mdc`:
```md theme={null}
---
description: Shared memory across agents
alwaysApply: true
---
Before answering questions about this project, call `recall` first — another
agent may have already decided it.
When a decision is made, call `decide` with its rationale.
When a plan is set, call `plan`.
When the user states a durable fact or preference, call `remember`.
Do not persist secrets, tool call logs, or file contents.
```
## Proving it works with Claude Code
The point of a shared workplace is that it does not matter which agent shows up.
Worth testing once:
Have Claude Code record a decision with `decide`. Then start a fresh Cursor
session and ask the same question in your own words.
It calls `recall`, finds the decision Claude Code wrote, and answers —
including *why*. You never re-explained it.
If that works, both agents are on the same org and the handover is live.
## Troubleshooting
Almost always the key. Cursor surfaces little detail, so test the endpoint
directly — a wrong or revoked key returns `401`:
```bash theme={null}
curl -i https://mcp.klio.tech/mcp -H "X-Klio-Key: YOUR_KEY"
```
Different keys mean different orgs, and orgs never share. Confirm both use
the same key — and that one is not passing a `space` the other omits.
# What is Klio?
Source: https://docs.klio.tech/index
A shared workplace for AI agents. One agent finishes and sets down what it decided; the next one picks it up and keeps going.
Your agents don't forget because their memories are bad. They forget because
each one keeps a private notebook.
Every vendor built memory for its own agent, inside its own product. Your Claude
Code context stays in Claude Code. Your editor's context stays in your editor.
No vendor has a reason to hand your work to a competitor's agent — so you end up
being the integration layer, re-explaining the same decisions to every tool.
**Klio is the workplace those agents share.** It is vendor-neutral and speaks
MCP, so any agent can clock in, read what the last one left, do its work, and
set down what it decided for whoever comes next.
Claude Code, Cursor, Codex, or your own agent. Klio is not one of the model
vendors, so it has no reason to keep your context away from any of them.
Memory lives under an org and a space — not one global pile. Strong scope is
the feature; see [Scope and spaces](/concepts/scope).
When a newer decision contradicts an older one, the old one is superseded.
Anyone can keep text; almost nobody retires it.
The core engine is open source and self-hostable, so the workplace can live
on your own hardware. The layers that reason over it stay on Cloud. See
[Self-hosting](/self-host/install).
## The shape of it
An agent connects over MCP and gets ten verbs. In practice two of them carry
most of the value:
```text Before acting theme={null}
recall("how do we handle auth errors in the API?")
→ 3 memories: 1 decision, 1 repo rule, 1 plan
```
```text After deciding theme={null}
decide(
content: "Retry 429s with jittered backoff, cap at 5 attempts",
rationale: "Upstream rate-limits per minute; fixed backoff synchronised our retries"
)
```
The next agent to open the same project — whichever vendor it came from — calls
`recall` and sees that decision. Nothing is re-explained, and no work is paid
for twice.
Writes are available to the next agent that reads, immediately. Klio does not
push to agents that are already mid-session — an agent sees new state the next
time it calls `recall`.
## Get started
Connect your first agent to hosted Klio in about two minutes.
The six movements that turn raw activity into something the next agent can
actually use.
All ten MCP tools, with their exact parameters and return shapes.
Run the open-source engine on your own machine or infrastructure.
# Quickstart
Source: https://docs.klio.tech/quickstart
Connect your first agent to a shared workplace, and watch a second agent pick up where it left off.
By the end of this page one agent will have written a decision and a second
agent — from a different vendor — will have read it back. That handover is the
whole product; everything else is detail.
## Prerequisites
* An MCP-capable agent. [Claude Code](/connect/claude-code),
[Cursor](/connect/cursor), or [anything else that speaks MCP](/connect/any-mcp-client).
* A free Klio account at [app.klio.tech](https://app.klio.tech/auth/sign-up).
Prefer to run everything yourself? Skip this page and go to
[Self-hosting](/self-host/install) — one command brings up the engine locally
and wires your agents to it.
## Get connected
Sign in at [app.klio.tech](https://app.klio.tech), open **Settings → API
keys**, and create a key. It looks like `vex_live_…`.
The key is shown once. Store it in your password manager or an environment
variable — never commit it to a repository.
Klio's hosted endpoint speaks Streamable HTTP MCP:
```
https://mcp.klio.tech/mcp
```
Authenticate with the `X-Klio-Key` header (the legacy `X-Vex-Key` name still works). For Claude Code:
```bash theme={null}
claude mcp add --transport http klio https://mcp.klio.tech/mcp \
--header "X-Klio-Key: YOUR_KEY" \
--header "X-Klio-Agent: claude-code"
```
`X-Klio-Agent` is optional but recommended — it is what lets you see which
agent wrote what. Clients that omit it share a single default agent
identity. See [Authentication](/tools/authentication) for every header.
Ask your agent to list its tools, or run:
```bash theme={null}
claude mcp list
```
You should see ten Klio tools: `recall`, `remember`, `observe`, `plan`,
`decide`, `note`, `artifact_put`, `artifact_get`, `space`, and `forget`.
## Prove the handover
This is worth doing once, deliberately, so you can see the mechanism work.
In your connected agent, say:
> We're going to retry 429s with jittered backoff, capped at five attempts.
> Record that decision — the reason is that the upstream rate-limits per
> minute, and fixed backoff was synchronising our retries.
The agent should call `decide`. You can also call it directly:
```json theme={null}
{
"content": "Retry 429s with jittered backoff, cap at 5 attempts",
"rationale": "Upstream rate-limits per minute; fixed backoff synchronised our retries"
}
```
Open a *different* agent — Cursor if the first was Claude Code — connected
with the same key. Start a fresh session and ask:
> How do we handle rate limiting on the upstream API?
It calls `recall`, finds the decision, and answers with the rationale. You
never re-explained it.
If the second agent found the decision, your workplace is working. Every other
page here is about doing this well at scale — scoping it properly, keeping the
right things, and retiring what stops being true.
## Make it a habit
Agents recall well when asked, but the value compounds when writing is
automatic. Add something like this to your agent's system prompt or rules file:
```md theme={null}
Before answering questions about this project, call `recall` first.
When a decision is made, call `decide` with its rationale.
When a plan is set, call `plan`.
Do not persist secrets, tool call logs, or file contents.
```
Klio strips secrets and personal data before anything is stored, and the
redaction step fails closed — if content cannot be made safe, it is not
written. That is a backstop, not a licence to send it everything. See
[What Klio keeps](/concepts/what-klio-keeps).
## Next
Keep a team's memory shared without turning it into one global pile.
Exact parameters and defaults for all ten tools.
# CLI reference
Source: https://docs.klio.tech/self-host/cli
Every command and flag in the klio launcher.
The launcher ships as [`@klio-tech/klio`](https://www.npmjs.com/package/@klio-tech/klio)
on npm. Run it with `npx` — there is nothing to install globally.
```bash theme={null}
npx @klio-tech/klio [options]
```
## Commands
Set up the local stack and wire your AI agents (Claude Code, Cursor, and any
other supported client found on the machine).
```bash theme={null}
npx @klio-tech/klio init [--cloud | --local] [--email ] [--image-tag ]
```
| Flag | Effect |
| ------------------- | --------------------------------------------------------- |
| `--local` | Run the full stack locally; skip the hosted/local prompt |
| `--cloud` | Wire agents to Klio Cloud instead of a local stack |
| `--email ` | Pre-fill the claim email |
| `--image-tag ` | Pin a specific container image tag instead of the default |
Show what is running, where, and as whom. The first thing to run when
something looks wrong.
```bash theme={null}
npx @klio-tech/klio status
```
Re-prompt for provider, curator and agent settings — or upgrade the stack to a
new release.
```bash theme={null}
npx @klio-tech/klio update [--check | --to-latest | --to-version ] [--watch]
```
| Flag | Effect |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `--check` | Report whether an upgrade is available; change nothing |
| `--to-latest` | Upgrade the stack to the newest release |
| `--to-version ` | Upgrade (or pin) to a specific version |
| `--watch` | Run a long-lived host-side process that applies background auto-updates the bridge has detected — avoids needing docker-in-docker |
Tweak runtime settings such as auto-update mode and claim email.
```bash theme={null}
npx @klio-tech/klio configure
```
Stop the stack. **Data is preserved** — this is the safe way to pause.
```bash theme={null}
npx @klio-tech/klio down
```
Stop the stack, **delete all data**, and restore agent configs from the
backups taken during `init`.
```bash theme={null}
npx @klio-tech/klio uninstall
```
This deletes your memory store. Use `down` if you only want to stop the
stack.
Print the package version.
```bash theme={null}
npx @klio-tech/klio version
# or
npx @klio-tech/klio --version
```
## Global flags
| Flag | Effect |
| ----------------- | ------------------------- |
| `-h`, `--help` | Print usage |
| `-v`, `--version` | Print the package version |
# Self-hosting Klio
Source: https://docs.klio.tech/self-host/install
Run the open-source engine on your own hardware. One command brings up the stack and wires your agents to it.
The Klio engine is open source and self-hostable. Self-hosting is free, nothing
leaves your machine, and the custody guarantees are stronger than Cloud's:
memory is encrypted at rest under a key **you** own, and every write is chained
with SHA-256 so the history is tamper-evident.
What you get is the **core memory engine** — capture, recall, spaces, projects
and the curator. The layers built on top of the store are Cloud-only: the
knowledge graph that links memories to the entities they are about, hybrid
recall that matches exact identifiers as well as meaning, artifacts,
compression, and the reconciliation pass that judges when a new fact
contradicts an older one and retires it.
So the trade is custody against capability. If memory must not leave your
network, self-host. If you want everything that reasons over the store, use
Cloud — all of it is on the Free plan, so you can compare them without paying.
## Prerequisites
* **Docker** running — check with `docker info`
* **Node 20+** — needed only to run the launcher via `npx`
* **\~8 GB free disk** for the container images
* *(macOS, if you choose the Ollama provider)* a working `ollama` install for
Metal acceleration. On Linux the `docker-ollama` compose profile is used as a
CPU fallback.
## Install
```bash theme={null}
npx @klio-tech/klio init
```
That single command pulls the containers (engine, bridge, dashboard, plus
Postgres and Redis), boots them, runs migrations, walks you through provider and
model selection, detects every supported AI agent on your machine, and patches
each one's MCP config.
Roughly 30 seconds on a warm Docker, about two minutes on a cold first run.
`klio init` accepts `--local` or `--cloud` to skip the prompt, and `--email ` to pre-fill the claim address. Pin a specific build with
`--image-tag `.
## Confirm it is up
```bash theme={null}
npx @klio-tech/klio status
```
This reports what is running, where, and as whom. Your agents should now list
the ten Klio tools without any further configuration — the launcher already
wrote their MCP config for you.
## What you get that Cloud does not
Memory is encrypted at rest under a key you hold. On Klio Cloud the keys are
ours.
Every write is chained with SHA-256, so the history can be independently
verified. Cloud writes are not hash-chained today.
Anywhere the docs mention a user-held encryption key or a hash chain, that
refers to the **self-hosted** engine specifically. See
[What Klio keeps](/concepts/what-klio-keeps#where-it-physically-lives) for the
Cloud equivalent.
## Connecting agents manually
The launcher patches agent configs for you, but if you need to wire one by hand
it is the same MCP endpoint on your own host:
```
http://localhost:8080/mcp
```
Everything in [Any MCP client](/connect/any-mcp-client) applies unchanged —
only the base URL differs.
## Day-two operations
| Command | What it does |
| ------------------------------- | --------------------------------------------------------------- |
| `npx @klio-tech/klio status` | Show what is running, where, and as whom |
| `npx @klio-tech/klio update` | Re-prompt provider/model, or upgrade the stack |
| `npx @klio-tech/klio configure` | Tweak runtime settings (auto-update mode, claim email) |
| `npx @klio-tech/klio down` | Stop the stack — **data is preserved** |
| `npx @klio-tech/klio uninstall` | Stop and **delete all data**; restore agent configs from backup |
See the [CLI reference](/self-host/cli) for the full flag set.
`uninstall` deletes your memory store. `down` is the one you want for stopping
the stack temporarily.
## Source and licence
The engine lives at
[github.com/klio-tech/klio](https://github.com/klio-tech/klio). Klio is open
core, and the line is capability rather than hosting: the memory engine and MCP
shim are open source and free to run yourself, while the graph, hybrid recall,
artifacts, compression and reconciliation are part of Klio Cloud. Cloud is not
the same software with a bill attached.
# artifact_get
Source: https://docs.klio.tech/tools/artifact-get
Fetch an artifact surfaced by recall — its text, paginated, or a download link.
The read half of [artifacts](/concepts/artifacts). Takes the `artifact_id`
from a card's `metadata.artifact_id`, surfaced by a normal
[`recall`](/tools/recall) call.
## Parameters
From a recall result's `metadata.artifact_id`.
`text` returns the contents, paginated. `url` returns a presigned download
link instead.
Character offset to continue reading from. Used with `mode: "text"`.
## `mode: "text"` (default)
Returns the contents one page at a time. Page size is 51,200 characters.
```json theme={null}
{ "artifact_id": "art_01J...", "mode": "text" }
```
```json theme={null}
{
"artifact_id": "art_01J...",
"title": "Q3 pricing analysis",
"kind": "document",
"text": "# Q3 Pricing Analysis\n\n...",
"offset": 0,
"has_more": true
}
```
Continue by passing the next offset:
```json theme={null}
{ "artifact_id": "art_01J...", "mode": "text", "offset": 51200 }
```
Binary artifacts — PDFs, Office documents, images — return
`{"artifact_id": "...", "title": "...", "kind": "...", "error": "not_text"}`
rather than an extraction attempt. Klio does no text extraction from binary
formats in this version; use `mode: "url"` to download and read them
yourself.
## `mode: "url"`
Returns a presigned download link.
```json theme={null}
{ "artifact_id": "art_01J...", "mode": "url" }
```
```json theme={null}
{
"artifact_id": "art_01J...",
"title": "Onboarding deck",
"kind": "file",
"url": "https://...",
"expires_in": 900
}
```
The link is valid for 900 seconds and forces a download
(`Content-Disposition: attachment`) rather than rendering inline — including
for content types a browser would otherwise open directly, like PDFs or
images.
## Notes
A page is capped at 51,200 characters regardless of how large the
underlying artifact is. A multi-megabyte document takes several calls with
increasing `offset` to read in full — `has_more` tells you whether another
page remains.
`mode: "text"` works for plain text, JSON, XML, and YAML content. PDFs,
Word and Excel files, and images are not extracted — `mode: "text"` on one
of these returns `not_text`, and `mode: "url"` is the only way to retrieve
it.
# artifact_put
Source: https://docs.klio.tech/tools/artifact-put
Store a document, JSON record, or file, and make it discoverable through recall.
Writes an artifact and, alongside it, the card that makes it findable — see
[Artifacts](/concepts/artifacts) for how the two connect. Always writes to the
org's shared memory; there is no `agent`-scoped, private artifact.
Three behaviours, selected by which arguments are present.
## Inline: `content` given
One call. For text up to 64KB — the only write path available to clients that
speak MCP but cannot make an arbitrary HTTP request, such as ChatGPT
connectors and Claude.ai custom connectors.
```json theme={null}
{
"title": "Q3 pricing analysis",
"summary": "Competitor pricing comparison and margin model for the Q3 review",
"kind": "document",
"content": "# Q3 Pricing Analysis\n\n..."
}
```
```json theme={null}
{
"stored": true,
"artifact_id": "art_01J...",
"version": 1,
"status": "active"
}
```
## Presigned upload: `content` omitted, `mime_type` and `size_bytes` given
For larger or binary content. Returns an upload URL rather than storing
anything yet.
```json theme={null}
{
"title": "Onboarding deck",
"summary": "Slide deck used in the July onboarding call",
"kind": "file",
"mime_type": "application/pdf",
"size_bytes": 2400000
}
```
```json theme={null}
{
"stored": false,
"artifact_id": "art_01J...",
"upload_url": "https://...",
"method": "PUT",
"expires_in": 900
}
```
`upload_url` is signed for exactly the `size_bytes` you declared — an upload
of any other size is rejected at the storage layer, so declare the real
size. Upload the bytes with an HTTP `PUT`, then confirm.
## Confirm: `artifact_id` given
Call again with the `artifact_id` from the presigned response, no other
arguments needed.
```json theme={null}
{ "artifact_id": "art_01J..." }
```
```json theme={null}
{
"stored": true,
"artifact_id": "art_01J...",
"version": 1,
"status": "active"
}
```
The server measures the uploaded object itself at confirm time — size and
checksum both — and enforces the plan's limits against that measured value,
not the `size_bytes` you declared when requesting the URL. Confirm is what
activates the artifact; nothing is discoverable through recall until it
succeeds.
## Parameters
Short name for the artifact. Up to 200 characters.
One line describing what this is. Other agents find the artifact by this
text, via the card — write it as the search phrase you would want to match.
Up to 500 characters.
`document` for prose, `record` for JSON, `file` for binary content.
Inline text, up to 64KB. Omit to use the presigned upload path instead.
MIME type of the upload. Required when `content` is omitted.
Byte size of the upload. Required when `content` is omitted — the upload URL
is signed for exactly this many bytes.
Optional space slug. Omit to file to the org's default store.
Optional project identifier. See [Scope](/concepts/scope) for how project
resolution works.
Id of an artifact this one replaces. The superseded version is retired and
its card hidden, the same way a superseded memory stops surfacing in
recall.
Confirms an upload started earlier. Pass only this (and no other argument)
to confirm.
## Limits
`content` over 64KB is rejected outright, with a message pointing at the
presigned path — it does not silently truncate or fall back.
Both the inline and presigned paths are checked against the plan's
per-file limit and the org's remaining pool before anything is stored. See
[storage limits](/concepts/artifacts#storage-limits).
A presigned upload is one HTTP `PUT`, so `size_bytes` above 100MB is
rejected with `too_large_for_single_upload`. Multipart upload is not
available yet, so an artifact larger than that cannot be stored in this
release.
This only affects plans whose per-file limit is above 100MB — on every
other plan the plan limit is reached first and the error is
`file_too_large`. Nothing is reserved when a request is refused.
A full pool rejects further `artifact_put` calls. It does not affect
`artifact_get` or `recall` — existing artifacts stay readable.
# Authentication
Source: https://docs.klio.tech/tools/authentication
Endpoint, headers, and how a request resolves to an org, an agent, and a project.
## Endpoint
Hosted Klio speaks MCP over Streamable HTTP:
```
https://mcp.klio.tech/mcp
```
Self-hosted installs expose the same path on whatever host you run the MCP
service on.
## API key
Authenticate with the `X-Klio-Key` header:
```bash theme={null}
curl https://mcp.klio.tech/mcp \
-H "X-Klio-Key: vex_live_..." \
-H "X-Klio-Agent: claude-code"
```
Create keys in the dashboard under **Settings → API keys**. The key resolves the
request to an org, and that org boundary is the one guarantee nothing crosses.
A key is org-wide. Anyone holding it can read and write everything in that
org's memory. Treat it like a database credential: environment variables or a
password manager, never a committed file.
`X-Vex-Key` and the older `X-AgentGuard-Key` are accepted as legacy aliases
for `X-Klio-Key`, and they will keep working — existing configs never break.
New integrations should use the `X-Klio-*` names. When both a Klio and a Vex
header are present, the Klio one wins.
### OAuth
The hosted MCP server also accepts OAuth bearer tokens, verified against the
Klio Cloud issuer, for clients that support the MCP authorization flow. In that
case the agent identity defaults to the OAuth `client_id` unless `X-Klio-Agent`
overrides it.
## Headers
| Header | Required | Purpose |
| ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `X-Klio-Key` | Yes¹ | API key. Resolves the org. Legacy alias: `X-Vex-Key`. |
| `X-Klio-Agent` | No | Stable agent identity. Drives attribution. Legacy alias: `X-Vex-Agent`. |
| `X-Klio-Project` | No | Declares the workspace's project — a git remote URL, or the repo root path for a non-git workspace. Legacy alias: `X-Vex-Project`. |
¹ Unless you are authenticating with an OAuth bearer token instead.
### `X-Klio-Agent`
A stable, per-agent string — `claude-code`, `cursor`, `codex`, `ci-bot`. It is
what makes a ledger of "who wrote what" possible, and it is what
`scope: "agent"` resolves against.
Clients that omit it are all folded into a single default agent identity. Things
still work, but attribution collapses and private scope stops being meaningfully
private.
### `X-Vex-Project`
Lets a workspace declare its project once instead of passing `project` on every
call. It accepts a git remote URL when the workspace is a git repo, and the
repository root directory path when it is not — both resolve to the same
project a matching `project` argument would.
Project resolution **fails open**: a missing header, an unrecognised
identifier, or a lookup error all fall back to org-wide rather than failing the
request. This keeps a misconfigured client working, but it means the header is
a convenience — never a security control. The security boundary is the org.
## Errors
| Status | Meaning | Fix |
| ------ | ------------------------------------------------ | ----------------------------------------------------------- |
| `401` | Missing or invalid `X-Vex-Key` | Check the header name and that the key has not been revoked |
| `403` | Key is valid but not permitted for this resource | Confirm the key belongs to the org you are addressing |
| `429` | Plan quota or rate limit reached | See usage in the dashboard, or upgrade the plan |
A bare `GET https://mcp.klio.tech/mcp` returning `401` is expected — that is the
endpoint confirming it is alive and requires auth.
# decide
Source: https://docs.klio.tech/tools/decide
Record a decision and its rationale as soon as one is made.
The highest-value verb. A decision recorded with its reasoning is the thing
that stops the next agent from quietly undoing your work — and stops a human
from having to explain the same trade-off twice.
## Parameters
The decision that was made.
Why this was chosen over the alternatives. Optional, but decisions without
reasoning get re-litigated the first time someone disagrees.
`org` (default) writes to the shared team memory. `agent` keeps it private to
the identity in the `X-Vex-Agent` header. See [Scope](/concepts/scope).
Optional space slug. Omit to write to the org's default store.
## Examples
```json With rationale theme={null}
{
"content": "Retry 429s with jittered backoff, cap at 5 attempts",
"rationale": "Upstream rate-limits per minute; fixed backoff synchronised our retries"
}
```
```json A rejected alternative is still a decision theme={null}
{
"content": "Not moving to a monorepo this quarter",
"rationale": "CI migration cost outweighs the benefit while we are three services"
}
```
## Guidance
The decision tells the next agent *what*; the rationale tells it *whether the
reasoning still holds*. Circumstances change — an agent that can see why a
choice was made can tell when it should be revisited. One that only sees the
choice cannot.
"We considered X and chose not to" is worth as much as what you picked.
Without it, someone re-proposes X every quarter.
A newer decision that contradicts an older one retires it — the old one stops
surfacing in recall. You do not need to clean up first. See
[What Klio keeps](/concepts/what-klio-keeps).
# forget
Source: https://docs.klio.tech/tools/forget
Retract a memory or artifact by id so it stops surfacing in recall.
Explicit retraction. Use it when something was written that should not have
been, or when a fact has stopped being true and nothing has contradicted it yet.
`memory_id` also accepts an artifact id. Klio tries it as an artifact first —
a hit retracts the artifact and hides its [card](/concepts/artifacts) in the
same call, so recall stops surfacing it immediately. If it is not an artifact
id, the ordinary memory retraction runs instead.
## Parameters
The id of the memory or artifact to retract. Memory ids come back on
[`recall`](/tools/recall) results; artifact ids come back on
[`artifact_put`](/tools/artifact-put) responses or in a card's
`metadata.artifact_id`.
## Example
Recall first to find the id, then retract:
```json theme={null}
{ "query": "deployment schedule" }
```
```json theme={null}
{ "memory_id": "mem_01HZX9..." }
```
## When to reach for it
An agent recorded a customer name, or an internal detail that does not belong
in shared memory. Retract it.
"Deploys are paused during the migration" was true and is not any more, and
there is no newer decision to supersede it. Retract rather than leaving it to
mislead.
If a memory is technically true but consistently crowds out better answers,
retracting it is a legitimate fix.
You usually do not need `forget` for ordinary changes of mind. Recording the
new decision with [`decide`](/tools/decide) supersedes the one it contradicts
automatically — and unlike retraction, that keeps the reasoning trail intact.
A retracted memory stops surfacing in recall for everyone on the org. If
several agents depended on it, they lose it at the same moment — and unlike
superseding, there is no replacement left in its place.
# note
Source: https://docs.klio.tech/tools/note
Free-form durable context that is not a fact, decision, or plan.
The catch-all. Use it when something is worth keeping but does not fit the
other verbs — context, caveats, warnings about the shape of the codebase.
## Parameters
The free-form note to store.
`org` (default) writes to the shared team memory. `agent` keeps it private to
the identity in the `X-Vex-Agent` header. See [Scope](/concepts/scope).
Optional space slug. Omit to write to the org's default store.
## Examples
```json A caveat about the code theme={null}
{
"content": "The legacy/ directory is scheduled for deletion after Q3 — do not invest in refactoring it"
}
```
```json Context a newcomer would lack theme={null}
{
"content": "The odd retry logic in the webhook handler exists because a partner's endpoint returns 200 before it has actually processed anything"
}
```
## Guidance
If you find yourself reaching for `note` constantly, check whether the content
is really a [decision](/tools/decide) or a [fact](/tools/remember). Those types
carry more weight in recall, so using the right verb makes the memory easier to
find later.
# observe
Source: https://docs.klio.tech/tools/observe
Log something the agent saw or did this session.
Session activity worth carrying forward — a surprising behaviour, a discovered
constraint, a result that will matter later.
This is the verb most likely to fill a store with noise, so it is also the one
to use most sparingly.
## Parameters
What the agent observed or did this session.
`org` (default) writes to the shared team memory. `agent` keeps it private to
the identity in the `X-Klio-Agent` header. See [Scope](/concepts/scope).
Optional space slug. Omit to write to the org's default store.
## Examples
```json A discovered constraint theme={null}
{
"content": "The integration suite fails on Node 18 but passes on 20 — the fetch polyfill is the cause"
}
```
```json A surprising behaviour theme={null}
{
"content": "The staging Postgres rejects connections without the VPN, even from CI"
}
```
## Guidance
Do not log routine activity. An agent that calls `observe` after every tool
call produces a store where recall returns noise instead of signal. Before
writing, ask whether it will still matter next week. If not, skip it.
"The suite fails on Node 18" is an observation. Once you have confirmed it
and pinned the version, it has become a fact — write it with
[`remember`](/tools/remember) so it carries the right weight.
# Tools overview
Source: https://docs.klio.tech/tools/overview
The ten MCP tools Klio exposes, and when each one is the right verb.
Klio's surface is deliberately small: one verb per job, so an agent choosing
between them does not have to think hard. Every MCP client gets all ten
automatically on connection — there is no SDK to wire up.
## The ten verbs
| Tool | Purpose | Required |
| ------------------------------------- | ----------------------------------------------------- | -------------------------- |
| [`recall`](/tools/recall) | Retrieve relevant memories before acting | `query` |
| [`remember`](/tools/remember) | Store a durable fact or preference | `content` |
| [`decide`](/tools/decide) | Record a decision and its rationale | `content` |
| [`plan`](/tools/plan) | Record a forward-looking plan | `content` |
| [`observe`](/tools/observe) | Log what the agent saw or did this session | `content` |
| [`note`](/tools/note) | Durable context that is not a fact, decision, or plan | `content` |
| [`artifact_put`](/tools/artifact-put) | Store a document, JSON record, or file | `title`, `summary`, `kind` |
| [`artifact_get`](/tools/artifact-get) | Fetch an artifact surfaced by recall | `artifact_id` |
| [`space`](/tools/space) | List, create, inspect, or switch spaces | `action` |
| [`forget`](/tools/forget) | Retract a memory or artifact by id | `memory_id` |
## Choosing a write verb
The five write tools differ only in the type they record, but that type is what
makes recall useful later — a decision and a stray observation should not carry
the same weight.
Use whenever an option was picked over alternatives. Pass `rationale`; a
decision without its reasoning gets re-litigated the first time someone
disagrees with it.
> "We'll use jittered backoff for 429s, because fixed backoff was
> synchronising our retries."
Facts about the project, the stack, or the people. Call it proactively when
a user reveals one, not only when asked to.
> "The team deploys on Tuesdays." · "Abhishek prefers TypeScript strict mode on."
Forward-looking intent, so the next agent can continue rather than restart.
> "Next: migrate the auth service to the new token format, then backfill."
Session activity worth carrying forward. Use sparingly — this is the verb
most likely to fill a store with noise. If it will not matter next week, do
not write it.
> "The integration suite fails on Node 18 but passes on 20."
The catch-all for context that is worth keeping but is not a fact, decision,
or plan.
> "The `legacy/` directory is scheduled for deletion after Q3 — don't invest in it."
## Common parameters
Every write tool (`remember`, `decide`, `plan`, `observe`, `note`) accepts:
`org` shares the memory with every agent on the org. `agent` keeps it private
to the identity in the `X-Klio-Agent` header. See [Scope](/concepts/scope).
Optional space slug. Omit to write to the org's default store.
[`artifact_put`](/tools/artifact-put) is the one write tool without a
`scope` parameter — artifacts are always org-shared, with no private,
`agent`-scoped equivalent. It does accept `space` and `project`. See
[Artifacts](/concepts/artifacts).
## What good usage looks like
Agents use Klio well when reading is reflexive and writing is selective:
```md theme={null}
Before answering questions about this project, call `recall` first.
When a decision is made, call `decide` with its rationale.
When a plan is set, call `plan`.
Do not persist secrets, tool call logs, or file contents.
```
The failure mode is not too little memory — it is too much. An agent that
calls `observe` after every tool call produces a store where recall returns
noise. Write conclusions, not activity.
# plan
Source: https://docs.klio.tech/tools/plan
Record a forward-looking plan when one is set.
Intent, so the next agent continues the work instead of restarting it. A plan
written at the end of a session is what turns a hard stop into a handover.
## Parameters
The forward-looking plan to record.
`org` (default) writes to the shared team memory. `agent` keeps it private to
the identity in the `X-Vex-Agent` header. See [Scope](/concepts/scope).
Optional space slug. Omit to write to the org's default store.
## Examples
```json A sequence of work theme={null}
{
"content": "Next: migrate the auth service to the new token format, then backfill existing sessions, then remove the compatibility shim"
}
```
```json With a stopping point theme={null}
{
"content": "Refactoring the invoice generator in three passes. Pass 1 (extraction) is done; pass 2 splits the tax calculation out next.",
"space": "billing"
}
```
## Guidance
A plan is most useful when it records progress against itself. "Three passes,
first is done" tells the next agent where to start. "Refactor in three
passes" does not.
When the work is done or the approach changes, record the new decision. The
superseding step retires the plan it contradicts, so you are not maintaining
a to-do list by hand.
# recall
Source: https://docs.klio.tech/tools/recall
Retrieve relevant memories for the current org before acting.
The read verb. An agent should call this before answering questions about a
project — it is the half of the handover that makes the other half worth doing.
## Parameters
What to search the team's memory for. Search matches on meaning and on exact
wording at the same time, so you can phrase it the way you would ask a
colleague, or paste the identifier you are actually looking for.
`org` searches the shared team memory plus your own private memories and the
projects you belong to. `private` narrows to your own memories only. `agent`
searches what this agent identity wrote and is not a privacy boundary. See
[Scope](/concepts/scope).
Optional space slug, to confine the search to one project's store.
Optional project identifier, to narrow results without switching space.
Matches a git remote URL, a repository root path, a project id, or a
display name. See [Scope](/concepts/scope#narrowing-a-recall).
Maximum memories to return. Between `1` and `50`.
## Examples
```json Basic theme={null}
{
"query": "how do we handle auth errors in the API?"
}
```
```json Scoped to a space theme={null}
{
"query": "when do invoices generate?",
"space": "billing",
"limit": 5
}
```
```json This agent's private notes theme={null}
{
"query": "what was I in the middle of?",
"scope": "agent"
}
```
```json An exact identifier theme={null}
{
"query": "KLIO_METRICS_TOKEN"
}
```
## Notes on behaviour
If a decision has been contradicted by a newer one, recall returns the
current version only. You are not asked to reconcile the two — that is the
point of the retire step in the [handover loop](/concepts/handover-loop).
A memory written by another agent a second ago is available to the next
recall. There is no push to already-running agents, so an agent mid-session
sees new state only when it calls `recall` again.
Recall runs two searches and combines them. One matches meaning, so a
paraphrase finds a memory that used different words. The other matches the
text itself, so a rare literal finds the memory that contains it.
This matters because the two fail in opposite places. A search for a flag
name, an error string, a commit hash or a function name is the case where
meaning-based search is weakest: the token is rare, so it carries little
signal, and memories that are merely *about* the same topic can crowd out
the one memory that actually names it. Pasting the literal is not a worse
query than describing it. It is usually the better one.
An identifier is matched as its parts, in order. `pool_pre_ping` matches
text containing `pool pre ping` adjacent and in that sequence, so a memory
that happens to use all three words separately does not match.
The default of `10` suits most questions. Raising it toward `50` spends the
model's context on progressively less relevant results — usually a sign the
query should be narrower, or the memories should live in a
[space](/concepts/scope).
Passing `project` returns that project's memories first, then fills any
remaining slots (up to `limit`) with unfiled memories — never with another
project's. If a query has few hits inside the project, expect the rest of
the page to be org-wide unfiled results, not padding from elsewhere.
Results carry a memory id. Pass it to [`forget`](/tools/forget) to retract
something that surfaced and should not have.
# remember
Source: https://docs.klio.tech/tools/remember
Store a durable fact or preference for the team.
Facts that stay true past this session — about the project, the stack, or the
people working on it.
Call it **proactively**. When a user reveals a durable preference or fact in
passing, that is the moment to write it; waiting to be asked means it is
usually never written at all.
## Parameters
The durable fact or preference to store.
`org` (default) writes to the shared team memory. `agent` keeps it private to
the identity in the `X-Vex-Agent` header. See [Scope](/concepts/scope).
Optional space slug. Omit to write to the org's default store.
## Examples
```json A team convention theme={null}
{
"content": "The team deploys on Tuesdays; no releases after 15:00 UTC on Fridays"
}
```
```json A personal preference theme={null}
{
"content": "Abhishek prefers TypeScript with strict mode on, and no default exports"
}
```
```json Scoped to a project theme={null}
{
"content": "Invoices are generated nightly at 02:00 UTC",
"space": "billing"
}
```
## Guidance
"We deploy on Tuesdays" is a fact. "We deployed on Tuesday" is an event —
that belongs in [`observe`](/tools/observe), if anywhere.
Memories are read cold, months later, by an agent with none of your context.
"Use the new endpoint" is useless. "The billing service moved to
/v2/invoices in June; /v1 is read-only" survives.
# space
Source: https://docs.klio.tech/tools/space
List, create, inspect, or switch the named stores within an org.
A space is a named subdivision of an org's memory — one per project, service, or
workstream. Spaces are how a shared workplace stays useful as it grows, instead
of collapsing into one pile where every query returns everything.
## Parameters
The operation to perform.
Human-readable space name. Used by `create`.
Space slug. Used by `info`, `switch`, and optionally `create`.
## Actions
Enumerate the spaces on this org.
```json theme={null}
{ "action": "list" }
```
Create a space. Pass a readable `name`; supply `slug` to control the
identifier rather than having one derived.
```json theme={null}
{
"action": "create",
"name": "Billing service",
"slug": "billing"
}
```
Inspect one space by slug.
```json theme={null}
{ "action": "info", "slug": "billing" }
```
Make a space the active one for this session, so subsequent calls do not
need to pass `space` each time.
```json theme={null}
{ "action": "switch", "slug": "billing" }
```
## Using a space
Once created, pass the slug on reads and writes:
```json theme={null}
{ "content": "Invoices generate nightly at 02:00 UTC", "space": "billing" }
```
```json theme={null}
{ "query": "when do invoices generate?", "space": "billing" }
```
Or `switch` once and let the session carry it.
## Spaces are not a tenancy boundary
Spaces subdivide an org's memory for relevance. They are **not** an isolation
boundary — anyone holding the org's API key can read any space in it.
If two bodies of work must never see each other — separate clients, separate
customers — they need separate orgs and separate keys, not separate spaces.
See [Scope and spaces](/concepts/scope) for how spaces interact with `org` and
`agent` scope.