# MCP server

One cloud endpoint that every AI tool connects to over the Model Context Protocol. Sign in with OAuth - no API key - and read and write your memory through six tools.

## The endpoint

Agentage Memory exposes a single MCP endpoint over **Streamable HTTP**:

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

> **Auth: OAuth 2.1, no API key**
>
> Clients authenticate with **OAuth 2.1** (PKCE + dynamic client registration) - sign in once in the browser, no API key to copy or leak. The same account in every client shares one memory.

The client discovers it needs auth from the endpoint itself, sends you to sign in at the authorization server, then reconnects with the token it gets back - no key to paste:

_[Diagram omitted in the Markdown export - see the HTML page.]_

## Connect your client

The same endpoint works across every MCP client. Pick yours - each has a one-click install and a manual config:

- [Claude Code](/docs/claude-code)
- [Claude (Desktop & claude.ai)](/docs/claude)
- [VS Code](/docs/vs-code)
- [Cursor](/docs/cursor)
- [ChatGPT](/docs/chatgpt)
- [Grok](/docs/grok)

## Tools

Once connected, every client gets the same six memory operations. Click a tool to expand its contract - arguments, an example call, and the JSON it returns (also at the [MCP tools reference](/docs/mcp-tools)):

### 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 }
```

> 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.

## Try it

1. **Search and read**
   
   _"Search my memory for postgres and read the top result."_ → ranks notes by match count, then returns the top note in full. (`memory__search → memory__read`)
2. **Write a note**
   
   _"Save a note at projects/acme/stack.md: we use Postgres for full-text search."_ → creates the note; readable from every AI on the same account. (`memory__write`)
3. **List and tag**
   
   _"List everything under projects/ and tag the roadmap note as launched."_ → shows the folder tree, then merges a tag into the note frontmatter. (`memory__list → memory__edit`)

## Limitations

- Notes are plain markdown addressed by **path** (e.g. `work/tasks/foo.md`), not by title or ID.
- Search is literal keyword/substring matching - not semantic or vector search; search one distinctive keyword, not a phrase.
- `search` returns ranked paths + snippets, never full bodies - use `read` for the whole note.
- `list` shows the folder tree two levels deep, up to 100 entries per folder - call it again with a subfolder to go deeper.
- `edit` shallow-merges top-level frontmatter keys and cannot remove a key; use `write` to fully replace a note.
- `delete` is a recoverable soft-delete (kept in history), not a hard wipe.
