@abderraouf-yt/got-mcp 3.2.1 → 4.0.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.
package/README.md CHANGED
@@ -13,16 +13,14 @@
13
13
  <a href="https://github.com/Abderraouf-yt/got-mcp"><img src="https://img.shields.io/github/stars/Abderraouf-yt/got-mcp?style=flat-square&color=ff00ff&label=stars" alt="GitHub stars" /></a>
14
14
  <a href="#"><img src="https://img.shields.io/badge/node-%3E%3D20-00e5ff?style=flat-square" alt="Node.js" /></a>
15
15
  <a href="#"><img src="https://img.shields.io/badge/MCP-1.26+-ff00ff?style=flat-square" alt="MCP SDK" /></a>
16
- <a href="#"><img src="https://img.shields.io/badge/tools-10-00ff88?style=flat-square" alt="Tools" /></a>
16
+ <a href="#"><img src="https://img.shields.io/badge/tools-15-00ff88?style=flat-square" alt="Tools" /></a>
17
17
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-yellow?style=flat-square" alt="License" /></a>
18
- <a href="https://got-mcp-visualizer.netlify.app"><img src="https://img.shields.io/badge/demo-live-00e5ff?style=flat-square&logo=netlify" alt="Live Demo" /></a>
19
18
  <a href="https://arxiv.org/abs/2308.09687"><img src="https://img.shields.io/badge/GoT-Besta%20et%20al.%202023-ff6b6b?style=flat-square" alt="GoT Paper" /></a>
20
19
  </p>
21
20
 
22
21
  <p align="center">
23
22
  <a href="#-quick-start">⚡ Quick Start</a> •
24
- <a href="https://got-mcp-visualizer.netlify.app">� Live Demo</a> •
25
- <a href="#-tools-10">🛠 Tools</a> •
23
+ <a href="#-tools-15">🛠 Tools</a> •
26
24
  <a href="#-how-it-thinks">🧬 How It Thinks</a> •
27
25
  <a href="#-governance">🔒 Governance</a> •
28
26
  <a href="#-visualizer">📊 Visualizer</a>
@@ -152,7 +150,7 @@ The demo state ships with the repo in `docs/assets/demo-state.json`.
152
150
 
153
151
  ---
154
152
 
155
- ## 🛠 Tools (10)
153
+ ## 🛠 Tools (15)
156
154
 
157
155
  <table>
158
156
  <tr><th>Tool</th><th>What it does</th><th>Category</th></tr>
@@ -166,11 +164,16 @@ The demo state ships with the repo in `docs/assets/demo-state.json`.
166
164
  <tr><td><code>get_graph_metrics</code></td><td>Node count, max depth, prune ratio, avg score, status breakdown</td><td>📊 Ops</td></tr>
167
165
  <tr><td><code>export_snapshot</code></td><td>Full graph serialization for replay/recovery</td><td>🔁 Replay</td></tr>
168
166
  <tr><td><code>restore_snapshot</code></td><td>Restore from previously exported snapshot</td><td>🔁 Replay</td></tr>
167
+ <tr><td><code>reflect_and_refine</code></td><td>Self-reflection: 4-axis confidence + auto-critique + branch</td><td>🔬 v4.0</td></tr>
168
+ <tr><td><code>context_set</code></td><td>Write key-value to shared context store with provenance</td><td>📦 v4.0</td></tr>
169
+ <tr><td><code>context_get</code></td><td>Read value + source from shared context store</td><td>📦 v4.0</td></tr>
170
+ <tr><td><code>context_list</code></td><td>List all context store entries and their sources</td><td>📦 v4.0</td></tr>
171
+ <tr><td><code>export_reasoning_trace</code></td><td>Export winning path as Long CoT trace (DeepSeek-R1/o3 format)</td><td>📤 v4.0</td></tr>
169
172
  </table>
170
173
 
171
174
  ### Edge Relations
172
175
 
173
- `refinement` · `contradiction` · `support` · `branch` · `aggregation`
176
+ `refinement` · `contradiction` · `support` · `branch` · `aggregation` · `reflection`
174
177
 
175
178
  ### Node Statuses
176
179
 
@@ -313,7 +316,9 @@ Based on [Besta et al., 2023 — "Graph of Thoughts"](https://arxiv.org/abs/2308
313
316
  | **Converge** | `find_winning_path` — beam search | v3.0 |
314
317
  | **Governance** | Engine-level guards + session isolation | v3.0 |
315
318
  | **Replay** | `export_snapshot` / `restore_snapshot` | v3.0 |
316
- | Volume Control | Roadmap | |
319
+ | **Self-Reflect** | `reflect_and_refine` — 4-axis confidence | v4.0 |
320
+ | **Context Store** | `context_set` / `context_get` / `context_list` | v4.0 |
321
+ | **Reasoning Trace** | `export_reasoning_trace` — Long CoT export | v4.0 |
317
322
  | Controller Loop | Roadmap | — |
318
323
 
319
324
  ---
@@ -338,13 +343,17 @@ thought-graph/
338
343
  ├── src/
339
344
  │ ├── index.ts # Bootstrap (Stdio + HTTP)
340
345
  │ ├── server/
341
- │ │ ├── mcp.ts # 10 MCP tool registrations
342
- │ │ └── http.ts # Express bridge + SSE
346
+ │ │ ├── mcp.ts # 15 MCP tool registrations
347
+ │ │ └── http.ts # Express bridge (minimal inline CORS)
343
348
  │ ├── graph/
344
349
  │ │ ├── ThoughtGraph.ts # Core DAG engine + governance
345
350
  │ │ └── index.ts # Session registry exports
346
- └── types.ts # GraphLimits, SessionContext, GraphMetrics
347
- ├── visualizer/ # React + Vite dashboard
351
+ ├── context/
352
+ │ │ ├── ContextStore.ts # Shared context store (CA-MCP)
353
+ │ │ └── index.ts # Context singleton export
354
+ │ └── types.ts # GraphLimits, ConfidenceVector, ReasoningTrace
355
+ ├── visualizer/ # React + Vite dashboard (local only)
356
+ ├── evaluations/ # XML evaluation framework
348
357
  ├── tests/ # Unit tests
349
358
  ├── docs/assets/ # Demo screenshots & state
350
359
  └── dist/ # Compiled output
@@ -796,6 +796,194 @@ export class ThoughtGraph {
796
796
  exportedAt: new Date().toISOString(),
797
797
  };
798
798
  }
799
+ // ==========================================
800
+ // v4.0: CONTROLLER LOOP — Autonomous GoT Cycle
801
+ // ==========================================
802
+ /**
803
+ * Controller Loop: Autonomous Graph of Thoughts reasoning cycle.
804
+ *
805
+ * Orchestrates the full GoT pipeline on a given prompt:
806
+ * 1. GENERATE — propose initial thoughts from the prompt
807
+ * 2. EVALUATE — score each active leaf using confidence vectors
808
+ * 3. BRANCH — create alternatives for mid-scoring thoughts
809
+ * 4. REFLECT — auto-critique top candidates via reflectAndRefine
810
+ * 5. PRUNE — remove branches below autoPruneBelow threshold
811
+ * 6. CONVERGE — find the winning path via beam search
812
+ *
813
+ * Governance:
814
+ * - maxIterations caps the number of generate→evaluate→prune cycles
815
+ * - convergenceThreshold stops early when the best path score exceeds it
816
+ * - autoPruneBelow automatically soft-prunes low-scoring branches
817
+ * - All existing graph limits (node cap, depth cap, etc.) still apply
818
+ *
819
+ * @param prompt - The reasoning question or problem statement
820
+ * @param thoughts - Array of initial thought branches to explore
821
+ * @param options - Controller loop configuration
822
+ * @returns The final winning path, reasoning trace, and iteration metrics
823
+ */
824
+ runControllerLoop(prompt, thoughts, options) {
825
+ const maxIterations = options?.maxIterations ?? 5;
826
+ const convergenceThreshold = options?.convergenceThreshold ?? 0.85;
827
+ const autoPruneBelow = options?.autoPruneBelow ?? 0.3;
828
+ const beamWidth = options?.beamWidth ?? 2;
829
+ const iterationLog = [];
830
+ // Step 1: GENERATE — seed the graph with the prompt + initial thoughts
831
+ const rootId = this.addNode(prompt);
832
+ this.updateNode(rootId, { score: 0.5, status: "active" });
833
+ for (const thought of thoughts) {
834
+ const childId = this.addNode(thought);
835
+ this.addEdge(rootId, childId, "branch");
836
+ this.updateNode(childId, { score: 0.5, status: "active" });
837
+ }
838
+ let converged = false;
839
+ let iteration = 0;
840
+ // Step 2-6: Iterate until convergence or budget exhausted
841
+ while (iteration < maxIterations && !converged) {
842
+ iteration++;
843
+ // --- EVALUATE: score all active leaf nodes ---
844
+ const activeLeaves = this.getActiveLeaves();
845
+ let scored = 0;
846
+ let pruned = 0;
847
+ let branched = 0;
848
+ let reflected = 0;
849
+ for (const leaf of activeLeaves) {
850
+ // Auto-score based on thought quality heuristics:
851
+ // - Length factor: longer = more detailed (diminishing returns)
852
+ // - Depth factor: deeper = more refined thinking
853
+ // - Specificity: presence of numbers, comparisons, evidence markers
854
+ const depth = this.getNodeDepth(leaf.id);
855
+ const lengthScore = Math.min(leaf.thought.length / 500, 1.0);
856
+ const depthBonus = Math.min(depth * 0.05, 0.2);
857
+ const specificity = this.estimateSpecificity(leaf.thought);
858
+ const autoScore = Math.min(Math.round((lengthScore * 0.3 + depthBonus + specificity * 0.5) * 100) / 100, 1.0);
859
+ this.updateNode(leaf.id, {
860
+ score: autoScore,
861
+ status: autoScore >= convergenceThreshold ? "validated" : "active",
862
+ });
863
+ scored++;
864
+ }
865
+ // --- PRUNE: remove low-scoring branches ---
866
+ const lowScorers = Array.from(this.nodes.values())
867
+ .filter(n => n.status === "active" && n.score < autoPruneBelow && n.score > 0);
868
+ for (const weak of lowScorers) {
869
+ try {
870
+ const result = this.pruneFromNode(weak.id, `Auto-pruned: score ${weak.score} < ${autoPruneBelow}`, {
871
+ mode: "soft",
872
+ decayFactor: 0.3,
873
+ trigger: "auto",
874
+ });
875
+ pruned += result.pruned.length;
876
+ }
877
+ catch {
878
+ // Skip if prune fails (e.g. cascade limit)
879
+ }
880
+ }
881
+ // --- BRANCH: create alternatives for mid-tier thoughts ---
882
+ const midTier = Array.from(this.nodes.values())
883
+ .filter(n => n.status === "active" && n.score >= autoPruneBelow && n.score < convergenceThreshold);
884
+ for (const mid of midTier.slice(0, 3)) { // Limit branching to top 3
885
+ const existingChildren = this.edges.filter(e => e.from === mid.id).length;
886
+ if (existingChildren < this.limits.maxBranchFactor && this.nodes.size < this.limits.maxNodes - 5) {
887
+ try {
888
+ const altId = this.addNode(`[Alternative] What if we reconsider: ${mid.thought.substring(0, 200)}...`);
889
+ this.addEdge(mid.id, altId, "branch");
890
+ this.updateNode(altId, { score: 0.5, status: "active" });
891
+ branched++;
892
+ }
893
+ catch {
894
+ // Skip if limits hit
895
+ }
896
+ }
897
+ }
898
+ // --- REFLECT: critique the best candidates ---
899
+ const topCandidates = Array.from(this.nodes.values())
900
+ .filter(n => n.status !== "rejected" && n.score >= 0.5)
901
+ .sort((a, b) => b.score - a.score)
902
+ .slice(0, 2);
903
+ for (const top of topCandidates) {
904
+ try {
905
+ const confidence = {
906
+ factual: Math.min(top.score + 0.1, 1),
907
+ logical: Math.min(top.score + 0.05, 1),
908
+ relevance: Math.min(top.score + 0.15, 1),
909
+ novelty: Math.max(top.score - 0.1, 0),
910
+ };
911
+ this.reflectAndRefine(top.id, `Iteration ${iteration} auto-reflection: evaluating strength of reasoning.`, confidence);
912
+ reflected++;
913
+ }
914
+ catch {
915
+ // Skip if reflection fails
916
+ }
917
+ }
918
+ // --- CONVERGE CHECK ---
919
+ const winningPath = this.findWinningPath({ beamWidth, scoreThreshold: autoPruneBelow });
920
+ const avgPathScore = winningPath.path.length > 0
921
+ ? winningPath.totalScore / winningPath.path.length
922
+ : 0;
923
+ iterationLog.push({
924
+ iteration,
925
+ nodesScored: scored,
926
+ nodesPruned: pruned,
927
+ nodesBranched: branched,
928
+ nodesReflected: reflected,
929
+ totalNodes: this.nodes.size,
930
+ bestPathScore: Math.round(avgPathScore * 100) / 100,
931
+ converged: avgPathScore >= convergenceThreshold,
932
+ });
933
+ if (avgPathScore >= convergenceThreshold) {
934
+ converged = true;
935
+ }
936
+ }
937
+ // Final convergence
938
+ const finalPath = this.findWinningPath({ beamWidth, scoreThreshold: 0 });
939
+ const trace = this.exportReasoningTrace();
940
+ const metrics = this.getMetrics();
941
+ this.stateVersion++;
942
+ this.save();
943
+ return {
944
+ converged,
945
+ iterations: iteration,
946
+ winningPath: {
947
+ pathIds: finalPath.pathIds,
948
+ totalScore: finalPath.totalScore,
949
+ conclusion: finalPath.path.length > 0
950
+ ? finalPath.path[finalPath.path.length - 1].thought
951
+ : "No conclusion reached",
952
+ },
953
+ trace,
954
+ metrics,
955
+ iterationLog,
956
+ };
957
+ }
958
+ /**
959
+ * Get active leaf nodes (nodes with no outgoing edges that aren't rejected).
960
+ */
961
+ getActiveLeaves() {
962
+ const nodesWithOutgoing = new Set(this.edges.map(e => e.from));
963
+ return Array.from(this.nodes.values())
964
+ .filter(n => !nodesWithOutgoing.has(n.id) && n.status !== "rejected");
965
+ }
966
+ /**
967
+ * Estimate the specificity of a thought based on content heuristics.
968
+ * Higher specificity = more concrete, evidence-based reasoning.
969
+ */
970
+ estimateSpecificity(thought) {
971
+ let score = 0.3; // Base score
972
+ // Indicators of specific, evidence-based thinking
973
+ if (/\d+/.test(thought))
974
+ score += 0.1; // Contains numbers
975
+ if (/vs\.?|versus|compared|better|worse/i.test(thought))
976
+ score += 0.1; // Comparisons
977
+ if (/because|since|therefore|thus|hence/i.test(thought))
978
+ score += 0.1; // Causal reasoning
979
+ if (/however|but|although|despite/i.test(thought))
980
+ score += 0.1; // Nuance
981
+ if (/example|specifically|for instance/i.test(thought))
982
+ score += 0.1; // Concreteness
983
+ if (/\b(data|evidence|research|study|benchmark)\b/i.test(thought))
984
+ score += 0.1; // Evidence
985
+ return Math.min(score, 1.0);
986
+ }
799
987
  }
800
988
  // ==========================================
801
989
  // SESSION REGISTRY (replaces singleton)
@@ -1,5 +1,4 @@
1
1
  import express from "express";
2
- import cors from "cors";
3
2
  import net from "net";
4
3
  import crypto from "crypto";
5
4
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
@@ -43,15 +42,13 @@ async function findAvailablePort(startPort) {
43
42
  export async function startHttpServer() {
44
43
  const port = await findAvailablePort(PREFERRED_PORT);
45
44
  const app = express();
46
- // Allow both the default Vite dev port and any localhost origin for flexibility
47
- app.use(cors({
48
- origin: [
49
- 'http://localhost:5173',
50
- 'http://localhost:5174',
51
- 'http://127.0.0.1:5173',
52
- 'http://127.0.0.1:5174',
53
- ]
54
- }));
45
+ // Minimal localhost-only CORS (no external dependency needed)
46
+ app.use((_req, res, next) => {
47
+ res.header("Access-Control-Allow-Origin", "http://localhost:5173");
48
+ res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
49
+ res.header("Access-Control-Allow-Headers", "Content-Type");
50
+ next();
51
+ });
55
52
  app.use(express.json());
56
53
  const activeSessions = new Map();
57
54
  app.get("/sse", async (req, res) => {
@@ -473,5 +473,44 @@ export function createServerInstance() {
473
473
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
474
474
  }
475
475
  });
476
+ // ==========================================
477
+ // v4.0: Controller Loop — Autonomous GoT Orchestrator
478
+ // ==========================================
479
+ server.registerTool("run_controller_loop", {
480
+ description: "Autonomous GoT Controller Loop: Seeds a graph with a prompt + initial thoughts, then iteratively evaluates, branches, reflects, prunes, and converges until a winning path emerges or the iteration budget is exhausted. Returns the winning conclusion, full reasoning trace, and per-iteration metrics.",
481
+ inputSchema: {
482
+ prompt: z.string().min(1).max(5000).describe("The reasoning question or problem statement"),
483
+ thoughts: z.array(z.string().min(1).max(5000)).min(1).max(10).describe("Initial thought branches to explore (1-10)"),
484
+ maxIterations: z.number().int().min(1).max(20).default(5).describe("Maximum reasoning cycles (default: 5)"),
485
+ convergenceThreshold: z.number().min(0).max(1).default(0.85).describe("Stop when best path average score exceeds this (default: 0.85)"),
486
+ autoPruneBelow: z.number().min(0).max(1).default(0.3).describe("Auto soft-prune branches scoring below this (default: 0.3)"),
487
+ beamWidth: z.number().int().min(1).max(10).default(2).describe("Number of top paths to track during convergence (default: 2)"),
488
+ },
489
+ annotations: { destructiveHint: true }
490
+ }, async ({ prompt, thoughts, maxIterations, convergenceThreshold, autoPruneBelow, beamWidth }) => {
491
+ try {
492
+ const result = graph.runControllerLoop(prompt, thoughts, {
493
+ maxIterations,
494
+ convergenceThreshold,
495
+ autoPruneBelow,
496
+ beamWidth,
497
+ });
498
+ notifyUpdate();
499
+ const summary = [
500
+ `Controller Loop ${result.converged ? "CONVERGED" : "EXHAUSTED"} after ${result.iterations} iterations.`,
501
+ `Winning path: ${result.winningPath.pathIds.join(" → ")} (score: ${result.winningPath.totalScore})`,
502
+ `Conclusion: "${result.winningPath.conclusion.substring(0, 200)}"`,
503
+ `Graph: ${result.metrics.nodeCount} nodes, ${result.metrics.edgeCount} edges`,
504
+ `Prune ratio: ${Math.round(result.metrics.pruneRatio * 100)}%`,
505
+ ].join("\n");
506
+ return {
507
+ content: [{ type: "text", text: summary }],
508
+ structuredContent: result,
509
+ };
510
+ }
511
+ catch (err) {
512
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
513
+ }
514
+ });
476
515
  return server;
477
516
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abderraouf-yt/got-mcp",
3
- "version": "3.2.1",
3
+ "version": "4.0.0",
4
4
  "description": "Graph of Thoughts (GoT) MCP Server — bounded, auditable reasoning for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -48,12 +48,10 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@modelcontextprotocol/sdk": "^1.26.0",
51
- "cors": "^2.8.5",
52
51
  "express": "^4.21.0",
53
52
  "zod": "^4.3.6"
54
53
  },
55
54
  "devDependencies": {
56
- "@types/cors": "^2.8.17",
57
55
  "@types/express": "^4.17.21",
58
56
  "@types/node": "^20.19.30",
59
57
  "typescript": "^5.9.3"