beeper-mcp
CRITICAL: TOKENS PAY RENT
Every output token must produce actionable value. Violations:
- NO PASSIVE SUMMARIES - Regurgitating conversation content without action items, code, or artifacts is FORBIDDEN
- NO AGREEMENT WITHOUT IMPLEMENTATION - "I agree with X" must be followed by code/file/commit implementing X
- NO RHETORICAL QUESTIONS - Ask only when you cannot proceed without the answer
- NO PRAISE/VALIDATION - Skip "great question" / "you're right" - proceed to work
When reviewing message history:
- Extract ACTION ITEMS → create files, send messages, write code
- Extract DECISIONS → update configs, create artifacts documenting the decision
- Extract BLOCKERS → file issues, send follow-up messages
- NEVER just summarize what was discussed
Enforcement: If output contains summary without artifact, STOP and create the artifact first.
Beeper MCP Skill
Access all messaging networks through Beeper's unified interface.
Quick Start
# Search for a chat
mcp__beeper__search "contact name"
# Send a message
mcp__beeper__send_message chatID="..." text="Hello!"
# List recent chats
mcp__beeper__search_chats limit=10
Core Tools
| Tool | Purpose |
|---|---|
search |
Find chats, groups, or people by name |
search_chats |
List/filter chats by type, inbox, activity |
search_messages |
Find messages by content (literal match) |
get_chat |
Get chat details and participants |
list_messages |
Get messages from a specific chat |
send_message |
Send text message to a chat |
archive_chat |
Archive/unarchive a chat |
set_chat_reminder |
Set reminder for a chat |
focus_app |
Open Beeper Desktop to specific chat |
Search Guidelines
CRITICAL: Queries are LITERAL WORD MATCHING, not semantic search.
- RIGHT:
query="dinner"orquery="flight" - WRONG:
query="dinner plans tonight"orquery="travel arrangements"
Multiple words = ALL must match. Use single keywords.
User Identity Clarification
IMPORTANT: Beeper/Matrix has TWO identifiers per user:
- Matrix userID:
@username:beeper.com(permanent, searchable) - Display name: User-chosen name (can differ from userID)
Example: User @jsmith:beeper.com may have display name "John Smith"
When search finds a match:
- The search matched the userID OR display name
- Chat participant lists show display names, not userIDs
- To see userIDs, use
list_messagesand checksenderIDfield
When reporting search results:
- Cross-reference
list_messagesto mapsenderID↔senderName - Report as "username (displays as 'Display Name')" for clarity
Resource-Aware Message Processing
CRITICAL: Always work backwards from most recent messages to avoid:
- Re-processing already-handled tasks
- Repeating fixed issues
- Heap exhaustion from loading too much history
Default Workflow (Backwards-First)
// 1. Start with most recent (no cursor = newest first)
const recent = await list_messages(chatID, { limit: 20 });
// 2. Check if already processed (use DuckDB tracking)
const unprocessed = recent.items.filter(msg => !isProcessed(msg.id));
// 3. Process only new messages
for (const msg of unprocessed) {
await processMessage(msg);
markProcessed(msg.id);
}
// 4. If need more history, paginate backwards
if (needsMoreContext) {
const older = await list_messages(chatID, {
cursor: recent.cursor,
direction: 'before',
limit: 20
});
}
DuckDB Tracking Schema
CREATE TABLE IF NOT EXISTS beeper_processed_messages (
message_id VARCHAR PRIMARY KEY,
chat_id VARCHAR,
processed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
task_extracted BOOLEAN,
issue_status VARCHAR -- 'open', 'fixed', 'duplicate'
);
CREATE INDEX idx_msg_chat ON beeper_processed_messages(chat_id, processed_at DESC);
Check Before Processing
function isAlreadyFixed(messageText) {
// Query DuckDB for similar fixed issues
const similar = db.query(`
SELECT issue_id, description
FROM fixed_issues
WHERE description % ? -- Full-text similarity
LIMIT 1
`, [messageText]);
if (similar.length > 0) {
console.warn(`⚠️ Similar issue already fixed: ${similar[0].description}`);
return true;
}
return false;
}
Workflow Patterns
Find and Message Someone
search "person name"- Get chatID- Verify identity:
list_messages chatID="..." limit=5- Check recent messages ONLY send_message chatID="..." text="..."
Identify Users in a Chat
list_messages chatID="..." limit=10- Get RECENT messages only (not entire history)- Map userID ↔ displayName from the subset
Search Message History (Resource-Aware)
search_chatsto get chatIDs of relevant chatssearch_messages chatIDs=[...] query="keyword" limit=20 dateAfter="2026-01-08T00:00:00Z"- Never omit dateAfter - prevents loading entire chat history
Monitor Unread
search_chats unreadOnly=true inbox="primary" limit=10
Filter by Network
Use accountIDs parameter after getting accounts via get_accounts.
Message Formatting
Messages support Markdown. Use sparingly for clarity.
Chat Types
single- Direct messages (1:1)group- Group chatsany- All types
Inbox Filters
primary- Non-archived, non-low-prioritylow-priority- Low priority inboxarchive- Archived chats
Resource-Aware Random Walk Pattern
NEVER pull full message history into context. Instead:
1. Query in DuckDB First
-- Store messages incrementally, query locally
CREATE TABLE IF NOT EXISTS beeper_messages (
id VARCHAR PRIMARY KEY,
chat_id VARCHAR,
sender_id VARCHAR,
sender_name VARCHAR,
text TEXT,
timestamp TIMESTAMPTZ,
processed BOOLEAN DEFAULT FALSE
);
-- Sample recent messages via random walk
SELECT * FROM beeper_messages
WHERE chat_id = ?
ORDER BY RANDOM() -- Ergodic sampling
LIMIT 5;
2. TreeSitter for Structure Extraction
# Extract code blocks from messages without loading full text
tree-sitter parse --scope source.markdown message.md \
| grep -E "(fenced_code_block|code_span)"
3. Triadic Walker Pattern
MINUS (-1): Validate message exists in DuckDB before fetching
ERGODIC (0): Random walk sample from local cache
PLUS (+1): Fetch ONLY if not in cache, with strict limit
4. Context Budget Enforcement
CONTEXT_BUDGET = 10000 # chars
current_context = 0
def safe_fetch(chat_id, limit=5):
# Check DuckDB cache first
cached = db.query("SELECT * FROM beeper_messages WHERE chat_id = ? LIMIT ?", chat_id, limit)
if len(cached) >= limit:
return cached # Zero network cost
# Fetch only missing, with limit
remaining = limit - len(cached)
fresh = mcp__beeper__list_messages(chatID=chat_id, limit=remaining)
# Enforce budget
for msg in fresh.items:
msg_size = len(msg.get('text', ''))
if current_context + msg_size > CONTEXT_BUDGET:
break
current_context += msg_size
db.insert("beeper_messages", msg)
return db.query("SELECT * FROM beeper_messages WHERE chat_id = ? LIMIT ?", chat_id, limit)
5. SICP Lazy Evaluation
;; Don't fetch until actually needed
(define (beeper-messages chat-id)
(delay
(mcp__beeper__list_messages chatID: chat-id limit: 5)))
;; Only force when required
(define (get-latest-sender chat-id)
(let ((msgs (force (beeper-messages chat-id))))
(cdar msgs))) ; Just sender from first message
GF(3) Integration
This skill operates as ERGODIC (0) in triadic compositions:
- Coordinates message flow between networks
- Synthesizes cross-platform conversations
- Neutral hub for communication triads
Triadic Fetch Strategy
| Trit | Role | Beeper Action |
|---|---|---|
| MINUS (-1) | Validator | Check DuckDB cache, reject if stale |
| ERGODIC (0) | Coordinator | Random walk sample, enforce budget |
| PLUS (+1) | Generator | Fetch fresh data, strict limit param |
Conversation Branch Awareness (Higher-Order Wiring)
Track conversation threads as wiring diagrams - morphisms between topic states.
Branch Detection Schema
CREATE TABLE IF NOT EXISTS beeper_conversation_branches (
branch_id VARCHAR PRIMARY KEY,
chat_id VARCHAR NOT NULL,
parent_branch_id VARCHAR, -- NULL for root
topic VARCHAR NOT NULL,
first_message_id VARCHAR,
last_message_id VARCHAR,
status VARCHAR DEFAULT 'open', -- 'open', 'resolved', 'merged', 'stale'
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
resolved_at TIMESTAMP,
FOREIGN KEY (parent_branch_id) REFERENCES beeper_conversation_branches(branch_id)
);
CREATE TABLE IF NOT EXISTS beeper_branch_transitions (
from_branch VARCHAR,
to_branch VARCHAR,
transition_type VARCHAR, -- 'fork', 'merge', 'abandon', 'resolve'
message_id VARCHAR, -- message that triggered transition
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (from_branch, to_branch, message_id)
);
Wiring Diagram Structure
Conversation as Category:
- Objects: Topic states (branches)
- Morphisms: Message sequences that transform one topic to another
- Composition: Thread continuation
┌─────────────────┐
│ patents (open) │◄──── current focus
└────────┬────────┘
│ fork @ msg:838941332
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────┐ ┌───────────┐ ┌──────────┐
│ GF(3) │ │bisimulation│ │ toad OOM │
│resolved│ │ resolved │ │ open │
└────────┘ └───────────┘ └──────────┘
Branch Detection Heuristics
def detect_branch(messages: list) -> list[Branch]:
branches = []
current_topic = None
for msg in messages:
# Topic markers
if msg.text.startswith('**Re:') or msg.text.startswith('Re:'):
# Explicit reply = potential branch
topic = extract_topic(msg.text)
if topic != current_topic:
branches.append(Branch(
topic=topic,
fork_message=msg.id,
parent=current_topic
))
# Numbered lists often indicate parallel threads
if re.match(r'^\d+\.', msg.text):
items = extract_numbered_items(msg.text)
for item in items:
branches.append(Branch(topic=item, parent=current_topic))
# Questions create potential branches
if msg.text.strip().endswith('?'):
branches.append(Branch(
topic=f"Q: {msg.text[:50]}",
status='awaiting_response'
))
return branches
Zigger Chat Branch State
Track active branches in zigger conversation:
-- Query current branch state for a chat
SELECT
b.branch_id,
b.topic,
b.status,
COUNT(t.to_branch) as child_count
FROM beeper_conversation_branches b
LEFT JOIN beeper_branch_transitions t ON b.branch_id = t.from_branch
WHERE b.chat_id = '!NhltGRLZWLUeHEBiFT:beeper.com' -- zigger
GROUP BY b.branch_id
ORDER BY b.created_at DESC;
Before Responding: Check Branch Context
def get_branch_context(chat_id: str) -> dict:
"""Always call before responding to understand conversation topology."""
# Get open branches
open_branches = db.query("""
SELECT topic, status, first_message_id
FROM beeper_conversation_branches
WHERE chat_id = ? AND status = 'open'
""", chat_id)
# Get unresolved questions
questions = db.query("""
SELECT topic FROM beeper_conversation_branches
WHERE chat_id = ? AND topic LIKE 'Q:%' AND status = 'awaiting_response'
""", chat_id)
return {
'open_branches': open_branches,
'unanswered_questions': questions,
'should_address': questions[0] if questions else open_branches[0]
}
Wiring Composition Rules
- Fork: One message spawns multiple topics → create child branches
- Merge: Response addresses multiple branches → mark as merged
- Resolve: Explicit closure ("done", "fixed", "shipped") → mark resolved
- Abandon: No activity for 7 days → mark stale
Integration with Tokens Pay Rent
When reviewing messages, branch tracking prevents:
- Re-answering resolved questions
- Missing open threads
- Losing context on forked discussions
Update branch state as side effect of every beeper interaction.
MCP Server Config
{
"beeper": {
"command": "/bin/sh",
"args": [
"-c",
"BEEPER_ACCESS_TOKEN=$(/Users/alice/.cargo/bin/fnox get BEEPER_ACCESS_TOKEN --age-key-file /Users/alice/.age/key.txt) exec npx -y @beeper/desktop-mcp"
]
}
}
Requires:
fnoxat/Users/alice/.cargo/bin/fnox- Age key at
/Users/alice/.age/key.txt npxin PATH- Beeper Desktop running
More from plurigrid/asi
academic-research
Search academic papers across arXiv, PubMed, Semantic Scholar, bioRxiv, medRxiv, Google Scholar, and more. Get BibTeX citations, download PDFs, analyze citation networks. Use for literature reviews, finding papers, and academic research.
49wev-tesseract
WEV Tesseract Skill
33tree-sitter
AST-based code analysis using tree-sitter. Use for parsing code structure, extracting symbols, finding patterns with tree-sitter queries, analyzing complexity, and understanding code architecture. Supports Python, JavaScript, TypeScript, Go, Rust, C, C++, Swift, Java, Kotlin, Julia, and more.
21alife
Comprehensive Artificial Life skill combining ALIFE2025 proceedings, classic texts (Axelrod, Epstein-Axtell), ALIEN simulation, Lenia, NCA, swarm intelligence, and evolutionary computation. 337 pages extracted, 80+ papers, 153 figures.
16reverse-engineering
Reverse Engineering Skill
16bdd-mathematical-verification
BDD-Driven Mathematical Content Verification Skill
16