mcp-coordinator 0.6.0 → 0.7.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.
Files changed (66) hide show
  1. package/README.md +24 -0
  2. package/dist/src/agent-activity.d.ts +13 -9
  3. package/dist/src/agent-activity.js +45 -24
  4. package/dist/src/agent-registry.d.ts +7 -7
  5. package/dist/src/agent-registry.js +19 -18
  6. package/dist/src/announce-workflow.d.ts +1 -0
  7. package/dist/src/announce-workflow.js +13 -12
  8. package/dist/src/auth/providers/registry.d.ts +4 -0
  9. package/dist/src/auth/providers/registry.js +7 -0
  10. package/dist/src/auth/providers/types.d.ts +11 -0
  11. package/dist/src/auth/providers/types.js +1 -0
  12. package/dist/src/auth.d.ts +24 -5
  13. package/dist/src/auth.js +172 -23
  14. package/dist/src/conflict-detector.d.ts +1 -0
  15. package/dist/src/conflict-detector.js +4 -4
  16. package/dist/src/consultation.d.ts +28 -14
  17. package/dist/src/consultation.js +101 -68
  18. package/dist/src/context-provider.d.ts +2 -2
  19. package/dist/src/context-provider.js +3 -4
  20. package/dist/src/database.js +203 -4
  21. package/dist/src/dependency-map.d.ts +25 -4
  22. package/dist/src/dependency-map.js +49 -11
  23. package/dist/src/file-tracker.d.ts +5 -4
  24. package/dist/src/file-tracker.js +16 -14
  25. package/dist/src/git-cochange-builder.d.ts +11 -2
  26. package/dist/src/git-cochange-builder.js +15 -7
  27. package/dist/src/http/handle-health.d.ts +9 -5
  28. package/dist/src/http/handle-health.js +22 -8
  29. package/dist/src/http/handle-rest.d.ts +3 -0
  30. package/dist/src/http/handle-rest.js +86 -57
  31. package/dist/src/http/utils.d.ts +4 -0
  32. package/dist/src/http/utils.js +7 -1
  33. package/dist/src/impact-scorer.d.ts +3 -0
  34. package/dist/src/impact-scorer.js +65 -51
  35. package/dist/src/introspection.d.ts +13 -7
  36. package/dist/src/introspection.js +34 -11
  37. package/dist/src/metrics.js +2 -1
  38. package/dist/src/mqtt-bridge.d.ts +3 -2
  39. package/dist/src/mqtt-bridge.js +33 -23
  40. package/dist/src/mqtt-broker.d.ts +16 -7
  41. package/dist/src/mqtt-broker.js +57 -15
  42. package/dist/src/security/audit.d.ts +11 -0
  43. package/dist/src/security/audit.js +7 -0
  44. package/dist/src/security/encryption.d.ts +17 -0
  45. package/dist/src/security/encryption.js +5 -0
  46. package/dist/src/serve-http.js +136 -57
  47. package/dist/src/server-setup.d.ts +12 -2
  48. package/dist/src/server-setup.js +33 -15
  49. package/dist/src/sse-emitter.d.ts +7 -4
  50. package/dist/src/sse-emitter.js +27 -21
  51. package/dist/src/tools/agents-tools.d.ts +2 -1
  52. package/dist/src/tools/agents-tools.js +36 -12
  53. package/dist/src/tools/consultation-tools.d.ts +2 -1
  54. package/dist/src/tools/consultation-tools.js +106 -40
  55. package/dist/src/tools/dependencies-tools.d.ts +2 -1
  56. package/dist/src/tools/dependencies-tools.js +25 -7
  57. package/dist/src/tools/files-tools.d.ts +2 -1
  58. package/dist/src/tools/files-tools.js +26 -8
  59. package/dist/src/tools/mqtt-tools.d.ts +7 -1
  60. package/dist/src/tools/mqtt-tools.js +27 -4
  61. package/dist/src/tools/status-tools.d.ts +7 -1
  62. package/dist/src/tools/status-tools.js +26 -9
  63. package/dist/src/types.d.ts +2 -0
  64. package/dist/src/working-files-tracker.d.ts +21 -11
  65. package/dist/src/working-files-tracker.js +32 -21
  66. package/package.json +4 -1
@@ -2,6 +2,7 @@ export type AgentConnectionStatus = "online" | "offline";
2
2
  export type ActivityStatus = "working" | "idle" | "waiting" | "offline";
3
3
  export interface Agent {
4
4
  id: string;
5
+ org_id: string;
5
6
  name: string;
6
7
  modules: string;
7
8
  status: AgentConnectionStatus;
@@ -19,6 +20,7 @@ export type ThreadStatus = "open" | "resolving" | "resolved" | "cancelled" | "po
19
20
  export type ResolutionType = "consensus" | "auto_resolved" | "timeout" | "closed" | "max_rounds" | "agent_departure";
20
21
  export interface Thread {
21
22
  id: string;
23
+ org_id: string;
22
24
  initiator_id: string;
23
25
  subject: string;
24
26
  plan: string | null;
@@ -6,10 +6,10 @@ import type { Metrics } from "./metrics.js";
6
6
  * historical log; this is current state with TTL.
7
7
  *
8
8
  * Lifecycle:
9
- * PreToolUse → start(agent, file, ttlMin) → UPSERT row
10
- * PostToolUse → stop(agent, file) → DELETE row
11
- * Sweeper → sweepExpired() → DELETE rows past claim_until
12
- * Agent LWT → clearForAgent(agent) → DELETE all rows for agent
9
+ * PreToolUse → start(orgId, agent, file, ttlMin) → UPSERT row
10
+ * PostToolUse → stop(orgId, agent, file) → DELETE row
11
+ * Sweeper → sweepExpired() → DELETE rows past claim_until (cross-org)
12
+ * Agent LWT → clearForAgent(agent) → DELETE all rows for agent (cross-org maintenance)
13
13
  */
14
14
  export declare class WorkingFilesTracker {
15
15
  private sweeperHandle;
@@ -18,18 +18,26 @@ export declare class WorkingFilesTracker {
18
18
  constructor(logger?: Logger, metrics?: Metrics);
19
19
  /**
20
20
  * Start (or refresh) a working-files claim. Idempotent: re-calling with
21
- * the same (agent_id, file_path) updates last_activity_at + claim_until
21
+ * the same (org_id, agent_id, file_path) updates last_activity_at + claim_until
22
22
  * without erroring.
23
23
  */
24
- start(agentId: string, filePath: string, ttlMinutes: number): void;
24
+ start(orgId: string, agentId: string, filePath: string, ttlMinutes: number): void;
25
25
  /**
26
26
  * Stop a working-files claim. No-op when no row matches (PostToolUse can
27
27
  * arrive after a TTL eviction or before the matching PreToolUse on slow Pre).
28
28
  */
29
- stop(agentId: string, filePath: string): void;
30
- /** Returns number of rows evicted. */
29
+ stop(orgId: string, agentId: string, filePath: string): void;
30
+ /** Returns number of rows evicted.
31
+ * cross-org maintenance — intentional: TTL expiry is a housekeeping concern,
32
+ * not a data-visibility concern; sweeping only own-org would leave other orgs'
33
+ * expired rows accumulating indefinitely.
34
+ */
31
35
  sweepExpired(): number;
32
- /** Called when an agent goes offline (MQTT LWT). Returns rows deleted. */
36
+ /** Called when an agent goes offline (MQTT LWT). Returns rows deleted.
37
+ * cross-org maintenance — intentional: MQTT topics carry no org_id today
38
+ * (see TODO(Task 22) in serve-http.ts); clearing by agent_id only is safe
39
+ * in single-tenant Phase 1 and matches the cross-org setOffline("default", …) pattern.
40
+ */
33
41
  clearForAgent(agentId: string): number;
34
42
  /**
35
43
  * Background sweeper. unref() so it doesn't keep the loop alive at shutdown.
@@ -37,6 +45,8 @@ export declare class WorkingFilesTracker {
37
45
  */
38
46
  startSweeper(intervalMs?: number): void;
39
47
  stopSweeper(): void;
40
- /** Read in-flight files map: file_path → set<agent_id>, excluding caller. */
41
- getIndex(filePaths: string[], excludeAgentId: string): Map<string, Set<string>>;
48
+ /** Read in-flight files map: file_path → set<agent_id>, excluding caller.
49
+ * Scoped to orgId so cross-org agent lists are not leaked.
50
+ */
51
+ getIndex(orgId: string, filePaths: string[], excludeAgentId: string): Map<string, Set<string>>;
42
52
  }
@@ -6,10 +6,10 @@ import { silentLogger } from "./logger.js";
6
6
  * historical log; this is current state with TTL.
7
7
  *
8
8
  * Lifecycle:
9
- * PreToolUse → start(agent, file, ttlMin) → UPSERT row
10
- * PostToolUse → stop(agent, file) → DELETE row
11
- * Sweeper → sweepExpired() → DELETE rows past claim_until
12
- * Agent LWT → clearForAgent(agent) → DELETE all rows for agent
9
+ * PreToolUse → start(orgId, agent, file, ttlMin) → UPSERT row
10
+ * PostToolUse → stop(orgId, agent, file) → DELETE row
11
+ * Sweeper → sweepExpired() → DELETE rows past claim_until (cross-org)
12
+ * Agent LWT → clearForAgent(agent) → DELETE all rows for agent (cross-org maintenance)
13
13
  */
14
14
  export class WorkingFilesTracker {
15
15
  sweeperHandle = null;
@@ -21,30 +21,34 @@ export class WorkingFilesTracker {
21
21
  }
22
22
  /**
23
23
  * Start (or refresh) a working-files claim. Idempotent: re-calling with
24
- * the same (agent_id, file_path) updates last_activity_at + claim_until
24
+ * the same (org_id, agent_id, file_path) updates last_activity_at + claim_until
25
25
  * without erroring.
26
26
  */
27
- start(agentId, filePath, ttlMinutes) {
27
+ start(orgId, agentId, filePath, ttlMinutes) {
28
28
  const db = getDb();
29
- const existing = db.prepare("SELECT 1 FROM working_files WHERE agent_id = ? AND file_path = ?")
30
- .get(agentId, filePath);
31
- db.prepare(`INSERT INTO working_files (agent_id, file_path, started_at, last_activity_at, claim_until)
32
- VALUES (?, ?, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), strftime('%Y-%m-%dT%H:%M:%SZ', 'now', '+' || CAST(? AS TEXT) || ' minutes'))
33
- ON CONFLICT(agent_id, file_path) DO UPDATE SET
29
+ const existing = db.prepare("SELECT 1 FROM working_files WHERE org_id = ? AND agent_id = ? AND file_path = ?")
30
+ .get(orgId, agentId, filePath);
31
+ db.prepare(`INSERT INTO working_files (org_id, agent_id, file_path, started_at, last_activity_at, claim_until)
32
+ VALUES (?, ?, ?, strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), strftime('%Y-%m-%dT%H:%M:%SZ', 'now', '+' || CAST(? AS TEXT) || ' minutes'))
33
+ ON CONFLICT(org_id, agent_id, file_path) DO UPDATE SET
34
34
  last_activity_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
35
- claim_until = strftime('%Y-%m-%dT%H:%M:%SZ', 'now', '+' || CAST(? AS TEXT) || ' minutes')`).run(agentId, filePath, ttlMinutes, ttlMinutes);
35
+ claim_until = strftime('%Y-%m-%dT%H:%M:%SZ', 'now', '+' || CAST(? AS TEXT) || ' minutes')`).run(orgId, agentId, filePath, ttlMinutes, ttlMinutes);
36
36
  this.metrics?.workingFilesStarts.inc({ result: existing ? "updated" : "inserted" });
37
37
  }
38
38
  /**
39
39
  * Stop a working-files claim. No-op when no row matches (PostToolUse can
40
40
  * arrive after a TTL eviction or before the matching PreToolUse on slow Pre).
41
41
  */
42
- stop(agentId, filePath) {
42
+ stop(orgId, agentId, filePath) {
43
43
  const db = getDb();
44
- db.prepare("DELETE FROM working_files WHERE agent_id = ? AND file_path = ?")
45
- .run(agentId, filePath);
44
+ db.prepare("DELETE FROM working_files WHERE org_id = ? AND agent_id = ? AND file_path = ?")
45
+ .run(orgId, agentId, filePath);
46
46
  }
47
- /** Returns number of rows evicted. */
47
+ /** Returns number of rows evicted.
48
+ * cross-org maintenance — intentional: TTL expiry is a housekeeping concern,
49
+ * not a data-visibility concern; sweeping only own-org would leave other orgs'
50
+ * expired rows accumulating indefinitely.
51
+ */
48
52
  sweepExpired() {
49
53
  const db = getDb();
50
54
  const result = db.prepare("DELETE FROM working_files WHERE claim_until < strftime('%Y-%m-%dT%H:%M:%SZ', 'now')").run();
@@ -55,7 +59,11 @@ export class WorkingFilesTracker {
55
59
  }
56
60
  return evicted;
57
61
  }
58
- /** Called when an agent goes offline (MQTT LWT). Returns rows deleted. */
62
+ /** Called when an agent goes offline (MQTT LWT). Returns rows deleted.
63
+ * cross-org maintenance — intentional: MQTT topics carry no org_id today
64
+ * (see TODO(Task 22) in serve-http.ts); clearing by agent_id only is safe
65
+ * in single-tenant Phase 1 and matches the cross-org setOffline("default", …) pattern.
66
+ */
59
67
  clearForAgent(agentId) {
60
68
  const db = getDb();
61
69
  const result = db.prepare("DELETE FROM working_files WHERE agent_id = ?").run(agentId);
@@ -87,17 +95,20 @@ export class WorkingFilesTracker {
87
95
  this.sweeperHandle = null;
88
96
  }
89
97
  }
90
- /** Read in-flight files map: file_path → set<agent_id>, excluding caller. */
91
- getIndex(filePaths, excludeAgentId) {
98
+ /** Read in-flight files map: file_path → set<agent_id>, excluding caller.
99
+ * Scoped to orgId so cross-org agent lists are not leaked.
100
+ */
101
+ getIndex(orgId, filePaths, excludeAgentId) {
92
102
  const index = new Map();
93
103
  if (filePaths.length === 0)
94
104
  return index;
95
105
  const db = getDb();
96
106
  const placeholders = filePaths.map(() => "?").join(",");
97
107
  const rows = db.prepare(`SELECT DISTINCT file_path, agent_id FROM working_files
98
- WHERE file_path IN (${placeholders})
108
+ WHERE org_id = ?
109
+ AND file_path IN (${placeholders})
99
110
  AND agent_id != ?
100
- AND claim_until > strftime('%Y-%m-%dT%H:%M:%SZ', 'now')`).all(...filePaths, excludeAgentId);
111
+ AND claim_until > strftime('%Y-%m-%dT%H:%M:%SZ', 'now')`).all(orgId, ...filePaths, excludeAgentId);
101
112
  for (const r of rows) {
102
113
  let set = index.get(r.file_path);
103
114
  if (!set) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-coordinator",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
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
  }