@contextableai/clawg-ui
v0.2.6
Published
AG-UI protocol channel plugin for OpenClaw — connect CopilotKit and AG-UI clients to your OpenClaw gateway
Maintainers
Readme
clawg-ui

An OpenClaw channel plugin that exposes the gateway as an AG-UI protocol-compatible HTTP endpoint. AG-UI clients such as CopilotKit UIs and @ag-ui/client HttpAgent instances can connect to OpenClaw and receive streamed responses.
Installation
npm install @contextableai/clawg-uiOr with the OpenClaw plugin CLI:
openclaw plugins install @contextableai/clawg-uiThen restart the gateway. The plugin auto-registers the /v1/clawg-ui endpoint and the clawg-ui channel.
How it works
The plugin registers as an OpenClaw channel and adds an HTTP route at /v1/clawg-ui. When an AG-UI client POSTs a RunAgentInput payload, the plugin:
- Authenticates the request using device pairing (see Authentication)
- Parses the AG-UI messages into an OpenClaw inbound context
- Routes to the appropriate agent via the gateway's standard routing
- Dispatches the message through the reply pipeline (same path as Telegram, Teams, etc.)
- Streams the agent's response back as AG-UI SSE events
AG-UI Client OpenClaw Gateway
| |
| POST /v1/clawg-ui (RunAgentInput) |
|------------------------------------->|
| | Auth (device token)
| | Route to agent
| | Dispatch inbound message
| |
| SSE: RUN_STARTED |
|<-------------------------------------|
| SSE: TEXT_MESSAGE_START |
|<-------------------------------------|
| SSE: TEXT_MESSAGE_CONTENT (delta) |
|<-------------------------------------| (streamed chunks)
| SSE: TEXT_MESSAGE_CONTENT (delta) |
|<-------------------------------------|
| SSE: TOOL_CALL_START |
|<-------------------------------------| (if agent uses tools)
| SSE: TOOL_CALL_END |
|<-------------------------------------|
| SSE: TEXT_MESSAGE_END |
|<-------------------------------------|
| SSE: RUN_FINISHED |
|<-------------------------------------|Usage
Prerequisites
- OpenClaw gateway running (
openclaw gateway run) - A paired device token (see Authentication)
curl
# Using your device token (obtained through pairing)
curl -N -X POST http://localhost:18789/v1/clawg-ui \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Authorization: Bearer $CLAWG_UI_DEVICE_TOKEN" \
-d '{
"threadId": "thread-1",
"runId": "run-1",
"messages": [
{"role": "user", "content": "What is the weather in San Francisco?"}
]
}'@ag-ui/client HttpAgent
import { HttpAgent } from "@ag-ui/client";
// Device token obtained through the pairing flow
const deviceToken = process.env.CLAWG_UI_DEVICE_TOKEN;
const agent = new HttpAgent({
url: "http://localhost:18789/v1/clawg-ui",
headers: {
Authorization: `Bearer ${deviceToken}`,
},
});
const stream = agent.run({
threadId: "thread-1",
runId: "run-1",
messages: [
{ role: "user", content: "Hello from CLAWG-UI" },
],
});
for await (const event of stream) {
console.log(event.type, event);
}CopilotKit
import { CopilotKit } from "@copilotkit/react-core";
// Device token obtained through the pairing flow
const deviceToken = process.env.CLAWG_UI_DEVICE_TOKEN;
function App() {
return (
<CopilotKit
runtimeUrl="http://localhost:18789/v1/clawg-ui"
headers={{
Authorization: `Bearer ${deviceToken}`,
}}
>
{/* your app */}
</CopilotKit>
);
}Request format
The endpoint accepts a POST with a JSON body matching the AG-UI RunAgentInput schema:
| Field | Type | Required | Description |
|---|---|---|---|
| threadId | string | no | Conversation thread ID. Auto-generated if omitted. |
| runId | string | no | Unique run ID. Auto-generated if omitted. |
| messages | Message[] | yes | Array of messages. At least one user message required. |
| tools | Tool[] | no | Client-side tool definitions (reserved for future use). |
| state | object | no | Client state (reserved for future use). |
Message format
{
"role": "user",
"content": "Hello"
}Supported roles: user, assistant, system, tool.
Response format
The response is an SSE stream. Each event is a data: line containing a JSON object with a type field from the AG-UI EventType enum:
| Event | When |
|---|---|
| RUN_STARTED | Immediately after validation |
| TEXT_MESSAGE_START | First assistant text chunk |
| TEXT_MESSAGE_CONTENT | Each streamed text delta |
| TEXT_MESSAGE_END | After last text chunk |
| TOOL_CALL_START | Agent invokes a tool |
| TOOL_CALL_END | Tool execution complete |
| RUN_FINISHED | Agent run complete |
| RUN_ERROR | On failure |
Authentication
clawg-ui uses device pairing to authenticate clients. This provides secure, per-device access control without exposing the gateway's master token.
Device Pairing Flow
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Gateway Owner │ │ OpenClaw Server │ │ AG-UI Client │
└────────┬────────┘ └────────┬─────────┘ └────────┬────────┘
│ │ │
│ │ 1. POST (no auth) │
│ │<────────────────────────│
│ │ │
│ │ 2. Return device token │
│ │ + pairing code │
│ │────────────────────────>│
│ │ 403 pairing_pending │
│ │ { pairingCode, token }
│ │ │
│ 3. Share pairing code (out of band) │
│<─────────────────────────────────────────────────│
│ │ │
4. Approve device │ │
│ openclaw pairing approve clawg-ui ABCD1234 │
│───────────────────────>│ │
│ │ │
│ │ 5. POST with device token
│ │<────────────────────────│
│ │ Authorization: Bearer <token>
│ │ │
│ │ 6. Success - SSE stream│
│ │────────────────────────>│
│ │ │Step-by-Step Setup
1. Client initiates pairing
The client sends a POST request without any authorization header:
curl -X POST http://localhost:18789/v1/clawg-ui \
-H "Content-Type: application/json" \
-d '{}'Response (403):
{
"error": {
"type": "pairing_pending",
"message": "Device pending approval",
"pairing": {
"pairingCode": "ABCD1234",
"token": "MmRlOTA0ODIt...b71d",
"instructions": "Save this token for use as a Bearer token and ask the owner to approve: openclaw pairing approve clawg-ui ABCD1234"
}
}
}The client must save the token for future requests.
2. Approve the device (gateway owner)
The client shares the pairingCode with the gateway owner, who approves it:
# List pending pairing requests
openclaw pairing list clawg-ui
# Approve the device
openclaw pairing approve clawg-ui ABCD12343. Client uses Bearer token
Once approved, the client uses their Bearer token for all requests:
curl -N -X POST http://localhost:18789/v1/clawg-ui \
-H "Authorization: Bearer MmRlOTA0ODIt...b71d" \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello"}]}'CLI Commands
| Command | Description |
|---------|-------------|
| openclaw clawg-ui devices | List approved devices |
| openclaw pairing list clawg-ui | List pending pairing requests awaiting approval |
| openclaw pairing approve clawg-ui <code> | Approve a device by its pairing code |
Error Responses
| Status | Type | Meaning |
|--------|------|---------|
| 401 | unauthorized | Invalid device token |
| 403 | pairing_pending | No auth (initiates pairing) or valid token but device not yet approved |
Deprecated: Direct Bearer Token
Deprecated: Previous versions (0.1.x) allowed using the gateway's master token (
OPENCLAW_GATEWAY_TOKEN) directly. This approach is no longer supported. All clients must now use device pairing.Old (deprecated):
Authorization: Bearer $OPENCLAW_GATEWAY_TOKENNew (required):
POST without Authorization header # Initiates pairing Authorization: Bearer <device-token> # After approval
Agent routing
The plugin uses OpenClaw's standard agent routing. By default, messages route to the main agent. To target a specific agent, set the X-OpenClaw-Agent-Id header:
curl -N -X POST http://localhost:18789/v1/clawg-ui \
-H "Authorization: Bearer $CLAWG_UI_DEVICE_TOKEN" \
-H "X-OpenClaw-Agent-Id: my-agent" \
-d '{"messages":[{"role":"user","content":"Hello"}]}'Error responses
Non-streaming errors return JSON:
| Status | Type | Meaning |
|---|---|---|
| 400 | invalid_request_error | Invalid request (missing messages, bad JSON) |
| 401 | unauthorized | Invalid device token |
| 403 | pairing_pending | No auth header (initiates pairing) or valid token but device not yet approved |
| 405 | — | Method not allowed (only POST accepted) |
Streaming errors emit a RUN_ERROR event and close the connection.
Development
git clone https://github.com/contextablemark/clawg-ui
cd clawg-ui
npm install
npm testLicense
MIT
