摘要
在 AI 原生时代,前端不再只是静态页面的渲染器,而是 AI 与用户之间的交互舞台。模型上下文协议(MCP)为前端提供了新的交互范式:会话化、工具化、流式化。本文聚焦前端实践,深入解析 MCP 在前端架构中的角色,提供完整的 React Hook 实现、流式渲染策略、用户可控性设计与安全治理方案。通过案例与代码,展示如何让前端真正"舞动" MCP,成为 AI Agent 的最佳舞伴。
关键词
MCP;前端架构;React Hook;流式渲染;用户体验
目录
- 引言
- MCP 与前端的关系
- 架构演进:从直连到舞动
- React Hook 实战:useMcp 与 useMcpTool
- 流式渲染与用户体验设计
- 安全边界与可观测性
- 案例演示:AI 驱动的订单查询前端
- 结论与三步行动清单
- 附录与参考资源
1 引言
在第1篇中,我们强调了 MCP 的定位与能力模型。本篇将视角转向前端:如何让前端在 MCP 的支持下,既保持轻量,又能承载 AI 的复杂交互。我们将从架构演进、Hook 实现、流式渲染、用户可控性、安全治理等方面展开,最终形成一套可落地的前端实践指南。
2 MCP 与前端的关系
- 前端是舞台:用户与 AI 的交互都在前端发生。
- MCP 是编舞者:负责工具发现、会话管理、流式输出。
- AI Agent 是舞者:在 MCP 的编排下执行动作,前端负责呈现。
类比卡片
REST 像点菜,前端只是菜单展示;
MCP 像舞蹈,前端是舞台,AI 是舞者,MCP 是编舞者。
3 架构演进:从直连到舞动
3.1 传统模式
前端直接调用多个 REST API,认证、错误处理、格式适配分散在前端,导致复杂度高。
3.2 MCP 模式
前端只与 MCP Server 通信,后端能力统一封装为工具。前端关注 UX 与会话呈现,复杂逻辑集中到 MCP Server。
架构流程图
User Frontend MCPServer REST API gRPC Service Database Low-code Platform
4 React Hook 实战:useMcp 与 useMcpTool
4.1 Hook 设计原则
- 统一调用接口:前端组件通过 Hook 调用 MCP 工具。
- 流式支持:支持 SSE/WebSocket,逐步更新 UI。
- 错误与取消:提供取消与错误处理机制。
- 可扩展性:网络层抽象为适配器,便于替换。
4.2 useMcpTool 完整代码(示例)
tsx
import { useState, useRef, useCallback } from 'react';
export function useMcpTool<T = any>(toolName: string) {
const [data, setData] = useState<T | null>(null);
const [chunks, setChunks] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const abortRef = useRef<AbortController | null>(null);
const callTool = useCallback(async (input: any) => {
setLoading(true); setError(null); setChunks([]); setData(null);
abortRef.current?.abort();
const ac = new AbortController();
abortRef.current = ac;
try {
const resp = await fetch('/mcp/call', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${getToken()}` },
body: JSON.stringify({ tool: toolName, input }),
signal: ac.signal
});
if (resp.headers.get('Content-Type')?.includes('stream')) {
const reader = resp.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
try {
setChunks(prev => [...prev, JSON.parse(line)]);
} catch {
setChunks(prev => [...prev, line]);
}
}
}
} else {
const result = await resp.json();
setData(result.result);
}
} catch (e: any) {
setError(e.message);
} finally {
setLoading(false);
}
}, [toolName]);
const cancel = useCallback(() => {
abortRef.current?.abort();
abortRef.current = null;
}, []);
return { data, chunks, loading, error, callTool, cancel };
}
function getToken() {
return sessionStorage.getItem('mcp_token') || '';
}
4.5 Vue 版实战:useMcpTool(组合式 API)
设计思路
- 组合式 API :用
ref管理状态,用watchEffect或onUnmounted管理副作用。 - 统一调用接口 :暴露
callTool与cancel方法。 - 流式支持 :解析 SSE/流式响应,逐步更新
chunks。 - 错误与取消:提供错误提示与取消逻辑。
完整代码示例
ts
// useMcpTool.ts (Vue 3 组合式 API)
import { ref, onUnmounted } from 'vue'
export function useMcpTool<T = any>(toolName: string) {
const data = ref<T | null>(null)
const chunks = ref<any[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
let abortCtrl: AbortController | null = null
async function callTool(input: any, opts?: { sessionId?: string }) {
loading.value = true
error.value = null
chunks.value = []
data.value = null
abortCtrl?.abort()
abortCtrl = new AbortController()
try {
const resp = await fetch('/mcp/call', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getShortLivedToken()}`,
'X-Session-Id': opts?.sessionId ?? ''
},
body: JSON.stringify({ tool: toolName, input }),
signal: abortCtrl.signal
})
const contentType = resp.headers.get('Content-Type') || ''
if (contentType.includes('text/event-stream') || contentType.includes('stream')) {
const reader = resp.body!.getReader()
const decoder = new TextDecoder()
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (!line.trim()) continue
try {
const obj = JSON.parse(line)
chunks.value.push({ type: 'partial', payload: obj })
} catch {
chunks.value.push({ type: 'event', payload: line })
}
}
}
} else {
const result = await resp.json()
if (result.error) throw new Error(result.error.message || 'tool error')
data.value = result.result
}
} catch (e: any) {
error.value = e.name === 'AbortError' ? 'cancelled' : e.message
} finally {
loading.value = false
}
}
function cancel() {
abortCtrl?.abort()
abortCtrl = null
}
onUnmounted(() => {
abortCtrl?.abort()
})
return { data, chunks, loading, error, callTool, cancel }
}
function getShortLivedToken() {
return sessionStorage.getItem('mcp_token') || ''
}
使用示例(Vue 组件中)
vue
<script setup lang="ts">
import { useMcpTool } from './useMcpTool'
const { data, chunks, loading, error, callTool, cancel } = useMcpTool('orders.query')
function queryOrders() {
callTool({ date: '2025-12-11' })
}
</script>
<template>
<div>
<button @click="queryOrders">查询订单</button>
<button @click="cancel">取消</button>
<div v-if="loading">正在查询...</div>
<div v-if="error">错误: {{ error }}</div>
<div v-if="data">结果: {{ data }}</div>
<ul>
<li v-for="(chunk, i) in chunks" :key="i">
{{ chunk.type }}: {{ chunk.payload }}
</li>
</ul>
</div>
</template>
Vue 版的优势
- 更贴近国内前端团队:Vue 在国内应用广泛,提供 Vue 版能降低学习成本。
- 响应式数据流 :
ref与模板绑定天然适合流式渲染。 - 生命周期管理 :
onUnmounted自动清理,避免内存泄漏。 - 组合式 API:逻辑清晰,可在不同组件中复用。
5 流式渲染与用户体验设计
5.1 渐进式呈现
在流式输出中展示"AI 正在思考"的中间片段,并标注来源(模型推理 / 工具调用)。
5.2 用户可控性
- 中止按钮:允许用户随时取消。
- 确认弹窗:高风险操作需确认。
- 详情面板:显示调用详情,增强透明度。
5.3 可观测性
记录关键事件(请求发起、流式片段接收、错误),并与后端审计日志关联。
6 安全边界与可观测性
6.1 最小权限原则
前端仅持有最小权限凭证,避免长期暴露高权限令牌。
6.2 输入输出校验
对工具参数与返回值进行严格校验,防止注入或越权调用。
6.3 审计链路
在前端记录会话 ID、用户 ID、工具名、时间戳等关键事件,并与后端审计日志关联。
7 案例演示:AI 驱动的订单查询前端
场景 :用户输入"帮我查一下昨天的订单"。
流程:
- 前端调用 MCP 工具
orders.query。 - MCP Server 封装 REST API,返回订单数据。
- 前端流式渲染结果,用户可中止或查看详情。
表格示例
| 时间 | 订单号 | 客户 | 金额 |
|---|---|---|---|
| 2025-12-11 | ORD123 | 张三 | ¥1200 |
| 2025-12-11 | ORD124 | 李四 | ¥800 |
8 结论与三步行动清单
核心结论
MCP 为前端提供了新的交互范式:会话化、工具化、流式化。前端在 MCP 的支持下,不仅简化了集成,还提升了用户体验与安全治理。
三步行动清单
- 实现 Hook :在现有项目中实现
useMcpTool。 - 流式渲染:优化前端 UI,支持渐进式输出。
- 治理策略:设计最小权限与审计链路,确保安全。
9 附录与参考资源
- Anthropic --- Introducing the Model Context Protocol
- MCP Specification 官方文档
- MCP 社区 SDK 示例仓库
- Power Apps 自定义连接器文档
- OAuth 2.0 授权码流实践指南