mcp-coordinator 0.5.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.
- package/README.md +938 -860
- package/dashboard/Dockerfile +19 -19
- package/dashboard/public/index.html +1201 -1201
- package/dist/src/agent-activity.js +6 -6
- package/dist/src/agent-registry.js +6 -6
- package/dist/src/consultation.js +20 -20
- package/dist/src/database.js +165 -165
- package/dist/src/dependency-map.js +3 -3
- package/dist/src/file-tracker.js +12 -12
- package/dist/src/http/handle-rest.js +56 -13
- package/dist/src/impact-scorer.js +15 -15
- package/dist/src/introspection.js +1 -1
- package/dist/src/tools/consultation-tools.js +4 -4
- package/dist/src/tools/files-tools.js +1 -1
- package/dist/src/working-files-tracker.js +7 -7
- package/package.json +103 -100
|
@@ -54,15 +54,15 @@ export class ImpactScorer {
|
|
|
54
54
|
if (params.target_symbols && params.target_symbols.length > 0 && params.target_files.length > 0) {
|
|
55
55
|
const db = getDb();
|
|
56
56
|
const placeholders = params.target_files.map(() => "?").join(",");
|
|
57
|
-
const rows = db.prepare(`SELECT agent_id, file_path, symbols_touched
|
|
58
|
-
FROM file_activity
|
|
59
|
-
WHERE file_path IN (${placeholders})
|
|
60
|
-
AND symbols_touched IS NOT NULL
|
|
61
|
-
AND id IN (
|
|
62
|
-
SELECT MAX(id) FROM file_activity
|
|
63
|
-
WHERE file_path IN (${placeholders})
|
|
64
|
-
AND symbols_touched IS NOT NULL
|
|
65
|
-
GROUP BY agent_id, file_path
|
|
57
|
+
const rows = db.prepare(`SELECT agent_id, file_path, symbols_touched
|
|
58
|
+
FROM file_activity
|
|
59
|
+
WHERE file_path IN (${placeholders})
|
|
60
|
+
AND symbols_touched IS NOT NULL
|
|
61
|
+
AND id IN (
|
|
62
|
+
SELECT MAX(id) FROM file_activity
|
|
63
|
+
WHERE file_path IN (${placeholders})
|
|
64
|
+
AND symbols_touched IS NOT NULL
|
|
65
|
+
GROUP BY agent_id, file_path
|
|
66
66
|
)`).all(...params.target_files, ...params.target_files);
|
|
67
67
|
symbolsByFileAgent = new Map();
|
|
68
68
|
for (const r of rows) {
|
|
@@ -182,7 +182,7 @@ export class ImpactScorer {
|
|
|
182
182
|
// touched the partner file, apply the co-change score.
|
|
183
183
|
const db = getDb();
|
|
184
184
|
for (const targetFile of params.target_files) {
|
|
185
|
-
const rows = db.prepare(`SELECT file_a, file_b, count, total_commits FROM git_cochange
|
|
185
|
+
const rows = db.prepare(`SELECT file_a, file_b, count, total_commits FROM git_cochange
|
|
186
186
|
WHERE file_a = ? OR file_b = ?`).all(targetFile, targetFile);
|
|
187
187
|
for (const r of rows) {
|
|
188
188
|
const partner = r.file_a === targetFile ? r.file_b : r.file_a;
|
|
@@ -195,9 +195,9 @@ export class ImpactScorer {
|
|
|
195
195
|
if (layer4Score === 0)
|
|
196
196
|
continue;
|
|
197
197
|
// Did the OTHER agent touch the partner file recently?
|
|
198
|
-
const partnerActivity = db.prepare(`SELECT 1 FROM file_activity
|
|
199
|
-
WHERE file_path = ? AND agent_id = ?
|
|
200
|
-
AND created_at > datetime('now', '-60 minutes')
|
|
198
|
+
const partnerActivity = db.prepare(`SELECT 1 FROM file_activity
|
|
199
|
+
WHERE file_path = ? AND agent_id = ?
|
|
200
|
+
AND created_at > datetime('now', '-60 minutes')
|
|
201
201
|
LIMIT 1`).get(partner, agent.id);
|
|
202
202
|
if (partnerActivity) {
|
|
203
203
|
maxScore = Math.max(maxScore, layer4Score);
|
|
@@ -224,8 +224,8 @@ export class ImpactScorer {
|
|
|
224
224
|
}
|
|
225
225
|
getRecentSymbolsForFile(filePath, agentId) {
|
|
226
226
|
const db = getDb();
|
|
227
|
-
const row = db.prepare(`SELECT symbols_touched FROM file_activity
|
|
228
|
-
WHERE agent_id = ? AND file_path = ? AND symbols_touched IS NOT NULL
|
|
227
|
+
const row = db.prepare(`SELECT symbols_touched FROM file_activity
|
|
228
|
+
WHERE agent_id = ? AND file_path = ? AND symbols_touched IS NOT NULL
|
|
229
229
|
ORDER BY id DESC LIMIT 1`).get(agentId, filePath);
|
|
230
230
|
if (!row || !row.symbols_touched)
|
|
231
231
|
return null;
|
|
@@ -4,7 +4,7 @@ export class IntrospectionManager {
|
|
|
4
4
|
create(params) {
|
|
5
5
|
const db = getDb();
|
|
6
6
|
const id = randomUUID();
|
|
7
|
-
db.prepare(`INSERT INTO introspections (id, thread_id, agent_id, score, reasons)
|
|
7
|
+
db.prepare(`INSERT INTO introspections (id, thread_id, agent_id, score, reasons)
|
|
8
8
|
VALUES (?, ?, ?, ?, ?)`).run(id, params.thread_id, params.agent_id, params.score, JSON.stringify(params.reasons));
|
|
9
9
|
return this.get(id);
|
|
10
10
|
}
|
|
@@ -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 }) => {
|
|
@@ -28,10 +28,10 @@ export class WorkingFilesTracker {
|
|
|
28
28
|
const db = getDb();
|
|
29
29
|
const existing = db.prepare("SELECT 1 FROM working_files WHERE agent_id = ? AND file_path = ?")
|
|
30
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
|
|
34
|
-
last_activity_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
|
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
|
|
34
|
+
last_activity_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'),
|
|
35
35
|
claim_until = strftime('%Y-%m-%dT%H:%M:%SZ', 'now', '+' || CAST(? AS TEXT) || ' minutes')`).run(agentId, filePath, ttlMinutes, ttlMinutes);
|
|
36
36
|
this.metrics?.workingFilesStarts.inc({ result: existing ? "updated" : "inserted" });
|
|
37
37
|
}
|
|
@@ -94,9 +94,9 @@ export class WorkingFilesTracker {
|
|
|
94
94
|
return index;
|
|
95
95
|
const db = getDb();
|
|
96
96
|
const placeholders = filePaths.map(() => "?").join(",");
|
|
97
|
-
const rows = db.prepare(`SELECT DISTINCT file_path, agent_id FROM working_files
|
|
98
|
-
WHERE file_path IN (${placeholders})
|
|
99
|
-
AND agent_id != ?
|
|
97
|
+
const rows = db.prepare(`SELECT DISTINCT file_path, agent_id FROM working_files
|
|
98
|
+
WHERE file_path IN (${placeholders})
|
|
99
|
+
AND agent_id != ?
|
|
100
100
|
AND claim_until > strftime('%Y-%m-%dT%H:%M:%SZ', 'now')`).all(...filePaths, excludeAgentId);
|
|
101
101
|
for (const r of rows) {
|
|
102
102
|
let set = index.get(r.file_path);
|
package/package.json
CHANGED
|
@@ -1,100 +1,103 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mcp-coordinator",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"mcpName": "io.github.swoofer/mcp-coordinator",
|
|
5
|
-
"description": "Embedded MQTT broker + MCP server for multi-agent coordination",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"license": "MIT",
|
|
8
|
-
"author": "Maxime Gagnon",
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/swoofer/mcp-coordinator.git"
|
|
12
|
-
},
|
|
13
|
-
"homepage": "https://swoofer.github.io/mcp-coordinator",
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/swoofer/mcp-coordinator/issues"
|
|
16
|
-
},
|
|
17
|
-
"keywords": [
|
|
18
|
-
"mcp",
|
|
19
|
-
"mqtt",
|
|
20
|
-
"broker",
|
|
21
|
-
"multi-agent",
|
|
22
|
-
"coordination",
|
|
23
|
-
"claude",
|
|
24
|
-
"anthropic"
|
|
25
|
-
],
|
|
26
|
-
"main": "./dist/src/index.js",
|
|
27
|
-
"types": "./dist/src/index.d.ts",
|
|
28
|
-
"exports": {
|
|
29
|
-
".": {
|
|
30
|
-
"types": "./dist/src/index.d.ts",
|
|
31
|
-
"default": "./dist/src/index.js"
|
|
32
|
-
},
|
|
33
|
-
"./types": {
|
|
34
|
-
"types": "./dist/src/types.d.ts",
|
|
35
|
-
"default": "./dist/src/types.js"
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
"bin": {
|
|
39
|
-
"mcp-coordinator": "./dist/cli/index.js"
|
|
40
|
-
},
|
|
41
|
-
"files": [
|
|
42
|
-
"dist/src/",
|
|
43
|
-
"dist/cli/",
|
|
44
|
-
"dashboard/",
|
|
45
|
-
"LICENSE",
|
|
46
|
-
"README.md"
|
|
47
|
-
],
|
|
48
|
-
"scripts": {
|
|
49
|
-
"build": "tsc",
|
|
50
|
-
"test": "vitest run",
|
|
51
|
-
"test:watch": "vitest",
|
|
52
|
-
"cli": "tsx cli/index.ts",
|
|
53
|
-
"start": "node dist/src/serve-http.js",
|
|
54
|
-
"dev": "tsx src/serve-http.ts",
|
|
55
|
-
"dev:stdio": "tsx src/index.ts",
|
|
56
|
-
"prepublishOnly": "npm run build && npm test"
|
|
57
|
-
},
|
|
58
|
-
"dependencies": {
|
|
59
|
-
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
60
|
-
"aedes": "^1.0.2",
|
|
61
|
-
"better-sqlite3": "^12.8.0",
|
|
62
|
-
"commander": "^14.0.3",
|
|
63
|
-
"jose": "^6.2.2",
|
|
64
|
-
"mqtt": "^5.15.0",
|
|
65
|
-
"pino": "^10.3.1",
|
|
66
|
-
"prom-client": "^15.1.3",
|
|
67
|
-
"tar": "^7.4.3",
|
|
68
|
-
"ws": "^8.20.0",
|
|
69
|
-
"zod": "^3.23.0"
|
|
70
|
-
},
|
|
71
|
-
"devDependencies": {
|
|
72
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
73
|
-
"@types/node": "^22.0.0",
|
|
74
|
-
"@types/ws": "^8.18.1",
|
|
75
|
-
"pino-pretty": "^13.1.3",
|
|
76
|
-
"tsx": "^4.19.0",
|
|
77
|
-
"typescript": "^5.7.0",
|
|
78
|
-
"vitest": "^4.1.0"
|
|
79
|
-
},
|
|
80
|
-
"optionalDependencies": {
|
|
81
|
-
"tree-sitter": "^0.21.1",
|
|
82
|
-
"tree-sitter-typescript": "^0.21.2",
|
|
83
|
-
"tree-sitter-javascript": "^0.21.4",
|
|
84
|
-
"tree-sitter-python": "^0.21.0",
|
|
85
|
-
"tree-sitter-go": "^0.21.0",
|
|
86
|
-
"tree-sitter-rust": "^0.21.2",
|
|
87
|
-
"tree-sitter-java": "^0.21.0",
|
|
88
|
-
"tree-sitter-c-sharp": "^0.21.3",
|
|
89
|
-
"tree-sitter-c": "^0.21.0",
|
|
90
|
-
"tree-sitter-cpp": "^0.22.0",
|
|
91
|
-
"tree-sitter-ruby": "^0.21.0",
|
|
92
|
-
"tree-sitter-php": "^0.22.0",
|
|
93
|
-
"tree-sitter-kotlin": "^0.3.0",
|
|
94
|
-
"tree-sitter-swift": "^0.6.0",
|
|
95
|
-
"tree-sitter-bash": "^0.21.0"
|
|
96
|
-
},
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
}
|
|
100
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-coordinator",
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"mcpName": "io.github.swoofer/mcp-coordinator",
|
|
5
|
+
"description": "Embedded MQTT broker + MCP server for multi-agent coordination",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Maxime Gagnon",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/swoofer/mcp-coordinator.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://swoofer.github.io/mcp-coordinator",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/swoofer/mcp-coordinator/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"mqtt",
|
|
20
|
+
"broker",
|
|
21
|
+
"multi-agent",
|
|
22
|
+
"coordination",
|
|
23
|
+
"claude",
|
|
24
|
+
"anthropic"
|
|
25
|
+
],
|
|
26
|
+
"main": "./dist/src/index.js",
|
|
27
|
+
"types": "./dist/src/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/src/index.d.ts",
|
|
31
|
+
"default": "./dist/src/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./types": {
|
|
34
|
+
"types": "./dist/src/types.d.ts",
|
|
35
|
+
"default": "./dist/src/types.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"bin": {
|
|
39
|
+
"mcp-coordinator": "./dist/cli/index.js"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist/src/",
|
|
43
|
+
"dist/cli/",
|
|
44
|
+
"dashboard/",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"README.md"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"cli": "tsx cli/index.ts",
|
|
53
|
+
"start": "node dist/src/serve-http.js",
|
|
54
|
+
"dev": "tsx src/serve-http.ts",
|
|
55
|
+
"dev:stdio": "tsx src/index.ts",
|
|
56
|
+
"prepublishOnly": "npm run build && npm test"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
60
|
+
"aedes": "^1.0.2",
|
|
61
|
+
"better-sqlite3": "^12.8.0",
|
|
62
|
+
"commander": "^14.0.3",
|
|
63
|
+
"jose": "^6.2.2",
|
|
64
|
+
"mqtt": "^5.15.0",
|
|
65
|
+
"pino": "^10.3.1",
|
|
66
|
+
"prom-client": "^15.1.3",
|
|
67
|
+
"tar": "^7.4.3",
|
|
68
|
+
"ws": "^8.20.0",
|
|
69
|
+
"zod": "^3.23.0"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
73
|
+
"@types/node": "^22.0.0",
|
|
74
|
+
"@types/ws": "^8.18.1",
|
|
75
|
+
"pino-pretty": "^13.1.3",
|
|
76
|
+
"tsx": "^4.19.0",
|
|
77
|
+
"typescript": "^5.7.0",
|
|
78
|
+
"vitest": "^4.1.0"
|
|
79
|
+
},
|
|
80
|
+
"optionalDependencies": {
|
|
81
|
+
"tree-sitter": "^0.21.1",
|
|
82
|
+
"tree-sitter-typescript": "^0.21.2",
|
|
83
|
+
"tree-sitter-javascript": "^0.21.4",
|
|
84
|
+
"tree-sitter-python": "^0.21.0",
|
|
85
|
+
"tree-sitter-go": "^0.21.0",
|
|
86
|
+
"tree-sitter-rust": "^0.21.2",
|
|
87
|
+
"tree-sitter-java": "^0.21.0",
|
|
88
|
+
"tree-sitter-c-sharp": "^0.21.3",
|
|
89
|
+
"tree-sitter-c": "^0.21.0",
|
|
90
|
+
"tree-sitter-cpp": "^0.22.0",
|
|
91
|
+
"tree-sitter-ruby": "^0.21.0",
|
|
92
|
+
"tree-sitter-php": "^0.22.0",
|
|
93
|
+
"tree-sitter-kotlin": "^0.3.0",
|
|
94
|
+
"tree-sitter-swift": "^0.6.0",
|
|
95
|
+
"tree-sitter-bash": "^0.21.0"
|
|
96
|
+
},
|
|
97
|
+
"overrides": {
|
|
98
|
+
"ip-address": "^10.2.0"
|
|
99
|
+
},
|
|
100
|
+
"engines": {
|
|
101
|
+
"node": ">=20"
|
|
102
|
+
}
|
|
103
|
+
}
|