archgraph-argo 0.4.2 → 0.5.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.
|
@@ -16,6 +16,8 @@ const {
|
|
|
16
16
|
} = require('./repositoryArgoEnvironment.js');
|
|
17
17
|
const {
|
|
18
18
|
getWorkspaceRoot,
|
|
19
|
+
hasStaticWorkspace,
|
|
20
|
+
setMcpWorkspaceRoots,
|
|
19
21
|
} = require('./argo-paths.js');
|
|
20
22
|
const canonicalSemanticInitStorage = new AsyncLocalStorage();
|
|
21
23
|
|
|
@@ -503,6 +505,59 @@ function send(message) {
|
|
|
503
505
|
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
504
506
|
}
|
|
505
507
|
|
|
508
|
+
let rootRequestSeq = 0;
|
|
509
|
+
let rootRequestInFlight = false;
|
|
510
|
+
let rootRequestTimer = null;
|
|
511
|
+
let rootsSettled = false;
|
|
512
|
+
const pendingRootRequests = new Map();
|
|
513
|
+
const deferredToolCalls = [];
|
|
514
|
+
|
|
515
|
+
function flushDeferredToolCalls() {
|
|
516
|
+
const pending = deferredToolCalls.splice(0);
|
|
517
|
+
for (const request of pending) {
|
|
518
|
+
void (async () => {
|
|
519
|
+
const response = await handleRequest(request);
|
|
520
|
+
if (response) {
|
|
521
|
+
send(response);
|
|
522
|
+
}
|
|
523
|
+
})();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function settleRoots() {
|
|
528
|
+
if (rootsSettled) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
rootsSettled = true;
|
|
532
|
+
if (rootRequestTimer) {
|
|
533
|
+
clearTimeout(rootRequestTimer);
|
|
534
|
+
rootRequestTimer = null;
|
|
535
|
+
}
|
|
536
|
+
rootRequestInFlight = false;
|
|
537
|
+
pendingRootRequests.clear();
|
|
538
|
+
flushDeferredToolCalls();
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function requestRoots() {
|
|
542
|
+
if (rootRequestInFlight || rootsSettled) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
rootRequestInFlight = true;
|
|
546
|
+
const id = `argo-roots-${++rootRequestSeq}`;
|
|
547
|
+
rootRequestTimer = setTimeout(() => {
|
|
548
|
+
rootRequestTimer = null;
|
|
549
|
+
rootRequestInFlight = false;
|
|
550
|
+
settleRoots();
|
|
551
|
+
}, 8000);
|
|
552
|
+
pendingRootRequests.set(id, (result) => {
|
|
553
|
+
if (result && Array.isArray(result.roots)) {
|
|
554
|
+
setMcpWorkspaceRoots(result.roots);
|
|
555
|
+
}
|
|
556
|
+
settleRoots();
|
|
557
|
+
});
|
|
558
|
+
send({ jsonrpc: '2.0', id, method: 'roots/list', params: {} });
|
|
559
|
+
}
|
|
560
|
+
|
|
506
561
|
async function handleRequest(request, dependencies = undefined) {
|
|
507
562
|
const { id, method, params } = request;
|
|
508
563
|
|
|
@@ -512,7 +567,7 @@ async function handleRequest(request, dependencies = undefined) {
|
|
|
512
567
|
id,
|
|
513
568
|
result: {
|
|
514
569
|
protocolVersion: '2024-11-05',
|
|
515
|
-
capabilities: { tools: {} },
|
|
570
|
+
capabilities: { tools: {}, roots: { listChanged: true } },
|
|
516
571
|
serverInfo: {
|
|
517
572
|
name: 'argo',
|
|
518
573
|
version: '1.0.0',
|
|
@@ -525,6 +580,14 @@ async function handleRequest(request, dependencies = undefined) {
|
|
|
525
580
|
return null;
|
|
526
581
|
}
|
|
527
582
|
|
|
583
|
+
if (method === 'notifications/roots/list_changed') {
|
|
584
|
+
setMcpWorkspaceRoots(params && params.roots);
|
|
585
|
+
if (params && Array.isArray(params.roots) && params.roots.length > 0) {
|
|
586
|
+
settleRoots();
|
|
587
|
+
}
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
|
|
528
591
|
if (method === 'tools/list') {
|
|
529
592
|
// Deduplicate tools by name — last writer wins.
|
|
530
593
|
// Priority order: validatorMcp > systemArchitectureMcp > local TOOLS.
|
|
@@ -606,10 +669,32 @@ async function main() {
|
|
|
606
669
|
} catch {
|
|
607
670
|
continue;
|
|
608
671
|
}
|
|
672
|
+
|
|
673
|
+
// A JSON-RPC message without a `method` is a response to a server-sent request.
|
|
674
|
+
if (typeof request.method === 'undefined' && request.id !== undefined) {
|
|
675
|
+
const resolver = pendingRootRequests.get(request.id);
|
|
676
|
+
if (resolver) {
|
|
677
|
+
pendingRootRequests.delete(request.id);
|
|
678
|
+
resolver(request.result);
|
|
679
|
+
}
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// Workspace-dependent tool calls must wait for roots so the global server
|
|
684
|
+
// targets the current workspace instead of the home directory.
|
|
685
|
+
if (request.method === 'tools/call' && !hasStaticWorkspace() && !rootsSettled) {
|
|
686
|
+
deferredToolCalls.push(request);
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
|
|
609
690
|
const response = await handleRequest(request);
|
|
610
691
|
if (response) {
|
|
611
692
|
send(response);
|
|
612
693
|
}
|
|
694
|
+
if ((request.method === 'initialize' || request.method === 'notifications/initialized')
|
|
695
|
+
&& !hasStaticWorkspace()) {
|
|
696
|
+
requestRoots();
|
|
697
|
+
}
|
|
613
698
|
}
|
|
614
699
|
}
|
|
615
700
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('node:fs');
|
|
2
2
|
const path = require('node:path');
|
|
3
|
+
const { fileURLToPath } = require('node:url');
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Shared path resolution for the Argo toolchain.
|
|
@@ -17,10 +18,12 @@ const path = require('node:path');
|
|
|
17
18
|
* The workspace root is resolved from, in priority order:
|
|
18
19
|
* 1. ARGO_REPO_ROOT — explicit override (set by MCP client configs).
|
|
19
20
|
* 2. WORKSPACE_FOLDER — set by some hosts.
|
|
20
|
-
* 3. The
|
|
21
|
+
* 3. The MCP `roots` advertised by the client (a global server resolves the
|
|
22
|
+
* root that contains design/KG/SystemArchitecture.json when several roots
|
|
23
|
+
* are present, so it never operates on an unrelated folder).
|
|
24
|
+
* 4. The embedded repo root, when the Argo installation is still inside a repo
|
|
21
25
|
* (i.e. `<argoRoot>/../design/KG/SystemArchitecture.json` exists).
|
|
22
|
-
*
|
|
23
|
-
* set to the target workspace root.
|
|
26
|
+
* 5. process.cwd() — last-resort fallback when no root is available.
|
|
24
27
|
*/
|
|
25
28
|
|
|
26
29
|
function getArgoRoot() {
|
|
@@ -28,20 +31,71 @@ function getArgoRoot() {
|
|
|
28
31
|
return path.resolve(__dirname, '..');
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
let mcpWorkspaceRoots = [];
|
|
35
|
+
|
|
36
|
+
function setMcpWorkspaceRoots(roots) {
|
|
37
|
+
mcpWorkspaceRoots = Array.isArray(roots) ? roots : [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function rootToPath(root) {
|
|
41
|
+
if (!root) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const uri = typeof root === 'string' ? root : root.uri;
|
|
45
|
+
if (typeof uri !== 'string' || uri.trim() === '') {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (uri.startsWith('file://')) {
|
|
49
|
+
try {
|
|
50
|
+
return fileURLToPath(uri);
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return uri;
|
|
56
|
+
}
|
|
57
|
+
|
|
31
58
|
function getWorkspaceRoot() {
|
|
32
59
|
const explicit = process.env.ARGO_REPO_ROOT || process.env.WORKSPACE_FOLDER;
|
|
33
60
|
if (explicit && String(explicit).trim() !== '') {
|
|
34
61
|
return path.resolve(explicit);
|
|
35
62
|
}
|
|
36
63
|
|
|
64
|
+
// The client may expose several roots (a multi-root workspace). Never
|
|
65
|
+
// silently operate on an unrelated folder: prefer the root that actually
|
|
66
|
+
// contains the ArchGraph marker before falling back to the first root.
|
|
67
|
+
const marker = path.join('design', 'KG', 'SystemArchitecture.json');
|
|
68
|
+
for (const root of mcpWorkspaceRoots) {
|
|
69
|
+
const rootPath = rootToPath(root);
|
|
70
|
+
if (rootPath && fs.existsSync(path.join(rootPath, marker))) {
|
|
71
|
+
return path.resolve(rootPath);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const root of mcpWorkspaceRoots) {
|
|
76
|
+
const rootPath = rootToPath(root);
|
|
77
|
+
if (rootPath) {
|
|
78
|
+
return path.resolve(rootPath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
37
82
|
const embedded = path.resolve(getArgoRoot(), '..');
|
|
38
|
-
if (fs.existsSync(path.join(embedded,
|
|
83
|
+
if (fs.existsSync(path.join(embedded, marker))) {
|
|
39
84
|
return embedded;
|
|
40
85
|
}
|
|
41
86
|
|
|
42
87
|
return path.resolve(process.cwd());
|
|
43
88
|
}
|
|
44
89
|
|
|
90
|
+
function hasStaticWorkspace() {
|
|
91
|
+
const explicit = process.env.ARGO_REPO_ROOT || process.env.WORKSPACE_FOLDER;
|
|
92
|
+
if (explicit && String(explicit).trim() !== '') {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
const embedded = path.resolve(getArgoRoot(), '..');
|
|
96
|
+
return fs.existsSync(path.join(embedded, 'design', 'KG', 'SystemArchitecture.json'));
|
|
97
|
+
}
|
|
98
|
+
|
|
45
99
|
function resolveArgoPath(...segments) {
|
|
46
100
|
return path.resolve(getArgoRoot(), ...segments);
|
|
47
101
|
}
|
|
@@ -71,7 +125,9 @@ module.exports = {
|
|
|
71
125
|
getArgoRoot,
|
|
72
126
|
getArgoEnvPath,
|
|
73
127
|
getWorkspaceRoot,
|
|
128
|
+
hasStaticWorkspace,
|
|
74
129
|
normalizeRelativePath,
|
|
75
130
|
resolveArgoPath,
|
|
76
131
|
resolveWorkspacePath,
|
|
132
|
+
setMcpWorkspaceRoots,
|
|
77
133
|
};
|
package/install-argo.ps1
CHANGED
package/package.json
CHANGED