session-collab-mcp 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,11 +3,11 @@
3
3
  [![npm version](https://img.shields.io/npm/v/session-collab-mcp.svg)](https://www.npmjs.com/package/session-collab-mcp)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A Model Context Protocol (MCP) server for Claude Code that prevents conflicts when multiple sessions work on the same codebase simultaneously.
6
+ A provider-agnostic Model Context Protocol (MCP) server that prevents conflicts when multiple agent sessions work on the same codebase simultaneously.
7
7
 
8
8
  ## Problem
9
9
 
10
- When using parallel Claude Code sessions or the parallel-dev workflow:
10
+ When using parallel coding-agent sessions or multi-agent workflows:
11
11
 
12
12
  - Session A is refactoring some code
13
13
  - Session B doesn't know and thinks the code "has issues" - deletes or reverts it
@@ -25,29 +25,20 @@ Session Collab MCP provides a **Work-in-Progress (WIP) Registry** that allows se
25
25
  4. **Protect** - Guard critical files from accidental changes
26
26
  5. **Release** - Free files when done
27
27
 
28
- ## Installation
28
+ ## Positioning
29
29
 
30
- ### Option 1: Claude Code Plugin (Recommended)
30
+ `session-collab-mcp` has two layers:
31
31
 
32
- Install as a Claude Code plugin for automatic MCP server setup, hooks, and skills:
32
+ - **Core server**: provider-agnostic MCP server over stdio or HTTP JSON-RPC
33
+ - **Optional integrations**: provider-specific packaging such as the Claude Code plugin in [`plugin/`](plugin/)
33
34
 
34
- ```bash
35
- # Add marketplace
36
- /plugin marketplace add leaf76/session-collab-mcp
35
+ The core server should be the default mental model. Claude Code is one integration target, not the product boundary.
37
36
 
38
- # Install plugin
39
- /plugin install session-collab@session-collab-plugins
40
- ```
41
-
42
- The plugin includes:
43
- - **MCP Server**: Automatically configured
44
- - **Hooks**: SessionStart and PreToolUse reminders
45
- - **Skills**: `collab-start` for full initialization
46
- - **Commands**: `/session-collab:status` and `/session-collab:end`
37
+ ## Installation
47
38
 
48
- ### Option 2: MCP Server Only
39
+ ### Option 1: Generic MCP Client over stdio
49
40
 
50
- Add to your `~/.claude.json`:
41
+ Use this with any MCP client that can launch a local stdio server:
51
42
 
52
43
  ```json
53
44
  {
@@ -60,34 +51,59 @@ Add to your `~/.claude.json`:
60
51
  }
61
52
  ```
62
53
 
63
- ### Option 3: Global Installation
54
+ The exact config wrapper depends on your MCP client, but the server contract is the same.
64
55
 
65
- ```bash
66
- npm install -g session-collab-mcp
67
- ```
56
+ ### Option 2: HTTP Server + CLI
68
57
 
69
- ### Option 4: HTTP Server + CLI (Universal)
58
+ Use this when your client prefers MCP over HTTP JSON-RPC or when you want a generic shell-friendly wrapper:
70
59
 
71
60
  ```bash
72
61
  # Start HTTP server
73
62
  session-collab-http --host 127.0.0.1 --port 8765
74
63
 
75
- # CLI wrapper (HTTP client)
64
+ # CLI wrapper (convenience REST client)
76
65
  session-collab health
77
66
  session-collab tools
78
67
  session-collab call --name collab_session_start --args '{"project_root":"/repo","name":"demo"}'
79
68
  ```
80
69
 
70
+ For MCP-over-HTTP clients, use `POST /mcp` with JSON-RPC requests. The `/v1/*` endpoints are a convenience REST facade for lightweight automation and shell usage.
71
+
72
+ ### Option 3: Claude Code Plugin (Optional Integration)
73
+
74
+ Install as a Claude Code plugin only if Claude Code is your MCP client and you want automatic server setup, hooks, and skills:
75
+
76
+ ```bash
77
+ # Add marketplace
78
+ /plugin marketplace add leaf76/session-collab-mcp
79
+
80
+ # Install plugin
81
+ /plugin install session-collab@session-collab-plugins
82
+ ```
83
+
84
+ The plugin includes:
85
+ - **MCP Server**: Automatically configured
86
+ - **Hooks**: SessionStart, Stop, and PreCompact reminders
87
+ - **Skills**: `collab-start` for full initialization
88
+ - **Commands**: `/session-collab:status` and `/session-collab:end`
89
+
90
+ ### Option 4: Global Installation
91
+
92
+ ```bash
93
+ npm install -g session-collab-mcp
94
+ ```
95
+
81
96
  ## Features
82
97
 
83
- ### Automatic Session Management
98
+ ### Guided Session Workflow
84
99
 
85
- Once installed, Claude will:
100
+ The MCP tools give you a stable collaboration workflow across providers:
86
101
 
87
- 1. Register a session when conversation starts
88
- 2. Check for conflicts before editing files
89
- 3. Warn you if another session is working on the same files
90
- 4. Clean up when the conversation ends
102
+ 1. Start a session with `collab_session_start`
103
+ 2. Check files with `collab_claim(action="check")`
104
+ 3. Reserve files with `collab_claim(action="create")`
105
+ 4. Save important context with `collab_memory_save`
106
+ 5. End the session with `collab_session_end`
91
107
 
92
108
  ### Working Memory
93
109
 
@@ -102,11 +118,11 @@ Context persistence that survives context compaction:
102
118
 
103
119
  ### File Protection
104
120
 
105
- Guard critical files from accidental changes:
121
+ Guard important plan files or created files from accidental deletion:
106
122
 
107
- - Register protected files with reasons and priorities
108
- - Automatic conflict detection for protected files
109
- - Configurable protection levels
123
+ - Register a protected plan with `collab_protect(action="register", type="plan", ...)`
124
+ - Register a created file with `collab_protect(action="register", type="file", ...)`
125
+ - Check protection status before deleting or replacing a file
110
126
 
111
127
  ### Conflict Handling Modes
112
128
 
@@ -167,7 +183,7 @@ Configure behavior with `collab_config`:
167
183
 
168
184
  ### HTTP API (v1)
169
185
 
170
- Core endpoints map 1:1 to MCP tools:
186
+ `/v1/*` endpoints map 1:1 to MCP tools and return JSON responses with `trace_id` on failures:
171
187
 
172
188
  - `POST /v1/sessions/start` → `collab_session_start`
173
189
  - `POST /v1/sessions/end` → `collab_session_end`
@@ -186,6 +202,13 @@ Core endpoints map 1:1 to MCP tools:
186
202
  - `GET /v1/protect/list` → `collab_protect` (list)
187
203
  - `POST /v1/tools/call` / `GET /v1/tools` (generic access)
188
204
 
205
+ ### MCP over HTTP
206
+
207
+ - `POST /mcp` accepts JSON-RPC `initialize`, `tools/list`, and `tools/call` requests
208
+ - `GET /mcp` currently returns a clear "stream not supported" response instead of pretending to be full Streamable HTTP SSE
209
+ - Localhost binds enforce Host and Origin validation
210
+ - Non-local binds require both `SESSION_COLLAB_HTTP_TOKEN` and an allowed-host list via `SESSION_COLLAB_ALLOWED_HOSTS` or repeated `--allowed-host`
211
+
189
212
  ## Usage Examples
190
213
 
191
214
  ### Basic Workflow
@@ -193,17 +216,17 @@ Core endpoints map 1:1 to MCP tools:
193
216
  ```bash
194
217
  # Session A starts working
195
218
  collab_session_start(project_root="/my/project", name="feature-auth")
196
- collab_claim(action="create", files=["src/auth.ts"], intent="Adding JWT support")
219
+ collab_claim(session_id="session-a", action="create", files=["src/auth.ts"], intent="Adding JWT support")
197
220
 
198
221
  # Session B checks before editing
199
- collab_claim(action="check", files=["src/auth.ts"])
222
+ collab_claim(session_id="session-b", action="check", files=["src/auth.ts"])
200
223
  # Result: "CONFLICT: src/auth.ts is claimed by 'feature-auth'"
201
224
 
202
225
  # If you want to include your own claims in the check
203
- collab_claim(action="check", files=["src/auth.ts"], exclude_self=false)
226
+ collab_claim(session_id="session-a", action="check", files=["src/auth.ts"], exclude_self=false)
204
227
 
205
228
  # Session A finishes
206
- collab_claim(action="release", claim_id="...")
229
+ collab_claim(session_id="session-a", action="release", claim_id="...")
207
230
  ```
208
231
 
209
232
  ### Working Memory
@@ -211,6 +234,7 @@ collab_claim(action="release", claim_id="...")
211
234
  ```bash
212
235
  # Save a finding
213
236
  collab_memory_save(
237
+ session_id="abc123",
214
238
  category="finding",
215
239
  key="auth_bug_root_cause",
216
240
  content="Missing token validation in refresh flow",
@@ -218,26 +242,29 @@ collab_memory_save(
218
242
  )
219
243
 
220
244
  # Recall active memories
221
- collab_memory_recall(active=true, priority_threshold=70)
245
+ collab_memory_recall(session_id="abc123", active=true)
222
246
  ```
223
247
 
224
248
  ### File Protection
225
249
 
226
250
  ```bash
227
- # Protect critical file
251
+ # Protect a plan document
228
252
  collab_protect(
229
253
  action="register",
230
- file_path="src/core/auth.ts",
231
- reason="Core authentication logic",
232
- priority=95
254
+ session_id="abc123",
255
+ type="plan",
256
+ file_path="docs/feature-plan.md",
257
+ title="Feature plan",
258
+ content_summary="Steps, risks, and rollout notes"
233
259
  )
234
260
 
235
261
  # Check before editing
236
262
  collab_protect(
237
263
  action="check",
238
- file_paths=["src/core/auth.ts"]
264
+ session_id="abc123",
265
+ file_path="docs/feature-plan.md"
239
266
  )
240
- # Result: "BLOCKED: File is protected - Core authentication logic"
267
+ # Result: "Protected (plan). Confirm before deleting."
241
268
  ```
242
269
 
243
270
  ### Status Monitoring
@@ -248,7 +275,7 @@ collab_status(session_id="abc123")
248
275
  # Result: {
249
276
  # session: { id: "abc123", name: "feature-auth", status: "active" },
250
277
  # claims: [...],
251
- # active_memories: 5,
278
+ # other_sessions: 1,
252
279
  # message: "Session active. 2 claim(s), 5 memories."
253
280
  # }
254
281
  ```
@@ -269,7 +296,8 @@ Version 2.0 introduces breaking changes with a simplified API. See [MIGRATION.md
269
296
  All data is stored locally in `~/.claude/session-collab/collab.db` (SQLite).
270
297
 
271
298
  - No remote server required
272
- - No API token needed
299
+ - Localhost HTTP usage works without an API token
300
+ - Non-local HTTP binds require `SESSION_COLLAB_HTTP_TOKEN`
273
301
  - Works offline
274
302
  - Uses WAL mode for multi-process safety
275
303
 
@@ -316,13 +344,17 @@ HTTP integration tests require a local listen port. Enable them with:
316
344
  SESSION_COLLAB_HTTP_TESTS=true npx vitest run src/http/__tests__/server-integration.test.ts
317
345
  ```
318
346
 
347
+ ### Historical Notes
348
+
349
+ The changelog entries below document historical milestones, including tools and workflows that were removed before the current v2 API. Treat the tool tables and examples above as the source of truth for the current public surface.
350
+
319
351
  ### Project Structure
320
352
 
321
353
  ```
322
354
  session-collab-mcp/
323
355
  ├── bin/ # Executable entry point
324
356
  ├── migrations/ # SQLite migration files
325
- ├── plugin/ # Claude Code Plugin
357
+ ├── plugin/ # Optional Claude Code integration
326
358
  ├── src/
327
359
  │ ├── cli.ts # CLI entry point
328
360
  │ ├── constants.ts # Version and server instructions
@@ -340,6 +372,14 @@ session-collab-mcp/
340
372
 
341
373
  ## Changelog
342
374
 
375
+ ### v2.1.0
376
+
377
+ - Add HTTP server + CLI wrapper for universal AI CLI usage
378
+ - Add HTTP API endpoints and utils tests
379
+ - Add legacy entry for deprecated schemas/queries (optional build)
380
+ - Improve claim conflict accuracy and release summaries
381
+ - Expand test coverage across MCP tools and DB flows
382
+
343
383
  ### v2.0.0 (Breaking)
344
384
 
345
385
  - **Major Simplification**: Reduced from 50+ tools to 10 core tools
@@ -4,11 +4,28 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
+ // src/db/migrations.ts
8
+ import { readFileSync, readdirSync } from "fs";
9
+ import { join } from "path";
10
+ var VERSIONED_MIGRATION_PATTERN = /^\d{4}_.+\.sql$/;
11
+ function listMigrationFiles(migrationsDir) {
12
+ return readdirSync(migrationsDir).filter((file) => VERSIONED_MIGRATION_PATTERN.test(file)).sort((left, right) => left.localeCompare(right, "en"));
13
+ }
14
+ function loadMigrationsFromDir(migrationsDir) {
15
+ return listMigrationFiles(migrationsDir).map(
16
+ (file) => readFileSync(join(migrationsDir, file), "utf-8")
17
+ );
18
+ }
19
+ function splitMigrationStatements(migration) {
20
+ const withoutCommentLines = migration.split("\n").filter((line) => !line.trim().startsWith("--")).join("\n");
21
+ return withoutCommentLines.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0);
22
+ }
23
+
7
24
  // src/db/sqlite-adapter.ts
8
25
  import Database from "better-sqlite3";
9
26
  import { randomUUID } from "crypto";
10
27
  import { existsSync, mkdirSync } from "fs";
11
- import { dirname, join } from "path";
28
+ import { dirname, join as join2 } from "path";
12
29
  import { homedir } from "os";
13
30
  if (typeof globalThis.crypto === "undefined") {
14
31
  globalThis.crypto = {
@@ -93,7 +110,7 @@ var SqliteDatabase = class {
93
110
  // Handles upgrades gracefully by ignoring "already exists" and "duplicate column" errors
94
111
  initSchema(migrations) {
95
112
  for (const migration of migrations) {
96
- const statements = migration.split(";").map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("--"));
113
+ const statements = splitMigrationStatements(migration);
97
114
  for (const stmt of statements) {
98
115
  try {
99
116
  this.runSql(stmt);
@@ -116,8 +133,8 @@ var SqliteDatabase = class {
116
133
  }
117
134
  };
118
135
  function getDefaultDbPath() {
119
- const dataDir = join(homedir(), ".claude", "session-collab");
120
- return join(dataDir, "collab.db");
136
+ const dataDir = join2(homedir(), ".claude", "session-collab");
137
+ return join2(dataDir, "collab.db");
121
138
  }
122
139
  function createLocalDatabase(dbPath) {
123
140
  const path = dbPath ?? getDefaultDbPath();
@@ -125,13 +142,13 @@ function createLocalDatabase(dbPath) {
125
142
  }
126
143
 
127
144
  // src/constants.ts
128
- import { readFileSync } from "fs";
129
- import { join as join2, dirname as dirname2 } from "path";
145
+ import { readFileSync as readFileSync2 } from "fs";
146
+ import { join as join3, dirname as dirname2 } from "path";
130
147
  import { fileURLToPath } from "url";
131
148
  function getVersion() {
132
149
  try {
133
150
  const __dirname2 = dirname2(fileURLToPath(import.meta.url));
134
- const pkg = JSON.parse(readFileSync(join2(__dirname2, "..", "package.json"), "utf-8"));
151
+ const pkg = JSON.parse(readFileSync2(join3(__dirname2, "..", "package.json"), "utf-8"));
135
152
  return pkg.version;
136
153
  } catch {
137
154
  return "0.0.0";
@@ -4239,6 +4256,27 @@ var JsonRpcRequestSchema = external_exports.object({
4239
4256
  method: external_exports.string(),
4240
4257
  params: external_exports.record(external_exports.unknown()).optional()
4241
4258
  });
4259
+ var MCP_ERROR_CODES = {
4260
+ PARSE_ERROR: -32700,
4261
+ INVALID_REQUEST: -32600,
4262
+ METHOD_NOT_FOUND: -32601,
4263
+ INVALID_PARAMS: -32602,
4264
+ INTERNAL_ERROR: -32603
4265
+ };
4266
+ function createSuccessResponse(id, result) {
4267
+ return {
4268
+ jsonrpc: "2.0",
4269
+ id,
4270
+ result
4271
+ };
4272
+ }
4273
+ function createErrorResponse(id, code, message, data) {
4274
+ return {
4275
+ jsonrpc: "2.0",
4276
+ id,
4277
+ error: { code, message, data }
4278
+ };
4279
+ }
4242
4280
  function createToolResult(text, isError = false) {
4243
4281
  return {
4244
4282
  content: [{ type: "text", text }],
@@ -4974,7 +5012,7 @@ var claimUpdatePrioritySchema = external_exports.object({
4974
5012
  var claimCheckSchema = external_exports.object({
4975
5013
  files: filesArraySchema,
4976
5014
  symbols: symbolClaimsArraySchema.optional(),
4977
- session_id: external_exports.string().optional(),
5015
+ session_id: sessionIdSchema,
4978
5016
  exclude_self: external_exports.boolean().optional()
4979
5017
  });
4980
5018
  var claimReleaseSchema = external_exports.object({
@@ -5958,7 +5996,76 @@ async function handleProtectionTool(db, name, args) {
5958
5996
  }
5959
5997
 
5960
5998
  // src/mcp/server.ts
5999
+ var SERVER_INFO = {
6000
+ name: SERVER_NAME,
6001
+ version: VERSION
6002
+ };
6003
+ var CAPABILITIES = {
6004
+ tools: {}
6005
+ };
5961
6006
  var ALL_TOOLS = [...sessionTools, ...claimTools, ...memoryTools, ...protectionTools];
6007
+ var McpServer = class {
6008
+ constructor(db, authContext) {
6009
+ this.db = db;
6010
+ this.authContext = authContext;
6011
+ }
6012
+ authContext;
6013
+ async handleRequest(request) {
6014
+ const { method, params, id } = request;
6015
+ try {
6016
+ switch (method) {
6017
+ case "initialize":
6018
+ return await this.handleInitialize(id);
6019
+ case "notifications/initialized":
6020
+ return createSuccessResponse(id, {});
6021
+ case "tools/list":
6022
+ return await this.handleToolsList(id);
6023
+ case "tools/call":
6024
+ return await this.handleToolCall(id, params);
6025
+ case "ping":
6026
+ return createSuccessResponse(id, {});
6027
+ default:
6028
+ return createErrorResponse(id, MCP_ERROR_CODES.METHOD_NOT_FOUND, `Method not found: ${method}`);
6029
+ }
6030
+ } catch (error) {
6031
+ const message = error instanceof Error ? error.message : "Unknown error";
6032
+ return createErrorResponse(id, MCP_ERROR_CODES.INTERNAL_ERROR, message);
6033
+ }
6034
+ }
6035
+ async handleInitialize(id) {
6036
+ return createSuccessResponse(id, {
6037
+ protocolVersion: "2024-11-05",
6038
+ serverInfo: SERVER_INFO,
6039
+ capabilities: CAPABILITIES,
6040
+ instructions: SERVER_INSTRUCTIONS
6041
+ });
6042
+ }
6043
+ async handleToolsList(id) {
6044
+ return createSuccessResponse(id, { tools: ALL_TOOLS });
6045
+ }
6046
+ async handleToolCall(id, params) {
6047
+ const { name, arguments: args = {} } = params;
6048
+ let result;
6049
+ const userId = this.authContext?.userId !== "legacy" ? this.authContext?.userId : void 0;
6050
+ try {
6051
+ if (name.startsWith("collab_session_") || name === "collab_config" || name === "collab_status") {
6052
+ result = await handleSessionTool(this.db, name, args, userId);
6053
+ } else if (name === "collab_claim") {
6054
+ result = await handleClaimTool(this.db, name, args);
6055
+ } else if (name.startsWith("collab_memory_")) {
6056
+ result = await handleMemoryTool(this.db, name, args);
6057
+ } else if (name === "collab_protect") {
6058
+ result = await handleProtectionTool(this.db, name, args);
6059
+ } else {
6060
+ result = createToolResult(`Unknown tool: ${name}`, true);
6061
+ }
6062
+ } catch (error) {
6063
+ const message = error instanceof Error ? error.message : "Tool execution failed";
6064
+ result = createToolResult(message, true);
6065
+ }
6066
+ return createSuccessResponse(id, result);
6067
+ }
6068
+ };
5962
6069
  function getMcpTools() {
5963
6070
  return ALL_TOOLS;
5964
6071
  }
@@ -5982,12 +6089,16 @@ async function handleMcpRequest(db, name, args) {
5982
6089
  }
5983
6090
 
5984
6091
  export {
6092
+ loadMigrationsFromDir,
5985
6093
  getDefaultDbPath,
5986
6094
  createLocalDatabase,
6095
+ JsonRpcRequestSchema,
6096
+ generateId,
5987
6097
  VERSION,
5988
6098
  SERVER_NAME,
5989
6099
  SERVER_INSTRUCTIONS,
6100
+ McpServer,
5990
6101
  getMcpTools,
5991
6102
  handleMcpRequest
5992
6103
  };
5993
- //# sourceMappingURL=chunk-V4APNPXF.js.map
6104
+ //# sourceMappingURL=chunk-SOUW3JSS.js.map