
摘要MCP三大核心原语Tools工具、Resources资源、Prompts提示的完整实战教程用Python代码演示每种原语的定义方式、调用场景和最佳实践建立MCP开发的基础认知。MCP三大原语初体验 Tools Resources Prompts一个都不少前面四篇我们用的都是Tools定义个函数让AI调用。但MCP其实有三类原语Tools、Resources、Prompts。很多人写了一堆Server只用Tools根本没碰过另外两个。我一开始也这样直到有天我想让AI读一份固定的配置每次都让模型调工具去取来回一轮对话才拿到数据又慢又费token。后来我把那份配置改成ResourceAI启动时就能直接读到省了一大圈。三种原语各有定位用对了能让Server更高效更好用。这篇我用一个完整的Server把三种原语全演示一遍讲清楚它们的区别和适用场景。三种原语分别是什么先快速过一遍定义后面用代码说话。Tools工具。AI主动调用的函数有副作用比如发邮件、写数据库、调外部API。模型根据用户意图决定调不调用、传什么参数。这是我们前四篇一直在用的。Resources资源。只读的数据源客户端按URI去读拿到内容作为上下文。它没有副作用就是给AI看数据。比如一份配置文件、一个数据库表结构、一段说明文档。关键是它由客户端决定什么时候读不一定每次对话都触发。Prompts提示模板。预定义的参数化消息模板帮用户快速发起一类任务。比如代码审查这个prompt用户填个文件路径它生成一段结构化的审查请求消息。它返回的是消息内容不是执行结果。三者的本质区别在于谁主导、有没有副作用、返回什么。Tools是模型主导调用、可有副作用、返回执行结果。Resources是客户端主导读取、只读无副作用、返回数据内容。Prompts是用户触发、返回的是消息模板。完整Server代码我写一个笔记管理Server把三种原语都用上。功能是一个简易的本地笔记系统能加笔记、看笔记列表、读笔记内容、用模板生成总结请求。# notes_server.py# 演示MCP三种原语的笔记管理Server# Tools用于写操作Resources用于读数据Prompts用于生成请求模板importsysimportjsonimportloggingfromdatetimeimportdatetimefrommcp.server.fastmcpimportFastMCP# 日志走stderr保护stdio协议流logging.basicConfig(levellogging.INFO,streamsys.stderr,format%(asctime)s [%(levelname)s] %(message)s,)loggerlogging.getLogger(notes)# 内存里存笔记真实项目换成数据库# 用字典模拟key是笔记idnotes_store:dict[int,dict]{}# 自增id_next_id1# 初始化Server实例mcpFastMCP(notes)def_next_note_id()-int:生成自增的笔记id。global_next_id nid_next_id _next_id1returnnid# Tools 工具写操作有副作用 mcp.tool()defadd_note(title:str,content:str)-str:添加一条新笔记。 Args: title: 笔记标题 content: 笔记正文内容 logger.info(f添加笔记:{title})# 生成id并存入字典nid_next_note_id()notes_store[nid]{id:nid,title:title,content:content,created_at:datetime.now().isoformat(),}returnf笔记已添加编号{nid}标题《{title}》mcp.tool()defdelete_note(note_id:int)-str:删除一条笔记。 Args: note_id: 要删除的笔记编号 logger.info(f删除笔记:{note_id})# 不存在就提示ifnote_idnotinnotes_store:returnf编号{note_id}的笔记不存在# 删除并确认titlenotes_store[note_id][title]delnotes_store[note_id]returnf已删除笔记《{title}》# Resources 资源只读数据无副作用 mcp.resource(notes://list)deflist_notes()-str:返回所有笔记的列表摘要。logger.info(读取笔记列表资源)ifnotnotes_store:returnjson.dumps({notes:[],message:暂无笔记},ensure_asciiFalse)# 只返回摘要不含正文避免数据过大summaries[{id:n[id],title:n[title],created_at:n[created_at]}forninnotes_store.values()]returnjson.dumps({notes:summaries},ensure_asciiFalse)mcp.resource(notes://{note_id}/content)defget_note_content(note_id:int)-str:返回指定笔记的完整内容。 这是一个资源模板note_id从URI里提取。 logger.info(f读取笔记内容资源:{note_id})ifnote_idnotinnotes_store:returnjson.dumps({error:f笔记{note_id}不存在},ensure_asciiFalse)returnjson.dumps(notes_store[note_id],ensure_asciiFalse)# Prompts 提示模板生成结构化请求消息 mcp.prompt()defsummarize_note(note_id:int)-str:生成一个请求总结指定笔记的提示消息。 Args: note_id: 要总结的笔记编号 logger.info(f生成总结提示:{note_id})# prompt返回的是消息文本不是执行结果# 这里把笔记内容嵌进提示让模型直接总结ifnote_idnotinnotes_store:returnf编号{note_id}的笔记不存在无法生成总结notenotes_store[note_id]return(f请帮我总结下面这条笔记的要点用三条以内列出\nf标题:{note[title]}\nf内容:{note[content]}\n)mcp.prompt()defreview_notes()-str:生成一个请求审查所有笔记的提示消息。logger.info(生成审查提示)# 返回多条消息模拟一段对话# 这里简单返回单条titles[n[title]forninnotes_store.values()]ifnottitles:return当前没有笔记无需审查returnf请帮我审查以下笔记列表指出哪些标题不够清晰\n{, .join(titles)}if__name____main__:logger.info(笔记Server启动)mcp.run(transportstdio)三种原语逐个体验把这个Server配进Claude Desktop重启后逐个体验。先体验Tools。对话里说帮我加一条笔记标题叫周计划内容是周一开会周二写代码。AI调用add_note工具返回笔记已添加编号1标题《周计划》“。再说再加一条标题读书笔记内容是读了三章设计模式”。又调用一次编号2。Tools的特点是模型根据你的话判断要不要调用每次调用都有明确的参数和返回。再体验Resources。在Claude Desktop的界面里输入框上方有个资源图标有的版本在附件菜单里点开能看到notes://list这个资源。点它资源内容会被加载进对话上下文。你会看到所有笔记的摘要列表。Resources的特点是它不依赖模型决定调用而是由你或客户端主动加载加载后内容作为上下文存在模型据此回答。资源模板notes://{note_id}/content用的时候客户端会列出这个模板你填note_id比如填1它会读取notes://1/content拿到编号1笔记的完整内容。模板让一个资源定义能服务多个实例省去为每条笔记单独写资源的麻烦。最后体验Prompts。在Claude Desktop里输入框附近有个斜杠命令或提示菜单能看到summarize_note和review_notes两个prompt。选summarize_note它会让你填note_id参数填1它生成一段消息请帮我总结下面这条笔记的要点…这段消息直接进入对话框你按发送模型就开始总结。Prompts的特点是它返回的是消息内容帮你把任务的开场白结构化它本身不执行任何操作。三者到底有什么区别我用一个表把区别说清楚这是这篇的核心。对比维度ToolsResourcesPrompts谁主导模型决定调用客户端主动读取用户触发选择有无副作用可以有写操作无纯只读无只生成文本返回什么执行结果数据内容消息文本触发时机模型推理中按需调用客户端加载上下文时用户挑选模板时典型用途发邮件、写库、调API配置文件、表结构、文档代码审查、总结请求装饰器mcp.tool()mcp.resource(“uri”)mcp.prompt()URI概念无有按URI寻址无一句话总结。Tools是让AI动手做事Resources是给AI看现成数据Prompts是帮用户定好对话的开场。理解了这个分工你设计Server时就知道什么能力该用哪种原语。什么时候用哪种这是新手最困惑的点我给几条判断标准。如果这个操作会改变状态比如写文件、发消息、改数据库用Tools。Resources和Prompts都不能有副作用。如果这个数据是静态的、需要反复读取作为背景比如一份配置、一个schema用Resources。每次对话都让模型调工具去取同一份数据很浪费Resource让客户端缓存一次就行。如果你想让用户快速发起一类固定流程的任务比如代码审查、生成报告用Prompts。它把任务的开场白标准化省得用户每次自己组织措辞。实际项目里三者经常配合。比如这个笔记Server加笔记用Tools看笔记用Resources总结笔记用Prompts。各司其职模型负担最小token最省。常见问题与避坑坑一把只读数据也用Tools暴露。这是最高频的误用。很多人所有能力都用Tools导致模型每次都要调一次工具去取固定数据多一轮往返多耗token。判断标准很简单如果这个操作只是返回数据不改任何状态优先用Resource。坑二Resource的URI写错导致客户端读不到。Resource靠URI寻址URI写错或重复客户端就读不到或读到错的。notes://list和notes://{note_id}/content是两个不同的东西前者是固定资源后者是模板。模板的URI里要有花括号占位符且占位符名要和函数参数名一致。坑三Prompt返回了执行结果却没返回消息文本。Prompt的返回值应当是给模型看的消息内容跟工具的执行结果有本质区别。如果你在prompt里去执行数据库查询然后把结果当返回值语义就错了。正确的做法是prompt返回一段引导性文字真正的数据获取交给Tools或Resources。我见过有人在prompt里塞一大段查询结果导致消息臃肿还容易超长。坑四Cursor下Resources和Prompts不显示。前面提过Cursor主要消费Tools对Resources和Prompts支持不全。你在Cursor里看不到资源列表和提示模板是正常的。验证Resources和Prompts优先用Claude Desktop。坑五资源模板参数类型不匹配。模板URI里的{note_id}是字符串形式传进来的但你的函数参数标注的是int。FastMCP会自动转换但如果你标注了复杂类型比如list[int]转换可能出问题。模板参数尽量用简单类型int、str、float。和单用Tools的方案对比场景只用Tools三种原语配合读取固定配置每次调用一次工具多一轮往返Resource加载一次复用上下文发起代码审查用户自己组织措辞Prompt模板一键生成开场白写入数据Tools合适Tools无变化Token消耗较高重复取数据较低数据复用客户端支持广度所有客户端都支持部分客户端支持Resources和Prompts设计复杂度简单全用一个装饰器需要分辨场景选原语只图省事全用Tools也能跑但在数据复用和任务标准化上会吃亏。三种原语配合能让Server更高效代价是你要多想一步这个能力该归哪类。小结MCP三种原语各有定位。Tools让AI动手做事可以有副作用由模型按需调用。Resources给AI看只读数据无副作用由客户端主动加载。Prompts帮用户生成结构化任务消息返回的是文本不是结果。判断用哪种的标准是看有没有副作用、谁来主导、返回什么。只读数据别用Tools固定开场白用Prompts。把三者配合好Server既高效又省token。这一篇也是这个系列的收尾希望这五篇能帮你从零跑通MCP写出自己真正能用的Server。相关推荐MCP是什么为什么2026年每个AI开发者都需要了解它Tools原语深度解析从定义到调用全流程Resources原语让AI读取你的数据