Skip to main content
An MCP server gives an agent the ability to remember. It does not give it the habit. You still end up typing “remember that” and “what did we decide about the deploy” every session. Agent Skills close that gap. A skill is a Markdown file the agent loads when its description matches what you are doing, so recall at the start of a task and recording at the end become part of how the agent works.

Install

The installer detects the agents already on your machine, copies the skills in, stores your key at ~/.anona/config.env with mode 600, and registers the MCP server with Claude Code when the claude CLI is on your PATH. It edits no shell profile and writes nothing outside the directories it prints.
Skills load at session start. Restart your agent once after installing, or the skill is on disk and invisible.

The three skills

anona-memory

The agent’s own memory. Recalls what is already known before a task, records what the task settled after it.

anona-memory-sdk

Building on Anona. The SDK and API surface, scoping rules, error codes and framework adapters, so generated code compiles the first time.

anona-cli

Driving the anona command line from a shell, a script or CI: uploads, batch ingest, jobs, settings, and what every exit code means.
Install only some of them with --skill, repeated:

Options

An existing install is moved under ~/.anona/backups/ rather than deleted, and deliberately not left beside the skill: an agent reads every directory under skills/, so a backup parked there would load as a second skill claiming the same name.

How the skill reaches your memory

Four ways, tried in order. You only need one.
1

MCP tools

If the host exposes record, retrieve, reason and list_spaces, the skill uses them and there is nothing to configure.
See MCP for OAuth, the local stdio server, Hermes and OpenClaw.
2

The anona CLI

Otherwise, if the anona CLI is installed (pip anona 0.24.0 or later), the skill runs anona status once, then anona retrieve, anona record and anona reason with --json, and branches on the exit code. The CLI finds its own credential: an API key (--api-key, ANONA_API_KEY, or ~/.anona/config.env), the command-line login from anona login, or a temporary profile from anona start. It never uses the MCP login, so a machine signed in for MCP only exits 4 here and the skill moves on. Before a command it has not used, it reads anona guide --json rather than guessing a flag.
3

REST with curl

Otherwise the skill reads ANONA_API_KEY from the environment or from ~/.anona/config.env and calls https://api.anonalabs.com directly. This is what makes the skill work in a harness with no MCP support and no Python.
4

Neither

If anona or uvx is available, the skill may run anona start for a temporary profile and tell you so in one line; you keep it with anona login. Otherwise it says once that memory is not configured and does the task without it. A memory layer that blocks the work is worse than no memory layer.
MCP OAuth sign-in needs no API key. An organization that has never created one works from its first tool call. If you see 403 no_api_key, that is a fault on our side, not a missing step on yours; the one case where it is intended is an organization that revoked all of its keys.

The anona-cli skill

anona-memory uses the CLI only for the memory loop: recall, record, reason. anona-cli teaches an agent the rest of the anona CLI, and fires when there are no MCP tools, in a script or CI job, for a file upload or a batch ingest, for managing spaces, jobs or settings, or on any anona exit code. It teaches four habits:
  • Look before guessing. Run anona guide --json (or anona <command> --help) before composing an unfamiliar command. It is generated from the installed CLI, so it never lists a flag that copy lacks. See For AI agents.
  • Always --json, branch on the exit code. 3 is an accepted write that stored nothing, not a failure to retry; 8 is a job still running, to poll with anona jobs get.
  • Never re-send a write after exit 7 without checking: it may have been stored. The CLI itself never re-sends one.
  • --yes is the user’s decision. Destructive commands refuse without it, and the agent confirms with the user before adding it.
It also carries the credential order, how to set one up without running anona login on a person’s behalf, and scoping with --user-id. The details live in three reference files the agent opens only when needed: commands by task, JSON shapes and exit codes, and credentials.

Which space

The skill picks one per session, in this order:
  1. ANONA_SPACE_ID, set by --space or in your environment.
  2. The git repository name, lowercased, when working in a repo.
  3. default.
With more than one space and no ANONA_SPACE_ID, the skill lets Anona choose: it sends route: "auto" in place of a space, with the space picked above as fallback_space_id, and reads searched / routed_to to see where the call went. A pinned ANONA_SPACE_ID always wins. Routing works over MCP and REST; the anona CLI has no route flag, so on the CLI the skill keeps naming the space. A write creates the space. A read does not, so 404 space_not_found on a space nothing has been written to yet means empty, not broken.
Serving several people through one space? The skill passes user_id when you give it one, and the match is strict in both directions: a scoped read never returns an unscoped memory. See Scoping.

What gets recorded

Preferences, decisions and their reasons, fixes that cost time, constraints the code does not state, and outcomes. One fact per memory. Not recorded: secrets, anything already in the repository, narration of the session, or anything still a hypothesis. The skill carries the judgment call and worked examples of both sides.

Saving a session

Ask the agent to save, remember or export the session (“save this to Anona”, “save the deploy decisions”) and it makes a full record pass: every durable fact the conversation settled that is not in Anona yet, one memory each, then one line saying how many it wrote and to which space. It never stores the transcript or a summary as a single memory, because a blob retrieves badly and is mostly narration. In Claude Code, the plugin adds a command for the same thing:
Claude Code names a plugin’s commands after the plugin, which is why it is anona-memory: and not anona:. An exported conversation (a Claude Code /export file, or a conversation from a claude.ai data export) works the same way: hand it to the agent and ask it to save what is in it. For many conversations, a pass each is slow and costly; the skill suggests writing the facts to a JSON Lines file and loading it with anona record --batch instead. No hook is involved. Claude Code fires none on /export, and the hooks already keep a redacted excerpt at every compaction and a work log at every session end, so the command is for the moments you want saved now.

Compaction

Compacting a conversation swaps it for a summary, and whatever the summary drops is gone. The skill treats that as a deadline: what the session settled goes to Anona before it can be lost.
  • Before. Tell the agent you are about to compact and it records what the session settled first, then says how many memories it wrote.
  • After. When a conversation resumes from a summary, the agent records the durable facts in it before going back to the task.
It sends facts, one per memory and into the session’s usual space, never the transcript or the summary itself. Over REST each one carries "trigger": "compaction" in metadata; the MCP record tool has no metadata field, so there it goes without the marker. The same rules apply: no secrets, nothing already in the repository, nothing still a hypothesis.

Hooks

Skill instructions only run when the agent has a turn, and some of the moments that matter most happen without one: Claude Code compacts without asking, and a session can end mid-thought. A Claude Code hook is a command the client runs on a lifecycle event, so it covers exactly those moments. What leaves your machine. The pre-compact excerpt holds the user’s prompts and the agent’s prose. It never holds tool output, which is where file contents and command output live; pasted blocks and system text are dropped, the excerpt is capped at the last 40,000 characters, and key-shaped strings (API keys from common providers, tokens, bearer headers, webhook URLs, private keys, passwords, credentials in URLs) are replaced with [redacted] before it is sent. Redaction is pattern matching, not a guarantee: turn the digest off with ANONA_HOOK_DIGEST=0 in sessions that handle secrets in plain prose. What it costs. Each pull is one retrieve. Each push is one async record, billed like any other write: an excerpt costs more than a one-line fact because extraction reads all of it. Which space. The same order as the skill: ANONA_SPACE_ID, then the repository name, then default. When you can see more than one space and none is pinned, the pull sends route: "auto" with that space as the fallback. The pushes do not route by default. They carry raw session text the agent never reviewed, and routing chooses among every space you can see, including one shared with you by another organization or one a Slack channel or GitHub repository reads from. Filing a conversation excerpt there would show it to everyone on that space. So the pushes write only to a space that is your own: the repository’s space once it exists, or ANONA_SPACE_ID. They route only when you set ANONA_HOOK_ROUTE=auto, and a routed write that cannot be placed then goes to that same space. While the repository has no space of your own, the pushes are skipped rather than sent by bare name, since a bare name resolves to a space someone shared with you under that name when you have none. The agent’s first record, or creating the space in the dashboard, starts them. The agent’s own records, made under the skill, still route. Space ids are case-sensitive, so the repository name is matched against your spaces ignoring case: a checkout of anona-memory uses an existing Anona-memory. The space list is cached for an hour. No API key needed. The hooks use the first of these that is available:
  1. ANONA_API_KEY, in the environment or ~/.anona/config.env: REST calls.
  2. The anona CLI on PATH with a login from anona login (or a temporary profile from anona start): the hooks start anona mcp, the CLI’s stdio bridge, and call its retrieve and record tools. The bridge holds the login and refreshes it, so it keeps working for as long as the login does (30 days from the last use). This is the same login the anona MCP server in Claude Code uses.
  3. Neither: every hook is a silent no-op.
Over the bridge, a write carries no metadata (the MCP record tool has no such field), so trigger is not stored; the excerpt and the work log still say in their first line where they came from. Force one path with ANONA_HOOK_TRANSPORT=rest or mcp. Safety. The hooks never fail or slow a session. The pull is an async hook: startup does not wait for it, and what it finds arrives with your first prompt. Pushes are handed to a detached process and the hook returns at once. The script is standard-library Python 3 and needs no install.

Adding them by hand

Sign in once with anona login (or set ANONA_API_KEY), download the script, then register it in ~/.claude/settings.json (every project) or .claude/settings.json (one project):
If the file already has a hooks object, merge these events into it rather than replacing it. Run /hooks in Claude Code to confirm they are registered.

Settings

Read from the environment, then from ~/.anona/config.env:
The anona CLI reads only ANONA_API_KEY, ANONA_SPACE_ID and ANONA_BASE_URL from config.env. Put the ANONA_HOOK* switches in your shell environment, or in the env object of Claude Code’s settings.json, which applies them to hooks too.
Turning them off. For the plugin, set ANONA_HOOKS=off, or disable the plugin under /plugin, which also unloads the skills. If you added them by hand, delete the entries. Checking they work. Start a session in a repository whose space has memories: the first turn’s context should open with “Anona memory recalled at session start”. Edit a file and end the session; a minute later, a retrieve for “what was worked on” in that space returns the work log.

Claude Desktop and claude.ai

Those two do not read a skills directory. They take a ZIP upload under Customize > Skills, so build one:
That writes anona-memory.zip, anona-memory-sdk.zip and anona-cli.zip into the current directory, with the skill folder as the archive root, which is what the upload expects. Every skill stays inside the caps that surface enforces on name and description, 64 and 200 characters. anona-cli is of no use there, since that surface has no shell. Then give them tools. There is no shell there, so the CLI and curl fallbacks cannot run: add Anona as a custom connector under Settings > Connectors.
Sign in with OAuth when prompted. See MCP for the same flow in other clients.
The Claude Code that runs inside Claude Desktop is a different surface, and it reads ~/.claude/skills/ normally. The installer at the top of this page already covers it. The ZIP is only for the chat surface.

Supported agents

The installer knows Claude Code, Codex, OpenCode, Cursor, Hermes and OpenClaw (into ~/.openclaw/skills, or $OPENCLAW_STATE_DIR/skills if you moved it), and takes --dir for anything else. The community npx skills CLI covers 75+ harnesses from the same repository:

Source

The skills live in skills/ in the SDK repository. Read them before you install them; they are Markdown files and one shell script.

Next steps

MCP

The transport the skill prefers, with OAuth and stdio.

Python SDK

The programmatic client the second skill documents.