DEV Community

johann
johann

Posted on

How to Coordinate Role-Based CLI Seats & Session Handovers for AI Agents: A Governance MCP Server

How to Coordinate Role-Based CLI Seats & Session Handovers for AI Agents: A Governance MCP Server in Python

In 2026, multi-agent software engineering swarms (Claude Desktop, Cursor, OpenAI Codex, Roo-Code, AutoGen) frequently operate directly on local repositories and shell environments. Teams deploy autonomous agents to build features, fix bugs, run test suites, and execute deployments.

However, operating multiple agent sessions in the same terminal environment quickly exposes three critical concurrency and governance hazards:

  1. Race Conditions & Write Collisions: When two agent sessions attempt to run git commands, build artifacts, or modify configuration files simultaneously, uncoordinated processes overwrite each other, producing corrupt states or broken builds.
  2. Session Handover Amnesia: When an agent session terminates—whether reaching its context window limit, hitting a timeout, or concluding its turn—the successor agent starts blind. Without an auditable session handover ledger, the new agent doesn't know what commands ran, what uncommitted changes exist, or what steps remain pending.
  3. Out-of-Domain Execution: An agent tasked with CLI scaffolding (domain:cli) accidentally runs destructive database or cloud infrastructure commands without boundary enforcement.

To eliminate these hazards, we built CliSeat: an open Model Context Protocol (MCP) server in Python that enforces role-based single-writer seat locks, logs cryptographically hashed session handover briefs to an immutable SQLite WAL ledger, automatically reaps abandoned seats via heartbeats, and redacts sensitive credentials from command I/O.


🏗️ The System Architecture

CliSeat acts as an authoritative governance sidecar between your AI assistants and the host shell:

           +-----------------------------------------------+
           |       AI Agent / LLM (Claude, Cursor, Swarm)  |
           +-----------------------------------------------+
                                   |
                          (MCP JSON-RPC Tools)
                                   v
           +-----------------------------------------------+
           |            CliSeat Pro MCP Server             |
           +-----------------------------------------------+
              /                    |                    \
             v                     v                     v
   +-------------------+  +-------------------+  +-------------------+
   | Single-Writer Seat|  | Handover Ledger   |  | Command Governor  |
   | Lock (domain:cli) |  | (SQLite WAL SHA256|  | & Secret Masking  |
   +-------------------+  +-------------------+  +-------------------+
             \                     |                     /
              +--------------------+--------------------+
                                   |
                                   v
                      +-------------------------+
                      |   Governed Shell I/O    |
                      +-------------------------+
Enter fullscreen mode Exit fullscreen mode

1. Single-Writer Seat Dispatch: Enforcing Mutual Exclusion

Only one agent session should hold mutation rights over a specific architectural domain at any given moment.

CliSeat manages domain seats (domain:cli, domain:db, domain:infra):

class SeatManager:
    def __init__(self):
        self.seats = {}

    def claim_seat(self, seat_id: str, holder_session_id: str, lease_ttl_sec: int = 180):
        seat = self.get_or_create(seat_id)
        now = datetime.now(timezone.utc).isoformat()

        # Reject if held by an active session
        if seat.status == "occupied" and not seat.is_expired():
            if seat.holder != holder_session_id:
                return {
                    "status": "rejected",
                    "error": f"Seat '{seat_id}' is already held by '{seat.holder}'.",
                    "seat": seat.to_dict()
                }
            # Renew lease if already held
            seat.last_active = now
            return {"status": "renewed", "seat": seat.to_dict()}

        # Grant exclusive lock
        seat.holder = holder_session_id
        seat.status = "occupied"
        seat.claimed_at = now
        seat.last_active = now
        return {"status": "claimed", "seat": seat.to_dict()}
Enter fullscreen mode Exit fullscreen mode

If another session attempts to mutate the repository while domain:cli is occupied, the claim is rejected immediately with the current holder's session ID and remaining lease time.


2. Preventing Context Amnesia: The Handover Ledger

When an agent finishes its task or reaches token budget exhaustion, it must produce a structured sign-off brief.

CliSeat persists every handover to an atomic SQLite WAL database sealed with a SHA-256 state hash:

import sqlite3
import hashlib
import uuid
from datetime import datetime, timezone

class HandoverLedger:
    def __init__(self, db_path: str = "ledger.db"):
        self.db_path = db_path
        self._init_db()

    def record_handover(self, seat_id: str, event_type: str, session_id: str, brief: str):
        entry_id = f"hnd_{uuid.uuid4().hex[:12]}"
        now = datetime.now(timezone.utc).isoformat()

        # Generate tamper-evident cryptographic hash
        hash_payload = f"{entry_id}:{seat_id}:{event_type}:{session_id}:{brief}:{now}"
        state_hash = hashlib.sha256(hash_payload.encode("utf-8")).hexdigest()

        with sqlite3.connect(self.db_path, isolation_level=None) as conn:
            conn.execute("""
                INSERT INTO handover_ledger (entry_id, seat_id, event_type, session_id, brief, state_hash, created_at)
                VALUES (?, ?, ?, ?, ?, ?, ?);
            """, (entry_id, seat_id, event_type, session_id, brief, state_hash, now))

        return {"entry_id": entry_id, "state_hash": state_hash[:16], "timestamp": now}
Enter fullscreen mode Exit fullscreen mode

When the next agent takes the seat, it simply calls get_handover_history(seat_id="domain:cli") to see:

  • What previous agents completed
  • Unfinished branch names
  • Known issues or test failures
  • Pending pull request links

3. Boundary Enforcement & Secret Masking

To prevent accidents and credential leaks, CliSeat intercepts terminal commands:

  1. Domain Whitelists: An agent on domain:cli is allowed git, npm, pip, pytest, and cargo, but blocked from executing database or infrastructure mutations.
  2. Credential Redactor: Masks GitHub PATs, AWS secret keys, Bearer tokens, and passwords from command arguments and output logs.
SECRET_PATTERNS = [
    (re.compile(r"Bearer\s+[A-Za-z0-9_\-\.]{15,}", re.IGNORECASE), "Bearer [REDACTED_CREDENTIAL]"),
    (re.compile(r"(?:ghp|gho|ghu|github_pat)_[A-Za-z0-9_]{20,}", re.IGNORECASE), "[REDACTED_CREDENTIAL]"),
]

def redact(text: str) -> str:
    for pattern, replacement in SECRET_PATTERNS:
        text = pattern.sub(replacement, text)
    return text
Enter fullscreen mode Exit fullscreen mode

4. Configuring with Claude Desktop or Cursor

Adding CliSeat to your local AI environment takes seconds. Add the following to claude_desktop_config.json:

{
  "mcpServers": {
    "cliseat": {
      "command": "python",
      "args": ["-m", "cliseat.server"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Available MCP tools:

  • claim_seat(seat_id, holder_session_id)
  • heartbeat(seat_id, holder_session_id)
  • release_seat(seat_id, holder_session_id, handover_notes)
  • get_seat_status(seat_id)
  • execute_domain_command(seat_id, holder_session_id, command_line)
  • get_handover_history(seat_id, limit)
  • reap_expired_leases()

🚀 Get Started: Open-Source & Enterprise

The open-source CliSeat Lite is available immediately on GitHub under the MIT license:

👉 GitHub Repository: https://github.com/johannschiffer/cliseat-mcp

For enterprise development teams requiring the persistent SQLite WAL handover ledger, automatic heartbeat reapers for crashed sessions, credential masking, and complete container manifests:

👉 Explore CliSeat Pro on Gumroad ($14.99 USD)


How do you handle multi-agent coordination and command safety in your development stack? Let's discuss in the comments below!

Top comments (0)