From 84b81b06f7334fea3a8a8c676946c81eb1d07d60 Mon Sep 17 00:00:00 2001 From: renovate-bot Date: Wed, 8 Jul 2026 16:29:15 +0000 Subject: [PATCH 1/2] chore(deps): update dependency @types/node to v26 --- mcp-wrapper/package-lock.json | 16 ++++++++-------- mcp-wrapper/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mcp-wrapper/package-lock.json b/mcp-wrapper/package-lock.json index f179f3c..09729bf 100644 --- a/mcp-wrapper/package-lock.json +++ b/mcp-wrapper/package-lock.json @@ -14,7 +14,7 @@ "iai-mcp-wrapper": "dist/index.js" }, "devDependencies": { - "@types/node": "^25.0.0", + "@types/node": "^26.0.0", "tsx": "^4.7.0", "typescript": "^6.0.0" }, @@ -517,13 +517,13 @@ } }, "node_modules/@types/node": { - "version": "25.9.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.5.tgz", - "integrity": "sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==", + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/accepts": { @@ -1631,9 +1631,9 @@ } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "dev": true, "license": "MIT" }, diff --git a/mcp-wrapper/package.json b/mcp-wrapper/package.json index 6b94a57..f453a68 100644 --- a/mcp-wrapper/package.json +++ b/mcp-wrapper/package.json @@ -18,7 +18,7 @@ "@modelcontextprotocol/sdk": "^1.0.0" }, "devDependencies": { - "@types/node": "^25.0.0", + "@types/node": "^26.0.0", "typescript": "^6.0.0", "tsx": "^4.7.0" }, From 2d6de461a02f6a51c61bce04609fefc2380c46ad Mon Sep 17 00:00:00 2001 From: Apunkt Date: Tue, 14 Jul 2026 16:32:15 +0200 Subject: [PATCH 2/2] fix(graph): defer igraph rebuild to O(1) instead of O(m) per edge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MemoryGraph.add_edge() rebuilt the entire igraph after each edge write once backend switched (at 500 nodes), making edge addition O(m × (n+m)) — 49.81s for 5931 edges, causing 30s MCP timeout. Defer rebuild via _ig_stale flag; rebuild once on first read (centrality) instead of after every write. Edge addition now 0.77s. --- src/iai_mcp/graph.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/iai_mcp/graph.py b/src/iai_mcp/graph.py index 0bba61c..4becfb5 100644 --- a/src/iai_mcp/graph.py +++ b/src/iai_mcp/graph.py @@ -42,6 +42,7 @@ class MemoryGraph: def __init__(self) -> None: self._nx: nx.Graph = nx.Graph() self._ig: "ig.Graph | None" = None + self._ig_stale: bool = False self._attrs: dict[UUID, dict[str, Any]] = {} self._backend: str = "networkx" @@ -79,10 +80,11 @@ class MemoryGraph: self._nx.add_edge( str(src), str(dst), weight=weight, edge_type=edge_type ) - if self._ig is not None: - # igraph mirror is immutable by topology; rebuild after each edge - # write while in igraph backend. Cheap enough at Phase-1 scale. - self._rebuild_igraph() + self._maybe_switch_backend() + if self._backend == "igraph": + # Lazy rebuild: igraph mirror is stale after edge writes + # and will be rebuilt on next read. + self._ig_stale = True # ------------------------------------------------------ backend switching @@ -105,11 +107,17 @@ class MemoryGraph: weights = [ float(self._nx[u][v].get("weight", 1.0)) for u, v in self._nx.edges() ] - g = ig.Graph(n=len(nodes), edges=edges, directed=False) + g = ig.Graph(n=len(nodes), edges=edges, directed=False) # type: ignore[unbound] g.vs["name"] = nodes if weights: g.es["weight"] = weights self._ig = g + self._ig_stale = False + + def _ensure_igraph(self) -> None: + """Rebuild igraph mirror if stale from deferred edge writes.""" + if self._ig_stale and _HAS_IGRAPH: + self._rebuild_igraph() # ---------------------------------------------------------- graph metrics @@ -124,6 +132,7 @@ class MemoryGraph: bc = nx.betweenness_centrality(self._nx, weight="weight") return {UUID(n): float(c) for n, c in bc.items()} # igraph path + self._ensure_igraph() assert self._ig is not None has_weight = "weight" in self._ig.es.attributes() raw = self._ig.betweenness(weights="weight" if has_weight else None)