session-collab-mcp 2.2.0 → 2.3.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
@@ -64,6 +64,7 @@ session-collab-http --host 127.0.0.1 --port 8765
64
64
  # CLI wrapper (convenience REST client)
65
65
  session-collab health
66
66
  session-collab tools
67
+ session-collab doctor
67
68
  session-collab call --name collab_session_start --args '{"project_root":"/repo","name":"demo"}'
68
69
  ```
69
70
 
@@ -101,9 +102,11 @@ The MCP tools give you a stable collaboration workflow across providers:
101
102
 
102
103
  1. Start a session with `collab_session_start`
103
104
  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`
105
+ 3. Reserve files with `collab_claim(action="create")`; conflicting claims are blocked by default
106
+ 4. Update visible progress with `collab_session_update`
107
+ 5. Save important context with `collab_memory_save`
108
+ 6. Release claims with `collab_claim(action="release")`
109
+ 7. End the session with `collab_session_end`
107
110
 
108
111
  ### Working Memory
109
112
 
@@ -145,13 +148,14 @@ Configure behavior with `collab_config`:
145
148
 
146
149
  ## MCP Tools Reference
147
150
 
148
- ### Session Management (5 tools)
151
+ ### Session Management
149
152
 
150
153
  | Tool | Purpose |
151
154
  |------|---------|
152
155
  | `collab_session_start` | Register a new session |
153
156
  | `collab_session_end` | End session and release all claims |
154
- | `collab_session_list` | List active sessions |
157
+ | `collab_session_list` | List active sessions with current task and active claim summaries |
158
+ | `collab_session_update` | Update heartbeat, current task, todos, and progress |
155
159
  | `collab_config` | Configure session behavior |
156
160
  | `collab_status` | Get session status summary |
157
161
 
@@ -159,7 +163,7 @@ Configure behavior with `collab_config`:
159
163
 
160
164
  | Tool | Actions |
161
165
  |------|---------|
162
- | `collab_claim` | `create`, `check`, `release`, `list` (check: `exclude_self` defaults to true) |
166
+ | `collab_claim` | `create`, `check`, `release`, `list` (check: `exclude_self` defaults to true; create blocks conflicts unless `allow_conflicts=true`) |
163
167
 
164
168
  ### Working Memory (3 tools)
165
169
 
@@ -187,6 +191,7 @@ Configure behavior with `collab_config`:
187
191
 
188
192
  - `POST /v1/sessions/start` → `collab_session_start`
189
193
  - `POST /v1/sessions/end` → `collab_session_end`
194
+ - `POST /v1/sessions/update` → `collab_session_update`
190
195
  - `GET /v1/sessions` → `collab_session_list`
191
196
  - `POST /v1/config` → `collab_config`
192
197
  - `GET /v1/status` → `collab_status`
@@ -217,11 +222,16 @@ Configure behavior with `collab_config`:
217
222
  # Session A starts working
218
223
  collab_session_start(project_root="/my/project", name="feature-auth")
219
224
  collab_claim(session_id="session-a", action="create", files=["src/auth.ts"], intent="Adding JWT support")
225
+ collab_session_update(session_id="session-a", current_task="Adding JWT support")
220
226
 
221
227
  # Session B checks before editing
222
228
  collab_claim(session_id="session-b", action="check", files=["src/auth.ts"])
223
229
  # Result: "CONFLICT: src/auth.ts is claimed by 'feature-auth'"
224
230
 
231
+ # create is also safe by default; use allow_conflicts=true only after coordination
232
+ collab_claim(session_id="session-b", action="create", files=["src/auth.ts"], intent="Coordinated auth work")
233
+ # Result: "Claim not created. 1 conflict(s) detected."
234
+
225
235
  # If you want to include your own claims in the check
226
236
  collab_claim(session_id="session-a", action="check", files=["src/auth.ts"], exclude_self=false)
227
237
 
@@ -334,6 +344,8 @@ npm run start:dev # Start in development mode
334
344
  npm run typecheck # Run TypeScript type checking
335
345
  npm run lint # Run ESLint
336
346
  npm run test # Run tests with Vitest
347
+ npm run test:http # Run HTTP integration tests
348
+ npm run test:release # Run release gate: typecheck, lint, tests, HTTP tests, npm pack dry-run
337
349
  ```
338
350
 
339
351
  ### HTTP Integration Tests
@@ -341,7 +353,15 @@ npm run test # Run tests with Vitest
341
353
  HTTP integration tests require a local listen port. Enable them with:
342
354
 
343
355
  ```bash
344
- SESSION_COLLAB_HTTP_TESTS=true npx vitest run src/http/__tests__/server-integration.test.ts
356
+ npm run test:http
357
+ ```
358
+
359
+ ### HTTP CLI Doctor
360
+
361
+ When using the HTTP server, validate the running server with:
362
+
363
+ ```bash
364
+ session-collab doctor --base-url http://127.0.0.1:8765
345
365
  ```
346
366
 
347
367
  ### Historical Notes
@@ -372,6 +392,15 @@ session-collab-mcp/
372
392
 
373
393
  ## Changelog
374
394
 
395
+ ### v2.3.0
396
+
397
+ - Add `collab_session_update` for heartbeat, current task, todo, and progress reporting
398
+ - Enrich `collab_session_list` with current task and active claim summaries
399
+ - Make `collab_claim(action="create")` block conflicts by default; use `allow_conflicts=true` only after coordination
400
+ - Add `session-collab doctor` for HTTP server health and tool-surface checks
401
+ - Add `npm run test:http` and `npm run test:release` release gates
402
+ - Update dev test tooling to clear npm audit findings
403
+
375
404
  ### v2.1.0
376
405
 
377
406
  - Add HTTP server + CLI wrapper for universal AI CLI usage
@@ -161,12 +161,13 @@ var SERVER_INSTRUCTIONS = `
161
161
 
162
162
  Coordinate sessions and persist context across conversations.
163
163
 
164
- ## 10 Core Tools
164
+ ## Core Tools
165
165
 
166
- ### Session (5 tools)
166
+ ### Session
167
167
  - \`collab_session_start\`: Start session with project_root
168
168
  - \`collab_session_end\`: End session, release claims
169
- - \`collab_session_list\`: List active sessions
169
+ - \`collab_session_list\`: List active sessions and their active claim summaries
170
+ - \`collab_session_update\`: Update heartbeat, current task, todos, and progress
170
171
  - \`collab_config\`: Configure session behavior
171
172
  - \`collab_status\`: Get session status and summary
172
173
 
@@ -187,10 +188,11 @@ Coordinate sessions and persist context across conversations.
187
188
 
188
189
  1. **On start**: \`collab_session_start\` with project_root
189
190
  2. **Before editing**: \`collab_claim\` action="check"
190
- 3. **For changes**: \`collab_claim\` action="create"
191
+ 3. **For changes**: \`collab_claim\` action="create" (blocked by default if another active session conflicts)
191
192
  4. **Save context**: \`collab_memory_save\` for important findings
192
- 5. **When done**: \`collab_claim\` action="release"
193
- 6. **On end**: \`collab_session_end\`
193
+ 5. **While working**: \`collab_session_update\` with current_task/todos
194
+ 6. **When done**: \`collab_claim\` action="release"
195
+ 7. **On end**: \`collab_session_end\`
194
196
 
195
197
  ## Memory Categories
196
198
 
@@ -4334,6 +4336,39 @@ async function listSessions(db, params = {}) {
4334
4336
  const result = await db.prepare(query).bind(...bindings).all();
4335
4337
  return result.results;
4336
4338
  }
4339
+ async function updateSessionHeartbeat(db, id, statusUpdate) {
4340
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4341
+ let progress = null;
4342
+ let todosJson = null;
4343
+ if (statusUpdate?.todos) {
4344
+ const total = statusUpdate.todos.length;
4345
+ const completed = statusUpdate.todos.filter((t) => t.status === "completed").length;
4346
+ progress = {
4347
+ completed,
4348
+ total,
4349
+ percentage: total > 0 ? Math.round(completed / total * 100) : 0
4350
+ };
4351
+ todosJson = JSON.stringify(statusUpdate.todos);
4352
+ }
4353
+ let query = "UPDATE sessions SET last_heartbeat = ?";
4354
+ const bindings = [now];
4355
+ if (statusUpdate?.current_task !== void 0) {
4356
+ query += ", current_task = ?";
4357
+ bindings.push(statusUpdate.current_task);
4358
+ }
4359
+ if (progress) {
4360
+ query += ", progress = ?";
4361
+ bindings.push(JSON.stringify(progress));
4362
+ }
4363
+ if (todosJson) {
4364
+ query += ", todos = ?";
4365
+ bindings.push(todosJson);
4366
+ }
4367
+ query += " WHERE id = ? AND status = 'active'";
4368
+ bindings.push(id);
4369
+ const result = await db.prepare(query).bind(...bindings).run();
4370
+ return result.meta.changes > 0;
4371
+ }
4337
4372
  async function endSession(db, id, release_claims = "abandon") {
4338
4373
  const claimStatus = release_claims === "complete" ? "completed" : "abandoned";
4339
4374
  await db.prepare("UPDATE claims SET status = ?, updated_at = ? WHERE session_id = ? AND status = 'active'").bind(claimStatus, (/* @__PURE__ */ new Date()).toISOString(), id).run();
@@ -4998,7 +5033,8 @@ var claimCreateSchema = external_exports.object({
4998
5033
  symbols: symbolClaimsArraySchema.optional(),
4999
5034
  intent: external_exports.string().min(1, "intent is required"),
5000
5035
  scope: claimScopeSchema.optional(),
5001
- priority: external_exports.number().min(0).max(100).optional()
5036
+ priority: external_exports.number().min(0).max(100).optional(),
5037
+ allow_conflicts: external_exports.boolean().optional()
5002
5038
  }).refine(
5003
5039
  (data) => data.files && data.files.length > 0 || data.symbols && data.symbols.length > 0,
5004
5040
  { message: "Either files or symbols must be provided" }
@@ -5104,6 +5140,14 @@ function validationError(message) {
5104
5140
  }
5105
5141
 
5106
5142
  // src/mcp/tools/session.ts
5143
+ function parseJsonField(value) {
5144
+ if (!value) return null;
5145
+ try {
5146
+ return JSON.parse(value);
5147
+ } catch {
5148
+ return null;
5149
+ }
5150
+ }
5107
5151
  var sessionTools = [
5108
5152
  {
5109
5153
  name: "collab_session_start",
@@ -5159,6 +5203,39 @@ var sessionTools = [
5159
5203
  }
5160
5204
  }
5161
5205
  },
5206
+ {
5207
+ name: "collab_session_update",
5208
+ description: "Update session heartbeat, current task, todos, and progress.",
5209
+ inputSchema: {
5210
+ type: "object",
5211
+ properties: {
5212
+ session_id: {
5213
+ type: "string",
5214
+ description: "Session ID to update"
5215
+ },
5216
+ current_task: {
5217
+ type: "string",
5218
+ description: "Current work summary"
5219
+ },
5220
+ todos: {
5221
+ type: "array",
5222
+ items: {
5223
+ type: "object",
5224
+ properties: {
5225
+ content: { type: "string" },
5226
+ status: {
5227
+ type: "string",
5228
+ enum: ["pending", "in_progress", "completed"]
5229
+ }
5230
+ },
5231
+ required: ["content", "status"]
5232
+ },
5233
+ description: "Current todo state for progress reporting"
5234
+ }
5235
+ },
5236
+ required: ["session_id"]
5237
+ }
5238
+ },
5162
5239
  {
5163
5240
  name: "collab_config",
5164
5241
  description: "Configure session behavior.",
@@ -5308,7 +5385,17 @@ Decisions:
5308
5385
  id: session.id,
5309
5386
  name: session.name,
5310
5387
  status: session.status,
5388
+ current_task: session.current_task,
5389
+ progress: parseJsonField(session.progress),
5390
+ todos: parseJsonField(session.todos),
5311
5391
  active_claims: claims.length,
5392
+ claims: claims.map((c) => ({
5393
+ id: c.id,
5394
+ files: c.files,
5395
+ intent: c.intent,
5396
+ priority: getPriorityLevel(c.priority),
5397
+ created_at: c.created_at
5398
+ })),
5312
5399
  last_heartbeat: session.last_heartbeat
5313
5400
  };
5314
5401
  })
@@ -5318,6 +5405,34 @@ Decisions:
5318
5405
  total: sessionsWithClaims.length
5319
5406
  });
5320
5407
  }
5408
+ case "collab_session_update": {
5409
+ const validation = validateInput(statusUpdateSchema, args);
5410
+ if (!validation.success) {
5411
+ return validationError(validation.error);
5412
+ }
5413
+ const input = validation.data;
5414
+ const sessionResult = await validateActiveSession(db, input.session_id);
5415
+ if (!sessionResult.valid) {
5416
+ return sessionResult.error;
5417
+ }
5418
+ const updated = await updateSessionHeartbeat(db, input.session_id, {
5419
+ current_task: input.current_task,
5420
+ todos: input.todos
5421
+ });
5422
+ if (!updated) {
5423
+ return errorResponse(ERROR_CODES.SESSION_NOT_FOUND, "Session not found");
5424
+ }
5425
+ const session = await getSession(db, input.session_id);
5426
+ return successResponse({
5427
+ success: true,
5428
+ session_id: input.session_id,
5429
+ current_task: session?.current_task ?? null,
5430
+ progress: parseJsonField(session?.progress ?? null),
5431
+ todos: parseJsonField(session?.todos ?? null),
5432
+ last_heartbeat: session?.last_heartbeat ?? null,
5433
+ message: "Session updated."
5434
+ });
5435
+ }
5321
5436
  case "collab_config": {
5322
5437
  const validation = validateInput(configSchema, args);
5323
5438
  if (!validation.success) {
@@ -5457,6 +5572,10 @@ var claimTools = [
5457
5572
  type: "boolean",
5458
5573
  description: "Force release even if not owner (for release action)"
5459
5574
  },
5575
+ allow_conflicts: {
5576
+ type: "boolean",
5577
+ description: "Explicitly create a claim even when conflicts are detected (for create action)"
5578
+ },
5460
5579
  summary: {
5461
5580
  type: "string",
5462
5581
  description: "Release summary (for release action)"
@@ -5492,6 +5611,21 @@ async function handleClaimTool(db, name, args) {
5492
5611
  }
5493
5612
  const symbolFiles = (input.symbols ?? []).map((symbol) => symbol.file);
5494
5613
  const files = Array.from(/* @__PURE__ */ new Set([...input.files ?? [], ...symbolFiles]));
5614
+ const conflicts = await checkConflicts(db, files, input.session_id, input.symbols);
5615
+ if (conflicts.length > 0 && !input.allow_conflicts) {
5616
+ return successResponse({
5617
+ success: false,
5618
+ status: "blocked_by_conflicts",
5619
+ files,
5620
+ symbols: input.symbols ?? [],
5621
+ conflicts: conflicts.map((c) => ({
5622
+ session_name: c.session_name,
5623
+ file: c.file_path,
5624
+ intent: c.intent
5625
+ })),
5626
+ message: `Claim not created. ${conflicts.length} conflict(s) detected. Coordinate before proceeding.`
5627
+ });
5628
+ }
5495
5629
  const { claim } = await createClaim(db, {
5496
5630
  session_id: input.session_id,
5497
5631
  files,
@@ -5516,7 +5650,6 @@ Files: ${files.join(", ")}`,
5516
5650
  related_claim_id: claim.id,
5517
5651
  metadata: { claim_id: claim.id, files }
5518
5652
  });
5519
- const conflicts = await checkConflicts(db, files, input.session_id, input.symbols);
5520
5653
  if (conflicts.length > 0) {
5521
5654
  return successResponse({
5522
5655
  success: true,
@@ -6101,4 +6234,4 @@ export {
6101
6234
  getMcpTools,
6102
6235
  handleMcpRequest
6103
6236
  };
6104
- //# sourceMappingURL=chunk-SOUW3JSS.js.map
6237
+ //# sourceMappingURL=chunk-TFGXE3EI.js.map