---
title: State & memory
description: The persistence every Guuey agent gets — managed conversation history, a key-value store scoped per user, a durable per-user home directory, and a consent-gated cross-app profile.
---

Guuey gives agents and MCP servers a small, deliberate set of persistence primitives. Each is scoped tightly — per user, per app — and each has hard caps. The limits are the point: your server stays stateless from its own point of view, and your users keep ownership of their data.

:::note
Durable state is rolling out per environment while Guuey is in early preview. This applies to the hosted key-value binding, the durable home directory, and the cross-app profile below. The APIs are stable — code you write against them today keeps working unchanged as the rollout reaches you.
:::

## Conversation history

Every conversation with a hosted agent is persisted automatically by the platform. Users reconnect and the thread is there. Don't store chat history in any of the primitives below — that job is already done.

## Small state: `@guuey/state`

A key-value store scoped to `(user, MCP server)`. It's built for the state most MCP servers actually need: idempotency tokens, rate-limit counters, OAuth nonces, small per-user preferences.

```ts
import { createGuueyState } from "@guuey/state";

const kv = createGuueyState({
  context: { userId: "u_abc", mcpId: "mcp_xyz" },
});

await kv.set("user-prefs", { theme: "dark" }, { ttl: 60 * 60 * 24 * 7 });
const prefs = await kv.get<{ theme: string }>("user-prefs");
```

In production you don't pass the context by hand — it's derived from the signed request Guuey sends your server. See the [package README](https://www.npmjs.com/package/@guuey/state) for the per-request wiring.

The caps are enforced everywhere, and they are the product:

- 1 MiB per `(user, server)` scope; 64 KiB per value.
- Every write needs a TTL — no permanent keys; 90 days max.
- KV only: no queries, joins, or transactions.
- Strict isolation: one MCP server can never read another server's keys, even for the same user.

Without a hosted binding in your environment, the same API runs in-memory — exactly right for tests and `guuey dev`, non-durable by design. If you outgrow the caps, that's the signal to move the data to a real backend and call it from your MCP server.

## A home directory per user

Every hosted agent runs with three directories bound in, used with plain `node:fs` — the `@guuey/fs` helpers just tell you where they are:

| Helper         | What it is                                 | Lifetime                                     |
| -------------- | ------------------------------------------ | -------------------------------------------- |
| `homeDir()`    | Read-write, per (app, user)                | Durable — survives restarts and new sessions |
| `appDir()`     | Read-only files that ship with the app     | Same for every user                          |
| `sessionDir()` | Read-write scratch (the working directory) | This session only                            |

The durable home is what makes "my agent remembers me" work: files written for a signed-in user today are there next week, from a fresh pod. The convention is a `memories/MEMORY.md` file in the home directory — the platform reads it before each turn and injects it into the model's context for you, identically on all three frameworks. Your agent can also save memories mid-conversation; the platform tells it how.

Anonymous guests get a real, writable home too — but it's deliberately ephemeral. Guests never accumulate durable storage; don't build a feature that assumes otherwise.

## Cross-app profile

A consent-gated user profile that can follow a user between apps — rolling out now.

- Your app opts in by declaring `profileAccess: "read"` or `"read-write"` in `guuey.json`. No declaration, no access, and no prompt — ever.
- The first time your app wants the profile in a conversation, the user sees a consent card in chat and chooses: always, only this conversation, or deny. Grants can be reviewed and revoked later from their Guuey settings.
- Each app writes only its own section of the profile — one app structurally cannot touch another's contribution.
- Signed-in users only, and every unclear case fails closed: without consent the profile is simply absent and the conversation continues normally.

## Your users own their data

State is visible to the people it's about: users can see and delete what an app has stored about them. Design your server so that a user clearing their data is a fresh start, never an error.