# Autopoetic MCP Server — Public Guide

Autopoetic exposes a [Model Context Protocol](https://modelcontextprotocol.io)
server giving LLM clients and agents a working surface over a project's
conversations, messages, and knowledge repository. The MCP surface mirrors the
REST v1 API: the same backing operations, the same error envelope, the same
pagination tokens.

The full per-tool reference is embedded in this document — rendered from
the live server's `tools/list` metadata, so it always matches the deployed
tools.

## Endpoint & transport

- **URL**: `https://app.autopoetic.ai/mcp` (or your deployment's `/mcp` path)
- **Transport**: MCP streamable HTTP (JSON-RPC 2.0 over HTTP POST)
- **Stateless**: no `Mcp-Session-Id` bookkeeping — every request is handled by
  a fresh transport and authenticated on its own. You can use any MCP client
  without session management.
- The handshake methods (`initialize`, `tools/list`) are reachable without a
  token and reveal only the public tool metadata. Every `tools/call` requires
  a valid `Authorization: Bearer <token>` header.

## Authentication

Send a Bearer access token with every request:

```
Authorization: Bearer <token>
```

Tokens are verified via Keycloak, the same way the REST API verifies them.
The token's project roles (`/customer_{customer_uuid}/project_{project_uuid}/{role}`
groups) determine what the tools will allow:

| Tier | Grants |
|---|---|
| viewer | all read tools (conversations, messages, knowledge) |
| editor | everything above plus writes: append messages, create/update/delete knowledge entries, record events, create relationships |
| admin | (same as editor for MCP — the MCP surface has no admin-only tools) |

Project agents typically authenticate with a **project API key** (a Keycloak
client credential, created by a project admin via the REST API or the UI).

<!-- BEGIN GENERATED TOOL REFERENCE — maintained by scripts/gen_mcp_docs.py; do not edit this section. -->

## Tool reference

Autopoetic's MCP server exposes ten project-scoped tools — every call names the `project_uuid` it acts on. This reference is rendered from the live server's `tools/list` metadata, so it always matches the deployed tools.

| Tool | Access tier |
|---|---|
| [`list_conversations`](#list_conversations) | viewer+ |
| [`read_conversation`](#read_conversation) | viewer+ |
| [`read_recent_messages`](#read_recent_messages) | viewer+ |
| [`append_message`](#append_message) | editor+ |
| [`search_knowledge`](#search_knowledge) | viewer+ |
| [`read_knowledge_entry`](#read_knowledge_entry) | viewer+ |
| [`upsert_knowledge_entry`](#upsert_knowledge_entry) | editor+ |
| [`delete_knowledge_entry`](#delete_knowledge_entry) | editor+ |
| [`record_event`](#record_event) | editor+ |
| [`create_knowledge_relationship`](#create_knowledge_relationship) | editor+ |

### `list_conversations`

List a project's conversations, newest activity first. Pass the returned next_cursor to the next call to page through older conversations; null means no more pages. Requires viewer access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project to list conversations in. | string | yes |  |  |
| `cursor` — Opaque cursor from a previous call's next_cursor. | string | null | no | `null` |  |
| `limit` — Page size (max 100). | integer | no | `25` | 1–100 |

### `read_conversation`

Read a conversation: its index (title, settings) and its recent messages. Requires viewer access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project the conversation belongs to. | string | yes |  |  |
| `conversation_uuid` — The conversation to read. | string | yes |  |  |

### `read_recent_messages`

Read a page of a conversation's messages, newest first. Pass the returned next_cursor to get the next-older page; null means the beginning of the conversation. Requires viewer access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project the conversation belongs to. | string | yes |  |  |
| `conversation_uuid` — The conversation to read messages from. | string | yes |  |  |
| `cursor` — Opaque cursor from a previous call's next_cursor. | string | null | no | `null` |  |
| `limit` — Page size (max 100). | integer | no | `25` | 1–100 |

### `append_message`

Append a message to a conversation (user or agent role). The append is the live write path: encryption and turn records are handled internally. Usage metering: this tool intentionally sends zero prompt/completion/GPU usage (plan §Phase 4 minimal signature) — appends made through MCP meter nothing, which under-counts and never over-counts. Requires editor access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project the conversation belongs to. | string | yes |  |  |
| `conversation_uuid` — The conversation to append to. | string | yes |  |  |
| `role` — Message role: user or agent. | string | no | `"user"` | pattern `^(user|agent)$` |
| `content` — Message body text. | string | no | `""` |  |

### `search_knowledge`

Search the project's knowledge index by name. Returns matching entries with their types and hints. Requires viewer access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project whose knowledge to search. | string | yes |  |  |
| `query` — Name (or name prefix) to search for. | string | yes |  |  |
| `limit` — Maximum results (max 50). | integer | no | `10` | 1–50 |

### `read_knowledge_entry`

Read a single knowledge entry by UUID (name, type, hint, characteristics, relationships). Requires viewer access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project the entry belongs to. | string | yes |  |  |
| `entry_uuid` — The entry's UUID. | string | yes |  |  |

### `upsert_knowledge_entry`

Create or update a knowledge entry by name+type (upsert). Pass expected_entry_generation from your last read or write to detect concurrent edits; omit it to skip the check. Writes stage into the shared overlay and are not persisted until a flush. Requires editor access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project the entry belongs to. | string | yes |  |  |
| `name` — Entry name. | string | yes |  |  |
| `type` — Entry type: entity, place, or thing. | string | yes |  | pattern `^(entity|place|thing)$` |
| `hint` — Short summary (max 256 chars). | string | null | no | `null` |  |
| `characteristics` — Simple singular adjectives. | list[string] | null | no | `null` |  |
| `date_of_origin` — Free-form date string. | string | null | no | `null` |  |
| `expected_entry_generation` — Per-entry generation from your last read/write; omit to skip the CAS check. | integer | null | no | `null` |  |

### `delete_knowledge_entry`

Delete a knowledge entry by UUID. Pass expected_entry_generation from your last read or write to detect concurrent edits. Requires editor access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project the entry belongs to. | string | yes |  |  |
| `entry_uuid` — The entry's UUID. | string | yes |  |  |
| `expected_entry_generation` — Per-entry generation from your last read/write; omit to skip the CAS check. | integer | null | no | `null` |  |

### `record_event`

Record an immutable, hash-chained event in the project's knowledge repository (e.g. a life event, meeting, or change). Link related entries via related_entries (by UUID). Writes stage into the shared overlay and are not persisted until a flush. Requires editor access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project to record the event in. | string | yes |  |  |
| `kind` — Event kind (open vocabulary, e.g. 'meeting', 'employment'). | string | yes |  |  |
| `occurred_at` — When the event occurred (free-form date string, e.g. '2026-08-15'). | string | yes |  |  |
| `status` — Optional status qualifier (e.g. 'laid off'). | string | no | `""` | ≤ 128 chars |
| `hint` — Short summary (max 1024 chars). | string | no | `""` | ≤ 1024 chars |
| `payload` — Structured event details. | object | null | no | `null` |  |
| `related_entries` — Related entries: [{uuid, hint?}]. | list[object] | null | no | `null` |  |

### `create_knowledge_relationship`

Create (or update) a directed relationship from one entry to another, with a hint describing it (e.g. 'mentor of'). Requires editor access or higher.

| Parameter | Type | Required | Default | Constraints |
|---|---|---|---|---|
| `project_uuid` — The project the entries belong to. | string | yes |  |  |
| `entry_uuid` — Source entry UUID. | string | yes |  |  |
| `target_uuid` — Target entry UUID. | string | yes |  |  |
| `hint` — Relationship description (max 256 chars). | string | yes |  | ≤ 256 chars |
| `expected_entry_generation` — Source entry's generation from your last read/write; omit to skip the CAS check. | integer | null | no | `null` |  |
<!-- END GENERATED TOOL REFERENCE -->

## Pagination

List-style tools (`list_conversations`, `read_recent_messages`) paginate with
**opaque cursor tokens**. The response carries `next_cursor`; pass it back on
the next call to get the next page. `next_cursor: null` means you have reached
the end. The tokens are encrypted and scoped to the resource they were minted
for — treat them as opaque strings, and never construct or modify them
yourself. (This is deliberately friendlier to LLM callers than offset paging:
there is nothing to interpret or miscount.)

## Errors

Tool failures return tool results with `isError: true` whose text is a JSON
error envelope — **the same shape the REST v1 API returns**, so one parser
covers both surfaces:

```json
{
  "error": {
    "type": "permission_error",
    "code": "",
    "message": "Token does not have access to project <uuid>."
  }
}
```

| `type` | Meaning |
|---|---|
| `authentication_error` | missing or invalid Bearer token |
| `permission_error` | token valid, but no access to that project / tier |
| `not_found_error` | conversation, entry, or resource does not exist (or is deleted) |
| `invalid_request_error` | malformed arguments, invalid UUIDs, invalid cursor (`code: "invalid_cursor"`), unsupported entry type |
| `conflict` (`invalid_request_error`) | generation mismatch (concurrent write) or duplicate resource; `details` carries the structured body |
| `autopoetic_error` | internal error or service temporarily unavailable (`code: "not_primary"`) |

Optional `param` and `details` fields appear when relevant (e.g. per-field
validation errors, or the conflicting generations on a CAS failure).

## Concurrent-write protection (CAS)

Knowledge tools accept an optional `expected_entry_generation` — the
generation you observed on your last read or write of that entry. Pass it to
detect concurrent edits: if someone else wrote first you get a `conflict`
error; re-read the entry and retry. Omit it to skip the check (last write
wins). Message appends and event recordings have no CAS check — they are
app-only structures.

## Write visibility (shared overlay)

Knowledge writes (`upsert_knowledge_entry`, `delete_knowledge_entry`,
`record_event`, `create_knowledge_relationship`) stage into the project's
shared overlay. They are immediately visible to subsequent reads but are not
durably persisted until a flush is performed (by the application — the UI or
the knowledge REST API; the MCP surface has no flush tool by design).

## What is deliberately not exposed

The MCP surface is a **curated working surface**. There are no tools for
projects (create/delete/fork), members, invitations, API keys, or files:
administration and access control are human/operator territory, and an agent
managing its own credentials or membership would be a privilege-escalation
footgun. Use the REST v1 API for those operations.

## Client configuration

Claude Desktop (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "autopoetic": {
      "type": "http",
      "url": "https://app.autopoetic.ai/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}
```

opencode (`opencode.json` / `opencode.jsonc`):

```json
{
  "mcp": {
    "autopoetic": {
      "type": "remote",
      "url": "https://app.autopoetic.ai/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}
```

To explore interactively, run the
[MCP Inspector](https://github.com/modelcontextprotocol/inspector) against the
endpoint with your Bearer header — `initialize`, browse `tools/list`, and make
test calls.

## Versioning

The server advertises `version` in its `initialize` result. Changes to the
tool surface are additive: new tools and new optional parameters do not bump
the major version; removed tools or changed argument semantics will come with
a major version bump and advance notice.