十分钟让 React 应用支持 MCP 协议
快速集成 Model Context Protocol,使 React 应用能接收 AI agents 的上下文。是连接前端与 AI 系统的核心交互模式。
快速集成 Model Context Protocol,使 React 应用能接收 AI agents 的上下文。是连接前端与 AI 系统的核心交互模式。
CopilotKit 现已支持 Angular · 在 Angular 中构建智能体应用和生成式 UI · 现已可用
AI 智能体已经解锁了强大的用例,因此开发者正在自动化复杂的工作流。
但有时候你只是想构建智能的东西而不需要所有额外的复杂性,这可以通过无智能体架构来实现。
今天,我们将学习这意味着什么、MCP 在整个场景中的位置,以及如何使用 CopilotKit(框架)和 Composio 服务器构建我们自己的无智能体应用。
简而言之,我们将详细覆盖以下主题。
什么是无智能体架构?
MCP 及其核心组件简介。
如何在 30 分钟内启动并运行 Next.js + MCP 客户端。
在 Next.js 项目中添加 MCP 支持的分步指南。
构建一个工作内存项目,从 Linear 等工具管理任务和项目。
一些具有实际用例的真实示例。
注:CopilotKit(构建 AI Copilot 的框架)最近推出了对 MCP 的内置支持,这正是我们将在本指南中使用的。
为了使事情更简单,我们将其连接到 Composio,它提供开箱即用的 MCP 服务器,具有内置身份验证和最小化的设置。
我们将覆盖很多内容,所以让我们开始吧。
[更新] 顺便说一下,如果你正在使用 MCP 和 CopilotKit 进行构建,请确保查看使用 Tadata 构建的新凭感觉编程服务器,旨在使编码智能体能够与 CopilotKit 可靠地编码。观看视频:
无智能体架构是指一种系统设计,其中前端(通常是网页或移动客户端)直接与智能后端(例如 MCP 服务器)交互,而无需部署或维护自定义智能体来管理状态、工具或操作。
在 AI 应用的背景下,这意味着你的 React 前端可以向 MCP 兼容的服务器发送结构化的提示和上下文。并接收智能响应,而无需托管你自己的 AI 智能体。
与其编写后端逻辑来处理工具、内存或操作(这些都是 MCP 的一部分),你只需向现有的 MCP 服务器发送请求,它就会为你处理这些智能的事情。
🧠 例子:React 应用中的 AI 任务助手
假设你正在构建一个基于 React 的项目管理应用。
使用无智能体架构:
用户输入:为我们的发布清单创建一个新任务。
你的应用将其转发到 MCP 服务器,如 CopilotKit 的。
MCP 服务器理解该请求,与你的工具(如 Linear)连接,并回复:任务"完成启动资产"已在营销项目中创建。
所有这些都无需托管你自己的 AI 智能体或编写编排逻辑。它简直就行得通。
何时选择无智能体 vs 基于智能体?
这取决于你用例的复杂性。以下是快速对比:
无智能体架构 | 基于智能体的架构
易于设置,不需要后端智能体 | 需要额外的设置来托管和管理智能体
非常适合简单的前端、静态站点或快速演示 | 最适合复杂的工作流、工具或需要内存的应用
没有内置内存,主要是无状态的 | 支持内存、历史和不断演变的上下文
理想用于 AI 聊天 UI、副驾驶或小型助手 | 完美用于开发智能体、RPA 机器人和完全自主的智能体
只需要访问 MCP 兼容的服务器 | 需要自己的后端或容器运行时
通过仅部署前端可轻松扩展到多个用户 | 需要为多个并发智能体制定后端扩展策略
我的建议是两者都试一试,看看什么有效。至少,你会对与每种相关的潜在问题有个概念。
现在我们已经了解了无智能体架构,让我们快速理解 MCP 及其核心组件。
Model Context Protocol(MCP)是一个新的开放协议,标准化了应用程序如何向 LLM 提供上下文和工具。
把它想象成 AI 的通用连接器。MCP 作为 Cursor 的插件系统工作,允许你通过连接到各种数据源和工具来扩展智能体的功能。
鸣谢来自 YouTube 的 Greg Isenburg
MCP 帮助你在 LLM 之上构建智能体和复杂的工作流。
例如,Obsidian 的 MCP 服务器帮助 AI 助手搜索和读取来自你的 Obsidian vault 的笔记。
你的 AI 智能体现在可以:
→ 通过 Gmail 发送电子邮件 → 在 Linear 中创建任务 → 在 Notion 中搜索文档 → 在 Slack 中发布消息 → 在 Salesforce 中更新记录
所有这些都只需通过标准化接口发送自然语言指令。
想一想这对生产力意味着什么。曾经需要在 5 个以上应用之间切换的任务现在可以在与你的智能体的单个对话中进行。
MCP 的核心遵循客户端-服务器架构,其中主机应用可以连接到多个服务器。
鸣谢来自 ByteByteGo
以下是任何通用 MCP 服务器的核心组件。
MCP 主机 - 像 Claude Desktop、Cursor、Windsurf 或想要通过 MCP 访问数据的 AI 工具这样的应用。
MCP 客户端 - 协议客户端,与 MCP 服务器维护 1:1 连接,充当通信桥梁。
MCP 服务器 - 轻量级程序,每个都通过标准化的 Model Context Protocol 公开特定的能力(如读取文件、查询数据库...)。
本地数据源 - MCP 服务器可以安全访问的计算机上的文件、数据库和服务。例如,浏览器自动化 MCP 服务器需要访问你的浏览器才能工作。
远程服务 - MCP 服务器可以连接的外部 API 和基于云的系统。
如果你有兴趣了解更多关于架构的信息,请查看官方文档。它涵盖了协议层、连接生命周期和错误处理以及总体实现。
我们将覆盖所有内容,但如果你有兴趣了解更多关于 MCP 的信息,请查看以下两个博客:
Builder.io 团队的《什么是 Model Context Protocol (MCP)?》
《MCP:它是什么以及为什么重要》Addy Osmani
在本部分,我们将讨论如何使用 CopilotKit 向你的 Next.js 项目添加 MCP 支持。
我们将集成 Copilotkit,它对 Model Context Protocol(MCP)具有内置支持。这将允许我们创建一个前端,直接连接到与 MCP 兼容的外部服务器。
如果你有兴趣自己阅读,请阅读位于 docs.copilotkit.ai/guides/model-context-protocol 的文档。如果你不想,也没关系;我将用概念详细解释所有步骤。
🎯 使用 CLI 安装 MCP 支持的一行命令
如果你没有现有的 Next.js 应用,你可以在终端中使用 npx create-next-app@latest 命令创建一个。
一旦你的项目准备好了,集成 MCP 支持的最快方法是使用 CopilotKit CLI。只需在你的终端中运行以下命令。
npx copilotkit@latest init -m MCP
这个命令在幕后做了很多事情:
安装所有必需的 CopilotKit 依赖项
设置与 MCP 服务器交互的开箱即用接口
添加组件以集成 MCP 并安装 shadcn/ui 用于 UI 样式设置
它将引导你安装必需的包,并建议你使用 Copilot Cloud 进行部署(不需要额外设置)。
它将验证配置并检测现有的 Next.js 应用是否有效。之后,你将获得选择默认项目或 mcp demo(这是由 CopilotKit 团队制作的展示项目)的选项。
我用 CLI 尝试过两者,但在本指南的范围内我们将使用默认项目。
然后它会提示你安装 shadcn/ui 用于内置组件样式设置。
由于我们使用的是 React 19,这是非常新的。一些库可能还没有正式支持它,npm 可能会因为版本冲突(称为对等依赖问题)而显示错误。
要修复这个问题,CLI 建议使用 --legacy-peer-deps,它告诉 npm 忽略那些版本警告并继续安装。
这是一个通常可以很好工作的解决方法,特别是对于仍在获得支持的较新 React 版本。
安装完成后,你将看到已添加内容的摘要。
你可以使用 npm run dev 在本地启动服务器,并导航到 http://localhost:3000/copilotkit 以查看你的 MCP 就绪前端。
📧 连接到 Gmail MCP 服务器(演示)
你需要输入你希望使用的 MCP 服务器的 SSE URL。你可以在 mcp.composio.dev 找到 100+ 个可用的托管 MCP 服务器列表。
让我们简要检查流程。我使用的是 mcp.composio.dev/gmail 处的 Gmail 服务器。
复制你从 composio 服务器页面生成的 MCP 服务器 URL,并将其粘贴到页面上的占位符字段(输入 MCP 服务器 URL)中。这是敏感信息,仅供个人使用,所以这就是为什么我已将其模糊处理。
我给出的提示词是:向 hi@anmolbaranwal.com 发送一封电子邮件,主题为 working demo of copilotkit mcp,并在邮件正文中写入 composio server works。
它会调用适当的 MCP 服务器(如果你配置了多个服务器),并根据你的提示词使用正确的操作。
由于当前没有活动连接,它会先建立连接(如上图所示)。你需要将 OAuth URL 复制到浏览器中进行身份验证。
💡 最好先使用测试账号进行尝试,尤其是在实验阶段。确认一切符合预期后,你再使用主账号实现自动化。
你需要向服务器授予访问权限,以便它根据你的提示词执行操作。
完成身份验证后,你会在浏览器中看到确认信息。
你只需要输入 done,AI 智能体就会验证活动连接。令我惊讶的是,它在发送电子邮件之前仍然会请求批准,从而确保控制权始终掌握在你手中。
获得批准后,AI 智能体会继续发送电子邮件。
不知道为什么它没有填写主题,但我们确实收到了邮件,而且正文内容正确。
太棒了!🎉 现在,你已经使用 CopilotKit 完成了与 MCP 服务器的端到端集成。
你可以对其他所有 MCP 服务器执行相同的操作,并创建多步骤工作流。
接下来:我们将从头开始手动构建这套流程,以便更深入地理解其底层组件。
🎯 从头搭建完整集成
下面我们将逐步了解如何从头搭建 CopilotKit 集成,以使用 MCP 服务器。这有助于你从端到端理解其架构和流程。
如果你已经有一个现成的 Next.js 项目,请直接跳到第 2 步。
第 1 步:创建使用 TypeScript 的 Next.js 项目
如果你还没有前端项目,可以使用以下命令创建一个使用 TypeScript 和 Tailwind CSS 的新 Next.js 项目。
npx create-next-app@latest
你的项目结构将如下所示。我们将使用最新版本的 Next.js 和 App Router。我还根据个人编码风格添加了一些文件,这是我在每个项目中都会做的事情,但并非必需。
🧠 你可能会注意到项目中没有 tailwind.config.js。这是因为使用 Tailwind CSS V4 后,我们现在可以直接在 globals.css 中自定义样式,新的 Next.js 应用也不再需要配置文件。
第 2 步:安装 CopilotKit 包并设置 Provider
安装所需的 CopilotKit 包:
npm install @copilotkit/react-core @copilotkit/react-ui
@copilotkit/react-core 提供核心上下文和逻辑,用于将 React 应用连接到 CopilotKit 后端和 MCP 服务器。
@copilotkit/react-ui 提供 <CopilotChat /> 等现成的 UI 组件,帮助你快速构建 AI 聊天或助手界面。
<CopilotKit> 组件必须包裹应用中需要使用 Copilot 功能的部分。大多数情况下,最好用它包裹整个应用,例如在 app/layout.tsx 中进行配置。
import type { Metadata } from 'next'
import './globals.css'
import '@copilotkit/react-ui/styles.css'
import './globals.css'
import { CopilotKit } from '@copilotkit/react-core'
export const metadata: Metadata = {
title: 'CopilotKit MCP Demo',
description: 'CopilotKit MCP Demo',
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body
className={
'relative flex flex-col overflow-x-hidden font-sans antialiased'
}
suppressHydrationWarning
>
<CopilotKit publicApiKey="<replace_with_your_own>">
{children}
</CopilotKit>
</body>
</html>
)
}
将 <replace_with_your_own> 替换为你从 cloud.copilotkit.ai 获取的 API 密钥。它可以免费使用,如果你要构建生产环境应用,也推荐采用这种方式。
如果你有兴趣配置自托管运行时并将其与 MCP 服务器结合使用,请阅读相关文档。
只需单击 Get Started,即可找到一个公共 API 密钥。
第 3 步:创建用于配置 MCP 服务器连接的组件
现在,我们将创建一个辅助组件,用于将应用连接到 MCP 服务器。在 src\components\McpServerManager.tsx 新建一个文件。
'use client'
import { useCopilotChat } from '@copilotkit/react-core'
import { useEffect } from 'react'
function McpServerManager() {
const { setMcpServers } = useCopilotChat()
useEffect(() => {
setMcpServers([
{
// Try a sample MCP server at https://mcp.composio.dev/
endpoint: 'your_mcp_sse_url',
},
])
}, [setMcpServers])
return null
}
export default McpServerManager
这个 McpServerManager 组件会将你的应用连接到 MCP 服务器。
组件加载时(即 useEffect 运行时),它会通过提供服务器 URL,告知 CopilotKit 应该连接到哪个 MCP 服务器。
组件本身不会渲染任何内容(return null),它只负责在后台建立连接。
页面加载时,它会将应用配置为自动与 MCP 服务器通信。你只需要在 endpoint 属性中设置具体的 MCP 端点(your_mcp_sse_url),即可访问相应工具。
第 4 步:添加聊天界面
ChatInterface 组件会创建实际的聊天 UI,并确保聊天功能知道你想连接哪个 MCP 服务器。在 src\components\ChatInterface.tsx 新建一个文件。
'use client'
import { CopilotChat } from '@copilotkit/react-ui'
import McpServerManager from './McpServerManager'
export default function ChatInterface() {
return (
<div className="flex h-screen p-4">
<McpServerManager />
<CopilotChat
instructions="You are a helpful assistant with access to MCP servers."
className="w-full flex-grow rounded-lg"
/>
</div>
)
}
下面简单解释一下这段代码的工作原理。
McpServerManager 会在页面加载时运行,并告知 CopilotKit 应使用哪个 MCP 服务器。
CopilotChat 会显示聊天框,用户可以在其中与 AI 对话。
instructions 用于告诉 AI,它应该扮演哪种类型的助手。
第 5 步:将 MCP 工具调用可视化(可选)
要监控助手触发的工具调用,可以添加一个 ToolRenderer 组件。这完全是可选的,但在开发过程中非常有用。
在 src\components\ToolRenderer.tsx 新建一个文件。
'use client'
import {
useCopilotAction,
CatchAllActionRenderProps,
} from '@copilotkit/react-core'
import McpToolCall from './McpToolCall'
export function ToolRenderer() {
useCopilotAction({
/**
* The asterisk (*) matches all tool calls
*/
name: '*',
render: ({ name, status, args, result }: CatchAllActionRenderProps<[]>) => (
<McpToolCall status={status} name={name} args={args} result={result} />
),
})
return null
}
然后创建 McpToolCall.tsx(放在同一个 components 目录下)。它会在可折叠的 UI 中显示工具名称、状态、参数和结果。
这是一个可视化调试组件,用于展示工具调用的详细信息,例如助手尝试执行了什么操作,以及它得到了什么返回结果。
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import * as React from 'react'
interface ToolCallProps {
status: 'complete' | 'inProgress' | 'executing'
name?: string
args?: any
result?: any
}
export default function McpToolCall({
status,
name = '',
args,
result,
}: ToolCallProps) {
const [isOpen, setIsOpen] = React.useState(false)
const classes = {
container:
'bg-white rounded-xl overflow-hidden w-full border-2 border-gray-200 shadow-md transition-all duration-200 hover:shadow-xl my-1',
header:
'p-4 flex items-center cursor-pointer group bg-gray-50 border-b border-gray-200',
title: 'text-gray-900 font-semibold overflow-hidden text-ellipsis',
statusContainer: 'ml-auto flex items-center gap-2',
statusText: 'text-xs text-gray-700 font-medium mr-1',
content: 'px-5 pb-5 pt-3 text-gray-800 font-mono text-xs',
section: 'mb-4',
sectionTitle:
'text-gray-700 text-xs uppercase tracking-wider mb-2 font-sans font-bold',
codeBlock:
'whitespace-pre-wrap max-h-[200px] overflow-auto text-gray-900 bg-gray-50 p-3 rounded border border-gray-200',
chevron: {
base: 'text-gray-700 mr-2 transition-transform duration-200',
open: 'rotate-90',
hover: 'group-hover:text-gray-900',
},
contentWrapper: {
base: 'overflow-hidden transition-all duration-300 ease-in-out',
open: 'max-h-[600px] opacity-100',
closed: 'max-h-0 opacity-0',
},
}
// Status indicator colors
const statusColors = {
complete: 'bg-emerald-500 shadow-emerald-500/40',
inProgress: 'bg-amber-500 shadow-amber-500/40',
executing: 'bg-blue-500 shadow-blue-500/40',
}
// 简化后的 format 函数 const format = (content: any): React.ReactNode => { if (!content) return null return typeof content === 'object' ? ( <span>{JSON.stringify(content, null, 2)}</span> ) : ( <span>{String(content)}</span> ) }
const getStatusColor = () => {
const baseColor = statusColors[status].split(' ')[0]
const shadowColor = statusColors[status].split(' ')[1]
return ${baseColor} ${status === 'inProgress' || status === 'executing' ? 'animate-pulse' : ''} shadow-[0_0_10px] ${shadowColor}
}
return (
<div className={classes.container}>
<div className={classes.header} onClick={() => setIsOpen(!isOpen)}>
<ChevronRight isOpen={isOpen} chevronClasses={classes.chevron} />
<span className={classes.title}>{name || 'MCP Tool Call'}</span>
<div className={classes.statusContainer}>
<span className={classes.statusText}>
{status === 'complete'
? 'Completed'
: status === 'inProgress'
? 'In Progress'
: 'Executing'}
</span>
<div className={h-3 w-3 rounded-full ${getStatusColor()}} />
</div>
</div>
<div
className={${classes.contentWrapper.base} ${isOpen ? classes.contentWrapper.open : classes.contentWrapper.closed}}
>
<div className={classes.content}>
<div className={classes.section}>
<div className={classes.sectionTitle}>Name</div>
<pre className={classes.codeBlock}>{name}</pre>
</div>
{args && (
<div className={classes.section}>
<div className={classes.sectionTitle}>Parameters</div>
<pre className={classes.codeBlock}>{format(args)}</pre>
</div>
)}
{status === 'complete' && result && ( <div className={classes.section}> <div className={classes.sectionTitle}>Result</div> <pre className={classes.codeBlock}>{format(result)}</pre> </div> )} </div> </div> </div> ) }
const ChevronRight = ({ isOpen, chevronClasses, }: { isOpen: boolean chevronClasses: any }) => { return ( <svg width="16" height="16" v
步骤 6:组合所有组件。
创建完所有独立组件后,就可以在 `src/app/page.tsx` 中将它们组合起来。
'use client'
import { CopilotChat } from '@copilotkit/react-ui' import McpServerManager from '../components/McpServerManager' import { ToolRenderer } from '../components/ToolRenderer'
export default function Page() { return ( <div className="flex h-screen p-4"> <McpServerManager /> <CopilotChat instructions="You are a helpful assistant with access to MCP servers." className="w-full flex-grow rounded-lg" /> <ToolRenderer /> </div> ) }
恭喜!🎉 你的 CopilotKit 和 MCP 服务器已经配置完成!
步骤 7:添加 MCP 服务器端点
现在,我们只需要添加端点 URL,以连接外部 MCP 服务器。具体做法是在 `McpServerManager` 组件中,通过 `endpoint` 属性传入 URL,如下所示。
setMcpServers([ { // Try a sample MCP server at https://mcp.composio.dev/ endpoint: 'your_mcp_sse_url', }, ])
有两种更简单的方式,可以立即连接 100 多个 MCP 服务器:
1)Composio——Composio 提供完全托管的服务器,无需进行复杂配置,并且内置身份验证。它支持 250 多种工具,并提供 20,000 多个预构建 API 操作,无需编写代码即可完成集成。
它既可以在本地运行,也可以远程运行。同时,它还能提供更高的工具调用准确率,让 AI 智能体能够顺畅地与集成应用交互。
这也意味着更少的停机时间和维护问题。你可以前往 `status.composio.dev/` 查看服务状态。
在每个选项中,你都可以看到活跃用户总数、当前版本、最近更新时间以及所有可用操作。
你可以找到使用 TypeScript 和 Python 安装它的说明;它还支持将 Claude(MacOS)、Windsurf(MacOS)和 Cursor 用作 MCP 宿主。
2)Zapier——Zapier 支持 8,000 多个应用,但每小时只能进行 80 次工具调用,这可能会成为一项限制。
如果其中一个服务器出现问题,只需使用另一个选项即可。
🔧 MCP 服务器故障排查
最简单的方法是使用 Cursor 或 Claude 等客户端(在 Cursor 中只需运行一条 `npx` 命令),然后打开聊天界面测试工具调用。
如果你不确定 MCP 服务器能否正常工作:⚡ 直接在浏览器中打开 MCP 服务器 URL。如果它返回数据流(通常是多行 JSON),就说明它正在工作。⚡ 检查浏览器 DevTools 中是否存在网络错误或控制台错误。⚡ 有时过期连接会导致无提示的失败,重启开发服务器即可解决这个问题。
步骤 8:添加 MCP 端点并运行服务器。
你可以使用 `npm run dev` 在 Next.js 应用中运行服务器。此时应该能看到聊天界面。
整个流程与上一个示例非常相似。我仍然使用位于 `https://mcp.composio.dev/gmail` 的 Gmail 服务器,帮助你理解从头配置与通过 CLI 配置所产生的结果有何不同。
添加端点后,只需输入提示词,你就能看到工具调用,因为我们使用了 `ToolRenderer` 组件。
与之前一样,它会请求批准,并根据要求发送电子邮件。我已经确认,电子邮件已成功送达我的收件箱。
下一节中,我们将介绍一个复杂示例。
## 4. 构建一个工作记忆项目,通过 Linear 等工具管理任务和项目
让我们研究一个工作记忆项目。它将教会我们如何使用 MCP 和 CopilotKit 构建交互式、由 AI 驱动的聊天应用,并与 Linear、Slack 等外部工具连接。
Next.js(App Router)+ TypeScript
CopilotKit React Core(状态管理)
CopilotKit React UI(AI 聊天)
Tailwind CSS(样式)
React Query(数据获取)
Framer Motion(动画)
Radix UI(无障碍组件)
React Flow(用于可视化的流程图)
继续之前,请确保你已经安装 Node.js(推荐使用 LTS 版本)。