Skip to main content

HumanLayer Repository - Integration Capabilities Analysis

Analysis Date: 2025-10-14
Focus: Integration points, extensibility, and API capabilities

Integration Architecture Overview​

The HumanLayer/CodeLayer system is designed as an integration platform with multiple extensibility points. The architecture supports three primary integration patterns: AI Agent Integration (via MCP), External Tool Integration (via REST/RPC), and UI Extension (via component architecture).

AI Agent Integration Layer​

Model Context Protocol (MCP) Integration​

Core MCP Implementation (/home/halcasteel/humanlayer/hlyr/src/mcp.ts:19-89):

export async function startClaudeApprovalsMCPServer() {
const server = new Server(
{ name: 'humanlayer-claude-local-approvals', version: '1.0.0' },
{ capabilities: { tools: {} } }
)

// Tool registration for AI agent consumption
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'request_permission',
description: 'Request permission from human before performing high-impact actions',
inputSchema: {
type: 'object',
properties: {
tool_name: {
type: 'string',
description: 'Name of the tool requiring approval'
},
input: {
type: 'object',
description: 'Arguments that would be passed to the tool'
},
tool_use_id: {
type: 'string',
description: 'Unique identifier for this tool invocation'
},
explanation: {
type: 'string',
description: 'Human-readable explanation of what this action will do'
}
},
required: ['tool_name', 'input', 'tool_use_id']
}
}
]
}
})

// Tool execution handler with async approval workflow
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'request_permission') {
const { tool_name, input, tool_use_id, explanation } = request.params.arguments

try {
// Create approval request via daemon
const approvalRequest = {
session_id: process.env.CLAUDE_SESSION_ID || 'unknown',
tool_name,
tool_input: JSON.stringify(input),
tool_use_id,
explanation
}

const approval = await daemonClient.createApproval(approvalRequest)

// Poll for human decision
const result = await pollForApprovalResult(approval.id)

return {
content: [{
type: 'text',
text: `Permission ${result.decision}: ${result.explanation || ''}`
}],
isError: result.decision === 'deny'
}
} catch (error) {
return {
content: [{
type: 'text',
text: `Approval request failed: ${error.message}`
}],
isError: true
}
}
}
})
}

MCP Integration Benefits:

  • Standard Protocol: Works with any MCP-compatible AI system
  • Type Safety: JSON Schema validation for all tool parameters
  • Async Operations: Non-blocking approval workflows
  • Error Handling: Graceful degradation when approval fails

Claude Code Specific Integration​

Session Management Integration (/home/halcasteel/humanlayer/claudecode-go/client.go:15-67):

// Go SDK for programmatic Claude Code session management
type Client struct {
daemonClient DaemonClient
config Config
}

// LaunchSession starts a new Claude Code session with approval integration
func (c *Client) LaunchSession(ctx context.Context, options SessionOptions) (*Session, error) {
// Validate session directory
if err := c.validateDirectory(options.Directory); err != nil {
return nil, fmt.Errorf("invalid directory: %w", err)
}

// Prepare Claude Code command with MCP integration
command := []string{
"claude-code",
"--model", options.Model,
"--directory", options.Directory,
}

// Add MCP server configuration
if c.config.EnableApprovals {
command = append(command,
"--mcp-server", "humanlayer-claude-local-approvals",
"--mcp-server-command", "hlyr mcp claude_approvals",
)
}

// Create session via daemon
sessionRequest := daemon.CreateSessionRequest{
Command: strings.Join(command, " "),
Directory: options.Directory,
Environment: options.Environment,
}

session, err := c.daemonClient.CreateSession(ctx, sessionRequest)
if err != nil {
return nil, fmt.Errorf("failed to create session: %w", err)
}

return &Session{
ID: session.ID,
Status: session.Status,
Directory: session.Directory,
StartTime: session.StartTime,
client: c,
}, nil
}

// AwaitCompletion waits for session to complete and returns final state
func (s *Session) AwaitCompletion(ctx context.Context) (*SessionResult, error) {
// Subscribe to session events
events, err := s.client.daemonClient.SubscribeToSessionEvents(ctx, s.ID)
if err != nil {
return nil, fmt.Errorf("failed to subscribe to events: %w", err)
}

for {
select {
case event := <-events:
if event.Type == "session_completed" || event.Type == "session_failed" {
// Fetch final session state
finalSession, err := s.client.daemonClient.GetSession(ctx, s.ID)
if err != nil {
return nil, fmt.Errorf("failed to get final session: %w", err)
}

return &SessionResult{
Session: finalSession,
Success: event.Type == "session_completed",
Events: s.getConversationEvents(ctx),
TokenUsage: finalSession.TokenUsage,
Duration: finalSession.EndTime.Sub(finalSession.StartTime),
}, nil
}
case <-ctx.Done():
return nil, ctx.Err()
}
}
}

External System Integration​

REST API Integration Layer​

OpenAPI-Driven External Integration (/home/halcasteel/humanlayer/hld/api/openapi.yaml:15-89):

# External system integration via REST API
paths:
/api/v1/sessions:
post:
summary: Create new coding session
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
command:
type: string
description: Command to execute (e.g., "claude-code --model sonnet")
directory:
type: string
description: Working directory for the session
environment:
type: object
additionalProperties:
type: string
description: Environment variables
required: [command, directory]
responses:
'201':
description: Session created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SessionResponse'

/api/v1/approvals:
get:
summary: List pending approvals
parameters:
- name: status
in: query
schema:
type: string
enum: [pending, approved, denied]
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 50
- name: offset
in: query
schema:
type: integer
minimum: 0
default: 0
responses:
'200':
description: List of approvals
content:
application/json:
schema:
$ref: '#/components/schemas/ApprovalsResponse'

post:
summary: Create approval request
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateApprovalRequest'
responses:
'201':
description: Approval created
content:
application/json:
schema:
$ref: '#/components/schemas/CreateApprovalResponse'

/api/v1/approvals/{approval_id}/decision:
post:
summary: Send approval decision
parameters:
- name: approval_id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
decision:
type: string
enum: [approve, deny]
explanation:
type: string
description: Optional explanation for the decision
required: [decision]
responses:
'200':
description: Decision processed
content:
application/json:
schema:
$ref: '#/components/schemas/DecideApprovalResponse'

# Server-Sent Events for real-time updates
/api/v1/events:
get:
summary: Subscribe to real-time events
parameters:
- name: event_types
in: query
schema:
type: array
items:
type: string
enum: [new_approval, approval_resolved, session_status_changed]
- name: session_id
in: query
schema:
type: string
description: Filter events by session ID
responses:
'200':
description: Event stream
content:
text/event-stream:
schema:
type: string

Generated Client Libraries (/home/halcasteel/humanlayer/hld/sdk/typescript/src/client.ts:15-89):

// Auto-generated TypeScript client with built-in error handling
export class HumanLayerClient {
private api: DefaultApi

constructor(config: Configuration = {}) {
this.api = new DefaultApi(config)
}

// Session management
async createSession(request: CreateSessionRequest): Promise<Session> {
try {
const response = await this.api.createSession({ createSessionRequest: request })
return response.data.session
} catch (error) {
throw new HumanLayerError('Failed to create session', error)
}
}

async getSession(sessionId: string): Promise<Session> {
try {
const response = await this.api.getSession({ sessionId })
return response.data.session
} catch (error) {
if (error.response?.status === 404) {
throw new SessionNotFoundError(sessionId)
}
throw new HumanLayerError('Failed to get session', error)
}
}

// Real-time event subscription
async subscribeToEvents(options: EventSubscriptionOptions): Promise<EventStream> {
const eventSource = new EventSource(
`/api/v1/events?${new URLSearchParams(options).toString()}`
)

return new EventStream(eventSource)
}

// Approval workflow integration
async createApproval(request: CreateApprovalRequest): Promise<Approval> {
const response = await this.api.createApproval({ createApprovalRequest: request })
return response.data.approval
}

async sendDecision(approvalId: string, decision: ApprovalDecision): Promise<void> {
await this.api.decideApproval({
approvalId,
decideApprovalRequest: { decision: decision.decision, explanation: decision.explanation }
})
}
}

// Strongly-typed event streaming
export class EventStream {
constructor(private eventSource: EventSource) {}

on<T extends Event>(eventType: EventType, handler: (event: T) => void): void {
this.eventSource.addEventListener(eventType, (event) => {
try {
const parsedEvent = JSON.parse(event.data)
handler(parsedEvent as T)
} catch (error) {
console.error('Failed to parse event:', error)
}
})
}

close(): void {
this.eventSource.close()
}
}

Database Integration Patterns​

Plugin-Style Data Access (/home/halcasteel/humanlayer/hld/store/store.go:15-67):

// Pluggable storage interface for different backends
type Store interface {
// Session management
CreateSession(session *Session) error
GetSession(id string) (*Session, error)
UpdateSession(id string, updates SessionUpdates) error
ListSessions(filters SessionFilters) ([]Session, error)

// Approval workflow
CreateApproval(approval *Approval) error
GetApproval(id string) (*Approval, error)
UpdateApprovalStatus(id string, status ApprovalStatus) error
ListApprovals(filters ApprovalFilters) ([]Approval, error)

// Event sourcing
StoreConversationEvent(event *ConversationEvent) error
GetConversationEvents(sessionID string, options EventOptions) ([]ConversationEvent, error)

// File snapshots for audit trail
StoreFileSnapshot(snapshot *FileSnapshot) error
GetFileSnapshots(sessionID string) ([]FileSnapshot, error)

// Health and maintenance
HealthCheck() error
Close() error
}

// PostgreSQL implementation (planned extension)
type PostgreSQLStore struct {
db *sql.DB
config PostgreSQLConfig
}

// MongoDB implementation (planned extension)
type MongoStore struct {
client *mongo.Client
database *mongo.Database
}

// Store factory for runtime backend selection
func NewStore(config StoreConfig) (Store, error) {
switch config.Type {
case "sqlite":
return NewSQLiteStore(config.SQLite)
case "postgresql":
return NewPostgreSQLStore(config.PostgreSQL)
case "mongodb":
return NewMongoStore(config.MongoDB)
default:
return nil, fmt.Errorf("unsupported store type: %s", config.Type)
}
}

UI Extension Architecture​

Component Plugin System​

Extensible React Architecture (/home/halcasteel/humanlayer/humanlayer-wui/src/components/internal/ConversationStream/EventContent/index.ts:15-45):

// Plugin-style component registration for different tool types
export interface ToolCallContentRenderer {
toolName: string
component: React.ComponentType<ToolCallContentProps>
priority?: number // Higher priority renders first
}

// Built-in tool renderers
const BUILT_IN_RENDERERS: ToolCallContentRenderer[] = [
{ toolName: 'bash', component: BashToolCallContent },
{ toolName: 'edit', component: EditToolCallContent },
{ toolName: 'read', component: ReadToolCallContent },
{ toolName: 'write', component: WriteToolCallContent },
{ toolName: 'grep', component: GrepToolCallContent },
{ toolName: 'glob', component: GlobToolCallContent },
{ toolName: 'ls', component: LSToolCallContent },
{ toolName: 'multi_edit', component: MultiEditToolCallContent },
{ toolName: 'task', component: TaskToolCallContent },
{ toolName: 'web_fetch', component: WebFetchToolCallContent },
{ toolName: 'web_search', component: WebSearchToolCallContent },
]

// Registry for custom tool renderers
class ToolRendererRegistry {
private renderers = new Map<string, ToolCallContentRenderer>()

constructor() {
// Register built-in renderers
BUILT_IN_RENDERERS.forEach(renderer => {
this.register(renderer)
})
}

register(renderer: ToolCallContentRenderer): void {
this.renderers.set(renderer.toolName, renderer)
}

getRenderer(toolName: string): ToolCallContentRenderer | null {
return this.renderers.get(toolName) || null
}

getAllRenderers(): ToolCallContentRenderer[] {
return Array.from(this.renderers.values())
.sort((a, b) => (b.priority || 0) - (a.priority || 0))
}
}

// Usage in conversation display
export const ToolCallContentRenderer: React.FC<ToolCallContentProps> = ({ toolCall }) => {
const renderer = toolRendererRegistry.getRenderer(toolCall.tool_name)

if (renderer) {
const Component = renderer.component
return <Component {...props} />
}

// Fallback for unknown tools
return <UnknownToolCallContent toolCall={toolCall} />
}

Theme and layout Extensibility (/home/halcasteel/humanlayer/humanlayer-wui/src/contexts/ThemeContext.tsx:15-67):

// Extensible theme system
export interface ThemeConfig {
name: string
displayName: string
colors: {
primary: string
secondary: string
accent: string
background: string
surface: string
text: {
primary: string
secondary: string
muted: string
}
status: {
success: string
warning: string
error: string
info: string
}
}
typography: {
fontFamily: {
sans: string[]
mono: string[]
}
fontSize: Record<string, string>
lineHeight: Record<string, string>
}
spacing: Record<string, string>
borderRadius: Record<string, string>
}

// Built-in themes
const THEMES: Record<string, ThemeConfig> = {
light: {
name: 'light',
displayName: 'Light Mode',
colors: {
primary: '#2563eb',
secondary: '#64748b',
// ... complete theme definition
}
},
dark: {
name: 'dark',
displayName: 'Dark Mode',
colors: {
primary: '#3b82f6',
secondary: '#94a3b8',
// ... complete theme definition
}
}
}

// Theme plugin registration
export class ThemeRegistry {
private themes = new Map<string, ThemeConfig>()

constructor() {
Object.values(THEMES).forEach(theme => {
this.register(theme)
})
}

register(theme: ThemeConfig): void {
this.themes.set(theme.name, theme)
}

getTheme(name: string): ThemeConfig | null {
return this.themes.get(name) || null
}

listThemes(): ThemeConfig[] {
return Array.from(this.themes.values())
}
}

// Custom theme loading from user configuration
export const loadCustomThemes = async (): Promise<ThemeConfig[]> => {
try {
const userConfigPath = await path.join(await path.appConfigDir(), 'themes')
const themeFiles = await fs.readDir(userConfigPath)

const customThemes: ThemeConfig[] = []

for (const file of themeFiles) {
if (file.name?.endsWith('.json')) {
const themePath = await path.join(userConfigPath, file.name)
const themeContent = await fs.readTextFile(themePath)
const theme: ThemeConfig = JSON.parse(themeContent)

// Validate theme structure
if (isValidThemeConfig(theme)) {
customThemes.push(theme)
}
}
}

return customThemes
} catch (error) {
console.warn('Failed to load custom themes:', error)
return []
}
}

Webhook and Event Integration​

External Webhook System​

Webhook Configuration and Management (/home/halcasteel/humanlayer/hld/config/config.go:123-167):

// Webhook integration for external system notifications
type WebhookConfig struct {
Enabled bool `mapstructure:"enabled"`
URL string `mapstructure:"url"`
Secret string `mapstructure:"secret"`
Events []string `mapstructure:"events"`
Headers map[string]string `mapstructure:"headers"`
Timeout time.Duration `mapstructure:"timeout"`
Retries int `mapstructure:"retries"`
}

// Webhook event payload structure
type WebhookEvent struct {
ID string `json:"id"`
Type string `json:"type"`
Timestamp time.Time `json:"timestamp"`
SessionID string `json:"session_id,omitempty"`
Data map[string]interface{} `json:"data"`
Signature string `json:"signature,omitempty"`
}

// Webhook dispatcher with retry logic
type WebhookDispatcher struct {
config WebhookConfig
client *http.Client
}

func (wd *WebhookDispatcher) SendEvent(event WebhookEvent) error {
if !wd.config.Enabled {
return nil // Webhooks disabled
}

// Create payload
payload, err := json.Marshal(event)
if err != nil {
return fmt.Errorf("failed to marshal webhook event: %w", err)
}

// Generate signature if secret is configured
if wd.config.Secret != "" {
signature := generateHMACSHA256(payload, wd.config.Secret)
event.Signature = signature
}

// Retry logic with exponential backoff
for attempt := 0; attempt <= wd.config.Retries; attempt++ {
req, err := http.NewRequest("POST", wd.config.URL, bytes.NewBuffer(payload))
if err != nil {
return fmt.Errorf("failed to create webhook request: %w", err)
}

// Add custom headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "HumanLayer-Webhook/1.0")
for key, value := range wd.config.Headers {
req.Header.Set(key, value)
}

ctx, cancel := context.WithTimeout(context.Background(), wd.config.Timeout)
req = req.WithContext(ctx)

resp, err := wd.client.Do(req)
cancel()

if err == nil && resp.StatusCode < 300 {
resp.Body.Close()
return nil // Success
}

if resp != nil {
resp.Body.Close()
}

// Exponential backoff before retry
if attempt < wd.config.Retries {
time.Sleep(time.Duration(1<<attempt) * time.Second)
}
}

return fmt.Errorf("webhook delivery failed after %d retries", wd.config.Retries)
}

Configuration Extension Points​

Plugin Configuration System​

Dynamic Configuration Loading (/home/halcasteel/humanlayer/hlyr/src/config.ts:89-134):

// Extensible configuration with plugin support
export interface ConfigPlugin {
name: string
version: string
configSchema: JsonSchema
defaultConfig: Record<string, unknown>
validateConfig: (config: unknown) => ConfigValidationResult
}

// Plugin registry
class ConfigPluginRegistry {
private plugins = new Map<string, ConfigPlugin>()

register(plugin: ConfigPlugin): void {
this.plugins.set(plugin.name, plugin)
}

getPlugin(name: string): ConfigPlugin | undefined {
return this.plugins.get(name)
}

getAllPlugins(): ConfigPlugin[] {
return Array.from(this.plugins.values())
}
}

// Dynamic configuration resolution with plugin support
export class ExtensibleConfigResolver {
constructor(
private pluginRegistry: ConfigPluginRegistry,
private baseResolver: ConfigResolver
) {}

async resolveConfig(): Promise<ExtendedConfig> {
// Load base configuration
const baseConfig = await this.baseResolver.resolve()

// Load plugin configurations
const pluginConfigs: Record<string, unknown> = {}

for (const plugin of this.pluginRegistry.getAllPlugins()) {
try {
const pluginConfigValue = await this.loadPluginConfig(plugin)
pluginConfigs[plugin.name] = pluginConfigValue
} catch (error) {
console.warn(`Failed to load config for plugin ${plugin.name}:`, error)
// Use default configuration
pluginConfigs[plugin.name] = plugin.defaultConfig
}
}

return {
...baseConfig,
plugins: pluginConfigs,
}
}

private async loadPluginConfig(plugin: ConfigPlugin): Promise<unknown> {
// Try multiple configuration sources
const sources = [
() => this.loadFromEnvironment(plugin),
() => this.loadFromConfigFile(plugin),
() => this.loadFromUserData(plugin),
]

for (const source of sources) {
try {
const config = await source()
if (config) {
const validation = plugin.validateConfig(config)
if (validation.valid) {
return config
} else {
console.warn(`Invalid config for ${plugin.name}:`, validation.errors)
}
}
} catch (error) {
// Continue to next source
}
}

// Return default configuration
return plugin.defaultConfig
}
}

// Example integration plugin
export const slackIntegrationPlugin: ConfigPlugin = {
name: 'slack',
version: '1.0.0',
configSchema: {
type: 'object',
properties: {
botToken: { type: 'string' },
signingSecret: { type: 'string' },
channels: {
type: 'array',
items: { type: 'string' }
},
approvalTimeoutMinutes: {
type: 'number',
minimum: 1,
maximum: 1440
}
},
required: ['botToken', 'signingSecret']
},
defaultConfig: {
channels: ['#approvals'],
approvalTimeoutMinutes: 30
},
validateConfig: (config: unknown) => {
// JSON Schema validation implementation
return validateAgainstSchema(config, slackIntegrationPlugin.configSchema)
}
}

Extension Development Kit​

SDK for Third-Party Integrations​

Plugin Development Framework (/home/halcasteel/humanlayer/packages/plugin-sdk/ - conceptual):

// Plugin SDK for third-party developers
export abstract class HumanLayerPlugin {
abstract name: string
abstract version: string
abstract description: string

// Plugin lifecycle hooks
async initialize(context: PluginContext): Promise<void> {
// Override in subclass
}

async shutdown(): Promise<void> {
// Override in subclass
}

// Tool integration
registerTools(): ToolDefinition[] {
return [] // Override to register custom tools
}

// UI integration
registerComponents(): ComponentDefinition[] {
return [] // Override to register custom UI components
}

// Event handling
handleEvent(event: HumanLayerEvent): Promise<void> {
// Override to handle system events
}
}

// Example Slack integration plugin
export class SlackIntegrationPlugin extends HumanLayerPlugin {
name = 'slack-integration'
version = '1.0.0'
description = 'Slack bot integration for approval workflows'

private slackClient: SlackClient | null = null

async initialize(context: PluginContext): Promise<void> {
const config = context.config.plugins.slack as SlackConfig
this.slackClient = new SlackClient(config.botToken)

// Register slash commands
await this.slackClient.registerSlashCommand('/approve', this.handleApprove.bind(this))
await this.slackClient.registerSlashCommand('/deny', this.handleDeny.bind(this))
}

async handleEvent(event: HumanLayerEvent): Promise<void> {
if (event.type === 'new_approval') {
await this.sendApprovalToSlack(event.data as Approval)
}
}

private async sendApprovalToSlack(approval: Approval): Promise<void> {
const message = {
channel: '#approvals',
text: `New approval request: ${approval.tool_name}`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*Tool:* ${approval.tool_name}\n*Session:* ${approval.session_id}\n*Description:* ${approval.explanation || 'No description'}`
}
},
{
type: 'actions',
elements: [
{
type: 'button',
text: { type: 'plain_text', text: 'Approve' },
style: 'primary',
action_id: `approve_${approval.id}`
},
{
type: 'button',
text: { type: 'plain_text', text: 'Deny' },
style: 'danger',
action_id: `deny_${approval.id}`
}
]
}
]
}

await this.slackClient?.postMessage(message)
}
}

This integration capabilities analysis reveals HumanLayer/CodeLayer as a comprehensive integration platform with:

  1. AI Agent Integration: Full MCP protocol support with async approval workflows
  2. External API Integration: REST API with OpenAPI specification and generated clients
  3. Database Extensibility: Pluggable storage backends for different deployment needs
  4. UI Extension System: Component registry and theme system for customization
  5. Event-Driven Architecture: Webhook integration and real-time event streaming
  6. Plugin Development Framework: SDK for third-party integrations and extensions

The architecture demonstrates sophisticated extensibility without compromising core functionality or performance.