# LLMSlim + Mastra

> Long-term memory for Mastra agents as tools and an output processor, with no LLM writing your memory and nothing injected into the prompt.

The llmslim npm package is a client for an llmslim serve server. Its llmslim/mastra entry gives a Mastra agent recall_memory and remember tools, and an output processor that saves each user message and reply. Messages are stored word for word and selected at read time within a token budget, so nothing is summarized or extracted into a wrong fact. Recalled memory reaches the model as a tool result, which stays in the agent's history like any other message.

Category: Framework

## Install

```
npm install llmslim @mastra/core   # @mastra/core 1.29.1 or newer
```

The client talks to an llmslim serve server (Python, one process, one SQLite file or Postgres). Call it from your backend: an API key gives access to every user's memory.

Start the memory server:

```bash
pip install "llmslim[server]"
llmslim serve          # http://127.0.0.1:8080; set LLMSLIM_API_KEYS to serve beyond localhost
```

## How it fits

- memoryProcessor is an output processor. Once per generate() or stream() it saves the latest user message and the reply's text to llmslim serve. It saves nothing when a guardrail stopped the run, when the run was aborted or failed, or while it waits for a tool approval.
- memoryTools gives the agent recall_memory and remember. The scope comes from your code for each request, through Mastra's request context, never from a tool argument, so a model steered by injected text can't read another user's memory.
- recall_memory returns the stored messages that answer the query, whole and under their dates, within a token budget, labelled as data, not instructions.
- Neither one changes the prompt, so prompt caches keep hitting and Claude's preserved thinking stays valid. There is no input processor that injects memory.

## Example

```typescript
import { Agent } from "@mastra/core/agent";
import { MASTRA_RESOURCE_ID_KEY } from "@mastra/core/request-context";
import { LLMSlim } from "llmslim";
import { memoryProcessor, memoryTools, type MemoryOptions } from "llmslim/mastra";

const memory: MemoryOptions = {
  client: new LLMSlim(), // LLMSLIM_BASE_URL and LLMSLIM_API_KEY, or pass baseUrl and apiKey
  // Whose memory: the signed-in user, from your server, for each request.
  scope: (requestContext) => ({ userId: requestContext.get(MASTRA_RESOURCE_ID_KEY) }),
};

export const assistant = new Agent({
  id: "assistant",
  name: "Assistant",
  instructions: "You are a helpful assistant.",
  model: "anthropic/claude-haiku-5-5",
  tools: { ...memoryTools(memory) }, // recall_memory and remember
  outputProcessors: [memoryProcessor(memory)], // after any guardrails
});
```

## Options

| Option | Default | Meaning |
|---|---|---|
| `client` | `required` | An LLMSlim client: new LLMSlim({ baseUrl, apiKey }), defaults from LLMSLIM_BASE_URL and LLMSLIM_API_KEY. |
| `scope` | `none` | A function of Mastra's request context that returns the userId, agentId and runId of each request. With no id, the tools refuse and nothing is saved. |
| `userId / agentId / runId` | `none` | Fixed ids instead of scope, for an agent that serves one user. |
| `budget` | `the server's` | Most tokens recall_memory returns (memoryTools only). |
| `onError` | `console.warn` | Called when saving fails; the response is returned either way (memoryProcessor only). |

## Deployment

Run llmslim serve next to your Mastra server (Docker image or pip), with an API key and Postgres when several instances share one memory. On Mastra's server, auth.mapUserToResourceId sets MASTRA_RESOURCE_ID_KEY from the signed-in user, and a client can't override it.

## Tips

- List memoryProcessor after output processors that can stop or redact a reply: it saves what they leave.
- Let the agent call recall_memory before answering anything that may depend on earlier conversations; the tool description says so.
- Use runId to keep one conversation's memory separate when that matters.

## Limitations

- Lexical recall matches words, not meanings: forms of a word match (move, moved), but a paraphrase such as allergy against allergic can miss. The server's semantic mode (llmslim[semantic]) covers paraphrases.
- @mastra/core 1.29.1 or newer, which needs Node 22.13 or newer.
- The save is awaited, so a slow server delays the response up to the client's timeoutMs.

## FAQ

### Why no input processor that adds memory to the prompt?

Memory added to the system prompt, or as a message next to the latest user message, isn't in the history the agent resends, so the next request differs from what the model saw before: prompt caches miss, and on Claude models with thinking the reasoning can't be kept. A tool result is part of the history, so nothing changes.

### Does it call a model to build memory?

No. Messages are stored word for word and selected at read time; writes cost nothing and nothing becomes a wrong extracted fact.

## Troubleshooting

- **A tool call fails with "needs a userId, agentId or runId":** scope returned no id for this request. Set MASTRA_RESOURCE_ID_KEY in the request context (Mastra's auth does it), or pass a fixed userId.
- **LLMSlimError with status 0 and type unreachable:** The client could not reach llmslim serve. Check baseUrl (or LLMSLIM_BASE_URL) and that the server is running.

## Further reading

- [npm: llmslim](https://www.npmjs.com/package/llmslim)
- [TypeScript client on GitHub](https://github.com/Thanatos9404/llmslim/tree/main/clients/typescript)
- [llmslim serve guide](https://github.com/Thanatos9404/llmslim/blob/main/docs/SERVER.md)
- [Mastra processors docs](https://mastra.ai/docs/agents/processors)

Canonical page: https://www.llmslim.app/integrations/mastra
