telegram-bot-mcp/docs/nexus-mcp-bridge-addendum.md
Mikkel Georgsen 494bb510d3 feat: add ingest API + health endpoint, fix bot-to-bot logging
Telegram bots can't see messages from other bots in groups. Added:
- POST /api/ingest - local services log messages into bridge DB
- GET /api/health - status check endpoint
- Fixed post_init not running (manual init lifecycle)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 08:39:44 +00:00

219 lines
6.2 KiB
Markdown

# MCP Bridge Spec — Addendum: Session Tracking & Per-Chat Context
Add these sections to the existing nexus-mcp-bridge-spec.md
---
## 11. Session Tracking
Claude Code sessions are ephemeral. If a session dies and restarts,
context is lost. The bridge must track session state to detect this.
### Agent-side: session file
On startup, each Claude Code session writes its session ID to a
known file in its working directory:
```python
# Agent writes on boot (in CLAUDE.md startup hook or .bashrc)
import uuid, json
from datetime import datetime
session = {
"id": str(uuid.uuid4()),
"started_at": datetime.utcnow().isoformat(),
"chat_id": TELEGRAM_CHAT_ID, # Which chat this session serves
"project": "homelab", # or "felt", "sentry", etc.
}
with open(os.path.expanduser("~/.claude-session"), "w") as f:
json.dump(session, f)
```
### Bridge-side: session awareness
The bridge reads the session file on every poll. If the session ID
changes between polls, it means the agent restarted:
```python
@dataclass
class SessionState:
session_id: str
started_at: datetime
chat_id: str
project: str
last_seen_id: str | None = None # Previous session for comparison
def check_session(self) -> tuple[SessionState, bool]:
"""Returns (current_session, has_restarted)."""
with open(os.path.expanduser("~/.claude-session")) as f:
data = json.load(f)
current = SessionState(**data)
restarted = (self.last_seen_id is not None
and self.last_seen_id != current.session_id)
self.last_seen_id = current.session_id
return current, restarted
```
### Restart notification
When a restart is detected, pull_updates includes a warning:
```json
{
"updates": [
{
"message_id": null,
"content": "⚠️ Agent session restarted. Previous context lost. Session: abc-123 → def-456",
"created_at": "2026-03-30T08:15:00Z",
"type": "session_restart"
}
]
}
```
### Persistent task state
The agent persists its current task state to disk so it can resume
after a restart instead of starting blind:
```
~/.claude-session-state.json
{
"session_id": "abc-123",
"current_task": "Fix nexus.mg DNS record",
"current_task_id": "msg-456",
"last_completed_step": 2,
"pending_messages": ["msg-789"],
"updated_at": "2026-03-30T01:15:00Z"
}
```
On restart, the agent reads this file, announces what it was doing,
and asks the owner whether to resume or start fresh.
---
## 12. Per-Chat Context Isolation
Each Telegram group chat maps to a separate Claude Code session with
its own context window, working directory, skills, and knowledge files.
### Chat → Session mapping
```python
CHAT_SESSIONS = {
"homelab": {
"chat_id": "<telegram-group-id-homelab>",
"workdir": "/home/mikkel/homelab",
"claude_md": "/home/mikkel/homelab/.claude/CLAUDE.md",
"session_file": "/home/mikkel/homelab/.claude-session",
},
"felt": {
"chat_id": "<telegram-group-id-felt>",
"workdir": "/home/mikkel/felt",
"claude_md": "/home/mikkel/felt/.claude/CLAUDE.md",
"session_file": "/home/mikkel/felt/.claude-session",
},
"sentry": {
"chat_id": "<telegram-group-id-sentry>",
"workdir": "/home/mikkel/sentry",
"claude_md": "/home/mikkel/sentry/.claude/CLAUDE.md",
"session_file": "/home/mikkel/sentry/.claude-session",
},
}
```
### Queue is per-chat
Each chat has its own queue. Messages dispatched to "homelab" don't
appear in the "felt" queue. The MCP tools gain a `target` parameter:
```
send_message(target="homelab", message="Fix the DNS record")
send_message(target="felt", message="Update the Go backend")
pull_updates(target="homelab")
pull_updates(target="felt")
queue_status() # Returns status for ALL targets
```
### Session lifecycle per chat
Each Claude Code session is started with its project-specific context:
```bash
# Homelab session
cd ~/homelab && claude --channels plugin:telegram@claude-plugins-official
# Felt session
cd ~/felt && claude --channels plugin:telegram@claude-plugins-official
# Sentry session
cd ~/sentry && claude --channels plugin:telegram@claude-plugins-official
```
Each session reads its own CLAUDE.md, has its own skills directory,
and only sees messages from its own Telegram group. Context stays
clean and focused.
### The bridge routes by target
```python
async def send_message(target: str, message: str) -> str:
session_config = CHAT_SESSIONS.get(target)
if not session_config:
return json.dumps({"queued": False, "reason": f"unknown target: {target}"})
msg = queues[target].enqueue(message)
if msg is None:
return json.dumps({"queued": False, "reason": "duplicate"})
await transport.send(
chat_id=session_config["chat_id"],
message=message,
)
return json.dumps({"queued": True, "id": msg.id, "target": target})
```
---
## 13. Updated MCP Tools Summary
| Tool | Parameters | Returns |
|------|-----------|---------|
| `send_message` | `target`, `message` | `{queued, id, target}` or `{reason}` |
| `pull_updates` | `target`, `since?` | `{updates[], session_restart?}` |
| `queue_status` | — | Per-target: `{pending, in_progress, current_task, session_id}` |
---
## 14. Agent System Prompt Addition (for CLAUDE.md)
```markdown
## Session Tracking
On startup:
1. Generate a session UUID
2. Write it to ~/.claude-session with timestamp and project name
3. Read ~/.claude-session-state.json if it exists
4. If resuming from a crash, announce:
"[Session restarted. Previous session was working on: <task>.
Resume or start fresh?]"
5. Persist task state to ~/.claude-session-state.json after every step
## Message Queue Awareness
You receive messages from multiple sources:
- Direct Telegram messages from Mikkel
- Queued tasks from claude.ai via the MCP bridge
When you receive a new message while working on a task:
1. Announce: "[New message received: <summary>]"
2. Assess urgency — URGENT or STOP = halt immediately
3. If non-urgent, acknowledge and continue current task
4. Address queued messages in order after current task completes
Always announce which message you're responding to:
"[Responding to: <first line of message>]"
```