> ## 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.

# Mastra

> SynapMemory class and search/store tools for Mastra (TypeScript).

Add persistent, per-user memory to a Mastra agent in TypeScript. The integration ships a `MastraMemory` subclass for always-on memory and a pair of tools the model can call when it wants explicit control.

<Warning>
  `@maximem/synap-mastra` wraps the JavaScript SDK, which in turn wraps the Python SDK as a subprocess. **It 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](/setup/installation#javascript-typescript-sdk).
</Warning>

<Note>
  Runtime: Node.js 18+ only. The JS SDK wraps the Python SDK as a subprocess, so it needs Python 3.11+ on the host and does NOT run on Edge / Cloudflare Workers / Bun / Deno / Lambda-Node-only. On Next.js, pin the route to runtime = "nodejs".
</Note>

## Overview

This guide shows how to add Synap to a Mastra application to build agents that:

* Recall and persist memory automatically on every `generate` call
* Search and store memories on demand when the model decides it's relevant
* Mix and match: use the memory class alone, the tools alone, or both together

The Synap Mastra integration ships three exports: one memory class and two tool factories.

| Export            | Mastra interface        | Purpose                                                        |
| ----------------- | ----------------------- | -------------------------------------------------------------- |
| `SynapMemory`     | `MastraMemory` subclass | Always-on memory: auto-recall + auto-store on every `generate` |
| `synapSearchTool` | Tool factory            | Returns a Mastra-compatible tool for explicit memory search    |
| `synapStoreTool`  | Tool factory            | Returns a Mastra-compatible tool for explicit memory storage   |

## Setup

Install the package alongside Mastra:

```bash theme={null}
npm install @maximem/synap-mastra @maximem/synap-js-sdk @mastra/core zod
```

<Note>
  Import the integration from its package name directly: `import { SynapMemory } from "@maximem/synap-mastra"`. The core SDK (`createClient`) comes from the separate `@maximem/synap-js-sdk` package.
</Note>

Configure your API key. Generate one from the [Synap Dashboard](https://synap.maximem.ai).

```bash .env theme={null}
SYNAP_API_KEY=synap_your_key_here
OPENAI_API_KEY=your-openai-api-key
```

Initialize the SDK once at application startup:

```typescript theme={null}
import { createClient } from "@maximem/synap-js-sdk";

const sdk = createClient();
await sdk.init();
```

See [SDK Initialization](/sdk/initialization) for the full lifecycle and configuration options.

## Basic integration

The smallest useful integration plugs `SynapMemory` into an `Agent`. Every `generate` call automatically pulls relevant memories into the prompt and writes the new turn back out:

```typescript theme={null}
// npm install @maximem/synap-mastra @maximem/synap-js-sdk @mastra/core zod
import { Agent } from "@mastra/core";
import { openai } from "@ai-sdk/openai";
import { createClient } from "@maximem/synap-js-sdk";
import { SynapMemory } from "@maximem/synap-mastra";

const sdk = createClient();
await sdk.init();

const agent = new Agent({
  name: "MemoryAgent",
  instructions: "You are an agent with persistent memory.",
  model: openai("gpt-4o"),

  memory: new SynapMemory({
    sdk,
    userId: "alice",
    customerId: "acme",   // optional, required for B2B instances
  }),
});

const result = await agent.generate("What do you remember about my project deadlines?");
console.log(result.text);
```

The scoping triple is bound when `SynapMemory` is constructed: the model never sees the user identity. **Memory reads degrade gracefully** (empty context on failure); writes raise so silent data loss is impossible.

To let the model decide *when* to recall or store (rather than running on every turn), use the tools below.

***

## Core concepts

### SynapMemory

`SynapMemory` extends Mastra's `MastraMemory` and overrides the storage layer to route through Synap:

```typescript theme={null}
import { SynapMemory } from "@maximem/synap-mastra";

const memory = new SynapMemory({
  sdk,
  userId: "alice",
  customerId: "acme",       // optional
  conversationId: crypto.randomUUID(),  // optional, scopes to a single session; must be a valid UUID
  maxResults: 8,
  mode: "fast",             // "fast" | "accurate"
});
```

The two retrieval modes trade latency against comprehensiveness:

|          | `fast`                                         | `accurate`                                              |
| -------- | ---------------------------------------------- | ------------------------------------------------------- |
| Search   | Vector + graph (no LLM subquery decomposition) | Vector + graph + LLM subquery decomposition + reranking |
| Best for | Real-time chat                                 | Multi-entity queries                                    |

`fast` is lower-latency and suited to the hot path; `accurate` adds LLM-driven query decomposition and reranking for relationship-aware queries at a higher latency cost.

Method behavior:

| Method                   | Behavior                                   |
| ------------------------ | ------------------------------------------ |
| `remember(message)`      | Ingests a message into Synap               |
| `recall(query, options)` | Semantic search; returns `MemoryMessage[]` |
| `getMessages(threadId)`  | Retrieves the message thread from Synap    |

### synapSearchTool and synapStoreTool

The tool factories return Mastra-compatible tool objects with Zod schemas. They're for agents that should decide *when* to query memory rather than running on every turn:

```typescript theme={null}
import { synapSearchTool, synapStoreTool } from "@maximem/synap-mastra";

const agent = new Agent({
  // ...
  tools: {
    synapSearch: synapSearchTool({
      sdk,
      userId: "alice",
      maxResults: 5,
      mode: "accurate",
    }),
    synapStore: synapStoreTool({ sdk, userId: "alice" }),
  },
});
```

**`synapSearchTool`** schema:

```typescript theme={null}
z.object({
  query: z.string().describe("What to search for in memory"),
  maxResults: z.number().optional().default(5),
})
```

**`synapStoreTool`** schema:

```typescript theme={null}
z.object({
  content: z.string().describe("The information to remember"),
  memoryType: z.string().optional().default("fact"),
})
```

### Memory vs. tools

|                   | `SynapMemory`                          | Tools                                  |
| ----------------- | -------------------------------------- | -------------------------------------- |
| Context injection | Automatic on every `generate`          | On-demand when model calls the tool    |
| Memory storage    | Automatic after every response         | On-demand when model calls the tool    |
| Best for          | Always-on memory for every interaction | Agents that decide when memory matters |

Use both for maximum coverage: `SynapMemory` handles the always-on path and `synapStoreTool` lets the model bookmark new information explicitly when it sees something worth remembering.

***

## Complete example: agent with memory + tools

The pattern below assembles all three exports. The agent has always-on memory via `SynapMemory` AND can call the search/store tools when it decides to:

```typescript theme={null}
import { Agent } from "@mastra/core";
import { openai } from "@ai-sdk/openai";
import {
  SynapMemory,
  synapSearchTool,
  synapStoreTool,
} from "@maximem/synap-mastra";

function buildAgent(sdk, userId: string, customerId?: string) {
  return new Agent({
    name: "PersonalAssistant",
    instructions: `You are a personal assistant with long-term memory.
Use synapSearch when you need older context not already in the prompt.
Use synapStore when the user shares a new fact, preference, or decision.`,
    model: openai("gpt-4o"),

    memory: new SynapMemory({
      sdk,
      userId,
      customerId,
      maxResults: 6,
      mode: "fast",
    }),

    tools: {
      synapSearch: synapSearchTool({ sdk, userId, customerId, maxResults: 5 }),
      synapStore: synapStoreTool({ sdk, userId, customerId }),
    },
  });
}


// Usage
const agent = buildAgent(sdk, "alice", "acme");

await agent.generate("I just upgraded to the Pro plan.");
// SynapMemory.remember persists the turn; the model may also call synapStore.

const reply = await agent.generate("What plan am I on?");
// SynapMemory.recall surfaces "Pro plan" automatically.
console.log(reply.text);  // → "You're on the Pro plan."
```

Three things to notice in this pattern:

1. **Memory and tools are complementary, not redundant.** `SynapMemory` handles the always-on baseline; tools handle the explicit "I should look this up" path.
2. **Scope is per-agent.** `buildAgent(...)` constructs a fresh agent per user, so each invocation has its scope baked in.
3. **The instructions are the policy.** Telling the model when to use `synapSearch` and `synapStore` is what produces the explicit-memory behavior.

***

## Advanced patterns

### Multi-tenant scoping

All three exports accept the standard scoping triple: `userId` (required), optional `customerId`, optional `conversationId`. `customerId` is required on B2B Synap instances and ignored on single-tenant ones. See [Memory Scopes](/concepts/memory-scopes).

```typescript theme={null}
const memory = new SynapMemory({
  sdk,
  userId: "alice",
  customerId: "acme",
});
```

For multi-tenant services, build agents per request rather than caching them; each agent should have its scope baked in.

### Choosing between memory, tools, or both

* **`SynapMemory` only**: always-on agents where every turn benefits from recall and ingestion.
* **Tools only**: agents that should be selective about memory (e.g. research agents that should only remember important findings).
* **Both**: production agents where automatic recall sets the baseline and tools let the model dig deeper or bookmark explicitly.

### Failure semantics

The integration follows the Synap-wide contract:

* **`SynapMemory.recall` degrades gracefully**: returns `[]` and logs on failure.
* **`SynapMemory.remember` surfaces failures**: raises `SynapIntegrationError`.
* **`synapSearchTool` degrades gracefully**: returns `[]` and logs on failure.
* **`synapStoreTool` 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.

***

## Going further

* [Patterns overview](/patterns/overview): reusable memory patterns across frameworks.
* [Cookbook overview](/cookbook/overview): end-to-end worked examples.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Vercel AI SDK" icon="triangle" href="/integrations/vercel-ai-sdk">
    Model middleware for the Vercel AI SDK.
  </Card>

  <Card title="Claude Agent SDK" icon="wand-magic-sparkles" href="/integrations/claude-agent">
    Hooks and MCP server for the Claude Agent SDK.
  </Card>

  <Card title="Context Fetch" icon="search" href="/sdk/context-fetch">
    The retrieval API behind `SynapMemory` and `synapSearchTool`.
  </Card>

  <Card title="Memory Scopes" icon="layer-group" href="/concepts/memory-scopes">
    How `userId`, `customerId`, and `conversationId` interact across reads.
  </Card>
</CardGroup>
