一小时构建简历MCP服务器:基于Node.js与MCP协议的AI应用开发实战

一小时构建简历MCP服务器:基于Node.js与MCP协议的AI应用开发实战 1. 项目概述一小时构建简历MCP的挑战与价值最近在开发者社区里一个关于“一小时构建简历MCP”的话题引起了我的兴趣。MCP即“模型上下文协议”是当前AI应用开发中的一个热门概念它本质上是一种标准化的接口允许大型语言模型LLM安全、可控地访问外部工具、数据源或执行特定操作。而“简历MCP”则是一个具体的应用场景——创建一个能让AI模型比如ChatGPT、Claude等读取、解析、甚至基于你的简历内容进行智能对话或提供建议的专用服务。你可能会问为什么要把简历做成MCP这背后其实有很强的实用价值。想象一下当你准备面试时可以直接让AI助手基于你简历上的完整项目经历为你模拟面试官提问或者在你更新求职意向时让AI分析你的技能矩阵与目标岗位的匹配度。传统上我们需要手动复制粘贴简历文本给AI上下文有限且容易丢失格式信息。而一个简历MCP服务相当于为你的简历创建了一个专属的、结构化的API任何兼容MCP协议的AI助手都能通过这个API“理解”你的职业全貌进行深度交互。那么“一小时构建”这个目标现实吗我的答案是完全可行但前提是你对现代Web开发的全栈流程有清晰的认知并且能熟练运用一些高效的开发工具和框架。这不仅仅是一个编码挑战更是一次对开发者工具链整合能力、架构设计思维和问题拆解速度的实战检验。接下来我将完整复盘我是如何在一小时内从零搭建一个功能完整、部署可用的简历MCP服务器的全过程并分享其中关键的决策逻辑、踩过的坑以及可以复用的经验。2. 核心思路与架构选型要在极短时间内完成一个可用的服务选对技术栈和架构模式是成功的一半。我的核心思路是最大化利用成熟、轻量的开源方案专注于实现MCP协议本身而非从零造轮子。2.1 为什么选择MCP协议首先我们需要理解为什么是MCP而不是自己定义一个REST API。MCPModel Context Protocol是由Anthropic等公司推动的一个开放协议旨在为LLM提供一个标准化、安全的方式来扩展其能力。它定义了一套清晰的规范包括工具Tools的定义、资源的Resources的访问以及提示Prompts的管理。对于简历这个场景其优势非常明显标准化与互操作性一旦你的服务遵循MCP协议它就能立即被所有支持MCP的客户端如Claude Desktop、Cursor等使用无需为每个客户端单独适配。安全性MCP协议设计之初就考虑了沙箱和安全边界服务器定义的工具和资源客户端可以按需、受控地调用避免了直接将敏感数据如简历暴露给模型的风险。声明式接口你只需要用JSON Schema定义好“读取简历基本信息”、“按技能筛选项目经历”等工具客户端就能自动发现并生成调用界面开发体验非常友好。2.2 技术栈的快速决策基于“一小时”的极限挑战我的技术选型遵循以下原则服务器框架Node.js Express。Node.js非阻塞I/O模型适合轻量级API服务Express框架极简、灵活能快速搭建HTTP服务器。相比Python的FastAPINode.js在依赖管理和项目启动速度上略有优势且与后续前端展示如果需要同属JS生态心智负担更小。MCP协议实现直接使用modelcontextprotocol/sdk官方SDK。这是最关键的决策。自己从零解析协议规范、处理SSE服务器发送事件通信不仅耗时而且容易出错。官方SDK封装了服务器Server和传输层Transport的核心逻辑我们只需要专注于实现工具Tools和资源Resources的处理函数。简历数据源为了极致简化我选择将简历内容直接以JSON格式硬编码在服务器代码中。这是一个权衡虽然失去了从数据库或文件动态加载的灵活性但在一小时的挑战中它消除了所有I/O延迟和配置复杂性让焦点完全集中在MCP协议的实现上。当然在实际产品中你肯定会将其替换为从数据库如SQLite、PostgreSQL或云存储如S3读取。开发与部署开发使用nodemon实现代码热重载提升开发效率。部署选用Vercel或Railway。它们都支持极简的Node.js应用部署通过Git连接即可自动构建和发布非常适合这种小型、快速验证的项目。Vercel在静态资源和Serverless函数部署上体验更丝滑。整个架构非常简单一个Express服务器集成MCP SDK暴露几个处理简历数据的工具端点然后通过HTTP或STDIO传输层与MCP客户端通信。注意在一小时挑战中切忌追求“完美的架构”。我们的目标是“可工作的原型”而不是“企业级解决方案”。因此硬编码数据、使用最简单的错误处理都是可以接受的先让核心流程跑通。2.3 项目初始化与依赖安装明确了思路我们开始动手。首先确保你的系统已安装Node.js建议版本18和npm。# 1. 创建项目目录并初始化 mkdir resume-mcp-server cd resume-mcp-server npm init -y # 2. 安装核心依赖 npm install express modelcontextprotocol/sdk # 3. 安装开发依赖用于热重载 npm install --save-dev nodemon # 4. 创建基础文件结构 touch server.js touch resumeData.json编辑package.json添加启动脚本{ scripts: { start: node server.js, dev: nodemon server.js } }3. 核心实现构建MCP服务器接下来是核心编码阶段。我们将逐步实现一个能响应MCP客户端请求的服务器。3.1 定义简历数据结构在resumeData.json中我们定义一份结构化的简历数据。这是整个服务的“数据模型”设计的好坏直接影响后续工具定义的难易程度。{ basics: { name: 张三, label: 全栈工程师, email: zhangsanexample.com, phone: (86) 138-0013-8000, website: https://zhangsan.dev, summary: 拥有5年全栈开发经验专注于现代Web技术栈。擅长React、Node.js与云原生应用开发对用户体验与系统性能优化有深入实践。, location: { city: 北京, countryCode: CN } }, work: [ { company: 某科技公司, position: 高级软件工程师, startDate: 2021-03-01, endDate: 至今, summary: 负责核心产品的前后端架构设计与开发。主导了从单体应用到微服务的迁移系统性能提升40%。, highlights: [ 使用React TypeScript重构前端 bundle大小减少35%, 设计并实现了基于Node.js GraphQL的后端BFF层, 引入Docker与Kubernetes实现CI/CD自动化部署 ] } ], skills: [ { name: JavaScript, level: 专家, keywords: [ES6, TypeScript, Node.js, React] }, { name: 云服务, level: 熟练, keywords: [AWS, Docker, Kubernetes, Serverless] } ], projects: [ { name: 内部效率平台, description: 一个用于优化团队任务管理与知识共享的全栈应用。, highlights: [ 前端采用Next.js实现服务端渲染(SSR), 后端使用Nest.js框架提供RESTful API, 集成AI助手用于自动生成任务摘要 ], keywords: [Next.js, Nest.js, PostgreSQL, OpenAI API] } ] }这个结构参考了JSON Resume等开源标准但做了简化只保留最核心的模块基本信息、工作经历、技能列表和项目经验。关键词keywords的添加是为后续实现“按技能过滤项目”等功能做准备。3.2 实现MCP服务器主逻辑现在打开server.js开始编写服务器代码。const express require(express); const { Server } require(modelcontextprotocol/sdk/server/index.js); const { StdioServerTransport } require(modelcontextprotocol/sdk/server/stdio.js); const { HttpServerTransport } require(modelcontextprotocol/sdk/server/http.js); // 加载简历数据 const resumeData require(./resumeData.json); // 创建Express应用和MCP服务器实例 const app express(); const port process.env.PORT || 3000; const mcpServer new Server( { name: resume-mcp-server, version: 1.0.0, }, { capabilities: { tools: {}, // 我们将在后面定义工具 resources: {}, // 可选定义资源 }, } ); // 工具1获取完整简历 const getFullResumeTool { name: get_full_resume, description: 获取候选人的完整简历信息包括基本信息、工作经历、技能和项目。, inputSchema: { type: object, properties: {}, // 此工具无需输入参数 additionalProperties: false, }, }; // 工具1的处理函数 mcpServer.setRequestHandler(tools/call, async (request) { if (request.params.name get_full_resume) { return { content: [ { type: text, text: JSON.stringify(resumeData, null, 2), // 美化输出JSON }, ], }; } // 其他工具的处理... throw new Error(Unknown tool: ${request.params.name}); }); // 工具2根据技能关键词搜索相关项目 const searchProjectsBySkillTool { name: search_projects_by_skill, description: 根据技能关键词如“React”、“Node.js”、“AWS”搜索简历中相关的项目经历。, inputSchema: { type: object, properties: { skillKeyword: { type: string, description: 需要搜索的技能关键词, }, }, required: [skillKeyword], additionalProperties: false, }, }; // 注册工具到服务器能力中 mcpServer.capabilities { tools: [getFullResumeTool, searchProjectsBySkillTool], }; // 更新工具调用处理器增加对新工具的处理 mcpServer.setRequestHandler(tools/call, async (request) { const { name, arguments: args } request.params; if (name get_full_resume) { return { content: [ { type: text, text: # ${resumeData.basics.name} 的简历\n\n**职位:** ${resumeData.basics.label}\n\n## 基本信息\n- **邮箱:** ${resumeData.basics.email}\n- **简介:** ${resumeData.basics.summary}\n\n## 工作经历\n${resumeData.work.map(job ### ${job.position} ${job.company}\n**期限:** ${job.startDate} - ${job.endDate}\n${job.summary}\n).join(\n)}, }, ], }; } if (name search_projects_by_skill) { const keyword args.skillKeyword.toLowerCase(); const matchedProjects resumeData.projects.filter(project project.keywords.some(k k.toLowerCase().includes(keyword)) || project.name.toLowerCase().includes(keyword) || project.description.toLowerCase().includes(keyword) ); if (matchedProjects.length 0) { return { content: [{ type: text, text: 未找到与技能关键词“${args.skillKeyword}”相关的项目。, }], }; } return { content: [{ type: text, text: 找到 ${matchedProjects.length} 个与“${args.skillKeyword}”相关的项目\n\n matchedProjects.map(p ### ${p.name}\n${p.description}\n**涉及技术:** ${p.keywords.join(, )}\n).join(\n), }], }; } throw new Error(未知工具: ${name}); }); // 设置传输层同时支持HTTP和STDIO用于Claude Desktop等客户端 async function main() { // HTTP传输层 const httpTransport new HttpServerTransport(app, /mcp); await mcpServer.connect(httpTransport); console.log(MCP HTTP server listening on /mcp); // 启动Express服务器 app.listen(port, () { console.log(Express app listening on http://localhost:${port}); }); // STDIO传输层用于命令行客户端 if (process.argv.includes(--stdio)) { const stdioTransport new StdioServerTransport(); await mcpServer.connect(stdioTransport); console.log(MCP server running over stdio); } } main().catch((error) { console.error(Server failed to start:, error); process.exit(1); });这段代码是服务器的核心。我们做了以下几件事初始化导入必要的模块创建Express应用和MCP Server实例。定义工具声明了两个工具Tool。每个工具都有name、description和inputSchema。inputSchema使用了JSON Schema来定义输入参数的格式和校验规则这是MCP协议要求的一部分能让客户端智能地生成调用界面。实现处理函数在tools/call请求处理器中根据不同的工具名执行相应的逻辑。对于get_full_resume我们返回格式化的简历文本对于search_projects_by_skill我们实现了一个简单的关键词过滤逻辑。设置传输层我们同时配置了HTTP和STDIO两种传输方式。HTTP方便通过浏览器或Postman测试STDIO则是Claude Desktop等客户端连接本地MCP服务器的标准方式。实操心得在定义工具的描述description时务必清晰、具体。这不仅是给开发者看的更是直接暴露给AI模型的提示词。好的描述能引导模型在正确的场景下调用你的工具。例如“搜索项目”比“获取项目”更能让AI理解这个工具的用途。3.3 本地测试与调试代码写好了我们需要验证它是否能正常工作。第一步启动服务器npm run dev如果看到“Express app listening on http://localhost:3000”和“MCP HTTP server listening on /mcp”的日志说明服务器启动成功。第二步使用HTTP测试工具如Postman或curl进行测试我们可以直接向/mcp端点发送MCP协议格式的请求来测试工具调用。# 测试 get_full_resume 工具 curl -X POST http://localhost:3000/mcp \ -H Content-Type: application/json \ -d { jsonrpc: 2.0, id: 1, method: tools/call, params: { name: get_full_resume, arguments: {} } }如果返回了格式化的简历信息说明第一个工具工作正常。第三步模拟MCP客户端连接更真实的测试为了模拟真实客户端如Claude Desktop的连接我们可以使用MCP SDK自带的测试工具或者编写一个简单的测试脚本。这里提供一个简易的测试脚本test_client.jsconst { Client } require(modelcontextprotocol/sdk/client/index.js); const { StdioClientTransport } require(modelcontextprotocol/sdk/client/stdio.js); async function test() { const client new Client( { name: test-client }, { capabilities: {} } ); // 假设服务器通过 stdio 运行node server.js --stdio const transport new StdioClientTransport({ command: node, args: [server.js, --stdio], }); await client.connect(transport); // 列出可用工具 const tools await client.listTools(); console.log(可用工具:, tools); // 调用 search_projects_by_skill 工具 const result await client.callTool({ name: search_projects_by_skill, arguments: { skillKeyword: React } }); console.log(工具调用结果:, result); await client.close(); } test().catch(console.error);运行node test_client.js你应该能看到服务器列出的工具列表以及调用搜索工具后返回的相关项目信息。这一步验证了MCP协议通信的完整性。4. 部署上线与客户端配置让服务在本地运行只是第一步我们需要将其部署到公网并配置到真正的AI助手客户端中。4.1 部署到VercelVercel的部署体验极其流畅特别适合Node.js项目。安装Vercel CLI并登录npm i -g vercel vercel login创建部署配置文件在项目根目录创建vercel.json。{ version: 2, builds: [ { src: server.js, use: vercel/node } ], routes: [ { src: /(.*), dest: server.js } ] }这个配置告诉Vercel将我们的server.js作为Serverless函数来部署。执行部署vercel --prod跟随命令行提示操作如果是首次部署需要关联Git仓库。部署完成后你会获得一个类似https://your-project.vercel.app的URL。注意事项Vercel的Serverless函数有超时限制默认10秒。对于简历MCP这种轻量级查询服务完全足够。但如果未来工具逻辑变得复杂需要考虑优化性能或使用其他无超时限制的平台如Railway、Fly.io。4.2 配置Claude Desktop使用你的MCP服务器目前Claude Desktop是体验MCP功能最方便的平台之一。找到配置文件macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.json编辑配置文件在mcpServers部分添加你的服务器配置。{ mcpServers: { my-resume-server: { command: npx, args: [ -y, vercel, --prod, --token, YOUR_VERCEL_TOKEN, your-project.vercel.app ], env: { VERCEL_ORG_ID: your-org-id, VERCEL_PROJECT_ID: your-project-id } } } }重要上述是通过Vercel CLI远程调用的示例更简单的方式是直接配置HTTP服务器。但请注意Claude Desktop目前更原生支持通过command启动本地进程或SSH。对于部署在公网的HTTP服务一种可行的方法是创建一个轻量级本地代理脚本该脚本通过HTTP调用你的远程服务然后通过STDIO与Claude通信。这稍微复杂一些。更简单的测试方案在开发阶段你可以直接配置Claude Desktop连接本地运行的服务器。{ mcpServers: { my-local-resume: { command: node, args: [/ABSOLUTE/PATH/TO/YOUR/PROJECT/server.js, --stdio] } } }保存配置并重启Claude Desktop。验证连接重启后在Claude Desktop中新建对话尝试输入“你能用我的简历工具看看我有哪些React项目经验吗” 如果配置正确Claude应该能识别并调用search_projects_by_skill工具并返回结果。4.3 扩展更多实用工具一个基础的简历MCP已经完成。但要让其真正有用我们可以扩展更多工具。以下是一些思路及其实现要点工具3分析技能与岗位匹配度输入目标岗位描述JD文本。逻辑使用简单的文本匹配或集成一个轻量级NLP库如natural提取JD中的关键词与简历中的skills和projects.keywords进行比对计算匹配度百分比并列出匹配和缺失的技能。价值在准备面试时快速进行差距分析。工具4生成针对性的面试问题输入目标公司或岗位类型如“前端开发”、“后端开发”。逻辑根据简历中的项目经历结合常见的面试问题模板生成具体的技术问题或行为面试问题。例如针对一个提到“性能优化”的项目可以生成“请详细描述你在XX项目中做性能优化的具体步骤和衡量指标”。价值个性化面试准备。工具5更新简历特定部分输入区块名如work、skills、新内容。逻辑这是一个“写”操作需要谨慎处理。可以在服务器内存中更新硬编码的数据仅对当前会话有效或者连接一个简单的数据库如SQLite。务必注意权限控制在实际应用中应加入认证。价值动态维护简历信息。实现这些工具只需遵循相同的模式在capabilities.tools数组中定义新工具并在tools/call请求处理器中添加对应的if分支和业务逻辑。5. 常见问题与优化实录在实际构建和测试过程中我遇到了几个典型问题以下是排查记录和解决方案。5.1 客户端连接失败报“无法初始化”错误问题现象Claude Desktop重启后右下角MCP图标显示错误或对话中AI表示找不到工具。排查步骤检查配置文件路径和格式确保JSON格式正确路径无拼写错误。特别是Windows的路径分隔符是反斜杠需要正确转义或使用正斜杠。检查服务器命令在终端中手动运行配置文件中command和args指定的命令看是否能正常启动并输出日志。常见错误是Node路径问题或依赖未安装。查看Claude Desktop日志在Claude Desktop设置中开启调试模式或查看其标准错误输出启动方式因系统而异里面通常有更详细的连接错误信息。验证MCP协议兼容性确保使用的modelcontextprotocol/sdk版本与客户端兼容。有时需要尝试更新到最新版本。解决方案我遇到的一次是nodemon在stdio模式下有缓冲问题。将command从nodemon改为node直接运行问题解决。对于生产环境应使用node。5.2 工具被调用但返回结果格式错误导致AI无法理解问题现象AI调用了工具但回复说“工具返回了错误”或无法解析结果。排查步骤严格遵守MCP响应格式MCP SDK的call处理函数要求返回特定结构。确保返回的对象包含content数组数组内是{type: text, text: ...}或{type: image, ...}等格式。直接返回字符串或任意JSON会导致错误。检查content.text的内容虽然返回了正确结构但如果text字段内容过于复杂或包含非法字符AI模型也可能解析失败。尽量返回清晰、格式化的纯文本或Markdown。在HTTP测试中模拟用Postman模拟客户端发送请求检查原始响应是否符合协议。解决方案我在get_full_resume工具中最初直接返回了JSON.stringify(resumeData)虽然结构对但AI处理长JSON体验不好。后来改为生成结构化的Markdown文本可读性大大提升。5.3 服务器在Vercel上部署后超时问题现象本地运行正常部署到Vercel后复杂工具调用如文本分析偶尔超时失败。原因分析Vercel Serverless免费版默认执行超时时间为10秒。如果工具处理逻辑涉及较重的计算或外部API调用如调用OpenAI接口进行分析可能超过此限制。解决方案优化工具逻辑检查代码移除不必要的循环或同步阻塞操作使用异步处理。设置增量响应对于耗时操作MCP协议支持服务器分块返回结果。这需要更复杂的实现但能改善用户体验。考虑其他部署平台如Railway、Fly.io或Google Cloud Run它们通常提供更长的超时时间或常驻实例。降级处理作为备选在工具实现中加入超时检查如果处理时间过长先返回一个“正在处理”的中间状态提示。5.4 安全性考量与改进当前的原型为了速度牺牲了安全性。在实际使用中尤其是简历包含真实个人信息时必须考虑认证与授权不应将服务无保护地暴露在公网。可以为MCP服务器添加一个简单的API密钥验证。在客户端配置中通过环境变量或头信息传递密钥。输入验证与消毒虽然inputSchema提供了基础验证但在工具处理函数中仍需对用户输入如skillKeyword进行严格的消毒防止注入攻击。数据存储将硬编码的简历数据移至环境变量或安全的数据库避免敏感信息泄露在代码仓库中。6. 项目复盘与未来展望回顾这一小时的构建过程核心的成就感不在于代码量而在于完整地实践了一个从协议理解、技术选型、快速开发、测试验证到部署上线的全流程。MCP协议降低了为AI构建专用扩展的门槛使得开发者可以聚焦在业务逻辑本身。这个简历MCP服务器虽然简单但已经形成了一个可扩展的骨架。你可以很容易地为其添加更多智能功能集成向量数据库将简历中的每段经历转换为向量嵌入实现基于语义的、而不仅仅是关键词的搜索和匹配。连接外部API例如连接GitHub API自动获取最新的项目信息或连接LinkedIn API同步工作经历。多模态支持除了文本MCP协议也支持图像资源。你可以添加一个工具来生成简历的数据可视化图表如技能雷达图并作为图像资源返回给AI客户端展示。我个人在实际操作中的体会是“一小时”更像是一个思维框架的约束。它强迫你做出最直接、最有效的技术决策避免过度设计。对于任何想快速验证一个AI集成点子的开发者来说MCP都是一个值得投入学习的协议。下次当你有一个想法想让AI更懂你的特定数据或业务时不妨试试看能否在一小时内也打造出一个属于你的MCP服务器。