MCP
xmemory exposes a Model Context Protocol server over Streamable HTTP. Any MCP-compatible client — Claude Desktop, Cursor, Windsurf, pydantic-ai, LangChain, Mastra, or a plain SDK call — can connect and get access to xmemory’s read and write tools with no custom code.
The MCP server supports two authentication paths:
- OAuth2 flow for interactive browser-based connectors.
- Direct fixed API-key flow for headless agents, CI, evals, and scripts.
Connecting a specific client? See the step-by-step guides for Claude (claude.ai, Desktop, and Claude Code), ChatGPT, Codex, and n8n. Any other MCP client (Cursor, Windsurf, VS Code, and other popular tools) connects with the same URL and Bearer token — see Authentication and Connection below.
Adding xmemory as a connector
Section titled “Adding xmemory as a connector”xmemory connects to any MCP-compatible client as a remote (Streamable HTTP) server at https://mcp.xmemory.ai/ — no local install or custom code. Most clients add it through their built-in connector or MCP-server settings.
- In your client, add a new custom connector / MCP server with the URL
https://mcp.xmemory.ai/. - Authenticate — either interactively (OAuth2), where you pick a connection type (instance or admin) on the xmemory connect page, or headless, by pointing at a qualified shortcut URL and sending your
xmem_...API key as a Bearer token. See Authentication below for exact tokens and headers. - Approve the connection — xmemory’s read and write tools then appear in the client.
Authentication
Section titled “Authentication”API key: To use xmemory APIs or integrations (including MCP), you need an API key. Get one from the xmemory console. Copy and securely store the key. Never share your API key publicly.
Option 1: OAuth2 (interactive)
Section titled “Option 1: OAuth2 (interactive)”Point your client to https://mcp.xmemory.ai/ and complete the OAuth2 login flow. The client receives an opaque MCP token (format: xmem_mcp_...) that it sends as the Authorization: Bearer token on every request. For OAuth-issued sessions, binding metadata (instance/admin/status) is stored server-side.
Option 2: Direct fixed API key (headless)
Section titled “Option 2: Direct fixed API key (headless)”Headless clients can skip OAuth and send an account API key directly as the Bearer token on MCP shortcut paths:
Authorization: Bearer <your xmemory API key>The session type is determined by the URL path, so a direct API-key request must target a qualified shortcut path (the bare root https://mcp.xmemory.ai/ is for OAuth only):
/instance/<instance_id>— instance tools bound to that instance./admin— global admin (fleet) tools. Admin is global-only;/admin/<instance_id>is not a recognized shortcut./status— status-only tool.
The API key is revalidated on every request.
An example of adding an xmemory instance to Claude Code using the direct API key method:
claude mcp add --transport http xmemory https://mcp.xmemory.ai/instance/<instance_id> --header "Authorization: Bearer <xmemory API key>"Connection
Section titled “Connection”Clients that use a config file — e.g. .mcp.json — expect an entry like:
{ "mcpServers": { "xmemory": { "url": "https://mcp.xmemory.ai/instance/<instance_id>", "headers": { "Authorization": "Bearer <token>" } } }}The url and token depend on which authentication path you use:
| Auth path | url |
Bearer token |
|---|---|---|
| OAuth2 (interactive) | https://mcp.xmemory.ai/ |
<xmemory_mcp_token> |
| Direct API key (instance) | https://mcp.xmemory.ai/instance/<instance_id> |
<xmemory_api_key> |
| Direct API key (admin) | https://mcp.xmemory.ai/admin |
<xmemory_api_key> |
For framework-specific setup, see the Example integrations guides in the sidebar — Pydantic AI, LangChain, Google ADK, and more. For chat apps and CLIs, see the Claude and ChatGPT guides.
The xmemory MCP server exposes 9 default tools to instance connections, all operating on the instance bound at login, plus an opt-in Schema management group (5 more) the instance owner can grant on the connect page.
For direct API-key usage, /instance/<instance_id> creates the same instance-scoped tool surface, while /admin switches to the global admin (fleet) tools. Admin is global-only — it owns no per-instance state, so /admin/<instance_id> is not a recognized shortcut; a specific instance’s schema is managed from that instance’s own connection.
Tool descriptions are dynamic — on each list_tools() call, the server fetches your instance’s schema and appends a summary of its object types and relations to each tool description. This means the LLM sees tool descriptions tailored to your specific instance, making it more likely to use the tools correctly.
Tool responses. There are two response families. Instance (bound) tools — read, write, the schema tools, etc. — return a flat JSON object with a top-level status ("ok" or "error") and the payload keys inline (no wrapper); get_instance_id is the one exception and returns a bare ID string. Admin tools instead return a JSON envelope — the parts that matter are items (the result; a single record is items[0]) and errors (populated on failure). Both families also carry a console_url linking to that operation in the console. Each tool below documents its own payload.
Tools
get_instance_idReturn the instance ID bound to the current session
Returns
A bare instance-ID string (32-char UUID hex, e.g. a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6) — not a JSON envelope.
Details
Useful for display, logging, or confirming which instance the agent is operating on.
get_instance_schemaReturn the bound instance's schema (object types, fields, relations, deduplication keys)
Returns
A flat object { status, data_schema } — data_schema holds the instance schema.
Details
The LLM can call this to understand what kinds of data the instance stores, which helps it formulate better write and read calls.
writeExtract structured entities from free-form text and persist them (synchronous — blocks until fully committed)
Parameters
textstringrequiredsession_idstring | nullclaude-qwxhjkmrtz).Returns
A flat object { status, write_id, cleaned_objects: { objects, relations }, changes: { created, updated, deleted }, trace_id, console_url }. changes is the per-object breakdown the diff engine applied; console_url links to the run in the console.
Example response
{ "status": "ok", "write_id": "<uuid>", "cleaned_objects": { "objects": [], "relations": [] }, "changes": { "created": {}, "updated": {}, "deleted": {} }, "trace_id": "...", "console_url": "https://console.xmemory.ai/..."}Details
Internally, the server runs a two-phase pipeline: an LLM extracts structured objects according to your instance's schema, then a diff engine compares them against existing data and applies inserts, updates, and deletes.
Because write blocks until committed, you can call read immediately after and get consistent results.
write_asyncSame as `write`, but enqueues the operation and returns immediately with a `write_id`
Parameters
textstringrequiredsession_idstring | nullReturns
A flat object { status, write_id, trace_id, console_url }. Poll completion with write_status.
Details
Important: do not call read immediately after write_async — the data may not be committed yet. Use write_status to poll, or use write (synchronous) when you need to read right after.
write_statusCheck the status of an async write previously submitted via `write_async`
Parameters
write_idstringrequiredwrite_async.Returns
A flat object { write_id, write_status, error_detail, error, completed_at, trace_id, console_url, status }. The lifecycle state is in write_status (see below); status is the call's own ok/error.
Details
write_status |
Meaning |
|---|---|
queued |
Waiting to be picked up |
processing |
Currently being extracted and applied |
extracting · extracted · applying |
Intermediate states of the two-phase pipeline |
completed |
Successfully committed — safe to read |
failed |
Extraction or persistence failed; see error_detail |
not_found |
No write with this ID exists |
Treat anything other than completed / failed / not_found as still in flight.
readQuery the instance and return a natural-language answer
Parameters
querystringrequiredsession_idstring | nullReturns
A flat object { status, reader_result, sql, pending_suggestions, trace_id, console_url }. The natural-language answer is at reader_result.answer.
Example response
{ "status": "ok", "reader_result": { "answer": "Bob Lee joined last Monday." }, "sql": null, "pending_suggestions": 0, "trace_id": "...", "console_url": "https://console.xmemory.ai/..."}Details
Internally, the server translates the question into SQL against the instance's knowledge graph, executes it with automatic retry and empty-result verification, and formats the result into a plain-text answer.
Schema evolution (suggestion engine)
Section titled “Schema evolution (suggestion engine)”xmemory learns from reads that couldn’t be fully answered and, on demand,
surfaces a single rolling proposal of schema improvements for the bound
instance. The flow is three tools — review → decide → apply — and a change
is only applied when you call apply_pending_decisions.
Tools
review_suggestionsReturn the consolidated proposal and a `proposal_version` token
Parameters
session_idstring | nullReturns
A flat object { status, instance_id, proposal, retry_after_seconds }, where proposal = { proposal_version, schema_version, items }. When a migration is already running, status is evolution_in_progress and retry_after_seconds is set.
decide_suggestionsRecord an `accept` / `reject` / `defer` per item, in bulk
Parameters
proposal_versionstringrequiredreview_suggestions.decisionsarrayrequired{ "item_fingerprint": "...", "decision": "accept" | "reject" | "defer", "edits"?: {...} }.session_idstring | nullReturns
A flat object { status, decisions_recorded, warnings, next_proposal_version }. Pass next_proposal_version to apply_pending_decisions.
apply_pending_decisionsCommit accepted decisions as one migration
Parameters
proposal_versionstringrequirednext_proposal_version from decide_suggestions).session_idstring | nullReturns
A flat object { status, migration_id, prior_version, new_version, applied_items, summary }. status is nothing_to_apply when no accepted items remained.
Always confirm with the user before deciding or applying. Rejecting an item suppresses that exact suggestion in future proposals.
Schema management (opt-in)
Section titled “Schema management (opt-in)”A separate, opt-in permission group lets an instance connection own its schema lifecycle directly — distinct from the suggestion engine above. These tools default off and are exposed only when the Schema management permission is granted on the OAuth connect page, so headless direct-key bindings can’t reach them. Schema dry-runs and migration history now live here: admin connections are global-only and no longer edit a specific instance’s schema.
A bound instance already has a schema, so it evolves that schema with enhance_schema — there is no per-instance generate_schema (generating a fresh schema from scratch is a new-instance concern that lives only on the admin connection, as admin_generate_schema).
Tools
enhance_schemaLLM: improve the bound instance's schema — returns the new YAML and a migration plan; does not apply it
Parameters
schema_descriptionstringrequiredschema_to_improvestringrequiredget_instance_schema).session_idstring | nullReturns
A flat object { status, generated_schema, migration_plan, summary, warnings, repair_log } — the improved YAML and a structured migration plan for non-additive changes. Apply it with update_instance_schema.
update_instance_schemaReplace the bound instance's schema, applied as a migration
Parameters
schema_ymlstringschema_yml / schema_json).schema_jsonstringschema_yml / schema_json).migration_planobjectenhance_schema) for non-additive changes.confirm_destructivebooleantrue to acknowledge ops that drop data. Defaults to false.session_idstring | nullReturns
A flat object { status, migration_id, prior_version, new_version, statements_executed } when a migration ran.
dry_run_schema_migrationPreview the DDL `update_instance_schema` would run, without applying it
Parameters
schema_ymlstringschema_yml / schema_json).schema_jsonstringschema_yml / schema_json).migration_planobjectupdate_instance_schema.confirm_destructivebooleantrue to preview destructive ops without the gate rejecting. Defaults to false.session_idstring | nullReturns
A flat object { status, current_version, statements, plan_summary, warnings } (statements are the planned DDL).
list_schema_migrationsList the bound instance's applied migrations, newest first
Parameters
limitinteger50.before_idstringinclude_yamlbooleanfalse.session_idstring | nullReturns
A flat object { status, items, next_before_id, has_more }; each record in items = { id, applied_at, source, prior_version, new_version, ops, ops_summary }.
get_schema_migrationGet one applied-migration record of the bound instance by ID
Parameters
migration_idstringrequiredlist_schema_migrations).include_yamlbooleanfalse.session_idstring | nullReturns
A flat object { status, record }, the migration record { id, applied_at, source, prior_version, new_version, ops, ops_summary, yaml_before?, yaml_after? }.
Important: never hand-write YAML schemas. Use
enhance_schemato evolve the instance’s schema, preview withdry_run_schema_migration, then apply withupdate_instance_schema.
Admin tools
Section titled “Admin tools”Selecting the admin connection type at login switches the tool surface to the global fleet management tools, all prefixed admin_. Admin operates across your whole fleet rather than a single instance, so it has no bound-instance state. To edit a specific instance’s schema, use that instance’s own connection and its Schema management group (above); admin’s by-id schema tool (admin_get_instance_schema_by_id) is read-only.
Important: never hand-write YAML schemas. Always use
admin_generate_schemaoradmin_enhance_schemato produce a valid schema, then pass the result toadmin_create_instance.
Stateless schema helpers
Tools
admin_generate_schemaGenerate a YAML schema from a free-form description
Parameters
schema_descriptionstringrequiredcluster_idstringsession_idstring | nullReturns
items[0] = { data_schema } (the generated schema).
admin_enhance_schemaImprove an existing YAML schema
Parameters
schema_descriptionstringrequiredschema_to_improvestringrequiredcluster_idstringsession_idstring | nullReturns
items[0] = { data_schema, migration_plan, summary, warnings, repair_log } — the enhanced schema and a structured migration plan for applying non-additive changes safely.
Instance lifecycle
Tools
admin_create_instanceCreate a new empty instance from a YAML schema
Parameters
schema_yamlstringrequiredadmin_generate_schema / admin_enhance_schema).cluster_idstringnamestringcluster_id is provided.descriptionstringsession_idstring | nullReturns
items[0] = { instance_id } (the new instance). Bind to it via a fresh /instance/<instance_id> connection.
Cluster-scoped management
Tools
admin_list_clustersList clusters accessible to the API key
Returns
items = clusters, each { id, org_id, name, description }.
admin_get_clusterGet a single cluster by ID
Parameters
cluster_idstringrequiredReturns
items[0] = a cluster { id, org_id, name, description }.
admin_list_instancesList instances in a cluster
Parameters
cluster_idstringrequiredverbosebooleanfalse, return instance IDs only. Defaults to true.Returns
items = instances in the cluster.
admin_get_instanceGet instance metadata within a cluster
Parameters
cluster_idstringrequiredinstance_idstringrequiredReturns
items[0] = the instance { id, cluster_id, name, description, data_schema, ... }.
admin_delete_instanceDelete an instance within a cluster
Parameters
cluster_idstringrequiredinstance_idstringrequiredReturns
ids = the deleted instance ID.
admin_update_instance_metadataReplace an instance's name/description within a cluster
Parameters
cluster_idstringrequiredinstance_idstringrequirednamestringrequireddescriptionstring | nullnull to clear).Returns
items[0] = the updated instance.
admin_patch_instance_metadataPartially update an instance's metadata within a cluster
Parameters
cluster_idstringrequiredinstance_idstringrequirednamestringdescriptionstringReturns
items[0] = the updated instance.
Instance-scoped management (no cluster_id needed)
Tools
admin_list_own_instancesList all instances across linked clusters
Parameters
verbosebooleanfalse, return instance IDs only. Defaults to true.Returns
items = instances across every cluster the API key can reach.
admin_get_instance_by_idGet instance metadata by instance ID
Parameters
instance_idstringrequiredReturns
items[0] = the instance.
admin_delete_instance_by_idDelete an instance by instance ID
Parameters
instance_idstringrequiredReturns
ids = the deleted instance ID.
admin_get_instance_schema_by_idGet an instance's schema by instance ID
Parameters
instance_idstringrequiredReturns
items[0] = the instance schema.
admin_update_instance_metadata_by_idReplace an instance's name/description by instance ID
Parameters
instance_idstringrequirednamestringrequireddescriptionstringReturns
items[0] = the updated instance.
admin_patch_instance_metadata_by_idPartially update an instance's metadata by instance ID
Parameters
instance_idstringrequirednamestringdescriptionstringReturns
items[0] = the updated instance.
Sync vs async writes
Section titled “Sync vs async writes”Use write when you need to read the data back immediately — it blocks until committed, guaranteeing consistency.
Use write_async + write_status when throughput matters more than immediate consistency — the client isn’t blocked, and you can poll for completion later.
Error handling
Section titled “Error handling”Tools never raise — on failure they return a parseable JSON error instead, in one of three shapes: {"error": "<message>"} (a guard failure, e.g. no instance bound to the session), {"status": "error", "error_message": "<message>"} (the flat instance-tool response on failure), or the admin envelope’s errors array — with schema-evolution failures carrying a structured error_type (stale_proposal_version, dependency_closure_failed, destructive_confirmation_required, …). Common messages:
| Error | Cause |
|---|---|
"no instance bound to this session" |
Token is invalid or not linked to an instance |
"text size (N bytes) exceeds maximum (M bytes)" |
Write payload too large (limit: 1 MB) |
"write queue not ready" |
Background processor hasn’t started |
"write failed: <detail>" |
Extraction or persistence failure |