Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt

Use this file to discover all available pages before exploring further.

Add persistent memory to an Anthropic Claude Agent in two ways: hooks for zero-friction automatic memory (the model never sees the plumbing), and an MCP server that exposes synap_search and synap_remember as explicit tools the model can call.

Overview

This guide shows how to add Synap to a Claude Agent SDK application to build agents that:
  • Inject relevant memories before every turn — automatically, with no tool calls
  • Record every completed turn back to Synap so memory grows with use
  • Expose explicit search/store tools the model can call mid-conversation when it needs to
The integration is available in both Python and TypeScript and ships three exports:
ExportLanguagePurpose
create_synap_hooksPython + TypeScriptHooks for automatic context injection and turn recording
create_synap_mcp_serverPython + TypeScriptMCP server exposing synap_search and synap_remember as tools
build_synap_toolsTypeScript onlyRaw tool definitions for manual composition
The TypeScript package wraps the JavaScript SDK, which wraps the Python SDK as a subprocess. The TypeScript install requires a Python 3.11+ runtime on the host. Edge Runtime, Cloudflare Workers, Bun, Deno Deploy, and AWS Lambda Node-only runtimes are not supported. See Installation → JavaScript / TypeScript SDK. The Python install has no such constraint.

Setup

pip install maximem-synap-claude-agent
Configure your API key. Generate one from the Synap Dashboard.
.env
SYNAP_API_KEY=synap_your_key_here
ANTHROPIC_API_KEY=your-anthropic-api-key
Initialize the SDK once at application startup:
from maximem_synap import MaximemSynapSDK

sdk = MaximemSynapSDK()
await sdk.initialize()
See SDK Initialization for the full lifecycle and configuration options.

Basic integration

The smallest useful integration uses hooks — memory injection and turn recording happen automatically, with no changes to the model’s tool surface:
import asyncio
from anthropic.claude_agent_sdk import query, ClaudeAgentOptions
from synap_claude_agent import create_synap_hooks

hooks = create_synap_hooks(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",            # optional — required for B2B instances
    conversation_id="conv-001",    # optional; auto-generated if omitted
)

async def main():
    async for message in query(
        prompt="What did I tell you about my trial account?",
        options=ClaudeAgentOptions(hooks=hooks),
    ):
        print(message)

asyncio.run(main())
The hooks intercept the agent’s lifecycle: before_query fetches Synap context and prepends it as a system message, and after_turn ingests the completed user/assistant exchange. Context fetch failures degrade gracefully — empty context is injected and the error is logged. Turn ingestion failures surface explicitly so silent data loss is impossible. For explicit memory control (the model decides when to search or store), layer in the MCP server — see below.

Core concepts

Hooks — automatic memory

create_synap_hooks returns a dict of hook callbacks that the Claude Agent SDK invokes around each turn:
HookBehavior
before_queryFetches Synap context for the incoming prompt and prepends it as a system message
after_turnIngests the full user + assistant turn back into Synap
hooks = create_synap_hooks(
    sdk=sdk,
    user_id="alice",
    customer_id="acme",
    conversation_id="conv-001",
)
The model never sees the hook plumbing — there are no tools added, no schemas to learn. This is the right primitive for production agents where memory should be omnipresent.

MCP server — explicit memory tools

When you want the model to decide when to query or store memories, register the Synap MCP server. It exposes two tools:
  • synap_search — search memories by natural-language query
  • synap_remember — store a new memory
from anthropic.claude_agent_sdk import query, ClaudeAgentOptions
from synap_claude_agent import create_synap_hooks, create_synap_mcp_server

hooks = create_synap_hooks(sdk=sdk, user_id="alice")
mcp_server = create_synap_mcp_server(sdk=sdk, user_id="alice")

async for message in query(
    prompt="Search your memory for anything about my project deadlines.",
    options=ClaudeAgentOptions(
        hooks=hooks,
        mcp_servers={"synap": mcp_server},
    ),
):
    print(message)
The MCP server can be used alone, or alongside hooks for layered memory (automatic context plus on-demand search/store).

Raw tool definitions (TypeScript)

For TypeScript users who want to compose tools manually rather than going through MCP, buildSynapTools returns raw Anthropic tool definitions:
import { buildSynapTools } from "@maximem/synap-claude-agent";

const tools = buildSynapTools({ sdk, userId: "alice", customerId: "acme" });
// tools = [synapSearchTool, synapRememberTool]
Pass them directly into the agent’s tool list. The Python integration uses MCP exclusively for explicit tooling, so this export is TypeScript-only.

Complete example: agent with hooks + MCP server

The pattern below combines both primitives. Hooks provide automatic memory on every turn, and the MCP server gives the model the option to dig deeper when it decides recall is needed:
from anthropic.claude_agent_sdk import query, ClaudeAgentOptions
from synap_claude_agent import create_synap_hooks, create_synap_mcp_server


async def handle_query(sdk, user_id: str, prompt: str, customer_id: str | None = None) -> str:
    hooks = create_synap_hooks(sdk=sdk, user_id=user_id, customer_id=customer_id)
    mcp = create_synap_mcp_server(sdk=sdk, user_id=user_id, customer_id=customer_id)

    full = []
    async for message in query(
        prompt=prompt,
        options=ClaudeAgentOptions(
            hooks=hooks,
            mcp_servers={"synap": mcp},
        ),
    ):
        full.append(message)
    return "\n".join(str(m) for m in full)


# Usage
reply = await handle_query(sdk, user_id="alice", prompt="What plan am I on?", customer_id="acme")
Three things to notice in this pattern:
  1. Hooks and MCP server cover different needs. Hooks are silent and always-on; MCP tools are explicit and model-driven.
  2. They compose. Use both — the model sees relevant context every turn AND can query for more when needed.
  3. Scope is per-request. Each handle_query invocation creates its own hooks and MCP server scoped to the right user.

Advanced patterns

Hooks vs. MCP server

HooksMCP server
Context injectionAutomatic, every turnOn-demand via tool call
Memory storageAutomatic, every turnOn-demand via tool call
Model awarenessModel doesn’t see the toolsModel can decide when to search/store
Best forProduction agents where memory is always neededResearch agents where explicit memory control matters
Use both together for maximum coverage: hooks handle the always-on path, MCP tools handle the explicit path.

Multi-tenant scoping

All three exports accept the standard scoping triple — user_id (required), optional customer_id, optional conversation_id. customer_id is required on B2B Synap instances and ignored on single-tenant ones. See Memory Scopes.
hooks = create_synap_hooks(sdk=sdk, user_id="alice", customer_id="acme")
For multi-tenant services, construct hooks/MCP per request rather than caching them globally.

Failure semantics

The integration follows the Synap-wide contract:
  • before_query (hooks) degrades gracefully — empty context on failure, error logged.
  • after_turn (hooks) surfaces failures — turn ingestion raises SynapIntegrationError.
  • synap_search (MCP) degrades gracefully — returns [] on failure.
  • synap_remember (MCP) surfaces failures — raises SynapIntegrationError.
This is by design: read failures shouldn’t break a user-facing turn, but silent write failures would let the memory drift away from reality.

Next steps

Vercel AI SDK

Middleware for any Vercel AI SDK model.

Mastra

SynapMemory for the Mastra ADK.

Context Fetch

The retrieval API behind hooks and synap_search — modes, scopes, and response shapes.

Memory Scopes

How user_id, customer_id, and conversation_id interact across reads.