archgraph-argo 0.4.1 → 0.5.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.
@@ -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.
@@ -28,12 +29,43 @@ function getArgoRoot() {
28
29
  return path.resolve(__dirname, '..');
29
30
  }
30
31
 
32
+ let mcpWorkspaceRoots = [];
33
+
34
+ function setMcpWorkspaceRoots(roots) {
35
+ mcpWorkspaceRoots = Array.isArray(roots) ? roots : [];
36
+ }
37
+
38
+ function rootToPath(root) {
39
+ if (!root) {
40
+ return null;
41
+ }
42
+ const uri = typeof root === 'string' ? root : root.uri;
43
+ if (typeof uri !== 'string' || uri.trim() === '') {
44
+ return null;
45
+ }
46
+ if (uri.startsWith('file://')) {
47
+ try {
48
+ return fileURLToPath(uri);
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+ return uri;
54
+ }
55
+
31
56
  function getWorkspaceRoot() {
32
57
  const explicit = process.env.ARGO_REPO_ROOT || process.env.WORKSPACE_FOLDER;
33
58
  if (explicit && String(explicit).trim() !== '') {
34
59
  return path.resolve(explicit);
35
60
  }
36
61
 
62
+ for (const root of mcpWorkspaceRoots) {
63
+ const rootPath = rootToPath(root);
64
+ if (rootPath) {
65
+ return path.resolve(rootPath);
66
+ }
67
+ }
68
+
37
69
  const embedded = path.resolve(getArgoRoot(), '..');
38
70
  if (fs.existsSync(path.join(embedded, 'design', 'KG', 'SystemArchitecture.json'))) {
39
71
  return embedded;
@@ -42,6 +74,15 @@ function getWorkspaceRoot() {
42
74
  return path.resolve(process.cwd());
43
75
  }
44
76
 
77
+ function hasStaticWorkspace() {
78
+ const explicit = process.env.ARGO_REPO_ROOT || process.env.WORKSPACE_FOLDER;
79
+ if (explicit && String(explicit).trim() !== '') {
80
+ return true;
81
+ }
82
+ const embedded = path.resolve(getArgoRoot(), '..');
83
+ return fs.existsSync(path.join(embedded, 'design', 'KG', 'SystemArchitecture.json'));
84
+ }
85
+
45
86
  function resolveArgoPath(...segments) {
46
87
  return path.resolve(getArgoRoot(), ...segments);
47
88
  }
@@ -71,7 +112,9 @@ module.exports = {
71
112
  getArgoRoot,
72
113
  getArgoEnvPath,
73
114
  getWorkspaceRoot,
115
+ hasStaticWorkspace,
74
116
  normalizeRelativePath,
75
117
  resolveArgoPath,
76
118
  resolveWorkspacePath,
119
+ setMcpWorkspaceRoots,
77
120
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archgraph-argo",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Deploy the ArchGraph ARGO toolchain, skills, and rules (schema, scripts, argo-init skill, global rule) with one command.",
5
5
  "license": "MIT",
6
6
  "bin": {