# Mastra

> Give a Mastra agent a memory of each of your users, through dynamic instructions or a recall tool, with the user in the runtime context.

Give a Mastra agent a memory of each of your users. What is known goes into the agent's instructions
through its runtime context, or the agent looks it up with a tool that reads the user from that context;
either way, each exchange is saved after the agent answers.

## Install

```bash
npm install @mastra/core geniffy zod
```

```bash
pnpm add @mastra/core geniffy zod
```

```bash
bun add @mastra/core geniffy zod
```

Set `ANTHROPIC_API_KEY`, and `GENIFFY_API_KEY` from **API keys** in the Geniffy app. Any model Mastra
supports works the same way.

## Remember each user

```ts
import { Agent } from "@mastra/core/agent";
import { RuntimeContext } from "@mastra/core/runtime-context";
import { Geniffy } from "geniffy";

const geniffy = new Geniffy();                    // reads GENIFFY_API_KEY

export const assistant = new Agent({
  name: "Assistant",
  instructions: ({ runtimeContext }) => {
    const memory = runtimeContext.get("memory");
    return `You are a helpful assistant.\n\n<memory>\n${memory}\n</memory>`;
  },
  model: "anthropic/claude-opus-5-5",
});

export async function chat(userId: string, message: string) {
  const mem = geniffy.space(`user_${userId}`);
  const runtimeContext = new RuntimeContext();
  runtimeContext.set("memory", await mem.context(message));   // what bears on the message

  const result = await assistant.generate(message, { runtimeContext });
  await mem.memories.add({
    messages: [
      { role: "user", content: message },
      { role: "assistant", content: result.text },
    ],
  });
  return result.text;
}
```

The agent sees what is known about this user, each line with where it came from. When nothing is known, the
block says so in one sentence, so the agent says it doesn't know instead of guessing.

## Let the agent look things up

To let the agent decide when to look something up, give it a `recall` tool. The tool reads the user from the
runtime context, so the model never sees or chooses whose memory it reads.

```ts
import { Agent } from "@mastra/core/agent";
import { RuntimeContext } from "@mastra/core/runtime-context";
import { createTool } from "@mastra/core/tools";
import { Geniffy } from "geniffy";
import { z } from "zod";

const geniffy = new Geniffy();

const recall = createTool({
  id: "recall",
  description: "Look up what is known about the user, with where it came from.",
  inputSchema: z.object({ query: z.string().describe("What to look up") }),
  execute: async ({ context, runtimeContext }) =>
    geniffy.space(`user_${runtimeContext.get("userId")}`).context(context.query),
});

export const assistant = new Agent({
  name: "Assistant",
  instructions:
    "You are a helpful assistant. Use recall before answering anything " +
    "that depends on what the user said before.",
  model: "anthropic/claude-opus-5-5",
  tools: { recall },
});

export async function chat(userId: string, message: string) {
  const runtimeContext = new RuntimeContext();
  runtimeContext.set("userId", userId);

  const result = await assistant.generate(message, { runtimeContext });
  await geniffy.space(`user_${userId}`).memories.add({
    messages: [
      { role: "user", content: message },
      { role: "assistant", content: result.text },
    ],
  });
  return result.text;
}
```

Mastra's own memory keeps a thread going; Geniffy is what the agent knows about the user across every
thread, with where each fact came from.

Source: https://docs.geniffy.com/integrations/mastra
