MCP 的 AI Agent 應用開發實踐
最近大家都在聊 MCP,發現有個最重要的點被忽略了: 通過標準化協議,將工具提供方與應用研發者解耦,這一點帶來的將是 AI Agent 應用研發范式的轉移(類似 Web 應用研發的前后端分離)。
本文以開發 Agent TARS(https://agent-tars.com/) 應用為例,盡可能詳細地介紹 MCP 在開發范式、工具生態擴展上起到的作用。
名詞解釋
AI Agent:在 LLM 語境下,AI Agent 是某種能自主理解意圖、規劃決策、執行復雜任務的智能體。Agent 并非 ChatGPT 升級版,它不僅告訴你“如何做”,更會幫你去做。如果 Copilot 是副駕駛,那么 Agent 就是主駕駛。類似人類“做事情”的過程,Agent 的核心功能,可以歸納為三個步驟的循環:感知(Perception)、規劃(Planning)和行動(Action)。
Copilot:Copilot 是指一種基于人工智能的輔助工具,通常與特定的軟件或應用程序集成,旨在幫助用戶提高工作效率。Copilot 系統通過分析用戶的行為、輸入、數據和歷史記錄,提供實時建議、自動化任務或增強功能,幫助用戶做出決策或簡化操作。
MCP:Model Context Protocol(模型上下文協議)是一個開放協議,它規范了應用程序如何為 LLMs 提供上下文。可以將 MCP 想象為 AI 應用的 USB-C 端口。就像 USB-C 提供了一種標準方式,讓你的設備連接到各種外設和配件,MCP 也提供了一種標準方式,讓你的 AI 模型連接到不同的數據源和工具。
Agent TARS:一個開源的多模態人工智能代理,提供與各種真實世界工具的無縫集成。
RESTful API:RESTful 是一種軟件架構風格、設計風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用于客戶端和服務器交互類的軟件。
背景
AI 從最初只能對話的 Chatbot,輔助人類決策的 Copilot,再到能自主感知和行動的 Agent,AI 在任務中的參與度不斷提升。這要求 AI 擁有更豐富的任務上下文 (Context),并擁有執行行動所需的工具集 (Tools)。
痛點
缺少標準化的上下文和工具集導致開發者的三大痛點:
- 開發耦合度高:工具開發者需要深入了解 Agent 的內部實現細節,并在 Agent 層編寫工具代碼。這導致在工具的開發與調試困難。
- 工具復用性差:因每個工具實現都耦合在 Agent 應用代碼內,即使是通過 API 實現適配層在給到 LLM 的出入參上也有區別。從編程語言角度來講,沒辦法做到跨編程語言進行復用。
- 生態碎片化:工具提供方能提供的只有 OpenAPI,由于缺乏標準使得不同 Agent 生態中的工具 Tool 互不兼容。
目標
"All problems in computer science can be solved by another level of indirection" -- Butler Lampson在計算機科學中,任何問題都可以通過一個抽象層解決。
將工具從 Agent 層解耦出來,單獨變成一層 MCP Server 層,并對開發、調用進行標準化。MCP Server 為上層 Agent 提供上下文、工具的標準化調用方式。
演示
從幾個例子中看 MCP 在 AI Agent 應用中發揮的作用。
例 1 (不構成投資建議,使用的是券商模擬賬戶下單)
- 指令:從技術面分析下股票,然后以市價買入 3 股股票
- 回放:
- 使用的 MCP Servers:
a.券商 MCP:https://github.com/longportapp/openapi/tree/main/mcp
b.文件系統 MCP:https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem
例 2
- 指令:我的機器中的 CPU、內存和網絡速度分別是多少?
- 回放:
- 使用的 MCP Servers:
a.命令行 MCP:https://github.com/g0t4/mcp-server-commands
b.代碼執行 MCP:https://github.com/formulahendry/mcp-server-code-runner
例 3
- 指令:在 ProductHunt 上找到點贊數最高的前 5 款產品
- 回放:
- 使用的 MCP Servers:
例 4
- 指令:根據這篇文章,調研下各個 Agent 框架的各維度對比,并生成一個報告 markdown
- 回放:
- 使用的 MCP Servers:Link Reader
已支持自定義 MCP Servers !415(https://github.com/bytedance/UI-TARS-desktop/pull/415) !489(https://github.com/bytedance/UI-TARS-desktop/pull/489),可以添加 Stdio、Streamable HTTP、SSE 類型的 MCP Server。
更多:https://agent-tars.com/showcase
介紹
什么是 MCP?
Model Context Protocol(模型上下文協議)是 Anthropic 在推出的用于 LLM 應用和外部數據源(Resources)或工具(Tools)通信的標準協議,遵循 JSON-RPC 2.0 的基礎消息格式。
可以把 MCP 想象成 AI 應用程序的 USB-C 接口,規范了應用程序如何為 LLMs 提供上下文。
架構圖如下:
- MCP Client:通過 MCP 協議與 Servers 通信,并保持 1:1 連接。
- MCP Servers:上下文提供方,暴露外部數據源(Resources)、工具(Tools)、提示詞(Prompts)等由 Client 進行調用。
- 語言支持層面:TypeScript 和 Python、Java、Kotlin、C#。
流程圖
一句話解釋就是 MCP 提供給 LLM 所需的上下文:Resources 資源、Prompts 提示詞、Tools 工具。
MCP 和 Function Call 區別?
MCP | Function Call | |
定義 | 模型和其他設備集成的標準接口,包含:
| 將模型連接到外部數據和系統,平鋪式的羅列 Tools 工具。 和 MCP Tools 的不同在于:MCP Tools 的函數約定了輸入輸出的協議規范。 |
協議 | JSON-RPC,支持雙向通信(但目前使用不多)、可發現性、更新通知能力
| JSON-Schema,靜態函數調用
|
調用方式 | Stdio/SSE/Str/eamable HTTP/同進程調用(見下文) | 同進程調用/編程語言對應的函數 |
適用場景 | 更適合動態、復雜的交互場景 | 單一特定工具、靜態函數執行調用 |
系統集成難度 | 高 | 簡單 |
工程化程度 | 高 | 低 |
從前后端分離看 MCP
早期 Web 開發在 JSP、PHP 盛行時,前端交互頁面都是耦合在后端邏輯里的,造成開發復雜度高、代碼維護困難、前后端協作不便,難以適應現代 Web 應用對用戶體驗和性能的更高要求。
AJAX、Node.js、RESTful API 推動前后端分離,對應 MCP 也正在實現 AI 開發的“工具分層”:
- 前后端分離:前端專注界面,后端專注 API 接口;
- MCP 分層:讓工具開發者和 Agent 開發者各司其職,工具質量和功能的迭代不需要 Agent 開發者感知。這種分層讓 AI Agent 開發者能像搭積木一樣組合工具,快速構建復雜 AI 應用。
實踐
整體設計
以 MCP Browser 瀏覽器工具(https://github.com/bytedance/UI-TARS-desktop/tree/main/packages/agent-infra/mcp-servers/browser)開發和接入為例,逐步解析具體實現。
在設計 Browser MCP Server 時,并沒有采用官方的 stdio call 方式(即通過 npx
方式跨進程調用)。原因是為了降低使用門檻,避免用戶在首次使用時先安裝 Npm、Node.js 或 UV,從而影響 Agent 開箱即用的體驗(相關 issue#64:https://github.com/modelcontextprotocol/servers/issues/64)。
因此,Agent 工具的設計分為兩類:
- 內置 MCP Servers:完全遵循 MCP 規范,同時支持 Stdio 和函數調用。(換句話說用 MCP 標準開發 Function Call)
- 用戶自定義 MCP Servers:針對需要擴展功能的用戶,默認他們已經具備 Npm 或 UV 環境,因此可以支持更靈活的擴展方式。
兩者的區別:
- 內置 MCP Servers 是完成當前 Agent 功能的必備工具
- 調用方式:內置方式不需要 Agent 用戶安裝 Node.js / npm,這對于普通用戶比較友好。
MCP Server 開發
以 mcp-server-browser 為例,其實就是一個 npm 包,package.json
配置如下:
{
"name": "mcp-server-browser",
"version": "0.0.1",
"type": "module",
"bin": {
"mcp-server-browser": "dist/index.cjs"
},
"main": "dist/server.cjs",
"module": "dist/server.js",
"types": "dist/server.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "rm -rf dist && rslib build && shx chmod +x dist/*.{js,cjs}",
"dev": "npx -y @modelcontextprotocol/inspector tsx src/index.ts"
}
}
bin
指定通過 stdio 調用的入口文件。main
、module
執行通過 Function Call 同進程調用的入口文件。
開發(dev)
實踐下來,通過 Inspector(https://modelcontextprotocol.io/docs/tools/inspector) 來開發調試 MCP Server 是比較好,Agent 與工具解耦,可以單獨調試和開發工具。
直接運行 npm run dev
啟動一個 Playground,里面包含 MCP Server 可調試的功能(Prompts、Resources、Tools 等)。
$ npx -y @modelcontextprotocol/inspector tsx src/index.ts
Starting MCP inspector...
New SSE connection
Spawned stdio transport
Connected MCP client to backing server transport
Created web app transport
Set up MCP proxy
?? MCP Inspector is up and running at http://localhost:5173 ??
注:用 Inspector 調試開發 Server 時,console.log 是無法顯示的,這點 debug 確實有點麻煩。
實現(Implement)
啟動入口(Entry)
為了內置 MCP Server 可以當Function call 同進程調用,這里在入口文件 src/server.ts 中導出三個共用方法:
listTools
:列舉所有函數callTool
:調用具體函數close
:Server 不使用后的清理函數
// src/server.ts
export const client: Pick<Client, 'callTool' | 'listTools' | 'close'> = {
callTool,
listTools,
close,
};
同時 Stdio 調用支持,直接在 src/index.ts
導入模塊即可使用。
#!/usr/bin/env node
// src/index.ts
import { client as mcpBrowserClient } from "./server.js";
const server = new Server(
{
name: "example-servers/puppeteer",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
// listTools
server.setRequestHandler(ListToolsRequestSchema, mcpBrowserClient.listTools);
// callTool
server.setRequestHandler(CallToolRequestSchema, async (request) =>
return await mcpBrowserClient.callTool(request.params);
);
async functionrunServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
runServer().catch(console.error);
process.stdin.on("close", () => {
console.error("Browser MCP Server closed");
server.close();
});
工具定義(Definition)
MCP 協議要求用 JSON Schema 約束工具的入參、出參,這里實踐下來:推薦用 zod (https://github.com/colinhacks/zod) 來定義一套 Zod Schema,導出到 MCP 時再將 zod 轉成 JSON Schema。
import { z } from 'zod';
const toolsMap = {
browser_navigate: {
description: 'Navigate to a URL',
inputSchema: z.object({
url: z.string(),
}),
handle: async (args) => {
// Implements
const clickableElements = ['...']
return {
content: [
{
type: 'text',
text: `Navigated to ${args.url}\nclickable elements: ${clickableElements}`,
},
],
isError: false,
}
}
},
browser_scroll: {
name: 'browser_scroll',
description: 'Scroll the page',
inputSchema: z.object({
amount: z
.number()
.describe('Pixels to scroll (positive for down, negative for up)'),
}),
handle: async (args) => {
return {
content: [
{
type: 'text',
text: `Scrolled ${actualScroll} pixels. ${
isAtBottom
? 'Reached the bottom of the page.'
: 'Did not reach the bottom of the page.'
}`,
},
],
isError: false,
};
}
},
// more
};
const callTool = async ({ name, arguments: toolArgs }) => {
return handlers[name].handle(toolArgs);
}
技巧 Tips:
與 OpenAPI 返回結構化數據不同,MCP 的返回值專為 LLM 模型設計。為了更好地連接模型與工具,返回的文本和工具的描述 description
應更具語義化,從而提升模型的理解能力,提高工具調用的成功率。
例如,browser_scroll(瀏覽器滾動)
在每次執行工具后,應返回頁面的滾動狀態(如距底部剩余像素、是否已到底等)。這樣模型在下次調用工具時即可精準提供合適的參數。
Agent 集成
開發完 MCP Server 后,需要在 Agent 應用中進行集成。原則上,Agent 無需關注 MCP Servers 提供的工具、入參和出參的具體細節。
MCP Servers 配置
在 MCP Servers 配置中分為「內置 Server」和「用戶擴展 Server」,內置 Server 通過同進程 Function Call 調用,保證 Agent 應用對小白用戶開箱即用,擴展 Server 則提供給高級用戶擴展 Agent 上限功能。
{
// Internal MCP Servers(in-process call)
fileSystem: {
name: 'fileSystem',
localClient: mcpFsClient,
},
commands: {
name: 'commands',
localClient: mcpCommandClient,
},
browser: {
name: 'browser',
localClient: mcpBrowserClient,
},
// External MCP Servers(remote call)
fetch: {
command: 'uvx',
args: ['mcp-server-fetch'],
},
longbridge: {
command: 'longport-mcp',
args: [],
env: {}
}
}
MCP Client
MCP Client 的核心任務是集成不同調用方式(Stdio / SSE / Streamable HTTP / Function Call)的 MCP Server。Stdio 和 SSE 方式直接復用了官方示例(https://modelcontextprotocol.io/quickstart/client),這里主要介紹下我們對 Function Call 調用是怎樣在 Client 中支持的。
Function Call 調用
exporttype MCPServer<ServerNames extends string = string> = {
name: ServerNames;
status: 'activate' | 'error';
description?: string;
env?: Record<string, string>;
+ /** in-process call, same as function call */
+ localClient?: Pick<Client, 'callTool' | 'listTools' | 'close'>;
/** Stdio server */
command?: string;
args?: string[];
};
MCP Client 調用方式如下:
import { MCPClient } from '@agent-infra/mcp-client';
import { client as mcpBrowserClient } from '@agent-infra/mcp-server-browser';
const client = new MCPClient([
{
name: 'browser',
description: 'web browser tools',
localClient: mcpBrowserClient,
}
]);
const mcpTools = await client.listTools();
const response = await openai.chat.completions.create({
model,
messages,
// Different model vendors need to convert to the corresponding tools data format.
tools: convertToTools(tools),
tool_choice: 'auto',
});
至此,MCP 的整體流程已全部實現,涵蓋了從 Server 配置、Client 集成到與 Agent 的銜接等各個環節。更多 MCP 細節/代碼已開源到 Github:
- Agent 集成:https://github.com/bytedance/UI-TARS-desktop/blob/fb2932afbdd54da757b9fae61e888fc8804e648f/apps/agent-tars/src/main/llmProvider/index.ts#L89-L91
- mcp-client:https://github.com/bytedance/UI-TARS-desktop/tree/main/packages/agent-infra/mcp-client
- mcp-servers:https://github.com/bytedance/UI-TARS-desktop/tree/main/packages/agent-infra/mcp-servers
Remote 遠程調用
如果是 Web 應用(無法使用 Stdio MCP Server),可以用 FaaS 將 Stdio 轉成 SSE MCP Server,從而在 Function Call 的基礎上無縫支持 MCP 類型的 Tools,這個過程換句話講是「MCP Servers 云化」。
調用代碼示例:
import asyncio
import openai
import json
from agents.mcp import MCPUtil
from agents.mcp import MCPServerSse
from agents import set_tracing_disabled
set_tracing_disabled(True)
async def chat():
client = openai.AzureOpenAI(
azure_endpoint=base_url,
api_versinotallow=api_version,
api_key=ak,
)
+ async with MCPServerSse(
+ name="fetch", params={"url": "https://{mcp_faas_id}.mcp.bytedance.net/sse"}
+ ) as mcp_server:
+ tools = await MCPUtil.get_function_tools(
+ mcp_server, convert_schemas_to_strict=False
+ )
prompt = "請求下 https://agent-tars.com/,主要是做什么的?"
completion = client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tokens,
+ tools=[
+ {
+ "type": "function",
+ "function": {
+ "name": tool.name,
+ "description": tool.description,
+ "parameters": tool.params_json_schema,
+ },
+ }
+ for tool in tools
+ ],
)
for choice in completion.choices:
if isinstance(choice.message.tool_calls, list):
for tool_call in choice.message.tool_calls:
if tool_call.function:
+ tool_result = await mcp_server.call_tool(
+ tool_name=tool_call.function.name,
+ arguments=json.loads(
+ tool_call.function.arguments
+ ), # or jsonrepair
+ )
+ print("tool_result", tool_result.content)
if __name__ == '__main__':
asyncio.run(chat())
思考
生態
MCP 生態不斷發展壯大,越來越多的應用支持 MCP,同時開放平臺也提供 MCP Server。同時也有像 Cloudflare、Composio、Zapier 使用 SSE 方式將 MCP 進行托管(即接入一個 MCP Endpoint 即接入一批 MCP Servers),通過 Stdio 方式最理想場景是 MCP Servers 和 Agent 系統跑在同一 Docker 容器中(類似 Sidecar 模式)。
MCP 生態圖
舉個例子:接入地圖廠商的 MCP Server 后,Agent 具備生活服務工具能力,遠遠優于單純依賴搜索的方式。
未來
- 目前的 MCP 開發非常初級,在工程化上缺少一套完善的框架來約束和規范。
- 根據 MCP Roadmap(https://modelcontextprotocol.io/development/roadmap),未來主要三件事:
a.Remote MCP Support:鑒權、服務發現、無狀態服務,很明顯奔著 K8S 架構去的,這樣才能構建一個生產級、可擴展的 MCP 服務。根據最近的 RFC Replace HTTP+SSE with new "Streamable HTTP" transport(https://github.com/modelcontextprotocol/modelcontextprotocol/pull/206),支持 Streamable HTTP,可以低延遲、雙向傳輸。
b.Agent Support:提升不同領域復雜的 Agent 工作流,并可以處理更好的人機交互。
c.Developer Ecosystem:更多的開發者和大廠商參與進來,才能擴展 AI Agent 的能力邊界。
- 實踐下來,MCP Server SSE 并不是理想的方案,因為需要保持連接和 session 狀態,而云服務(如 FaaS)更傾向于無狀態架構(issue#273:https://github.com/modelcontextprotocol/typescript-sdk/issues/273#issuecomment-2789489317),所以最近提出了更適配云場景的 Streamable HTTP Transport。
- MCP 模型調用與 RL 強化學習:如果 MCP 成為未來的規范,那么 Agent 應用能否準確調用各個 MCP,將成為模型 RL 未來需要支持的關鍵功能。與 Function Call 模型不同,MCP 是一個動態的工具庫,模型需要具備對新增 MCP 的泛化理解能力。
- Agent K8s:雖然目前 LLM 和上下文之間建立了標準化的通信協議,但 Agent 之間的交互協議尚未形成統一標準,Agent 服務發現、恢復、監控等一系列生產級問題等解決。目前 Google 的 A2A(Agent2Agent) 和社區的 ANP(Agent Network Protocol)在做這方面的探索與嘗試。
參考:
- 詳解 MCP:Agentic AI 中間層最優解,AI 應用的標準化革命
- a16z詳解MCP,以及AI工具的未來
- How to Build an MCP Server Fast: A Step-by-Step Tutorial:https://medium.com/@eugenesh4work/how-to-build-an-mcp-server-fast-a-step-by-step-tutorial-e09faa5f7e3b
- 智能體互聯網的三大趨勢:連接范式正在徹底重構:https://zhuanlan.zhihu.com/p/31870979392
- 有了 MCP,AI 才完整:https://www.youtube.com/watch?v=ElX5HO0wXyo
- 硅谷 101:AI Agent爆發前的黎明:Manus 不夠好,但天快亮了:https://www.youtube.com/watch?v=2PSCnOFkR3U
- MCP 終極指南:https://guangzhengli.com/blog/zh/model-context-protocol
- OpenAI 支持 MCP:https://x.com/sama/status/1904957253456941061