Claude Code Integration
July 1, 2025 · View on GitHub
Overview
The Claude Code Integration is a sophisticated service that bridges the MCP server with Anthropic's Claude CLI tool, enabling AI-powered coding assistance. It manages sessions, executes queries, streams responses, and parses structured events from Claude's output.
Architecture
MCP Server (Docker) Host Machine
┌─────────────────┐ ┌──────────────┐
│ Claude Code │ TCP │ Host Bridge │
│ Service │ ──────> │ Daemon │
├─────────────────┤ :9876 ├──────────────┤
│ Session Manager │ │ Claude CLI │
│ Query Executor │ │ Process │
│ Event Parser │ └──────────────┘
└─────────────────┘
Core Components
1. Claude Code Service (claude-code-service.ts)
The main service that orchestrates all Claude interactions:
class ClaudeCodeService extends EventEmitter {
// Singleton instance
static getInstance(): ClaudeCodeService
// Session management
createSession(options: ClaudeCodeOptions): Promise<string>
findSession(sessionId: string): ClaudeCodeSession | undefined
// Query execution
querySync(sessionId: string, prompt: string): Promise<string>
// Task integration
setTaskId(sessionId: string, taskId: string): void
setMcpSessionId(sessionId: string, mcpSessionId: string): void
}
Key Features:
- Singleton Pattern: Ensures single instance across the application
- Event Emission: Broadcasts session and task events
- Dual Execution Modes: SDK mode (with API key) or Host Proxy mode
- Session Isolation: Each query runs in its own session context
2. Session Manager (session-manager.ts)
Manages Claude Code sessions with lifecycle control:
interface ClaudeCodeSession {
id: string;
status: 'initializing' | 'ready' | 'busy' | 'error' | 'terminated';
workingDirectory: string;
taskId?: string;
mcpSessionId?: string;
streamBuffer: string[];
errorBuffer: string[];
}
Session States:
initializing: Session being createdready: Available for queriesbusy: Currently executing a queryerror: Encountered an errorterminated: Session ended
3. Host Proxy Client (host-proxy-client.ts)
Communicates with the daemon for Claude execution:
class HostProxyClient {
execute(
prompt: string,
workingDirectory: string,
onStream?: (data: string) => void,
env?: Record<string, string>,
sessionId?: string,
taskId?: string,
onEvent?: (event: ClaudeEvent) => void
): Promise<string>
}
Key Responsibilities:
- Path Mapping: Converts Docker paths to host paths
- TCP Communication: Manages socket connection to daemon
- Stream Processing: Handles real-time output streaming
- Event Integration: Parses and emits Claude events
4. Event Parser (event-parser.ts)
Extracts structured events from Claude's output stream:
class ClaudeEventParser {
parseLine(line: string, streamType: 'stdout' | 'stderr'): ClaudeEvent[]
}
Detected Event Types:
- Tool Usage: Start/end of tool executions
- Messages: Assistant explanations and thinking
- Errors: Error messages and stack traces
- Stream Data: Raw output for logging
Event System
Event Types
// Tool usage events
{
type: 'tool:start',
toolName: 'bash',
toolId: 'tool_123',
parameters: { command: 'npm test' }
}
{
type: 'tool:end',
toolName: 'bash',
toolId: 'tool_123',
duration: 1234,
success: true
}
// Message events
{
type: 'message',
content: "I'll run the tests now",
messageType: 'thinking'
}
// Process events
{
type: 'process:start',
pid: 12345,
command: 'claude',
workingDirectory: '/project'
}
Event Flow
- Claude outputs to stdout/stderr
- Daemon captures and streams to Docker
- Event parser extracts structured events
- Service emits events to listeners
- Task store logs events for persistence
Query Execution
Execution Flow
1. Client calls querySync(sessionId, prompt)
↓
2. Service validates session state
↓
3. Determines execution mode (SDK vs Host Proxy)
↓
4. For Host Proxy:
a. Maps Docker paths to host paths
b. Sends request to daemon via TCP
c. Daemon spawns Claude process
d. Streams output back
↓
5. Parses events and updates task progress
↓
6. Returns final response
Command Building
For Claude CLI execution:
claude \
-p \
--output-format json \
--dangerously-skip-permissions \
--max-turns 5 \
"Your prompt here"
Progress Tracking
The service integrates with task management for progress tracking:
class ProgressTracker {
// Logs assistant messages to task
logAssistantMessage(taskId: string, content: string): Promise<void>
// Parses progress from stream
parseProgressFromStream(session: ClaudeCodeSession, data: string): Promise<void>
}
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
ANTHROPIC_API_KEY | API key for SDK mode | None |
CLAUDE_PROXY_HOST | Daemon hostname | host.docker.internal |
CLAUDE_PROXY_PORT | Daemon port | 9876 |
HOST_FILE_ROOT | Host project root | /var/www/html/systemprompt-coding-agent |
Options
interface ClaudeCodeOptions {
workingDirectory?: string;
env?: Record<string, string>;
timeout?: number;
maxTurns?: number;
}
Error Handling
Error Types
- SessionNotReadyError: Session not in ready state
- HostProxyConnectionError: Cannot connect to daemon
- HostProxyTimeoutError: Command execution timeout
- HostProxyError: General proxy errors
Error Recovery
try {
const result = await service.querySync(sessionId, prompt);
} catch (error) {
if (error instanceof SessionNotReadyError) {
// Wait for session to be ready
} else if (error instanceof HostProxyConnectionError) {
// Check daemon is running
}
}
Integration Points
With Agent Manager
// Link Claude session to agent session
const agentSession = agentManager.findSessionByTaskId(taskId);
if (agentSession) {
env.CLAUDE_SESSION_ID = agentSession.id;
}
With Task Store
// Update task with Claude events
taskStore.updateTask(taskId, {
logs: [...events],
lastActivity: new Date()
});
With MCP Handler
// Create Claude session for MCP tool execution
const sessionId = await claudeService.createSession({
workingDirectory: '/workspace'
});
claudeService.setTaskId(sessionId, task.id);
Best Practices
Session Management
- Create per-task sessions: Each task should have its own session
- Set working directory: Always specify the correct working directory
- Clean up sessions: Terminate sessions when tasks complete
- Handle session states: Check session state before queries
Event Handling
- Subscribe early: Set up event listeners before execution
- Parse all events: Don't ignore stream events
- Log structured events: Store events for debugging
- Handle errors gracefully: Expect and handle parsing errors
Performance
- Stream processing: Process streams incrementally
- Event batching: Batch events for storage
- Connection pooling: Reuse TCP connections
- Timeout handling: Set appropriate timeouts
Troubleshooting
Common Issues
-
"Tool 'claude' is not available"
- Check CLAUDE_PATH environment variable
- Verify Claude CLI is installed on host
- Ensure daemon has execute permissions
-
Connection Refused
- Verify daemon is running
- Check port configuration
- Ensure Docker networking is correct
-
Path Not Found
- Verify path mapping configuration
- Check HOST_FILE_ROOT environment variable
- Ensure volumes are mounted correctly
Debug Logging
Enable verbose logging:
// In your code
logger.debug('Claude query', {
sessionId,
prompt: prompt.substring(0, 100),
workingDirectory
});
Testing Claude Integration
# Test daemon connection
nc -zv host.docker.internal 9876
# Test Claude execution
docker exec mcp-server \
node -e "
const service = require('./build/services/claude-code').ClaudeCodeService.getInstance();
service.createSession().then(async (id) => {
const result = await service.querySync(id, 'echo Hello');
console.log(result);
});
"
This integration provides a robust bridge between the MCP protocol and Claude's powerful coding capabilities, enabling sophisticated AI-assisted development workflows.