# MCP tools

The six memory operations every connected client gets - each with its exact arguments, an example call, and the JSON it returns. The tool contract is frozen.

## Six tools, one memory

```text
POST https://memory.agentage.io/mcp
```

Connect any MCP client to the single endpoint `memory.agentage.io/mcp` (Streamable HTTP, OAuth 2.1 with PKCE, sign-in at auth.agentage.io) and it gets these six `memory__*` tools. They read and write one shared markdown memory - the same notes across every AI you connect. New here? Start with the [MCP server](/docs/mcp-server) guide; for the read-only HTTP surface see the [REST API](/docs/rest-api).

> The memory tools run only when your request is about your notes - everyday questions like facts, math, or writing are answered normally, without touching your memory.

## The tools

Grouped by read and write. Click a row to expand its contract - description, arguments, an example input, the JSON result, and behavior notes. All six are live; the schema is CI-frozen, so fields are only ever added, never renamed or removed.

### Read

#### `memory__search`

Find notes by keyword across the whole memory.

Find memories by literal text, ranked by match count. Matches the query as one case-insensitive substring across titles, bodies, and tags - not semantic, not tokenized - so search a single keyword, not a phrase. Returns path + snippet + score, never full bodies.

_Returns ranked paths and snippets only, never full bodies; score is the match count, not a relevance percent. To browse the folder tree instead, use memory__list._

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `query` | string | required | Literal text to match as one case-insensitive substring across titles, bodies, and tags. Search a single distinctive keyword, not a phrase. |
| `folder` | string | optional | Restrict to this POSIX folder (no leading slash), e.g. work/tasks. Matches that folder only. Omit for the whole memory. |
| `tags` | string[] | optional | Frontmatter or inline #tags, AND-matched (all must be present), case-sensitive, bare without #. Up to 50. |
| `limit` | integer, 1-50 | optional, default 20 | Max results in this page; capped at 50. |
| `cursor` | string | optional | Opaque pagination token - pass the previous response's nextCursor verbatim. Omit for the first page. |

Example input:

```json
{ "query": "pkce", "limit": 5 }
```

Result:

```json
{
  "results": [
    {
      "path": "work/tasks/auth.md",
      "title": "Auth",
      "snippet": "...enable pkce for the oauth flow...",
      "score": 3,
      "updated": "2026-07-05T21:47:55+00:00"
    }
  ],
  "nextCursor": "eyJvIjoyMH0"
}
```

#### `memory__read`

Read a note by path.

Read one memory by its exact path: returns full frontmatter, markdown body, tags, and last-updated timestamp. Use a path from memory__search / memory__list, a prior write, or the user - do not guess one from a title.

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | string | required | Exact POSIX .md address (case-sensitive, no leading slash), e.g. work/tasks/foo.md. Not a title or query. |

Example input:

```json
{ "path": "work/tasks/auth.md" }
```

Result:

```json
{
  "path": "work/tasks/auth.md",
  "title": "Auth",
  "frontmatter": { "type": "task", "tags": ["project", "active"] },
  "body": "# Auth\n\nEnable PKCE for the OAuth flow.",
  "tags": ["project", "active"],
  "updated": "2026-07-05T21:47:55+00:00",
  "deleted": false
}
```

#### `memory__list`

Browse the folder tree - subfolders with file counts, two levels deep.

Browse the memory as a folder tree: files and subfolders under a folder, two levels deep by default, with per-folder file counts. No bodies, no ranking.

_A folder tree two levels deep with recursive file counts; folders over the per-folder entry limit are flagged truncated and not expanded - call memory__list again with that folder to see inside._

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `folder` | string | optional | Folder to browse (POSIX, no leading slash), e.g. work/tasks. Matches that folder only. Omit for the memory root. |
| `depth` | integer, 1-2 | optional, default 2 | 1 = direct children of the folder only; 2 = also expand each subfolder one more level. |
| `tags` | string[] | optional | Frontmatter or inline #tags, AND-matched (all must be present), case-sensitive, bare without #. Up to 50. |

Example input:

```json
{ "folder": "work", "depth": 2 }
```

Result:

```json
{
  "folder": "work",
  "entries": [
    {
      "type": "folder",
      "path": "work/tasks",
      "files": 12,
      "entries": [
        {
          "type": "file",
          "path": "work/tasks/auth.md",
          "title": "Auth",
          "updated": "2026-07-05T21:47:55+00:00"
        }
      ]
    },
    {
      "type": "file",
      "path": "work/plan.md",
      "title": "Plan",
      "updated": "2026-07-04T10:00:00+00:00"
    }
  ],
  "truncated": false,
  "files": 13
}
```

### Write

#### `memory__write`

Create or replace a note.

This is the user's persistent memory - save durable facts, notes, preferences, and decisions here. Creates a new memory or fully overwrites the one at path, replacing the entire body and frontmatter.

_Idempotent full replace - overwrites the whole body and frontmatter. To change only part of an existing memory, use memory__edit._

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | string | required | Exact POSIX .md address (case-sensitive, no leading slash), e.g. work/tasks/foo.md. |
| `body` | string | required | The complete Markdown body, excluding frontmatter (no --- fences). Overwrites the entire existing body. |
| `frontmatter` | object | optional | YAML metadata as a key -> value map (no --- fences). write replaces the whole map. |

Example input:

```json
{
  "path": "projects/acme/stack.md",
  "body": "# Stack\n\nWe use Postgres for full-text search.",
  "frontmatter": { "type": "note", "tags": ["project"] }
}
```

Result:

```json
{ "path": "projects/acme/stack.md", "updated": "2026-07-06T08:00:00+00:00" }
```

#### `memory__edit`

Apply a targeted edit to a note.

Amend an existing memory in place. mode=str_replace swaps one exact text match without resending the note; append adds to the end; replace overwrites the whole body; frontmatter always shallow-merges. Fails not-found if the path does not exist - use memory__write to create.

_Frontmatter shallow-merges top-level keys (nested values replaced wholesale) and a key cannot be removed via edit - use memory__write to fully replace._

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | string | required | Exact POSIX .md address of an existing memory (case-sensitive, no leading slash). |
| `body` | string | optional | New body (mode=replace) or appended text (mode=append). Not used with str_replace; omit to change only frontmatter. |
| `frontmatter` | object | optional | YAML key -> value map (no --- fences). Shallow-merges top-level keys. |
| `mode` | 'replace' \| 'append' \| 'str_replace' | optional, default replace | How to apply body: str_replace swaps old_str for new_str in place; append adds to the end; replace overwrites the whole body. |
| `old_str` | string | optional (str_replace) | Exact existing body text to replace - must match verbatim (including whitespace) and appear exactly once. |
| `new_str` | string | optional (str_replace) | The replacement text. Omit to delete old_str. |

Example input:

```json
{
  "path": "projects/acme/stack.md",
  "mode": "str_replace",
  "old_str": "Postgres",
  "new_str": "Postgres 16"
}
```

Result:

```json
{ "path": "projects/acme/stack.md", "updated": "2026-07-06T08:01:00+00:00" }
```

#### `memory__delete`

Remove a note.

Soft-delete (forget) a memory by path. Returns not-found if the path does not exist. To remove only part of a memory, use memory__edit.

_Recoverable soft-delete - the memory is tombstoned in git history, not destroyed._

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `path` | string | required | Exact POSIX .md address to soft-delete (case-sensitive, no leading slash). |

Example input:

```json
{ "path": "projects/acme/old.md" }
```

Result:

```json
{ "path": "projects/acme/old.md", "deleted": true }
```

## Addressing multiple memories

Own more than one vault? The first path segment, prefixed with `@`, is the vault name: `@<vault-name>/<path>`. It lives inside the existing `path` and `folder` arguments, so the six tool schemas are unchanged. Find your vault names with `memory__list` (no folder).

```json
memory__write { "path": "@<vault-name>/notes/plan.md", "body": "..." }
memory__read  { "path": "@<vault-name>/notes/plan.md" }

memory__write { "path": "@work/notes/plan.md", "body": "..." }
```

_The first segment after @ is the vault name; the rest is the path inside that vault. The last line shows a vault named work. Returned paths come back @-prefixed so they round-trip._

```json
memory__search { "query": "roadmap", "folder": "@<vault-name>" }

memory__search { "query": "roadmap", "folder": "@work" }
```

_folder: "@<vault-name>" scopes a search or list to one whole vault; hits come back @-prefixed._

- A bare path (no `@`) addresses your default memory, unchanged.
- `memory__list` with no folder lists each of your vaults as an `@<vault>/` root folder.
- A bare `@<vault>` names a vault, not a file: `memory__read` / `write` / `edit` / `delete` reject it, while `memory__search` / `list` treat it as whole-vault scope.
- Vault names are 1-64 characters from `A-Z a-z 0-9 _ -`.

> An unscoped `memory__search` (no `@`) covers your default memory only. To search another vault, scope it with `folder: "@<vault>"`.

## Try it

Once connected, ask your assistant in plain language - it picks the right tools and chains them for you:

- "Search my memory for "postgres" and read the top result." - Ranks notes mentioning postgres by match count, then returns the top note in full (frontmatter + markdown body). (`memory__search -> memory__read`)
- "Save a note at projects/acme/stack.md: we use Postgres for full-text search." - Creates (or fully replaces) that note. It persists server-side and is readable from every AI you connect to the same account. (`memory__write`)
- "List everything under projects/ and tag the roadmap note as launched." - Shows the folder tree under projects/ (subfolders with file counts + notes), then shallow-merges a launched tag into the roadmap note frontmatter without rewriting the body. (`memory__list -> memory__edit`)
