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.
A Model Context Protocol (MCP) server for inter-Claude communication and context sharing. Enables Claude instances to collaborate, share context, and fork conversations without interruption.

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:
npm install -g claude-senator
From shell:
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 : (Cursor, Windsurf, etc.)
{
"mcpServers": {
"claude-senator": {
"command": "npx",
"args": ["claude-senator"],
"env": {}
}
}
}
Features
šļø Current: Inter-Claude Messaging System
3-Function Interface
# 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:
// 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
āā šļø āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā 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.
# 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:
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:
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
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
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
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
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
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
// 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
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
# 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
# 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
āā šļø āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā [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
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
// 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
// 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
Extract core types: Move interfaces to shared package
Split session manager: Separate messaging and forking logic
Maintain compatibility: Ensure both systems can coexist
Document interfaces: Clear APIs for integration
Development
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
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.