MCP 核心组件如资源、工具和提示词使开发者能够创建强大的 AI 应用,同时保持数据安全和灵活性。这些组件共同构成了一个完整的框架,使 LLM 能够以标准化、安全且可扩展的方式与外部世界进行交互。
3.1 资源(Resources)详解
资源是 MCP 中的一个核心原语,允许服务器向客户端和 LLM 提供数据和内容。资源可以理解为类似于 REST API 中的 GET 端点,它们提供数据但不应执行重大计算或产生副作用。
3.1.1 资源的定义与类型
资源在 MCP 中是应用程序控制的,这意味着客户端应用程序可以决定如何以及何时使用它们。不同的 MCP 客户端可能会以不同的方式处理资源:
- Claude Desktop 目前要求用户在使用资源之前显式选择它们
- 其他客户端可能基于启发式算法自动选择资源
- 一些实现甚至可能允许 AI 模型自行决定使用哪些资源
服务器开发者在实现资源支持时应该准备好处理所有这些交互模式。如果想要向模型自动公开数据,服务器开发者应该使用模型控制的原语,如工具(Tools)。
资源的设计理念是让数据的访问和使用权掌握在应用程序手中,而非模型自身。这种设计在保护隐私和敏感数据方面尤为重要,同时也确保了数据使用的可控性和透明度。在资源访问模式中,应用程序可以实施访问控制策略,例如要求用户确认、限制资源数量或仅允许访问特定类型的资源。
每个资源都有一组核心属性,包括唯一标识符(URI)、名称、描述(可选)以及MIME类型。这些属性使客户端能够理解资源的性质和内容类型,从而正确处理和呈现资源。
资源类型
资源可以包含两种基本类型的内容:
文本资源:包含 UTF-8 编码的文本数据。适用于:
- 源代码文件和程序脚本
- 配置文件和系统设置
- 日志文件和诊断信息
- JSON/XML 结构化数据
- 纯文本文档和报告
文本资源的优势在于它们易于处理、搜索和分析。LLM可以直接理解文本内容,无需额外的解码或转换步骤。文本资源也通常比二进制资源占用更少的带宽和存储空间。
二进制资源:包含以 base64 编码的原始二进制数据。适用于:
- 图像和照片
- PDF文档和电子书
- 音频文件和音乐
- 视频文件和多媒体内容
- 其他非文本格式数据
二进制资源允许MCP处理和传输更广泛的内容类型。通过base64编码,二进制数据可以作为文本安全地传输,虽然这会增加约33%的数据大小。接收二进制资源的客户端需要正确解码并根据其MIME类型适当处理内容。
资源可以表示服务器想要提供给客户端的任何类型的数据,包括但不限于:
- 本地文件系统中的文件内容
- 数据库记录和查询结果
- 来自外部服务的API响应
- 实时系统数据和监控信息
- 屏幕截图和用户界面图像
- 应用程序日志和事件流
- 网页内容和网络资源
- 用户生成的内容和文档
资源的灵活性使MCP能够处理几乎任何类型的数据源,为LLM提供丰富的上下文信息。这种统一的接口简化了集成过程,使开发者能够轻松地向AI模型提供多样化的数据。
在 FastMCP 中,资源可以通过装饰器简洁地定义:
@mcp.resource("config://app")
def get_config() -> str:
"""静态配置数据"""
return "应用程序配置信息"
资源可以是静态的,也可以是动态的(带参数):
@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
"""动态用户数据"""
return f"用户 {user_id} 的个人资料"
3.1.2 资源URI与发现机制
资源 URI
MCP中的每个资源都通过唯一的URI(统一资源标识符)进行标识。这些URI遵循一个标准格式,使客户端能够一致地引用和访问资源:
[协议]://[主机]/[路径]
URI的各部分具有以下含义:
- 协议:指定资源的类型或访问方法,如”file”、”http”、”postgres”等
- 主机:表示资源所在的服务器或系统(对于本地资源可能为空)
- 路径:导向特定资源的位置信息
URI示例及其含义:
file:///home/user/documents/report.pdf
- 本地文件系统中的PDF文件postgres://database/customers/schema
- PostgreSQL数据库中的客户模式screen://localhost/display1
- 本地计算机的屏幕截图http://api.example.com/data
- 来自Web API的数据git://repository/main/src
- Git仓库中的源代码
协议和路径结构由MCP服务器实现定义,允许服务器创建自定义URI方案来表示其特有的资源类型。这种灵活性使MCP能够适应各种数据源和系统结构。
URI的设计考虑了可读性、一致性和扩展性。它们提供了一种语义明确的方式来引用资源,同时保持足够的灵活性以适应未来的资源类型。URI也可以包含查询参数,进一步细化对资源的访问,如weather://forecast?city=beijing&days=5
。
资源发现机制
资源发现是MCP协议的一个关键方面,它允许客户端了解服务器提供的可用资源。MCP提供了两种主要的资源发现机制:
- 直接资源列表
服务器通过resources/list
端点公开具体资源列表。每个资源包括:
- URI:资源的唯一标识符
- 名称:人类可读的资源名称
- 描述(可选):解释资源内容和用途
- MIME类型(可选):指定资源的内容类型
直接资源列表对于静态或预定义的资源特别有用。例如,服务器可能提供一组固定的配置文件、常用数据源或系统信息。这些资源不需要参数化,可以直接通过其URI访问。
通过资源列表,客户端可以向用户展示可用资源,或者根据任务需求自动选择相关资源。资源列表也使LLM能够了解可用的信息源,从而做出更明智的决策。
- 资源模板
对于动态资源,服务器可以公开URI模板,客户端可以使用这些模板构建有效的资源URI:
- URI模板:遵循RFC 6570的参数化URI模式
- 名称:此类型资源的人类可读名称
- 描述(可选):解释模板的用途和参数
- MIME类型(可选):指定此类资源的内容类型
资源模板对于参数化访问特别有用,例如:
weather://{city}/forecast
- 获取特定城市的天气预报user://{id}/profile
- 访问用户资料database://{table}/{id}
- 获取数据库记录
模板中的变量(用大括号标记)可以被替换为具体值以创建有效的URI。这使得客户端能够动态构建资源请求,而无需预先知道所有可能的资源URI。
资源发现过程通常遵循以下流程:
- 客户端连接到MCP服务器并完成初始化
- 客户端发送
resources/list
请求获取可用资源 - 服务器响应包含直接资源和资源模板的列表
- 客户端可以基于此信息向用户展示选项或自动选择资源
- 用户或LLM通过选择现有资源或填充模板参数来访问特定资源
高级客户端可能会实现智能资源发现策略,如:
- 基于上下文和任务自动选择相关资源
- 缓存常用资源以提高性能
- 实施资源访问策略和权限检查
- 提供用户友好的界面来浏览和选择资源
3.1.3 资源读取与更新
读取资源
资源读取是MCP中最基本的操作之一,它允许客户端获取资源的内容。读取过程设计为简单且一致,无论底层资源类型如何。
要读取资源,客户端向服务器发送包含资源URI的resources/read
请求。服务器处理此请求,检索资源内容,并返回适当格式的响应。这种统一接口隐藏了底层实现的复杂性,使客户端能够以相同的方式处理各种资源。
服务器响应包含资源内容的列表,每个内容项包括:
- URI:资源的标识符
- MIME类型(可选):内容的格式
- 内容:以下二选一
- text:用于文本资源
- blob:用于二进制资源(base64编码)
一个关键的设计考虑是,服务器可能响应一个resources/read
请求返回多个资源。这在某些情况下特别有用,例如:
- 读取目录时返回目录中的所有文件
- 查询返回多条记录时
- 资源有多个相关部分时
- 请求的资源被拆分为更小的片段以便有效传输
资源读取机制支持大型资源的高效处理。对于特别大的资源,服务器可能实现:
- 分页:将内容分块返回
- 流式传输:渐进式传送内容
- 压缩:减少传输数据量
- 部分读取:仅返回请求的部分内容
资源读取过程中,服务器负责处理所有必要的身份验证、授权和访问控制。如果客户端没有权限访问请求的资源,服务器应返回适当的错误响应,而不是资源内容。这确保了敏感数据的安全性。
一个典型的资源读取实现如下:
@app.read_resource()
async def read_resource(uri: AnyUrl) -> Union[str, bytes]:
"""根据 URI 读取资源"""
try:
if uri.startswith("file:///"):
path = uri.replace("file:///", "/")
with open(path, "r") as f:
return f.read()
elif uri.startswith("api://"):
# 处理 API 资源
endpoint = uri.replace("api://", "")
result = await call_api(endpoint)
return json.dumps(result)
else:
raise ValueError(f"不支持的 URI 协议: {uri}")
except Exception as e:
logger.error(f"读取资源 {uri} 时出错: {e}")
raise ResourceError(str(e))
资源更新
MCP还支持资源的实时更新,这对于动态内容和长时间运行的应用程序尤为重要。有两种主要机制支持资源更新:
- 资源列表变更通知
服务器可以通过notifications/resources/list_changed
通知客户端其可用资源列表已更改。这种机制适用于以下情况:
- 新资源变得可用
- 现有资源被移除
- 资源属性(如名称或描述)发生变化
当客户端接收到列表变更通知时,它可以再次调用resources/list
来获取更新后的资源列表。这使客户端能够保持其资源视图的最新状态,并相应地更新用户界面或决策逻辑。
- 资源内容更新订阅
对于需要监控特定资源内容变化的情况,MCP提供了资源订阅机制:
- 客户端发送
resources/subscribe
请求,指定要监控的资源URI - 服务器开始跟踪该资源的变更
- 当资源内容变化时,服务器发送
notifications/resources/updated
通知 - 客户端收到通知后可以使用
resources/read
获取最新内容 - 当不再需要监控时,客户端可以通过
resources/unsubscribe
停止订阅
这种基于订阅的模式特别适合以下场景:
- 实时数据监控和仪表板
- 协作编辑环境
- 日志和事件流分析
- 长时间运行的处理任务的进度跟踪
资源订阅机制使LLM能够基于最新数据做出决策,而无需客户端持续轮询资源状态。这提高了系统效率,并确保了LLM可以访问最新的上下文信息。
订阅模型通常实现节流或批处理机制,以避免在频繁变化的资源上产生过多通知。服务器可能会聚合短时间内的多个更改,或者仅在达到特定阈值时发送通知,以减少网络流量和处理开销。
一个资源订阅的实现示例:
# 资源订阅的处理
@app.subscribe_resource()
async def subscribe_resource(uri: AnyUrl) -> None:
"""订阅资源更新"""
# 添加到订阅列表
subscriptions.add(str(uri))
# 资源取消订阅的处理
@app.unsubscribe_resource()
async def unsubscribe_resource(uri: AnyUrl) -> None:
"""取消订阅资源更新"""
if str(uri) in subscriptions:
subscriptions.remove(str(uri))
# 资源更新时发送通知
async def notify_resource_update(uri: str) -> None:
"""当资源更新时通知客户端"""
if uri in subscriptions:
await app.send_resource_updated_notification(uri)
3.1.4 资源模板
资源模板是 MCP 的一个强大特性,允许服务器定义可用于创建动态资源的参数化 URI。
资源模板基于 RFC 6570 URI 模板规范,使用大括号 {}
表示变量。例如:
weather://{city}/forecast
database://{schema}/{table}
file://{path}
当在 FastMCP 中使用函数实现资源模板时,URI 参数会自动映射到函数参数:
@mcp.resource("weather://{city}/{period}")
def get_weather_forecast(city: str, period: str) -> str:
"""获取城市的天气预报"""
if period == "today":
return f"{city} 今天的天气预报: 晴朗,温度 25°C"
elif period == "tomorrow":
return f"{city} 明天的天气预报: 多云,温度 23°C"
else:
return f"{city} 未来一周天气预报: 数据暂未可用"
在上述例子中,资源模板 weather://{city}/{period}
有两个变量:city
和 period
。当客户端请求 weather://beijing/today
时,函数会以 city="beijing"
和 period="today"
为参数被调用。
模板语法与结构
资源模板的基本结构是一个包含变量占位符的URI字符串。例如:
weather://{city}/forecast
- 城市变量database://{schema}/{table}
- 模式和表变量file://{path}
- 文件路径变量user://{id}/profile?detail={level}
- 路径变量和查询参数
模板变量可以出现在URI的任何部分,包括协议、主机名、路径段和查询参数。这提供了高度的灵活性,允许服务器设计精确满足其数据模型需求的URI结构。
每个资源模板通常包括以下信息:
- URI模板:带有变量占位符的字符串
- 名称:模板的人类可读名称
- 描述:解释模板的用途和变量的含义
- 参数描述:每个变量的详细信息
- MIME类型:生成资源的内容类型
模板的工作原理
当客户端接收到资源模板列表时,它可以:
- 向用户展示模板及其参数,允许用户填充变量
- 基于上下文或用户输入自动填充模板变量
- 使用填充的模板构建完整的资源URI
- 使用生成的URI通过普通的
resources/read
请求获取资源
例如,给定模板weather://{city}/forecast
,客户端可能:
- 请求用户指定城市名称
- 从用户当前位置自动确定城市
- 从对话上下文中提取城市名称
- 根据用户偏好选择默认城市
然后客户端将变量值(如”beijing”)插入模板,生成最终URI weather://beijing/forecast
,并使用此URI请求资源。
服务器端,对模板请求的处理通常包括:
- 解析请求的URI,提取变量值
- 验证变量值(类型、范围、格式等)
- 使用变量值查询或生成适当的数据
- 将数据格式化为资源内容
- 返回资源响应
模板的高级用途
资源模板不仅限于简单的参数替换,它们还可以支持:
变量验证和类型转换 服务器可以实施验证规则,确保变量值符合预期的格式和范围。例如,对于
weather://{city}/forecast
,服务器可能检查城市名称是否在支持的城市列表中。默认值和可选参数 模板可以指定某些变量的默认值,或将其标记为可选。例如,
search://{query}?max_results={count=10}
中,count
有默认值10。依赖参数 某些参数可能依赖于其他参数的值。服务器可以实施这些依赖关系,确保生成的资源在语义上有效。
参数转换和规范化 服务器可以转换或规范化参数值,例如转换城市名称大小写、解析日期格式或处理特殊字符编码。
通过这些高级功能,资源模板为MCP提供了强大而灵活的动态资源生成系统,使LLM能够以自然且直观的方式访问参数化内容。
3.2 工具(Tools)详解
3.2.1 工具的定义与功能
工具的定义设计考虑了多方面的需求:
- 对人类和AI都可理解
- 提供足够的元数据以便明智选择
- 允许参数验证和类型检查
- 支持复杂的数据结构和类型
- 促进安全和有效的工具使用
在 FastMCP 中,可以通过简单的装饰器定义工具:
@mcp.tool()
def calculate_bmi(weight_kg: float, height_m: float) -> float:
"""计算 BMI(体重指数)
Args:
weight_kg: 体重(千克)
height_m: 身高(米)
"""
return weight_kg / (height_m ** 2)
工具的功能范围
MCP工具可以执行多种功能,几乎涵盖任何可编程操作。常见的工具类别包括:
数据处理工具
- 转换和格式化数据
- 执行统计分析和计算
- 过滤和查询数据集
- 生成可视化和报告
外部系统集成
- 调用第三方API
- 查询数据库和数据存储
- 与云服务交互
- 连接到企业系统
文件和媒体操作
- 读写文件
- 转换媒体格式
- 处理图像和视频
- 管理文档和内容
通信和通知
- 发送电子邮件和消息
- 通知用户或系统
- 发布内容到社交媒体
- 管理通信渠道
系统控制和自动化
- 执行系统命令
- 管理流程和服务
- 调度任务和作业
- 触发工作流和自动化
工具的实现可以简单如基本算术计算,也可以复杂如控制整个企业系统。这种灵活性使MCP适应各种应用场景和集成需求。
3.2.2 工具的调用流程
工具调用是一个受控的多步骤过程,涉及客户端、服务器、LLM和用户。这种流程设计确保操作既安全又透明,同时保持足够的灵活性以支持多种使用场景。
完整调用流程
工具调用遵循一个明确的流程,包含以下阶段:
发现阶段
- 客户端通过
tools/list
端点请求可用工具列表 - 服务器返回工具定义,包括名称、描述和输入模式
- 客户端处理和存储工具信息,并可能向用户或LLM展示
发现阶段使客户端了解服务器提供的功能,它可以定期刷新以获取工具更新。工具列表可以基于用户权限或上下文进行过滤。
- 客户端通过
选择阶段
- LLM分析用户请求和可用工具
- LLM确定完成任务需要使用哪个工具
- LLM构造适当的调用参数
- LLM生成工具调用请求
选择阶段依赖于LLM的理解能力,将自然语言请求转化为结构化工具调用。优质的工具描述和示例对于正确选择至关重要。
批准阶段
- 客户端向用户展示拟议的工具调用
- 用户查看工具名称、参数和目的
- 用户决定批准、拒绝或修改调用
- 客户端记录用户决定以及任何修改
批准阶段是人类在循环中的关键点,为用户提供控制权并建立系统透明度。界面设计应清晰呈现工具调用的含义和潜在影响。
执行阶段
- 客户端使用
tools/call
端点发送工具调用请求 - 服务器接收请求并验证参数
- 服务器执行工具操作
- 服务器构造并返回结果
执行阶段涉及实际操作的处理,可能包括外部系统交互、数据处理或状态修改。服务器负责处理错误和异常情况。
- 客户端使用
结果处理阶段
- 客户端接收工具执行结果
- 结果传递回LLM进行解释
- LLM生成用户可理解的响应
- 客户端向用户呈现最终响应
结果处理阶段将原始工具输出转化为有意义的用户反馈。LLM在解释复杂结果和技术输出方面发挥关键作用。
这个流程的各个阶段可能在不同实现中变化,但核心原则保持不变:发现可用工具,选择适当工具,获取用户批准,执行工具调用,处理和呈现结果。
人类在循环中的角色
工具调用流程中,人类用户扮演关键的监督角色,他们:
- 批准或拒绝拟议的工具调用
- 审查和可能修改调用参数
- 评估工具结果和后续步骤
- 提供额外的上下文或纠正错误理解
这种”人类在循环”模式为系统引入了重要的安全机制,特别是对于具有潜在影响的操作。它允许用户保持对LLM行动的控制,同时仍然从自动化中受益。
一些MCP实现可能根据工具的性质或用户偏好提供不同级别的批准要求,例如:
- 每次调用都需要批准
- 仅对特定工具或参数范围需要批准
- 批量批准类似操作
- 为低风险操作提供自动批准
这种灵活性使MCP能够在自动化和控制之间取得适当平衡,满足不同的安全和效率需求。
3.2.3 工具模式与示例
MCP工具可以采用各种模式和模型来满足不同的应用需求。了解这些常见模式有助于设计更有效、更易用的工具。以下是一些典型的工具模式,每种模式都有其特定用途和设计考虑。
1. 系统操作工具
系统操作工具允许LLM与底层操作系统或基础设施交互。这类工具通常需要谨慎设计和严格的权限控制,因为它们可能有广泛的系统影响。
典型用途:
- 执行命令和脚本
- 管理进程和服务
- 访问系统信息和指标
- 控制系统配置和设置
设计考虑:
- 实施严格的命令白名单或沙箱执行
- 限制操作范围和影响
- 明确记录和审计所有系统操作
- 提供详细的权限控制选项
2. API集成工具
API集成工具充当外部服务和API的包装器,使LLM能够访问第三方功能和数据。这些工具通常遵循底层API的模型,但添加了额外的验证、转换和错误处理。
典型用途:
- 与SaaS平台集成
- 访问云服务功能
- 连接到社交媒体平台
- 使用专业或垂直服务
设计考虑:
- 处理身份验证和凭证管理
- 转换和格式化API响应
- 实施速率限制和配额管理
- 处理API变更和版本控制
3. 数据处理工具
数据处理工具专注于数据的转换、分析和操作。这些工具通常接受复杂的输入模式,并执行可能超出LLM能力范围的计算和分析任务。
典型用途:
- 分析数据集和计算统计数据
- 转换文件格式和数据结构
- 过滤和聚合大型数据集
- 生成报告和可视化
设计考虑:
- 处理各种数据格式和结构
- 提供适当的性能和资源管理
- 支持批处理和流处理模式
- 实现数据验证和清理功能
4. 上下文感知工具
上下文感知工具能够访问会话上下文、用户偏好和环境信息。这些工具可以提供个性化和情境适应的行为,使LLM响应更加相关和有效。
典型用途:
- 获取用户历史和偏好
- 访问会话状态和进度
- 获取环境信息(如时间、位置)
- 管理上下文相关的资源
设计考虑:
- 保护用户隐私和敏感信息
- 提供适当的上下文范围和限制
- 支持上下文的有效存储和检索
- 在不同会话和用户间维护隔离
5. 多步骤和事务性工具
多步骤和事务性工具支持复杂的操作序列,可能涉及多个系统或资源。这些工具通常包含内部状态管理和事务逻辑,以确保操作的一致性和完整性。
典型用途:
- 执行业务流程和工作流
- 处理多阶段事务
- 协调跨系统操作
- 管理长时间运行的任务
设计考虑:
- 提供事务支持和错误恢复
- 实现状态管理和检查点
- 支持操作的暂停和恢复
- 提供进度跟踪和状态报告
工具设计的进阶模式
除了基本工具类型外,还有一些高级设计模式可以提升工具的有效性和可用性:
组合工具模式
组合工具将多个相关功能打包到单个工具中,具有统一的接口和参数模式。这简化了工具发现和使用,同时减少了LLM需要理解的工具数量。
示例:一个文件处理工具可能提供读取、写入、移动和删除操作,所有这些都通过单个统一接口访问,而不是作为单独的工具。
渐进式披露模式
渐进式披露工具初始提供简单界面,但允许高级用户访问更复杂的选项和功能。这使工具既易于入门又能满足高级需求。
示例:一个搜索工具可能默认提供简单的查询参数,但允许通过高级参数访问更精细的控制,如筛选器、排序选项和结果格式。
反馈增强模式
反馈增强工具提供丰富的元数据和诊断信息,帮助LLM和用户理解工具行为和结果。这促进了更有效的工具使用和问题解决。
示例:数据分析工具可能不仅返回结果,还提供处理统计、数据质量指标和潜在问题警告。
这些模式不是互斥的,可以根据需要组合使用以创建强大且用户友好的工具。
3.2.4 错误处理与安全模型
MCP的工具设计包含全面的错误处理和安全模型,确保工具既可靠又安全。这些机制是工具功能的基础,而非事后考虑,反映了MCP对稳健性和安全性的承诺。
错误处理机制
MCP工具错误处理的核心原则是透明性和有效沟通。工具应清晰传达错误状态,使LLM能够理解问题并采取适当行动。
工具错误应在结果对象内报告,而非作为MCP协议级错误。这使LLM能够查看并可能处理错误,而非仅将其视为通信失败。当工具遇到错误时,它应:
- 在结果中将
isError
设置为true
- 在
content
数组中包含错误详情,包括:- 错误类型或代码
- 人类可读的错误描述
- 可能的补救措施或替代方案
- 相关诊断信息(如适用)
这种结构化错误报告允许LLM:
- 识别并理解发生的错误
- 在可能的情况下自动恢复或尝试替代方法
- 向用户提供有用的解释和建议
- 在需要时请求用户干预或额外信息
错误处理还应该考虑不同类型的错误,包括:
- 验证错误:参数不符合预期的类型、格式或约束
- 资源错误:请求的资源不存在或无法访问
- 权限错误:用户没有执行操作的权限
- 系统错误:内部故障或不可用服务
- 业务规则错误:操作违反应用程序或领域规则
- 超时错误:操作未在预期时间内完成
对于复杂或长时间运行的工具,错误处理还可能包括部分成功状态,其中某些操作成功而其他操作失败。在这些情况下,结果应清楚地传达哪些部分成功、哪些失败以及整体状态。
安全模型
MCP工具的安全模型基于深度防御原则,通过多层保护确保系统安全。这种方法认识到没有单一的安全措施是完美的,而是依靠多层次的保护来减轻风险。
输入验证和清理
输入验证是安全模型的第一道防线,确保工具仅接受符合其设计和预期用途的数据。有效的输入验证包括:
- 针对模式验证所有参数的类型、格式和范围
- 清理和规范化输入,特别是文件路径和命令
- 验证外部标识符和引用的完整性
- 实施输入大小和复杂度限制
- 检测并防止注入攻击(如SQL注入、命令注入)
输入验证应遵循”白名单”而非”黑名单”方法,明确定义允许的内容而非尝试阻止已知的恶意内容。
权限和访问控制
强大的权限模型确保工具操作仅在授权用户的适当上下文中执行。有效的访问控制包括:
- 实施基于角色的访问控制(RBAC)或属性基于的访问控制(ABAC)
- 应用最小权限原则,仅授予完成任务所需的访问权限
- 为敏感操作实施多因素审批
- 持续验证和重新评估权限
- 维护详细的访问日志和审计记录
工具应集成到主机应用程序的更广泛权限框架中,而非实施单独的安全模型。
执行环境安全
工具执行环境应该安全且受控,防止未授权访问或操作。保护执行环境的措施包括:
- 在沙箱或隔离环境中运行工具代码
- 限制资源访问和系统调用
- 监控和限制资源使用(内存、CPU、网络等)
- 实施超时和终止机制
- 保护敏感数据和凭证
执行环境应基于”零信任”原则设计,即使在受信环境中也不假设安全。
人机交互安全控制
“人类在循环中”模型是MCP安全设计的关键方面,确保用户了解且批准工具行为:
- 清晰传达工具目的和预期影响
- 提供详细的参数描述和验证
- 对敏感或高影响操作显示显著警告
- 允许用户在执行前修改参数
- 提供撤销或回滚机制(如可能)
这些控制使用户能够做出明智的决策,同时防止误用或滥用工具。
数据保护和隐私
工具应该保护它们处理的数据的隐私和安全:
- 仅处理完成任务所需的数据
- 遵守数据最小化原则
- 实施适当的数据加密(静态和传输中)
- 安全地处理敏感信息
- 符合相关隐私法规和政策
数据保护应考虑整个数据生命周期,从收集到处理再到存储和最终删除。
这些安全层共同创建了一个强大的保护框架,同时保持工具的可用性和功能性。重要的是,安全不应被视为障碍,而是使工具能够可靠且负责任地完成其功能的使能因素。
3.3 提示词(Prompts)详解
提示词是MCP的另一个核心组件,使服务器能够定义可重用的提示模板和工作流。它们为标准化和共享常见LLM交互提供了强大的方式。
3.3.1 提示词的定义与结构
提示词的本质与目的
提示词是预定义的提示模板,旨在标准化和优化常见的LLM交互模式。它们在概念上类似于软件开发中的设计模式或库,提供经过测试和优化的解决方案,用于常见的交互挑战。
提示词的核心目的是:
- 封装提示工程的最佳实践
- 标准化常见交互模式
- 提高LLM响应的一致性和质量
- 简化复杂提示的创建
- 为用户提供预构建的工作流程
通过提示词,MCP服务器可以提供精心设计的交互模板,而不必依赖用户或客户端来构造有效的提示。这大大提高了整体系统的可用性和一致性。
提示词的正式结构
在MCP中,每个提示词都有一个明确定义的结构,包含以下核心元素:
{
"name": "string", // 提示词的唯一标识符
"description?": "string", // 人类可读的描述
"arguments?": [ // 可选的参数列表
{
"name": "string", // 参数标识符
"description?": "string", // 参数描述
"required?": boolean // 参数是否必需
}
]
}
这种结构设计具有多重优势:
- 为提示词提供清晰的身份和目的
- 明确定义参数及其要求
- 支持文档和自动发现
- 允许参数验证和类型检查
- 促进提示词库的有组织发展
提示词的消息结构
当提示词被渲染(即与特定参数一起使用)时,它生成一系列消息。这些消息遵循特定结构:
{
"role": "user" | "assistant", // 消息角色
"content": {
"type": "text" | "resource" | ..., // 内容类型
// 类型特定字段,例如:
"text": "string", // 文本内容
"resource": { ... } // 资源引用
}
}
这种消息结构允许提示词生成复杂的交互序列,包括:
- 多轮对话和交换
- 包含各种内容类型的混合消息
- 嵌入资源和上下文
- 结构化的响应和查询模式
通过这种灵活的消息结构,提示词可以模拟自然对话流程,同时包含丰富的上下文和媒体。
提示词的类型与分类
提示词可以根据其目的和复杂性分为多种类型:
单轮提示词:生成单个用户消息的简单模板
{
"name": "analyze-code",
"description": "分析代码以寻找潜在改进",
"arguments": [{ "name": "language", "required": true }, { "name": "code", "required": true }]
}
多轮对话提示词:模拟多轮交互的复杂提示词
{
"name": "debug-workflow",
"description": "引导用户完成调试过程",
"arguments": [{ "name": "error", "required": true }]
}
上下文增强提示词:自动注入其他资源或上下文的提示词
{
"name": "analyze-logs",
"description": "分析系统日志以发现模式",
"arguments": [{ "name": "timeframe", "required": true }]
}
专家提示词:模拟特定领域专家的提示词
{
"name": "medical-assessment",
"description": "模拟医疗专业人员评估症状",
"arguments": [{ "name": "symptoms", "required": true }]
}
这些类型不是互斥的,许多实际提示词会组合多种类型的特征。
3.3.2 提示词的发现与使用
提示词发现机制
MCP提供了标准化的机制,使客户端能够发现和了解可用的提示词。这种发现过程使用户能够利用服务器提供的预构建交互模式。
提示词发现通过prompts/list
端点实现:
// 请求
{
"method": "prompts/list"
}
// 响应
{
"prompts": [
{
"name": "analyze-code",
"description": "分析代码以寻找潜在改进",
"arguments": [
{
"name": "language",
"description": "编程语言",
"required": true
}
]
}
]
}
提示词发现过程允许客户端:
- 检索所有可用提示词的列表
- 了解每个提示词的目的和用法
- 识别需要的参数
- 在用户界面中适当地展示提示词选项
客户端通常会在用户界面中将提示词表示为斜杠命令、快速操作或上下文菜单项。这使用户能够轻松访问常用交互,而无需手动构造复杂提示。
提示词的使用流程
使用提示词遵循一个结构化流程,确保有效的参数验证和提示渲染:
选择阶段
- 客户端向用户展示可用提示词
- 用户选择适当的提示词
- 客户端准备收集必要的参数
参数收集阶段
- 客户端为提示词的每个参数提示用户输入
- 客户端验证输入符合参数要求
- 客户端构建参数集合
渲染阶段
- 客户端使用
prompts/get
端点请求渲染的提示 - 服务器处理参数并生成消息
- 服务器返回完全渲染的消息序列
- 客户端使用
使用阶段
- 客户端将渲染的消息添加到对话上下文
- 消息提交给LLM进行处理
- LLM生成响应
该流程设计为直观且用户友好,同时确保提示词以一致且有效的方式使用。
prompts/get
请求的典型交互:
// 请求
{
"method": "prompts/get",
"params": {
"name": "analyze-code",
"arguments": {
"language": "python"
}
}
}
// 响应
{
"description": "分析Python代码以寻找潜在改进",
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "请分析以下Python代码以寻找潜在改进:\n\n```python\ndef calculate_sum(numbers):\n total = 0\n for num in numbers:\n total = total + num\n return total\n\nresult = calculate_sum([1, 2, 3, 4, 5])\nprint(result)\n```"
}
}
]
}
提示词在用户界面中的表示
提示词可以在客户端界面中以多种方式呈现,每种方式提供不同的用户体验:
斜杠命令
- 用户输入”/“触发提示词列表
- 用户选择所需提示词
- 界面引导用户完成参数输入
- 最终提示自动插入对话
上下文菜单
- 用户右键点击或使用菜单按钮
- 显示适用于当前上下文的提示词
- 用户选择提示词并输入参数
- 生成的提示添加到对话中
快速操作
- 界面显示常用或上下文相关的提示词快捷方式
- 用户点击快捷方式以触发提示词
- 简化的参数收集(可能使用默认值)
- 快速将提示添加到对话
引导式工作流
- 提示词表示为多步骤工作流或向导
- 用户通过结构化流程逐步前进
- 每个步骤收集特定参数或上下文
- 完成时生成完整的提示序列
这些界面方法可以组合使用,为不同类型的用户和用例提供最佳体验。
3.3.3 动态提示词与多步工作流
动态提示词的设计需要在灵活性与可维护性之间取得平衡。复杂的逻辑应该封装在服务器端处理中,而非嵌入模板本身,以确保提示词仍然清晰且高效。
嵌入资源与上下文
动态提示词的一个强大功能是能够从各种来源嵌入资源和上下文。这允许提示词利用相关数据丰富交互:
{
"name": "analyze-project",
"description": "分析项目日志和代码",
"arguments": [
{
"name": "timeframe",
"description": "分析日志的时间段",
"required": true
},
{
"name": "fileUri",
"description": "要审查的代码文件URI",
"required": true
}
]
}
当处理prompts/get
请求时,服务器可能生成:
{
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "分析这些系统日志和代码文件是否有任何问题:"
}
},
{
"role": "user",
"content": {
"type": "resource",
"resource": {
"uri": "logs://recent?timeframe=1h",
"text": "错误和警告日志内容...",
"mimeType": "text/plain"
}
}
},
{
"role": "user",
"content": {
"type": "resource",
"resource": {
"uri": "file:///path/to/code.py",
"text": "Python代码内容...",
"mimeType": "text/x-python"
}
}
}
]
}
在此示例中,提示词自动包含了两个关键资源:系统日志和相关代码文件。这为LLM提供了完整的上下文,使其能够执行更深入的分析,而用户不必手动收集和提供这些资源。
嵌入资源的优势包括:
- 上下文完整性 - LLM接收到所有相关信息,无需多次交互
- 自动化数据收集 - 服务器处理资源检索,减轻用户负担
- 标准化呈现 - 资源以一致的格式提供给LLM
- 动态更新 - 资源内容可以在请求时生成或更新
通过嵌入资源,提示词可以创建丰富、信息充分的交互,而无需用户手动提供所有必要数据。这使交互更加高效和用户友好。
多步骤工作流
多步骤工作流是动态提示词的一个高级应用,它模拟结构化对话或引导式流程。这些工作流在复杂任务中特别有价值,需要按特定顺序收集信息或执行操作。
多步骤工作流可以:
- 引导用户完成复杂过程
- 确保信息以结构化方式收集
- 在每个步骤提供上下文相关指导
- 根据之前步骤的结果调整后续步骤
- 维护跨多个交互的状态
例如,调试工作流提示词可能结构化为:
const debugWorkflow = {
name: "debug-error",
async getMessages(error: string) {
return [
{
role: "user",
content: {
type: "text",
text: `这是我看到的错误: ${error}`
}
},
{
role: "assistant",
content: {
type: "text",
text: "我将帮助分析此错误。到目前为止您尝试了什么?"
}
},
{
role: "user",
content: {
type: "text",
text: "我已尝试重启服务,但错误仍然存在。"
}
}
];
}
};
这种工作流不仅包含用户消息,还预设了助手响应和后续用户输入。这创建了一个全面的交互模板,指导整个对话过程。
多步骤工作流可以是:
- 线性的:遵循预定义的步骤序列
- 分支的:基于用户输入或外部条件选择不同路径
- 迭代的:允许重复步骤直到满足特定条件
- 条件的:仅在满足特定标准时包含某些步骤
实现多步骤工作流通常需要更复杂的服务器逻辑来管理状态和控制流程。在某些情况下,它可能涉及与工具集成,允许工作流执行操作并基于结果做出决策。
多步骤工作流的关键优势包括:
- 结构化体验 - 引导用户完成复杂过程,减少困惑和错误
- 信息完整性 - 确保收集所有必要信息,不会遗漏关键细节
- 适应性指导 - 基于之前步骤提供相关上下文和建议
- 一致性 - 标准化常见任务流程,提高用户体验一致性
- 专业知识编码 - 将领域专家知识嵌入交互模式中
通过多步骤工作流,MCP使开发者能够创建复杂且结构化的用户体验,远超简单提示模板的可能性。
3.3.4 最佳实践与安全考量
提示词设计的最佳实践
创建有效的提示词需要仔细考虑设计和实现。以下是设计高质量提示词的关键最佳实践:
- 使用清晰、描述性的提示词名称 提示词名称应该简洁而有信息量,清晰传达提示词的目的。好的名称使用户和LLM能够快速理解提示词的功能。 例如,使用”analyze-code-security”而非模糊的”code-review”或过于详细的”find-security-vulnerabilities-in-source-code-and-suggest-mitigations”。
- 提供详细的提示词和参数描述 全面的描述帮助用户和LLM理解提示词的功能、限制和预期用途。参数描述应明确说明每个参数的目的、类型和约束。 好的描述不仅解释提示词做什么,还说明适用场景和任何重要限制。
- 验证所有必需的参数 提示词实现应该检查所有必需参数的存在和有效性。对无效或缺失参数提供清晰的错误消息,帮助用户理解并解决问题。 参数验证不仅应检查存在性,还应验证类型、格式和值范围。
- 优雅处理缺失的参数 对于可选参数,提示词应该实现合理的默认行为或值。这确保提示词仍然有效,即使用户没有提供所有可能的参数。 明确记录默认行为,使用户了解在不提供特定参数时会发生什么。
- 考虑提示词的可组合性 设计提示词时考虑它们如何相互组合或与其他MCP功能(如工具和资源)集成。模块化设计使提示词更灵活且可重用。 考虑创建可以组合成更复杂工作流的小型、专注的提示词,而非单个庞大的提示词。
- 使用适当的消息结构 根据提示词的目的选择最有效的消息结构。这可能是单条消息、多轮对话或包含多种内容类型的混合结构。 例如,代码分析提示词可能使用混合文本和代码块,而故障排除提示词可能受益于模拟多轮对话。
- 缓存动态内容 对于包含从外部源获取内容的动态提示词,实施适当的缓存策略。这减少了延迟并提高了性能,特别是对于不频繁变化的内容。 缓存机制应考虑内容的刷新频率和时间敏感性,以平衡性能与数据新鲜度。
- 测试各种输入 广泛测试提示词,使用各种参数值和场景。这确保提示词在不同条件下表现一致且可靠。 测试应包括典型用例、边缘情况和错误条件,确保提示词在所有情况下都表现良好。
- 关注国际化和可访问性 设计提示词时考虑多语言支持和可访问性需求。这可能包括支持不同语言、考虑文化差异,以及确保提示清晰且易于理解。 记录任何语言或地区特定的限制或考虑事项,以指导适当使用。
- 维护文档和示例 提供全面的文档和使用示例,使用户和其他开发者能够有效利用提示词。好的文档包括用途、参数、示例输出和任何已知限制。 示例应该涵盖常见用例以及更复杂或特殊的场景,展示提示词的全部功能。
提示词安全考量
提示词的安全与安全设计同样重要。以下是提示词安全的关键考虑因素:
- 输入验证和清理 验证所有提示词参数,确保它们符合预期类型、格式和范围。特别注意可能涉及敏感操作的参数,如文件路径、命令或查询。 清理和规范化输入,防止常见的注入攻击,如SQL注入、命令注入或提示注入。
- 限制敏感信息暴露 谨慎处理提示词中的敏感信息。限制敏感数据的暴露,如果必须包含,考虑模糊处理、截断或以其他方式保护它。 避免在提示中包含凭证、密钥或其他安全信息,即使它们在最终渲染中不可见。
- 实施内容过滤和安全保护 过滤提示词内容,防止不适当、有害或滥用行为。这可能包括检查敏感话题、不安全指令或违反使用政策的内容。 为不同的用户组和上下文实施适当的内容政策,确保提示词的使用安全且合适。
- 提示注入保护 防范提示注入攻击,即恶意用户尝试操纵提示以绕过系统限制或获取未授权行为。 技术包括参数清理、输入验证、界限标记和内容检测,以防止恶意提示操纵。
- 正确处理错误和异常 实施强大的错误处理,避免在错误条件下泄露敏感信息。错误消息应该提供足够的信息帮助解决问题,而不泄露内部细节。 记录错误和异常以进行监控和审计,同时保护敏感信息不被包含在日志中。
- 回退和降级策略 为提示词创建安全的回退和降级策略,以处理错误、不可用服务或其他故障条件。这确保系统仍能提供基本功能,即使在非理想条件下。 明确记录和传达回退行为,使用户了解在故障情况下会发生什么。
- 审计和监控 实施提示词使用的审计和监控,以检测潜在的滥用或安全问题。监控应该考虑访问模式、参数值和使用频率。 建立异常检测机制,识别可能表明安全问题的不寻常模式。
- 文档安全考虑 明确记录提示词的安全要求、限制和最佳实践。这使用户能够安全地使用提示词,同时了解任何潜在风险。 为不同安全级别的场景提供明确的使用指南和建议。
通过遵循这些最佳实践和安全考虑,开发者可以创建既有效又安全的提示词,丰富MCP生态系统,并为用户提供优质体验。
3.4 其他组件
除了核心的资源、工具和提示词组件外,MCP还包括一系列重要的辅助组件,这些组件增强了协议的功能和灵活性。这些组件为高级用例提供了额外的能力,使MCP能够满足更广泛的应用需求。
3.4.1 采样(Sampling)
采样是MCP的一个强大功能,允许服务器通过客户端从LLM请求完成内容。这个功能使服务器能够利用LLM的生成能力,同时保持安全性和隐私性,从而实现复杂的代理行为。
采样的本质与目的
采样功能允许MCP服务器在其自身的处理流程中利用LLM的能力。这在本质上创建了一个”LLM在循环中”的模式,其中服务器可以:
- 为特定任务生成定制提示
- 接收LLM的响应和建议
- 基于LLM输出做出决策或执行操作
- 在执行过程中反复咨询LLM
这种能力使服务器能够实现更智能和自适应的行为,同时仍然受到MCP安全模型的约束。
需要注意的是,截至知识库中的信息,采样功能尚未在Claude Desktop客户端中得到支持。这意味着虽然该功能在MCP规范中定义,但它可能在不同客户端实现中的可用性有所不同。
采样工作流程
采样遵循一个仔细设计的工作流程,确保安全性和用户控制:
- 服务器发送
sampling/createMessage
请求到客户端 - 客户端审查请求并可以修改它
- 客户端从LLM采样
- 客户端审查完成内容
- 客户端将结果返回给服务器
这种人机协作设计确保用户对LLM看到的内容和生成的内容保持控制权。客户端可以修改或拒绝采样请求,基于安全、隐私或其他考虑。
采样消息格式
采样请求使用标准化的消息格式:
{
"messages": [
{
"role": "user" | "assistant",
"content": {
"type": "text" | "image",
// 对于文本:
"text?": "string",
// 对于图像:
"data?": "string", // base64编码
"mimeType?": "string"
}
}
],
"modelPreferences?": {
"hints?": [{
"name?": "string" // 建议的模型名称/系列
}],
"costPriority?": number, // 0-1,降低成本的重要性
"speedPriority?": number, // 0-1,低延迟的重要性
"intelligencePriority?": number // 0-1,能力的重要性
},
"systemPrompt?": "string",
"includeContext?": "none" | "thisServer" | "allServers",
"temperature?": number,
"maxTokens": number,
"stopSequences?": string[],
"metadata?": Record<string, unknown>
}
这种格式允许服务器指定对话历史、模型偏好、上下文包含和采样参数,为特定任务定制LLM交互。
模型偏好
采样请求可以指定模型选择偏好,使服务器能够表达其需求:
hints
:客户端可以用来选择适当模型的模型名称建议数组name
:可以匹配完整或部分模型名称的字符串(如”claude-3”,”sonnet”)- 客户端可能将提示映射到来自不同提供商的等效模型
- 多个提示按偏好顺序评估
- 优先级值(0-1规范化):
costPriority
:最小化成本的重要性speedPriority
:低延迟响应的重要性intelligencePriority
:高级模型能力的重要性
客户端基于这些偏好和其可用模型做出最终模型选择。
上下文包含
includeContext
参数指定要包含的MCP上下文:
"none"
:不包含额外上下文"thisServer"
:包含来自请求服务器的上下文"allServers"
:包含来自所有连接的MCP服务器的上下文
这使服务器能够控制LLM可访问的上下文范围,平衡信息完整性与性能和隐私考虑。
采样参数
采样可以通过多个参数进行微调:
temperature
:控制随机性(0.0到1.0)maxTokens
:要生成的最大标记数stopSequences
:停止生成的序列数组metadata
:附加的提供商特定参数
这些参数允许服务器为特定任务定制LLM行为,如更具创造性的拓展、受控长度的响应或特定的生成停止条件。
采样响应格式
客户端返回完成结果:
{
"model": "string", // 使用的模型名称
"stopReason?": "endTurn" | "stopSequence" | "maxTokens" | string,
"role": "user" | "assistant",
"content": {
"type": "text" | "image",
"text?": "string",
"data?": "string",
"mimeType?": "string"
}
}
这种结构化响应包含关于所用模型、停止原因和生成内容的元数据,使服务器能够有效处理结果。
采样的高级用途
采样支持多种高级用例,包括:
- 代理工作流
- 读取和分析资源
- 基于上下文做出决策
- 生成结构化数据
- 处理多步骤任务
- 提供交互式辅助
- 上下文管理
- 请求最小必要上下文
- 清晰结构化上下文
- 处理上下文大小限制
- 根据需要更新上下文
- 清理过时上下文
采样使服务器能够实现更智能和自适应的行为,同时仍然受到MCP安全模型和人类监督的约束。
3.4.2 根目录(Roots)
根目录是MCP中的一个概念,它定义了服务器可以操作的边界。根目录为客户端提供了一种方式,通过声明服务器应该关注哪些资源及其位置,来指导服务器的操作范围。
根目录的定义与目的
根目录是客户端向服务器建议的应该关注的URI。当客户端连接到服务器时,它声明服务器应该使用哪些根目录。虽然主要用于文件系统路径,但根目录可以是任何有效的URI,包括HTTP URL。
根目录服务于几个重要目的:
- 指导:它们告知服务器相关资源和位置
- 清晰性:根目录明确表明哪些资源是工作区的一部分
- 组织:多个根目录让您可以同时使用不同的资源
根目录的工作原理
当客户端支持根目录时,它会:
- 在连接期间声明
roots
能力 - 向服务器提供建议根目录列表
- 在根目录变更时通知服务器(如果支持)
根目录是信息性的,而非严格强制的。这意味着它们提供指导而非硬性限制。然而,尊重这些服务器应该:
- 尊重提供的根目录
- 使用根目录URI定位和访问资源
- 优先考虑根目录边界内的操作
根目录的常见用例
根目录通常用于定义:
- 项目目录
- 仓库位置
- API端点
- 配置位置
- 资源边界
例如,一个开发环境可能定义以下根目录:
file:///home/user/projects/frontend
- 前端代码仓库file:///home/user/projects/backend
- 后端代码仓库https://api.example.com/v1
- 开发API端点
这种配置使服务器能够了解项目的组织结构,并相应地调整其行为,例如在搜索文件或提供建议时。
根目录规范
一个典型的MCP客户端可能按以下方式公开根目录:
{
"roots": [
{
"uri": "file:///home/user/projects/frontend",
"name": "前端仓库"
},
{
"uri": "https://api.example.com/v1",
"name": "API端点"
}
]
}
此配置建议服务器关注本地仓库和API端点,同时保持它们在逻辑上的分离。
根目录的使用最好与工具和资源功能集成,允许服务器在适当的上下文中处理用户请求。例如,文件搜索工具可能首先检查根目录内的文件,或者代码分析工具可能优先处理根目录下的文件。
3.4.3 调试与监控
调试和监控功能是成功开发和维护MCP集成的关键。MCP提供了多种工具和方法来检测、诊断和解决问题,无论是在开发过程中还是在生产环境中。
调试工具概述
MCP提供了几种用于不同级别调试的工具:
- MCP Inspector
- 交互式调试界面
- 直接服务器测试
- 请参阅《Inspector 指南》了解详情
- Claude Desktop 开发工具
- 集成测试
- 日志收集
- Chrome DevTools 集成
- 服务器日志记录
- 自定义日志实现
- 错误跟踪
- 性能监控
这些工具和方法相互补充,提供了从低级协议调试到高级行为分析的全面覆盖。
在 Claude Desktop 中调试
Claude Desktop 界面提供了基本的服务器状态信息:
- 点击图标可查看:
- 已连接的服务器
- 可用的提示词和资源
- 点击工具图标可查看:
- 向模型提供的工具
对于更详细的调试信息,可以查看 Claude Desktop 中的 MCP 日志:
# 实时跟踪日志
tail -n 20 -F ~/Library/Logs/Claude/mcp*.log
这些日志捕获:
- 服务器连接事件
- 配置问题
- 运行时错误
- 消息交换
使用 Chrome DevTools
可以在 Claude Desktop 内部访问 Chrome 的开发者工具,以调查客户端错误:
- 创建
developer_settings.json
文件,将allowDevTools
设置为 true:echo '{"allowDevTools": true}' > ~/Library/Application Support/Claude/developer_settings.json
- 打开 DevTools:
Command-Option-Shift-i
注意:会出现两个 DevTools 窗口:
- 主内容窗口
- 应用标题栏窗口
使用控制台面板检查客户端错误。使用网络面板检查:
- 消息负载
- 连接计时
常见问题
在使用 MCP 服务器时,一些常见问题及其解决方法包括:
- 工作目录问题
- 通过
claude_desktop_config.json
启动的服务器的工作目录可能未定义(如 macOS 上的/
),因为 Claude Desktop 可能从任何位置启动 - 在配置和
.env
文件中始终使用绝对路径,以确保可靠操作 - 对于通过命令行直接测试的服务器,工作目录将是您运行命令的位置
- 通过
- 环境变量
- MCP 服务器仅自动继承环境变量的子集,如
USER
、HOME
和PATH
- 要覆盖默认变量或提供自己的变量,可以在
claude_desktop_config.json
中指定env
键
- MCP 服务器仅自动继承环境变量的子集,如
- 服务器初始化问题
- 路径问题:服务器可执行文件路径不正确、缺少必需文件、权限问题
- 配置错误:无效的 JSON 语法、缺少必需字段、类型不匹配
- 环境问题:缺少环境变量、变量值不正确、权限限制
服务器日志记录
在构建使用本地 stdio 传输的服务器时,所有记录到 stderr(标准错误)的消息将自动被主机应用程序(如 Claude Desktop)捕获。本地 MCP 服务器不应将消息记录到 stdout(标准输出),因为这会干扰协议操作。
对于所有传输,也可以通过发送日志消息通知向客户端提供日志记录:
server.request_context.session.send_log_message(
level="info",
data="服务器成功启动",
)
重要的记录事件包括:
- 初始化步骤
- 资源访问
- 工具执行
- 错误条件
- 性能指标
开发周期
- 初始开发
- 使用 Inspector 进行基本测试
- 实现核心功能
- 添加日志点
- 集成测试
- 在 Claude Desktop 中测试
- 监控日志
- 检查错误处理
测试变更
要高效测试更改:
- 配置更改:重新启动 Claude Desktop
- 服务器代码更改:使用 Command-R 重新加载
- 快速迭代:在开发过程中使用 Inspector
日志记录策略
结构化日志记录
- 使用一致的格式
- 包含上下文
- 添加时间戳
- 跟踪请求 ID
错误处理
- 记录堆栈跟踪
- 包含错误上下文
- 跟踪错误模式
- 监控恢复
性能跟踪
- 记录操作计时
- 监控资源使用
- 跟踪消息大小
- 测量延迟
良好的日志记录策略是有效调试和监控的基础。结构化日志确保信息一致且可搜索,而全面的错误处理使开发者能够快速识别和解决问题。性能跟踪帮助识别瓶颈和优化机会。
安全考虑
在调试和监控时,安全性是一个重要考量:
日志敏感数据
- 避免记录密码、令牌和个人身份信息
- 在记录前清理敏感字段
- 实施适当的日志访问控制
- 考虑日志生命周期和保留策略
调试安全模型
- 在开发/调试环境中维持安全控制
- 为测试和生产使用不同的安全配置
- 确保调试工具不会创建安全漏洞
- 谨慎处理在调试期间获取的信息
调试需要平衡诊断能力与安全责任。要收集足够的信息来解决问题,同时保护敏感数据和系统完整性。
获取帮助
当遇到问题时:
第一步
- 检查服务器日志
- 使用Inspector测试
- 审查配置
- 验证环境
支持渠道
- GitHub问题
- GitHub讨论
- 社区论坛
提供信息
- 日志摘录
- 配置文件
- 重现步骤
- 环境详情
提供尽可能多的相关信息有助于更快速、更有效地解决问题。详细的错误信息、配置和环境详情使他人能够更容易地理解和解决问题。
通过这些调试和监控功能,开发者可以构建可靠且高性能的MCP集成,同时确保问题可以快速识别和解决。
总结
MCP的核心组件 - 资源、工具、提示词以及辅助功能如采样、根目录和调试工具 - 共同构成了一个全面的框架,使LLM能够以安全、标准化和可扩展的方式与外部世界交互。
资源提供了LLM获取数据的方式,工具提供了执行操作的能力,而提示词则优化了交互模式。这三个主要组件相互补充,创建了一个完整的生态系统:资源提供”知道什么”,工具实现”做什么”,提示词定义”如何交互”。辅助组件增强了这个生态系统,提供了更高级的功能、更好的组织和更强的可维护性。
通过这些组件,MCP实现了其核心价值主张:标准化LLM上下文提供、保持数据安全性以及创建开放且可扩展的生态系统。这些组件的深思熟虑的设计反映了MCP的设计原则,强调安全性、可用性和灵活性的平衡。
随着MCP的发展,这些核心组件可能会扩展和演变,但它们代表的基本范式 - 数据访问、操作能力和交互模式的结合 - 将继续是LLM应用程序架构的基础。通过掌握这些组件,开发者可以创建功能强大、安全且用户友好的应用程序,充分利用LLM的能力,同时尊重用户数据和系统完整性。
MCP为AI应用开发带来了新的可能性,它的开放协议方法将推动创新并促进更广泛的生态系统发展。随着更多开发者和组织采用MCP,我们可以期待看到越来越多的创新集成和应用场景。