Vercel AI SDK
Give an agent built on the Vercel AI SDK a memory that outlives the process: it recalls earlier decisions before answering, stores what it learned, and rates what helped so the next recall ranks it higher. Three ways in, all run live against the API (the package on 2026-10-03, the two hand-written paths on 2026-10-02):
- The package,
@mnemoverse/ai-sdk.createMnemoverseTools()returns the Mnemoverse MCP memory tools as AI SDK tools, run inside your process, onai5, 6 and 7. The recommended path. - Your own tools over REST.
remember,recallandrate, written withtool()andzod, callinghttps://core.mnemoverse.com/api/v1with your API key. For when you want to own the tool definitions, or when the model should see only your three verbs. - The MCP server over stdio.
createMCPClientfrom@ai-sdk/mcpstarts@mnemoverse/mcp-memory-serveras a subprocess. Useful when you already run MCP servers that way.
You need an API key from console.mnemoverse.com (the free tier is enough; keys start with mk_live_), Node 22 or newer (the package requires 22.12; ai 7 declares engines.node >=22), and the package of your model provider. The examples use @ai-sdk/anthropic; any provider the SDK supports goes in the same model slot.
The package: @mnemoverse/ai-sdk
npm install @mnemoverse/ai-sdk ai@^7 @ai-sdk/mcp@^2 zod@^4.6.5 @ai-sdk/anthropicOn ai 6 install ai@ai-v6 @ai-sdk/mcp@ai-v6, on ai 5 ai@ai-v5 @ai-sdk/mcp@ai-v5: the major of @ai-sdk/mcp has to match the major of ai (supported versions).
// agent.ts
import { generateText, isStepCount } from 'ai'; // stepCountIs on ai 5 and 6
import { createMnemoverseTools, SERVER_INSTRUCTIONS } from '@mnemoverse/ai-sdk';
import { anthropic } from '@ai-sdk/anthropic';
const memory = await createMnemoverseTools({
apiKey: process.env.MNEMOVERSE_API_KEY,
domain: 'project:acme', // optional; scopes what the model reads and writes
});
try {
const { text } = await generateText({
model: anthropic('claude-sonnet-4-5'),
system: SERVER_INSTRUCTIONS,
tools: memory.tools,
stopWhen: isStepCount(5),
prompt: 'I switched my editor to Neovim this week. Remember that, then tell me what you know about my deploy target.',
});
console.log(text);
} finally {
await memory.close();
}What you get, in the package's own terms (README):
- The real MCP surface, in process. The tools are the ones
@mnemoverse/mcp-memory-serverserves over stdio and the hosted connector serves over HTTP, with the same descriptions, schemas and answers; nothing is copied or converted, and no subprocess is started.SERVER_INSTRUCTIONSis the system-prompt text the server gives every MCP client, re-exported. - Three tools by default:
memory_write,memory_read,memory_list_recent. The shared-room tools are opt-in by name, and onai6 and 7 a call that acts on a shared room needs tool approval by default (roomApproval), because room content is written by every member and is untrusted input. - The key never reaches the model. It lives in the package's fetch closure, is never placed in a tool, a message or an error, and is never re-sent to a redirect target. Creating the tools in a browser is refused.
- Per-user keys. Create the tools per request with that user's key; a
domainpin scopes reads and writes but is not an access boundary. - Kept current. Each MCP server release is picked up by an automated bump and tested against
ai5, 6 and 7; a release that adds or renames a tool waits for a person to classify it.
The run on 2026-10-03 with 0.2.2 on ai 7.0.127, Node 22.23.2, the model reached through @ai-sdk/openai-compatible, in a test domain that already held the deploy facts from the runs below:
tools: memory_write, memory_read, memory_list_recent
tool call: memory_write {"content":"User switched their editor to Neovim this week ...","concepts":["editor","Neovim","developer tools"], ...}
tool call: memory_read {"query":"What is the user's deploy or deployment target, hosting platform, runtime environment, or infrastructure destination?","top_k":10, ...}
answer: Remembered: you switched to Neovim this week. Your project's deploy target is Railway. I also know deployments happen on Wednesdays, and the team avoids pushing to main on Fridays.The model called both tools on its own, with SERVER_INSTRUCTIONS as the system prompt and no further instruction. The two sections below are the same memory without the package, for when you want to own the tool definitions.
npm install ai zod @ai-sdk/anthropicYour own tools over REST
// memory-tools.ts
import { tool } from 'ai';
import { z } from 'zod';
const BASE = 'https://core.mnemoverse.com/api/v1';
// One namespace per project or user. A read never crosses it.
const DOMAIN = process.env.MNEMOVERSE_DOMAIN ?? 'project:acme';
async function call(path: string, body: unknown) {
const res = await fetch(`${BASE}${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.MNEMOVERSE_API_KEY!,
},
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`${path} -> ${res.status} ${await res.text()}`);
return res.json();
}
export const memoryTools = {
remember: tool({
description:
'Store a durable fact, decision or lesson in long-term memory. Write it as a self-contained statement.',
inputSchema: z.object({
content: z.string().min(1).max(10000),
concepts: z.array(z.string()).max(256).optional(),
}),
execute: async ({ content, concepts }) =>
call('/memory/write', { content, concepts, domain: DOMAIN }),
}),
recall: tool({
description:
'Search long-term memory for earlier decisions, preferences and project context before answering.',
inputSchema: z.object({
query: z.string().min(1).max(5000),
top_k: z.number().int().min(1).max(500).optional(),
}),
execute: async ({ query, top_k }) =>
call('/memory/read', { query, top_k: top_k ?? 10, domain: DOMAIN }),
}),
rate: tool({
description:
'Report whether recalled memories helped (1) or misled (-1), so the next recall ranks them accordingly.',
inputSchema: z.object({
atom_ids: z.array(z.string()).min(1),
outcome: z.number().min(-1).max(1),
}),
execute: async ({ atom_ids, outcome }) =>
call('/memory/feedback', { atom_ids, outcome }),
}),
};// agent.ts
import { generateText, isStepCount } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { memoryTools } from './memory-tools';
const { text, steps } = await generateText({
model: anthropic('claude-sonnet-4-5'),
tools: memoryTools,
stopWhen: isStepCount(6),
system:
'You have long-term memory tools. Recall before answering questions about the project; remember durable facts the user states.',
prompt:
'Remember that our deploy target is Railway and nobody pushes to main on Fridays. Then recall what you know about deploys and summarise it in one sentence.',
});
for (const step of steps)
for (const c of step.toolCalls ?? []) console.log('tool call:', c.toolName, JSON.stringify(c.input));
console.log(text);stopWhen: isStepCount(6) lets the model call a tool, read the result and continue; without it, generateText returns after the first tool call (AI SDK: multi-step calls).
The run on 2026-10-02, with MNEMOVERSE_DOMAIN=smoke:ai-sdk-2026-10-02 and the model reached through @ai-sdk/openai-compatible; the provider line is the only difference from the code above:
tool call: remember {"content":"The project's deploy target is Railway, and the team policy is that nobody pushes to main on Fridays.","concepts":["deployment","Railway","main branch","release policy"]}
tool call: recall {"query":"What is known about this project's deploy target, deploy process, branch rules, and timing restrictions?","top_k":20}
tool call: rate {"atom_ids":["39f4889d-5ac4-4323-bd76-51d3da3e37c3"],"outcome":1}
tool result: remember {"stored":true,"atom_id":"39f4889d-5ac4-4323-bd76-51d3da3e37c3","importance":1,"reason":"","superseded":[]}
tool result: recall {"items":[{"atom_id":"39f4889d-5ac4-4323-bd76-51d3da3e37c3","content":"The project's deploy target is Railway, and the team policy is that nobody pushes to main on Fridays.","relevance":0.65, ...
tool result: rate {"updated_count":1,"avg_valence":1,"coactivation_edges":0,"feedback_time_ms":104.45}
answer: Deploys target Railway, and nobody pushes to `main` on Fridays.The model called all three tools on its own, in the order the system prompt implies. The bounds in the schemas are the API's: content up to 10,000 characters, query up to 5,000 characters, concepts up to 256 items, top_k 1 to 500 with a default of 10 (API reference). recall returns items with atom_id, content, relevance and source; rate takes those ids back.
The MCP server over stdio
The same memory through the published server package as a subprocess, so the tools and their descriptions come from the server rather than from your code. The package above runs the same server in process without the subprocess; this path is for setups that already start MCP servers this way.
npm install @ai-sdk/mcp @modelcontextprotocol/sdk// agent-mcp.ts
import { generateText, isStepCount } from 'ai';
import { createMCPClient } from '@ai-sdk/mcp';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { anthropic } from '@ai-sdk/anthropic';
const mcpClient = await createMCPClient({
transport: new StdioClientTransport({
command: 'npx',
args: ['-y', '@mnemoverse/mcp-memory-server@latest'],
// The transport starts the server with a small safe subset of your
// environment unless you pass env; the key has to be passed explicitly.
env: { ...process.env, MNEMOVERSE_API_KEY: process.env.MNEMOVERSE_API_KEY! },
}),
});
try {
const tools = await mcpClient.tools();
const { text } = await generateText({
model: anthropic('claude-sonnet-4-5'),
tools,
stopWhen: isStepCount(6),
prompt:
'What do we know about deploys? Use memory_read with domain "project:acme" and answer in one sentence.',
});
console.log(text);
} finally {
await mcpClient.close();
}With streamText, close the client in the onEnd callback instead of finally (AI SDK: MCP tools). The run on 2026-10-02, against the memory written in the REST example:
tools from server: memory_write, memory_read, memory_list_recent, memory_feedback, memory_stats, memory_create_room, memory_invite_to_room, memory_join_room, memory_list_rooms, vault_list, memory_graph
tool call: memory_read {"query":"deploys deployment process decisions status","top_k":10,"domain":"smoke:ai-sdk-2026-10-02","order_by":"relevance", ...}
tool call: memory_feedback {"memory_ids":["39f4889d-5ac4-4323-bd76-51d3da3e37c3"],"outcome":1,"domain":"smoke:ai-sdk-2026-10-02"}
answer: Deploys target Railway, and team policy prohibits pushes to `main` on Fridays.The server's memory_read fills in top_k: 5 when the model omits it; the REST default is 10. What each tool accepts is on the MCP server page.
The hosted connector and the AI SDK
createMCPClient also speaks Streamable HTTP (transport: { type: 'http', url }), and the hosted connector lives at https://mcp.mnemoverse.com/mcp. It signs a user in through the browser (OAuth 2.1 with PKCE) and refuses an API key sent as a bearer token: a POST /mcp with Authorization: Bearer mk_live_... answered 401 on 2026-10-02, the same as a request with no credentials. The SDK's authProvider option is for a client that can complete that sign-in; a server-side app without a browser uses the two paths above. Details: Remote MCP server.
Habits that make the memory worth having
- Recall before answering, remember after. Put both in the system prompt, as the example does; a registered tool is not a used tool. The agent memory page has the standing rules.
- One domain per project or user.
domainis a byte-for-byte namespace; a read with a domain never returns another domain's memories. - Rate what you acted on.
ratewith1or-1changes the order of the next recall, not the stored text; nothing is erased. - Do not re-inject what the conversation already holds. When you build the prompt from a recall, replace the memory block, do not append a second one.
Common questions
How do I add long-term memory to a Vercel AI SDK agent?
Install @mnemoverse/ai-sdk, call createMnemoverseTools({ apiKey }) and pass memory.tools and SERVER_INSTRUCTIONS to generateText or streamText with a stopWhen condition; the package runs the Mnemoverse MCP tools in your process on ai 5, 6 and 7. If you would rather own the tool definitions, three tools with tool() and zod over the REST API do the same job. The page shows the code and a recorded run for each.
Can the AI SDK use the Mnemoverse MCP server directly?
Yes. createMCPClient from @ai-sdk/mcp with a stdio transport starts @mnemoverse/mcp-memory-server and converts every tool it exposes into an AI SDK tool. Pass the API key in the transport's env, and close the client when the response is done.
Does the hosted connector at mcp.mnemoverse.com work with createMCPClient over HTTP?
Only for a client that can complete a browser sign-in: the hosted connector uses OAuth 2.1 with PKCE and refuses an API key sent as a bearer token. A server-side app without a browser uses the REST tools or the stdio server instead.
Which AI SDK version does this need?
The package supports ai 5, 6 and 7; the major of @ai-sdk/mcp has to match the major of ai. The hand-written examples ran on ai 7.0.127 and @ai-sdk/mcp 2.0.66 with Node 22. In AI SDK 7 the multi-step condition is isStepCount and tool schemas use inputSchema; older releases named them stepCountIs and parameters.
Related
@mnemoverse/ai-sdkon GitHub: per-user keys, lifecycle, security notes, supported versions- Agent frameworks: LangChain, LangGraph, CrewAI and n8n
- Python SDK
- MCP server and the remote MCP server
- API reference
- Agent memory: when to recall and what to save