State & memory
read as.mdGuuey 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.
Conversation history
Section titled “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
Section titled “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.
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 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
Section titled “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
Section titled “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"inguuey.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
Section titled “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.