mcp-coordinator 0.2.0 → 0.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.
Files changed (44) hide show
  1. package/README.md +846 -835
  2. package/dashboard/Dockerfile +19 -19
  3. package/dashboard/public/index.html +1178 -1178
  4. package/dist/cli/dashboard.js +9 -5
  5. package/dist/cli/server/start.js +24 -1
  6. package/dist/cli/server/status.js +16 -23
  7. package/dist/src/agent-activity.js +6 -6
  8. package/dist/src/agent-registry.js +6 -6
  9. package/dist/src/announce-workflow.d.ts +52 -0
  10. package/dist/src/announce-workflow.js +91 -0
  11. package/dist/src/consultation.d.ts +14 -0
  12. package/dist/src/consultation.js +110 -45
  13. package/dist/src/database.js +126 -126
  14. package/dist/src/dependency-map.js +3 -3
  15. package/dist/src/file-tracker.js +8 -8
  16. package/dist/src/http/handle-rest.d.ts +23 -0
  17. package/dist/src/http/handle-rest.js +374 -0
  18. package/dist/src/http/utils.d.ts +15 -0
  19. package/dist/src/http/utils.js +39 -0
  20. package/dist/src/introspection.js +1 -1
  21. package/dist/src/mqtt-bridge.d.ts +2 -0
  22. package/dist/src/mqtt-bridge.js +2 -0
  23. package/dist/src/mqtt-broker.d.ts +16 -0
  24. package/dist/src/mqtt-broker.js +16 -1
  25. package/dist/src/path-guard.d.ts +14 -0
  26. package/dist/src/path-guard.js +44 -0
  27. package/dist/src/reset-guard.d.ts +16 -0
  28. package/dist/src/reset-guard.js +24 -0
  29. package/dist/src/serve-http.d.ts +31 -1
  30. package/dist/src/serve-http.js +154 -445
  31. package/dist/src/server-setup.js +15 -364
  32. package/dist/src/tools/agents-tools.d.ts +8 -0
  33. package/dist/src/tools/agents-tools.js +46 -0
  34. package/dist/src/tools/consultation-tools.d.ts +21 -0
  35. package/dist/src/tools/consultation-tools.js +170 -0
  36. package/dist/src/tools/dependencies-tools.d.ts +8 -0
  37. package/dist/src/tools/dependencies-tools.js +27 -0
  38. package/dist/src/tools/files-tools.d.ts +8 -0
  39. package/dist/src/tools/files-tools.js +28 -0
  40. package/dist/src/tools/mqtt-tools.d.ts +9 -0
  41. package/dist/src/tools/mqtt-tools.js +33 -0
  42. package/dist/src/tools/status-tools.d.ts +8 -0
  43. package/dist/src/tools/status-tools.js +63 -0
  44. package/package.json +81 -80
@@ -3,132 +3,132 @@ import { mkdirSync } from "fs";
3
3
  import { createRequire } from "module";
4
4
  const require = createRequire(import.meta.url);
5
5
  let db;
6
- const SCHEMA = `
7
- CREATE TABLE IF NOT EXISTS agents (
8
- id TEXT PRIMARY KEY,
9
- name TEXT NOT NULL,
10
- modules TEXT DEFAULT '[]',
11
- status TEXT DEFAULT 'offline',
12
- registered_at TEXT DEFAULT CURRENT_TIMESTAMP,
13
- last_seen_at TEXT DEFAULT CURRENT_TIMESTAMP
14
- );
15
-
16
- CREATE TABLE IF NOT EXISTS threads (
17
- id TEXT PRIMARY KEY,
18
- initiator_id TEXT NOT NULL,
19
- subject TEXT NOT NULL,
20
- plan TEXT,
21
- target_modules TEXT DEFAULT '[]',
22
- target_files TEXT DEFAULT '[]',
23
- status TEXT DEFAULT 'open',
24
- resolution_summary TEXT,
25
- conflicts TEXT,
26
- round INTEGER DEFAULT 1,
27
- max_rounds INTEGER DEFAULT 4,
28
- timeout_seconds INTEGER DEFAULT 600,
29
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
30
- resolved_at TEXT,
31
- expected_respondents TEXT,
32
- depends_on_files TEXT,
33
- exports_affected TEXT,
34
- claimed_by TEXT,
35
- claimed_at TEXT,
36
- FOREIGN KEY (initiator_id) REFERENCES agents(id)
37
- );
38
-
39
- CREATE TABLE IF NOT EXISTS thread_messages (
40
- id TEXT PRIMARY KEY,
41
- thread_id TEXT NOT NULL,
42
- agent_id TEXT NOT NULL,
43
- agent_name TEXT,
44
- type TEXT NOT NULL,
45
- content TEXT NOT NULL,
46
- context_snapshot TEXT,
47
- in_reply_to TEXT,
48
- round INTEGER NOT NULL,
49
- token_estimate INTEGER DEFAULT 0,
50
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
51
- FOREIGN KEY (thread_id) REFERENCES threads(id),
52
- FOREIGN KEY (agent_id) REFERENCES agents(id)
53
- );
54
-
55
- CREATE TABLE IF NOT EXISTS action_summaries (
56
- id TEXT PRIMARY KEY,
57
- session_id TEXT NOT NULL,
58
- agent_id TEXT NOT NULL,
59
- file_path TEXT,
60
- summary TEXT NOT NULL,
61
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
62
- FOREIGN KEY (agent_id) REFERENCES agents(id)
63
- );
64
-
65
- CREATE TABLE IF NOT EXISTS events (
66
- id INTEGER PRIMARY KEY AUTOINCREMENT,
67
- type TEXT NOT NULL,
68
- payload TEXT NOT NULL,
69
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
70
- );
71
-
72
- CREATE TABLE IF NOT EXISTS dependency_map (
73
- module_id TEXT PRIMARY KEY,
74
- depends_on TEXT DEFAULT '[]',
75
- exports TEXT DEFAULT '[]',
76
- owners TEXT DEFAULT '[]'
77
- );
78
-
79
- CREATE TABLE IF NOT EXISTS file_activity (
80
- id INTEGER PRIMARY KEY AUTOINCREMENT,
81
- session_id TEXT NOT NULL,
82
- agent_id TEXT NOT NULL,
83
- agent_name TEXT,
84
- tool_name TEXT NOT NULL,
85
- file_path TEXT NOT NULL,
86
- module TEXT,
87
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
88
- );
89
-
90
- CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status);
91
- CREATE INDEX IF NOT EXISTS idx_threads_initiator ON threads(initiator_id);
92
- CREATE INDEX IF NOT EXISTS idx_messages_thread ON thread_messages(thread_id);
93
- CREATE INDEX IF NOT EXISTS idx_messages_agent ON thread_messages(agent_id);
94
- CREATE INDEX IF NOT EXISTS idx_summaries_agent ON action_summaries(agent_id);
95
- CREATE INDEX IF NOT EXISTS idx_summaries_session ON action_summaries(session_id);
96
- CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
97
- CREATE INDEX IF NOT EXISTS idx_file_activity_agent ON file_activity(agent_id);
98
- CREATE INDEX IF NOT EXISTS idx_file_activity_path ON file_activity(file_path);
99
-
100
- CREATE TABLE IF NOT EXISTS introspections (
101
- id TEXT PRIMARY KEY,
102
- thread_id TEXT NOT NULL,
103
- agent_id TEXT NOT NULL,
104
- score INTEGER NOT NULL,
105
- reasons TEXT,
106
- status TEXT DEFAULT 'pending',
107
- response TEXT,
108
- concerned INTEGER DEFAULT 0,
109
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
110
- responded_at TEXT,
111
- FOREIGN KEY (thread_id) REFERENCES threads(id),
112
- FOREIGN KEY (agent_id) REFERENCES agents(id)
113
- );
114
-
115
- CREATE INDEX IF NOT EXISTS idx_introspections_agent ON introspections(agent_id);
116
- CREATE INDEX IF NOT EXISTS idx_introspections_status ON introspections(status);
117
-
118
- CREATE TABLE IF NOT EXISTS agent_activity_status (
119
- agent_id TEXT PRIMARY KEY,
120
- activity_status TEXT DEFAULT 'idle',
121
- current_file TEXT,
122
- current_thread TEXT,
123
- last_activity_at TEXT DEFAULT CURRENT_TIMESTAMP,
124
- FOREIGN KEY (agent_id) REFERENCES agents(id)
125
- );
126
-
127
- CREATE TABLE IF NOT EXISTS revoked_agents (
128
- agent_id TEXT PRIMARY KEY,
129
- revoked_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
130
- revoked_by TEXT NOT NULL
131
- );
6
+ const SCHEMA = `
7
+ CREATE TABLE IF NOT EXISTS agents (
8
+ id TEXT PRIMARY KEY,
9
+ name TEXT NOT NULL,
10
+ modules TEXT DEFAULT '[]',
11
+ status TEXT DEFAULT 'offline',
12
+ registered_at TEXT DEFAULT CURRENT_TIMESTAMP,
13
+ last_seen_at TEXT DEFAULT CURRENT_TIMESTAMP
14
+ );
15
+
16
+ CREATE TABLE IF NOT EXISTS threads (
17
+ id TEXT PRIMARY KEY,
18
+ initiator_id TEXT NOT NULL,
19
+ subject TEXT NOT NULL,
20
+ plan TEXT,
21
+ target_modules TEXT DEFAULT '[]',
22
+ target_files TEXT DEFAULT '[]',
23
+ status TEXT DEFAULT 'open',
24
+ resolution_summary TEXT,
25
+ conflicts TEXT,
26
+ round INTEGER DEFAULT 1,
27
+ max_rounds INTEGER DEFAULT 4,
28
+ timeout_seconds INTEGER DEFAULT 600,
29
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
30
+ resolved_at TEXT,
31
+ expected_respondents TEXT,
32
+ depends_on_files TEXT,
33
+ exports_affected TEXT,
34
+ claimed_by TEXT,
35
+ claimed_at TEXT,
36
+ FOREIGN KEY (initiator_id) REFERENCES agents(id)
37
+ );
38
+
39
+ CREATE TABLE IF NOT EXISTS thread_messages (
40
+ id TEXT PRIMARY KEY,
41
+ thread_id TEXT NOT NULL,
42
+ agent_id TEXT NOT NULL,
43
+ agent_name TEXT,
44
+ type TEXT NOT NULL,
45
+ content TEXT NOT NULL,
46
+ context_snapshot TEXT,
47
+ in_reply_to TEXT,
48
+ round INTEGER NOT NULL,
49
+ token_estimate INTEGER DEFAULT 0,
50
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
51
+ FOREIGN KEY (thread_id) REFERENCES threads(id),
52
+ FOREIGN KEY (agent_id) REFERENCES agents(id)
53
+ );
54
+
55
+ CREATE TABLE IF NOT EXISTS action_summaries (
56
+ id TEXT PRIMARY KEY,
57
+ session_id TEXT NOT NULL,
58
+ agent_id TEXT NOT NULL,
59
+ file_path TEXT,
60
+ summary TEXT NOT NULL,
61
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
62
+ FOREIGN KEY (agent_id) REFERENCES agents(id)
63
+ );
64
+
65
+ CREATE TABLE IF NOT EXISTS events (
66
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
67
+ type TEXT NOT NULL,
68
+ payload TEXT NOT NULL,
69
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
70
+ );
71
+
72
+ CREATE TABLE IF NOT EXISTS dependency_map (
73
+ module_id TEXT PRIMARY KEY,
74
+ depends_on TEXT DEFAULT '[]',
75
+ exports TEXT DEFAULT '[]',
76
+ owners TEXT DEFAULT '[]'
77
+ );
78
+
79
+ CREATE TABLE IF NOT EXISTS file_activity (
80
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
81
+ session_id TEXT NOT NULL,
82
+ agent_id TEXT NOT NULL,
83
+ agent_name TEXT,
84
+ tool_name TEXT NOT NULL,
85
+ file_path TEXT NOT NULL,
86
+ module TEXT,
87
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
88
+ );
89
+
90
+ CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status);
91
+ CREATE INDEX IF NOT EXISTS idx_threads_initiator ON threads(initiator_id);
92
+ CREATE INDEX IF NOT EXISTS idx_messages_thread ON thread_messages(thread_id);
93
+ CREATE INDEX IF NOT EXISTS idx_messages_agent ON thread_messages(agent_id);
94
+ CREATE INDEX IF NOT EXISTS idx_summaries_agent ON action_summaries(agent_id);
95
+ CREATE INDEX IF NOT EXISTS idx_summaries_session ON action_summaries(session_id);
96
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
97
+ CREATE INDEX IF NOT EXISTS idx_file_activity_agent ON file_activity(agent_id);
98
+ CREATE INDEX IF NOT EXISTS idx_file_activity_path ON file_activity(file_path);
99
+
100
+ CREATE TABLE IF NOT EXISTS introspections (
101
+ id TEXT PRIMARY KEY,
102
+ thread_id TEXT NOT NULL,
103
+ agent_id TEXT NOT NULL,
104
+ score INTEGER NOT NULL,
105
+ reasons TEXT,
106
+ status TEXT DEFAULT 'pending',
107
+ response TEXT,
108
+ concerned INTEGER DEFAULT 0,
109
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
110
+ responded_at TEXT,
111
+ FOREIGN KEY (thread_id) REFERENCES threads(id),
112
+ FOREIGN KEY (agent_id) REFERENCES agents(id)
113
+ );
114
+
115
+ CREATE INDEX IF NOT EXISTS idx_introspections_agent ON introspections(agent_id);
116
+ CREATE INDEX IF NOT EXISTS idx_introspections_status ON introspections(status);
117
+
118
+ CREATE TABLE IF NOT EXISTS agent_activity_status (
119
+ agent_id TEXT PRIMARY KEY,
120
+ activity_status TEXT DEFAULT 'idle',
121
+ current_file TEXT,
122
+ current_thread TEXT,
123
+ last_activity_at TEXT DEFAULT CURRENT_TIMESTAMP,
124
+ FOREIGN KEY (agent_id) REFERENCES agents(id)
125
+ );
126
+
127
+ CREATE TABLE IF NOT EXISTS revoked_agents (
128
+ agent_id TEXT PRIMARY KEY,
129
+ revoked_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
130
+ revoked_by TEXT NOT NULL
131
+ );
132
132
  `;
133
133
  function createBetterSqlite3(dataDir) {
134
134
  mkdirSync(dataDir, { recursive: true });
@@ -16,9 +16,9 @@ export class DependencyMapper {
16
16
  }
17
17
  setMap(map) {
18
18
  const db = getDb();
19
- const stmt = db.prepare(`INSERT INTO dependency_map (module_id, depends_on, exports, owners)
20
- VALUES (?, ?, ?, ?)
21
- ON CONFLICT(module_id) DO UPDATE SET
19
+ const stmt = db.prepare(`INSERT INTO dependency_map (module_id, depends_on, exports, owners)
20
+ VALUES (?, ?, ?, ?)
21
+ ON CONFLICT(module_id) DO UPDATE SET
22
22
  depends_on = excluded.depends_on, exports = excluded.exports, owners = excluded.owners`);
23
23
  const tx = db.transaction(() => {
24
24
  for (const [id, info] of Object.entries(map)) {
@@ -3,7 +3,7 @@ export class FileTracker {
3
3
  log(params) {
4
4
  const db = getDb();
5
5
  const module = this.fileToModule(params.file_path);
6
- db.prepare(`INSERT INTO file_activity (session_id, agent_id, agent_name, tool_name, file_path, module)
6
+ db.prepare(`INSERT INTO file_activity (session_id, agent_id, agent_name, tool_name, file_path, module)
7
7
  VALUES (?, ?, ?, ?, ?, ?)`).run(params.session_id, params.agent_id, params.agent_name || null, params.tool_name, params.file_path, module);
8
8
  }
9
9
  getBySession(sessionId) {
@@ -12,11 +12,11 @@ export class FileTracker {
12
12
  }
13
13
  getHotFiles(sinceMinutes = 30) {
14
14
  const db = getDb();
15
- const rows = db.prepare(`SELECT file_path, COUNT(DISTINCT agent_id) as agent_count, GROUP_CONCAT(DISTINCT agent_id) as agents
16
- FROM file_activity
17
- WHERE created_at > datetime('now', '-' || ? || ' minutes')
18
- GROUP BY file_path
19
- HAVING COUNT(DISTINCT agent_id) > 1
15
+ const rows = db.prepare(`SELECT file_path, COUNT(DISTINCT agent_id) as agent_count, GROUP_CONCAT(DISTINCT agent_id) as agents
16
+ FROM file_activity
17
+ WHERE created_at > datetime('now', '-' || ? || ' minutes')
18
+ GROUP BY file_path
19
+ HAVING COUNT(DISTINCT agent_id) > 1
20
20
  ORDER BY agent_count DESC`).all(sinceMinutes);
21
21
  return rows.map((r) => ({
22
22
  file_path: r.file_path,
@@ -26,8 +26,8 @@ export class FileTracker {
26
26
  }
27
27
  checkFileConflict(filePath, agentId, withinMinutes = 30) {
28
28
  const db = getDb();
29
- const rows = db.prepare(`SELECT DISTINCT agent_id FROM file_activity
30
- WHERE file_path = ? AND agent_id != ?
29
+ const rows = db.prepare(`SELECT DISTINCT agent_id FROM file_activity
30
+ WHERE file_path = ? AND agent_id != ?
31
31
  AND created_at > datetime('now', '-' || ? || ' minutes')`).all(filePath, agentId, withinMinutes);
32
32
  return { conflict: rows.length > 0, agents: rows.map((r) => r.agent_id) };
33
33
  }
@@ -0,0 +1,23 @@
1
+ import type { IncomingMessage, ServerResponse } from "http";
2
+ import type { CoordinatorServices } from "../server-setup.js";
3
+ import type { Logger } from "../logger.js";
4
+ /**
5
+ * S1: REST router extracted from serve-http.ts. Was a 382-line `handleRest`
6
+ * function inside startServer's closure with module-scope captures for
7
+ * services, httpLog, currentRunConfig, AUTH_ENABLED, etc.
8
+ *
9
+ * The body is unchanged — only ambient closure references became explicit
10
+ * fields on RestContext. parseBody/json moved to ./utils.js to share between
11
+ * REST + SSE + auth handlers.
12
+ *
13
+ * The currentRunConfig mutable state stays in serve-http.ts (single source
14
+ * of truth) and is exposed via getRunConfig/setRunConfig on the context.
15
+ */
16
+ export interface RestContext {
17
+ services: CoordinatorServices;
18
+ httpLog: Logger;
19
+ authEnabled: boolean;
20
+ getRunConfig: () => Record<string, unknown> | null;
21
+ setRunConfig: (cfg: Record<string, unknown> | null) => void;
22
+ }
23
+ export declare function handleRest(req: IncomingMessage, res: ServerResponse, ctx: RestContext): Promise<void>;