gRPC、MCP 与 CloudEvents
本页讲 HTTP 之外的三个协议适配器:gRPC 把操作发布成一元方法,MCP 把操作发布成大模型可调用的 tool,CloudEvents 把 HTTP 上的事件接进消息子系统并把消息推给别的服务,适用于同一个应用要服务多种调用方的场景。各包的完整 API 见 Protocols.Grpc、Protocols.Mcp、Protocols.CloudEvents。
一份契约,多个载体
gRPC 与 MCP 暴露的是操作契约,与 HTTP 适配器读同一个操作登记表、同一份由 Kernel.Generators 生成的请求 / 响应 JSON Schema:HTTP 拿它生成 OpenAPI,gRPC 拿它推出 protobuf 线格式与 .proto,MCP 把它原样作为 tool 的 inputSchema / outputSchema。每次调用都以自己的调用上下文经过完整的内核管道,认证、按契约授权、校验、事务与 HTTP 上完全相同。操作写在哪些协议上由 [Operation] 的 Expose 决定,写法见操作契约;Stability.Internal 的操作不在任何协议上发布。
CloudEvents 不暴露操作:它是消息子系统信封(CloudEvents 1.0)的 HTTP 绑定,入站事件按类型映射到主题、交给 IMessageHandler,出站把信封 POST 到别的服务。
| 适配器 | 暴露条件 | 映射 | 端点 |
|---|---|---|---|
Protocols.Grpc | Expose.Grpc,且有 Schema | 服务 {Resource}、方法 {Verb} | MapXiHanGrpc()、MapXiHanProto()(GET /operations.proto) |
Protocols.Mcp | Expose.Mcp | tool {resource}_{verb} | MapXiHanMcp()(POST /mcp);命令行宿主的 mcp 命令(stdio) |
Protocols.CloudEvents | 事件类型能映射到主题 | 事件类型 → 主题 | MapXiHanCloudEvents()(POST /events);CloudEventsWebhookPublisher |
samples/SimpleApp 同时装配了四个适配器:orders.create 在 HTTP 上是 POST /orders,在 gRPC 上是 Orders/Create,在 MCP 上是 orders_create。
gRPC
启用
- 引用
XiHan.Framework.Protocols.Grpc,UseFeature<GrpcProtocolFeature>()。 - 给 gRPC 一个 HTTP/2 端点。不用 TLS 时 Kestrel 不能在同一个端口上同时协商 HTTP/1.1 与 HTTP/2,示例单开 5001 端口跑 h2c。
UseXiHan()之后调用MapXiHanGrpc(),需要给客户端发.proto时再调用MapXiHanProto()。
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://localhost:5000" },
"Grpc": { "Url": "http://localhost:5001", "Protocols": "Http2" }
}
}
}using XiHan.Framework.Protocols.Grpc;
using XiHan.Framework.Protocols.Grpc.Services;
// 接「HTTP 与 OpenAPI」页「启用」一节的 Program.cs:AddXiHan 里再加 .UseFeature<GrpcProtocolFeature>()
app.MapXiHanGrpc(); // /Orders/Create、/Orders/List
app.MapXiHanProto(); // GET /operations.proto服务端不需要编译 .proto,也不依赖 Google.Protobuf:方法经 Grpc.AspNetCore.Server 的 IServiceMethodProvider 动态绑定,消息以字节传递,适配器按 Schema 在 protobuf 与 JSON 之间转换。只支持一元方法,流式方法与 gRPC 反射服务未提供;客户端用 /operations.proto 生成桩代码,或交给 grpcurl:
curl -s localhost:5000/operations.proto -o operations.proto
grpcurl -plaintext -proto operations.proto -H "authorization: ApiKey dev-alice-key" localhost:5001 Orders/List方法名
服务名是资源段的 PascalCase(连字符分段,order-lines → OrderLines),方法名是动词的 PascalCase。配置节 XiHan:Protocols:Grpc:
| 键 | 类型 | 缺省 | 说明 |
|---|---|---|---|
Package | string | 空 | protobuf 包名,小写标识符以点分隔(如 acme.orders);写进 .proto 的 package,也是方法路径的前缀 /{package}.{Service}/{Method} |
Methods:<操作 ID> | string | — | Service/Method,显式覆盖推导的名字 |
服务名与方法名匹配 ^[A-Za-z_][A-Za-z0-9_]*$。以下情形在映射时失败,应用启动不起来:
| 情形 | 错误码 |
|---|---|
| 推导或配置的服务 / 方法名不合法 | PROTOCOLS_VALIDATION_007 |
| 两个操作得到同一个服务 / 方法名 | PROTOCOLS_CONFLICT_004 |
| 暴露给 gRPC 的操作没有 Schema,或 Schema 映射不成 protobuf(嵌套数组、映射里的映射) | PROTOCOLS_VALIDATION_006 |
| 同名消息在不同操作里形状不同 | PROTOCOLS_CONFLICT_003 |
由 JSON Schema 推出线格式
请求与响应各对应一个消息,消息名取 Schema 的 title(封闭泛型带实参名,如 PageResultOfUserResponse,ADR-0034),$defs 里的每个对象定义一个消息。字段号按 properties 的声明顺序从 1 起;.proto 里的字段名是属性名的 lower_snake_case,推导的 json_name 与属性名不同时显式标注。
| JSON Schema | protobuf |
|---|---|
string;contentEncoding: base64 | string;bytes |
boolean | bool |
integer + format: int32 / int64 | int32 / int64 |
integer 无格式 | string,十进制文本 |
number + format: float / double | float / double |
number 其他(decimal、无格式) | string,十进制文本 |
array | repeated T,元素只能是标量或消息 |
object + additionalProperties | map<string, T>;键总是 string(JSON 属性名的文本),Dictionary<int, T> 也一样,propertyNames 不影响映射 |
$ref 到对象定义 | 嵌套消息,允许递归 |
$ref 到字符串枚举 | string,不建 proto enum,线格式不变 |
可空(type 或 anyOf 含 null) | optional |
无约束({}) | string,JSON 文本原样;作数组元素或映射值时同样(JsonArray → repeated string,JsonObject → map<string, string>) |
解码时,线上缺席的布尔、数值、repeated 与 map 字段按 proto3 默认值补进 JSON(零值、[]、{}),契约 Schema 的 required 因此在 gRPC 上成立;字符串、字节与嵌套消息缺席即缺席。samples/SimpleApp 的 /operations.proto:
// Generated by XiHan Framework from operation contracts; the server derives it from the same schemas at runtime.
syntax = "proto3";
message ListOrdersRequest {
}
message OrderList {
repeated OrderResponse items = 1;
}
message OrderResponse {
string id = 1;
string name = 2;
int32 amount = 3;
string created_at = 4;
}
message PlaceOrderRequest {
string name = 1;
int32 amount = 2;
}
service Orders {
// orders.create
rpc Create (PlaceOrderRequest) returns (OrderResponse);
// orders.list
rpc List (ListOrdersRequest) returns (OrderList);
}上下文、状态与错误
gRPC 元数据就是 HTTP/2 头,上下文映射与 HTTP 相同:traceparent、baggage、authorization、accept-language、xihan-deadline;没有 xihan-deadline 时接受 gRPC 标准的 grpc-timeout。gRPC 方法带 PipelineBypassMetadata,不经 UseXiHan() 的外层调用:每次调用在处理器里以请求头建自己的上下文、经完整管道执行,调用来源记 grpc 与方法全名(如 /Orders/Create),远端地址与 User-Agent 沿用 HTTP/2 请求。
| 错误 KIND | gRPC 状态 |
|---|---|
VALIDATION | InvalidArgument |
NOTFOUND | NotFound |
CONFLICT | Aborted |
DENIED | PermissionDenied;SECURITY_DENIED_001 与 SECURITY_DENIED_003 为 Unauthenticated(ADR-0042) |
UNAVAILABLE | Unavailable |
TIMEOUT | DeadlineExceeded |
CANCELLED | Cancelled |
| 其他 | Internal |
grpc-message 是按 accept-language 翻译的文案;尾部元数据 xihan-error-code 是框架错误码,xihan-trace-id 是 trace-id,成功时也有。适配器自己的失败:请求不是合法 protobuf 或与 Schema 不符为 InvalidArgument + PROTOCOLS_VALIDATION_005;响应编码不成 protobuf 为 Internal + PROTOCOLS_UNEXPECTED_001;未登记的方法为 Unimplemented(gRPC 服务端默认);应用未就绪(启动中或正在停止)为 Unavailable + KERNEL_UNAVAILABLE_001,不进管道,但与其他失败一样带两个尾部元数据,文案按 accept-language 翻译。
MCP
启用
- 引用
XiHan.Framework.Protocols.Mcp,UseFeature<McpProtocolFeature>()。 UseXiHan()之后调用MapXiHanMcp(),pattern缺省/mcp。- 在消息目录里给要暴露的操作写
<操作 ID>.title与<操作 ID>.description,大模型据此选择 tool。
using XiHan.Framework.Protocols.Mcp;
using XiHan.Framework.Protocols.Mcp.Transports;
// 接「HTTP 与 OpenAPI」页「启用」一节的 Program.cs:AddXiHan 里再加 .UseFeature<McpProtocolFeature>()
app.MapXiHanMcp(); // POST /mcp:initialize、ping、tools/list、tools/call{
"orders.create.title": "Place an order",
"orders.create.description": "Place an order with a name and an amount.",
"orders.list.title": "List orders",
"orders.list.description": "List the orders of the current tenant in creation order."
}上面是 samples/SimpleApp/Localization/en-US.json 的内容,其余九种语言各一个文件。description 里不写授权要求,由协议适配器统一呈现。
tool 名与 tools/list
tool 名按 {resource}_{verb} 推导(orders.create → orders_create),显式覆盖写在 XiHan:Protocols:Mcp:Tools:<操作 ID>。名字匹配 ^[A-Za-z0-9_-]{1,64}$,不合法时以 PROTOCOLS_VALIDATION_004、两个操作同名时以 PROTOCOLS_CONFLICT_001 启动失败。tools/list 不分页,每个 tool 的字段:
| 字段 | 来源 |
|---|---|
name | tool 名 |
title / description | 消息目录 <操作 ID>.title / <操作 ID>.description(- 换 _),按调用方区域翻译;没有就不给 |
inputSchema | 请求 Schema;Schema 的 type 不是 object 或操作没有 Schema 时为 {"type":"object"} |
outputSchema | 响应 Schema,仅当其 type 是 object |
annotations | 按契约推导,见下表 |
| 契约 | readOnlyHint | destructiveHint | idempotentHint |
|---|---|---|---|
只读(get、list,或 ReadOnly = true 的非标准动词) | true | false | true |
create | false | false | false |
update | false | false | true |
delete | false | true | true |
| 其他动词 | false | true | false |
只读必须在 [Operation] 上显式声明(ADR-0041),没有声明的查询类自定义动词按 MCP 默认标成可能破坏,客户端会要求用户确认。
tools/call
arguments(缺省 {},必须是对象)就是操作的载荷。每个 tools/call 是一次独立调用:以承载请求的头部建新的调用上下文,在自己的服务作用域里经完整管道执行,工作单元与作用域服务不与外层 HTTP 请求共享;调用来源记 mcp 与 tool 名。成功时 content[0].text 是响应 JSON,响应是对象时同时给 structuredContent。操作失败(校验、未授权、冲突)不是协议错误,而是 isError: true 的结果,文本是 <错误码>: <文案>,文案按调用方区域翻译。
curl -s -X POST localhost:5000/mcp -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl -s -X POST localhost:5000/mcp -H "Content-Type: application/json" -H "Authorization: ApiKey dev-bob-key" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"orders_create","arguments":{"name":"widget","amount":3}}}'bob 没有 orders.write,第二个请求的答复:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [{ "type": "text", "text": "SECURITY_DENIED_004: Permission 'orders.write' is missing." }],
"isError": true
}
}传输与 JSON-RPC 错误
MCP 协议版本 2025-06-18,消息为 JSON-RPC 2.0,HTTP 上是 Streamable HTTP 传输:POST 一条消息一个答复(application/json);通知(没有 id,如 notifications/initialized)与客户端发来的响应不答复,回 202;不开 SSE 流、不建会话,GET 与 DELETE 为 405。请求带的 MCP-Protocol-Version 头与服务端版本不同时回 400,缺失按当前版本处理。
/mcp 本身是经过 UseXiHan() 的普通端点:请求凭据在外层认证,凭据无效时整个请求以 401 Problem Details 拒绝、带 WWW-Authenticate 质询,不进入 JSON-RPC;应用未就绪时 503。没有凭据的请求照常进入,按契约的授权在每个 tools/call 的管道里判定。
| 情形 | error.code | error.data.code | HTTP |
|---|---|---|---|
| 请求体不是 JSON | -32700 | PROTOCOLS_VALIDATION_001 | 400 |
不是 JSON-RPC 2.0 请求(非对象、jsonrpc 不是 2.0、缺 method、id 为 null、params 不是对象) | -32600 | PROTOCOLS_VALIDATION_001 | 400 |
MCP-Protocol-Version 不受支持 | -32600 | PROTOCOLS_VALIDATION_002 | 400 |
| 方法不受支持 | -32601 | PROTOCOLS_NOTFOUND_001 | 200 |
tools/call 缺 name | -32602 | PROTOCOLS_VALIDATION_001 | 200 |
| tool 未登记 | -32602 | PROTOCOLS_NOTFOUND_002 | 200 |
arguments 不是对象 | -32602 | PROTOCOLS_VALIDATION_003 | 200 |
stdio 传输
命令行宿主里同一个服务端经 stdio 提供:装配了 McpProtocolFeature 与 AddXiHanCommandLine() 后运行 mcp 命令,stdin 与 stdout 每行一条 JSON-RPC 消息,日志只写 stderr,MCP 客户端以子进程方式连接。stdin 的字节按 UTF-8 解码,与控制台代码页无关(Windows 上重定向的标准输入不按 936、437 等代码页解码,中文参数不会成乱码);答复里的非 ASCII 字符按 JSON 转义成 \uXXXX,只含 ASCII,按任何控制台输出代码页写出都是合法的 UTF-8。
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | dotnet run --project framework/samples/SimpleCli -- mcpmcp 命令接受与 invoke 相同的上下文选项,作用于整个会话里的每个 tools/call:凭据 --authorization '<scheme> <value>' 或环境变量 XIHAN_AUTHORIZATION(选项优先,MCP 规范要求 stdio 服务端从运行环境取凭据),--tenant、--correlation、--locale(tools/list 的标题与描述、失败文案按它翻译),--timeout <秒> 是每个 tools/call 的时限。每个 tools/call 经完整管道,凭据在这次调用的 Authenticate 阶段认证;stdio 没有外层认证,凭据无效时需要认证的 tool 得到 isError(SECURITY_DENIED_003),会话继续。选项不合法(凭据不是 <scheme> <value>、时限不是正数)时不进入会话,stderr 写一行用法错误,退出码 64。调用来源只有协议 mcp 与 tool 名。宿主的写法见命令行宿主。
选项
配置节 XiHan:Protocols:Mcp:
| 键 | 类型 | 缺省 | 说明 |
|---|---|---|---|
ServerName | string | 宿主的应用名 | initialize 答复的 serverInfo.name |
ServerVersion | string | 框架版本 | serverInfo.version |
Instructions | string | 空 | initialize 答复的 instructions,空时不写 |
Tools:<操作 ID> | string | — | 显式 tool 名 |
initialize 总是答复本服务端的协议版本与 capabilities.tools.listChanged = false,由客户端决定是否继续;ping 答复 {}。
CloudEvents
入站
- 引用
XiHan.Framework.Protocols.CloudEvents,UseFeature<CloudEventsProtocolFeature>()。它依赖messaging.queue@1,Profiles.Standard的内存消息实现即可满足。 - 用
AddMessageHandler<THandler>(topic)订阅主题,写法见消息与事件。 UseXiHan()之后调用MapXiHanCloudEvents(),pattern缺省/events。
using XiHan.Framework.Protocols.CloudEvents;
using XiHan.Framework.Protocols.CloudEvents.Inbound;
// 接「HTTP 与 OpenAPI」页「启用」一节的 Program.cs:AddXiHan 里再加 .UseFeature<CloudEventsProtocolFeature>()
app.MapXiHanCloudEvents(); // POST /events入站端点经过 UseXiHan(),请求凭据在外层认证。框架不给它加任何要求,权限要由宿主自己加:
using XiHan.Framework.Hosting.AspNetCore.Endpoints;
// 权限码由某个特性用 FeatureContext.AddPermission 登记
app.MapXiHanCloudEvents().RequireXiHanPermission("events.deliver");没有装配 security.authorization@1 时,这个要求只进入上下文、不被判定,端点等于敞开。
{
"XiHan": {
"Protocols": {
"CloudEvents": {
"Inbound": "Dispatch",
"Topics": { "orders.placed": "orders" }
}
}
}
}三种内容模式按请求识别:
| 模式 | 识别 | 事件 |
|---|---|---|
| structured | Content-Type: application/cloudevents+json | 请求体是一个结构化 JSON 事件 |
| batch | Content-Type: application/cloudevents-batch+json | 请求体是结构化事件的数组,按顺序处理 |
| binary | 其他内容类型且有 ce-specversion 头 | ce-id、ce-source、ce-type 必填,ce-time(RFC 3339)、ce-subject 可选,其余 ce-* 头是扩展属性,头值按百分号解码;Content-Type 是数据内容类型,请求体是数据 |
事件类型先查 Topics:<事件类型>,没有映射时类型本身须满足主题文法(orders.placed 可以直接当主题)。扩展属性 xihantenant、xihanprincipal、xihancorrelation、xihanlocale 与 traceparent 在派发时进入调用上下文,成为租户、主体、关联 ID、区域与追踪。
扩展属性里的租户与主体是事件发送方给的,派发时不和请求的身份核对,能调这个端点的人就能选租户、写审计主体:与 HTTP 的 baggage 头不同,这是这个协议的设计,框架不收紧(ADR-0121)。权限也不按租户收窄,只授给平台的服务账号或服务角色,不要授给租户里的角色。
Inbound 决定事件的去向:
Dispatch(缺省):在请求内经MessageDispatcher派发给主题的处理器,以新的作用域与上下文经完整管道执行,处理器的结果就是响应:成功 202、无正文,失败按错误 KIND 写 Problem Details,发送方据此重试。Publish:经IMessagePublisher发布到消息代理,成功即 202;至少一次投递、重试与死信由代理负责(含发件箱)。
batch 先把每个事件的类型解析成主题,任一事件没有映射时整批 400、一个都不处理;派发模式逐个处理,第一个失败即停止并作为响应;发布模式按主题分组各一次 PublishManyAsync,第一个失败的结果作为响应。已处理的事件不回滚,发送方重试整批时,处理器按 id + source 去重。
curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:5000/events \
-H "Authorization: ApiKey dev-relay-key" \
-H "ce-specversion: 1.0" -H "ce-id: evt-1" -H "ce-source: /external" -H "ce-type: orders.placed" \
-H "ce-xihantenant: acme" -H "Content-Type: application/json" -d '{"id":"evt-1"}'samples/SimpleApp 把 orders.placed 映射到主题 orders,事件交给 OrderPlacedHandler,返回 202。端点要求 events.deliver,只有服务账号 relay(dev-relay-key,不属于任何租户)持有:匿名 401,alice、bob 403。
| 情形 | 状态码 | 错误码 |
|---|---|---|
不是 CloudEvent(内容类型既不是 structured / batch,也没有 ce-specversion) | 400 | PROTOCOLS_VALIDATION_008 |
| 信封不合法(规范版本、必填属性、时间、扩展名) | 400 | MESSAGING_VALIDATION_002 |
| 事件类型没有映射,类型本身也不是合法主题 | 400 | PROTOCOLS_VALIDATION_009 |
| 派发模式下主题没有处理器 | 404 | PROTOCOLS_NOTFOUND_003 |
| 处理器或发布失败 | 按错误 KIND | 处理器或代理返回的错误码 |
出站 Webhook
CloudEventsWebhookPublisher 实现 IMessagePublisher,把信封 POST 到 Webhooks:<主题> 配置的地址。它只按具体类型登记,不替换消息代理的发布者,要推送的地方显式注入它。信封按原样发出,不从当前调用补齐扩展(内部发布者才补齐):这是定下的规则(ADR-0074),xihantenant、xihanprincipal、xihancorrelation、xihanlocale 与 traceparent 都不由框架加上。接收方是第三方,租户、主体、关联是本系统的内部标识,推出去会暴露内部结构与身份,并让第三方依赖本该可变的内部 ID;追踪链要不要跨出组织边界也由调用方决定。要带给接收方的扩展显式写进信封。HTTP 层同理(ADR-0083):启用了追踪时 .NET 的 HttpClient 本会按当前 Activity 在请求头里写 W3C 的 traceparent、tracestate 与 baggage(与信封扩展无关),Webhook 的命名客户端 xihan.protocols.cloudevents 缺省不写;确实要把追踪链接到接收方,设 XiHan:Protocols:CloudEvents:PropagateTraceContext 为 true。它只作用于 Webhook 的客户端,不改全局传播,出站请求的客户端跨度照常记录。要调这个客户端的连接参数,用 AddHttpClient("xihan.protocols.cloudevents").UseSocketsHttpHandler((handler, _) => …) 配置同一个处理器,缺省保留;用 ConfigurePrimaryHttpMessageHandler 整个换掉主处理器时,是否传播由换上的处理器决定。下例把 orders 主题上的每条消息转发给外部服务,Webhook 失败时处理器返回失败,消息按消息子系统的规则重试,用尽后进死信:
using XiHan.Framework.Messaging.Abstractions.Handlers;
using XiHan.Framework.Primitives;
using XiHan.Framework.Protocols.CloudEvents.Outbound;
public sealed class OrderWebhookForwarder(CloudEventsWebhookPublisher webhooks) : IMessageHandler
{
public ValueTask<Result> HandleAsync(MessageDelivery delivery, InvocationContext context, CancellationToken cancellationToken)
=> webhooks.PublishAsync(delivery.Topic, delivery.Envelope, cancellationToken);
}using XiHan.Framework.Messaging.Abstractions;
using XiHan.Framework.Protocols.CloudEvents.Outbound;
builder.Services.AddMessageHandler<OrderWebhookForwarder>("orders");
// 命名 HttpClient:超时与重试按 IHttpClientFactory 的方式配置
builder.Services.AddHttpClient(CloudEventsWebhookPublisher.HttpClientName, client => client.Timeout = TimeSpan.FromSeconds(10));{
"XiHan": {
"Protocols": {
"CloudEvents": {
"Outbound": "Binary",
"Webhooks": { "orders": "https://hooks.example.com/orders" }
}
}
}
}Outbound 为 Binary(缺省,ce-* 头加数据体,头值按规范百分号编码)或 Structured(application/cloudevents+json 请求体),不接受 Batch。PublishManyAsync 一条时沿用这个模式,多条时用 batch 内容模式一次 POST,接收方须支持 application/cloudevents-batch+json,一个响应对整批生效。Webhook 地址必须是绝对的 http / https 地址,Inbound、Outbound 的值不区分大小写,不合法时读取配置就抛出 ArgumentException。
| 情形 | 错误码 |
|---|---|
| 主题没有配置 Webhook | PROTOCOLS_NOTFOUND_004 |
Webhook 返回非 2xx(details.status 是状态码) | PROTOCOLS_UNAVAILABLE_001 |
| 连接失败 | PROTOCOLS_UNAVAILABLE_002,reason 只写异常类型的全名,完整异常进日志(ADR-0115) |
| 地址被出站地址策略拒绝 | PROTOCOLS_UNAVAILABLE_002,reason 是 OutboundAddressDeniedException 的类型全名,不发起连接 |
Webhook 的接收方是第三方,它的 3xx 能把请求引到内网,所以这个命名客户端的连接与重定向都过出站地址策略。接收方在内网时,把它写进 XiHan:Security:Outbound:AllowedHosts。
发布者自己不重试,重试由调用方决定:像上例那样放在消息处理器里,或经发件箱投递,得到至少一次语义。
相关
- Protocols.Grpc 包、Protocols.Mcp 包、Protocols.CloudEvents 包
- 操作契约、HTTP 与 OpenAPI、宿主与 Web 应用
- 命令行宿主:
mcp命令 - 消息与事件:主题、处理器、发件箱与 CloudEvents 信封
- 规范:
spec/protocol/grpc.md《gRPC 载体》、spec/protocol/mcp.md《MCP 载体》、spec/protocol/cloudevents.md《CloudEvents 载体》、RFC-0011《操作契约与 XGI》、RFC-0009《消息、CloudEvents 信封与发件箱》
