跳转到内容
搜索文档

协议消息

最后更新 查看 MarkdownAgent 设置

WebSocket 客户端连接到 Agent 时,框架会自动发送若干 JSON 文本帧 — 身份、状态与 MCP server 列表。对无法处理这些消息的客户端,可按连接 suppress 协议消息。

概览

每次新连接时 Agent 发送三种协议消息:

消息类型 内容
cf_agent_identity Agent 名称与 class
cf_agent_state 当前 agent 状态
cf_agent_mcp_servers 已连接 MCP 服务器列表

状态与 MCP 消息在变更时也会广播到所有连接。

对大多数 Web 客户端这没问题 — 客户端 SDKuseAgent hook 自动消费这些消息。但部分客户端无法处理 JSON 文本帧:

  • 仅 binary 客户端 — MQTT 设备、IoT 传感器、自定义 binary 协议
  • 轻量客户端 — WebSocket 栈最小的嵌入式系统
  • 非浏览器客户端 — 经 WebSocket 连接的硬件设备

对这些连接可抑制协议消息,同时保持 RPC、常规消息、this.broadcast() 等正常工作。

抑制协议消息

重写 shouldSendProtocolMessages 控制哪些连接接收协议消息。返回 false 即抑制。

import { Agent } from "agents";

export class IoTAgent extends Agent {
	shouldSendProtocolMessages(connection, ctx) {
		const url = new URL(ctx.request.url);
		return url.searchParams.get("protocol") !== "false";
	}
}
import { Agent, type Connection, type ConnectionContext } from "agents";

export class IoTAgent extends Agent<Env, State> {
	shouldSendProtocolMessages(
		connection: Connection,
		ctx: ConnectionContext,
	): boolean {
		const url = new URL(ctx.request.url);
		return url.searchParams.get("protocol") !== "false";
	}
}

此 hook 在 onConnect 期间、发送任何消息前运行。返回 false 时:

  • 连接时不发送 cf_agent_identitycf_agent_statecf_agent_mcp_servers
  • 该连接被排除在后续状态与 MCP 广播之外
  • RPC、onMessagethis.broadcast() 仍正常

使用 WebSocket subprotocol

也可检查 WebSocket subprotocol 头,这是经 WebSocket 协商协议的标准方式:

export class MqttAgent extends Agent {
	shouldSendProtocolMessages(connection, ctx) {
		// MQTT-over-WebSocket clients negotiate via subprotocol
		const subprotocol = ctx.request.headers.get("Sec-WebSocket-Protocol");
		return subprotocol !== "mqtt";
	}
}
export class MqttAgent extends Agent<Env, State> {
	shouldSendProtocolMessages(
		connection: Connection,
		ctx: ConnectionContext,
	): boolean {
		// MQTT-over-WebSocket clients negotiate via subprotocol
		const subprotocol = ctx.request.headers.get("Sec-WebSocket-Protocol");
		return subprotocol !== "mqtt";
	}
}

检查 protocol 状态

isConnectionProtocolEnabled 检查连接是否启用协议消息:

export class MyAgent extends Agent {
	@callable()
	async getConnectionInfo() {
		const { connection } = getCurrentAgent();
		if (!connection) return null;

		return {
			protocolEnabled: this.isConnectionProtocolEnabled(connection),
			readonly: this.isConnectionReadonly(connection),
		};
	}
}
export class MyAgent extends Agent<Env, State> {
	@callable()
	async getConnectionInfo() {
		const { connection } = getCurrentAgent();
		if (!connection) return null;

		return {
			protocolEnabled: this.isConnectionProtocolEnabled(connection),
			readonly: this.isConnectionReadonly(connection),
		};
	}
}

会 suppress 与不会 suppress 的内容

下表显示对某连接抑制协议消息后仍可用的能力:

操作 可用?
连接时接收 cf_agent_identity
连接时与广播接收 cf_agent_state
连接时与广播接收 cf_agent_mcp_servers
收发常规 WebSocket 消息
调用 @callable() RPC 方法
接收 this.broadcast() 消息
发送二进制数据
经 RPC 变更 agent 状态

与只读组合

连接可同时 readonly 与 suppress protocol。适用于应观察但不修改 state 的 binary 设备:

export class SensorHub extends Agent {
	shouldSendProtocolMessages(connection, ctx) {
		const url = new URL(ctx.request.url);
		// Binary sensors don't handle JSON protocol frames
		return url.searchParams.get("type") !== "sensor";
	}

	shouldConnectionBeReadonly(connection, ctx) {
		const url = new URL(ctx.request.url);
		// Sensors can only report data via RPC, not modify shared state
		return url.searchParams.get("type") === "sensor";
	}

	@callable()
	async reportReading(sensorId, value) {
		// This RPC still works for readonly+no-protocol connections
		// because it writes to SQL, not agent state
		this
			.sql`INSERT INTO readings (sensor_id, value, ts) VALUES (${sensorId}, ${value}, ${Date.now()})`;
	}
}
export class SensorHub extends Agent<Env, SensorState> {
	shouldSendProtocolMessages(
		connection: Connection,
		ctx: ConnectionContext,
	): boolean {
		const url = new URL(ctx.request.url);
		// Binary sensors don't handle JSON protocol frames
		return url.searchParams.get("type") !== "sensor";
	}

	shouldConnectionBeReadonly(
		connection: Connection,
		ctx: ConnectionContext,
	): boolean {
		const url = new URL(ctx.request.url);
		// Sensors can only report data via RPC, not modify shared state
		return url.searchParams.get("type") === "sensor";
	}

	@callable()
	async reportReading(sensorId: string, value: number) {
		// This RPC still works for readonly+no-protocol connections
		// because it writes to SQL, not agent state
		this
			.sql`INSERT INTO readings (sensor_id, value, ts) VALUES (${sensorId}, ${value}, ${Date.now()})`;
	}
}

两标志存储在连接的 WebSocket attachment 中,对 connection.state 隐藏 — 互不干扰,也不干扰用户定义的 connection state。

API 参考

shouldSendProtocolMessages

可 override 的 hook,决定连接建立时是否接收协议消息。

参数 类型 描述
connection Connection 正在连接的客户端
ctx ConnectionContext 含 upgrade request
返回值 boolean false 即 suppress 协议消息

默认:返回 true(所有连接接收协议消息)。

此钩子在连接时评估一次。结果持久化在 WebSocket 附件中,经受 休眠

isConnectionProtocolEnabled

检查连接当前是否启用协议消息。

参数 类型 描述
connection Connection 要检查的连接
返回值 boolean true 表示启用协议消息

随时可安全调用,包括 agent 从休眠唤醒后。

工作原理

协议状态作为内部标志存在连接的 WebSocket 附件中 — 与 只读连接 相同机制。因此:

  • 经受休眠 — 标志序列化并在唤醒时恢复
  • 无需清理 — 连接关闭时自动丢弃连接状态
  • 零开销 — 无数据库表或查询,仅用连接内置附件
  • 用户代码安全connection.stateconnection.setState() 不会暴露或覆盖标志

与可用 setConnectionReadonly() 动态切换的 只读 不同,协议状态在连接时设定一次,之后不可改。要变更协议状态,客户端须断开并重连。

相关资源

这篇文档对您有帮助吗?