从零实现Agent搜索MCP Server:接入Claude、Dify与Cursor
2026/8/28 17:35:29 网站建设 项目流程

先分享一个最近的实践体会:和朋友讨论 Agent 生态时,发现很多团队都在做自己的 Agent 市场或者 Agent 目录,但用户真正想用的时候,往往要在网页里搜到 Agent 说明,再去对应客户端里手动配置。整个流程非常割裂。MCP(Model Context Protocol)出现后,这个体验可以做成闭环:把“搜索 AI Agent”的能力封装成一个 MCP Server,任何支持 MCP 的客户端都能直接调用,不用重复开发集成层。

这篇文章就来拆解这个思路。我们会从 MCP 的核心概念讲起,然后从零实现一个“Agent 搜索类 MCP Server”,最后分别接入 Claude Desktop、Dify、Cursor 这类常见客户端,并整理实际开发中容易踩的坑。

如果你正在做 Agent 平台、AI 工具聚合、或者想给自己的业务加一个“AI 能直接调用的搜索能力”,这篇文章会比较适合你。学完后你能理解 MCP Server 的完整开发流程,也能照着代码跑通一个本地服务。

1. 弄清楚 MCP 是什么,以及为什么要用它

1.1 MCP 解决的核心问题

MCP 的全称是 Model Context Protocol,中文一般叫“模型上下文协议”。它由 Anthropic 提出并开源,目标是统一 AI 应用与外部数据源、工具之间的连接方式。

在 MCP 出现之前,每接入一个 AI 工具,开发团队差不多都要写一套专用集成:给 Claude 写一套工具调用,给 ChatGPT 写一套 Plugin,给 Dify 写一套自定义工具,给 Cursor 再写一套扩展。这种“点对点”集成的维护成本很高,而且 AI Agent 每换一个客户端,能力就无法复用了。

MCP 把连接方式标准化以后,“三方关系”变得很清晰:

  • MCP Host:承载 AI 能力的进程,比如 Claude Desktop、Dify、Cursor、自研 Agent。
  • MCP Client:Host 内部与 Server 建立连接的组件,负责协议通信。
  • MCP Server:对外提供工具、资源、提示词等能力的服务,通过 MCP 协议暴露给 Client。

也就是说,你的 AI Agent 搜索能力只需要实现一次,做成一个 MCP Server,就能被不同客户端复用。

1.2 为什么是“Search AI Agents from Any MCP Client”

现在很多 AI Agent 平台会提供 Agent 市场,用户可以按分类、标签、评分搜索 Agent。但搜索入口通常停留在 Web 页面。如果让 AI 助手能直接调用这个搜索能力,用户就可以在聊天中完成:搜索某个领域 Agent、查看其能力描述、获取调用建议,甚至一键拉起对话。

要做到这一点,最合适的方式不是再去开发一款新的 App,而是把 Agent 搜索封装成 MCP Server。只要对方客户端支持 MCP,就能通过自然语言触发搜索工具。这也是“Buy My Agent MCP Server”这类产品能成立的原因:它在卖的不是传统 API,而是“AI 可以直接使用的服务能力”。

1.3 MCP Server 与普通 API Server 的区别

很多开发者会问:既然我有 REST API,为什么还要转成 MCP?

REST API 解决的是“程序调用程序”的问题,AI 要使用它,还需要额外知道接口地址、鉴权方式、参数格式、返回结构,这些都需要人工写代码对接。

MCP Server 在协议层就解决了这个问题。它把接口描述、参数结构、调用权限这些信息以标准化的元数据暴露给 Client。AI Agent 通过 MCP 协议自动发现工具能力,按标准格式传参,再拿到结构化返回结果。从“教 AI 怎么用你的接口”变成了“AI 通过协议自然学会用你的工具”。

2. 环境准备与版本说明

2.1 运行环境

本文的实操案例使用 Node.js + TypeScript,因为官方 SDK 支持较完整,生态也比较成熟。如果你更熟悉 Python,也可以参考类似的思路使用mcpPython SDK。

项目说明
操作系统Windows / macOS / Linux 均可
运行时Node.js 18 或更高版本(含 npm)
语言TypeScript(编译后运行)
核心依赖@modelcontextprotocol/sdkzod
测试客户端Claude Desktop、Dify、Cursor 或任意 MCP Client

版本需要根据你的项目实际情况调整。MCP 协议本身迭代速度不慢,本文示例以常见环境为例,重点演示配置思路,具体 API 以你安装的 SDK 版本为准。

2.2 初始化项目

先创建一个新目录,并初始化 npm 项目:

mkdir agent-search-mcp-server cd agent-search-mcp-server npm init -y

然后安装核心依赖:

npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node tsx

创建tsconfig.json

{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["node_modules"] }

这里的关键配置是modulemoduleResolution。Node.js 目前对 ESM 支持成熟,使用 NodeNext 可以避免 CommonJS 和 ESM 之间的兼容问题。

3. MCP Server 核心概念拆解

在写代码之前,需要先理解 MCP Server 暴露能力的三种主要载体。它们是之后写所有工具的基础。

3.1 Tools:让模型执行动作

Tools 是 MCP Server 最常用的一种能力载体。它对应 AI Agent 的“动作”能力,例如搜索 Agent、获取 Agent 详情、提交评价等。

一个 Tool 由三部分组成:

  • 名称:机器可读的唯一标识。
  • 描述:告诉模型这个工具能做什么,模型会依据描述决定是否调用。
  • 输入 Schema:定义参数的结构和约束,通常用 JSON Schema 描述。
server.registerTool( "search_agents", { title: "搜索 AI Agent", description: "根据关键词和分类搜索可用的 AI Agent 列表", inputSchema: { type: "object", properties: { keyword: { type: "string", description: "搜索关键词" }, category: { type: "string", description: "分类筛选,可空" }, limit: { type: "number", minimum: 1, maximum: 20, default: 5 } }, required: ["keyword"] } }, async (args) => { // 业务逻辑 return { content: [{ type: "text", text: "搜索结果" }] }; } );

这里需要注意的是,模型的工具调用并不保证参数一定合理。开发者要在执行阶段做好参数校验和兜底。zod的作用就是帮助我们完成这部分校验。

3.2 Resources:让模型读取上下文

Resources 对应“读取”能力,用于向模型暴露结构化上下文,例如项目文档、Agent 的使用说明、配置文件片段。

server.registerResource({ name: "agent-usage-guide", mimeType: "text/markdown", uri: "guide://usage" }, async (uri) => ({ contents: [{ uri: "guide://usage", mimeType: "text/markdown", text: "# Agent 使用指南\n\n通过搜索工具可以查找可用 Agent。" }] }));

Resource 一般不需要模型“思考要不要调用”,它更像上下文注入。对于 Agent 搜索服务来说,我们可以把热门搜索词、分类白名单做成 Resource,减少模型乱猜分类的概率。

3.3 Prompts:为模型提供可复用的提示词

Prompts 允许 Server 向 Client 暴露预设提示词模板。它适合定义“搜索某种 Agent 的标准表达方式”。

server.registerPrompt( "search-agent-template", { title: "Agent 搜索提示词模板", description: "将用户输入转化为结构化的 Agent 搜索需求", arguments: [ { name: "userInput", description: "用户的原始需求", required: true } ] }, async (args) => ({ messages: [{ role: "user", content: { type: "text", text: `请从用户需求中提取关键词和分类,然后调用搜索工具。用户需求:${args.userInput}` } }] }) );

3.4 通信机制:stdio 与 SSE

MCP Server 与 Client 之间的通信主要分两种。

  • stdio:Server 作为子进程启动,Client 通过标准输入输出传输 JSON-RPC 消息。适合本地开发、桌面客户端接入。
  • SSE(Server-Sent Events):Server 作为独立 HTTP 服务,Client 通过 HTTP 连接。适合远程部署、多客户端共享。

开发初期推荐先用 stdio 模式,调试起来直观,也便于定位问题。需要给远端团队共享时再切换到 SSE。

4. 完整实战:构建 Agent 搜索 MCP Server

下面进入重点章节。我们实现一个本地可运行的 Agent 搜索 MCP Server。它内部维护一份 Agent 列表,通过搜索工具暴露给任何 MCP Client。

4.1 项目结构

agent-search-mcp-server/ ├── package.json ├── tsconfig.json ├── .env.example └── src/ ├── index.ts // 入口,加载配置并启动服务 ├── server.ts // MCP Server 实例与工具注册 ├── types.ts // 类型定义 └── agent-service.ts // Agent 搜索业务逻辑

4.2 定义类型与搜索逻辑

先建立 Agent 数据模型和搜索逻辑。

src/types.ts:

export interface AIAgent { id: string; name: string; description: string; category: string; tags: string[]; author: string; rating: number; endpoint?: string; } export interface SearchResult { total: number; items: AIAgent[]; }

src/agent-service.ts:

import { AIAgent, SearchResult } from "./types.js"; // 模拟的 Agent 数据库,生产环境可替换为 API 或数据库查询 const MOCK_AGENTS: AIAgent[] = [ { id: "agent-code-review-01", name: "CodeReviewGPT", description: "自动审查代码质量,发现潜在 Bug 和安全风险", category: "开发", tags: ["code", "review", "security"], author: "example-team", rating: 4.8 }, { id: "agent-data-analysis-01", name: "DataPilot", description: "连接数据库,通过自然语言生成 SQL 并分析数据", category: "数据分析", tags: ["sql", "database", "analysis"], author: "example-team", rating: 4.6 }, { id: "agent-doc-writer-01", name: "DocWriter", description: "根据对话内容自动生成项目文档和接口文档", category: "效率", tags: ["document", "writing"], author: "example-team", rating: 4.5 } ]; export class AgentService { private agents: AIAgent[]; constructor() { this.agents = MOCK_AGENTS; } async search(keyword: string, category?: string, limit = 5): Promise<SearchResult> { const kw = keyword.trim().toLowerCase(); let filtered = this.agents.filter((agent) => { const matchedKeyword = agent.name.toLowerCase().includes(kw) || agent.description.toLowerCase().includes(kw) || agent.tags.some((tag) => tag.toLowerCase().includes(kw)); const matchedCategory = category ? agent.category === category : true; return matchedKeyword && matchedCategory; }); // 按评分降序,再截取数量 filtered.sort((a, b) => b.rating - a.rating); const items = filtered.slice(0, limit); return { total: items.length, items }; } }

搜索结果按评分排序是关键逻辑之一。用户通过自然语言搜索时,往往期望排在前面的是更可靠的 Agent。

4.3 注册 Tool 到 MCP Server

src/server.ts是核心文件,负责创建 MCP Server 实例并注册工具。

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { AgentService } from "./agent-service.js"; export function createAgentSearchServer() { const server = new McpServer({ name: "agent-search-server", version: "1.0.0" }); const agentService = new AgentService(); server.registerTool( "search_agents", { title: "搜索 AI Agent", description: "根据关键词、分类和数量限制搜索可用的 AI Agent 列表。当用户需要查找 Agent、推荐 AI 工具或解决某个场景问题时,可以调用此工具。", inputSchema: { type: "object", properties: { keyword: { type: "string", description: "搜索关键词,例如:代码审查、数据分析" }, category: { type: "string", description: "分类名称,可选,例如:开发、数据分析、效率" }, limit: { type: "number", description: "最大返回条数,范围 1-10,默认 5" } }, required: ["keyword"] } }, async (args) => { const keyword = args.keyword; const category = args.category || undefined; const limit = args.limit || 5; const result = await agentService.search(keyword, category, limit); return { content: [ { type: "text", text: JSON.stringify(result, null, 2) } ] }; } ); server.registerTool( "get_agent_detail", { title: "获取 Agent 详情", description: "根据 Agent ID 获取某个 AI Agent 的详细信息", inputSchema: { type: "object", properties: { agentId: { type: "string", description: "Agent 的唯一标识" } }, required: ["agentId"] } }, async (args) => { const agents = await agentService.listAll(); const agent = agents.find((item) => item.id === args.agentId); if (!agent) { return { content: [{ type: "text", text: "未找到该 Agent" }], isError: true }; } return { content: [{ type: "text", text: JSON.stringify(agent, null, 2) }] }; } ); return server; }

如果你发现inputSchema的写法在你的 SDK 版本中不生效,可以改用registerTool的 zod 形式:

server.registerTool( "search_agents", { title: "搜索 AI Agent", description: "根据关键词搜索可用的 AI Agent 列表" }, { keyword: z.string().describe("搜索关键词"), category: z.string().optional().describe("分类名称"), limit: z.number().min(1).max(10).default(5).describe("最大返回条数") }, async ({ keyword, category, limit }) => { // 业务逻辑 } );

这种写法在类型推导上更友好,建议优先使用。具体重载形式参照你安装的 SDK 类型声明即可。

4.4 创建服务入口

src/index.ts负责接入 stdio 传输层,把标准输入输出作为 MCP 通信通道。

import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { createAgentSearchServer } from "./server.js"; async function main() { const server = createAgentSearchServer(); const transport = new StdioServerTransport(); await server.connect(transport); console.error("Agent Search MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); });

注意,这里不能用console.log输出调试信息。因为 stdio 模式下,标准输出是 MCP 协议通信通道,所有日志必须走标准错误输出,也就是console.error,否则会破坏 JSON-RPC 消息格式。

4.5 配置 npm scripts 并编译运行

修改package.json中的 scripts:

{ "scripts": { "dev": "tsx src/index.ts", "build": "tsc", "start": "node dist/index.js" } }

启动服务:

npm run dev

服务启动后不会有明显输出,因为它正在等待 MCP Client 通过 stdio 发起连接。此时不要急着 Ctrl+C,我们下一步会把它接入客户端测试。

5. 从任意 MCP Client 连接与测试

5.1 在 Claude Desktop 中添加本地 MCP Server

Claude Desktop 是 MCP 官方支持度最高的客户端之一。配置方式通常是编辑客户端的配置文件。

macOS 路径:

~/Library/Application Support/Claude/claude_desktop_config.json

Windows 路径:

%APPDATA%\Claude\claude_desktop_config.json

添加内容:

{ "mcpServers": { "agent-search": { "command": "node", "args": ["/absolute/path/to/agent-search-mcp-server/dist/index.js"] } } }

修改后重启 Claude Desktop,在会话中提问:“搜索代码审查相关的 AI Agent”。正常情况下,Claude 会调用search_agents工具并返回搜索结果。

如果项目使用tsx直接启动,也可以把 command 改为项目本地的 tsx 执行入口,但生产环境建议先编译为 JS。

5.2 在 Dify 中添加本地 MCP 服务

Dify 支持 MCP 服务的接入。进入工作台后在“工具”或“插件”页面找到 MCP 配置入口,选择“添加 MCP 服务”,类型选择 stdio:

  • 命令:node
  • 参数:/absolute/path/to/agent-search-mcp-server/dist/index.js
  • 环境变量:按需设置

配置完成后,Agent 应用即可通过“工具”节点调用搜索能力。Dify 中需要注意的是,不同版本对 MCP 的配置入口差异较大,如果找不到入口,优先查看对应版本文档,而不是直接复制社区里的老配置。

5.3 通过 .mcp 文件接入支持 MCP 的 IDE

近期的开发工具(例如 Trae、Cursor 等)开始支持.mcp文件作为项目级 MCP 配置。你可以在项目根目录创建.mcp文件:

{ "mcpServers": { "agent-search": { "command": "node", "args": ["/absolute/path/to/agent-search-mcp-server/dist/index.js"] } } }

.mcp文件的作用是把 MCP Server 配置随项目共享,团队其他成员拉取代码后自动获得工具能力。这个配置方式对开发类 Agent 场景尤其合适。

5.4 使用 MCP Inspector 调试

官方提供的@modelcontextprotocol/inspector可以在没有完整客户端的情况下测试 MCP Server。

npx @modelcontextprotocol/inspector node dist/index.js

启动后,浏览器打开 Inspector 页面,可以看到 Server 暴露的 Tools、Resources、Prompts 列表,也可以直接填入参数调用工具并查看返回结果。这个工具能帮你快速定位参数 Schema 写错、返回结构不标准等问题。

6. 常见问题与排查思路

MCP Server 开发过程中,不同类型的客户端会有各自奇怪的报错。这里整理几个最常遇到的问题。

问题现象常见原因解决思路
启动后没有任何输出stdio 模式下标准输出被占用检查代码里不要使用 console.log,日志全部改用 console.error
客户端提示failed to start login server或 token exchange failed客户端连接远程 MCP Server 时鉴权失败本地开发优先切到 stdio 模式;远程部署检查访问令牌和网络策略
Windows 下客户端启动失败路径分隔符或命令格式问题使用绝对路径;JSON 配置中反斜杠需转义为\\\\或使用正斜杠
工具调用报参数解析失败inputSchema 与业务代码参数不匹配用 Inspector 检查实际发送的参数结构,和 Schema 一一比对
修改代码后客户端仍使用旧逻辑客户端缓存了旧的 stdio 子进程重启客户端,或先结束残留的 node 子进程
远程服务部署后客户端连接超时防火墙或 SSE 路径配置问题确认端口开放;SSE 模式需要让 Server 支持跨域访问,路径需与 SDK 端点一致

在多个客户端间切换时,最容易忽略的是“同一个 MCP Server 是否支持同时被多个客户端连接”。stdio 模式下,每个客户端会启动一个独立子进程,因此不存在并发问题。SSE 模式下,需要额外处理并发和会话隔离。

7. 最佳实践与工程建议

7.1 工具描述要写“模型能看懂的话”

MCP 的工具描述不是给人类看的 API 文档,而是给大模型做工具选择的依据。描述越具体,模型越可能在正确的场景下调用工具。

错误写法:

搜索 Agent

正确写法:

根据用户需求搜索可用的 AI Agent 列表。当用户需要查找 Agent、推荐 AI 工具、寻找某个场景解决方案时,调用此工具。支持通过关键词和分类筛选。如果搜索结果为空,请提示用户更换关键词。

7.2 参数类型必须完整约束

使用 zod 或 JSON Schema 时,尽量把类型约束写全:字符串长度、数字范围、枚举值、是否必填。这既能减少非法请求,也能让模型在调用前自主判断参数是否符合要求。

7.3 不要把敏感信息写进代码

Agent 搜索服务可能会接入付费 API 或内部数据库,这时候密钥管理非常重要。建议通过环境变量注入,并在.env.example中只保留占位说明,不提交真实密钥。

AGENT_API_ENDPOINT=https://api.example.com/agents AGENT_API_KEY=your-key-here

7.4 返回结构要稳定

MCP 工具最终返回给模型的是一段文本。建议一律使用 JSON 字符串,并且保持字段稳定。模型对固定结构的理解能力远强于自由文本,字段不稳定会直接影响后续多轮对话中的引用效果。

7.5 注意超时与重试机制

模型调用工具时,用户耐心有限。远程 API 调用建议设置超时时间,超时后返回友好错误信息,同时通过isError: true标记失败,方便模型向用户解释。

const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); try { const response = await fetch(endpoint, { signal: controller.signal }); // ... } catch (error) { return { content: [{ type: "text", text: "搜索服务超时,请稍后重试" }], isError: true }; } finally { clearTimeout(timeout); }

7.6 权限与安全边界

MCP Server 暴露给 AI Agent 的能力,本质上是可被自动化调用的接口。在权限设计上要遵循最小原则:

  • 只暴露必要工具,不暴露与业务无关的内部方法。
  • 远程部署必须做鉴权,不裸奔。
  • 对用户输入做校验,防止注入恶意参数。
  • 涉及数据库、文件、支付等敏感操作,必须在工具描述中明确提示,并在代码层增加二次确认机制。

7.7 版本管理与发布

MCP Server 与普通 npm 包一样,建议使用语义化版本管理。升级工具 Schema 时要注意向后兼容,因为不同的客户端可能缓存了旧的工具定义。

如果你的目标是让外部团队也能使用这个 MCP Server,可以考虑发布到 npm 并维护一个精简的 README,说明启动命令、环境变量、工具能力列表和配置案例。

8. 下一步可以做什么

到这里,一个可复用的 Agent 搜索 MCP Server 已经跑通了:从 MCP 基础概念,到 TypeScript 开发,再到 Claude Desktop、Dify、IDE 客户端接入,最后还覆盖了常见报错排查和生产环境建议。

你可以沿着几个方向继续深入:

  • 把本地 Mock 数据替换为真实 Agent 目录 API,增加缓存层和限流。
  • 将 stdio 传输切换为 SSE 模式,部署到服务器,让团队多人共享一个服务。
  • 增加 Resources 能力,例如定期同步热门 Agent 分类,让模型在搜索前先了解分类体系。
  • 尝试用 Python SDK 重写一遍,对比不同语言生态下的开发体验。
  • 为你的 MCP Server 制作一个.mcp文件分发包,方便团队一键接入。

MCP 生态还处在快速演进期,工具注册方式、传输层协议、客户端支持范围都可能有变化。但只要抓住“Server 暴露能力、Client 消费能力、协议标准化连接”这个核心,后续不管 SDK 怎么升级,你都能很快跟过来。

建议现在就动手跑一个最小示例。不用一次做完整功能,先让搜索工具能在 Claude Desktop 里被调用,再逐步加细节。这个正反馈循环,会让后续学习顺畅很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询