Instrucciones de origen · Vista previa de solo lectura
name
phantom-ai-coworker
description
AI co-worker agent with its own computer, persistent memory, self-evolution, MCP server, and Slack/email identity built on Claude Agent SDK
triggers
["set up phantom ai agent","configure phantom co-worker","phantom self-evolving agent","phantom mcp server setup","phantom slack bot agent","build phantom ai coworker","phantom persistent memory agent","deploy phantom on docker"]
Phantom is an AI co-worker that runs on its own dedicated machine. Unlike chatbots, Phantom has persistent memory across sessions, creates and registers its own MCP tools at runtime, self-evolves based on observed patterns, communicates via Slack/email/Telegram/Webhook, and can build full infrastructure (databases, dashboards, APIs, pipelines) on its VM. Built on the Claude Agent SDK with TypeScript/Bun.
# Download compose file and env template
curl -fsSL https://raw.githubusercontent.com/ghostwright/phantom/main/docker-compose.user.yaml -o docker-compose.yaml
curl -fsSL https://raw.githubusercontent.com/ghostwright/phantom/main/.env.example -o .env# Edit .env with your credentials (see Configuration section)
nano .env# Start Phantom (includes Qdrant + Ollama)
docker compose up -d
# Check health
curl http://localhost:3100/health
# View logs
docker compose logs -f phantom
From Source (Bun)
git clone https://github.com/ghostwright/phantom.git
cd phantom
# Install dependencies
bun install
# Copy envcp .env.example .env# Edit .env# Start Qdrant (required for memory)
docker run -d -p 6333:6333 qdrant/qdrant
# Start Phantom
bun run start
# Development mode with hot reload
bun run dev
Configuration (.env)
# === Required ===
ANTHROPIC_API_KEY= # Your Anthropic API key# === Slack (required for Slack channel) ===
SLACK_BOT_TOKEN=xoxb- # Bot OAuth token
SLACK_APP_TOKEN=xapp- # App-level token (socket mode)
SLACK_SIGNING_SECRET= # Signing secret
OWNER_SLACK_USER_ID=U0XXXXXXXXX # Your Slack user ID# === Memory (Qdrant) ===
QDRANT_URL=http://localhost:6333 # Qdrant vector DB URL
QDRANT_API_KEY= # Optional, for cloud Qdrant
OLLAMA_URL=http://localhost:11434 # Ollama for embeddings# === Email (optional) ===
RESEND_API_KEY= # For email sending via Resend
PHANTOM_EMAIL=phantom@yourdomain # Phantom's email address# === Telegram (optional) ===
TELEGRAM_BOT_TOKEN= # BotFather token# === Infrastructure ===
PHANTOM_VM_DOMAIN= # Public domain for served assets
PHANTOM_PORT=3100 # HTTP port (default 3100)# === Self-Evolution ===
EVOLUTION_VALIDATION_MODEL=claude-3-5-sonnet-20241022 # Separate model for validation
EVOLUTION_ENABLED=true# === Credentials Vault ===
CREDENTIAL_ENCRYPTION_KEY= # AES-256-GCM key (auto-generated if empty)
Key Commands
# Docker operations
docker compose up -d # Start all services
docker compose down # Stop all services
docker compose logs -f phantom # Stream logs
docker compose pull # Update to latest image# Bun development
bun run start # Production start
bun run dev # Dev mode with watch
bun run test# Run test suite
bun run build # Build TypeScript# Health checks
curl http://localhost:3100/health
curl http://localhost:3100/status
# MCP server endpoint
curl http://localhost:3100/mcp
Core Concepts & Code Examples
1. Memory System (Qdrant + Embeddings)
Phantom stores memories as vector embeddings for semantic recall across sessions.
Phantom creates MCP tools at runtime that persist across restarts.
// Pattern: registering a dynamically created toolinterfacePhantomTool {
name: string;
description: string;
inputSchema: Record<string, unknown>;
handler: string; // serialized or endpoint URL
}
// Phantom internally registers tools like thisasyncfunctionregisterDynamicTool(tool: PhantomTool) {
// Store tool definition in persistent storageawaitstoreMemory(JSON.stringify(tool), {
type: 'mcp_tool',
toolName: tool.name,
});
// Register with MCP server at runtime
mcpServer.tool(tool.name, tool.description, tool.inputSchema, async (args) => {
returnawaitexecuteToolHandler(tool.handler, args);
});
}
// MCP server setup (how Phantom exposes tools to Claude Code)import { McpServer } from'@modelcontextprotocol/sdk/server/mcp.js';
const mcpServer = newMcpServer({
name: 'phantom',
version: '0.18.1',
});
// Connect Claude Code to Phantom's MCP server:// In claude_desktop_config.json or .cursor/mcp.json:// {// "mcpServers": {// "phantom": {// "url": "http://your-phantom-vm:3100/mcp"// }// }// }
3. Slack Channel Integration
// How Phantom handles Slack messagesimport { App } from'@slack/bolt';
const slack = newApp({
token: process.env.SLACK_BOT_TOKEN,
appToken: process.env.SLACK_APP_TOKEN,
socketMode: true,
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
// Phantom listens for direct messages and mentions
slack.event('message', async ({ event, say }) => {
if (event.subtype) return; // Skip bot messages, editsconst userMessage = (event asany).text;
const userId = (event asany).user;
// Recall relevant context from memoryconst memories = awaitrecallMemories(userMessage);
// Run Claude agent with memory contextconst response = awaitrunPhantomAgent({
message: userMessage,
userId,
memories,
channel: (event asany).channel,
});
awaitsay({ text: response, thread_ts: (event asany).ts });
});
// Phantom DMs you when readyasyncfunctionnotifyOwnerReady() {
await slack.client.chat.postMessage({
channel: process.env.OWNER_SLACK_USER_ID!,
text: "👻 Phantom is online and ready.",
});
}
4. Claude Agent SDK Integration
// Core agent loop using Anthropic Agent SDKimportAnthropicfrom'@anthropic-ai/sdk';
const anthropic = newAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
asyncfunctionrunPhantomAgent({
message,
userId,
memories,
channel,
}: PhantomAgentInput) {
const systemPrompt = buildSystemPrompt(memories);
// Agentic loop with tool useconst response = await anthropic.messages.create({
model: 'claude-opus-4-5',
max_tokens: 8096,
system: systemPrompt,
messages: [{ role: 'user', content: message }],
tools: awaitgetAvailableTools(), // includes dynamic MCP tools
});
// Handle tool calls in loopif (response.stop_reason === 'tool_use') {
returnawaithandleToolCalls(response, message, userId);
}
// Store this interaction as memoryawaitstoreMemory(`User ${userId} asked: ${message}. I responded: ${response.content}`, {
type: 'interaction',
userId,
channel,
});
returnextractTextContent(response.content);
}
functionbuildSystemPrompt(memories: string[]): string {
return`You are Phantom, an AI co-worker with your own computer.
You have persistent memory and can build infrastructure.
Relevant memories from past sessions:
${memories.map((m, i) => `${i + 1}. ${m}`).join('\n')}
You have access to your VM, can install software, build tools,
serve web pages on ${process.env.PHANTOM_VM_DOMAIN}, and register
new capabilities for yourself.`;
}
5. Secure Credential Collection
Phantom collects credentials via encrypted forms, never plain text.
Once Phantom is running, connect Claude Code to use all of Phantom's registered tools:
// ~/.claude/claude_desktop_config.json or .cursor/mcp.json{"mcpServers":{"phantom":{"url":"http://your-phantom-vm:3100/mcp"}}}
Or via CLI:
# Claude Code CLI
claude mcp add phantom --url http://your-phantom-vm:3100/mcp
# Verify connection
claude mcp list
Docker Compose Structure
# docker-compose.yaml (production user config)services:phantom:image:ghostwright/phantom:latestports:-"3100:3100"environment:-ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}-SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN}-SLACK_APP_TOKEN=${SLACK_APP_TOKEN}-SLACK_SIGNING_SECRET=${SLACK_SIGNING_SECRET}-OWNER_SLACK_USER_ID=${OWNER_SLACK_USER_ID}-QDRANT_URL=http://qdrant:6333-OLLAMA_URL=http://ollama:11434-PHANTOM_VM_DOMAIN=${PHANTOM_VM_DOMAIN}-RESEND_API_KEY=${RESEND_API_KEY}volumes:-phantom_data:/var/phantom-/var/run/docker.sock:/var/run/docker.sock# For Docker-in-Dockerdepends_on:-qdrant-ollamarestart:unless-stoppedqdrant:image:qdrant/qdrant:latestvolumes:-qdrant_data:/qdrant/storagerestart:unless-stoppedollama:image:ollama/ollama:latestvolumes:-ollama_data:/root/.ollamarestart:unless-stoppedvolumes:phantom_data:qdrant_data:ollama_data:
Install to workspace → copy Bot Token (xoxb-) to SLACK_BOT_TOKEN
Generate App-Level Token with connections:write → copy to SLACK_APP_TOKEN
Copy Signing Secret → SLACK_SIGNING_SECRET
Get your user ID: In Slack, click your profile → copy Member ID → OWNER_SLACK_USER_ID
Common Patterns
Asking Phantom to Build a Tool
In Slack:
@phantom Create an MCP tool that queries our internal metrics API at
https://metrics.internal/api/v2. It should accept a metric_name and
time_range parameter and return JSON.
Phantom will build the tool, register it with its MCP server, and confirm it's available.
Scheduling Recurring Tasks
@phantom Every weekday at 9am, check our GitHub repo myorg/myrepo for
open PRs older than 3 days and post a summary to #engineering
Requesting a Dashboard
@phantom Build a dashboard showing our deployment frequency over the
last 30 days. Make it shareable with the team.
Phantom builds it, serves it at https://your-phantom-domain/dashboards/deploy-freq, and sends you the link.
Memory Queries
@phantom What did I tell you about our database architecture last week?
@phantom What tools have you built for me so far?
@phantom Summarize everything you know about Project X
Troubleshooting
Phantom not starting
# Check all services are healthy
docker compose ps
# Qdrant must be ready before Phantom
docker compose logs qdrant
curl http://localhost:6333/health
# Ollama must pull embedding model
docker compose logs ollama
Verify SLACK_APP_TOKEN starts with xapp- (not xoxb-)
Socket mode must be enabled in Slack App settings
Check bot is invited to channels: /invite @Phantom
Verify OWNER_SLACK_USER_ID is correct (not display name, actual ID)
MCP tools not appearing in Claude Code
# Verify MCP server is running
curl http://localhost:3100/mcp
# Check tool registration
curl http://localhost:3100/mcp/tools
# Restart Claude Code after adding MCP config
Evolution not triggering
# Check env varecho$EVOLUTION_ENABLED# should be "true"# Verify validation model is setecho$EVOLUTION_VALIDATION_MODEL# Check logs for evolution cycle
docker compose logs phantom | grep -i evolv
Docker socket permission denied
# Add phantom user to docker group, or run with:sudo docker compose up -d
# Or add to docker-compose.yaml:# user: root
API Reference
Endpoint
Method
Description
/health
GET
Health check
/status
GET
Agent status + uptime
/mcp
GET/POST
MCP server endpoint
/mcp/tools
GET
List registered tools
/webhook/message
POST
Send message to agent
/credentials/:token
GET/POST
Secure credential form
/public/:slug
GET
Served static assets
Version History & Rollback
# Phantom versions its own evolution# View evolution history in logs
docker compose logs phantom | grep -i "evolved"# Pin to specific version# Edit docker-compose.yaml:# image: ghostwright/phantom:0.18.1# Roll back
docker compose down
# Change image tag in compose file
docker compose up -d