Skip to main content
Glama
Vvkmnn
by Vvkmnn
README.md
# claude-senator-mcp

> **āŒ under construction:** This project is under heavy construction and is not intended for public use / nor has it been published to npm. Information in the README below may be outdated, user discretion is advised.

<!-- ![Claude Senator](demo.gif) -->

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for **inter-Claude communication and context sharing**. Enables Claude instances to collaborate, share context, and fork conversations without interruption.

[![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
[![npm version](https://img.shields.io/npm/v/claude-senator.svg)](https://www.npmjs.com/package/claude-senator)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org/)

## Philosophy

**Simple for humans, rich for Claude.**

- **Human**: One-line commands that expand into ultra-rich context
- **Claude**: Dense collaboration intelligence with smart pointers
- **Architecture**: Zero-dependency file-based IPC with beautiful ASCII UI

## Install

Requirements:

- [Claude Code](https://claude.ai/code)

```bash
npm install -g claude-senator
```

**From shell:**

```bash
claude mcp add claude-senator -- npx claude-senator
```

**From inside Claude** (restart required):

```
Add this to our global mcp config: npx claude-senator
```

**From any manually configurable `mcp.json`**: (Cursor, Windsurf, etc.)

```json
{
  "mcpServers": {
    "claude-senator": {
      "command": "npx",
      "args": ["claude-senator"],
      "env": {}
    }
  }
}
```

## Features

### šŸ›ļø **Current: Inter-Claude Messaging System**

#### **3-Function Interface**

```bash
# Share context with other Claude instances
claude-senator send_context --target_pid 98471 --context_request "Help with documentation"

# Receive context from other Claude instances
claude-senator receive_context

# Live status of all Claude instances with collaboration recommendations
claude-senator status
```

#### **Smart Pointer Architecture**

Ultra-lightweight context sharing using pointers to existing `~/.claude/projects/` data rather than copying content:

```typescript
// Instead of copying data (expensive)
const heavyMessage = {
  conversationHistory: [
    /* 1000s of messages */
  ],
  fileContents: [
    /* large file contents */
  ],
};

// We use smart pointers (lightweight)
const smartMessage = {
  ctx_pointers: {
    projects_dir: `~/.claude/projects/${projectEncoded}`,
    conversation_history: this.getConversationPointer(),
    git_context: this.getGitContextString(),
    active_files: this.getActiveFilesArray(),
  },
};
```

#### **Beautiful ASCII UI**

```bash
╔═ šŸ›ļø ═══════════════════════════════════════════════════════════════════
ā•‘ → Claude 98471 • "Help with documentation..." • ~/.claude pointers sent
ā•‘ šŸ”„ Context shared • Ready for collaboration
ā•šā• šŸš€ 2 contexts processed • Rich collaboration network active
```

### šŸ”„ **New: Context Forking System**

#### **Problem**

You have Claude deep in work (318s, 9.3k tokens) and want to ask questions without interrupting.

#### **Solution**

Context forking creates new Claude instances with inherited context while preserving the working Claude's state.

```bash
# Fork context from working Claude
claude-senator fork_claude --source_pid 98471 --query "What's the documentation structure?"

# Result: New session with full context + your question
# Working Claude continues uninterrupted
```

## Technical Architecture

### **Directory Structure**

```
/tmp/claude-senator/
ā”œā”€ā”€ instances/          # Each Claude writes {pid}.json with status/info
ā”œā”€ā”€ messages/           # Individual message files in JSONL format
└── commands/           # Command injection files for input manipulation
```

### **Smart Pointer System**

References existing `~/.claude/projects/` data instead of copying:

```typescript
interface ContextPointers {
  projects_dir: string; // `~/.claude/projects/${encoded_path}`
  conversation_history: string; // Recent conversation file path
  git_context: string; // Compact git state (branch@commit+dirty)
  active_files: string[]; // Recently modified files
  current_task: string; // What Claude is working on
  collaboration_intent: string; // Why reaching out to other Claude
}
```

### **Message Format**

Ultra-dense messaging with context reconstruction:

```typescript
interface InterClaudeMessage {
  id: string;
  from: number; // Sender Claude PID
  to: number | 'all'; // Target Claude PID or broadcast
  type: 'ultra_dense_message';
  content: string; // Human-readable request
  timestamp: number;
  options: {
    claudeContext: {
      h: string; // Human message
      pid: number; // Claude PID
      ts: number; // Timestamp
      cwd: string; // Working directory
      ctx_pointers: ContextPointers; // Smart pointers to data
    };
  };
}
```

## Implementation Details

### **Current Messaging System**

#### **SessionManager Core Methods**

##### `createMessage(humanMessage: string)`

Creates ultra-dense context messages with smart pointers:

- **Purpose**: Lightweight context sharing
- **Input**: Human message string
- **Output**: Smart pointer context object with collaboration intelligence
- **Used by**: `send_context` tool

```typescript
private createMessage(humanMessage: string): any {
  const workingDir = process.cwd();
  const claudeDir = join(homedir(), '.claude');
  const projectEncoded = workingDir.replace(/\//g, '-').replace(/^-/, '');
  const pid = process.ppid || process.pid;

  return {
    h: humanMessage,
    pid: pid,
    ts: Date.now(),
    cwd: workingDir,
    ctx_pointers: {
      projects_dir: `~/.claude/projects/${projectEncoded}`,
      conversation_history: this.getConversationPointer(),
      git_context: this.getGitContextString(),
      active_files: this.getActiveFilesArray(),
      current_task: this.getCurrentTaskFromContext(),
      collaboration_intent: this.determineCollaborationIntent(humanMessage)
    }
  };
}
```

##### `reconstructRichContext(message: any)`

Rebuilds full context from smart pointers:

- **Purpose**: Context reconstruction on receiving end
- **Input**: Message with smart pointers
- **Output**: Full context object with conversation history
- **Used by**: `receive_context` tool

```typescript
private reconstructRichContext(message: any): any {
  const ctx = message.options?.claudeContext;
  if (!ctx?.ctx_pointers) return null;

  const pointers = ctx.ctx_pointers;
  const reconstructed = {
    sender_info: {
      human_message: ctx.h,
      pid: ctx.pid,
      working_directory: ctx.cwd,
      timestamp: ctx.ts
    },
    collaboration_context: {
      current_task: pointers.current_task,
      intent: pointers.collaboration_intent,
      git_state: pointers.git_context,
      active_files: pointers.active_files
    },
    shared_data_access: {
      projects_dir: pointers.projects_dir,
      conversation_history: pointers.conversation_history
    }
  };

  // Load conversation history if pointer exists
  if (pointers.conversation_history) {
    reconstructed.conversation_snippet = this.loadConversationSnippet(pointers.conversation_history);
  }

  return reconstructed;
}
```

##### `generateRichContextDisplay(instance: any, activity: any)`

Creates real-time status with collaboration scoring:

- **Purpose**: Live context display without transmission
- **Input**: Claude instance data and activity
- **Output**: Rich context with collaboration readiness score
- **Used by**: `status` tool

```typescript
private generateRichContextDisplay(instance: any, activity: any): any {
  const workingDir = instance.cwd || instance.projectPath || '/unknown';
  const encodedPath = String(workingDir).replace(/\//g, '-').replace(/^-/, '');

  const contextPointers = {
    projectsDir: `~/.claude/projects/${encodedPath}`,
    conversationHistory: this.getConversationPointer(workingDir),
    gitContext: this.getGitContextString(workingDir),
    activeFiles: this.getActiveFilesArray(workingDir),
    currentTask: activity.task || 'Active',
    collaborationIntent: this.determineCollaborationReadiness(activity, instance)
  };

  const collaborationMetrics = {
    hasRecentActivity: activity.lastActivity > Date.now() - 300000,
    hasActiveFiles: contextPointers.activeFiles.length > 0,
    hasCleanGitState: !contextPointers.gitContext.includes('dirty'),
    isWorkingOnKnownTask: activity.task && activity.task !== 'Active',
    projectType: this.detectProjectType(workingDir)
  };

  return {
    ...contextPointers,
    collaborationMetrics,
    collaborationReason: this.getCollaborationReason(collaborationMetrics),
    readinessScore: this.calculateReadinessScore(collaborationMetrics)
  };
}
```

#### **Collaboration Scoring Algorithm**

```typescript
private calculateCollaborationScore(contextData: any): number {
  const metrics = contextData.collaborationMetrics;
  if (!metrics) return 0;

  let score = 0;
  if (metrics.hasRecentActivity) score += 0.3;
  if (metrics.hasActiveFiles) score += 0.2;
  if (metrics.hasCleanGitState) score += 0.2;
  if (metrics.isWorkingOnKnownTask) score += 0.2;
  if (metrics.projectType !== 'unknown') score += 0.1;

  return Math.min(score, 1.0);
}
```

#### **Context Pointer Helpers**

```typescript
private getConversationPointer(): string | null {
  const claudeDir = join(homedir(), '.claude');
  const projectEncoded = process.cwd().replace(/\//g, '-').replace(/^-/, '');
  const projectDir = join(claudeDir, 'projects', projectEncoded);

  if (existsSync(projectDir)) {
    const files = readdirSync(projectDir).filter(f => f.includes('conversation'));
    if (files.length > 0) {
      return `~/.claude/projects/${projectEncoded}/${files[files.length - 1]}`;
    }
  }
  return null;
}

private getGitContextString(): string {
  try {
    const branch = execSync('git branch --show-current 2>/dev/null', { encoding: 'utf8' }).trim();
    const commit = execSync('git rev-parse --short HEAD 2>/dev/null', { encoding: 'utf8' }).trim();
    const status = execSync('git status --porcelain 2>/dev/null', { encoding: 'utf8' }).trim();
    const dirty = status ? '+dirty' : '';
    return `${branch}@${commit}${dirty}`;
  } catch (error) {
    return 'no-git';
  }
}

private getActiveFilesArray(): string[] {
  try {
    const recentFiles = execSync('git diff --name-only HEAD~1 2>/dev/null', {
      encoding: 'utf8'
    }).trim().split('\n').filter(f => f);
    return recentFiles.slice(0, 5);
  } catch (error) {
    return [];
  }
}
```

### **New Context Forking System**

#### **Core Implementation**

```typescript
// New MCP tool
{
  name: 'fork_claude',
  description: 'Create new Claude session with full context from target Claude',
  inputSchema: {
    source_pid: { type: 'number', description: 'Claude to copy from' },
    query: { type: 'string', description: 'Your question for the new Claude' }
  }
}

// Implementation
case 'fork_claude': {
  const sourcePid = args?.source_pid as number;
  const query = args?.query as string;

  // Find source Claude's conversation
  const sourceActivity = this.sessionManager.parseLiveActivity(sourcePid);
  const conversationPath = this.findConversationPath(sourcePid);

  // Copy conversation to new session
  const newSessionId = this.createForkedSession(conversationPath, query);

  return {
    content: [{
      type: 'text',
      text: `╔═ šŸ”„ Context Forked ═══════════════════════════════════════════════════
ā•‘ šŸ“ø Copied: ${sourceActivity.task} • Full conversation history
ā•‘ šŸ†• New session: ${newSessionId}
ā•‘ šŸŽÆ Ready for: "${query}"
ā•šā• ✨ Run: claude --session ${newSessionId} to start new Claude with context`
    }]
  };
}
```

#### **Session Creation**

```typescript
private createForkedSession(sourcePath: string, newQuery: string): string {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '');
  const newSessionId = `fork_${timestamp}`;
  const newSessionPath = join(homedir(), '.claude', 'sessions', newSessionId);

  // Create new session directory
  mkdirSync(newSessionPath, { recursive: true });

  // Copy conversation history
  if (existsSync(sourcePath)) {
    const conversationContent = readFileSync(sourcePath, 'utf8');
    const newConversationPath = join(newSessionPath, 'conversation.jsonl');
    writeFileSync(newConversationPath, conversationContent);

    // Add new query as next message
    const queryMessage = {
      type: 'user',
      message: { content: newQuery },
      timestamp: Date.now()
    };

    appendFileSync(newConversationPath, '\n' + JSON.stringify(queryMessage));
  }

  return newSessionId;
}
```

## Usage Examples

### **Basic Messaging Workflow**

```bash
# 1. Check who's available for collaboration
claude-senator status

# Output:
╔═ šŸ›ļø ═══════════════════════════════════════════════════════════════════
ā•‘ 3 Claudes active • rich context network
ā•‘ 🟢 Claude 98471 • rust-project • debugging memory leak • main@a1b2c3
ā•šā• šŸ”„ Live status • Smart pointer network active

# 2. Send context to specific Claude
claude-senator send_context --target_pid 98471 --context_request "Need help with async Rust debugging"

# Output:
╔═ šŸ›ļø ═══════════════════════════════════════════════════════════════════
ā•‘ → Claude 98471 • "Need help with async Rust debugging" • ~/.claude pointers sent
ā•‘ šŸ”„ Context shared • Ready for collaboration
ā•šā• šŸš€ Context transmitted • Collaboration network active

# 3. Receive context from other Claudes
claude-senator receive_context

# Output:
╔═ šŸ›ļø ═══════════════════════════════════════════════════════════════════
ā•‘ "Debug memory leak in tokio runtime" • debugging_assistance • rust-project
ā•‘ šŸ“Š Rich context: git state, active files, current task
ā•šā• šŸ“Ø 1 context processed • Ready for collaboration
```

### **Context Forking Workflow**

```bash
# Scenario: Claude deep in documentation work, want to ask questions
claude-senator status

# Output shows Claude 98471 working on documentation (318s, 9.3k tokens)
╔═ šŸ›ļø ═══════════════════════════════════════════════════════════════════
ā•‘ 1 Claude active • rich context network
ā•‘ 🟔 Claude 98471 • docs-project • Writing API documentation • main@x1y2z3
ā•šā• šŸ”„ Live status • Smart pointer network active

# Fork context without interrupting
claude-senator fork_claude --source_pid 98471 --query "What's the current API documentation structure?"

# Output:
╔═ šŸ”„ Context Forked ═══════════════════════════════════════════════════════
ā•‘ šŸ“ø Copied: Writing API documentation • 9.3k tokens • Full conversation
ā•‘ šŸ†• New session: fork_20250716_141509
ā•‘ šŸŽÆ Ready for: "What's the current API documentation structure?"
ā•šā• ✨ Run: claude --session fork_20250716_141509 to start new Claude

# Start new Claude with inherited context
claude --session fork_20250716_141509
# New Claude starts with full context + your question already asked
```

## Architecture Benefits

### **Zero Dependencies**

- No npm packages beyond @modelcontextprotocol/sdk
- No external services (Redis, databases, etc.)
- No system dependencies
- No elevated permissions

### **Token Efficiency**

- Smart pointers prevent data duplication
- Context reconstruction on-demand
- Minimal memory footprint
- Efficient git state tracking

### **Scalability**

- **2-20 Claudes**: Excellent performance (<10ms operations)
- **20-100 Claudes**: Good performance, minimal filesystem overhead
- **100+ Claudes**: May benefit from SQLite upgrade (see Future section)

### **Reliability**

- Each Claude owns its files (no shared writes)
- Automatic cleanup of dead instances
- Graceful error handling
- Self-healing directory structure

## UI Design Philosophy

### **3-Line ASCII Format**

```bash
╔═ šŸ›ļø ═══════════════════════════════════════════════════════════════════
ā•‘ [CONTENT LINE - exactly fits terminal width]
ā•‘ [STATUS LINE - shows current state and metrics]
ā•šā• [FOOTER LINE - shows next action or network status]
```

### **Truncation Rules**

- Content truncated to fit terminal width
- Important information prioritized
- Emoji indicators for quick scanning
- Consistent spacing and alignment

### **Tool-Specific Emoji**

- šŸ›ļø Always at top (Claude Senator identity)
- šŸ”„ Context sharing operations
- šŸ“Ø Message receiving
- šŸ”„ Context forking
- šŸš€ Network activity

## File Structure

### **Core Files**

```
src/
ā”œā”€ā”€ index.ts          # MCP server and tool handlers
ā”œā”€ā”€ session.ts        # SessionManager - core messaging logic
ā”œā”€ā”€ memory.ts         # MemoryManager - conversation search
ā”œā”€ā”€ injection.ts      # CommandInjector - input manipulation
└── types.ts          # TypeScript interfaces
```

### **Key Classes**

#### **SessionManager** (src/session.ts)

- **Purpose**: Core messaging and context management
- **Key methods**: `createMessage`, `reconstructRichContext`, `generateRichContextDisplay`
- **Handles**: Smart pointers, context reconstruction, collaboration scoring

#### **MemoryManager** (src/memory.ts)

- **Purpose**: Conversation history search and analysis
- **Key methods**: TBD (future implementation)
- **Handles**: Historical context retrieval, memory patterns

#### **CommandInjector** (src/injection.ts)

- **Purpose**: Input manipulation and command routing
- **Key methods**: TBD (future implementation)
- **Handles**: Inter-Claude command injection, workflow automation

## Future Enhancements

### **Messaging System Extensions**

```typescript
// Planned tools
{
  name: 'broadcast_context',
  description: 'Send context to all active Claude instances'
}

{
  name: 'handoff_context',
  description: 'Transfer work to another Claude with full context'
}

{
  name: 'sync_context',
  description: 'Synchronize context across multiple Claude instances'
}
```

### **Context Forking Improvements**

```typescript
// Selective inheritance
{
  name: 'fork_claude_selective',
  inputSchema: {
    source_pid: number,
    query: string,
    include_conversation: boolean,
    include_files: boolean,
    include_git_state: boolean
  }
}

// Context merging
{
  name: 'merge_context',
  description: 'Merge insights from forked Claude back to parent'
}
```

### **Performance Optimizations**

- **Context Caching**: LRU cache for frequently accessed contexts
- **Compression**: Gzip compression for large conversation histories
- **Streaming**: Real-time context updates as work progresses
- **SQLite Upgrade**: For 100+ concurrent Claude instances

### **Integration Possibilities**

- **IDE Plugins**: VS Code, Cursor, Windsurf integration
- **CI/CD**: Automated context sharing in build pipelines
- **Monitoring**: Real-time collaboration network visualization
- **Analytics**: Context sharing patterns and optimization

## Project Split Guide

If splitting into separate projects:

### **Core Messaging** (claude-senator-messaging)

- **Files**: `src/session.ts` (lines 1-883, 1075-1451), `src/types.ts`
- **Dependencies**: `src/memory.ts` for conversation search
- **Features**: `send_context`, `receive_context`, `status`
- **Size**: ~1400 lines, full messaging system

### **Context Forking** (claude-senator-forking)

- **Files**: New implementation in `src/forking.ts`
- **Dependencies**: `src/session.ts` (conversation finding methods)
- **Features**: `fork_claude` tool
- **Size**: ~50 lines, minimal implementation

### **Shared Infrastructure** (claude-senator-core)

- **Files**: `src/types.ts`, directory management utilities
- **Purpose**: Common interfaces and utilities
- **Size**: ~200 lines, essential types and helpers

### **Migration Strategy**

1. **Extract core types**: Move interfaces to shared package
2. **Split session manager**: Separate messaging and forking logic
3. **Maintain compatibility**: Ensure both systems can coexist
4. **Document interfaces**: Clear APIs for integration

## Development

```bash
git clone https://github.com/yourusername/claude-senator
cd claude-senator
npm install
npm run build
npm test
```

### **Contributing**

- Fork the repository and create feature branches
- Test with multiple Claude instances before submitting PRs
- Follow TypeScript strict mode and MCP protocol standards
- Document any new smart pointer patterns

### **Testing**

- Test messaging between 2-5 Claude instances
- Verify context reconstruction accuracy
- Test forking with large conversation histories
- Validate ASCII UI formatting across terminals

## License

[MIT](LICENSE)

---

_Claude Senator enables unprecedented collaboration between Claude instances through smart context sharing and non-disruptive forking. Built for developers who need their AI assistants to work together as seamlessly as they do._