专题:流行MCP服务器系统学习
关键词:MCP, MCP服务器, Model Context Protocol, MCP原语, Tools, Resources, Prompts, JSON Schema, 能力协商
Tools(工具)是MCP协议中最核心的原语之一,它定义了LLM可以调用的可执行函数。每一个工具本质上是一个"动作"——它允许AI模型在推理过程中主动触发外部操作,而不仅仅是生成文本回复。这种能力使LLM从被动的文本生成器转变为能够与外部世界交互的智能体。
在MCP的架构中,工具由服务器端定义和实现,客户端(即LLM运行的环境)负责将工具列表暴露给模型,而模型则根据对话上下文自主决定是否调用某个工具以及传入什么参数。这种"模型自主决策"的设计模式是MCP与传统API调用方式的核心区别——AI不再是简单地接收指令执行,而是能够判断何时需要调用外部能力来完成任务。
每个工具在MCP协议中通过标准化的JSON Schema进行描述,包含三个核心字段。第一个是 name,即工具的唯一标识名称,它在同一服务器的所有工具中必须保持唯一。第二个是 description,用自然语言描述工具的功能和适用场景,LLM会根据这段描述来判断何时调用该工具。第三个是 inputSchema,一个严格按照JSON Schema规范定义的参数结构,规定了工具接受哪些参数、参数的类型以及哪些是必需的。
上述示例展示了一个数据库查询工具的完整定义。当LLM需要查询数据时,它会读取这个工具的description了解其用途,然后根据inputSchema生成符合格式的调用参数。MCP服务器接收到调用请求后执行对应的函数,并将结果返回给LLM。
在实际的MCP服务器中,工具的种类非常丰富。数据库查询工具是最常见的类型之一,它能让LLM直接对数据库执行SELECT、INSERT等操作,实现数据驱动的智能问答。文件读写工具则赋予LLM操作文件系统的能力,使其能够读取项目文件、写入生成内容。API调用工具允许LLM与第三方服务交互,例如发送HTTP请求获取天气信息或调用外部搜索引擎。计算工具则提供精确的数学运算能力,弥补LLM在精确计算方面的不足。
当LLM决定调用某个工具时,完整的执行流程如下:首先LLM生成一个工具调用请求,包含工具名称和按照inputSchema构造的参数;客户端将这个请求通过JSON-RPC消息发送给MCP服务器;服务器执行对应的函数逻辑,将结果封装为响应消息返回给客户端;客户端将工具结果注入到LLM的上下文中,使模型能够基于结果继续推理。整个过程对用户来说是透明的,LLM会根据工具返回的结果自然地调整其后续输出。
要点:工具是MCP中唯一能够产生"副作用"的原语——调用工具可能改变外部系统的状态(如写入数据库、发送邮件等)。因此,工具的设计需要特别关注安全性和权限控制。
Resources(资源)是MCP协议中负责数据暴露的原语。与工具不同,资源的核心定位是"读取"而非"执行"——它向LLM提供结构化、可读取的数据内容,作为模型推理的上下文信息。你可以将资源理解为MCP世界中的"文件系统":服务器暴露文件和数据结构,LLM根据需求读取这些内容来获取必要的上下文。
MCP中的每个资源都通过一个唯一的URI来标识,这一设计与Web的资源定位方式一脉相承。典型的资源URI格式为 scheme://host/path,例如 file:///project/src/main.py 表示一个文件资源,database://schema/tables 表示数据库模式信息。这种统一的URI方案使得资源的定位和引用变得标准化,不同类型的资源可以通过不同的URI Scheme进行区分。
每个资源在注册时都需要指定其MIME类型,以便客户端和LLM正确解析内容。最常见的MIME类型包括 text/plain(纯文本)、application/json(JSON数据)、text/markdown(Markdown文档)、image/png(图片资源)等。MIME类型的正确设置对于LLM理解资源内容至关重要,不同的MIME类型会影响模型对数据的解析方式和处理策略。
资源模板是MCP协议中一个强大的特性,它允许服务器定义参数化的动态URI。当资源数量庞大或需要根据条件动态生成时,资源模板比逐个注册静态URI更加高效。资源模板使用花括号 {} 作为占位符,客户端在请求时传入实际参数值来生成完整的URI。
实际MCP服务器中的资源类型非常多样。项目文件内容是最直观的资源——服务器可以将整个项目的源代码文件暴露为资源,当LLM需要理解代码上下文时按需读取。数据库模式信息是另一个重要场景,将数据库的表结构、字段定义暴露为资源,使LLM能够理解数据模型。API文档资源让LLM了解外部服务的接口规范。配置文件资源则提供应用运行时的配置信息,帮助LLM做出符合当前环境的决策。
要点:资源是"按需读取"的——与工具不同,资源不会主动推送数据给LLM。模型在需要特定上下文时才会请求对应的资源,这种设计避免了不必要的数据传输,提高了效率。
Prompts(提示)是MCP协议中用于行为预设的原语。如果说工具定义了LLM能做什么,资源定义了LLM能看什么,那么提示就定义了LLM"应该怎么做"。提示是可复用的模板,为特定任务预设LLM行为指令,确保AI在特定场景下以期望的方式响应。
提示最显著的特性是可复用性。一个精心设计的提示模板可以在多个会话、多个用户之间共享。这与传统的手动编写提示词不同,MCP中的提示是服务器端定义的结构化资源,客户端可以通过标准协议获取和加载。这意味着提示可以像代码一样进行版本管理、测试和优化。
高级提示模板支持参数化占位符,使模板能够根据不同的上下文动态生成最终的提示内容。占位符通常采用 {{variable}} 或 {variable} 的语法格式,服务器在返回提示时会将实际参数值注入到模板中。
MCP的提示不仅支持单条消息模板,还支持多轮对话模板。这意味着服务器可以定义完整的对话流程,包括系统消息、用户消息和助手消息的序列。这种能力对于构建结构化的交互体验非常有用,例如故障排查流程可以定义为一个从问题描述到诊断再到解决方案的多步骤提示。
某些高级MCP服务器实现了动态提示生成功能,即服务器根据当前上下文实时构建提示内容。例如,在代码审查场景中,服务器可以先自动分析代码库的结构,然后基于分析结果动态生成最适合当前项目的审查指南。这种动态生成使提示始终与实际情况保持同步,避免了静态模板可能出现的过时或不适用问题。
代码审查提示是最常见的应用之一,它预设了代码审查的标准和关注点,确保LLM以统一的标准审视代码质量。Bug分析提示则引导LLM按照系统化的步骤排查问题:复现Bug、定位根因、评估影响范围、提出修复方案。架构设计提示帮助LLM在讨论系统设计时保持结构化的思维方式,考虑可扩展性、可维护性、安全性等非功能性需求。
注意:提示与工具和资源不同,它不直接参与LLM与外部系统的交互。提示的作用是"引导"LLM的思考方式和回复风格,而不是提供数据或执行操作。合理使用提示可以显著提升LLM输出的质量和一致性。
理解三种原语的各自定位之后,我们需要将它们放在一起审视,才能真正掌握MCP协议的设计哲学。三种原语从三个不同的维度扩展了LLM的能力边界:工具代表"动作",资源代表"数据",提示代表"指令"。
| 维度 | Tools(工具) | Resources(资源) | Prompts(提示) |
|---|---|---|---|
| 核心语义 | 动作(Action) | 数据(Data) | 指令(Instruction) |
| LLM交互方式 | 主动调用(Call) | 按需读取(Read) | 被动加载(Load) |
| 是否产生副作用 | 可能产生(如写数据库) | 不产生(只读) | 不产生(文本模板) |
| JSON Schema | name / description / inputSchema | uri / mimeType / name / description | name / description / arguments |
| 典型用途 | 查询、写入、计算、API调用 | 文件内容、DB模式、文档、配置 | 代码审查、Bug分析、架构设计 |
| 类比 | 函数/API接口 | 文件系统/FTP | 角色设定/系统提示词 |
在实际的MCP会话中,三种原语往往协同工作,共同支撑起一次完整的智能交互。一个典型的协作场景如下:首先,客户端通过Prompts加载一个"代码审查"提示模板,引导LLM以审查者的角色思考。接着,LLM通过Resources读取项目中的源代码文件和相关的编码规范文档。在分析过程中,LLM发现一处性能问题,于是通过Tools调用静态代码分析工具进行深入检查。最后,LLM整合所有信息,生成结构化的审查报告。整个过程三种原语各司其职,无缝衔接。
最佳实践:设计MCP服务器时,应当从"LLM需要什么"的角度出发来规划三种原语。如果LLM需要执行操作,使用工具;如果LLM需要了解信息,使用资源;如果LLM需要行为引导,使用提示。三者之间不存在优劣之分,选择哪种原语取决于具体的应用场景。
MCP协议基于JSON-RPC 2.0规范进行通信,所有原语的调用和响应都遵循这一消息格式。JSON-RPC 2.0是一种轻量级的远程过程调用协议,使用JSON格式进行数据传输。理解其消息格式对于开发MCP客户端和服务器至关重要。
当LLM决定调用一个工具时,客户端会构建一个JSON-RPC请求消息,包含工具名称和参数。服务器执行后返回响应消息,包含执行结果或错误信息。
客户端首先通过 resources/list 方法获取服务器提供的资源列表,然后通过 resources/read 方法读取特定资源的内容。这种"先发现后读取"的模式确保了LLM只在必要时加载数据。
客户端通过 prompts/get 方法获取预定义的提示模板,传入所需的参数值,服务器返回完整的提示内容,包括角色设定和对话消息序列。
MCP协议的JSON-RPC 2.0消息格式设计兼具简洁性和可扩展性。请求消息包含方法名和参数,响应消息包含结果或错误。这种统一的消息格式降低了客户端和服务器的实现复杂度,也使协议本身易于理解和调试。
MCP协议的一个重要特性是客户端和服务端之间的能力协商。在连接建立阶段,双方通过 JSON-RPC 消息交换各自支持的原语和功能。客户端声明它支持哪些原语(如 tools、resources、prompts)以及各自的版本。服务端响应它实际实现了哪些原语。这种协商机制确保了不同实现的互操作性。
能力协商的过程遵循最小共识原则:双方只使用都支持的协议特性。如果客户端不支持某个原语,服务器不会尝试发送相关消息。这一设计使得MCP协议具有前向兼容性,新增的原语不会破坏现有实现。
在设计和实现MCP服务器时,以下最佳实践可以帮助你更好地规划三种原语。对于工具设计,应该保持单一职责原则——每个工具只做一件事并且做好;提供清晰的描述信息,帮助LLM正确理解工具的用途;合理设计参数,避免过多可选参数造成的决策困难。对于资源设计,应该合理组织URI命名空间,使资源的定位直观可预测;选择合适的MIME类型,确保LLM正确解析内容;利用资源模板减少重复定义。
对于提示设计,应该聚焦于特定场景,避免过于通用的提示模板;充分利用参数化能力,使提示模板具有灵活性;考虑多轮对话场景,设计完整的对话流程而非单条消息。最重要的是,始终从用户体验出发,让三种原语的协作自然流畅,而不是让用户感知到底层的协议复杂性。