Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

プロトコルメッセージ

最終更新 Markdown で表示Agent セットアップ

WebSocket クライアントが Agent に接続すると、フレームワークは識別情報、状態、MCP サーバー一覧などの JSON テキストフレームを自動送信します。これらのプロトコルメッセージを扱えないクライアント向けに、接続ごとに抑制できます。

概要

新しい接続のたびに、Agent は次の 3 つのプロトコルメッセージを送ります。

メッセージ種別 内容
cf_agent_identity Agent の名前とクラス
cf_agent_state 現在のエージェント状態
cf_agent_mcp_servers 接続中の MCP サーバー一覧

状態と MCP のメッセージは、変化するたびにすべての接続へブロードキャストされます。

ほとんどの Web クライアントでは問題ありません。クライアント SDKuseAgent フックが、これらのメッセージを自動で消費します。一方、JSON テキストフレームを扱えないクライアントもあります。

  • バイナリ専用クライアント — MQTT デバイス、IoT センサー、独自のバイナリプロトコル
  • 軽量クライアント — 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";
	}
}

このフックは onConnect 中、メッセージ送信前に実行されます。false を返すと次のようになります。

  • 接続時に cf_agent_identitycf_agent_statecf_agent_mcp_servers は送られません
  • 以降、状態と MCP のブロードキャストからも除外されます
  • RPC 呼び出し、通常の onMessage 処理、this.broadcast() は通常どおり動作します

WebSocket サブプロトコルを使う

WebSocket サブプロトコルヘッダーを確認することもできます。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";
	}
}

プロトコル状態を確認する

接続でプロトコルメッセージが有効かどうかは、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),
		};
	}
}

抑制されるものと抑制されないもの

次の表は、接続でプロトコルメッセージを抑制したときに、何がまだ動作するかを示します。

操作 動作する?
接続時に cf_agent_identity を受信する いいえ
接続時とブロードキャストで cf_agent_state を受信する いいえ
接続時とブロードキャストで cf_agent_mcp_servers を受信する いいえ
通常の WebSocket メッセージの送受信 はい
@callable() RPC メソッドの呼び出し はい
this.broadcast() メッセージの受信 はい
バイナリデータの送信 はい
RPC 経由でのエージェント状態の変更 はい

readonly と組み合わせる

接続は、readonly かつプロトコル抑制の両方にできます。状態を観察するだけで変更すべきでないバイナリデバイスに向いています。

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 からは見えません。互いに干渉せず、ユーザー定義の接続状態とも干渉しません。

API リファレンス

shouldSendProtocolMessages

接続時にプロトコルメッセージを受け取るかを決める、オーバーライド可能なフックです。

パラメーター 説明
connection Connection 接続中のクライアント
ctx ConnectionContext アップグレードリクエストを含みます
戻り値 boolean false でプロトコルメッセージを抑制

既定: true を返します(すべての接続がプロトコルメッセージを受け取ります)。

このフックは接続時に一度だけ評価されます。結果は接続の WebSocket attachment に保存され、ハイバネーション を越えて残ります。

isConnectionProtocolEnabled

接続でプロトコルメッセージが現在有効かを確認します。

パラメーター 説明
connection Connection 確認する接続
戻り値 boolean プロトコルメッセージが有効なら true

いつでも呼べます。エージェントがハイバネーションから起きたあとも安全です。

仕組み

プロトコル状態は、接続の WebSocket attachment 内の内部フラグとして保存されます。readonly 接続 と同じ仕組みです。つまり次のとおりです。

  • ハイバネーションを越える — フラグはシリアライズされ、エージェント起床時に復元されます
  • クリーンアップ不要 — 接続が閉じると、接続状態は自動で破棄されます
  • オーバーヘッドなし — データベースのテーブルやクエリはなく、接続の組み込み attachment だけです
  • ユーザーコードから安全connection.stateconnection.setState() は、フラグを公開も上書きもしません

readonlysetConnectionReadonly() で動的に切り替えられますが、プロトコル状態は接続時に一度だけ設定され、あとから変更できません。接続のプロトコル状態を変えるには、クライアントが切断して再接続する必要があります。

関連リソース

役に立ちましたか?