"""FastMCP server exposing bridge tools to claude.ai.""" import json from datetime import datetime, timezone from fastmcp import FastMCP from .db import Database from .config import get_group_chat_id # Will be initialized in __main__ with shared db instance db: Database | None = None mcp = FastMCP( name="homelab-bridge", instructions=( "This MCP server bridges claude.ai to a homelab Telegram group chat. " "Use pull_updates to read conversation history (supports cursor-based pagination). " "Use send_message to post messages to the group (attributed as [claude.ai]). " "Use queue_status for a quick summary." ), ) def init(database: Database): """Set the shared database instance.""" global db db = database @mcp.tool() def send_message(message: str) -> str: """Send a message to the homelab Telegram group chat. The message will be posted with [claude.ai] attribution so participants know the message came from claude.ai. Args: message: The text to send to the group chat. """ chat_id = get_group_chat_id() outbound_id = db.queue_outbound(chat_id, message) return json.dumps({"sent": True, "id": outbound_id}) @mcp.tool() def pull_updates(since_id: int = 0, since: str | None = None, limit: int = 50) -> str: """Pull conversation messages from the Telegram group. Returns messages from all participants (Mikkel, homelab bot, MCP bot). Supports cursor-based pagination: use the returned 'cursor' value as 'since_id' in the next call to get only new messages. Args: since_id: Return messages with id > this value. Use cursor from previous response. since: ISO 8601 timestamp. Alternative to since_id — returns messages after this time. limit: Maximum number of messages to return (default 50, max 200). """ limit = min(limit, 200) if since: messages = db.get_messages_since_timestamp(since, limit) else: messages = db.get_messages_since_id(since_id, limit) # Enrich with attachment info for msg in messages: if msg["has_attachment"]: msg["attachments"] = db.get_attachments_for_message(msg["id"]) else: msg["attachments"] = [] del msg["has_attachment"] cursor = messages[-1]["id"] if messages else since_id return json.dumps({ "messages": messages, "cursor": cursor, "count": len(messages), }) @mcp.tool() def queue_status() -> str: """Get current status of the bridge. Returns message counts, last activity, and pending outbound messages. """ status = db.get_status() return json.dumps(status)