制品传递 (Artefact Hand-Off)
Issue: #349Crate:
dcc-mcp-artefactScheme:artefact://sha256/<hex>
流水线在工具之间以及工作流步骤之间传递文件:导入的场景、 质检报告、暂存的 .uasset、烘焙的模拟。内联传递原始字节会 膨胀 MCP 传输;传递绝对路径会跨机器中断并丢失 MIME / 大小 / 校验和元数据。
dcc-mcp-artefact 提供一个小型值类型(FileRef)加上一个 内容寻址存储后端,并将一个 artefact:// URI scheme 接入 MCP Resources 原语,使 MCP 客户端可以通过 URI resources/read 文件传递。
概念
FileRef— 对已存储文件的可序列化引用:uri— 规范形式,例如artefact://sha256/<hex>mime,size_bytes,digest(sha256:<hex>)producer_job_id(可选,用于工作流步骤输出)created_at(RFC-3339)metadata(工具定义的 JSON — 宽/高、帧号等)
ArtefactStore— trait,带有put/get/head/delete/list。存储是内容寻址的:提交相同的字节两次会返回相同的 URI。FilesystemArtefactStore— 默认持久化后端。每个制品存放在<root>/<sha256>.bin下,并带有一个携带FileRef的<sha256>.jsonsidecar。McpHttpServer将其存储锚定在<registry_dir>/dcc-mcp-artefacts(或当未配置注册表时的 OS 临时目录)。InMemoryArtefactStore— 非持久化后端,用于测试。
启用 Artefact Resources
from dcc_mcp_core import McpHttpConfig, McpHttpServer, ToolRegistry
cfg = McpHttpConfig(port=8765)
cfg.enable_artefact_resources = True # 默认关闭
server = McpHttpServer(ToolRegistry(), cfg)
handle = server.start()一旦启用:
resources/list将每个已存储的制品作为artefact://URI 包含在内。resources/read以正确的 MIME 类型返回 body,作为 base64 blob。- 未知 URI 的
resources/read返回 MCP 错误-32002。
禁用(默认)路径仍然识别该 scheme 并以 -32002 "not enabled" 错误响应,因此客户端可以区分 "scheme unknown" 和 "scheme recognized but backing store off"。
Python 辅助函数
from dcc_mcp_core import (
FileRef,
artefact_put_bytes,
artefact_put_file,
artefact_get_bytes,
artefact_list,
)
# 将磁盘上的文件放入并获取 FileRef。
ref = artefact_put_file("/tmp/render.png", mime="image/png")
print(ref.uri) # artefact://sha256/<hex>
print(ref.digest) # sha256:<hex>
print(ref.size_bytes) # 1024
# 往返一个字节缓冲区。
bref = artefact_put_bytes(b"hello", mime="text/plain")
assert artefact_get_bytes(bref.uri) == b"hello"
# 清单。
for entry in artefact_list():
print(entry.uri, entry.mime, entry.size_bytes)辅助函数目标是一个进程级默认存储 (<temp_dir>/dcc-mcp-artefacts)。在服务器进程内部, McpHttpServer 会自动将辅助函数指向它自己的存储。
Rust API
use dcc_mcp_artefact::{
ArtefactBody, ArtefactFilter, ArtefactStore,
FilesystemArtefactStore, InMemoryArtefactStore,
put_bytes, put_file,
};
// 持久化存储 — 真实服务器的默认选项。
let store = FilesystemArtefactStore::new_in("/var/cache/dcc/artefacts")?;
let fr = put_bytes(&store, b"payload".to_vec(), Some("text/plain".into()))?;
assert!(fr.uri.starts_with("artefact://sha256/"));
// 通过 URI 查找。
let body = store.get(&fr.uri)?.unwrap();
assert_eq!(body.into_bytes()?, b"payload");
// 按生产作业筛选列出制品。
let refs = store.list(ArtefactFilter {
producer_job_id: Some(job_id),
..Default::default()
})?;工作流集成
工作流运行器通过步骤输出传播 FileRef:
- 一个步骤发出一个
ToolResult,其context包含{"file_refs": [{"uri": "artefact://sha256/...", "mime": "image/png"}]}。 - 运行器将
FileRef存储在步骤记录上,并将其替换到下一步骤的 参数上下文中。 - 下游步骤通过
artefact_get_bytes(uri)获取字节 — 或者当从 运行器进程外部使用 MCP resources 原语时通过resources/read获取。
本 PR 落地了类型、存储和 resource 接线。运行器集成超出范围。
注意事项
- 重复内容 → 相同 URI。 不要依赖 URI 的唯一性进行逻辑排序 — 改用
producer_job_id和metadata。 put_file会复制。 源路径保持不动;存储拥有规范副本。- Sidecar 对元数据是权威的。 手动编辑 JSON sidecar 是支持的; 编辑
.bin文件则不支持(digest 会不匹配)。 - 尚无 GC。 存储永远不会自动删除。TTL / 引用计数 GC 在一个 未来的 issue 中追踪。
- 尚无远程后端。 S3 / SFTP / HTTP 指针已声明在路线图中但尚未 实现 — 一个未来的 issue。
参见
docs/api/resources.md— 承载artefact://的 更广泛的 Resources 原语。- Issue #348 — 消费
FileRef的工作流运行器。 - Issue #350 — Resources 原语(已合并)。