Skip to main content
Glama

temporal-graph-mcp (experimental)

A small, standalone MCP server that answers one question: can the Baligács (2026) "Temporal Cliques Admit Linear Spanners" algorithm compress a code knowledge graph the way it compresses a linguistic co-occurrence graph?

It is not a fork of DeusData/codebase-memory-mcp (a real gh repo fork of that MIT-licensed project was made separately, for provenance/attribution only — no code from it is used here; it's a 38MB+ C codebase with 158 tree-sitter grammars and a custom Cypher engine, well outside the scope of this experiment). This project borrows only its tool naming convention (index_repository, search_graph, get_architecture, ...) as a point of reference, and implements a small, honest subset of it.

What's actually new here

corpus_engine already applies the Baligács algorithm to word co-occurrence cliques in a Turkish linguistic corpus. This project mirrors that pipeline one-to-one, but the graph comes from git commit co-change history instead of corpus co-occurrence:

corpus_engine (words)

temporal-graph-mcp (code)

two words co-occur in a document/sentence

two files (or two functions) change together in a commit

edge timestamp = document date

edge timestamp = commit's unix time (earliest co-change kept)

PMI/NPMI threshold gates noisy pairs

min_cochanges=2 gates one-off co-changes; commits touching too many items (>20 files / >60 functions) are dropped (mass rename/format/reflow noise)

maximal clique = "semantic field"

maximal clique = "files/functions that are really one subsystem"

spanner = sparse edge set preserving all temporal reachability

same, applied per coupling-clique

Two granularities are supported, both from the same git log, no AST parser needed:

  • file (default): nodes are file paths, from git log --name-only.

  • function: nodes are "<file>::<function>", extracted from git log -p --unified=0's hunk headers (@@ ... @@ def foo(...):). Git already ships per-language "funcname" heuristics (Python, JS, Java, C, C++, Go, ...) used to generate that trailing context on a diff hunk — this just reuses it instead of vendoring a per-language AST parser. Whole-file adds have no funcname context (nothing to diff against) and are skipped at this granularity; file-level coupling already covers "these were added together".

The pure-stdlib algorithm itself (vendor/spanner/, vendor/backend/algorithm/) is copied verbatim from temporal-spanner-analyzer — no changes, so its existing test suite / correctness proof still applies. The one addition is graph_utils_lite.py, a copy of that repo's maximal_cliques (Bron-Kerbosch, Tomita pivot) with the pydantic GraphSchema dependency stripped out (plain dict[str, set[str]] adjacency instead), so this package needs nothing beyond the mcp SDK.

Related MCP server: code-graph-mcp

Verified result (see demo.py)

Run against temporal-spanner-analyzer's own git history, both granularities:

$ python demo.py <repo> file
30/46 commits used (rest touched < 2 or > 20 files)
47 files, 279 coupling edges (co-changed >= 2 times)
26 cliques, 43/47 files covered
total: 716 original edges -> 326 spanner edges (54.5% fewer edges)
tokens (tiktoken/cl100k_base): 10012 -> 4473 (55.3% fewer tokens)

$ python demo.py <repo> function
27/46 commits used (rest touched < 2 or > 60 functions)
97 functions, 798 coupling edges (co-changed >= 2 times)
47 cliques, 90/97 functions covered
total: 1799 original edges -> 619 spanner edges (65.6% fewer edges)
tokens (tiktoken/cl100k_base): 60921 -> 20897 (65.7% fewer tokens)

All cliques verified (both runs): reachability preserved, 7n bound honored.

The token counts are measured, not inferred: tokens.py tokenizes the actual JSON edge-list payload (original vs spanner) an MCP tool would put on the wire, with tiktoken's cl100k_base encoding -- a widely used proxy (Anthropic has no public offline tokenizer; count_tokens is an API call now). Token savings track edge savings closely (55.3% vs 54.5%, 65.7% vs 65.6%), which is expected: both are counting the same JSON-shaped list, just in different units.

Function-level compresses better here (67% vs 54%): finer-grained nodes mean more, smaller, tighter cliques (some genuinely large — a 25-function clique with 300 original edges compressed to 41), and the 7n bound is relatively more generous per node than at file granularity. This isn't guaranteed to hold on every repo, but it's consistent with the intuition that coupling is a stronger, less noisy signal at function granularity than at file granularity (a file with 10 unrelated functions dilutes the file-level signal; the function-level graph doesn't have that problem).

(Exact numbers wobble slightly run-to-run — e.g. 53.5% vs 54.1% observed across runs here — because Python's per-process hash randomization changes set iteration order, which changes Bron-Kerbosch's pivot tie-breaking and therefore which maximal cliques get found first. The savings stay in the same ballpark every time; only the exact clique decomposition varies.)

Every clique is checked two ways before being reported: verify_spanner confirms every pair of items still has a temporal path in the compressed edge set (a silently-broken spanner is worse than no spanner), and the result is asserted to be <= 7n per the paper's bound. demo.py hard-fails (assert, not a warning) if either check doesn't hold.

Why this matters for an LLM agent querying a code graph: a real tool like get_architecture's clusters, or a coupling query, would otherwise have to return all O(n²) edges of a tightly-coupled clique to answer "what's coupled to X". Returning only the O(n) spanner edges answers the same reachability question (which files/functions could a change to X eventually reach, in commit-causal order) with far fewer edges — and, as measured above, far fewer actual tokens on the wire. Same order of magnitude as codebase-memory-mcp's "99% fewer tokens" claim, on a much smaller scale and a different signal (coupling, not call-graph) -- and here it's a number temporal_compress computes and reports itself, not a claim made about it.

Token benchmark vs. the naive path

docs/TOKEN_BENCHMARK.md — a small, right-sized version of codebase-memory-mcp's own Graph-vs-Explorer token methodology: real MCP tool calls vs. real git/grep commands for the same questions, real tiktoken counts on both sides, one repo, four questions, including a deliberate losing case (grep-only queries get no advantage — MCP overhead can even cost slightly more). Run it yourself: python token_benchmark.py <repo_path> [name]. 4.5x fewer tokens on this run — an order of magnitude below their 10-120x claims, which is expected given the much narrower tool surface; not a claim that this project matches their result.

Tool surface

Implemented (real, working):

  • index_repository(repo_path, name?, granularity="file"|"function") — builds the coupling graph + runs compression, persists to ~/.temporal-graph-mcp/<project>.json

  • list_projects() / index_status(project) / delete_project(project)

  • get_architecture(project) — coupling cliques reported as clusters (files-that-move-together, the coupling-graph analog of a real call-graph's community detection)

  • search_code(project, pattern, file_pattern?) — thin grep -rn wrapper, not graph-augmented (no AST index to rank/dedupe by)

  • get_code_snippet(project, file_path, start?, end?) — plain file read

  • temporal_compress(project)the actual point of this project: per-clique before/after edge counts AND actual token counts (real tiktoken tokenization of the edge-list payload, not a proxy) + repo-wide savings %

  • trace_path(function_name, project, mode="calls") — Python-only, ast-based call graph built at index time (call_graph.py): BFS up to depth 3 for what a function calls and what calls it. Name-only resolution (no import/type resolution) — a call to run() links to every function named run in the repo, so common names fan out. Good enough for "what does X touch" on a small/medium repo; not a real symbol resolver.

Explicitly not implemented — each returns a clear "not supported in this prototype" error rather than fabricating an answer: trace_path for mode != "calls", query_graph, manage_adr, ingest_traces, detect_changes. These need a real import-resolved call graph or a Cypher engine, which is exactly the part that makes the real codebase-memory-mcp a 38MB C project — out of scope for what this experiment is testing.

Known limitations (intentional, for a v1 prototype)

  • Function identity is a diff hunk's funcname string, not a real symbol. Two functions with the same name in two different files get distinct node IDs (file::name), but a renamed function loses continuity with its pre-rename history (git's funcname heuristic re-derives the context from the current hunk each time, it doesn't track renames) — a real AST/call graph wouldn't have this gap. Good enough to test the spanner idea; not a substitute for a real per-language resolver.

  • Coupling ≠ semantics. Two files/functions that change together aren't necessarily conceptually related (could be pure incidental churn) the way two co-occurring words are more reliably semantically related. The min_cochanges=2 / max_items_per_commit gates are a blunt filter for this, not a real signal-quality measure.

  • JSON-file storage, not a real graph database — fine for a prototype index of a few dozen/hundred nodes, would not scale to a large monorepo as-is.

  • trace_path's call graph is name-only, Python-only. No import resolution, so two unrelated functions sharing a name (e.g. two run() methods on different classes) are both linked from any call to run(). Non-Python repos get an empty call graph and a clear error, not a fabricated one.

Running it

pip install mcp                       # already present on this machine
python demo.py <repo_path> [file|function]   # standalone self-check, no MCP transport
python server.py                             # runs the MCP server on stdio

To register with Claude Code, add to mcpServers in ~/.claude.json:

"temporal-graph-mcp": {
  "command": "python",
  "args": ["C:/Users/user/OneDrive/Masaüstü/idk/temporal-graph-mcp/server.py"]
}

(Requires restarting Claude Code to pick up a new MCP server.)

Attribution

  • Algorithm: Baligács (2026), "Temporal Cliques Admit Linear Spanners" — implementation vendored from temporal-spanner-analyzer (this user's own project).

  • Tool-naming inspiration: DeusData/codebase-memory-mcp (MIT license) — no code reused, see note above.

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Cross-repository code knowledge graph MCP server for Java, Kotlin, JavaScript, and TypeScript. Indexes source code into embedded KuzuDB via tree-sitter and exposes 30+ tools for call-flow tracing, multi-hop taint analysis (OWASP/CWE/PCI/STIG), entry-point reachability filtering, performance hotspot detection, and license compliance — without reading source files. 95% fewer tokens vs source-read
    Last updated
    33
    1
    MIT
  • A
    license
    -
    quality
    A
    maintenance
    A high-performance code knowledge graph server implementing MCP, indexing codebases into a structured AST knowledge graph with semantic search, call graph traversal, and HTTP route tracing.
    Last updated
    4,375
    60
    MIT

View all related MCP servers

Related MCP Connectors

  • A MCP server built for developers enabling Git based project management with project and personal…

  • An MCP server that gives your AI access to the source code and docs of all public github repos

  • AI Reasoning Cache & Consensus Layer with 11 MCP tools via Streamable HTTP.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iatagun/temporal-graph-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server