核心概念:Tools、Resources、Templates 与 Prompts
这篇文档解释了 ZCP 和 MCP 集成中最常见、也是由服务端拥有的对象模型。
总览
大多数应用设计问题最终都会落到一个选择上:
- 这是一个操作吗
- 这是一个内容对象吗
- 这是一个 prompt 脚手架吗
在 ZCP 和 MCP 语境里,这意味着要在以下对象之间做选择:
- tools
- resources 和 resource templates
- prompts
这个选择是否正确,会直接影响 token 效率、客户端发现能力、认证边界,以及长期 API 稳定性。
Tools
Tool 是可调用的服务端操作。当客户端需要“让服务端做点什么”,而不只是“读点什么”时,它就是正确的抽象。
典型的 tool 元数据包括:
nametitledescriptioninputSchemaoutputSchemaannotationsiconsexecution_meta
在 Python SDK 中,tool 通常通过 FastZCP.tool(...) 注册。
from zcp import FastZCP
app = FastZCP("Operations Backend")
@app.tool(
name="incident.create",
description="Create an incident ticket.",
input_schema={
"type": "object",
"properties": {
"title": {"type": "string"},
"severity": {"type": "string", "enum": ["sev1", "sev2", "sev3"]},
},
"required": ["title", "severity"],
"additionalProperties": False,
},
output_schema={
"type": "object",
"properties": {
"incidentId": {"type": "string"},
"url": {"type": "string"},
},
"required": ["incidentId", "url"],
},
output_mode="scalar",
inline_ok=True,
)
def create_incident(title: str, severity: str, ctx=None):
return {"incidentId": "INC-123", "url": "https://ops.example.com/INC-123"}什么时候应该使用 Tool
当服务端预期要做以下事情时,应使用 tool:
- 接收参数后执行查询
- 产生副作用
- 执行工作流
- 校验结构化输入
- 在工作发生前先执行策略检查
示例:
- 检索工单系统
- 发送邮件
- 启动部署
- 从后端 API 中拉取一个带作用域的记录
Tool 输出模式
ZCP 同时支持面向 MCP 的结构化输出,以及面向原生运行时的优化路径。
以下场景适合使用内联结构化输出:
- 结果很小
- 客户端或模型需要立即消费
- 输出 schema 校验很重要
以下场景更适合使用面向 handle 或脱离上下文的存储:
- 结果较大
- 完整 payload 不应该反复进入 prompt 上下文
- 客户端之后还需要再次获取或引用该产物
这正是原生 ZCP 能比单纯 MCP 风格调用更省 token 的关键位置之一。
Execution Metadata
Tool 的 execution 元数据用于显式声明运行时行为。常见示例包括:
- 是否支持 task
- 是否需要 approval
- 调度提示
- 其他实现相关的运行时元数据
如果一个 tool 可以以 task 方式运行,就应该把这一点写清楚,而不是让客户端自己猜。
Tool 设计建议
推荐:
- 使用稳定命名,例如
domain.action - 使用收敛、清晰的 JSON schema
- 对副作用进行明确描述
- 当结构化内容重要时提供 output schema
- 一个 tool 只表示一个清晰的业务操作
避免:
- “什么都能做”的巨型 tool
- 类型松散的参数袋
- 把策略藏在描述性文本里
- 把静态参考数据强行通过 tool 调用暴露出去
Resources
Resource 是通过 URI 访问、可读取的服务端内容。
示例:
weather://citiesfile:///workspace/README.mddb://schemas/public/userstenant://acme/config
在 Python SDK 中,resource 通过 FastZCP.resource(...) 注册。
@app.resource(
"tenant://acme/config",
name="Tenant Config",
mime_type="application/json",
)
def tenant_config():
return {"region": "us-east-1", "features": ["alerts", "audit"]}什么时候应该使用 Resource
当主要动作是以下类型时,应使用 resource:
- 读取
- 查看
- 浏览
- 订阅更新
示例:
- 生成后的报告
- 源文件
- schema
- 租户配置
- 清单类数据
Resource 内容形态
运行时可以把不同 handler 输出投影成 MCP 兼容内容:
- 文本类输出会变成 text content
- JSON 类输出会变成 structured content 或序列化内容
- 二进制输出会变成 blob content
这使得同一个 resource 模型既能表达简单内容,也能表达更丰富的内容。
订阅
如果某个 resource 会变化,并且客户端关心这些变化,就应该通过订阅和 notifications/resources/updated 把这一点显式表达出来。订阅适用于“像数据一样变化的内容”,而不是那些本质上更应该建模成 task 的工作流。
Resource Templates
Resource template 用来声明一组参数化 URI,而不需要提前列出每个具体实例。
示例:
@app.resource_template(
"repo://{owner}/{name}/issues/{id}",
name="Repository Issue",
mime_type="application/json",
)
def repo_issue(uri: str):
return {"uri": uri}以下场景适合使用 template:
- URI 空间很大
- 实例命名规律明确
- 相比预先枚举,模式发现更重要
Template 对文件类、实体类数据尤其有用,因为客户端可以在发现模式后自行补全参数。
Prompts
Prompt 是由服务端拥有的 prompt 模板,返回的是 message,而不是可执行逻辑。它把 prompt 构建过程集中放在服务端,避免客户端重复拼装同样的结构。
在 Python SDK 中,prompt 通过 FastZCP.prompt(...) 注册。
from zcp import PromptArgument
@app.prompt(
name="incident.summary",
description="Build a handoff prompt for an on-call engineer.",
arguments=[
PromptArgument(name="incident_id", required=True),
PromptArgument(name="audience"),
],
)
def incident_summary_prompt(incident_id: str, audience: str | None = None):
return [
{"role": "system", "content": "You write concise incident summaries."},
{
"role": "user",
"content": f"Summarize incident {incident_id} for {audience or 'an engineer'}.",
},
]什么时候应该使用 Prompt
当满足以下条件时,应使用 prompt:
- prompt 构建应该由后端统一拥有
- 多个客户端都需要同一套模板
- 参数应该具备可发现性
- prompt 结构本身就是产品接口的一部分
示例:
- 报告总结模板
- 代码评审脚手架
- 事故交接 prompt
- 某个领域专用的分析模板
Prompt 不是 Tool
Prompt 不应该被用来偷偷塞入可执行逻辑或校验规则。
Tool 负责执行工作。 Resource 负责暴露内容。 Prompt 负责定义可复用的 prompt 脚手架。
如果一个输出必须被校验,或者必须被执行,那么 prompt 大概率就不应该是它的主要抽象。
如何选择正确的抽象
可以用下面这条规则:
- 需要一个操作时,选 tool
- 需要一个可读内容对象时,选 resource
- 需要一个可复用的 prompt 组装模板时,选 prompt
如果拿不准,就问客户端到底想做什么:
- “运行某个东西”通常是 tool
- “读取某个东西”通常是 resource
- “构造消息”通常是 prompt
ZCP 特有的优势
MCP 投影层看起来故意很熟悉,但其背后的运行时并不受限于同样的 prompt 可见开销。
ZCP 可以:
- 把规范化校验留在 prompt 上下文之外
- 在原生流程中避免重复发送大型 registry
- 通过 handle 或运行时状态保存大结果
- 在兼容边界上仍然保持官方 MCP 响应 shape
这也是为什么这些抽象在 ZCP 里不只是理论分类,而是实际影响运行成本的设计选择。