@magnet-ai/magnet-agent-server
v0.1.17
Published
A WebSocket server companion to Magnet AI (https://www.magnet.run) that proxies requests to Claude Code API
Readme
Magnet Agent Server
A WebSocket server that proxies requests to the Claude Code API, enabling real-time communication with Claude's code assistance capabilities.
Features
- 🔌 WebSocket-based communication with Claude Code API
- 🚀 Built with Bun for high performance
- ⚡ Real-time streaming of Claude responses
- 🌳 Git worktree isolation for safe code changes
- 💾 Saved Sessions - persistent session storage and retrieval
- 🛑 Session management with abort capabilities
- 📊 Git diff functionality for worktree changes
- 🌿 Automatic branch creation from worktree changes
- 🔍 Issue-based session tracking
- 📈 Session continuation support
- 🏥 Health check endpoint
- 📦 Easy CLI installation and usage
Requirements
- Bun >= 1.0.0
- Anthropic API key
- TypeScript >= 5.0
Installation
Global Installation
bun install -g @magnet-ai/magnet-agent-serverDevelopment Installation
git clone <repository-url>
cd magnet-agent-server
bun installUsage
Starting the Server
# Using global installation
magnet-agent-server
# With custom port
magnet-agent-server --port 8080
magnet-agent-server -p 3000
# Show help
magnet-agent-server --help
# Show version
magnet-agent-server --version
# Using npm scripts
bun run dev # Development mode
bun run start # Production mode
# Custom port via environment variable
MAGNET_SERVER_PORT=8080 bun run devThe server will start on http://localhost:3002 by default.
CLI Options
-p, --port <number>- Port to run the server on (default: 3002)-h, --help- Show help message-v, --version- Show version number
Environment Variables
MAGNET_SERVER_PORT- Server port (default: 3002, overridden by CLI --port option)ANTHROPIC_API_KEY- Can be set globally or passed per request
Git Worktree Isolation
By default, magnet-agent-server creates an isolated git worktree for each Claude Code session. This provides several benefits:
Benefits
- 🛡️ Safety: Changes are made in an isolated copy, protecting your main working directory
- 🔬 Experimentation: Try different approaches without affecting your current work
- 📋 Review: Examine all changes before deciding to integrate them
- 🚫 No Conflicts: Avoid merge conflicts with your ongoing work
- 🌿 Branch Creation: Automatically create branches from completed changes
How It Works
- When a Claude Code session starts, the server checks if your
projectRootis a git repository - If yes, it creates a new worktree in the system temp directory with pattern
magnet-{issueId}--{timestamp} - The worktree contains all your current code and uses the same branch as a detached HEAD
- Claude Code operates in this isolated environment
- When the session ends, you can optionally create a branch from the changes
- The worktree is automatically cleaned up after session completion
Configuration
Enable/Disable Options:
- Default: Enabled for all git repositories
- Per-Session: Include
"useWorktree": falsein WebSocket prompt message
Example Worktree Structure
System Temp Directory:
├── magnet-abc123--1703123456789/ # Session 1 worktree
│ ├── src/ # Copy of your code
│ └── package.json # Changes made here
└── magnet-def456--1703123567890/ # Session 2 worktree (if overlapping)Session Management
Issue-Based Tracking
Each Claude Code session is tracked using an issueId parameter, which provides:
- 🔍 Unique Identification: Each session has a distinct identifier
- 📊 Better Tracking: Monitor multiple concurrent sessions
- 🔄 Session Continuation: Continue previous sessions with the same issueId
- 🌿 Branch Management: Create branches tied to specific issues
Session Continuation
The server supports continuing previous Claude Code sessions within the same WebSocket connection:
- First Prompt: Creates a new session with the given issueId
- Subsequent Prompts: Continues the existing session, maintaining context and history
- Max Turns: Configurable limit to prevent infinite loops (default: 50)
- Connection-Based: Sessions only persist within the same WebSocket connection
- Memory-Based: Sessions are lost when the connection closes or server restarts
Saved Sessions
The server now supports persistent session storage, allowing you to retrieve session history even after the connection is closed or the server restarts.
How It Works
- Automatic Logging: All Claude Code messages are automatically saved to disk during the session
- File Storage: Sessions are stored as JSON files in a
sessions/directory - Persistent: Sessions persist across server restarts and connection drops
- Metadata: Each session includes metadata like issueId, branchName, creation time, and status
Session Lifecycle
- Created: When a new session starts, a session file is created with metadata
- Active: Messages are logged in real-time as they stream through the WebSocket
- Completed: Session is marked as completed when the Claude Code session ends normally
- Aborted: Session is marked as aborted when manually terminated or on error
Storage Format
Sessions are stored in sessions/{issueId}.json with the following structure:
{
"metadata": {
"issueId": "issue-123",
"branchName": "feature/fix-bug",
"createdAt": "2024-01-01T00:00:00Z",
"lastUpdatedAt": "2024-01-01T00:30:00Z",
"status": "completed"
},
"messages": [
{
"timestamp": "2024-01-01T00:00:00Z",
"type": "claude_message",
"data": { /* Full Claude Code message */ }
}
]
}Benefits
- 📚 Session History: Review past interactions with Claude Code
- 🔄 Resume Context: Understand what was discussed in previous sessions
- 🐛 Debugging: Trace issues across multiple sessions
- 📊 Analytics: Track usage patterns and session outcomes
- 🔗 Integration: Build workflows that depend on session history
Git Diff Functionality
When using git worktree isolation, you can request diffs to see what changes Claude has made to your code. This is useful for:
- 🔍 Reviewing Changes: See exactly what Claude modified before integrating changes
- 📋 Code Review: Get a clear diff view of all modifications
- 🧪 Testing: Understand the scope of changes before applying them
- 📊 Progress Tracking: Monitor changes as Claude works on your project
How to Request Diffs
Send a get_diff message to the WebSocket connection:
{
"type": "get_diff",
"issueId": "your-issue-id",
"includeUntracked": true
}Parameters:
issueId(required): The issue ID of the active sessionincludeUntracked(optional, default:true): Whether to include untracked files in the response
Diff Response Format
{
"type": "diff",
"diff": "git diff output...",
"stats": {
"filesChanged": 3,
"insertions": 45,
"deletions": 12
},
"hasChanges": true,
"untrackedFiles": ["new-file.js", "temp.txt"],
"worktreePath": "/tmp/magnet-abc123--1703123456789",
"originalBranch": "main",
"issueId": "abc123"
}Response Fields:
diff: The complete git diff output showing all changesstats: Summary statistics of the changeshasChanges: Boolean indicating if any changes were detecteduntrackedFiles: Array of newly created files not yet tracked by gitworktreePath: Path to the isolated worktree directoryoriginalBranch: The original branch the worktree was created fromissueId: The issue ID for the session
Branch Creation
When a session completes with changes, the server can automatically create a branch:
- Automatic Detection: Checks for changes in the worktree
- Smart Naming: Creates branches with pattern
magnet-{issueId}--{timestamp} - Commit Creation: Creates a commit with all changes in the worktree
- Branch Creation: Creates the branch in the original repository
- Optional: Only creates branches if there are actual changes
API Reference
WebSocket Endpoint
Connect to: ws://localhost:3002/claude-code
Message Formats
Sending a Prompt
{
"type": "prompt",
"content": "Your prompt here",
"projectRoot": "/path/to/your/project",
"anthropicApiKey": "your-api-key-here",
"issueId": "unique-issue-identifier",
"useWorktree": true,
"maxTurns": 50,
"promptToAppend": "Additional system prompt"
}Parameters:
content(required): Your prompt/question for ClaudeprojectRoot(required): Path to your project directoryanthropicApiKey(required): Your Anthropic API keyissueId(required): Unique identifier for this session/issueuseWorktree(optional): Override server default for worktree usagemaxTurns(optional): Maximum number of turns for this session (default: 50)promptToAppend(optional): Additional system prompt to append (first turn only)
Aborting a Session
{
"type": "abort",
"issueId": "your-issue-id"
}Requesting a Diff
{
"type": "get_diff",
"issueId": "your-issue-id",
"includeUntracked": true
}Parameters:
issueId(required): The issue ID of the active sessionincludeUntracked(optional): Include untracked files in response (default: true)
Response Types
Connection Established
{
"type": "connected",
"message": "Connected to Claude Code server. Send your prompt to begin.",
"issueId": "system"
}Status Updates
{
"type": "status",
"message": "Processing your request...",
"issueId": "abc123"
}Worktree Created
{
"type": "status",
"message": "Created isolated git worktree: /tmp/magnet-abc123--1703123456789",
"issueId": "abc123",
"worktreePath": "/tmp/magnet-abc123--1703123456789",
"originalProjectRoot": "/path/to/project"
}Worktree Warning/Info
{
"type": "warning",
"message": "Failed to create git worktree, using original project root: error details",
"issueId": "abc123"
}{
"type": "info",
"message": "Git worktree isolation not available (not a git repository)",
"issueId": "abc123"
}Claude Messages
{
"type": "claude_message",
"data": {
"type": "assistant",
"message": {
"id": "msg_123",
"content": [{"type": "text", "text": "Claude's response"}],
"model": "claude-3-5-sonnet-20241022",
"role": "assistant",
"stop_reason": "end_turn",
"usage": {
"input_tokens": 100,
"output_tokens": 200
}
},
"session_id": "session-123"
},
"issueId": "abc123"
}Session Complete
{
"type": "complete",
"message": "Claude Code session completed",
"issueId": "abc123",
"worktreePath": "/tmp/magnet-abc123--1703123456789"
}Session Aborted
{
"type": "aborted",
"message": "Claude Code session aborted successfully",
"issueId": "abc123"
}Abort Failed
{
"type": "abort_failed",
"message": "Failed to abort session: no active session found",
"issueId": "abc123"
}Diff Response
{
"type": "diff",
"diff": "git diff output showing changes...",
"stats": {
"filesChanged": 3,
"insertions": 45,
"deletions": 12
},
"hasChanges": true,
"untrackedFiles": ["new-file.js"],
"worktreePath": "/tmp/magnet-abc123--1703123456789",
"originalBranch": "main",
"issueId": "abc123"
}Errors
{
"type": "error",
"message": "Error description",
"issueId": "abc123"
}HTTP Endpoints
Health Check
GET /healthResponse:
{
"status": "healthy",
"timestamp": "2024-01-01T00:00:00.000Z"
}Version
GET /versionReturns the server version and package name.
Response:
{
"version": "0.1.15",
"name": "@magnet-ai/magnet-agent-server"
}Root
GET /Returns basic server information.
Get Saved Session
GET /session?issueId=<issueId>Retrieves a saved session by issueId.
Parameters:
issueId(required): The unique identifier of the session to retrieve
Response:
{
"metadata": {
"issueId": "abc123",
"branchName": "feature-branch",
"createdAt": "2024-01-01T00:00:00.000Z",
"lastUpdatedAt": "2024-01-01T00:30:00.000Z",
"status": "completed"
},
"messages": [
{
"timestamp": "2024-01-01T00:00:00.000Z",
"type": "claude_message",
"data": {
"type": "assistant",
"message": {
"content": [{"type": "text", "text": "Response from Claude"}],
"role": "assistant"
},
"session_id": "session-123"
}
}
],
"isCurrentlyActive": false
}Status Codes:
200- Session found and returned404- Session not found for the given issueId400- Missing or invalid issueId parameter500- Server error or session logger not initialized
Client Example
JavaScript/TypeScript Client
const ws = new WebSocket('ws://localhost:3002/claude-code');
ws.onopen = () => {
console.log('Connected to Magnet Agent Server');
// Send a prompt
ws.send(JSON.stringify({
type: 'prompt',
content: 'Help me write a function to calculate fibonacci numbers',
projectRoot: '/path/to/my/project',
anthropicApiKey: 'your-api-key',
issueId: 'fibonacci-task-001',
useWorktree: true,
maxTurns: 25,
promptToAppend: 'Please focus on performance and readability'
}));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
switch (message.type) {
case 'connected':
console.log('Server ready');
break;
case 'claude_message':
console.log(`[${message.issueId}] Claude response:`, message.data);
break;
case 'complete':
console.log(`[${message.issueId}] Session completed`);
if (message.worktreePath) {
console.log(`Worktree: ${message.worktreePath}`);
}
break;
case 'error':
console.error(`[${message.issueId}] Error:`, message.message);
break;
case 'diff':
console.log(`[${message.issueId}] Diff:`, message.diff);
console.log(`Files changed: ${message.stats.filesChanged}`);
break;
}
};
// Request diff of current changes
const getDiff = (issueId: string) => {
ws.send(JSON.stringify({
type: 'get_diff',
issueId: issueId,
includeUntracked: true
}));
};
// Abort current session
const abortSession = (issueId: string) => {
ws.send(JSON.stringify({
type: 'abort',
issueId: issueId
}));
};
// Continue a previous session (within the same WebSocket connection)
const continueSession = (issueId: string, newPrompt: string) => {
ws.send(JSON.stringify({
type: 'prompt',
content: newPrompt,
projectRoot: '/path/to/my/project',
anthropicApiKey: 'your-api-key',
issueId: issueId, // Same issueId continues the session
useWorktree: true
}));
};Test Client
The project includes a test client:
bun run test-client
bun run test-diff # Test diff functionality
bun run test-worktree # Test worktree functionality
bun run test-worktree-enabled # Test with worktree enabled
bun run test-worktree-disabled # Test with worktree disabledExample Usage
The project includes an interactive diff example:
# Start the server
bun run dev
# In another terminal, run the diff example
ANTHROPIC_API_KEY=your_key bun examples/diff-example.ts /path/to/your/projectThis example demonstrates:
- Starting a Claude Code session with worktree
- Making changes through Claude
- Requesting periodic diffs to see progress
- Displaying diff results in a readable format
- Creating branches from completed changes
Session Management
- Multiple Claude Code sessions can be active simultaneously (one per issueId per connection)
- Sessions are automatically cleaned up when connections close
- Use the
abortmessage type to cancel active sessions - Each session gets a unique
issueIdfor tracking and continuation - Sessions can be continued by using the same
issueIdin subsequent prompts within the same WebSocket connection - Sessions do not persist across WebSocket reconnections or server restarts
- Worktrees are isolated per session and automatically cleaned up
Development
Running Tests
bun testHealth Check
bun run health
# or
curl http://localhost:3002/healthProject Structure
magnet-agent-server/
├── bin/ # CLI executable
├── lib/ # Library modules
│ ├── git-worktree.ts # Git worktree utilities
│ ├── ClaudeCodeSession.ts # Claude Code session management
│ └── types.ts # Type definitions and schemas
├── index.ts # Main server file
├── test-client.ts # Test client
├── test-worktree.ts # Worktree test client
├── package.json
└── README.mdError Handling
The server handles various error scenarios:
- Invalid JSON: Malformed message handling
- Missing required fields: Validation for prompt, API key, project root, and issueId
- Session conflicts: Manages multiple concurrent sessions per issueId per connection
- API errors: Forwards Claude Code API errors to clients
- Connection drops: Automatic cleanup of resources and worktrees
- Worktree failures: Graceful fallback to original project root
- Branch creation errors: Non-blocking failures with proper logging
Security Considerations
- API keys are passed per request and not stored
- Sessions are isolated per issueId within each WebSocket connection
- Git worktrees provide additional isolation for code changes in system temp directory
- Automatic cleanup prevents resource leaks (including worktrees)
- Input validation for all incoming messages using Zod schemas
- Worktrees are created with restricted permissions
- Session continuation only works within the same WebSocket connection
Performance Considerations
- Worktree Cleanup: Automatic cleanup of old worktrees older than 24 hours
- Session Limits: Configurable
maxTurnsto prevent infinite loops - Memory Management: Proper cleanup of AbortControllers and sessions
- Concurrent Sessions: Support for multiple simultaneous sessions
- Resource Isolation: Each session operates in its own worktree
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
License
MIT
Support
For issues and questions, please open an issue in the repository.
