Skip to main content
Glama

nextjs-mcp-kit

一个 MCP 服务器、一个 MCP 客户端,以及一个与提供商无关的 Next.js App Router 聊天 UI——以路由处理器、组件和可安装的类型化状态的形式提供。

为你的应用提供一个真正了解你的应用的小聊天。

智能聊天——回复是你自己写的,是你上传的文档

  • 你添加的工具 你在浏览器中通过表单添加答案来创建工具。 无需重新训练,无需向量数据库,无需重新部署。

它刻意不追求聪明。它是一个会打招呼、知道你的营业时间的小聊天,大约一分钟就能拥有一个。

npm i nextjs-mcp-kit && npx nextjs-mcp-kit init

nextjs-mcp-kit——脚手架选项

npx nextjs-mcp-kit init // 在当前目录中搭建脚手架

npx nextjs-mcp-kit init --force // 覆盖已存在的文件

npx nextjs-mcp-kit init --dir web // 搭建到 ./web

然后跳到你的第一个工具,60 秒搞定

六个界面,刻意彼此分离:

路由

用途

/

MCP 提示词聊天 — 由你应用自己的 MCP 服务器提供的提示词

/chat

普通聊天 — 选择提供商 + 模型,设置指令,然后对话。无工具。

/add-tool

创建工具 — 通过表单、上传 .md/.txt 文件,或从技能创建

/mcp-dashboard

你的 MCP 服务器提供什么,以及用于让客户端指向它的 mcp.json

/personal-chat

带工具的聊天 — 流式输出,并且始终标明运行了什么

/smart-chat

访客聊天 — 输入一个问题,输出一个有依据的答案

从浏览器添加一个工具,它立即可在聊天中使用——并通过 MCP 提供给任何指向你应用的东西,包括别人的客户端。


Related MCP server: Example Next.js MCP Server

你的第一个工具,60 秒搞定

无需 API、无需密钥、无需代码。运行 npm run dev,打开 /add-tool,并将第一个选项保留为 “返回我编写的文本”

字段

输入此内容

名称

opening_hours

描述

The shop opening hours. Call this when asked when we are open.

返回的文本

Open 9am to 6pm, Monday to Friday. Closed weekends.

将参数留空。点击 添加工具

现在打开 /personal-chat,勾选 opening_hours,然后问 “你们周六开门吗?”

模型回答 “不——周末关门”,在答案下方它会显示 opening_hours 已运行,并准确展示工具返回的内容。它原本并不知道这些。是你在三十秒前通过一个表单告诉它的。

这就是完整的循环。本 README 中的其他内容都是这个循环的更多选择。

为什么描述比看起来更重要

描述不是文档——它是模型决定是否调用该工具的依据"opening hours" 有一半时间会被忽略。"The shop opening hours. Call this when asked when we are open." 则会被调用。模糊的描述意味着工具虽然已注册,却永远不会被选中。


这适合做什么

为你的访客提供一个小聊天。 它不够聪明,当不了支持代理,也没打算当。它足够聪明,能向某人问好,并回答你的应用真正被问到的六个问题,答案来自你编写的内容。这些答案中的每一个都是一个 skill 工具:一个名称、一个描述,以及要返回的文本。

答案以你告诉它的内容为依据。 当某个工具与问题匹配时,模型会调用它,并根据返回的内容作答——而不是根据它对商店的模糊记忆。你的工具覆盖的问题,由你的工具来回答。

你始终能看到发生了什么。 每一条使用过工具的回复都会标明工具名称并显示其返回内容。如果答案来自你的文本,你可以证明;如果模型自行回答,跟踪记录为空,你也能看到。无需猜测你得到的是哪一种。

没有静默回退。 如果提供商无法调用工具,或某个 Ollama 模型不具备该能力,你会在回合运行前收到带原因的 503——绝不会得到一个悄悄忽略你所勾选工具的答案。

免费运行。 Ollama 是本地运行的,因此访客聊天每条消息不花费任何费用,数据也不会离开机器。切换到 Claude 可以使用相同的工具获得更好的体验——选择器会用 💳 表示付费回合,用 🖥️ 表示本地回合。

它也是一个 MCP 服务器。 你从浏览器添加的相同工具会通过 MCP 提供,因此 Claude Desktop——或任何人的客户端——都可以指向你部署的应用并使用它们。参见连接 MCP 客户端

该把哪个页面给你的访客

/smart-chat 就是你应该让他们访问的页面。输入一个问题,输出一个答案——它会检查你注册的每一个工具,使用匹配的那个,并说明运行了哪一个。无需维护对话,无需存储历史,访客也无需配置任何东西。

// app/ask/page.tsx — your public "ask us anything" page
export { SmartChatPage as default } from 'nextjs-mcp-kit/pages';

或者只把组件放入你自己的页面:

import { SmartChat } from 'nextjs-mcp-kit/components';

/personal-chat 是为你自己准备的:完整的对话、指令,以及本次对话可以使用哪些工具的清单。你在这里试用新工具,然后再让别人接触它。

/add-tool/mcp-dashboard 也是你的,而不是访客的。两者都没有任何认证——把它们放在你自己的认证后面,或者干脆不要把它们搭建到公共应用中。

支持 Ollama(本地、免费)和 Claude(Anthropic)。添加第三个 Provider 只需要一个文件和一个数组条目。


安装

需要 Next.js 16+ 和 Node 20.9+。 peer 范围刻意设为 >=16.0.0 而不是 >=15.0.0:16 是唯一一个经过构建和测试的大版本,peer 范围应该描述实际验证过的情况,而不是可能碰巧能用的版本。在 Next 15 上,npm i 会报告 peer 冲突——这是预期的信号,不是 bug。

集成到现有的 Next.js 应用中

npm i nextjs-mcp-kit
npx nextjs-mcp-kit init

init 会写入路由处理器和 /chat。它不会改动你的根布局——请自行添加两行:

// app/layout.tsx
import { GlobalProvider } from 'nextjs-mcp-kit/context';
import 'nextjs-mcp-kit/styles.css';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <GlobalProvider>{children}</GlobalProvider>
      </body>
    </html>
  );
}

然后运行 cp env.local.example .env.localnpm run dev

导入组件:最让人容易出错的一点

组件不会从包根目录导出。 这样会失败:

import { AgentChat } from 'nextjs-mcp-kit';
// The export AgentChat was not found in module .../dist/index.js [app-rsc]
// Did you mean to import initialAgent?

这样可行:

import { AgentChat } from 'nextjs-mcp-kit/components';

根入口刻意设计为服务器安全——参见导出。经验法则:如果它会渲染,它就不在根目录。

修正导入是必要的,但还不够:AgentChat 需要你的应用中存在 /api/providers/api/chat/api/instructions,并且在其上方有 GlobalProvidernpx nextjs-mcp-kit init 会写入路由;布局由你负责。一个完整可运行的应用——以及按实际出错顺序排列的故障排查清单——位于 examples 分支

独立使用,从空目录开始

mkdir my-app && cd my-app
npm init -y
npm i nextjs-mcp-kit
npx nextjs-mcp-kit init          # detects the empty dir, writes a whole app
npm i next react react-dom
npm i -D typescript@^5 @types/node @types/react @types/react-dom
cp env.local.example .env.local
npm run dev

typescript@^5 是刻意固定的。裸执行 npm i -D typescript 目前会解析到 TypeScript 7,其重构后的 lib/ 是 Next 16 无法检测到的——即使它已安装,Next 16 也会报告“你尚未安装所需的包”,并且构建会失败。


配置

OLLAMA_API_URL=http://localhost:11434   # 11434 is Ollama's default port
ANTHROPIC_API_KEY=                      # empty is fine — runs local-only
NEXTJS_MCP_DATA_DIR=                    # defaults to ./.data

ANTHROPIC_API_KEY 留空是一种受支持的模式,而不是故障:选择器会显示 Claude 不可用并附原因,而 Ollama 仍然可用。这正是 isAvailable() 的意义所在——缺少密钥是一种提前报告的正常状态,而不是你按下发送时才抛出的异常。

在无服务器主机上,设置 NEXTJS_MCP_DATA_DIR=/tmp/nextjs-mcp-kit;它们的打包文件系统除了 /tmp 之外都是只读的。参见持久化


路由

路由

方法

用途

/api/chat

POST

每个提供商共用一个聊天端点。绝不按模型分支。

/api/providers

GET

存在哪些提供商、可用性,以及(通过 ?provider=)它们的模型

/api/instructions

GET, POST

指令预设,已持久化

/api/agent-chat

POST

一个带工具的回合。当 stream: true 时为 NDJSON

/api/tools

GET, POST, DELETE

工具注册表,已持久化

/api/tools/upload

POST

一个 .md/.txt 文档变成一个技能工具

/api/mcpserver/[transport]

GET, POST, DELETE

你应用的 MCP 服务器。连接 URL:/api/mcpserver/mcp

/api/mcpserver/prompts

GET

提示词目录

/api/mcpserver/tools

GET

工具目录

/api/mcpclient-prompt

GET, POST

列出提示词 / 用参数填充一个提示词

状态码具有明确含义:提供商未就绪时返回 503(请求本身没问题),输入错误时返回 400,真正的失败时返回 500

curl -X POST localhost:3000/api/chat -H 'content-type: application/json' -d '{
  "provider": "ollama",
  "model": "llama3.1:8b",
  "system": "Answer in one word.",
  "messages": [{ "role": "user", "content": "Capital of France?" }]
}'
# {"answer":"Paris","provider":"ollama","model":"llama3.1:8b","billed":false}

工具

/api/chat 没有工具,也永远不会有。工具调用是独立的路由,直接调用提供商注册表——它不包装 /api/chat

curl -X POST localhost:3000/api/agent-chat -H 'content-type: application/json' -d '{
  "provider": "anthropic",
  "model": "claude-haiku-4-5-20251001",
  "messages": [{ "role": "user", "content": "What is the refund window?" }],
  "tools": ["refund_policy"]
}'
# {"answer":"…","provider":"anthropic","model":"…","billed":true,
#  "trace":[{"name":"refund_policy","result":"…","isError":false,"ms":2}]}

trace 是这件事值得拥有的原因:使用过工具的答案可以证明这一点。

添加 "stream": true 以使用 NDJSON——每行一个 JSON 对象,{"type":"token"}{"type":"done"}。请使用 nextjs-mcp-kit/client 中的 streamAgentChat 来读取,而不是自己解析。

运行哪些工具完全由调用方决定:没有 tools[] 就意味着普通回合。任何内容都不会被静默丢弃——无法调用工具的提供商,或不具备该能力的 Ollama 模型,会收到带原因的 503,而不是一个悄悄忽略你请求的答案。

路由段配置

每个脚手架生成的路由都声明了自己的 runtime

export { POST } from 'nextjs-mcp-kit/api/chat';

export const runtime = 'nodejs';
export const maxDuration = 120;

这不是你可以删掉的样板代码。Next 会从路由模块本身静态地读取段配置,因此重新导出的 runtime 会被静默忽略,处理器会在错误的运行时上运行。


添加提供商

提供商层是唯一了解模型后端的地方。两步:

1. 编写一个 ChatProvider

import type { ChatProvider } from 'nextjs-mcp-kit/types';

export const myProvider: ChatProvider = {
  id: 'mine',
  label: 'My backend',
  defaultModel: 'some-model',
  billed: false,
  dynamicModels: false,

  // Never throws. A missing key or a down daemon is a normal state.
  async isAvailable() {
    return process.env.MY_KEY
      ? { available: true }
      : { available: false, reason: 'MY_KEY is not set' };
  },

  async listModels() {
    return [{ id: 'some-model', label: 'Some model' }];
  },

  // `system` arrives separately: Anthropic takes it as a top-level field,
  // Ollama as a message role. That difference is absorbed here, per provider.
  async chat({ model, system, messages }) {
    return { text: '…', model };
  },
};

2. 将它添加到注册表。

其他什么都不用改。路由不用改,reducer 不用改,选择器不用改,类型联合也不用改——ProviderId 刻意是 string/api/providersProviderModelPicker 由注册表驱动,因此新提供商出现在两个下拉菜单中时客户端修改。

billed 驱动 💳/🖥️ 徽章。付费回合绝不能让人意外。

要让你的提供商调用工具,请添加可选的 chatWithTools。它保持可选,因此没有它的提供商仍然完全可以正常使用——并且会被报告为不具备工具能力,而不是在未使用所请求工具的情况下悄悄作答:

async chatWithTools({ model, system, messages, tools, run, onToken }) {
  // `tools` is neutral — translate it with your dialect:
  //   import { DIALECTS } from 'nextjs-mcp-kit/tools';
  //   const declared = DIALECTS.openai.toTools(tools);
  // Then loop: ask, ingestToolCalls(raw), await run(call), feed results back.
  return { text: '…', model, trace: [] };
}

大多数新后端都与 OpenAI 兼容,而且 DIALECTS.openai 已经存在——因此新提供商通常完全不需要添加方言。


工具在底层如何工作

一个工具存储一次,采用一种中立的形态。每个提供商的拼写都是从它派生的:

import { deriveByProvider } from 'nextjs-mcp-kit/tools';

deriveByProvider(tools).anthropic; // [{ name, description, input_schema }]
deriveByProvider(tools).ollama;    // [{ type:'function', function:{ … } }]

这很重要,因为 Anthropic 和 Ollama 在每一步上都不一致——schema 键、调用是否携带 id,以及结果如何返回。两份手工维护的列表会在其中一份被编辑时第一次出现偏差。

两种类型,从第一天起就都可调用:

类型

作用

endpoint

将模型的参数 POST 到 URL;响应体即为结果

skill

返回其自身存储的指令文本

skill 是一种让文档或 SKILL.md 形状的正文无需文件系统即可成为工具的方式。该文本是记录上的一个字段。

id 问题及其解决方案。 Anthropic 为每次工具调用分配一个 id,并通过 tool_use_id 配对结果;Ollama 的原生 API 完全不发送 id,而是按顺序配对。如果某个循环只假设其中一种情况,就会在另一种情况下出错。因此,除一个文件外,代码不做任何假设:ingestToolCalls() 在有 id 时保留 id,在没有 id 时生成 name#index,并将以 JSON 字符串形式到达的参数规范化。ingest 之后,两个提供者无法区分,并且各自仍按其 API 要求的方式返回结果。


子路径

内容

nextjs-mcp-kit

提供者、MCP 服务器/客户端、store、reducers — 服务端安全

nextjs-mcp-kit/context

GlobalProvider, useContextState, useContextActions

nextjs-mcp-kit/components

AgentChat, ProviderModelPicker, InstructionForm, McpPromptChat, PersonalChat, SmartChat, McpDashboard, ToolForm, ToolUploadForm, SkillToolForm, ToolChecklist, ToolTraceView

nextjs-mcp-kit/pages

ChatPage, McpPromptPage, AddToolPage, McpDashboardPage, PersonalChatPage, SmartChatPage

nextjs-mcp-kit/providers

PROVIDERS, getProvider, DEFAULT_PROVIDER_ID

nextjs-mcp-kit/tools

DIALECTS, deriveByProvider, validateFor

nextjs-mcp-kit/client

类型化 fetch 封装、streamAgentChatpostAgentChat

nextjs-mcp-kit/types

所有公共类型

nextjs-mcp-kit/api/*

供重新导出的路由处理器

nextjs-mcp-kit/styles.css

主题 tokens

React 相关部分位于各自的子路径下,因此导入它们不会把 Node 内置模块——或 ANTHROPIC_API_KEY——拖入客户端 bundle。如果它会渲染,那它就不在根路径。

子路径通过 package.json 中的 exports 映射解析,这需要在你的 tsconfig.json 中设置 "moduleResolution": "bundler"create-next-app 已经设置了该项;在旧的 "node" 设置下,每个子路径都会解析失败,报错 Cannot find module 'nextjs-mcp-kit/components'


状态

一个将值拆分的 Context:{ state, actions },通过 useContextState() / useContextActions() 消费。只进行 dispatch 的组件不会在无关状态变化时重新渲染。

'use client';
import { useContextState, useContextActions } from 'nextjs-mcp-kit/context';

function MyChat() {
  const { agent, instruction } = useContextState();
  const { sendChat, selectProvider } = useContextActions();
  // agent.chat, agent.provider, agent.model, agent.routing …
}

两个切片:agentinstruction。actions 通过 ref 而非闭包读取当前状态,这使得每个 action 的身份在 provider 的整个生命周期内保持稳定——否则 sendChat 会在每次按键时被重建。

预设 vs. systemText

这是两个不同的东西,把它们混为一谈会是一个 bug:

  • presets — 已保存、持久化的列表。

  • systemText — 实际随下一轮发送的可编辑文本。

选择预设会填充 systemText。之后编辑它不会修改已保存的预设。预设是起点,不是牢笼。用已有名称保存会编辑该预设(id 由名称派生),而不是累积近似重复项。


主题

所有颜色都来自 CSS 自定义属性。在导入之后覆盖其中任何一个——这就是主题的全部内容:

:root {
  --mcp-bubble-user: #dcfce7;
  --mcp-border: #cbd5e1;
}

浅色和深色都通过 prefers-color-scheme 定义。


持久化

指令预设和工具以 JSON 形式存储在 NEXTJS_MCP_DATA_DIR(默认 ./.data)下——这是重启后仍能保留的最小单元。将 .data/ 添加到你的 .gitignore 中。

.data/
  instructions.json
  tools.json

这是一个文件存储,因此在 serverless 环境下它是每实例且临时的——除了 /tmp 之外,打包后的文件系统是只读的,而 /tmp 会在每次调用之间被清空。如果你需要持久性,替换两个文件:src/store/instructions.tssrc/store/tools.ts 是路由唯一读写的地方。

skill 的正文是工具记录上的一个字段,而不是磁盘上的文件。 上传文档不会在任何地方创建 SKILL.md,这个包中的任何代码都不会写入你的应用源码树。


连接 MCP 客户端

{
  "mcpServers": {
    "nextjs-mcp-kit-local": {
      "type": "http",
      "url": "http://localhost:3000/api/mcpserver/mcp"
    }
  }
}

注意 /mcp 后缀——该路由是一个动态的 [transport] 段,因此仅将客户端指向 /api/mcpserver 是无法连接的。

这个端点是一个公共表面,而不是私密入口。部署你的应用后,任何人都可以用自己的模型和自己的 key 将他们的 MCP 客户端指向 https://your-app.example.com/api/mcpserver/mcp——那里没有你的任何东西可供他们获取。他们会得到你添加的每一个提示词和每一个工具,而 /mcp-dashboard 会精确展示这些内容。


本包刻意不做的事

  • /chat 中没有工具。 该路由只发送消息,别无其他,这是有意为之。工具位于 /api/agent-chat 和上面的四个页面中。

  • /api/chat 不支持流式。 它的响应完整到达,形态没有改变。/api/agent-chat 支持流式。

  • 没有认证。 将这些路由挂载在你自己的认证之后。注意 /api/mcpserver/mcp 设计上就是公开的——它就是要被指向的。

  • 不支持上传 .docx.pdf 只支持 .md.txt,因为支持它们会给你安装的包增加零依赖。添加一种格式只需在 src/server/extractText.ts 中加一个分支。

  • 没有数据库。 工具和预设是 JSON 文件。在 serverless 环境下,这是每实例且临时的——替换那两个 store 文件即可。

  • 没有为“以后”预建的东西。 没有占位注册表,没有死抽象。


要求

Next.js ≥ 16(App Router)、React ≥ 18.3、Node ≥ 20.9。已在 Next 16.2 和 React 19.2 上测试。


说明

nextjs-mcp-kit 架构实现了一条严格的边界,将公共客户端组件与安全的服务端执行分隔开来。基于 React 的客户端层协调交互状态和聊天界面,但对敏感的环境变量或第三方凭据完全无感。安全性得以维持,是因为所有 API 密钥、本地工具执行和直接 LLM 调用都只存在于安全的 Node.js 运行时中的 Next.js Route Handlers 之后。这些边界之间的通信围绕标准 HTTP POST 操作(用于用户发起的事件)和 Server-Sent Events(SSE)(用于单向实时数据流)来组织。这种设置确保复杂的多智能体工作流保持高度响应,同时遵循严格的现代企业安全准则。

nextjs-mcp-kit 架构实现了一条严格的边界,将公共客户端组件与安全的服务端执行分隔开来。基于 React 的客户端层协调交互状态和聊天界面,但对敏感的环境变量或第三方凭据完全无感。安全性得以维持,是因为所有 API 密钥、本地工具执行和直接 LLM 调用都只存在于安全的 Node.js 运行时中的 Next.js Route Handlers 之后。这些边界之间的通信围绕标准 HTTP POST 操作(用于用户发起的事件)和 Server-Sent Events(SSE)(用于单向实时数据流)来组织。这种设置确保复杂的多智能体工作流保持高度响应,同时遵循严格的现代企业安全准则。

这个包的有趣之处在于,它需要你付出的时间少得惊人。

通常需要做的工作——provider 接缝、工具调用循环、两个 provider 在工具声明方式和结果返回方式上的分歧、流式、MCP 服务器、持久化——都已经完成了。是安装,不是复制。当你 npm update 时它依然保持完成状态,而且这些都不是你需要阅读、拥有或维护的代码。

留给你的,是唯一真正属于你的部分:决定你的聊天应该知道什么。 那是一个名称、一句描述何时使用它的句子,以及答案。在浏览器里填一个表单,不到一分钟就能写完。十个这样的条目,你就拥有了一个比任何通用助手都更了解你应用的聊天——因为没有人拥有你的营业时间、你的退款期限或你的发货规则。

所以这项工作的形态不同寻常:一个下午,而且大部分时间花在思考你的访问者真正会问什么,而不是工具 schema 和 provider API。这个 demo 构建起来很短,展示效果却出奇地好,因为人们觉得惊艳的地方——它竟然知道关于你应用的这件事——来自只花了你一分钟的部分,而不是花了数月的部分。

开始之前有两件事值得了解,以免这里有任何夸大。这是一个专注的聊天,设计使然:它非常擅长根据你提供的内容来回答,并且它并不试图成为通用助手。此外,这个包中没有任何认证——/add-tool/mcp-dashboard 是你的,而不是你的访问者的。把它们放在你自己的认证后面,或者干脆不要挂载到公共应用中。

除此之外,去用它构建点什么吧。它写出来是为了被扩展,而不仅仅是被欣赏:一个新的 provider 就是一个文件加一个数组条目,一种新的工具类型就是一个分支,而 store 就是两个可以替换为真实数据库的文件。如果你用它做出了什么,我真的很想看看。


致谢 ❤️

这个工具包是薄薄的一层,站在其他人扎实的工作之上。

Ollama ❤️ — 感谢它让本地模型变得真正简单。无需账户、无需 key、无需账单:拉取一个模型,它就会回答。这就是 nextjs-mcp-kit 在你安装的那一刻就能发挥作用的原因,也是默认 provider 是本地 provider 的原因。

ClaudeAnthropic ❤️ ——为了这些模型,也为了 Model Context Protocol。 MCP 是 / 路由所依赖的东西,它作为开放规范被公开,而不是被当作护城河。没有它,这个包就不会以这种形态存在。

两个提供商在这里都是有意作为一等公民。一个本地且免费,一个托管且优秀,提供者接缝的存在是为了让两者都不必胜出。

许可证

MIT

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A drop-in Model Context Protocol server implementation for Next.js projects that enables AI tools, prompts, and resources integration using the Vercel MCP Adapter.
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A sample implementation of Model Context Protocol server using Next.js and the Vercel MCP Adapter, allowing developers to create custom AI agent backends with tools, prompts, and resources.
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    A Model Context Protocol server that bridges MCP clients with local LLM services, enabling seamless integration with MCP-compatible applications through standard tools like chat completion, model listing, and health checks.
    -