Skip to main content
Glama
J-X0

mardale-trading-search-ranking-mcp

by J-X0

mardale-trading-search-ranking-mcp

Project Fathom: relevance-drift monitoring for search ranking, built for Mardale Trading's aviation-maintenance document search.

The system answers one operational question: is our ranking getting worse? It ranks candidate documents for a query, scores that ranking against editorial relevance judgements, and compares the result to an earlier baseline to detect and classify drift. Every decision -- each ranking and each drift verdict -- is written to a tamper-evident audit trail, because the engagement requires that every decision carry a reviewable record.

Why the audit trail is central

The hard constraint on this build is auditability. It is not a logging layer added at the end; it drives the design:

  • fathomsearch/audit.py maintains an append-only, hash-chained ledger. Each record hashes its own content together with the previous record's hash, so any edit, deletion, or reordering is detectable by AuditLog.verify().

  • rank_documents and monitor_drift accept an AuditLog and emit a record for every decision, including the rationale and the thresholds applied.

  • Domain types are frozen dataclasses: once a decision is hashed, the data behind it cannot be mutated out from under the record.

Related MCP server: findocs-mcp

Layout

fathomsearch/
  types.py            domain types (Document, Query, Judgment, Ranking, DriftReport, ...)
  audit.py            hash-chained audit ledger
  metrics.py          nDCG / precision / recall against editorial judgements
  ranking.py          provider scores -> ordered ranking (+ audit)
  drift.py            central algorithm: relevance-drift monitoring (+ audit)
  providers/
    base.py           RelevanceProvider interface
    stub.py           deterministic, offline lexical provider (used by tests)
    real.py           HTTP-backed provider (production; optional httpx)
tests/

Install and test

make venv
make install
make test

make targets run through $(PY) (default .venv/bin/python); override the interpreter with make test PY=/path/to/python.

The test suite runs fully offline with no API key: all model-like behaviour is behind RelevanceProvider, and the tests use DeterministicStubProvider. tests/test_offline.py disables socket creation and runs the whole rank -> evaluate -> drift pipeline to prove nothing reaches the network.

Quickstart

Sample data lives in examples/. From the repository root, score a baseline, score a degraded corpus (one relevant document removed), then compare them:

python -m fathomsearch evaluate \
  --documents examples/documents.json --queries examples/queries.json \
  --judgments examples/judgments.json --label v1 \
  --out v1.eval.json --audit-out v1.audit.jsonl

python -m fathomsearch evaluate \
  --documents examples/documents_degraded.json --queries examples/queries.json \
  --judgments examples/judgments.json --label v2 --out v2.eval.json

python -m fathomsearch drift --baseline v1.eval.json --current v2.eval.json \
  --out drift.json

The drift command exits 1 because removing d1 from the corpus regresses query q1; drift.json shows the per-query deltas and v1.audit.jsonl holds the verifiable ledger for the baseline run.

Using the core

from fathomsearch.types import Query, Document, Judgment, JudgmentSet, Evaluation
from fathomsearch.providers.stub import DeterministicStubProvider
from fathomsearch.ranking import rank_documents
from fathomsearch.metrics import evaluate_ranking
from fathomsearch.drift import monitor_drift
from fathomsearch.audit import AuditLog

audit = AuditLog()
provider = DeterministicStubProvider()

query = Query("q1", "hydraulic leak inspection")
docs = [
    Document("d1", "hydraulic leak inspection checklist"),
    Document("d2", "galley equipment manual"),
]
judgments = JudgmentSet([Judgment("q1", "d1", 3), Judgment("q1", "d2", 0)])

ranking = rank_documents(provider, query, docs, audit=audit)
evaluation = evaluate_ranking(ranking, judgments, k=10)

# Compare two snapshots to detect drift:
baseline = Evaluation("v1", {"q1": evaluation})
current = Evaluation("v2", {"q1": evaluation})
report = monitor_drift(baseline, current, metric="ndcg", audit=audit)

assert audit.verify()          # chain intact
audit.write_jsonl("run.audit.jsonl")

Drift severity bands

monitor_drift classifies each query's metric change (current minus baseline) using DriftThresholds:

  • major_regression: delta <= major (default -0.15)

  • minor_regression: major < delta <= minor (default -0.05)

  • stable: within +/- stable_band (default 0.02)

  • improved: delta >= stable_band

A query that appears in only one snapshot is a drift signal that cannot be measured as a delta; it is excluded from the deltas and recorded separately.

The real provider

fathomsearch/providers/real.py posts query/document text to an HTTP scoring backend. It requires httpx (pip install ".[real]") and an API key via FATHOM_API_KEY or the constructor. It never falls back to a heuristic on failure: a silent fallback would poison drift comparisons. It is not imported by the test suite.

Command-line interface

Installed as fathomsearch (or run python -m fathomsearch). Input files are JSON:

  • documents: [{"id": "...", "text": "...", "metadata": {}}, ...]

  • queries: [{"id": "...", "text": "..."}, ...]

  • judgments: [{"query_id": "...", "doc_id": "...", "grade": 0}, ...]

Score a run into an evaluation file, capturing the audit ledger:

python -m fathomsearch evaluate \
  --documents docs.json --queries queries.json --judgments qrels.json \
  --label v1 --out v1.eval.json --audit-out v1.audit.jsonl

Compare two evaluations and classify drift:

python -m fathomsearch drift --baseline v1.eval.json --current v2.eval.json \
  --out drift.json --audit-out drift.audit.jsonl

Exit codes: 0 no regressions, 1 one or more regressions found (usable as a CI gate), 2 an expected error (bad input, missing file, bad config).

Start the MCP server (needs the mcp package installed):

python -m fathomsearch serve

Configuration

Configuration is read from the environment and validated at startup:

Variable

Default

Meaning

FATHOM_PROVIDER

stub

stub or real

FATHOM_METRIC

ndcg

ndcg, precision, or recall for drift

FATHOM_K

10

cutoff for metrics

FATHOM_MAX_DOCUMENTS

10000

per-query candidate limit (resource guard)

FATHOM_MAX_QUERY_LENGTH

2048

max query characters

FATHOM_LOG_LEVEL

INFO

logging level

FATHOM_LOG_JSON

1

0 for human-readable logs

FATHOM_AUDIT_PATH

unset

default audit ledger path

FATHOM_REAL_ENDPOINT / FATHOM_REAL_MODEL / FATHOM_API_KEY

unset

real provider settings

Error handling and observability

  • fathomsearch/errors.py defines the failure hierarchy; the CLI turns any FathomError into exit code 2 with a message, and lets real bugs crash.

  • fathomsearch/loader.py reports missing files, bad JSON, and wrong-shaped records with the file and record index named.

  • fathomsearch/service.py enforces input validation and resource limits, times each operation, and logs structured events.

  • fathomsearch/logging_config.py emits one JSON object per log line and a timed() context manager that records duration even when the block raises.

  • A failed audit write is surfaced (persist_audit returns False and logs an error) rather than swallowed, because auditability is the binding constraint.

Architecture

Layered so the audit trail and validation are enforced once, regardless of entry point:

CLI (cli.py)  /  MCP server (server.py)
        \             /
       RankingService (service.py)      validation, limits, timing, audit
        /      |          \
 ranking.py  metrics.py  drift.py       core algorithm
        \      |          /
          providers/ (base, stub, real) + audit.py + types.py

config.py builds the Config and the provider; loader.py handles file I/O. Both entry points call RankingService, so an MCP caller and a CLI caller get identical validation, resource limits, and audit records.

Design decisions with a real alternative are recorded in docs/adr/.

Known limitations

  • The stub provider is lexical (term overlap), not a learned relevance model. It exists for offline runs and tests; production relevance needs the real provider against a scoring backend.

  • The audit chain proves internal consistency, not authorship: a party who rewrites the whole ledger produces a valid chain. Detecting that requires signing or an external anchor, which is out of scope here (see ADR 0001).

  • nDCG treats a query with no relevant judgement as a perfect 1.0, which can flatter the aggregate; read nDCG alongside precision (see ADR 0002).

  • The MCP server (serve) requires the mcp package, which is not installed by the offline test environment. build_server is imported lazily and is not exercised by the suite.

  • Metrics assume graded judgements are complete enough to trust; unjudged documents count as non-relevant (the standard pooled-qrels assumption), which can understate quality on shallow pools.

Status

Delivered: the core algorithm, providers, configuration, the CLI and MCP entry points, input validation, error handling, structured logging, ADRs, and the test suite. The MCP server transport imports mcp lazily, so everything else runs and tests without it installed.


Mardale Trading is an illustrative client; this repository is a self-directed reference implementation built to work end to end.

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.

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

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    An MCP server that provides web search and semantic reranking capabilities using the LangSearch API. It enables searching billions of web documents with AI-optimized results and reordering them based on semantic relevance scores.
    -
  • A
    license
    A
    quality
    C
    maintenance
    An eval-first MCP server for semantic search and grounded Q\&A over financial documentation, with a CI regression gate that fails on retrieval or faithfulness regressions.
    3
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for documentation search that automatically indexes web documentation sites and provides semantic, full-text, or hybrid search capabilities.
    14
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server for AI search crawler governance, brand safety, and search infrastructure auditing. Provides tools to audit robots.txt, canonical links, sitemaps, redirects, and send IndexNow notifications.
    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/J-X0/mardale-trading-search-ranking-mcp'

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