Skip to main content
Glama
jonburchel

MCP Session Coordinator

by jonburchel
README.md
# šŸ”„ MCP Session Coordinator

**Coordinate multiple AI coding sessions with file locking, intent broadcasting, and cross-session messaging.**

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Node.js >= 18](https://img.shields.io/badge/Node.js-%3E%3D%2018-green.svg)](https://nodejs.org/)

---

## The Problem

When running multiple AI coding sessions (like GitHub Copilot CLI) simultaneously on the same codebase, sessions have no awareness of each other. This leads to:

- **File edit conflicts** — two sessions modify the same file at the same time
- **Duplicate git operations** — multiple sessions attempt conflicting commits, rebases, or merges
- **Missed collaboration opportunities** — one session discovers something valuable but has no way to tell the others
- **Wasted effort on overlapping work** — sessions independently tackle the same task without knowing

## The Solution

A lightweight [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server that acts as a shared coordination layer, enabling sessions to:

- **Register themselves** and discover other active sessions working on the same project
- **Lock files** before editing to prevent conflicts
- **Broadcast intentions** before executing operations (edits, git commands, builds)
- **Exchange messages and ideas** across sessions in real time
- **Share discoveries, decisions, and conventions** as structured context
- **Detect conflicts proactively** before they cause problems

---

## Features

### 1. Session Management (5 tools)

| Tool | Description |
|------|-------------|
| `session_register` | Register a new session with name, project, branch, and working directory |
| `session_heartbeat` | Send periodic heartbeat to keep session active |
| `session_list` | List all active sessions, optionally filtered by project or branch |
| `session_get` | Get detailed information about a specific session |
| `session_deregister` | Gracefully deregister a session and release all its locks |

### 2. File Locking (4 tools)

| Tool | Description |
|------|-------------|
| `lock_acquire` | Acquire a lock on a file path (exclusive, shared, or intent) |
| `lock_release` | Release a previously acquired lock |
| `lock_list` | List all active locks, optionally filtered by session or file path |
| `lock_check` | Check whether a specific file is currently locked and by whom |

**Lock types:**
- **Exclusive** — only one session can hold the lock; blocks all others
- **Shared** — multiple sessions can read; blocks exclusive locks
- **Intent** — advisory lock signaling planned future edits; does not block but warns

All locks support configurable TTL with automatic expiry.

### 3. Intent Broadcasting (4 tools)

| Tool | Description |
|------|-------------|
| `intent_announce` | Announce an intention to perform an operation |
| `intent_update` | Update the status of a previously announced intent |
| `intent_list` | List active intents, optionally filtered by type or session |
| `intent_clear` | Clear a completed or abandoned intent |

**Intent types:** `file_edit`, `git_commit`, `git_push`, `git_rebase`, `git_merge`, `build`, `test`, `refactor`, `dependency_change`

### 4. Cross-Session Messaging (3 tools)

| Tool | Description |
|------|-------------|
| `message_send` | Send a message to a specific session or broadcast to all |
| `message_list` | List messages for the current session, optionally filtered by channel |
| `message_mark_read` | Mark messages as read |

**Channels:** `general`, `coordination`, `ideas`, `alerts`, `git`

**Priority levels:** `low`, `normal`, `high`, `urgent`

Messages can be sent directly to a specific session or broadcast to all active sessions.

### 5. Conflict Detection (1 tool)

| Tool | Description |
|------|-------------|
| `conflict_check` | Comprehensive conflict check across locks, intents, and active sessions |

Analyzes potential conflicts before an operation by cross-referencing:
- Active file locks held by other sessions
- Announced intents from other sessions targeting the same files
- Other sessions working on the same branch

### 6. Shared Context (3 tools)

| Tool | Description |
|------|-------------|
| `context_share` | Share a piece of knowledge with all sessions |
| `context_query` | Query shared context by type, project, or keyword |
| `context_delete` | Remove outdated or incorrect shared context |

**Context types:** `decision`, `discovery`, `convention`, `blocker`, `note`

**Total: 20 tools** across 6 categories.

---

## Installation

```bash
# Clone the repository
git clone https://github.com/jonburchel/mcp-session-coordinator.git
cd mcp-session-coordinator

# Install dependencies
npm install

# Build
npm run build
```

## Configuration

Add the server to your MCP client configuration.

### GitHub Copilot CLI

In your `settings.json` or `mcp.json`:

```json
{
  "mcpServers": {
    "session-coordinator": {
      "command": "node",
      "args": ["F:/home/mcp-session-coordinator/dist/index.js"],
      "env": {}
    }
  }
}
```

### Claude Desktop

In your Claude Desktop config (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "session-coordinator": {
      "command": "node",
      "args": ["/path/to/mcp-session-coordinator/dist/index.js"]
    }
  }
}
```

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `MCP_COORDINATOR_DB_PATH` | Path to SQLite database file | `~/.mcp-session-coordinator/coordinator.db` |
| `MCP_CLEANUP_INTERVAL_SEC` | Stale session cleanup interval (seconds) | `60` |
| `MCP_SESSION_TIMEOUT_SEC` | Seconds before a session is considered stale | `300` |

---

## Usage Examples

### Basic Session Lifecycle

```
1. Register your session
   → session_register(name="feature-auth", project="my-app", branch="feature/auth")
   ← { session_id: "abc-123", status: "registered" }

2. Before editing a file, acquire a lock
   → lock_acquire(session_id="abc-123", file_path="src/auth.ts")
   ← { granted: true, lock_type: "exclusive" }

3. Announce your intent
   → intent_announce(session_id="abc-123", intent_type="file_edit", target="src/auth.ts")
   ← { intent_id: "int-456", status: "announced" }

4. After editing, release the lock
   → lock_release(session_id="abc-123", file_path="src/auth.ts")
   ← { released: true }

5. Before committing, check for conflicts
   → conflict_check(session_id="abc-123", intent_type="git_commit")
   ← { conflicts: [], safe_to_proceed: true }

6. When finished, deregister
   → session_deregister(session_id="abc-123")
   ← { status: "deregistered", locks_released: 0 }
```

### Two Sessions Coordinating

**Session A** is implementing authentication. **Session B** is building the user profile page. Both are working on the same project.

```
Session A registers:
  → session_register(name="auth-implementation", project="my-app", branch="feature/auth")
  ← { session_id: "session-A" }

Session B registers:
  → session_register(name="profile-page", project="my-app", branch="feature/profile")
  ← { session_id: "session-B" }

Session A locks the user model:
  → lock_acquire(session_id="session-A", file_path="src/models/user.ts", lock_type="exclusive")
  ← { granted: true }

Session B tries to edit the same file:
  → lock_acquire(session_id="session-B", file_path="src/models/user.ts", lock_type="exclusive")
  ← { granted: false, held_by: "session-A", holder_name: "auth-implementation" }

Session B sends a message to Session A:
  → message_send(
      from_session="session-B",
      to_session="session-A",
      channel="coordination",
      content="I need to add a 'bio' field to the User model. Can you include it in your changes?"
    )

Session A sees the message and responds:
  → message_send(
      from_session="session-A",
      to_session="session-B",
      channel="coordination",
      content="Done! Added 'bio: string' to the User interface. Releasing lock now."
    )

Session A releases the lock:
  → lock_release(session_id="session-A", file_path="src/models/user.ts")

Session B can now proceed:
  → lock_acquire(session_id="session-B", file_path="src/models/user.ts", lock_type="shared")
  ← { granted: true }
```

### Sharing Context Across Sessions

One session discovers an important architectural detail and shares it:

```
Session A discovers something:
  → context_share(
      session_id="session-A",
      context_type="discovery",
      project="my-app",
      content="The auth module uses JWT tokens (not sessions). Tokens are stored in
               httpOnly cookies, not localStorage. See src/auth/jwt.ts for the implementation."
    )

Later, Session B queries for relevant context:
  → context_query(project="my-app", context_type="discovery")
  ← [
      {
        content: "The auth module uses JWT tokens (not sessions)...",
        shared_by: "auth-implementation",
        context_type: "discovery"
      }
    ]

Session B can also search by keyword:
  → context_query(project="my-app", keyword="auth")
  ← [ ...matching context entries... ]
```

### Announcing Git Operations

Before performing git operations that could affect other sessions:

```
Session A is about to rebase:
  → intent_announce(
      session_id="session-A",
      intent_type="git_rebase",
      target="feature/auth",
      description="Rebasing feature/auth onto latest main"
    )

Session B checks before pushing:
  → conflict_check(session_id="session-B", intent_type="git_push")
  ← {
      conflicts: [{
        type: "git_operation",
        description: "Session 'auth-implementation' is currently rebasing branch feature/auth",
        severity: "high"
      }],
      safe_to_proceed: false
    }

Session B waits. Session A finishes and clears the intent:
  → intent_clear(session_id="session-A", intent_id="int-789")
```

---

## Architecture

```
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  AI Session  │  │  AI Session  │  │  AI Session  │
│  (Copilot)   │  │  (Copilot)   │  │  (Claude)    │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
       │                 │                 │
       │     MCP Protocol (stdio)          │
       │                 │                 │
       ā–¼                 ā–¼                 ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│           MCP Session Coordinator               │
│                                                 │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │
│  │  Session   │ │   Lock   │ │    Intent     │  │
│  │  Manager   │ │  Manager │ │   Broadcaster │  │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”  │
│  │  Message   │ │ Conflict │ │    Context    │  │
│  │  Router    │ │ Detector │ │    Store      │  │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜  │
│                                                 │
│  ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”    │
│  │         SQLite (WAL mode)               │    │
│  │    ~/.mcp-session-coordinator/db        │    │
│  ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜    │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
```

**Key design decisions:**

- **SQLite with WAL mode** for safe concurrent multi-process access from multiple MCP server instances
- **Automatic stale session cleanup** via configurable interval, removing sessions that miss heartbeats
- **Lock expiry with configurable TTL** so crashed sessions don't hold locks forever
- **Graceful shutdown handling** that releases all locks and deregisters the session on exit
- **No external dependencies** beyond Node.js and SQLite (via better-sqlite3)

---

## Roadmap

Future considerations for v2 and beyond:

- **Cloud sync** via GitHub or Google authentication for cross-machine coordination
- **Web dashboard** for monitoring active sessions, locks, and message traffic
- **Webhook notifications** for external integrations (Slack, Discord)
- **Cross-machine coordination** for distributed teams running sessions on different hosts
- **Session groups** for organizing related sessions into logical units
- **Lock queuing** with automatic grant when the current holder releases

---

## Contributing

Contributions are welcome! Here's how to get started:

1. Fork the repository
2. Create a feature branch: `git checkout -b feature/your-feature`
3. Make your changes and add tests
4. Ensure all tests pass: `npm test`
5. Submit a pull request

Please follow the existing code style and include tests for new functionality.

---

## License

This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.