Headless gRPC Server in OpenClaude: Architecture, Purpose, and Integration Guide

The headless gRPC server in OpenClaude transforms the CLI-based agent into a network-accessible service, exposing bidirectional streaming capabilities through the AgentService.Chat method.

This server enables external programs to interact with OpenClaude's agentic capabilities without requiring the full interactive terminal interface. By implementing a gRPC service in src/grpc/server.ts, the codebase supports programmatic integration from any language that can consume gRPC, making it ideal for CI/CD pipelines, custom dashboards, and automated workflows.

Core Purpose: Why OpenClaude Uses a Headless gRPC Server

The headless gRPC server serves as a bridge between OpenClaude's interactive agent core and external systems. Traditional CLI-based agents lock users into terminal sessions. The gRPC server removes this constraint by:

  • Exposing agent functionality as a language-agnostic network service
  • Supporting bidirectional streaming for real-time, multi-turn conversations
  • Enabling tool-call mediation with explicit client approval workflows
  • Maintaining session persistence across disconnected interactions

According to the OpenClaude source code, this architecture allows embedding into environments where a full TTY interface is impractical or impossible.

Service Entry Point and Startup

The server initialization logic resides in GrpcServer.start (lines 97-108 of src/grpc/server.ts). This method creates a grpc.Server instance, binds to localhost:50051 by default (configurable via host/port parameters), and registers the AgentService definition loaded from the Protocol Buffer specification.


# Launch the headless gRPC service (default port 50051)

npm run dev:grpc

The CLI entry point at scripts/start-grpc.ts instantiates the GrpcServer class and invokes start(), providing a clean bootstrap mechanism for production deployments.

Bidirectional Streaming Architecture

The centerpiece of the server is the AgentService.Chat method (lines 111-220 of src/grpc/server.ts). handleChat implements a duplex stream that handles:

  • Client requests containing session_id, message, and model parameters
  • Server responses as incremental text chunks
  • Tool-start events signaling pending tool invocations
  • Action-required prompts requesting explicit user approval
  • Final results completing the interaction cycle

This streaming model mirrors the interactive CLI experience but decouples it from terminal I/O constraints.

Tool-Call Mediation and Permission Flow

Before executing any tool, the server enforces an explicit approval workflow through the canUseTool callback (lines 60-84 of src/grpc/server.ts):

  1. Server emits a tool_start event to the client
  2. Server issues an action_required prompt requesting approval
  3. Client response routes back via the pending-requests map
  4. Tool executes only after affirmative client confirmation

This security-critical mechanism prevents unauthorized autonomous actions when OpenClaude operates in server mode.

Session Persistence Across Streams

The server maintains conversation state through an in-memory session store (this.sessions map). Referenced around lines 41-69 and 60-70 of src/grpc/server.ts, this store:

  • Associates message history with unique session_id values
  • Enables multi-turn conversations across multiple stream connections
  • Provides continuity for long-running agent interactions

Note that sessions are volatile; server restarts clear all conversation history.

Graceful Interruption and Cleanup

Production-grade reliability requires robust cancellation support. The server implements:

  • Client-initiated cancellation via the cancel message type (lines 89-95)
  • Automatic cleanup on stream end events (lines 108-119)
  • Propagation to QueryEngine ensuring underlying operations terminate cleanly

These mechanisms prevent resource leaks and hanging processes during unexpected disconnections.

Integration Example: Programmatic Node.js Client

Consume the headless gRPC server from any supported language. This Node.js example demonstrates connection establishment and basic interaction:

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import path from 'path';

const PROTO_PATH = path.resolve(import.meta.dirname, '../src/proto/openclaude.proto');
const pkg = grpc.loadPackageDefinition(
  protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true })
) as any;
const client = new pkg.openclaude.v1.AgentService('localhost:50051', grpc.credentials.createInsecure());

const call = client.Chat();

call.on('data', (msg) => console.log('Server:', msg));
call.write({ request: { session_id: 'demo', message: 'Hello', model: 'gpt-4' } });

For interactive testing, use the bundled CLI client:


# Open a second terminal and execute the lightweight CLI client

npm run dev:grpc:cli

Key Source Files Reference

File Role
src/grpc/server.ts Core server implementation, streaming logic, tool-permission handling, session store
src/proto/openclaude.proto gRPC service and message definitions
scripts/start-grpc.ts CLI entry point for server instantiation
scripts/grpc-cli.ts Minimal client for manual testing
docs/grpc-server.md User-facing documentation for deployment

Summary

  • The headless gRPC server converts OpenClaude from a terminal-bound tool into a network service
  • AgentService.Chat provides bidirectional streaming for real-time agent interactions
  • Tool-call mediation enforces explicit client approval before any tool execution
  • Session persistence maintains conversation state across multiple connections
  • Graceful interruption ensures clean resource cleanup on cancellation or disconnection
  • Default binding to localhost:50051 with configurable host/port parameters

Frequently Asked Questions

What port does the OpenClaude gRPC server use by default?

The server binds to port 50051 on localhost by default. This is hardcoded in the GrpcServer.start implementation at lines 97-108 of src/grpc/server.ts, though both host and port are configurable through constructor parameters.

How does the gRPC server handle tool approvals differently from the CLI?

The CLI can prompt directly in the terminal. The gRPC server emits tool_start and action_required events through the stream, then waits for the client to respond via the pending-requests map before proceeding, as implemented in the canUseTool callback (lines 60-84).

Can multiple clients connect to the same session simultaneously?

The source code shows session state stored in this.sessions keyed by session_id, but the implementation does not include explicit concurrency controls for simultaneous access. For production use, assume single-client-per-session semantics unless you implement external coordination.

Is conversation history persisted to disk?

No. The session store is in-memory only (lines 41-69, 60-70 of src/grpc/server.ts). Server restarts, crashes, or deployments will lose all active conversation history. For persistence requirements, implement external storage through client-side logging or a custom wrapper service.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →