进阶主题
会话 (Sessions)
使用 useMcpSession() 在工具调用之间持久化每个会话的状态。
什么是会话 (Sessions)?
默认情况下,MCP 服务器是无状态的——每次请求都会创建一个全新的服务器实例。这对于简单的请求/响应工具来说没问题,但某些场景要求服务器在多次工具调用之间记住上下文。
提示词
为我的 Nuxt MCP 服务器 (@nuxtjs/mcp-toolkit) 添加会话支持。
- 在 nuxt.config.ts 中启用会话:mcp: { sessions: true }
- 在工具处理程序中使用 useMcpSession<T>()(自动导入)来获取/设置每个会话的状态
- await session.get(key) 和 await session.set(key, value) 可在同一会话的工具调用之间持久化数据
- 为你的会话数据定义一个 TypeScript 接口,并将其作为泛型参数传递给 useMcpSession<MySession>()
- 会话由自动分配的 MCP-Session-Id 请求头标识
- 自定义超时设置:mcp: { sessions: { enabled: true, maxDuration: 3600000 } }
- 会话还支持 SSE 流式传输和可恢复性
文档:https://mcp-toolkit.nuxt.dev/advanced/sessions
启用会话后,服务器会为每个客户端连接分配一个唯一的 MCP-Session-Id。该 ID 会包含在后续的每个请求中,从而使服务器能够:
- 在同一对话的多次工具调用之间保持状态
- 启用 SSE 流式传输以实现服务器到客户端的实时通信
- 支持可恢复性,以便客户端可以重新连接到现有会话
何时使用会话
当你的 MCP 工具需要以下功能时,会话将非常有用:
| 使用场景 | 示例 |
|---|---|
| 跟踪对话上下文 | 在会话中记住用户偏好、语言或之前的回答 |
| 累积数据 | 在多次工具调用中构建购物车、笔记列表或一组选项 |
| 多步骤工作流 | 引导用户完成向导(例如表单构建器、部署流水线),其中每个步骤都依赖于之前的输入 |
| 按会话计数器 | 跟踪会话内的 API 使用量、速率限制或进度 |
| 临时缓存 | 缓存仅与当前会话相关的昂贵计算结果 |
如果你的工具完全是无状态的(例如获取数据、执行计算、读取文件),则不需要会话。仅当跨工具调用的状态能带来实际价值时才启用它们。
设置
在你的 nuxt.config.ts 中启用会话:
nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/mcp-toolkit'],
mcp: {
sessions: true,
},
})
你还可以配置会话超时时间:
nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/mcp-toolkit'],
mcp: {
sessions: {
enabled: true,
maxDuration: 60 * 60 * 1000, // 1 小时(默认:30 分钟)
},
},
})
useMcpSession()
useMcpSession() 服务器工具函数提供了一个类型安全的、按会话划分的键值存储。它支持自动导入,并由 unstorage 提供支持,因此开箱即可与任何存储驱动配合使用。
类型化会话(推荐)
为你的会话数据定义一个接口并将其作为泛型传入。键和值将进行完整的类型检查:
server/mcp/tools/counter.ts
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
interface CounterSession {
counter: number
}
export default defineMcpTool({
name: 'increment',
description: 'Increment a per-session counter',
handler: async () => {
const session = useMcpSession<CounterSession>()
const count = await session.get('counter') ?? 0
await session.set('counter', count + 1)
return `Counter: ${count + 1}`
},
})
TypeScript 将强制执行以下规则:
session.get('counter')返回number | nullsession.set('counter', 'wrong')会导致编译错误session.get('unknown_key')会导致编译错误
非类型化会话
不使用泛型时,存储接受任何字符串键和未类型化的值:
const session = useMcpSession()
await session.set('key', { any: 'value' })
const data = await session.get('key')
API 参考
| 方法 | 描述 |
|---|---|
get(key) | 按键检索值(如果不存在则返回 null) |
set(key, value) | 为指定键存储一个值 |
remove(key) | 从会话中删除一个键 |
has(key) | 检查键是否存在 |
keys() | 列出会话中的所有键 |
clear() | 移除会话中的所有数据 |
storage | 访问底层的 unstorage 实例 |
除 storage 外,所有方法均为异步方法并返回一个 Promise。
示例
记事本
一对工具,允许 AI 在对话期间做笔记并在稍后检索它们:
server/mcp/tools/add-note.ts
import { z } from 'zod'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
interface NotesSession {
notes: { text: string, createdAt: string }[]
}
export default defineMcpTool({
name: 'add_note',
description: 'Add a note to the session notepad',
inputSchema: {
note: z.string().describe('The note content'),
},
handler: async ({ note }) => {
const session = useMcpSession<NotesSession>()
const notes = await session.get('notes') ?? []
notes.push({ text: note, createdAt: new Date().toISOString() })
await session.set('notes', notes)
return `Note added (${notes.length} total).`
},
})
server/mcp/tools/get-notes.ts
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
interface NotesSession {
notes: { text: string, createdAt: string }[]
}
export default defineMcpTool({
name: 'get_notes',
description: 'Retrieve all notes from the session notepad',
handler: async () => {
const session = useMcpSession<NotesSession>()
const notes = await session.get('notes') ?? []
if (notes.length === 0) return 'No notes yet.'
return notes
},
})
多步骤向导
引导用户完成多步骤表单,其中每个步骤都依赖于上一步:
server/mcp/tools/wizard.ts
import { z } from 'zod'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
interface WizardSession {
step: number
projectName: string
framework: string
}
export default defineMcpTool({
name: 'wizard_next',
description: 'Advance to the next step of the project setup wizard',
inputSchema: {
answer: z.string().describe('Answer for the current step'),
},
handler: async ({ answer }) => {
const session = useMcpSession<WizardSession>()
const step = await session.get('step') ?? 1
if (step === 1) {
await session.set('projectName', answer)
await session.set('step', 2)
return `Project name set to "${answer}". Step 2: Choose a framework (nuxt, next, svelte).`
}
if (step === 2) {
await session.set('framework', answer)
await session.set('step', 3)
const name = await session.get('projectName')
return `Creating "${name}" with ${answer}. Setup complete!`
}
return 'Wizard already completed. Use session.clear() to restart.'
},
})
用户偏好
在会话持续期间记住用户偏好:
server/mcp/tools/set-preference.ts
import { z } from 'zod'
import { defineMcpTool } from '@nuxtjs/mcp-toolkit/server'
interface PreferencesSession {
language: string
verbose: boolean
}
export default defineMcpTool({
name: 'set_preference',
description: 'Set a user preference for this session',
inputSchema: {
language: z.string().optional().describe('Preferred response language'),
verbose: z.boolean().optional().describe('Enable verbose output'),
},
handler: async ({ language, verbose }) => {
const session = useMcpSession<PreferencesSession>()
if (language) await session.set('language', language)
if (verbose !== undefined) await session.set('verbose', verbose)
return 'Preferences updated.'
},
})
自定义存储驱动
默认情况下,会话数据存储在内存中。服务器重启时数据会丢失,这对于开发和大多数用例来说是可以接受的。
对于需要持久化或在多个服务器实例间共享状态的生产环境,可以通过标准的 Nitro storage 配置来配置不同的存储后端:
nuxt.config.ts
export default defineNuxtConfig({
mcp: { sessions: true },
nitro: {
storage: {
'mcp:sessions': {
driver: 'redis',
url: 'redis://localhost:6379',
},
},
},
})
可以使用任何 unstorage 驱动:Redis、文件系统、Cloudflare KV、Vercel KV 等。
生命周期与清理
会话数据会在以下情况下自动清理:
- 客户端关闭会话(传输层
onclose) - 会话在
maxDuration的不活动期后过期(默认:30 分钟)
你无需手动管理清理工作。
要求
useMcpSession() 需要:- 在配置中启用
mcp.sessions nitro.experimental.asyncContext为true(Nuxt 3.8+ 起默认开启)