Skip to content

contextablemark/clawg-ui

Repository files navigation

clawg-ui

Banner

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-ui

Or with the OpenClaw plugin CLI:

openclaw plugins install @contextableai/clawg-ui

Then 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:

  1. Authenticates the request using device pairing (see Authentication)
  2. Parses the AG-UI messages into an OpenClaw inbound context
  3. Routes to the appropriate agent via the gateway's standard routing
  4. Dispatches the message through the reply pipeline (same path as Telegram, Teams, etc.)
  5. 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 ABCD1234

3. 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_TOKEN

New (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 test

License

MIT

About

AG-UI Channel for OpenClaw

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •