Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cloudflare/agents/llms.txt

Use this file to discover all available pages before exploring further.

The AgentClient class and agentFetch function provide a framework-agnostic way to connect to agents from any JavaScript runtime: browsers, Node.js, Deno, Bun, or edge functions.

Installation

npm install agents partysocket

AgentClient

The AgentClient class provides a WebSocket connection to an agent with state synchronization and RPC calls.

Basic Usage

import { AgentClient } from "agents/client";

const client = new AgentClient({
  agent: "ChatAgent",
  name: "room-123",
  host: "your-worker.your-subdomain.workers.dev",
  onStateUpdate: (state) => {
    console.log("New state:", state);
  }
});

// Call a method
const response = await client.call("sendMessage", ["Hello!"]);

// Clean up when done
client.close();

Constructor Options

agent
string
required
Name of the agent class to connect to. Automatically converted from camelCase to kebab-case for the URL.
host
string
required
Worker host for the WebSocket connection.
name
string
default:"default"
Name of the specific agent instance.
path
string
Custom path prefix for the connection URL.
query
Record<string, string>
Query parameters to send with the connection.
onStateUpdate
(state: State, source: 'server' | 'client') => void
Callback invoked when the agent’s state is updated.
onStateUpdateError
(error: string) => void
Callback invoked when a state update fails.
onIdentity
(name: string, agent: string) => void
Callback invoked when the server sends the agent’s identity on connect.
onIdentityChange
(oldName: string, newName: string, oldAgent: string, newAgent: string) => void
Callback invoked when identity changes on reconnect.

Properties

agent
string
The kebab-case agent name.
name
string
The agent instance name. Updated when identity is received from the server.
identified
boolean
Whether the client has received identity from the server. Becomes true after the first identity message is received. Resets to false on connection close.
ready
Promise<void>
Promise that resolves when identity has been received from the server. Useful for waiting before making calls that depend on knowing the instance. Resets on connection close so it can be awaited again after reconnect.

Methods

setState
(state: State) => void
Push state updates to the agent.
client.setState({ count: 42 });
call
<T>(method: string, args?: unknown[], options?: CallOptions | StreamOptions) => Promise<T>
Call a method on the agent.
const result = await client.call("getUser", ["user-123"]);
send
(data: string | ArrayBuffer | Blob) => void
Send raw WebSocket message.
client.send(JSON.stringify({ type: "ping" }));
close
(code?: number, reason?: string) => void
Close the WebSocket connection. Immediately rejects all pending RPC calls.
client.close();
reconnect
() => void
Force reconnection.
client.reconnect();
addEventListener
(event: string, listener: EventListener) => void
Add event listeners for WebSocket events (inherited from PartySocket).
client.addEventListener("open", () => console.log("Connected"));
client.addEventListener("close", () => console.log("Disconnected"));
client.addEventListener("error", (e) => console.error("Error:", e));
client.addEventListener("message", (e) => console.log("Message:", e.data));

Calling Methods

Basic Calls

// Call with arguments
const user = await client.call("getUser", ["user-123"]);

// Call with multiple arguments
const post = await client.call("createPost", [title, content, tags]);

// Call with no arguments
const stats = await client.call("getStats");

Call Options

The call method accepts an optional third parameter for configuring timeouts and streaming:
// With timeout
const result = await client.call("slowMethod", [data], {
  timeout: 10000 // 10 seconds
});

// With streaming
await client.call("generateText", [prompt], {
  stream: {
    onChunk: (chunk) => console.log("Chunk:", chunk),
    onDone: (final) => console.log("Done:", final),
    onError: (error) => console.error("Error:", error)
  }
});

// With both timeout and streaming
await client.call("generateText", [prompt], {
  timeout: 30000,
  stream: {
    onChunk: (chunk) => console.log("Chunk:", chunk),
    onDone: (final) => console.log("Done:", final),
    onError: (error) => console.error("Error:", error)
  }
});
For backward compatibility, the legacy format with streaming options directly in the third parameter is still supported: client.call(method, args, { onChunk, onDone, onError }).

Streaming Responses

Handle streaming responses with callbacks:
await client.call("generateText", [prompt], {
  stream: {
    onChunk: (chunk) => {
      process.stdout.write(chunk);
    },
    onDone: (finalResult) => {
      console.log("\nComplete!");
    },
    onError: (error) => {
      console.error("Stream error:", error);
    }
  }
});

State Management

Receiving State Updates

const client = new AgentClient({
  agent: "GameAgent",
  name: "game-123",
  host: "my-worker.workers.dev",
  onStateUpdate: (state, source) => {
    console.log(`State from ${source}:`, state);
    if (source === "server") {
      // Agent pushed state
      updateUI(state);
    } else {
      // We pushed state
      console.log("Our state was accepted");
    }
  }
});

Pushing State Updates

client.setState({ score: 100, level: 5 });
When you call setState(), the agent broadcasts the new state to all connected clients. Your onStateUpdate callback will fire with source: "client".

Connection Lifecycle

Waiting for Identity

Use the ready promise to wait for the agent to send its identity:
const client = new AgentClient({
  agent: "MyAgent",
  name: "instance-1",
  host: "my-worker.workers.dev"
});

await client.ready;
console.log(`Connected to ${client.agent}/${client.name}`);

Event Listeners

client.addEventListener("open", () => {
  console.log("Connection opened");
});

client.addEventListener("close", () => {
  console.log("Connection closed");
});

client.addEventListener("error", (error) => {
  console.error("Connection error:", error);
});

client.addEventListener("message", (event) => {
  console.log("Raw message:", event.data);
});

Manual Reconnection

client.reconnect();
The client automatically reconnects on connection loss using PartySocket’s reconnection logic.

Closing the Connection

// Close when done
client.close();

// Close with custom code and reason
client.close(1000, "Normal closure");
Calling close() immediately rejects all pending RPC calls. Any calls made after close() will be rejected when the WebSocket close event fires.

Type Safety

Pass your agent class and state type as type parameters:
import type { MyAgent, MyAgentState } from "./agents/my-agent";

const client = new AgentClient<MyAgentState>({
  agent: "MyAgent",
  name: "instance-1",
  host: "my-worker.workers.dev",
  onStateUpdate: (state) => {
    // state is typed as MyAgentState
    console.log(state.count);
  }
});

HTTP Requests

For one-off requests without maintaining a WebSocket connection, use agentFetch:

Basic Usage

import { agentFetch } from "agents/client";

// GET request
const response = await agentFetch({
  agent: "DataAgent",
  name: "instance-1",
  host: "my-worker.workers.dev"
});

const data = await response.json();

POST Request

const response = await agentFetch(
  {
    agent: "DataAgent",
    name: "instance-1",
    host: "my-worker.workers.dev"
  },
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ action: "process", data: "value" })
  }
);

When to Use agentFetch

Use agentFetch

  • One-time requests
  • Server-to-server calls
  • Simple REST-style API
  • No persistent connection needed

Use AgentClient

  • Real-time updates needed
  • Bidirectional communication
  • State synchronization
  • Multiple RPC calls

Examples

Simple Counter Client

import { AgentClient } from "agents/client";

const client = new AgentClient({
  agent: "CounterAgent",
  name: "shared-counter",
  host: "my-worker.workers.dev",
  onStateUpdate: (state) => {
    console.log("Count:", state.count);
  }
});

// Wait for connection
await client.ready;

// Increment counter
await client.call("increment");

// Clean up
client.close();

Streaming AI Client

import { AgentClient } from "agents/client";

const client = new AgentClient({
  agent: "AIAgent",
  name: "assistant",
  host: "my-worker.workers.dev"
});

await client.ready;

let fullResponse = "";

await client.call("generateResponse", ["Hello!"], {
  stream: {
    onChunk: (chunk) => {
      fullResponse += chunk;
      process.stdout.write(chunk);
    },
    onDone: () => {
      console.log("\n\nGeneration complete!");
      console.log("Full response:", fullResponse);
    },
    onError: (error) => {
      console.error("Error:", error);
    }
  }
});

client.close();

Node.js Server-to-Agent Communication

import { AgentClient } from "agents/client";
import express from "express";

const app = express();
app.use(express.json());

app.post("/process", async (req, res) => {
  const client = new AgentClient({
    agent: "ProcessorAgent",
    name: req.body.agentId,
    host: "my-worker.workers.dev"
  });

  try {
    await client.ready;
    const result = await client.call("process", [req.body.data], {
      timeout: 5000
    });
    res.json({ success: true, result });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  } finally {
    client.close();
  }
});

app.listen(3000);

Edge Function with agentFetch

import { agentFetch } from "agents/client";

export default async function handler(request: Request) {
  const { agentId } = await request.json();

  const response = await agentFetch(
    {
      agent: "DataAgent",
      name: agentId,
      host: "my-worker.workers.dev"
    },
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ action: "getData" })
    }
  );

  const data = await response.json();
  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" }
  });
}

Error Handling

RPC Errors

try {
  const result = await client.call("riskyMethod", [data]);
} catch (error) {
  console.error("RPC failed:", error.message);
}

Connection Errors

client.addEventListener("error", (error) => {
  console.error("WebSocket error:", error);
});

client.addEventListener("close", (event) => {
  console.log(`Connection closed: ${event.code} ${event.reason}`);
});

Streaming Errors

await client.call("streamingMethod", [data], {
  stream: {
    onChunk: (chunk) => handleChunk(chunk),
    onError: (error) => {
      console.error("Stream error:", error);
      // Handle stream-specific errors
    }
  }
});

Timeout Errors

try {
  const result = await client.call("slowMethod", [data], {
    timeout: 5000
  });
} catch (error) {
  if (error.message.includes("timed out")) {
    console.error("Request timed out after 5 seconds");
  }
}

Best Practices

In long-running processes, always call close() when you are done with the client to free up resources.
const client = new AgentClient({ agent: "MyAgent", host: "..." });
try {
  await client.call("doWork", [data]);
} finally {
  client.close();
}
Use the ready promise to ensure the agent identity is received before making RPC calls.
await client.ready;
const result = await client.call("method", [args]);
Use the timeout option for operations that may take longer than expected.
await client.call("longRunningTask", [data], {
  timeout: 60000 // 60 seconds
});
If you only need to make a single request, use agentFetch instead of creating a WebSocket connection.
const response = await agentFetch({
  agent: "MyAgent",
  name: "instance",
  host: "my-worker.workers.dev"
});

See Also

Client SDK Overview

Learn about the full client SDK capabilities

React Hooks

Use the useAgent hook in React applications

Build docs developers (and LLMs) love