mcp-coordinator 0.6.0 → 0.6.1

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.
@@ -3,6 +3,7 @@ import { getDb } from "../database.js";
3
3
  import { runCommonAnnounceFlow } from "../announce-workflow.js";
4
4
  import { canResetDb } from "../reset-guard.js";
5
5
  import { parseBody, json } from "./utils.js";
6
+ import { normalizePath } from "../path-normalize.js";
6
7
  export async function handleRest(req, res, ctx) {
7
8
  const { services, httpLog, authEnabled, getRunConfig, setRunConfig } = ctx;
8
9
  const url = req.url || "";
@@ -378,6 +379,15 @@ export async function handleRest(req, res, ctx) {
378
379
  json(res, { error: "agent_name must be string when present" }, 400);
379
380
  return;
380
381
  }
382
+ const repoRoot = process.env.COORDINATOR_REPO_ROOT || null;
383
+ let filePath;
384
+ try {
385
+ filePath = normalizePath(repoRoot, body.file_path);
386
+ }
387
+ catch (err) {
388
+ json(res, { error: `invalid file_path: ${err.message}` }, 400);
389
+ return;
390
+ }
381
391
  const MAX_CONTENT = 262144;
382
392
  let symbols = null;
383
393
  let contentHash = null;
@@ -387,14 +397,14 @@ export async function handleRest(req, res, ctx) {
387
397
  return;
388
398
  }
389
399
  contentHash = createHash("sha256").update(body.content).digest("hex");
390
- symbols = ctx.services.treeSitter.extract(body.file_path, body.content, null);
400
+ symbols = ctx.services.treeSitter.extract(filePath, body.content, null);
391
401
  }
392
402
  ctx.services.fileTracker.log({
393
403
  session_id: body.session_id,
394
404
  agent_id: body.agent_id,
395
405
  agent_name: body.agent_name,
396
406
  tool_name: body.tool_name,
397
- file_path: body.file_path,
407
+ file_path: filePath,
398
408
  content_hash: contentHash,
399
409
  symbols_touched: symbols,
400
410
  });
@@ -405,8 +415,17 @@ export async function handleRest(req, res, ctx) {
405
415
  json(res, { error: "agent_id and file_path required" }, 400);
406
416
  return;
407
417
  }
418
+ const repoRoot = process.env.COORDINATOR_REPO_ROOT || null;
419
+ let filePath;
420
+ try {
421
+ filePath = normalizePath(repoRoot, body.file_path);
422
+ }
423
+ catch (err) {
424
+ json(res, { error: `invalid file_path: ${err.message}` }, 400);
425
+ return;
426
+ }
408
427
  const ttl = parseInt(process.env.COORDINATOR_WORKING_FILES_TTL_MIN || "30", 10);
409
- services.workingFiles.start(body.agent_id, body.file_path, ttl);
428
+ services.workingFiles.start(body.agent_id, filePath, ttl);
410
429
  json(res, { ok: true });
411
430
  }
412
431
  else if (url === "/api/working-files/stop" && req.method === "POST") {
@@ -414,7 +433,16 @@ export async function handleRest(req, res, ctx) {
414
433
  json(res, { error: "agent_id and file_path required" }, 400);
415
434
  return;
416
435
  }
417
- services.workingFiles.stop(body.agent_id, body.file_path);
436
+ const repoRoot = process.env.COORDINATOR_REPO_ROOT || null;
437
+ let filePath;
438
+ try {
439
+ filePath = normalizePath(repoRoot, body.file_path);
440
+ }
441
+ catch (err) {
442
+ json(res, { error: `invalid file_path: ${err.message}` }, 400);
443
+ return;
444
+ }
445
+ services.workingFiles.stop(body.agent_id, filePath);
418
446
  json(res, { ok: true });
419
447
  }
420
448
  else if (url?.startsWith("/api/scoring-stats") && req.method === "GET") {
@@ -25,9 +25,9 @@ export function registerConsultationTools(server, services, mcpLog) {
25
25
  subject: z.string(),
26
26
  plan: z.string().optional(),
27
27
  target_modules: z.array(z.string()),
28
- target_files: z.array(z.string()),
29
- depends_on_files: z.array(z.string()).optional(),
30
- exports_affected: z.array(z.string()).optional(),
28
+ target_files: z.array(z.string()).describe("Repo-relative file paths (forward-slash, e.g. 'src/foo.ts'). Absolute paths are not accepted in team-mode."),
29
+ depends_on_files: z.array(z.string()).optional().describe("Repo-relative file paths your work depends on."),
30
+ exports_affected: z.array(z.string()).optional().describe("Repo-relative file paths whose exports your work modifies."),
31
31
  keep_open: z.boolean().optional().describe("Keep thread open even if no agents are concerned (for manual coordination like games or debates)"),
32
32
  assigned_to: z.string().optional().describe("Directed-dispatch: only this agent_id will be allowed to claim the thread. Use for lead→worker handoffs in maitre/chaine/relais presets. Implies keep_open=true."),
33
33
  target_symbols: z.array(z.string().max(256)).max(200).optional()
@@ -162,7 +162,7 @@ export function registerConsultationTools(server, services, mcpLog) {
162
162
  server.tool("log_action_summary", "Log a one-liner summary of an action", {
163
163
  session_id: z.string(),
164
164
  agent_id: z.string(),
165
- file_path: z.string().optional(),
165
+ file_path: z.string().optional().describe("Repo-relative file path."),
166
166
  summary: z.string(),
167
167
  }, async ({ session_id, agent_id, file_path, summary }) => {
168
168
  const result = consultation.logActionSummary({ session_id, agent_id, file_path, summary });
@@ -18,7 +18,7 @@ export function registerFilesTools(server, services, _mcpLog) {
18
18
  return { content: [{ type: "text", text: JSON.stringify(files) }] };
19
19
  });
20
20
  server.tool("check_file_conflict", "Check if another agent is editing a file", {
21
- file_path: z.string(),
21
+ file_path: z.string().describe("Repo-relative file path."),
22
22
  agent_id: z.string(),
23
23
  within_minutes: z.number().optional(),
24
24
  }, async ({ file_path, agent_id, within_minutes }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-coordinator",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "mcpName": "io.github.swoofer/mcp-coordinator",
5
5
  "description": "Embedded MQTT broker + MCP server for multi-agent coordination",
6
6
  "type": "module",
@@ -94,6 +94,9 @@
94
94
  "tree-sitter-swift": "^0.6.0",
95
95
  "tree-sitter-bash": "^0.21.0"
96
96
  },
97
+ "overrides": {
98
+ "ip-address": "^10.2.0"
99
+ },
97
100
  "engines": {
98
101
  "node": ">=20"
99
102
  }