Connect with MCP

Connect an MCP client to a Redis Agent Memory store to search, curate, and ingest long-term memories.

Every Redis Agent Memory store serves a Model Context Protocol (MCP) endpoint. Connect an MCP client to let an agent search and curate long-term memories and submit documents for extraction without writing API calls.

The MCP tools cover long-term memory and document ingestion. Session memory is not exposed, because the application that owns the conversation writes its events. Use the SDK or REST API for sessions.

Before you begin

Get the data-plane endpoint, store ID, and an API key with write access to the store. The MCP endpoint requires write access for every tool, including search. Save and load the IRIS_AGENT_MEMORY_* variables as shown in Save the connection values.

For the Python client, use Python 3.10 or later and install these dependencies:

Python

python -m pip install mcp==2.3.0 httpx2==2.13.1

HTTP

curl --version
jq --version

Configure the connection

Use a client that supports Streamable HTTP and custom request headers:

Setting Value
Transport Streamable HTTP
Endpoint <data-plane-base-url>/iris/data/v1/agent-memory/stores/<store-id>/mcp
Request header Authorization: Bearer <api-key>

The server is stateless and returns JSON responses, so clients do not need to keep an MCP session open between calls. The endpoint must be reachable from the client runtime. A hosted client needs an HTTPS endpoint reachable from its provider. Keep the API key in connection settings and out of agent instructions.

On a self-managed deployment with ownership enforcement, use a delegated or user-bound token as described in Authentication. The tools enforce the same ownership rules as the REST API.

Use the tools

Call tools/list on an initialized connection to get the input schemas. Tool arguments use snake_case names, such as owner_id and namespace_id, rather than the REST API's camelCase fields.

Tool Use it to
search_long_term_memory Search by text, or filter by owner_id, namespace_id, session_id, memory_type, and topics. Text results rank by similarity; filter-only results can sort by createdAt or updatedAt.
create_long_term_memories Store 1–100 new memories, each with a client-supplied id and required owner_id. An existing ID is reported in errors and left unchanged.
edit_long_term_memory Change only the provided fields of one memory. Changing text re-embeds the memory.
upsert_long_term_memory Create or fully replace one memory, including custom attributes. Omitted optional fields reset to their defaults.
delete_long_term_memories Delete 1–100 memories by ID.
create_document_ingestion Submit one base64-encoded text/plain or text/markdown document for MemoryETL extraction with an enabled extractor.
get_document_ingestion Check processing progress and source cleanup for an ingestion.
list_document_ingestion_outputs Page through the memory IDs an ingestion produced.
list_memory_sources, list_memory_source_evidence, list_source_memories Trace a memory to its sources and evidence, or list the memories linked to a source.

edit_long_term_memory and upsert_long_term_memory accept expected_revision to apply a change only when the memory's revision is current. The write tools accept idempotency_key so a retry replays the original result instead of writing twice.

Connect a client

This client connects, lists the tools, creates a memory, searches for it, and then deletes it.

Copy the complete program and save it as mcp_client.py or mcp_client.sh. Run python mcp_client.py or bash mcp_client.sh with the connection values you exported.

import asyncio
import json
import os
from uuid import uuid4

import httpx2
from mcp import Client
from mcp.client.streamable_http import streamable_http_client

OWNER_ID = "mcp-quickstart-user"


def read_result(result):
    if result.is_error:
        raise RuntimeError(f"Agent Memory tool failed: {result.content}")
    if result.structured_content is not None:
        return result.structured_content
    return json.loads(next(block.text for block in result.content if block.type == "text"))


async def main():
    url = (
        f"{os.environ['IRIS_AGENT_MEMORY_URL'].rstrip('/')}"
        f"/iris/data/v1/agent-memory/stores/{os.environ['IRIS_AGENT_MEMORY_STORE_ID']}/mcp"
    )
    memory_id = f"mcp-example-{uuid4().hex[:12]}"
    async with httpx2.AsyncClient(
        headers={"Authorization": f"Bearer {os.environ['IRIS_AGENT_MEMORY_API_KEY']}"}
    ) as http:
        async with Client(streamable_http_client(url, http_client=http)) as client:
            print("Tools:", [tool.name for tool in (await client.list_tools()).tools])

            created = await client.call_tool(
                "create_long_term_memories",
                {
                    "idempotency_key": str(uuid4()),
                    "memories": [
                        {
                            "id": memory_id,
                            "text": "The user prefers aisle seats on long flights.",
                            "owner_id": OWNER_ID,
                            "topics": ["travel"],
                        }
                    ],
                },
            )
            print(json.dumps(read_result(created), indent=2))

            found = await client.call_tool(
                "search_long_term_memory",
                {"text": "Which seat does the user like?", "owner_id": OWNER_ID, "limit": 3},
            )
            print(json.dumps(read_result(found), indent=2))

            deleted = await client.call_tool(
                "delete_long_term_memories", {"memory_ids": [memory_id]}
            )
            print(json.dumps(read_result(deleted), indent=2))


asyncio.run(main())
set -euo pipefail
MCP_URL="${IRIS_AGENT_MEMORY_URL%/}/iris/data/v1/agent-memory/stores/$IRIS_AGENT_MEMORY_STORE_ID/mcp"
PROTOCOL_VERSION=2025-11-25
OWNER_ID=mcp-quickstart-user
MEMORY_ID="mcp-example-$(openssl rand -hex 6)"

post() {
  curl --fail-with-body --silent --show-error "$MCP_URL" \
    -H "Authorization: Bearer $IRIS_AGENT_MEMORY_API_KEY" \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json, text/event-stream' \
    -H "MCP-Protocol-Version: $PROTOCOL_VERSION" \
    --data-binary "$1"
}

rpc() {
  local request response
  request=$(jq -n --arg method "$1" --argjson params "$2" --argjson id "$3" \
    '{jsonrpc: "2.0", id: $id, method: $method, params: $params}') || return $?
  response=$(post "$request") || return $?
  jq -e '
    if .error then error("MCP request failed: " + .error.message)
    elif .result.isError then error("Agent Memory tool failed: " + (.result.content | tostring))
    elif has("result") | not then error("MCP response has no result")
    else .result end
  ' <<< "$response"
}

read_result() {
  jq -e '.structuredContent // ([.content[] | select(.type == "text")][0].text | fromjson)'
}

INITIALIZED=$(rpc initialize '{
  "protocolVersion": "2025-11-25",
  "capabilities": {},
  "clientInfo": {"name": "agent-memory-curl-example", "version": "1.0.0"}
}' 1)
PROTOCOL_VERSION=$(jq -er '.protocolVersion' <<< "$INITIALIZED")
post '{"jsonrpc":"2.0","method":"notifications/initialized"}' > /dev/null
rpc tools/list '{}' 2 | jq -r '.tools[].name'

rpc tools/call "$(jq -n --arg id "$MEMORY_ID" --arg owner "$OWNER_ID" --arg key "$(uuidgen)" '{
  name: "create_long_term_memories",
  arguments: {idempotency_key: $key, memories: [{
    id: $id, text: "The user prefers aisle seats on long flights.",
    owner_id: $owner, topics: ["travel"]
  }]}
}')" 3 | read_result

rpc tools/call "$(jq -n --arg owner "$OWNER_ID" '{
  name: "search_long_term_memory",
  arguments: {text: "Which seat does the user like?", owner_id: $owner, limit: 3}
}')" 4 | read_result

rpc tools/call "$(jq -n --arg id "$MEMORY_ID" '{
  name: "delete_long_term_memories", arguments: {memory_ids: [$id]}
}')" 5 | read_result

Expect the tool names, followed by the created memory ID, a search result that contains the memory, and the deleted memory ID. A request or tool error stops the program with an error.

Troubleshoot the connection

  • If the connection fails, check the full endpoint, network reachability, and API key. A 403 means the key lacks write access to the store.
  • If a tool result has isError set, read its content before using the result. Connection and transport failures raise separately.
  • If a search returns nothing for a new memory, check owner_id and other filters. Memories created by extraction appear only after background processing.

Keep diagnostic details without logging keys or private content.

RATE THIS PAGE
Back to top ↑