beeper-mcp

Unified messaging via Beeper Desktop MCP. Search chats, send messages, manage conversations across all networks (iMessage, WhatsApp, Signal, Telegram, Discord, Slack, etc.)

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "beeper-mcp" with this command: npx skills add plurigrid/asi/plurigrid-asi-beeper-mcp

CRITICAL: TOKENS PAY RENT

Every output token must produce actionable value. Violations:

  1. NO PASSIVE SUMMARIES - Regurgitating conversation content without action items, code, or artifacts is FORBIDDEN
  2. NO AGREEMENT WITHOUT IMPLEMENTATION - "I agree with X" must be followed by code/file/commit implementing X
  3. NO RHETORICAL QUESTIONS - Ask only when you cannot proceed without the answer
  4. 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

ToolPurpose
searchFind chats, groups, or people by name
search_chatsList/filter chats by type, inbox, activity
search_messagesFind messages by content (literal match)
get_chatGet chat details and participants
list_messagesGet messages from a specific chat
send_messageSend text message to a chat
archive_chatArchive/unarchive a chat
set_chat_reminderSet reminder for a chat
focus_appOpen Beeper Desktop to specific chat

Search Guidelines

CRITICAL: Queries are LITERAL WORD MATCHING, not semantic search.

  • RIGHT: query="dinner" or query="flight"
  • WRONG: query="dinner plans tonight" or query="travel arrangements"

Multiple words = ALL must match. Use single keywords.

User Identity Clarification

IMPORTANT: Beeper/Matrix has TWO identifiers per user:

  1. Matrix userID: @username:beeper.com (permanent, searchable)
  2. 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_messages and check senderID field

When reporting search results:

  • Cross-reference list_messages to map senderIDsenderName
  • 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

  1. search "person name" - Get chatID
  2. Verify identity: list_messages chatID="..." limit=5 - Check recent messages ONLY
  3. send_message chatID="..." text="..."

Identify Users in a Chat

  1. list_messages chatID="..." limit=10 - Get RECENT messages only (not entire history)
  2. Map userID ↔ displayName from the subset

Search Message History (Resource-Aware)

  1. search_chats to get chatIDs of relevant chats
  2. search_messages chatIDs=[...] query="keyword" limit=20 dateAfter="2026-01-08T00:00:00Z"
  3. 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 chats
  • any - All types

Inbox Filters

  • primary - Non-archived, non-low-priority
  • low-priority - Low priority inbox
  • archive - 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

TritRoleBeeper Action
MINUS (-1)ValidatorCheck DuckDB cache, reject if stale
ERGODIC (0)CoordinatorRandom walk sample, enforce budget
PLUS (+1)GeneratorFetch 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

  1. Fork: One message spawns multiple topics → create child branches
  2. Merge: Response addresses multiple branches → mark as merged
  3. Resolve: Explicit closure ("done", "fixed", "shipped") → mark resolved
  4. 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:

  • fnox at /Users/alice/.cargo/bin/fnox
  • Age key at /Users/alice/.age/key.txt
  • npx in PATH
  • Beeper Desktop running

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

alife

No summary provided by upstream source.

Repository SourceNeeds Review
General

asi-integrated

No summary provided by upstream source.

Repository SourceNeeds Review
General

bdd-mathematical-verification

No summary provided by upstream source.

Repository SourceNeeds Review
beeper-mcp | V50.AI