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, andmodelparameters - 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):
- Server emits a
tool_startevent to the client - Server issues an
action_requiredprompt requesting approval - Client response routes back via the pending-requests map
- 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_idvalues - 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
cancelmessage type (lines 89-95) - Automatic cleanup on stream
endevents (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.Chatprovides 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:50051with 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →