Skip to main content
Glama

Quorum — Agentic Meeting Intelligence

Ask an archive of recorded meetings a question in plain language, and get an answer where every claim points back at a speaker and a timestamp in an actual recording — or an explicit refusal when the archive does not support one.

Exposed as an MCP server speaking protocol revision 2026-07-28, and as a FastAPI service. Runs offline on a fresh clone: no API key, no model download.

pip install -e . && quorum ingest && quorum ask "What was the root cause of INC-4471?"

What it looks like

Verbatim output from quorum ask against the five-meeting demo archive in data/meetings/. Nothing here is edited.

A question the archive answers.

$ quorum ask "What was the root cause of INC-4471?"

"Ada Okonjo: Root cause was a connection pool exhaustion in the shipment-tracking
service." [1] "Ada Okonjo: INC-4471 was the two-hour partial outage on the
twenty-eighth of February." [1]

Sources
  [1] Platform Sync (2026-03-04) - Ada Okonjo, Ben Sørensen, Priya Raman @ 00:02:13

retrieval: accept@0.82
grounded: True (1.00) | abstained: False | 11 ms
cost: 3 model call(s), 27 tokens, $0.0000
trace: 39e563cfe873c8fb81059ba3db556f69

A question it does not. No hedged paragraph assembled from whatever matched:

$ quorum ask "How many warehouses do we operate in Brazil?"

The archive does not contain enough to answer that. The closest passages
retrieved did not cover the question.

retrieval: abstain@0.21
grounded: True (1.00) | abstained: True | 24 ms
cost: 2 model call(s), 22 tokens, $0.0000

A question where the first retrieval is too weak, and the corrective loop rescues it. Watch the retrieval: line — two re-queries, then an answer:

$ quorum ask "Which team owns the legacy customs work?"

"Ada Okonjo: The eleventh is the legacy customs-declaration service, which nobody
wants to touch, and honestly that one might be a rewrite rather than a patch." [1]
"Tomás Lindqvist: They don't use customs declarations at all, they're
domestic-only." [1] "Tomás Lindqvist: The customs service also isn't in Halden's
path." [1]

Sources
  [1] Halden Escalation Review (2026-03-25) - Mei Watanabe, Ada Okonjo, Tomás Lindqvist @ 00:02:33

retrieval: requery@0.34 -> requery@0.34 -> accept@0.34
grounded: True (1.00) | abstained: False | 21 ms
cost: 7 model call(s), 70 tokens, $0.0000

The question never says "customs-declaration service" or "Halden". The first two retrievals score below the accept threshold, the agent folds the archive's own vocabulary into the query, and the third round finds it.


Related MCP server: tero-mcp-lite

The MCP surface

Five typed tools over the archive, on MCP Python SDK v2 speaking revision 2026-07-28. Three parts of that revision are used deliberately rather than incidentally.

Typed tools. Every tool returns a Pydantic model, so each advertises a JSON outputSchema and every result carries structuredContent. Clients deserialize an object; they never parse prose.

Trace context crosses the protocol boundary. The revision documents W3C trace context propagation through _metatraceparent, tracestate, baggage (SEP-414). Quorum reads it, so a span opened in the calling client is the parent of every span the agent produces:

result = await client.call_tool(
    "ask_meetings",
    {"question": "What was the root cause of INC-4471?"},
    meta={"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"},
)
result.structured_content["trace_id"]   # '4bf92f3577b34da6a3ce929d0e0e4736'

One trace covers the client call, planning, each retrieval round, each model call, synthesis and citation verification — instead of two disconnected ones. Asserted in tests/test_mcp.py and again over a real subprocess in scripts/smoke_mcp.py.

Stateless by construction. The revision removed protocol sessions and the initialize handshake. Nothing here holds per-connection state: chunk_id is a server-minted handle passed as an ordinary tool argument, which is the pattern the revision prescribes.

Tool

Returns

list_meetings

archive inventory + speaker roster

ask_meetings

cited answer, retrieval rounds, cost, trace id

search_transcripts

ranked passages with speaker + timestamp

get_transcript_context

the passages either side of a citation

summarise_meeting

cited summary by decisions / actions / risks

Wiring instructions for stdio and streamable HTTP: docs/mcp.md.


How it works

plan ──▶ retrieve ──▶ grade ──┬── accept ───▶ synthesise ──▶ verify ──▶ answer
              ▲               │
              │               ├── requery ──▶ rewrite ──┘
              └───────────────┘
                              └── abstain ──▶ refusal

A LangGraph state machine. The interesting part is what stops it from answering.

Three gates, cheapest first.

  1. Corpus coverage. Do the question's content words appear anywhere in any transcript? If a question asks about an acquisition and no transcript contains the word, no re-query can find it. One set intersection, and it catches the largest class of unanswerable question before any model call.

  2. Relevance grading — the corrective-RAG step. Each passage is scored; the accept/re-query decision uses the mean of the best three, not the whole candidate list, so weak tail hits cannot veto a strong head. Below threshold, the query is rewritten from the archive's own vocabulary and retrieval runs again, bounded at three rounds.

  3. Citation verification. Every [n] marker must resolve to a passage that was actually retrieved; unresolvable markers are stripped and recorded. Each sentence's content must appear in what it cites. An answer that ends up citing nothing is withdrawn and becomes a refusal.

Gate 3 is deterministic on purpose. Asking a model whether its own answer is grounded fails in precisely the case that matters — a model that hallucinated a claim will also attest that the claim is supported.

Retrieval is hybrid because meeting questions come in two shapes: "what did we decide about the vendor contract" is a paraphrase (dense wins); "who mentioned INC-4471" is an exact token with no semantics (BM25 wins). Fusion is a weighted sum of min-max normalised scores rather than RRF, because RRF discards score magnitude and gate 2 thresholds on magnitude.

Chunking never splits a segment. Segments are what diarization produced, so they are the smallest span whose speaker and time range can be trusted. Cut inside one and every citation built from it starts misattributing who said what. tests/test_ingest.py asserts this and the properties that follow from it.

More detail: docs/architecture.md.


Results

Measured on the 29-case golden set in evals/golden_set.jsonl, offline backends, regenerated by python evals/run_eval.py. Full report: docs/results.md.

Metric

Value

What it means

False answer rate

0%

never answered a question the archive cannot support

Abstention rate on unanswerable

100%

declined all 6 unanswerable questions

Answer rate on answerable

100%

never abstained out of excessive caution

Citation accuracy

100%

cited the meeting the answer is actually in

Speaker accuracy

100%

attributed to the right speaker

Content accuracy

96%

22/23 — the answer contains the expected fact

Mean retrieval rounds

1.21

the corrective loop fires on 3 of 29 questions

p50 / p95 latency

4.2 / 9.4 ms

offline backends, 27 chunks, one local machine*

* Latency is the one machine-dependent number here, so it is deliberately kept out of the committed docs/results.md — that report is diffed by CI as a regression gate, and a file that changes on every runner cannot serve as one.

Both directions of the abstention trade-off are reported, because a system that reports only one is hiding half its errors: refusing everything scores 100% on abstention, and answering everything scores 100% on answer rate.

The one failure is real and is in the table. fact-12 ("How long would migrating away from Meridian take?") retrieves and cites the correct passage but extracts the wrong sentence from it: the deliberately conservative stemmer does not merge migrating with migration, so the sentence that echoes the question outranks the sentence that answers it. Merging derivational pairs would fix this case and risk wrong citations elsewhere, which is the worse failure for this system. It is left failing and documented rather than tuned away.


Quickstart

git clone https://github.com/AnjanaSuresh01/agentic-meeting-intelligence
cd agentic-meeting-intelligence
pip install -e ".[dev]"

quorum ingest                                     # build the index (~1s)
quorum ask "Why was the notification-dispatch migration deferred?"
quorum search "INC-4471" -k 3                     # raw retrieval hits
quorum summarise 2026-03-18-security-review --focus risks
quorum doctor                                     # config + readiness

pytest -q                                         # 103 tests, offline
python evals/run_eval.py                          # the numbers above
python scripts/smoke_mcp.py                       # MCP over a real subprocess

Serving:

quorum serve-mcp          # MCP over stdio (how desktop MCP clients attach)
quorum serve-mcp --http   # MCP over streamable HTTP, :8765
quorum serve-api          # FastAPI, :8080  (docs at /docs)

Using real models

Everything above runs with no key. To use a hosted or local model:

pip install -e ".[anthropic]"
export QUORUM_LLM_BACKEND=anthropic ANTHROPIC_API_KEY=sk-ant-...
# or, fully local:
export QUORUM_LLM_BACKEND=ollama QUORUM_LLM_MODEL=llama3.1

And for semantic rather than lexical embeddings:

pip install -e ".[embeddings]"
export QUORUM_EMBEDDER=sentence-transformers
quorum ingest        # required: vectors from different embedders are not comparable

All settings and their defaults: .env.example.


What is and is not verified

Claims in this README are things the repository actually does. This table says which ones a machine checked.

Component

Status

Ingest, chunking, retrieval, agent, citations

✅ 103 tests, every run

MCP tools, schemas, _meta trace propagation

✅ in-memory transport (tests) and real subprocess over stdio (scripts/smoke_mcp.py)

FastAPI endpoints, traceparent header

✅ tests

Golden-set metrics

✅ regenerated by CI; docs/results.md is diffed to catch drift

numpy vector store

✅ default, every run

faiss / chroma vector stores

✅ tested — asserted to rank identically to numpy

sentence-transformers embedder

⚠️ code path exercised, not run in CI (model download)

Anthropic / Ollama backends

⚠️ written and reviewed, not run in CI (needs a key / a daemon)

ASR ingest (quorum/ingest/asr.py)

⚠️ error paths tested only; transcription never executed — see the module docstring

Dockerfile, docker-compose

⚠️ written and reviewed, never built or run

OTLP export to a collector

⚠️ spans are produced and asserted; export to Jaeger not run

Known limitations

  • The demo corpus is synthetic. Five fictional meetings about a fictional freight company, written for this repo. They are realistic in shape — decisions reversed, numbers corrected mid-discussion, one participant catching another's error — but no real meeting, person or customer is depicted. There is no public benchmark of speaker-attributed meeting transcripts with citation ground truth, so the alternative was no evaluation at all.

  • Groundedness is uninformative for the offline backend. It answers by quoting transcript sentences verbatim, so groundedness is 1.00 by construction. The metric only discriminates once a generative backend paraphrases.

  • The offline embedder is lexical, not semantic. The hashing embedder will not connect budget to spend. It exists so a clone runs and CI is byte-reproducible; sentence-transformers is one env var away.

  • The offline grader has no word sense. Content-term coverage cannot tell lift (a freeze lifting) from lift (raising something).

  • Stemming is inflectional only. starts/start and credits/credit merge; migrating/migration deliberately do not. See fact-12 above.

  • Single-archive scope. No multi-tenancy, no access control. Every tool can read every meeting.

Repository layout

quorum/
  text.py            tokenisation, stopwords, the conservative stemmer
  config.py          env-driven settings; every default works offline
  llm.py             deterministic / anthropic / ollama backends
  costs.py           per-query token and money ledger
  telemetry.py       OTel spans, GenAI semconv, MCP _meta trace propagation
  ingest/            VTT + JSON loaders, speaker-turn chunking, optional ASR
  index/             hashing + sentence-transformers embedders, BM25,
                     numpy/faiss/chroma stores, hybrid retriever
  agent/             LangGraph graph, reasoner (LLM + algorithmic paths),
                     citation verification
  mcp_app/           MCP server, five typed tools
  api/               FastAPI service
  cli.py             the `quorum` command
data/meetings/       the synthetic corpus (4 × .vtt, 1 × .json)
evals/               golden set + harness that generates docs/results.md
scripts/smoke_mcp.py MCP stdio smoke test, run in CI
tests/               103 tests, all offline

Licence

MIT. The transcripts in data/meetings/ are synthetic and MIT-licensed with the rest of the repository.

A
license - permissive license
-
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

  • F
    license
    -
    quality
    C
    maintenance
    MCP server that acts as a research intelligence agent, converting natural language queries into SQL or full-text search against Hacker News and arXiv data, and returning answers with supporting evidence.
  • F
    license
    -
    quality
    B
    maintenance
    A read-only MCP server that provides conversational querying of quotations data (counts, values, lookups) via tools like quotation_stats, search_quotations, and find_by_number.
  • F
    license
    A
    quality
    B
    maintenance
    MCP server providing governed access to board meeting data, with tools to query meetings, motions, and documents, automatically applying anonymity policies before returning answers.
    6

View all related MCP servers

Related MCP Connectors

  • GibsonAI MCP server: manage your databases with natural language

  • Query any docs site via MCP. Submit a URL, ask questions, get cited answers.

  • Official remote MCP server for Archivist AI TTRPG campaign memory: characters, sessions, and more.

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/AnjanaSuresh01/agentic-meeting-intelligence'

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