Skip to main content

Connect an agent

Every agent connects the same way: one URL and one API key.

MCP endpointhttps://api.memoryrelay.net/mcp (Streamable HTTP)
AuthenticationAuthorization: Bearer mem_… or X-API-Key: mem_…
REST APIhttps://api.memoryrelay.net with the same key

Give each agent its own key under Connected agents in the app. You pick what the key may do and which workspaces it may touch; after the key is created, the page shows the setup below with copy buttons, already filled in. The secret is shown once, so set it where the agent runs:

export MEMORYRELAY_API_KEY=mem_prod_xxxxx

Every snippet on this page reads the key from MEMORYRELAY_API_KEY, so the config files hold no secret and can be committed.

ICM workspaces are in early access

The memory tools work for every key. The icm_* tools work on any workspace the key is confined to while that workspace is switched on; a workspace the operator has switched off answers that ICM is unavailable rather than falling back to memory search.

Claude Code​

Add it once per machine:

claude mcp add --transport http memoryrelay https://api.memoryrelay.net/mcp \
--header "Authorization: Bearer $MEMORYRELAY_API_KEY"

Or commit a .mcp.json at the root of the repository. Claude Code expands ${MEMORYRELAY_API_KEY} from the environment:

{
"mcpServers": {
"memoryrelay": {
"type": "http",
"url": "https://api.memoryrelay.net/mcp",
"headers": { "Authorization": "Bearer ${MEMORYRELAY_API_KEY}" }
}
}
}

The Claude Code plugin​

The memoryrelay plugin adds the same server plus the habits that make it useful in every repository, so no CLAUDE.md has to explain them:

  • Session start: the agent is told the repository (owner/name from git remote get-url origin) and the step, if one is known (a step in the directory's .memoryrelay.json, else the branch prefix: fix/ → fix, feat/ → implement, release/, review/). It calls icm_context_for; when that answers no_binding it looks the repository up with icm_root and icm_route_list and builds a route with icm_context_build. Then it searches the repository's memories with memory_search.
  • Stop: once per context receipt that has no report, the agent is asked to call icm_report_reads (the supplied files it read) and, when the task has ended, icm_report_outcome (success, partial, failed or abandoned, with the pull request as evidence).
  • After a compaction: the agent stores durable findings with memory_store, under the repository's namespace and tagged with it.
  • Privacy filter: a memory_store or memory_update whose content looks like a password, key, token, private key or personal identifier is refused before it leaves the machine.
  • Commands: /memoryrelay:memory-search, memory-store, memory-list, memory-get, memory-update, memory-delete, memory-entity, memory-agent, memory-health, icm-context, icm-search and icm-report, each calling the tool of the same purpose.

Install it from the MemoryRelay marketplace (the plugin's source is plugins/memoryrelay in the API repository):

claude plugin marketplace add memoryrelay/memoryrelay-marketplace
claude plugin install memoryrelay@memoryrelay

or, inside a session, /plugin marketplace add memoryrelay/memoryrelay-marketplace and /plugin install memoryrelay@memoryrelay. To try it from a clone of the API repository instead: claude --plugin-dir plugins/memoryrelay.

It needs MEMORYRELAY_API_KEY set where Claude Code starts, and Node.js on the PATH (the hooks are Node scripts, so they run the same on Windows, macOS and Linux). Give the key icm:context, icm:observe, read and write; without icm:context the agent skips pinned context and says so once. If you already added the server with claude mcp add, Claude Code connects once and uses your entry; the plugin's hooks work with either.

The plugin replaces memoryrelay/claude-plugin, whose hooks called tools the API no longer serves. The marketplace entry keeps its name, so an existing memoryrelay@memoryrelay install moves to this source when you refresh the marketplace (/plugin marketplace update memoryrelay) and update the plugin; a copy installed from memoryrelay/claude-plugin by hand should be removed.

Codex​

In ~/.codex/config.toml (or the project's .codex/config.toml):

[mcp_servers.memoryrelay]
url = "https://api.memoryrelay.net/mcp"
bearer_token_env_var = "MEMORYRELAY_API_KEY"

This needs a Codex CLI that supports Streamable HTTP servers; older releases also need experimental_use_rmcp_client = true at the top of the file.

Any other MCP client​

Point any client that speaks MCP over Streamable HTTP at https://api.memoryrelay.net/mcp with the Authorization: Bearer (or X-API-Key) header. The server is stateless: it issues no session id, answers every request with a single JSON body, and answers GET /mcp with 405, which tells a client there is no server-initiated stream.

Fewer tools: ?tools=​

An agent that only needs pinned context does not need the memory tools in its tool list. Add ?tools= to the URL with one or more groups, comma-separated:

ValueTools
icmthe icm_* tools
corethe memory, entity and agent tools
contextcontext_build_v2
all, or no tools=everything (the default)

For example https://api.memoryrelay.net/mcp?tools=icm. The filter narrows both the tool list and what can be called: calling a tool outside it gets the same "Unknown tool" error as a tool that does not exist. It never adds a right: each call is still decided by the key's scopes and workspaces. An unknown group name is refused with 400 rather than leaving the agent with no tools. The groups are the npm package's MEMORYRELAY_TOOLS groups.

To run the server locally over stdio instead (for example in Claude Desktop):

{
"mcpServers": {
"memoryrelay": {
"command": "npx",
"args": ["-y", "@memoryrelay/mcp-server"],
"env": { "MEMORYRELAY_API_KEY": "mem_prod_xxxxx" }
}
}
}
claude.ai and Claude Desktop connectors

Connectors added through claude.ai or Claude Desktop's connector settings sign in with OAuth. MemoryRelay does not offer OAuth yet, so those connectors cannot use the remote endpoint; use the local npx configuration above in Claude Desktop meanwhile.

REST and the Python SDK​

curl -H "Authorization: Bearer $MEMORYRELAY_API_KEY" \
https://api.memoryrelay.net/v2/icm/workspaces
# pip install memoryrelay
from memoryrelay import MemoryRelay

client = MemoryRelay() # reads MEMORYRELAY_API_KEY
print(client.icm.list_workspaces())

What the agent can do​

The remote endpoint and the npm package serve the same tools. A tool call is decided exactly as the matching REST call: the key's scopes, the workspaces it was given, and its role there (a key is at most a contributor). A refusal names the missing scope; it is recorded in the workspace's activity like any other.

ToolsScopeWhat for
icm_context_for, icm_resolveicm:contextThe context bound to this repo and step, pinned to a version
icm_rooticm:contextWhat your workspaces include and what is bound; a table, never a fallback
icm_workspace_list, icm_route_list, icm_context_build, icm_release_get, icm_receipt_get, icm_source_scan, icm_score, icm_maintenanceicm:contextBuild context for an entry, route, stage, record or notes; read the repository scan, the scorecard and what needs re-verifying
icm_searchany icm:*Find which sections of your workspaces' files answer a question: ranked, with the path and lines to read next. It sees only what the key could read file by file
icm_pullany icm:*The command that writes a workspace to a folder (Use a workspace as a folder)
icm_run_start, icm_stage_context, icm_stage_write, icm_stage_submit, icm_run_statusicm:runDo a stage of a run and hand it to a person
icm_report_readsicm:observeReport which supplied files the agent read
icm_draft_get, icm_draft_write, icm_draft_proposeicm:run, a key confined to the workspaceChange a workspace's files as a draft and propose it: a person publishes it, or merges its pull request on a Git workspace (Work on a Git workspace)
memory_search, memory_store, memory_list, memory_get, …read, write, deleteMemory as evidence

A run, as an agent does it​

  1. icm_run_start {workspace_id, name} pins a run to the live version.
  2. icm_stage_context {workspace_id, run_id, stage} starts the stage and returns its contract and its context: the entry, the stage's contract, its inputs and the outputs earlier stages had approved. It also lists the output paths.
  3. icm_stage_write {…, path, content} saves each output. Writing over an output needs the revision you last saw, so an output a person edited is never overwritten blindly.
  4. icm_report_reads {…, receipt_id, paths} records what the agent actually read.
  5. icm_stage_submit {…, stage} hands the stage to a person and returns the review_url.

Approval stays with people. No tool approves, sends back or publishes, and the API refuses a key that tries. A person reads the output at the review link, edits it in place if needed, and approves or sends it back with a note; the next stage can only start after that. When a stage comes back, icm_stage_context starts it again and returns the reviewer's note.

Keys and sessions​

The MCP endpoint accepts API keys only. A signed-in session token is refused, because an agent holding it would act as the person, and approving or publishing must stay with that person.

What a key can see about itself​

GET /v2/icm/capabilities (and the icm_capabilities tool) includes a principal block describing the caller, never another key and never a secret:

{
"principal": {
"kind": "api_key",
"key_id": "5d0e…",
"name": "Command Center",
"scopes": ["icm:context", "icm:observe", "read"],
"icm_confined": true,
"expires_at": "2027-03-01T00:00:00+00:00",
"workspace_ids": ["9b1c…"]
}
}

workspace_ids are the workspaces granted to the key itself. For a person, kind is person, and key_id, name and expires_at are null. A client can use it to warn when its key is broader than it needs (write, admin, memory scopes, not confined, no expiry). A key confined to its workspaces cannot create a workspace (403 key_confined): a person creates it and grants the key.

Some things are always a person's to do in MemoryRelay: creating a workspace, connecting its repository, making it ICM-ready, granting a key. An app that integrates MemoryRelay (Command Center, for example) links the person to a prefilled form instead of doing it. Opening a link changes nothing; the person reviews the form and presses the button. A parameter that does not pass its check (repository owner/name, branch and folder characters, template slug, key id) is ignored.

Link (on https://app.memoryrelay.ai)Opens
/dashboard/workspaces/new?name=&authority=git&repository=owner/name&ref=&path=&template=<slug>&form=New workspace, prefilled; after Create, Source carries the branch and folder
/dashboard/w/{workspace_id}/source?setup=1&repository=&ref=&path=Source settings, prefilled when no repository is connected yet
/dashboard/w/{workspace_id}/source?make_ready=1&template=<slug>Make it ICM-ready, with that template preselected (people with the publisher role)
/dashboard/w/{workspace_id}/members?grant_key=<key_id>Members, with that key preselected in the grant form (role contributor by default)

The app then reads the result back with its own key: the workspace appears in principal.workspace_ids once it is granted.

Observations are idempotent​

POST /v2/icm/workspaces/{id}/receipts/{receipt_id}/observations takes an event_id (1 to 128 characters) that is unique per receipt. Retrying with the same event_id writes nothing and answers 200 with the observation already stored (recorded: false, the same observation_id); the first answer is 201 with recorded: true. Use a stable id such as <run id>:<delivery id>.