hubmesh

io.github.DemigodDSKPublished by GitHub account DemigodDSKv0.4.2Updated Oct 24, 2026

Deterministic multi-hop graph retrieval for RAG. Zero LLM calls in the query path.

VerifiedSTDIODesktop onlyDatabasesKnowledge & MemoryAI & ML

Overview

AI-generated overview

Exposes a deterministic, LLM-free GraphRAG retrieval planner over your vector database, with tools for indexing, multi-hop retrieval, entity resolution, and…

What you can do
hubmesh is a Python retrieval planner that sits between an existing vector database and an LLM. Its MCP server exposes operator tools over stdio: index_corpus, retrieve (seed-steerable), resolve_entities, entity_neighbors, path_between, get_document, graph_stats, and list_corpora. Retrieval combines cosine relevance with Personalized PageRank over an entity knowledge graph and packs results into a token budget, returning sources and reasoning paths. The query path makes zero LLM calls and is deterministic.
When to use it
Use it when multi-hop questions over a corpus fail with naive top-k vector retrieval and you want an agent to drive iterative hops itself. It fits existing Qdrant, Chroma, or in-memory stores without migration. It is pre-alpha, so evaluate on your own workload first.
Requirements
Install the PyPI package hubmesh with the mcp extra, plus the spaCy model en_core_web_sm for KG mode. Runs as a local stdio process; corpora persist as JSON/NPZ under ~/.hubmesh/corpora. Optional SSE serving over a tunnel requires the HUBMESH_API_KEY environment variable and clients must send an Authorization Bearer header.
Before you install
Tunneled serving requires HUBMESH_API_KEY and defaults to read-only; --allow-writes re-enables index_corpus, which writes corpora. Read-only still allows get_document to return full text, so an unauthenticated tunnel edge exposes private corpora. The README warns that injecting the Authorization header for anonymous traffic grants every caller read access. Benchmarks show the graph layer can hurt top-rank recall with strong embeddings.

Installation

In SourceWeft

  1. Open hubmesh in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

hubmesh

[tests] [Python] [License: MIT] [Release]

Centrality-aware GraphRAG retrieval planner. Drop-in layer over any vector DB.

hubmesh is a Python library that improves multi-hop RAG quality on top of an existing vector database. You don't replace your infrastructure — you add a smart planner between your vector DB and your LLM.

What problem this solves

Naive vector retrieval ("embed query, get top-k by cosine similarity") fails on multi-hop questions like "Where was the founder of the company that acquired Slack born?" The correct answer requires retrieving entities along a reasoning path, not the single most similar item.

GraphRAG and HippoRAG showed that running a small Personalized PageRank over a knowledge graph at query time can substantially improve multi-hop retrieval. hubmesh extends that line with two contributions:

  1. Entity-anchored seeding, multi-component ranking. In KG mode the PPR seeds are the question's own entities resolved against the corpus graph (alias index; falls back to the entities of the top cosine matches when the question names none); in kNN mode they are the ANN top-k. The multi-component score — cosine relevance, pooled PPR mass, and multi-anchor convergence, min-max normalized and fused 3:1:1 — is applied to the document ranking, not to seed choice.
  2. Budget-aware context packing. Once relevant entities are scored, pack them into the LLM's context window with explicit coverage and redundancy control rather than just truncating top-k.

Quickstart

In-memory (testing, small corpora)

python
from hubmesh import Plannerfrom hubmesh.adapters import InMemoryStore
embed = ...   # callable: text -> np.ndarraydocs = [...]  # list of Document or strings or dicts
store = InMemoryStore.from_documents(docs, embed=embed)planner = Planner(store=store, embed=embed)result = planner.retrieve(query="...", top_k=10, budget_tokens=4000)

Qdrant adapter (production)

python
from hubmesh import Plannerfrom hubmesh.adapters import QdrantStore
store = QdrantStore.from_documents(docs)                          # in-memorystore = QdrantStore.from_documents(docs, path="./qdrant_data")    # on-diskstore = QdrantStore.from_documents(docs, url="http://localhost:6333")  # remote
planner = Planner(store=store, embed=embed)result = planner.retrieve(query="...", top_k=10)

Chroma adapter

python
from hubmesh.adapters import ChromaStore
store = ChromaStore.from_documents(docs)                          # ephemeralstore = ChromaStore.from_documents(docs, persist_directory="./chroma_data")store = ChromaStore.from_documents(docs, host="localhost", port=8000)

Multi-hop / KG mode

python
from hubmesh.kg import build_entity_kgimport spacy
nlp = spacy.load("en_core_web_sm")kg = build_entity_kg(docs, nlp=nlp)
planner = Planner(store=store, kg=kg, nlp=nlp, embed=embed)   # embed= needed for text queriesresult = planner.retrieve(query="Where was the founder of the company that bought Slack born?",                          top_k=10, budget_tokens=4000)
# RetrievalResult includes reasoning paths showing why each doc was returnedfor path in result.reasoning:    print(f"  score={path.score:.3f}  {' → '.join(path.node_ids)}")

LLM-extracted KG (richer than spaCy)

python
from hubmesh.kg_llm import build_entity_kg_llmfrom hubmesh.entity_linker import EmbeddingLinker, make_st_embedder
def llm(prompt):  # provider-agnostic — bring your own    return your_llm_call(prompt)
kg = build_entity_kg_llm(docs, llm=llm, cache_path="kg_cache.json")
# optional: cross-document entity dedup — same Linker protocol as the spaCy pathkg = build_entity_kg_llm(docs, llm=llm, cache_path="kg_cache.json",                         linker=EmbeddingLinker(embed=make_st_embedder()),                         llm_identity="gpt-5-mini")   # namespaces the cache
planner = Planner(store=store, kg=kg, nlp=nlp, embed=embed)

Better entity linking

python
from hubmesh.kg import build_entity_kgfrom hubmesh.entity_linker import EmbeddingLinker, make_st_embedder
# Cluster surface variations: "United States" / "U.S." / "USA" → one entitylinker = EmbeddingLinker(embed=make_st_embedder(), threshold=0.82)kg = build_entity_kg(docs, linker=linker)

Iterative multi-hop: let your agent drive

python
r1 = planner.retrieve(query=question, top_k=5)
# your agent reads r1, spots the bridge entity, then aims hop 2 at it:r2 = planner.retrieve(    query=question, top_k=5,    seed_entities=["Nimbus Analytics"],           # merged with the query's own seeds    exclude_docs=[s.doc.id for s in r1.sources],  # don't re-retrieve consumed docs)

Seed mentions resolve through the alias index, so free-text entity names work. The query path stays deterministic and LLM-free — the planning intelligence lives in the caller.

MCP server: plug hubmesh into any agent

bash
pip install "hubmesh[mcp]"python -m spacy download en_core_web_sm
json
{"mcpServers": {"hubmesh": {"command": "hubmesh-mcp"}}}

Exposes the planner as deterministic operator tools over stdio — index_corpus, retrieve (seed-steerable, as above), resolve_entities, entity_neighbors, path_between, get_document, graph_stats, list_corpora. Your agent is the solver: it decomposes the question, reads each hop, and aims the next one; the server answers in milliseconds with zero LLM calls. Corpora persist as plain JSON/NPZ under ~/.hubmesh/corpora.

The server warms up models and persisted corpora in the background at launch (~5-10s on first run), so tool calls stay fast from the start — relevant for strict-timeout connector clients (Perplexity, etc.).

For web-based connector clients, serve SSE natively — no gateway process needed:

bash
export HUBMESH_API_KEY="$(openssl rand -hex 24)"   # any strong secrethubmesh-mcp --transport sse --port 8000 --allow-tunnelngrok http 8000     # paste https://<your-url>/sse into the connector

Tunneled serving requires the API key (the server refuses to start without one) and defaults to read-only — pass --allow-writes to keep index_corpus enabled. Clients must send Authorization: Bearer <key>. If your connector client cannot set headers, the tunnel edge must authenticate callers itself (ngrok OAuth / IP-restriction traffic policy, Cloudflare Access, …) before it adds the upstream header — injecting the header for anonymous traffic hands every caller full read access (read-only protects corpora from replacement, not from disclosure; get_document returns full text). A client that can neither send the header nor sit behind an authenticating edge is unsupported for private corpora.

Tunnel field notes (from a live Perplexity integration): ngrok works (free tier included); cloudflared quick tunnels buffer SSE bodies and hang tool calls; supergateway is unnecessary here and crashes on reconnect. --allow-tunnel accepts the tunnel's forwarded Host header — without it, proxied requests get 421 Misdirected Request.

Full field report — setup, error decoder, a 9/9 test battery run through Perplexity, and two findings about reasoning-model behaviour — in docs/perplexity.md.

Chunking long documents

python
from hubmesh import chunk_by_sentences, chunk_documents
chunks = chunk_documents(    [{"id": "doc1", "text": long_text}, ...],    strategy="sentences", target_tokens=200,)# Then embed chunks and index normally

Installation

bash
pip install hubmesh                   # corepip install "hubmesh[qdrant]"         # Qdrant adapterpip install "hubmesh[chroma]"         # Chroma adapterpip install "hubmesh[kg]"             # entity-linked KG (spaCy)pip install "hubmesh[linker]"         # embedding-based entity linkerpip install "hubmesh[all]"            # everythingpython -m spacy download en_core_web_sm   # required for KG mode

Design

KG mode — the benchmarked, production path:

query ─► spaCy NER ─► alias index ─► entity seeds ─► Personalized PageRank over the corpus KG  │                   (fallback: entities of the top-3 cosine documents)              │  └───────────► cosine similarity against every document ─────────────────────────────┤                                                                                      ▼            3·minmax(cosine) + 1·minmax(pooled PPR) + 1·minmax(per-anchor geomean)   [weighted sum]                                                                                      ▼                          budget-aware packing ─► context + sources + reasoning paths

kNN mode (no KG; prototyping): first-pass ANN → capped induced proximity subgraph → PPR from the ANN seeds → the same scoring and packing. Community anchoring exists for single-topic retrieval and is off by default.

Each layer is independently testable and replaceable. Adapters wrap your existing vector DB so you don't have to migrate — note that KG mode scores every document (vectors are gathered once per store version and cached) and uses the store's ANN index only for the seed fallback. The full design, including the store contract, persistence, the serving security model and measured scaling limits, is in docs/ARCHITECTURE.md.

Benchmarks

Supporting-fact paragraph recall over pooled distractor corpora. Every row is a separate experiment: document representation and embedding model change the absolute numbers materially, so rows are never compared across representations. Protocol, ablations and limits are in BENCHMARKS.md.

Full HotpotQA dev (7,405 questions, 66,581 pooled paragraphs), hubmesh vs naive cosine, v0.4 defaults:

representation · embeddingnaive @10hubmesh @10Δ @10Δ @5Δ @2
body only · MiniLM-L669.3%75.2%+5.90+4.21−0.75
title+body · MiniLM-L670.0%77.3%+7.24+5.88+0.25
title+body · bge-m383.5%84.8%+1.38−1.41−9.09

Read both directions. With a small embedding the graph layer adds 5–7 points of depth recall; with a strong one the depth gain shrinks to +1.4 and the defaults hurt the top ranks (−9.1 at recall@2). The convergence term trades top-rank precision for depth: for top-2/top-5 workloads on strong embeddings use use_convergence=False or plain cosine, and evaluate on your own workload before turning the graph layer on everywhere.

Full MuSiQue-Ans dev (2,417 questions, MiniLM, body only), hubmesh vs naive, recall@10 with paired 95% CIs: +3.67 [+3.00, +4.39] overall (+2.2 at @2, +3.3 at @5); by hop count +2.9 / +4.1 / +5.3 (n = 1,252 / 760 / 405). The gain grows with hop count, and on MuSiQue hubmesh beats naive at recall@2 as well.

What the scoring adds (HotpotQA N=500, body only, recall@10): on the same graph, seeds, fallback and packer, cosine-fused scoring reaches 0.871 against 0.676 for the pure structural (PPR-only) signal — +19.5 pts [+16.3, +22.7] (MuSiQue N=300: +16.3). Earlier versions quoted +29.8 against a HippoRAG-style ranker; that comparison also changed the pipeline and is no longer cited as scoring attribution.

Convergence term (default on): +0.9 pts @10 over convergence-off on full MuSiQue dev and +1.1 on HotpotQA N=500. A single-solve log-pooled signal in the same slot matches it in aggregate; the geomean keeps ~1 pt at three and four hops. Multi-seed queries cost ~1.5–2× (still zero LLM tokens, deterministic).

Latency: ~22 ms mean / 26 ms p95 per query on a 7K-node KG (after PPR matrix caching). ~3 s/query was measured at the 66K-paragraph full-dev scale with convergence on, before the per-query vector re-gather was removed; that scale has not been re-measured since.

Reproduce (each run writes a JSON with per-query records and a manifest carrying the commit, dirty flag and source/harness content hashes):

bash
python benchmarks/run_hotpotqa.py --n 500 --kg --out hotpot.jsonpython benchmarks/run_musique.py  --n 300 --kg --out musique.jsonpython benchmarks/run_ablation_coherence.py --dataset musique --n 2417python benchmarks/profile_query.py        # latency profile

Status

Pre-alpha (v0.4.2). Core algorithms implemented and validated; adapters for in-memory, Qdrant, and Chroma; entity-linked KG with both spaCy NER and LLM-based extraction (both linker-aware); alias-indexed entity resolution; NNSI-KG scoring (multi-source convergence default-on, hub-discounted PPR opt-in); agent-driven iterative multi-hop via seed_entities / exclude_docs; MCP operator server (hubmesh-mcp, native SSE) with JSON/NPZ corpus persistence; document chunking; reasoning-path explanation; PPR-cache latency optimisation. Pinecone / pgvector / Weaviate adapters and additional multi-hop benchmarks are tracked as good first issues.

License

MIT

Source: README.md at commit de4f605

Tools

0
SourceWeft has not started this package in its sandbox yet, so its tools are not known.

Version history

1
  1. v0.4.2LatestSep 16, 2026

Score

50% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts

    Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.

  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts