Skip to main content
Glama
rich-atkins

grounded-mcp

by rich-atkins

grounded-mcp

An MCP server for markdown knowledge vaults that ships with its own eval suite.

grounded-mcp demo: cited search, structural abstention, index-level entitlements, and the eval gate going red when weakened

Plenty of servers expose your notes over the Model Context Protocol. Before you point an agent at a knowledge store, though, you need answers to three questions most of them skip:

  1. Can I trust what it returns? Every content-bearing response carries a stable citation id (path#heading + line spans) — nothing is returned that can't be quoted with provenance.

  2. Does it know when it doesn't know? Search abstains — an explicit, structured "no adequate answer" — instead of dressing weak matches up as results.

  3. Who is allowed to see what? Declarative entitlements, enforced at index level: content a profile can't see is never indexed for it, so it can't leak through scores, snippets, or rankings. A redaction guard refuses to serve notes that look like they contain credentials, regardless of entitlements.

And none of that is a claim — it's a CI-gated scorecard run against the committed demo vault:

retrieval   (staff, n=22):  hit@1 1.00   recall@5 1.00   MRR 1.00
abstention  (n=10):         rate  1.00   (target 1.00)
leakage     : 0 (must be 0)
redaction   : 0 (must be 0)
EVAL GATE: PASS

Weaken the abstention gates and the build fails — try it:

GROUNDED_MIN_SCORE=0 GROUNDED_MIN_COVERAGE=0 python evals/run_evals.py   # EVAL GATE: FAIL, exit 1

Quickstart

pip install -e .
grounded-mcp          # serves the bundled ACME demo vault over stdio

Point it at your own vault (Obsidian works as-is — frontmatter, [[wikilinks]], tags):

GROUNDED_VAULT=~/notes GROUNDED_PROFILE=default grounded-mcp

Claude Code / Claude Desktop

{
  "mcpServers": {
    "grounded": {
      "command": "grounded-mcp",
      "env": { "GROUNDED_VAULT": "/path/to/your/vault" }
    }
  }
}

Related MCP server: fenced-obsidian-sync-mcp

Tools

Tool

What it does

search(query, k)

BM25 over titles/headings/tags/body. Cited hits — or an explicit abstention with the scores that failed the bar.

read_note(citation, section_only)

A note or single section by citation id; every block carries its own citation anchor.

backlinks(path)

Wikilink graph: what links here, within your entitlements.

browse(prefix, tag)

List visible notes by folder/tag.

Read-only by design — a server that can quote your vault but never rewrite it is a trust feature, not a missing feature.

Configuration

Env var

Default

Meaning

GROUNDED_VAULT

./demo_vault

vault root

GROUNDED_PROFILE

default

entitlements profile to serve as

GROUNDED_ENTITLEMENTS

<vault>/entitlements.yaml

rules file (absent = allow all)

GROUNDED_MIN_SCORE

1.0

abstention score threshold

GROUNDED_MIN_COVERAGE

0.5

abstention term-coverage threshold

GROUNDED_SERVE_REDACTED

false

serve notes containing secret patterns

GROUNDED_TRANSPORT

stdio

stdio | http (v0.2)

GROUNDED_TOKENS

REQUIRED in http mode: yaml token map (sha256 → client/profile)

GROUNDED_HOST / GROUNDED_PORT

127.0.0.1 / 8000

http bind address

Entitlements (deny wins, then allow, then default-deny):

profiles:
  staff:
    allow: ["public/**", "internal/**"]
    deny:  ["restricted/**"]
  contractor:
    allow: ["public/**"]

Why abstention needs two gates (a measured finding)

BM25 score alone cannot separate true answers from confident-looking misses on a small vault: in our golden set, genuine answers score as low as 2.4 while wrong-but-plausible matches reach 4.3 — a single common word ("policy") landing in a weighted title field looks like a result. The discriminator is term coverage: what fraction of the query's content words the note actually contains. Both gates together take abstention from 0.30 → 1.00 with zero retrieval loss. The eval suite is what made that tuning honest — full details in evals/run_evals.py and the scorecard baseline.

HTTP mode (v0.2): real per-client entitlements

python -m grounded_mcp.authz my-raw-token        # -> sha256 for the tokens file
GROUNDED_TRANSPORT=http GROUNDED_TOKENS=tokens.yaml GROUNDED_VAULT=~/notes grounded-mcp

Each client's bearer token maps to an entitlements profile (see example-tokens.yaml); the server keeps one index per profile, so enforcement stays index-level per VERIFIED identity. Unknown token = 401, never a default. Empty token file = server refuses to start. Tested over the wire: two tokens against one live server get different vaults, and the leakage probe runs as a real client.

Honest boundaries

  • stdio = one user. Over stdio, client and server run as the same user, so profiles demonstrate the deployment pattern rather than enforce against a hostile peer — for real multi-client enforcement, use HTTP mode (above). The enforcement machinery is identical either way — index-level, not response-filtering.

  • A denied note and a nonexistent note return the same response. The server refuses to be an existence oracle for content outside your entitlements.

  • The redaction patterns are high-precision, not exhaustive. They catch key-shaped strings (AWS/GitHub/Slack tokens, private-key blocks, api_key = "..." assignments), not every secret.

  • BM25 is the deliberate v0.1 baseline — deterministic, dependency-free, measurable. Hybrid semantic retrieval lands in v0.3 with its eval delta published against this baseline.

The demo vault

A fictional company handbook ("ACME Ltd") with three zones — public/, internal/, restricted/ — plus one deliberately seeded fake-credentials note (the classic AWS documentation example key) that the redaction guard must refuse to serve. All content is synthetic; the vault exists so the eval suite has something real-shaped to prove things against.

Development

pip install -e ".[dev]"
pytest -q                      # unit tests
python evals/run_evals.py      # the eval gate (CI runs both)
python evals/run_evals.py --write-baseline   # accept current scores after a deliberate change

Roadmap

  • v0.2 ✅ shipped — streamable HTTP transport with per-client entitlements.

  • v0.3 — hybrid semantic retrieval, landing only with its eval delta vs the BM25 baseline published.

  • Pluggable store backends (the vault interface is small); community adapters welcome.

MIT © Richard Atkins

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

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

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    An MCP server that provides semantic search and vault operations for Obsidian notes, enabling agents to recall and manage notes with provenance and low token usage.
    680
    6
    ISC
  • A
    license
    Not graded
    quality
    B
    maintenance
    A deny-by-default MCP server for Obsidian vaults where operators declare exact capabilities (list, read, create, etc.) scoped by path globs; everything not permitted is impossible by construction as disallowed tools are never registered.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server that exposes an Obsidian-style markdown vault as agent-readable memory. It provides tools for search, read, graph, and guarded capture without using embeddings.
    1
    MIT

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/rich-atkins/grounded-mcp'

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