# Connect with MCP

```json metadata
{
  "schema_version": 2,
  "title": "Connect with MCP",
  "description": "Connect an MCP client to a Redis Agent Memory store to search, curate, and ingest long-term memories.",
  "categories": ["docs","develop","ai"],
  "tableOfContents": {"sections":[{"id":"before-you-begin","title":"Before you begin"},{"id":"configure-the-connection","title":"Configure the connection"},{"id":"use-the-tools","title":"Use the tools"},{"id":"connect-a-client","title":"Connect a client"},{"id":"troubleshoot-the-connection","title":"Troubleshoot the connection"}]}

,
  "codeExamples": [{"codetabsId":"iris_agent_memory_mcp_client-stepagent-memory-mcp-client","difficulty":"beginner","id":"agent-memory-mcp-client","languages":[{"clientId":"iris-python","clientName":"Python (Iris)","id":"Python (Iris)","langId":"python","panelId":"panel_Python (Iris)_iris_agent_memory_mcp_client-stepagent-memory-mcp-client"},{"clientId":"iris-curl","clientName":"cURL (Iris)","id":"cURL (Iris)","langId":"bash","panelId":"panel_cURL (Iris)_iris_agent_memory_mcp_client-stepagent-memory-mcp-client"}]}]
}
```

## Code examples

These examples connect to Iris services using SDKs or HTTP requests with curl. Follow one language throughout each walkthrough.


Every Redis Agent Memory store serves a [Model Context Protocol](https://modelcontextprotocol.io) (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](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/sessions) 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](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/quickstart#connect-to-the-service).

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

**Python**

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

**HTTP**

```bash
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](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/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](https://redis.io/docs/latest/develop/ai/context-engine/agent-memory/memory-etl) 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.

**Available in:** Python (Iris), cURL (Iris)

##### Python (Iris)

```python
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())
```

##### cURL (Iris)

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

