knowledge-engine
Searches the arXiv API for recent papers using terms derived from the research question, providing curated technical sources for the grounded research tool.
Optionally uses the Brave Search API when BRAVE_API_KEY is set for higher-quality open-web results instead of the default DuckDuckGo backend.
Performs tuned open-web searches to supplement knowledge-base results with current, lower-trust sources in the research tool.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@knowledge-engineresearch best practices for LLM evaluation"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
amiticia-knowledge-engine
Watches AI/dev content and writes it into the AmiticIA knowledge repository
(amiticia-knowledge), then DMs a pointer via the existing whatsapp-mcp
service. Four entrypoints share the codebase — three one-shot cron jobs that
write the repo, and one long-lived server that reads it:
watch.py— mentors. Curated people we follow (Akita, Lucas Montano, Simon Willison). High trust: every item is summarized and scored, no relevance gate. Mon/Wed/Fri cron.research.py— research feeds. High-volume, noisy sources (arXiv, Import AI, Ahead of AI, AlphaSignal, METR, Epoch). Hard-filtered to 2026+ and LLM-sifted for relevance before anything is kept. Own cron.synthesize.py— topic synthesis. Reads theinbox/the other two wrote, classifies each new entry into one of the fixedtopics/, and rewrites those topic pages as rank-ordered card lists. Runs last in the cron chain. The LLM only classifies; all ranking and rendering is plain Python, so topic pages are reproducible and the LLM never rewrites accumulated knowledge.serve.py— research MCP server. A long-lived server (not a cron job) that exposes the knowledge repo as two MCP tools —query_knowledge(BM25 retrieval, free) andresearch(the grounded loop: KB + live web + LLM synthesis → a dated, cited answer). The tool to consult before an architecture decision, so the answer comes from current reality.
Why this exists: leading-edge AI-agent knowledge moves faster than any model's training data. This is durable external memory — current, dated, verified — so architecture decisions are made from 2026 reality, not stale training.
▶ Start here:
serve.py— the read side, and the only long-lived process. Everything else exists to fill the corpus it serves.
Look at first
serve.py— the research MCP server. Two tools:query_knowledge(BM25 + a local cross-encoder rerank, free, deterministic, no network) andresearch(the grounded loop that returns a JSON contract — answer, citations with a tier per source, confidence, retrieval date, warnings). The tier label is the interesting part: a curated source and an open-web result are never presented as equally trustworthy.prompts/— every LLM call this system makes, as plain markdown files, one per seam: sift, classify, expand, link, research, summarize. The design rule is visible here — the LLM only ever sifts or classifies; ranking, rendering and page assembly are plain Python, so topic pages are reproducible and no accumulated knowledge is ever LLM-rewritten.promote.py— the enrichment loop, and the best example of the trust posture. It mines the deep-research history the server persists and promotes the sources those runs discovered — never the synthesized answer. Each candidate URL is re-fetched, sifted for relevance and authority-tagged, so a low-trust page is visibly marked rather than silently absorbed.
Related MCP server: pramana-mcp
What each run does
Pull the latest items per source (
sources.py/sources_research.py).Skip items already in the seen-ID state (
state.json/research_state.json).research.py only: drop items dated before 2026.
LLM-sift each new item with DeepSeek-V3 via OpenRouter:
mentors →
prompts/sift_mentor.md(summary + relevance 1-5, never skipped),research →
prompts/sift_research.md(summary + relevance, orSKIP).
Append the keepers as markdown entries to the knowledge repo's
inbox/<YYYY-MM-DD>.md(knowledge.py).Persist seen IDs. (The watchers no longer DM —
synthesize.pyis the sole digest author; see below. They print feed-health warnings to the cron log.)
The knowledge repo itself lives at KNOWLEDGE_DIR; on the VPS that is a git
clone, and the host cron commits + pushes after each run (see Deploy).
Synthesis (synthesize.py)
After the two watchers append to inbox/, synthesize.py rolls those entries
into the readable topics/ pages:
Parse every
inbox/*.mdentry; skip those already insynth_state.json.One LLM call per new entry → a topic slug + optional business "so what" + optional supersedes flag (
prompts/classify.md).For each touched topic: parse the existing cards out of the page, add the new ones (deduped by link), recompute every card's rank (
relevance + recency-decay + authority, weights in the repo'sranking.toml), re-sort, rewritetopics/<slug>.md.
It only ever writes topics/, AGENTS-BRIEFING.md, the magazine under k/,
links.json and synth_state.json — never inbox/, never git. --backfill
reprocesses the whole inbox ignoring state; --rerank-all re-ranks every page
without needing new entries (no LLM call).
After ranking, two things happen (both best-effort — a failure never aborts the canonical topic-page write):
Related links. For each new/changed card, BM25 proposes up to eight keyword-similar candidates (
links.py, reusing thequery.pyretrieval), and one bounded LLM call (prompts/link_related.md) keeps the 2-3 genuinely related ones with a short reason. These are sidecar metadata inlinks.json(keyed by card link, cached by content hash) — cards are never mutated, so the classify-not-rewrite rule holds. The topic pages and magazine render a "Related" block from them.Themed digest DM.
synthesize.pyis now the sole WhatsApp digest author: it groups this run's new cards by topic (📚 *Knowledge digest*, one*Topic*section each,• title — so-whatlines, ≤8 with an "…and N more" tail), and rides the dead-feed + stale-topic ⚠️ warnings (read from both ingest stats files) on the same DM. Sent only when something new was classified, after whichsynth_state.jsonandlinks.jsonare saved — the after-send rule, so a failed send replays cleanly.
Research MCP server (serve.py)
serve.py is the read side. It never writes the repo — it serves it. Two MCP
tools:
query_knowledge(question, topic?)— two-stage KB retrieval over thetopics/cards: BM25 pulls a wide candidate set (recall), then a local FlashRank cross-encoder reranks it for precision (see Search rerank below). Free, fast, deterministic; no web, no LLM. Returns ranked dated cards + a coverage report.research(question)— tiered grounded research. Gathers from three tiers, in priority order, then synthesizes:KB — BM25 over the
topics/cards (the already-sifted cache).Curated sources — the arXiv query API (terms derived from the question) + domain-scoped search of our curated source set (
site:metr.org <q>…). The trusted moat — not the open web.Open web — tuned DuckDuckGo: LLM-expanded queries, 2026+ floor, content-farm domains dropped. Supplementary, lower-trust, labelled.
Two LLM calls: question → search plan (
prompts/expand.md), then synthesis (prompts/research.md). Returns a JSON grounding contract —answer,citations(url/title/date/tier),confidence,retrieval_date,warnings,sources_used. Every claim must trace to a retrieved source.
The curated-domain set is derived from the watcher's own source registry
(sources.py / sources_research.py — the domain field, curated_domains()),
so it stays in sync. Web search works with no API keys (ddgs); set
BRAVE_API_KEY / TAVILY_API_KEY for higher-quality results. KB retrieval uses
BM25, not embeddings — no vector DB, no index to rebuild.
Search rerank (rerank.py). BM25 is recall-only: it matches keywords, so a
paraphrased question ("how do I make my agents reliable") misses a card titled
"Reducing tool-call errors in agents". search_kb (query.py) therefore pulls
a wide BM25 candidate set (RERANK_CANDIDATES, default 50) and reranks it with a
FlashRank cross-encoder (ms-marco-MiniLM-L-12-v2, ~34 MB, local CPU,
Apache-2 — no vector DB, no API spend), returning the reranked top-N. The
FlashRank call is a single seam (rerank._cross_encoder_scores), stubbed in
tests. It degrades gracefully: if the model can't load or RERANK_DISABLED=1,
retrieval falls back to plain BM25 order, so the cron path (which never searches)
and the test suite never depend on the model download. The model is downloaded
on first use to FLASHRANK_CACHE (a writable bind-mount on the VPS so it
persists across restarts).
Knowledge graph (/k/graph). A public, framework-free interactive graph of
the KB: nodes are cards (colored by topic, sized by rank), edges are the
links.json related-card links. Built by the pure graph.build_graph_payload
(graph.py) and written as k/graph.json by synthesize.py alongside the
magazine (best-effort, never aborts synthesis); rendered client-side by
serve.py with a vendored Cytoscape.js (vendor/cytoscape.min.js, no CDN). Tap
a node for a side panel (source / authority / relevance / summary, "Open source",
"Ask about this" deep-link into /k/ask, connected cards); topic-filter chips +
a search box highlight nodes.
Research-trends dashboard (/k/trends). A framework-free "Demand × Coverage"
view of the read-side demand signal: an SVG bubble scatter (x = how often a theme
is asked, y = how well the KB covers it) with the high-demand / low-coverage
gap zone shaded, a ranked theme bar list with GAP badges, and a
"most-demanded cards" strip. Themes + card demand come from demand.py
(demand_themes / card_demand), computed live on each request from
query_log.jsonl (pure Python — no LLM, no network, nothing written) and served
by _trends_payload. A 7/30/90-day window switch re-queries the same endpoint.
It is a plain /k surface like the graph — linked from the Magazine/Ask/Graph
navs and reachable at https://example.com/k/trends. serve.py applies no auth of
its own to /k*; whatever access layer fronts the deployment guards the whole
/k surface (and therefore the agent questions this page surfaces) uniformly.
Run locally (dev)
No configuration is needed to see it work. The repository ships a small demo
corpus at examples/seed-kb/ — notes about this engine itself — and that is the
default KNOWLEDGE_DIR, so a fresh clone answers questions out of the box:
uv sync --extra dev
uv run pytest -q # full suite, no network, no API key
uv run python serve.py --help
MCP_TRANSPORT=stdio uv run python serve.py # MCP server over the seed corpusOnly the paths that call an LLM (--selftest, the watchers, synthesize.py)
need OPENROUTER_API_KEY; query_knowledge over the seed corpus does not.
cp .env.example .env # OPENROUTER_API_KEY and the messaging transport
uv run python watch.py --dry-run # mentors: prints would-be entries
uv run python research.py --dry-run # research: prints sifted entries
uv run python research.py --seed # populate research_state.json only
uv run python synthesize.py --dry-run # synthesis: prints would-be topic pages
uv run python synthesize.py --backfill # (re)build all topic pages from inbox
uv run python serve.py --selftest "<question>" # run research once, print JSON, exit
MCP_TRANSPORT=stdio uv run python serve.py # MCP server over stdio (local Claude Code)
uv run python serve.py # MCP server over httpStream (deploy mode)watch.py / research.py support --dry-run (no write/DM/persist) and
--seed (populate state, no sift). synthesize.py supports --dry-run,
--backfill (reprocess the whole inbox) and --rerank-all. A --dry-run
still calls the LLM, so it needs a real OPENROUTER_API_KEY; --seed does not.
Deploy
The engine is a plain Docker image plus a scheduler. There are four entrypoints:
three one-shot cron jobs (watch.py, research.py, promote.py, each followed
by synthesize.py) and one long-lived server (serve.py).
Deployment is deliberately not vendored into this repository — a compose file or a Kubernetes manifest encodes one site's hostnames, mounts and routing, and is misleading anywhere else. See docs/deploying.md for the shape the engine needs: the two volumes, the environment, the scheduling order, and the auth surface you must put in front of it.
The one ordering rule that is not obvious: synthesize.py runs last in every
chain, after the watchers have appended to inbox/, so a single commit
captures the raw entries and the regenerated topic pages, and the digest fires
once per chain rather than once per watcher.
promote.py — the enrichment loop. It runs between research.py and
synthesize.py in the research chain. It mines the deep-research history the
long-lived serve.py persists ($RESEARCH_HISTORY_DIR/*.json) and promotes the sources those
interactive runs discovered — never the synthesized answer — into
references/<slug>.md. Each candidate URL is re-fetched, LLM-sifted for
relevance (dropped below PROMOTE_REL_BAR, default 3) and authority-tagged, so a
low-trust page is visibly marked, never silently trusted. query.load_cards
then indexes references/ for query_knowledge / research, off the magazine
and graph. Like the watchers, promote.py never runs git (the host cron
commits) and saves its processed-id state (promote_state.json) only after a
successful pass. Demand (below) promotes high-demand / under-covered sources
first. Dry-run: python promote.py --dry-run.
Demand signal (demand.py). synthesize.py reads the query log
($QUERY_LOG_FILE) and attaches a per-card demand score — how often
recent agent queries retrieve each card (recency-weighted BM25 replay). It blends
into the rank via weights.demand in ranking.toml (0 by default, so a no-op
until tuned; an empty log is graceful). scripts/query_trends.py prints the
"Google-Trends" view: recurring question themes, frequency, coverage, and a GAP
flag for high-demand / low-coverage themes — the enrichment priority list.
Environment variables
Var | Required | Default | Notes |
| no |
| Use |
| yes | — | Same token as the whatsapp-mcp stack. |
| yes | — | Recipient id in |
| yes | — | From openrouter.ai. The only provider key — chat and Whisper both. |
| no |
| Any OpenRouter chat model. Per-seam overrides: |
| no |
| Tier-B video transcripts. OpenRouter has no |
| no | = | Model for |
| no |
| The corpus root. Unset, the read path falls back to the bundled seed corpus so a fresh clone runs; the write paths ( |
| no |
| Where the pointer DM sends you to read/search — the magazine. |
| no |
| Latest N items pulled per source per run. |
| server | — |
|
| no |
|
|
| no |
|
|
| no | — |
|
| no | — |
|
| no | — | Set |
| no |
| FlashRank cross-encoder model name. |
| no |
| BM25 candidate pool size handed to the reranker. |
| no | (lib default) | Writable dir for the downloaded rerank model; set to a bind-mount so it persists. |
| no |
| Graph link target embedded in the magazine nav. |
Adding a source
Mentor (trusted person) → append to
SOURCESinsources.py.Research feed (needs date-filter + sift) → append to
RESEARCH_SOURCESinsources_research.pywith"kind": "rss"(or"arxiv").
The name is the state-tracking key — don't rename existing sources casually
or all their items look new on the next run.
Layout
knowledge-engine/
├── watch.py # mentor entrypoint + shared helpers (state, feed, summarize)
├── research.py # research-feed entrypoint (date-filter, arXiv retry, sift)
├── synthesize.py # topic-synthesis entrypoint (inbox/ → topics/)
├── serve.py # research MCP server (4th entrypoint — long-lived)
├── query.py # research logic: BM25 search_kb (+ FlashRank rerank) + research() loop
├── rerank.py # FlashRank cross-encoder rerank seam (precision over BM25 recall)
├── graph.py # pure build_graph_payload → k/graph.json (nodes=cards, edges=links)
├── magazine.py # pure render of the static magazine (k/index.html + k/cards.json)
├── search_backends.py # pluggable web search (Brave/Tavily/ddgs) + URL fetch
├── knowledge.py # repository helpers: sift/inbox/card parsing, entry + page render
├── ranking.py # pure rank math (recency decay, weighted sum, sort)
├── topics.py # topic registry + source→authority lookup
├── vendor/ # vendored static assets (cytoscape.min.js — no CDN)
├── sources.py # mentor feeds
├── sources_research.py # research feeds + arXiv query builder
├── backfill.py # one-off historical seeder
├── prompts/ # sift_mentor.md, sift_research.md, classify.md, summarize.md, research.md
├── tests/ # pytest unit tests (pure helpers only)
├── Dockerfile / pyproject.toml / .env.example
├── examples/seed-kb/ # bundled demo corpus — the default KNOWLEDGE_DIR
├── docs/deploying.md # what a deployment needs (no site-specific manifest)
└── data/ # bind-mount target; state files live hereAbout this public export
This is the real repository with its history kept, filtered rather than squashed. Some things are deliberately absent:
The knowledge corpus itself is not published.
KNOWLEDGE_DIRnormally points at a private repository of accumulated notes. What ships instead isexamples/seed-kb/— a small demo corpus of notes about this engine — and it is the default, so a fresh clone answers questions with no configuration.No deployment manifest. A compose file or a Kubernetes manifest encodes one site's hostnames, mounts and routing and is misleading anywhere else.
docs/deploying.mddescribes the shape a deployment needs — the two volumes, the environment, the scheduling order, the auth surface you must put in front of it — without pretending to be that site.No credentials, and no host names. Secrets are referenced by variable name only;
.env.exampleis a template. Hostnames in the docs and defaults areexample.complaceholders.The messaging recipient is a configuration value (
RECIPIENT_JID), never a checked-in number.
uv sync --extra dev
uv run pytest -q # 366 tests, no network, no API key (re-derived 2026-09-17)Design notes (in case you forget)
Why entrypoints, not a framework?
watch.py/research.py/synthesize.py/serve.py/backfill.pyare plain scripts sharing helpers by import — same pattern, no harness. Frameworks earn their cost at agent #3+. See thefeedback-no-framework-before-threememory.serve.pyuses themcpSDK as a server; that is a protocol library (same SDK the watcher already uses as a WhatsApp client), not an agent framework.Why BM25, not embeddings, for
research? ~100 markdown cards of technical-entity queries is the textbook BM25-wins case; embeddings would add a vector index to rebuild on every card change for no real gain. The knowledge repo's ownPageIndexcard says the same thing.Why does synthesis classify-only, not LLM-rewrite the page? A full-page LLM rewrite is non-deterministic, untestable under the no-mock TDD rule, and can silently drop or distort cards. The LLM does one thing Python can't — semantic classification — and Python does everything mechanical. Topic pages are a reproducible render; git diffs stay clean.
Why does the watcher not touch git? It only writes files to the
/knowledgebind-mount; the host cron does the commit+push, reusing the VPS's existing SSH deploy key. No credentials inside the container.Why OpenRouter and not the Anthropic API? Andre refuses paid Anthropic API for personal work. He consolidated every provider onto OpenRouter on 2026-08-24 and closed the Groq and DeepInfra accounts — the pain was holding a separate prepaid balance with each vendor, not the cost. DeepSeek-V3 (
deepseek/deepseek-chat) still sifts well and is unchanged by the move; OpenRouter resells the same weights.Why bind-mount, not docker volume? Direct
cataccess on the VPS, and Borg picks up/storage/*automatically.Why
build_messageis still here (watch.py):backfill.py— the one-off historical seeder — still builds digest-style messages. The live paths useknowledge.format_entry+pointer_messageinstead.
License
MIT — see LICENSE.
Available Tools
2 toolsquery_knowledgeA
Search the AmiticIA knowledge base — current, dated, rank-ordered AI-agent
findings — for material relevant to question. Free and fast: keyword (BM25)
retrieval only, no web, no LLM. Use this for a quick grounded read of what
has already been accumulated; use research when you need live web evidence
and a synthesized answer.
CHECK THE SDK FIRST — this is the research-frontier layer, not the
how-to-build layer. If you are building an agentic feature, the proven 2026
pattern almost always already lives in your SDK's own docs (LangGraph via the
docs-langchain MCP; Vercel AI SDK / OpenAI / Google via Context7). Reach for
those first; this KB exists for what the SDK docs do NOT yet settle.
SCOPE — this KB covers only the fast-moving AI-engineering frontier (agents, LLMs, harnesses, evals, context engineering, agent security, AI coding tools, model capability/cost trends): the areas where your training is most likely stale. It is NOT for general/evergreen software architecture (CRM, omnichannel, helpdesk, classical patterns — answer those from your own knowledge) and NOT for library/framework/API docs (use Context7). If the question is one of those, prefer your own knowledge or Context7 over this KB.
Args: question: what to look up. topic: optional filter — one of the known topic slugs (agent-architecture, agent-harness, context-engineering, coding-agents, capability-and-cost-trends, agent-security, methodology, evals-and-benchmarks).
Returns JSON: {cards: [...], coverage: {count, newest, oldest}}.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | No | ||
| question | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure, and it succeeds: it states retrieval type (BM25), constraints (no web, no LLM), performance expectations (free and fast), result properties (current, dated, rank-ordered), and return shape (JSON with cards and coverage). This gives an agent an accurate model of what will happen when invoked.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although the description is longer than typical, it is tightly organized into purpose, usage routing, scope exclusions, and argument definitions. Every section earns its place because it prevents a specific misuse, and the core purpose is front-loaded in the first line.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the lack of annotations, and the absence of a formal output schema, the description is complete. It covers what the tool does, when to prefer alternatives, what inputs it accepts, what it returns, and what it explicitly does not cover — nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description fully compensates. It defines `question` as 'what to look up' and `topic` as an optional filter with an explicit list of known slugs. This is considerably more informative than the bare schema, which provides no descriptions and no enums.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource — 'Search the AmiticIA knowledge base' — and immediately distinguishes itself from the sibling `research` tool by naming the alternative and its different purpose. An agent can tell exactly what this tool does and what it is not for.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance ('quick grounded read of what has already been accumulated') and when-not-to-use guidance ('use `research` when you need live web evidence and a synthesized answer'). It further provides SDK-first routing and clear exclusions for general architecture questions and library/API docs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
researchA
Answer a decision question with grounded, current, cited evidence.
Runs the full loop: searches the knowledge base, runs live web searches, fetches the top pages, and synthesizes a dated answer with DeepSeek-V3. Every claim is traced to a retrieved source.
CHECK THE SDK FIRST — do NOT reach for this before checking whether the SDK
already ships the primitive you need. The default authority for "how do I
build this" is the SDK's own documented pattern (LangGraph via the
docs-langchain MCP; Vercel AI SDK / OpenAI / Google via Context7) — most
2026 agent work is a solved, established pattern. Consult this tool ONLY when
the established SDK pattern is insufficient or absent, or when you are tuning
/ choosing between mature options and need current field evidence. This KB is
biased toward the research frontier by construction; letting it drive a
decision the SDK already answers leads to reinventing wheels the SDK ships.
SCOPE — the fast-moving AI-engineering frontier (agents, LLMs, harnesses, evals, agent security, AI coding tools, model capability/cost trends), where current reality outruns your training. It is NOT for general/evergreen software architecture (CRM, omnichannel, helpdesk, classical patterns — you already know those) or library/API docs (use Context7). Sources discovered here are promoted back into the KB, so keeping questions on-frontier keeps the KB clean.
Returns JSON: {answer, citations: [{url,title,date}], confidence,
retrieval_date, kb_coverage, warnings, sources_used}. Check warnings and
confidence before trusting the answer.
| Name | Required | Description | Default |
|---|---|---|---|
| question | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so thoroughly: it discloses the internal workflow (search, fetch, synthesize with DeepSeek-V3), the side effect of promoting sources back into the KB, and the return envelope with warnings/confidence. This goes well beyond a generic 'research' label.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but deliberately structured in labeled sections (default authority, scope, returns), and each section carries actionable guidance that would be hard to compress further without losing value. It is front-loaded with the core purpose before diving into usage nuances.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with no annotations, the description covers purpose, scope, exclusions, alternatives, workflow, output fields, and trust caveats. The output schema is described as a JSON object with citations, confidence, warnings, and retrieval_date, so the agent has everything needed to invoke and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only the parameter name 'question' with 0% description coverage. The description compensates by clarifying what qualifies as a question (a decision question on the frontier) and what is out of scope, which helps the agent frame the input even though it doesn't restate the parameter format verbatim.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Answer a decision question with grounded, current, cited evidence.' It also differentiates itself from the sibling query_knowledge by explicitly describing the full loop (searches KB, live web searches, fetches pages, synthesizes) rather than a simple KB lookup.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use and when-not-to-use guidance: 'CHECK THE SDK FIRST — do NOT reach for this before checking whether the SDK already ships the primitive you need.' It names alternatives (docs-langchain MCP, Context7) and defines the scope boundary (frontier AI engineering vs general software architecture and docs).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v0.1.0- First observed
query_knowledge - First observed
research
TDQS
Scored across 2 tools
The two tools have clearly distinct purposes: query_knowledge is a quick, free BM25 retrieval from the KB, while research runs a full loop with live web searches and synthesis. The descriptions explicitly cross-reference each other, telling the agent when to use which, so there is little risk of misselection.
The tool names follow different conventions: 'query_knowledge' uses a verb_noun snake_case pattern, while 'research' is a bare verb with no object prefix. With only two tools, the inconsistency is noticeable and breaks the expected pattern established by the first name.
Two tools is on the thin side for a knowledge engine. The server's scope is reasonably narrow (quick keyword search vs. deep research), so it is not egregiously underbuilt, but it clearly sits at the borderline where a few more specialized operations (e.g., listing topics, retrieving specific cards) could make the set feel more complete.
The surface covers the core workflows of querying the KB and running grounded research, with good attention to fallback guidance (check SDK first, scope restrictions). Minor gaps exist, such as no way to list valid topic slugs or browse the KB without a question, but these are workable and do not create dead ends.
Maintenance
Related MCP Connectors
AI research grounded in 300M scientific works — every citation a verifiable DOI.
Source-traced evidence research for AI agents. We organise the evidence; you decide.
Keep AI answers fresh with free source previews, scheduled monitoring, evidence history, and alerts.
Real-time web and scholarly search with cited answers and multi-step deep research.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables autonomous research by integrating multiple free sources including web search, Wikipedia, arXiv, and Crossref, with no API keys required.MIT
- AlicenseNot gradedqualityDmaintenanceEnables citation-audited deep research with tools for web-grounded answers, source conflict detection, and per-claim citation auditing.MIT
- FlicenseNot gradedqualityBmaintenanceProvides read-only access to a personal RAG knowledge base, enabling hybrid search, evidence-grounded retrieval with citations, and knowledge gap tracking for LLM agents.-
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to create verifiable, replayable citations, search private knowledge bases, and publish Markdown with verified citation markers.MIT