Architecture

OpenClaw provider plugin that spawns Claude CLI subprocesses using Max plan OAuth instead of API keys.

Request flow

  1. OpenClaw gateway receives a user message
  2. Gateway calls GlueClaw’s createStreamFn with model ID and agent directory
  3. createClaudeCliStreamFn resolves the model (glueclaw-sonnet -> claude-sonnet-4-6)
  4. System prompt is scrubbed — 6 detection triggers replaced (see detection patterns)
  5. ANTHROPIC_API_KEY is deleted from the subprocess environment, forcing Max plan OAuth
  6. Claude CLI is spawned: claude --dangerously-skip-permissions -p --output-format stream-json --verbose
  7. If resuming a session: --resume <session_id> replaces --system-prompt
  8. NDJSON events stream back through readline: system (init), stream_event (deltas), assistant (fallback), result (final)
  9. Response tokens are unscrubbed — 3 renamed tokens translated back to gateway format
  10. Stream events emitted to OpenClaw: start -> text_delta (per chunk) -> done

Auth model

GlueClaw does not use API keys. In src/stream.ts, the subprocess environment is copied from process.env and both ANTHROPIC_API_KEY and ANTHROPIC_API_KEY_OLD are deleted. The Claude CLI then falls back to its local OAuth session from claude auth login, which uses the Max plan subscription.

The plugin registers with a synthetic auth key (glueclaw-local) so OpenClaw’s auth system is satisfied without real credentials.

Resiliency

  • Request timeout — each stream has a 120s timeout (REQUEST_TIMEOUT_MS). If the CLI hangs, the process is killed and an error event emitted.
  • SIGKILL fallback — after SIGTERM, a 5s grace period allows cleanup. If the process is still alive, SIGKILL is sent.
  • Atomic session writessessions.json is written to a .tmp file then renamed, preventing corruption on crash.
  • Session eviction — the session map is capped at 1000 entries. Oldest sessions are evicted on overflow.
  • Stderr capture — CLI stderr is collected and included in error events for diagnostics (auth failures, rate limits).
  • Concurrent safety — parallel streams with different session keys are independent. Same-key concurrent access uses last-writer-wins semantics without corruption.

Detection scrubbing

OpenClaw’s system prompt contains tokens that Anthropic’s API rejects. scrubPrompt() rewrites them before sending to the CLI, unscrubResponse() reverses them in responses.

Scrub (prompt -> CLI):

Pattern Replacement
personal assistant running inside OpenClaw ...inside GlueClaw
HEARTBEAT_OK GLUECLAW_ACK
reply_to_current reply_current
[[reply_to: [[reply:
openclaw.inbound_meta glueclaw.inbound_meta
generated by OpenClaw generated by GlueClaw

Unscrub (response -> gateway):

Pattern Replacement
GLUECLAW_ACK HEARTBEAT_OK
reply_current reply_to_current
[[reply: [[reply_to:

Session persistence

Sessions enable multi-turn conversation memory across separate requests.

  • Storage: ~/.glueclaw/sessions.json — a JSON object mapping session keys to Claude session IDs
  • Key format: glueclaw:{agentDir} (e.g., glueclaw:default)
  • Capture: Session ID is extracted from NDJSON system/init and result events
  • Resume: When a session key exists, --resume <session_id> is passed instead of --system-prompt
  • Working directory: CLI runs from ~/.glueclaw so Claude’s own session data persists

MCP bridge

Gives the Claude CLI subprocess access to OpenClaw gateway tools (message, sessions, memory, web search, etc.).

  1. install.sh patches OpenClaw’s server-*.js to expose __GLUECLAW_MCP_PORT and __GLUECLAW_MCP_TOKEN as env vars when the MCP loopback server starts
  2. getMcpLoopback() reads these env vars at runtime
  3. writeMcpConfig() creates a temporary mcp.json pointing to http://127.0.0.1:{port}/mcp with auth headers
  4. CLI is invoked with --strict-mcp-config --mcp-config {path}
  5. Temp config is cleaned up in a finally block

Installer

install.sh runs 7 idempotent steps:

Step What it does
1 npm install — project dependencies
2 Adds GLUECLAW_KEY=local to shell profile
3 Registers plugin with openclaw plugins install --link (fallback: manual config)
4 Configures 3 models, sets default to glueclaw-sonnet, allows gateway tools
5 Writes auth profile to ~/.openclaw/agents/main/agent/auth-profiles.json
6 Patches server-*.js to expose MCP loopback port and token via env vars
7 Starts gateway on port 18789, waits for readiness

Re-run after OpenClaw updates to re-apply the MCP patch.

Source files

File Purpose
index.ts Plugin entry: provider registration, model catalog, stream function wiring
src/stream.ts Core: subprocess spawn, NDJSON parsing, scrub/unscrub, sessions, MCP bridge
src/openclaw.d.ts Type declarations for the OpenClaw plugin SDK
openclaw.plugin.json Plugin manifest: provider ID, auth env vars, auth choices
install.sh Installer: deps, config, auth, MCP patch, gateway startup
vitest.config.ts Test runner config (forks pool, 30s timeout)

Test coverage

61 automated tests across 3 layers. See testing for details.

Layer Tests What it validates
Unit 37 scrubPrompt, unscrubResponse, buildUsage, buildMsg, getMcpLoopback, writeMcpConfig
Integration 17 Mock CLI (11 NDJSON scenarios), request timeout, stderr capture, 4 concurrency tests
E2E 7 Real Claude CLI with Max plan OAuth, session resume, OpenClaw plugin registration

This site uses Just the Docs, a documentation theme for Jekyll.