hn-tech-signal-mcp
Query live tech/AI signals across HackerNews, arXiv, Lobste.rs, and GitHub through 8 read-only MCP tools.
Fetch HackerNews stories from top, best, new, Ask HN, Show HN, and YC job feeds, with score filtering.
Search all HackerNews history by keyword, tag, and date range using Algolia.
Read nested HackerNews comment threads to see actual arguments, caveats, and practitioner replies.
Get latest arXiv AI/ML papers by category, or search arXiv by keyword, title, or author.
Fetch hottest Lobste.rs stories and filter by tag for curated tech signal.
Find trending GitHub repositories by topic, stars, and recent activity.
Generate a one-call cross-source tech signal digest focused on a topic, with all four sources combined.
Use no API key by default, optionally set GITHUB_TOKEN for higher GitHub rate limits, and run via stdio or Streamable HTTP.
Provides full-text search across all HackerNews history via the Algolia API.
Fetches latest arXiv papers by category and supports keyword/title/author search.
Retrieves HackerNews top, best, and new stories using the Firebase API.
Searches trending AI repositories and other repos on GitHub by topic, stars, and activity.
Gets hottest Lobste.rs stories, with optional tag filtering.
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., "@hn-tech-signal-mcpGive me a tech signal digest on AI today"
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.
π¨π Part of the Swiss Public Data MCP Portfolio
π‘ hn-tech-signal-mcp
MCP server for global tech & AI signal intelligence β aggregates HackerNews, arXiv, Lobste.rs and GitHub into a structured briefing. No API key required.
Demo
Overview
hn-tech-signal-mcp turns any AI assistant into a proactive tech intelligence analyst. The server aggregates four signal layers β research frontier, developer discourse, curated signal, and open-source practice β into a single, structured briefing.
No authentication required. All four data sources are public APIs. Optional: set GITHUB_TOKEN for higher GitHub rate limits (5,000 req/h vs. 60 req/h unauthenticated).
Anchor demo query: "Give me a tech signal digest on AI today β what is happening in research, developer discourse and open source?"
Related MCP server: ai-news-mcp
Signal Architecture
FRONTIER arXiv API β Latest AI/ML papers (cs.AI, cs.LG, cs.CL, cs.CV)
DISCOURSE HackerNews β Six feeds + Algolia search + comment threads
Lobste.rs β Curated, lower-noise tech signal
PRACTICE GitHub Search β What engineers are actually building right now
HN Show HN β What individuals are shipping this weekThink of the four layers as a radar: arXiv shows what's coming over the horizon, HN and Lobste.rs show what practitioners are discussing, and GitHub shows what teams are actually shipping.
Within the discourse layer there are two levels of depth. The feeds and the search tell you what is being discussed; hn_discussion tells you what is actually being argued β the counter-arguments and the "we tried this in production" replies that carry the real signal.
Features
π¬ Research frontier β Latest arXiv papers by category (cs.AI, cs.LG, cs.CL, and more)
π arXiv full-text search β Find papers by keyword, title, or author
π£οΈ HackerNews feeds β top, best, new, Ask HN, Show HN and YC job posts
π HackerNews search β Full history via Algolia, with date range filter
π¬ HackerNews comment threads β Read the actual discussion under a story, nested, with a bounded fetch budget
π§ Lobste.rs hottest β Curated developer signal, filterable by tag
π οΈ GitHub trending AI repos β Search by topic, stars, sort by activity or popularity
π Tech signal digest β One-call cross-source briefing in Markdown
βοΈ Dual transport β stdio for Claude Desktop, Streamable HTTP for cloud deployment
# | Tool | Source | Description |
1 |
| HackerNews | Six feeds: top/best/new/ask/show/job, with score filter |
2 |
| HN Algolia | Full-text search across all HN history |
3 |
| HackerNews | Nested comment thread under a story |
4 |
| arXiv | Latest papers by category (cs.AI etc.) |
5 |
| arXiv | Search papers by keyword/title/author |
6 |
| Lobste.rs | Curated tech stories, filterable by tag |
7 |
| GitHub | Trending AI repos by topic and stars |
8 |
| All sources | Aggregated Markdown briefing |
HackerNews feeds
Feed | Content | Upstream size |
| Front page as ranked right now | 500 items |
| Highest-voted recent stories | 200 items |
| Newest submissions, unfiltered | 500 items |
| Ask HN β what practitioners are stuck on | ~30 items |
| Show HN β what people are shipping | 200 items |
| YC portfolio job posts ( | ~30 items |
ask and job are short feeds upstream, so a large limit may return fewer stories than requested.
Prerequisites
Python 3.11+
uvorpipNo API key required
Optional:
GITHUB_TOKENfor higher GitHub rate limits
Installation
# Recommended: uvx (no install step needed)
uvx hn-tech-signal-mcp
# Alternative: pip
pip install hn-tech-signal-mcpQuickstart
# Start the server (stdio mode for Claude Desktop)
uvx hn-tech-signal-mcp
# With optional GitHub token for higher rate limits
GITHUB_TOKEN=ghp_yourtoken uvx hn-tech-signal-mcpTry immediately in Claude Desktop:
"Give me a tech signal digest on AI today" "What are the latest cs.AI papers from the last 48 hours?" "What is HackerNews discussing about MCP this week?" "Show me trending GitHub repos for the topic 'ai-agents'"
Configuration
Environment Variables
Variable | Default | Description |
| β | Optional. GitHub personal access token. Without it: 60 req/h. With it: 5,000 req/h. The token is only sent to |
|
| Transport: |
|
| Bind host for HTTP transport. Non-loopback values require |
|
| Port for HTTP transport |
| β | Required when |
Claude Desktop Configuration
{
"mcpServers": {
"hn-tech-signal": {
"command": "uvx",
"args": ["hn-tech-signal-mcp"],
"env": {
"GITHUB_TOKEN": "ghp_yourtoken_optional"
}
}
}
}Config file locations:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
After restarting Claude Desktop, all 7 tools are available.
Cloud Deployment (Streamable HTTP)
For use via claude.ai in the browser (e.g. on managed workstations):
Render.com (recommended):
Push/fork the repository to GitHub
On render.com: New Web Service β connect GitHub repo
Optionally set
GITHUB_TOKENin the Render dashboardIn claude.ai under Settings β MCP Servers, add:
https://your-app.onrender.com/mcp
# Local HTTP mode (binds 127.0.0.1 by default)
MCP_TRANSPORT=streamable_http MCP_PORT=8000 python -m hn_tech_signal_mcp.server
# Public bind (requires bearer token, intended behind a reverse proxy that terminates TLS)
MCP_TRANSPORT=streamable_http \
MCP_HOST=0.0.0.0 \
MCP_BEARER_TOKEN="$(openssl rand -hex 32)" \
python -m hn_tech_signal_mcp.serverHardening: the server refuses to bind to non-loopback hosts unless
MCP_BEARER_TOKENis set. Run cloud deployments behind a TLS-terminating reverse proxy (Render, Fly, Caddy, β¦) and treatMCP_BEARER_TOKENas the shared client secret your proxy enforces.
Architecture
βββββββββββββββββββ βββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββ
β Claude / AI ββββββΆβ HN Tech Signal MCP ββββββΆβ HackerNews Firebase β
β (MCP Host) βββββββ (MCP Server) ββββββΆβ HN Algolia Search β
βββββββββββββββββββ β ββββββΆβ arXiv.org (Atom API) β
β 8 Tools ββββββΆβ Lobste.rs JSON API β
β Stdio | Streamable HTTP ββββββΆβ GitHub Search API β
βββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββArchitecture decision
This server uses Architecture A (live API only, two paths per source). There is no bulk dump to fall back on.
Rationale (verified live on 2026-07-28 against the official HackerNews API):
All six feed endpoints (
{top,best,new,ask,show,job}stories.json) answer HTTP 200 with 29β500 IDs. No auth, no rate-limit headers,Cache-Control: no-cache.HackerNews publishes no bulk export, so caching is entirely this server's responsibility. TTLs live in
CACHE_TTL.The Firebase API has no search. Historical and full-text queries go through the Algolia index instead β that is the second path, used by
hn_search.item/<id>.jsonis one request per item. Feeds and comment threads therefore fan out, which is why both are bounded (HN_MAX_CONCURRENCY,max_comments).
Consequences:
Every upstream call retries with exponential backoff (2s / 4s / 8s) on network errors, 5xx and 429. Other 4xx fail fast.
One process-wide pooled
httpx.AsyncClient, closed via the FastMCP lifespan.Unknown item IDs return HTTP 200 with a
nullbody rather than a 404 βhn_discussiontranslates that into an explicit "no item found" message.
Project Structure
hn-tech-signal-mcp/
βββ src/
β βββ hn_tech_signal_mcp/
β βββ __init__.py
β βββ server.py # All 8 tools
β βββ outputs.py # outputSchema models, one per tool
βββ tests/
β βββ __init__.py
β βββ test_server.py # 64 unit + 12 live tests
βββ pyproject.toml
βββ CHANGELOG.md
βββ CONTRIBUTING.md
βββ LICENSE
βββ README.md # This file (English)
βββ README.de.md # German versionMCP Protocol Version
This server speaks two protocol eras over the same endpoint. The client's first request on a connection decides which one applies; a later claim from the other era is refused.
Era | Revision | Who reaches it |
|
| What today's clients speak. The server answers with the revision asked for, or with the |
Per-request envelope |
| A request carrying the |
Both revisions are pinned in
tests/test_protocol_version.py and asserted
against the installed SDK, so a Dependabot bump of mcp cannot move either one
silently.
Spoken, not just named: tests/test_modern_era.py
actually connects β in-process in every era (2026-07-28, auto, legacy) and
over HTTP with single POSTs, no initialize and no Mcp-Session-Id, against
the ASGI app that MCP_TRANSPORT=streamable_http starts. It checks
server/discover, tools/list with its freshness hint, tools/call with the
Mcp-Name header, the serverInfo stamp in the _meta of every response, and
that no feature deprecated by SEP-2577 (sampling, roots, logging) is involved.
Two negative controls sit alongside: a wrong Mcp-Method header is rejected
with -32020, and the same request under 2025-11-25 still requires a session.
Known, SDK-side: server/discover advertises resources and prompts
(with listChanged / subscribe) although this server registers neither β
MCPServer always installs those handlers. Switching that off would mean
touching private attributes; instead the tests assert that both lists answer
empty and without error.
Note that the SDK's LATEST_PROTOCOL_VERSION is an alias for the modern
era, not for the handshake era β pinning against it alone would leave the era
that current clients actually negotiate free to drift.
Update policy. When the gate fails, do not edit the constant blindly: read
the spec changelog between the two revisions, verify the server still behaves,
then move the constant, this section, README.de.md and
CHANGELOG.md together.
Tool Output
Every tool declares its own outputSchema (closed: additionalProperties: false) from the models in src/hn_tech_signal_mcp/outputs.py, and answers in
the three forms the spec separates:
Case |
|
|
|
Success | The JSON object, pretty-printed | The same object |
|
Source failed |
| β |
|
Unknown or wrong ID ( | A sentence saying what to pass instead | β |
|
Digest with some sources down | The JSON object, with | The same object |
|
Before, the SDK derived a schema of {"result": string} from the -> str
annotation, so structured clients got the JSON as one string field, and every
failure arrived as isError: false.
The text stays byte-for-byte what it was β clients that only read text see no change. A response that does not fit its schema is reported as a server defect naming the offending field paths (never the values) instead of reaching the client unvalidated. The recorded fixtures and the daily live run both go through this path, in both protocol eras.
Testing
# Unit tests (no network required)
PYTHONPATH=src pytest tests/ -m "not live"
# Live integration tests (requires network)
PYTHONPATH=src pytest tests/ -m "live"Example Use Cases
KI-Fachgruppe / AI Working Group
"Give me a tech signal digest on AI today"
β tech_signal_digest(focus="AI")
"What are the top 5 arXiv papers on LLM agents this week?"
β arxiv_search(query="LLM agents", category_filter="cs.AI", limit=5)
"What is HackerNews discussing about model context protocol?"
β hn_search(query="model context protocol", days_back=30)Research Monitoring
"Show me the latest NLP papers from arXiv"
β arxiv_latest(category="cs.CL", limit=10)
"Search arXiv for papers on retrieval-augmented generation"
β arxiv_search(query="retrieval augmented generation RAG", limit=10)Open Source Intelligence
"What AI agent frameworks are trending on GitHub?"
β github_trending_ai(topic="ai-agents", sort="updated", limit=10)
"Show me the most starred MCP-related repos"
β github_trending_ai(topic="mcp", sort="stars", min_stars=50)
[β More use cases by audience β](EXAMPLES.md)arXiv Category Reference
Category | Full Name | Key Topics |
| Artificial Intelligence | Agents, planning, knowledge representation |
| Machine Learning | Training, optimisation, generalisation |
| Computation & Language | NLP, LLMs, translation, summarisation |
| Computer Vision | Image recognition, generation, multimodal |
| Robotics | Embodied AI, navigation |
| Statistics ML | Probabilistic methods, Bayesian ML |
Rate Limits
Source | Auth Required | Limit |
HackerNews Firebase | No | Very generous (Firebase) |
HN Algolia Search | No | ~10,000 req/hour |
arXiv | No | ~3 req/second (be respectful) |
Lobste.rs | No | Reasonable use |
GitHub Search | No | 60 req/hour |
GitHub Search |
| 5,000 req/hour |
Known Limitations
GitHub rate limit: 60 req/h without token. Set
GITHUB_TOKENfor production use.arXiv: Papers may take up to 24h to appear after submission. Weekends/holidays have delayed batches.
HackerNews: Top/best story lists update every few minutes. Very new stories may have low scores.
HackerNews
ask/jobfeeds: Only ~30 items exist upstream, so a largelimitreturns fewer stories than requested. Job posts carrytype: "job", no comment count, and a score of 1.hn_discussionis always a sample, never the full thread: one request per comment upstream means popular stories (900+ comments) cannot be fetched whole. The budget is split across nesting levels and spread round-robin across sibling threads, so you get a representative cross-section rather than one exhaustively-read sub-thread. Check thetruncatedflag.hn_discussioncomment text is plain text, not HTML: HN's markup is stripped for readability. Do not re-render the output as HTML β the conversion is not a sanitiser.Lobste.rs: Smaller community than HN; tech-focused but may not cover all AI topics.
tech_signal_digest: Makes ~4 concurrent requests; if one source is slow it may delay the full response.
Synergies with Other MCP Servers
hn-tech-signal-mcp combines well with:
Combination | Use Case |
| Global research + Swiss institutional media coverage |
| Tech discourse + Swiss regulatory context |
| AI research trends + education policy data |
| Tech landscape + Swiss economic/structural data |
Safety & Limits
Read-only: All tools perform HTTP GET requests only β no posts, comments, votes, or writes are issued upstream.
No personal data: The server queries public tech aggregators. No PII is collected; author handles in public posts/papers are returned as-is from upstream and never enriched or cross-referenced.
Rate limits: See the Rate Limits table. arXiv's β€3 req/sec guidance is respected by default; GitHub search is capped at 60 req/h without a
GITHUB_TOKEN. A request timeout is enforced per call.No bulk harvesting: This server is built for interactive, conversational use β not for scraping or mirroring. Do not use it to bypass upstream pagination or ToS limits.
Terms of service: Data is subject to the ToS of each source β HackerNews, arXiv API, Lobste.rs, GitHub.
No guarantees: Community project, not affiliated with HackerNews / Y Combinator, arXiv / Cornell, Lobste.rs, or GitHub. Availability depends on upstream APIs.
Changelog
See CHANGELOG.md
Contributing
See CONTRIBUTING.md (Deutsch).
Security
See SECURITY.md (Deutsch) for the security posture and how to report a vulnerability.
License
MIT License β see LICENSE
Author
Hayal Oezkan Β· malkreide
Credits & Related Projects
HackerNews API: hacker-news.firebaseio.com β Y Combinator / Firebase
arXiv API: export.arxiv.org β Cornell University / arXiv.org
Lobste.rs API: lobste.rs β community-run
GitHub API: api.github.com β GitHub / Microsoft
Protocol: Model Context Protocol β Anthropic / Linux Foundation
Related: news-monitor-mcp β Swiss institutional media monitoring
Portfolio: Swiss Public Data MCP Portfolio
Installation
Run via uv's uvx β no clone or manual install needed. Add to your MCP client config (mcpServers for Claude Desktop, Cursor and Windsurf; use a top-level servers key for VS Code in .vscode/mcp.json):
{
"mcpServers": {
"hn-tech-signal-mcp": {
"command": "uvx",
"args": [
"hn-tech-signal-mcp"
]
}
}
}Available Tools
8 toolsarxiv_latestARead-onlyIdempotent
Fetch the most recently submitted papers from arXiv AI/ML categories.
Papers appear hours before press coverage β the fastest signal of what is happening at the AI research frontier.
Categories: cs.AI (Artificial Intelligence), cs.LG (Machine Learning), cs.CL (NLP), cs.CV (Computer Vision), cs.NE (Neural Computing), stat.ML.
All categories are fetched in ONE query (cat:A OR cat:B OR β¦), so the
result is the most recent papers across the requested categories, grouped
per category. A paper cross-listed to several requested categories appears
under each of them, matching how a single-category query would return it.
Because one window is shared, a small category (cs.NE, stat.ML) can come
back with fewer than limit papers even though arXiv holds more. Those
categories are named in incomplete_categories β query a single category
for full depth.
Args: params (ArxivLatestInput): - categories (List[str]): arXiv category codes - limit (int): Max papers per category (1β20)
Returns: str: JSON with categories, total_papers, distinct_papers, by_category dict, and β only when a category fell short β incomplete_categories plus an explanatory note. Each paper: id, title, abstract (400 chars), authors, published, category (primary), categories (all), url, pdf.
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| categories | Yes | |
| fetched_at | Yes | |
| by_category | Yes | |
| total_papers | Yes | |
| distinct_papers | Yes | |
| incomplete_categories | No | Present only when a category returned fewer than limit within the window |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld/non-destructive, but the description adds substantial non-obvious behavior: all categories are fetched in ONE OR query, cross-listed papers repeat under each category, a shared window can truncate small categories, and truncation surfaces via incomplete_categories. These are exactly the quirks that would otherwise cause misreads of results.
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?
Front-loaded with the core action, then edge-case behavior, then args/returns. Efficient overall, though the 'Returns' block largely restates the output schema and slightly inflates length against a description that already exists alongside structured return data.
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 a read-only fetch tool with an output schema present, the description covers everything an agent needs: what is returned, the per-category grouping, the truncation edge case, and the remedy. No meaningful gap remains.
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 reported as 0%, so the description must carry parameter meaning. It spells out the valid category codes with human-readable names and clarifies that limit is per-category (1β20) rather than a global cap β a distinction the schema alone does not make.
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?
States a specific verb (Fetch), resource (recently submitted arXiv papers), and scope (AI/ML categories, latest submissions). 'Most recently submitted' naturally distinguishes it from the sibling arxiv_search, which is a keyword search tool, without needing to name it.
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?
Gives clear usage context ('Papers appear hours before press coverage β the fastest signal') and an explicit operational rule: query a single category for full depth when a category is truncated. It stops short of naming arxiv_search as the alternative for topical lookups, so the when-not-to-use case is left implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
arxiv_searchARead-onlyIdempotent
Search arXiv for papers matching a query, sorted by submission date.
Searches title, abstract and author fields. Optionally restrict to a specific AI/ML category.
Args: params (ArxivSearchInput): - query (str): Search terms (e.g. 'LLM agents tool use') - category (Optional[str]): arXiv category filter - limit (int): Papers to return (1β20)
Returns: str: JSON with query, category, count, papers[]. Each paper: id, title, abstract, authors, published, url, pdf.
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| query | Yes | |
| papers | Yes | |
| category | Yes | |
| fetched_at | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds useful behavioral context: it searches title, abstract, and author fields, sorts by submission date, and returns a structured JSON payload. It does not mention rate limits or auth, but those are minor omissions given the annotation coverage.
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 front-loaded with the core purpose, then systematically lists Args and Returns. It is appropriately sized for a search tool, though the explicit Returns section is somewhat redundant because an output schema exists.
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 simple search tool with annotations covering safety and an output schema present, the description is largely complete: it covers field scope, sorting, category filtering, and return shape. The main gap is guidance against the sibling arxiv_latest, which would help avoid misselection.
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?
Given the reported 0% schema description coverage, the description compensates by enumerating query, category, and limit, including examples for query and category and the 1β20 range for limit. It does not explain category syntax beyond the example, but it adds enough meaning for correct invocation.
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: search arXiv for papers matching a query, with sorting by submission date and field coverage. It is clear enough to distinguish from generic search tools, but it does not explicitly contrast with the sibling arxiv_latest, which is the closest alternative.
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 only explains that category filtering is optional and does not say when to use this tool instead of arxiv_latest or other search siblings. Usage context is implied by the purpose but not guided, so it falls short of explicit when-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
github_trending_aiARead-onlyIdempotent
Search GitHub for trending repositories by topic.
A surge of starred repos on a topic is a strong adoption signal. No auth required (60 req/h). Set GITHUB_TOKEN for 5,000 req/h.
Args: params (GithubTrendingAiInput): - topic (str): GitHub topic tag (e.g. 'llm', 'mcp', 'ai-agents') - limit (int): Repos to return (1β15) - min_stars (int): Minimum stars filter - sort (str): 'stars' or 'updated'
Returns: str: JSON with topic, total_found, count, repos[]. Each repo: name, description, stars, forks, language, topics, updated_at, url.
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| sort | Yes | |
| count | Yes | |
| repos | Yes | |
| topic | Yes | |
| fetched_at | Yes | |
| total_found | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld, and non-destructive, so the safety profile is covered. The description adds genuinely useful behavioral context beyond the annotations: unauthenticated rate limit (60 req/h) versus 5,000 req/h with GITHUB_TOKEN, plus the JSON return shape.
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?
Front-loaded with the purpose sentence and the adoption-signal rationale before the arg/return listings. The structured Args/Returns block is scannable, though it partially duplicates what the schema already encodes.
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 read-only search tool with an output schema, the description covers auth/rate limits, all parameters, and the return shape, so an agent has what it needs. Minor gaps: no pagination or result-truncation behavior notes, and no explicit routing against the other signal-source siblings.
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?
Reported schema description coverage is 0%, so the description carries the parameter burden and does so reasonably: it names all four params, gives the limit range (1β15), the sort values ('stars'/'updated'), and concrete topic examples. It doesn't add much beyond the values already in the schema, but it does compensate for the stated coverage gap.
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?
States a specific verb (Search) and resource (GitHub trending repositories) with the scoping dimension (by topic). The GitHub source inherently distinguishes it from the hn_* / arxiv_* / lobsters_* siblings, though it never explicitly frames that contrast.
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?
'A surge of starred repos on a topic is a strong adoption signal' explains why one would want this data, which implies usage, but there is no explicit when-to-use-this-vs-alternatives guidance and no mention of the sibling tools that surface other signal types.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hn_discussionARead-onlyIdempotent
Read the comment thread under a HackerNews story.
Where hn_top_stories and hn_search tell you what is being discussed, this tells you what is actually being argued β the counter-arguments, the practitioner caveats, the "we tried this in production" replies that carry the real signal. Algolia can search comment text but does not return thread structure, so this is the only way to see who replied to whom.
Get a story_id from hn_top_stories or hn_search first.
Comments are walked breadth-first, so the highest-ranked top-level comments come back first. Deleted and flagged comments are skipped. Popular threads run to several hundred comments and each one costs a request upstream, so both depth and total count are capped β check the 'truncated' flag to see whether the thread was cut short.
Args: params (HnDiscussionInput): - story_id (int): HackerNews item ID - max_depth (int): Reply nesting levels (1β4, default 2) - max_comments (int): Total comment budget (1β100, default 25) - text_chars (int): Per-comment text truncation (100β2000)
Returns: str: JSON with story{}, total_comments (as reported by HN), fetched_comments, truncated, comments[]. Each comment: id, by, posted, text, reply_count, replies[] (same shape, nested).
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| story | Yes | |
| comments | Yes | |
| truncated | Yes | True if depth or comment budget cut the thread short |
| fetched_at | Yes | |
| story_text | Yes | |
| total_comments | Yes | As reported by HN |
| fetched_comments | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false. The description adds meaningful behavioral context beyond those hints: breadth-first comment walking, skipped deleted/flagged comments, upstream request cost, and capped depth/comment count with a 'truncated' flag to detect cut-off threads.
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 front-loaded with purpose, sibling differentiation, and prerequisites before moving into behavior and arguments. It is somewhat verbose, and the Returns section partially duplicates the available output schema, but most sentences earn their place.
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 read-only, idempotent thread-fetching tool with an output schema, the description supplies all needed context: prerequisite source for story_id, traversal behavior, truncation semantics, and parameter limits. Nothing critical is missing for correct invocation.
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 reported as 0%, so the description carries the burden and does so by listing story_id, max_depth, max_comments, and text_chars with ranges, defaults, and practical meaning. It adds source guidance for story_id and interpretation for max_depth, though some details mirror the schema's own property descriptions.
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: read the comment thread under a HackerNews story. It explicitly distinguishes this tool from hn_top_stories and hn_search, and explains that it returns thread structure rather than just comment text.
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?
It gives explicit prerequisites ('Get a story_id from hn_top_stories or hn_search first') and contrasts the tool with siblings by saying Algolia can search comment text but does not return thread structure. The agent knows exactly when this tool is the right choice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hn_searchARead-onlyIdempotent
Search HackerNews by keyword using the Algolia search API.
Covers all historical HN content. Find discussions on specific technologies, papers, companies, or events.
Args: params (HnSearchInput): - query (str): Search terms - limit (int): Results (1β20) - days_back (int): Recency window in days - tags (Optional[str]): 'story', 'ask_hn', 'show_hn', or empty
Returns: str: JSON with query, total_found, count, hits[]. Each hit: id, title, url, score, comments, author, posted, hn_link, excerpt.
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| hits | Yes | |
| count | Yes | |
| query | Yes | |
| days_back | Yes | |
| total_found | Yes | Hits Algolia reports in total, not just returned |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, non-destructive, and openWorldHint, so the safety profile is covered. The description adds useful context β Algolia backend and full historical coverage β but says nothing about rate limits, auth, pagination, or result-ordering behavior beyond the schema.
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?
Front-loaded purpose sentence followed by scope and an Args/Returns breakdown β well structured with no filler. The Returns block is somewhat redundant given an output schema exists, which is the only wasted content.
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 search tool with annotations covering safety and an output schema covering returns, the description supplies purpose, scope, and parameter meaning. The only shortfall is the absence of sibling routing guidance among the seven related tools.
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 reported schema description coverage is 0%, so the description carries the param burden and does so adequately: it enumerates query, limit (1β20), days_back (recency window), and tags ('story', 'ask_hn', 'show_hn', or empty). This largely mirrors the schema constraints but adds the tags value list and the notion of an empty tag meaning 'all', giving it slight edge over the raw schema.
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?
States a specific verb and resource ('Search HackerNews by keyword') plus the backend ('Algolia search API') and scope ('Covers all historical HN content'). It is clearly a full-text keyword search, but it never names or contrasts with the sibling hn_top_stories or hn_discussion, so differentiation relies on the reader's inference.
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?
'Find discussions on specific technologies, papers, companies, or events' implies the right use case, and 'all historical content' implies it is not a recency-browse tool. However, there is no explicit when-not guidance or reference to the sibling tools (hn_top_stories, hn_discussion) that an agent would need to choose correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hn_top_storiesARead-onlyIdempotent
Fetch stories from any of the six HackerNews front-page feeds.
Feeds: 'top' (frontpage), 'best' (highest voted), 'new' (latest), 'ask' (Ask HN β questions to the community), 'show' (Show HN β projects people are shipping), 'job' (YC company job posts).
'show' is the strongest signal for what practitioners are actually building; 'ask' for what they are stuck on. Upstream, 'ask' and 'job' hold only ~30 items, so a large limit may return fewer results.
Args: params (HnTopStoriesInput): - feed (str): 'top', 'best', 'new', 'ask', 'show', or 'job' - limit (int): Stories to return (1β30) - min_score (int): Minimum score filter (job posts score 1)
Returns: str: JSON with feed, count, stories[]. Each story: id, type, title, url, score, comments, by, posted, hn_link.
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| feed | Yes | |
| count | Yes | |
| stories | Yes | |
| fetched_at | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/non-destructive/openWorld, so the bar is lower. The description adds a valuable upstream constraint not in annotations: 'ask' and 'job' hold only ~30 items so large limits may under-deliver. Doesn't mention rate limits or pagination, but the feed-shortness caveat is meaningful beyond what structured fields provide.
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?
Front-loaded purpose, feed glossary, and return shape are clearly structured. The practitioner-signal commentary and Args/Returns sections are useful, though the Args section restates schema content and slightly inflates length.
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?
An output schema exists, so return-shape detail is redundant but harmless. Feed semantics and the upstream
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 description coverage is 0% per context signals, partly because the params are nested under a $ref. The schema does contain descriptions for feed/limit/min_score, so the description largely duplicates them. It adds one extra bit ('job posts score 1') but does not add meaning beyond the schema's own wording.
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?
States a specific verb+resource ('Fetch stories from any of the six HackerNews front-page feeds') and enumerates each feed with its meaning. Distinguished from siblings like hn_search and hn_discussion by being the feed lister.
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?
Explicitly signals when each feed is most useful ('show' is strongest for what practitioners build, 'ask' for what they're stuck on), giving selection guidance. However, no explicit comparison to hn_search or hn_discussion for when to use this over those alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lobsters_hotARead-onlyIdempotent
Fetch the hottest stories from Lobste.rs, a curated tech community.
Lobste.rs is smaller and more technically focused than HackerNews. Invitation-only membership ensures higher signal-to-noise ratio.
Args: params (LobstersHotInput): - limit (int): Stories to return (1β25) - tag_filter (Optional[str]): Tag substring filter (e.g. 'ai', 'ml')
Returns: str: JSON with count, stories[]. Each story: title, url, score, comments, tags, submitter, submitted_at, lobsters_url.
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| stories | Yes | |
| fetched_at | Yes | |
| tag_filter | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld and non-destructive, so the safety profile is covered. The description adds the source's curation model and a return-shape sketch, but stays silent on pagination, rate limits, or freshness/recency behavior of 'hot' stories.
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?
Front-loads purpose, then compact Args/Returns blocks. The Lobste.rs-vs-HackerNews flavor earns its place by aiding tool selection, though 'higher signal-to-noise ratio' is closer to marketing than operational guidance.
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?
An output schema exists, annotations cover the safety profile, and the description still summarizes the return payload (count, story fields). For a single-parameter read tool this is nearly complete; only pagination/freshness semantics are 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?
With reported schema coverage at 0%, the description carries the burden and does document both fields: limit as a 1β25 range and tag_filter as a tag substring filter with examples. It adds the 'substring' nuance, though it doesn't clarify whether multiple tags are accepted or how the filter interacts with the hot ranking.
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?
States a specific verb (Fetch) and resource (hottest stories) with an explicit source (Lobste.rs), then contrasts it against HackerNews, which maps directly to the sibling hn_top_stories. An agent can choose between the two without opening either schema.
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 comparative framing ('smaller and more technically focused', 'invitation-only β¦ higher signal-to-noise') implies when this source is preferable, but there is no explicit when-to-use, when-not-to-use, or named alternative. Usage must be inferred from the source characterization.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tech_signal_digestARead-onlyIdempotent
Aggregate tech & AI signals from all four sources in one call.
The primary tool for a comprehensive daily or weekly tech intelligence briefing. Combines HackerNews, arXiv, Lobste.rs and GitHub into one structured JSON digest. Use 'focus' to filter for a specific topic.
Args: params (TechSignalDigestInput): - focus (Optional[str]): Topic filter (e.g. 'MCP', 'agents') - hn_limit (int): HN stories (1β10) - arxiv_limit (int): arXiv papers (1β10) - lobsters_limit (int): Lobste.rs stories (1β10) - github_limit (int): GitHub repos (1β10)
Returns: str: JSON digest with generated_at, focus, degraded_sources[] and sources{hn, arxiv, lobsters, github}. Each source has label, count and its items list.
A source that could not be reached still appears, with count 0
and an 'error' describing why, and its key is listed in
degraded_sources β treat its absence as unknown, not as zero.
The GitHub section may carry 'incomplete_topics' when part of
the topic sweep failed. Only if all four sources fail does this
tool return a plain error string instead of JSON.
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| focus | Yes | |
| sources | Yes | |
| generated_at | Yes | |
| degraded_sources | Yes | Sources that failed; treat their sections as unknown, not as empty |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/open-world, so the description correctly spends its budget on non-obvious behavior: partial-failure semantics (a failed source still appears with count 0, an error, and a degraded_sources entry, which must be read as 'unknown, not zero'), incomplete_topics on the GitHub sweep, and the all-fail plain-error-string case. This is exactly the failure-mode context annotations cannot convey.
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 summary and routing sentence are front-loaded and zero-waste. The Args/Returns blocks are longer than strictly necessary, but each line (ranges, degraded-source handling) carries actionable information rather than filler.
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?
Even though an output schema exists, the description adds the degraded_sources and incomplete_topics contract that an agent needs to interpret the JSON correctly, plus the fallback error-string case. Nothing needed to call or interpret this tool correctly appears to be 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 description coverage is reported as 0%, so the description must carry the parameter burden: it lists focus (with examples 'MCP', 'agents'), and all four limit params with their 1β10 bounds. It omits the default of 5 and does not clarify the empty-string focus case, so it compensates well but not completely.
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 first line states a specific verb+resource ('aggregate tech & AI signals from all four sources') and names the four sources explicitly. It clearly distinguishes itself from siblings like hn_top_stories or arxiv_latest, which cover only one source each.
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?
It states the primary use case ('daily or weekly tech intelligence briefing') and the focus-filter variant, which implicitly steers narrow single-source needs to the hn_/arxiv_/lobsters_/github_ siblings. It never says when NOT to use it or names a sibling explicitly, so a 4 rather than 5.
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.
8 tool updates
v0.5.0- Changed
arxiv_latest12 fields changed- added
Output schema / $defsAdded value: +{ + "ArxivPaper": { + "additionalProperties": false, + "properties": { + "abstract": { + "description": "First 400 characters", + "title": "Abstract", + "type": "string" + }, + "authors": { + "description": "First five", + "items": { + "type": "string" + }, + "title": "Authors", + "type": "array" + }, + "categories": { + "description": "All categories, primary included", + "items": { + "type": "string" + }, + "title": "Categories", + "type": "array" + }, + "category": { + "description": "Primary category", + "title": "Category", + "type": "string" + }, + "id": { + "title": "Id", + "type": "string" + }, + "pdf": { + "title": "Pdf", + "type": "string" + }, + "published": { + "title": "Published", + "type": "string" + }, + "title": { + "title": "Title", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + } + }, + "required": [ + "id", + "title", + "abstract", + "authors", + "published", + "category", + "categories", + "url", + "pdf" + ], + "title": "ArxivPaper", + "type": "object" + } +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / properties / by_categoryAdded value: +{ + "additionalProperties": { + "items": { + "$ref": "#/$defs/ArxivPaper" + }, + "type": "array" + }, + "title": "By Category", + "type": "object" +} - added
Output schema / properties / categoriesAdded value: +{ + "items": { + "type": "string" + }, + "title": "Categories", + "type": "array" +} - added
Output schema / properties / distinct_papersAdded value: +{ + "title": "Distinct Papers", + "type": "integer" +} - added
Output schema / properties / fetched_atAdded value: +{ + "title": "Fetched At", + "type": "string" +} - added
Output schema / properties / incomplete_categoriesAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Present only when a category returned fewer than limit within the window", + "title": "Incomplete Categories" +} - added
Output schema / properties / noteAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Note" +} - removed
Output schema / properties / resultRemoved value: -{ - "title": "Result", - "type": "string" -} - added
Output schema / properties / total_papersAdded value: +{ + "title": "Total Papers", + "type": "integer" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "fetched_at", + "categories", + "total_papers", + "distinct_papers", + "by_category" +] - changed
Output schema / titlePrevious value: -"arxiv_latestOutput"New value: +"ArxivLatestOutput"
- Changed
arxiv_search10 fields changed- added
Output schema / $defsAdded value: +{ + "ArxivPaper": { + "additionalProperties": false, + "properties": { + "abstract": { + "description": "First 400 characters", + "title": "Abstract", + "type": "string" + }, + "authors": { + "description": "First five", + "items": { + "type": "string" + }, + "title": "Authors", + "type": "array" + }, + "categories": { + "description": "All categories, primary included", + "items": { + "type": "string" + }, + "title": "Categories", + "type": "array" + }, + "category": { + "description": "Primary category", + "title": "Category", + "type": "string" + }, + "id": { + "title": "Id", + "type": "string" + }, + "pdf": { + "title": "Pdf", + "type": "string" + }, + "published": { + "title": "Published", + "type": "string" + }, + "title": { + "title": "Title", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + } + }, + "required": [ + "id", + "title", + "abstract", + "authors", + "published", + "category", + "categories", + "url", + "pdf" + ], + "title": "ArxivPaper", + "type": "object" + } +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / properties / categoryAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Category" +} - added
Output schema / properties / countAdded value: +{ + "title": "Count", + "type": "integer" +} - added
Output schema / properties / fetched_atAdded value: +{ + "title": "Fetched At", + "type": "string" +} - added
Output schema / properties / papersAdded value: +{ + "items": { + "$ref": "#/$defs/ArxivPaper" + }, + "title": "Papers", + "type": "array" +} - added
Output schema / properties / queryAdded value: +{ + "title": "Query", + "type": "string" +} - removed
Output schema / properties / resultRemoved value: -{ - "title": "Result", - "type": "string" -} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "query", + "category", + "fetched_at", + "count", + "papers" +] - changed
Output schema / titlePrevious value: -"arxiv_searchOutput"New value: +"ArxivSearchOutput"
- Changed
github_trending_ai11 fields changed- added
Output schema / $defsAdded value: +{ + "GithubRepo": { + "additionalProperties": false, + "properties": { + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "forks": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Forks" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Language" + }, + "name": { + "title": "Name", + "type": "string" + }, + "stars": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Stars" + }, + "topics": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Topics" + }, + "updated_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Updated At" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + } + }, + "required": [ + "name", + "description", + "stars", + "forks", + "language", + "topics", + "updated_at", + "url" + ], + "title": "GithubRepo", + "type": "object" + } +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / properties / countAdded value: +{ + "title": "Count", + "type": "integer" +} - added
Output schema / properties / fetched_atAdded value: +{ + "title": "Fetched At", + "type": "string" +} - added
Output schema / properties / reposAdded value: +{ + "items": { + "$ref": "#/$defs/GithubRepo" + }, + "title": "Repos", + "type": "array" +} - removed
Output schema / properties / resultRemoved value: -{ - "title": "Result", - "type": "string" -} - added
Output schema / properties / sortAdded value: +{ + "title": "Sort", + "type": "string" +} - added
Output schema / properties / topicAdded value: +{ + "title": "Topic", + "type": "string" +} - added
Output schema / properties / total_foundAdded value: +{ + "title": "Total Found", + "type": "integer" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "topic", + "sort", + "fetched_at", + "total_found", + "count", + "repos" +] - changed
Output schema / titlePrevious value: -"github_trending_aiOutput"New value: +"GithubTrendingOutput"
- Changed
hn_discussion12 fields changed- added
Output schema / $defsAdded value: +{ + "HnComment": { + "additionalProperties": false, + "properties": { + "by": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "By" + }, + "id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Id" + }, + "posted": { + "title": "Posted", + "type": "string" + }, + "replies": { + "items": { + "$ref": "#/$defs/HnComment" + }, + "title": "Replies", + "type": "array" + }, + "reply_count": { + "description": "Direct replies upstream, fetched or not", + "title": "Reply Count", + "type": "integer" + }, + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "by", + "posted", + "text", + "reply_count", + "replies" + ], + "title": "HnComment", + "type": "object" + }, + "HnStory": { + "additionalProperties": false, + "properties": { + "by": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "By" + }, + "comments": { + "title": "Comments", + "type": "integer" + }, + "hn_link": { + "title": "Hn Link", + "type": "string" + }, + "id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Id" + }, + "posted": { + "description": "UTC timestamp, or 'unknown'", + "title": "Posted", + "type": "string" + }, + "score": { + "title": "Score", + "type": "integer" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "'story' or 'job'", + "title": "Type" + }, + "url": { + "description": "Linked article, or the HN item itself for Ask HN", + "title": "Url", + "type": "string" + } + }, + "required": [ + "id", + "type", + "title", + "url", + "score", + "comments", + "by", + "posted", + "hn_link" + ], + "title": "HnStory", + "type": "object" + } +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / properties / commentsAdded value: +{ + "items": { + "$ref": "#/$defs/HnComment" + }, + "title": "Comments", + "type": "array" +} - added
Output schema / properties / fetched_atAdded value: +{ + "title": "Fetched At", + "type": "string" +} - added
Output schema / properties / fetched_commentsAdded value: +{ + "title": "Fetched Comments", + "type": "integer" +} - removed
Output schema / properties / resultRemoved value: -{ - "title": "Result", - "type": "string" -} - added
Output schema / properties / storyAdded value: +{ + "$ref": "#/$defs/HnStory" +} - added
Output schema / properties / story_textAdded value: +{ + "title": "Story Text", + "type": "string" +} - added
Output schema / properties / total_commentsAdded value: +{ + "description": "As reported by HN", + "title": "Total Comments", + "type": "integer" +} - added
Output schema / properties / truncatedAdded value: +{ + "description": "True if depth or comment budget cut the thread short", + "title": "Truncated", + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "fetched_at", + "story", + "story_text", + "total_comments", + "fetched_comments", + "truncated", + "comments" +] - changed
Output schema / titlePrevious value: -"hn_discussionOutput"New value: +"HnDiscussionOutput"
- Changed
hn_search10 fields changed- added
Output schema / $defsAdded value: +{ + "HnSearchHit": { + "additionalProperties": false, + "properties": { + "author": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Author" + }, + "comments": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Comments" + }, + "excerpt": { + "title": "Excerpt", + "type": "string" + }, + "hn_link": { + "title": "Hn Link", + "type": "string" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Id" + }, + "posted": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Posted" + }, + "score": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Score" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + } + }, + "required": [ + "id", + "title", + "url", + "score", + "comments", + "author", + "posted", + "hn_link", + "excerpt" + ], + "title": "HnSearchHit", + "type": "object" + } +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / properties / countAdded value: +{ + "title": "Count", + "type": "integer" +} - added
Output schema / properties / days_backAdded value: +{ + "title": "Days Back", + "type": "integer" +} - added
Output schema / properties / hitsAdded value: +{ + "items": { + "$ref": "#/$defs/HnSearchHit" + }, + "title": "Hits", + "type": "array" +} - added
Output schema / properties / queryAdded value: +{ + "title": "Query", + "type": "string" +} - removed
Output schema / properties / resultRemoved value: -{ - "title": "Result", - "type": "string" -} - added
Output schema / properties / total_foundAdded value: +{ + "description": "Hits Algolia reports in total, not just returned", + "title": "Total Found", + "type": "integer" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "query", + "days_back", + "total_found", + "count", + "hits" +] - changed
Output schema / titlePrevious value: -"hn_searchOutput"New value: +"HnSearchOutput"
- Changed
hn_top_stories9 fields changed- added
Output schema / $defsAdded value: +{ + "HnStory": { + "additionalProperties": false, + "properties": { + "by": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "By" + }, + "comments": { + "title": "Comments", + "type": "integer" + }, + "hn_link": { + "title": "Hn Link", + "type": "string" + }, + "id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Id" + }, + "posted": { + "description": "UTC timestamp, or 'unknown'", + "title": "Posted", + "type": "string" + }, + "score": { + "title": "Score", + "type": "integer" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "'story' or 'job'", + "title": "Type" + }, + "url": { + "description": "Linked article, or the HN item itself for Ask HN", + "title": "Url", + "type": "string" + } + }, + "required": [ + "id", + "type", + "title", + "url", + "score", + "comments", + "by", + "posted", + "hn_link" + ], + "title": "HnStory", + "type": "object" + } +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / properties / countAdded value: +{ + "title": "Count", + "type": "integer" +} - added
Output schema / properties / feedAdded value: +{ + "title": "Feed", + "type": "string" +} - added
Output schema / properties / fetched_atAdded value: +{ + "title": "Fetched At", + "type": "string" +} - removed
Output schema / properties / resultRemoved value: -{ - "title": "Result", - "type": "string" -} - added
Output schema / properties / storiesAdded value: +{ + "items": { + "$ref": "#/$defs/HnStory" + }, + "title": "Stories", + "type": "array" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "feed", + "fetched_at", + "count", + "stories" +] - changed
Output schema / titlePrevious value: -"hn_top_storiesOutput"New value: +"HnTopStoriesOutput"
- Changed
lobsters_hot9 fields changed- added
Output schema / $defsAdded value: +{ + "LobstersStory": { + "additionalProperties": false, + "properties": { + "comments": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Comments" + }, + "lobsters_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Lobsters Url" + }, + "score": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Score" + }, + "submitted_at": { + "title": "Submitted At", + "type": "string" + }, + "submitter": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Submitter" + }, + "tags": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Tags" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + } + }, + "required": [ + "title", + "url", + "score", + "comments", + "tags", + "submitter", + "submitted_at", + "lobsters_url" + ], + "title": "LobstersStory", + "type": "object" + } +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / properties / countAdded value: +{ + "title": "Count", + "type": "integer" +} - added
Output schema / properties / fetched_atAdded value: +{ + "title": "Fetched At", + "type": "string" +} - removed
Output schema / properties / resultRemoved value: -{ - "title": "Result", - "type": "string" -} - added
Output schema / properties / storiesAdded value: +{ + "items": { + "$ref": "#/$defs/LobstersStory" + }, + "title": "Stories", + "type": "array" +} - added
Output schema / properties / tag_filterAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Tag Filter" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "tag_filter", + "fetched_at", + "count", + "stories" +] - changed
Output schema / titlePrevious value: -"lobsters_hotOutput"New value: +"LobstersHotOutput"
- Changed
tech_signal_digest9 fields changed- added
Output schema / $defsAdded value: +{ + "ArxivPaper": { + "additionalProperties": false, + "properties": { + "abstract": { + "description": "First 400 characters", + "title": "Abstract", + "type": "string" + }, + "authors": { + "description": "First five", + "items": { + "type": "string" + }, + "title": "Authors", + "type": "array" + }, + "categories": { + "description": "All categories, primary included", + "items": { + "type": "string" + }, + "title": "Categories", + "type": "array" + }, + "category": { + "description": "Primary category", + "title": "Category", + "type": "string" + }, + "id": { + "title": "Id", + "type": "string" + }, + "pdf": { + "title": "Pdf", + "type": "string" + }, + "published": { + "title": "Published", + "type": "string" + }, + "title": { + "title": "Title", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + } + }, + "required": [ + "id", + "title", + "abstract", + "authors", + "published", + "category", + "categories", + "url", + "pdf" + ], + "title": "ArxivPaper", + "type": "object" + }, + "DigestArxivSection": { + "additionalProperties": false, + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Present only when the source failed; count is then 0 and means unknown", + "title": "Error" + }, + "label": { + "title": "Label", + "type": "string" + }, + "papers": { + "items": { + "$ref": "#/$defs/ArxivPaper" + }, + "title": "Papers", + "type": "array" + } + }, + "required": [ + "label", + "count", + "papers" + ], + "title": "DigestArxivSection", + "type": "object" + }, + "DigestGithubRepo": { + "additionalProperties": false, + "properties": { + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Language" + }, + "name": { + "title": "Name", + "type": "string" + }, + "stars": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Stars" + }, + "topics": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Topics" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + } + }, + "required": [ + "name", + "description", + "stars", + "language", + "topics", + "url" + ], + "title": "DigestGithubRepo", + "type": "object" + }, + "DigestGithubSection": { + "additionalProperties": false, + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Present only when the source failed; count is then 0 and means unknown", + "title": "Error" + }, + "incomplete_topics": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Topics whose sweep failed; the section is short by them", + "title": "Incomplete Topics" + }, + "label": { + "title": "Label", + "type": "string" + }, + "repos": { + "items": { + "$ref": "#/$defs/DigestGithubRepo" + }, + "title": "Repos", + "type": "array" + } + }, + "required": [ + "label", + "count", + "repos" + ], + "title": "DigestGithubSection", + "type": "object" + }, + "DigestHnSection": { + "additionalProperties": false, + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Present only when the source failed; count is then 0 and means unknown", + "title": "Error" + }, + "label": { + "title": "Label", + "type": "string" + }, + "stories": { + "items": { + "$ref": "#/$defs/HnStory" + }, + "title": "Stories", + "type": "array" + } + }, + "required": [ + "label", + "count", + "stories" + ], + "title": "DigestHnSection", + "type": "object" + }, + "DigestLobstersSection": { + "additionalProperties": false, + "properties": { + "count": { + "title": "Count", + "type": "integer" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Present only when the source failed; count is then 0 and means unknown", + "title": "Error" + }, + "label": { + "title": "Label", + "type": "string" + }, + "stories": { + "items": { + "$ref": "#/$defs/DigestLobstersStory" + }, + "title": "Stories", + "type": "array" + } + }, + "required": [ + "label", + "count", + "stories" + ], + "title": "DigestLobstersSection", + "type": "object" + }, + "DigestLobstersStory": { + "additionalProperties": false, + "properties": { + "lobsters_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Lobsters Url" + }, + "score": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Score" + }, + "tags": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Tags" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + } + }, + "required": [ + "title", + "url", + "score", + "tags", + "lobsters_url" + ], + "title": "DigestLobstersStory", + "type": "object" + }, + "DigestSources": { + "additionalProperties": false, + "properties": { + "arxiv": { + "$ref": "#/$defs/DigestArxivSection" + }, + "github": { + "$ref": "#/$defs/DigestGithubSection" + }, + "hn": { + "$ref": "#/$defs/DigestHnSection" + }, + "lobsters": { + "$ref": "#/$defs/DigestLobstersSection" + } + }, + "required": [ + "hn", + "arxiv", + "lobsters", + "github" + ], + "title": "DigestSources", + "type": "object" + }, + "HnStory": { + "additionalProperties": false, + "properties": { + "by": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "By" + }, + "comments": { + "title": "Comments", + "type": "integer" + }, + "hn_link": { + "title": "Hn Link", + "type": "string" + }, + "id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Id" + }, + "posted": { + "description": "UTC timestamp, or 'unknown'", + "title": "Posted", + "type": "string" + }, + "score": { + "title": "Score", + "type": "integer" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "'story' or 'job'", + "title": "Type" + }, + "url": { + "description": "Linked article, or the HN item itself for Ask HN", + "title": "Url", + "type": "string" + } + }, + "required": [ + "id", + "type", + "title", + "url", + "score", + "comments", + "by", + "posted", + "hn_link" + ], + "title": "HnStory", + "type": "object" + } +} - added
Output schema / additionalPropertiesAdded value: +false - added
Output schema / properties / degraded_sourcesAdded value: +{ + "description": "Sources that failed; treat their sections as unknown, not as empty", + "items": { + "type": "string" + }, + "title": "Degraded Sources", + "type": "array" +} - added
Output schema / properties / focusAdded value: +{ + "title": "Focus", + "type": "string" +} - added
Output schema / properties / generated_atAdded value: +{ + "title": "Generated At", + "type": "string" +} - removed
Output schema / properties / resultRemoved value: -{ - "title": "Result", - "type": "string" -} - added
Output schema / properties / sourcesAdded value: +{ + "$ref": "#/$defs/DigestSources" +} - changed
Output schema / requiredPrevious value: -[ - "result" -]New value: +[ + "generated_at", + "focus", + "degraded_sources", + "sources" +] - changed
Output schema / titlePrevious value: -"tech_signal_digestOutput"New value: +"TechSignalDigestOutput"
2 tool updates
v0.3.0- Added
hn_discussion - Changed
hn_top_stories2 fields changed- changed
Input schema / $defs / HnTopStoriesInput / properties / feed / descriptionPrevious value: -"Feed type: 'top' (frontpage), 'best' (highest voted), 'new' (latest)"New value: +"Feed type: 'top' (frontpage), 'best' (highest voted), 'new' (latest), 'ask' (Ask HN questions), 'show' (Show HN projects), 'job' (YC job posts). Note: 'ask' and 'job' are short feeds (~30 items upstream)." - changed
Input schema / $defs / HnTopStoriesInput / properties / feed / patternPrevious value: -"^(top|best|new)$"New value: +"^(top|best|new|ask|show|job)$"
7 tool updates
v0.2.4- First observed
arxiv_latest - First observed
arxiv_search - First observed
github_trending_ai - First observed
hn_search - First observed
hn_top_stories - First observed
lobsters_hot - First observed
tech_signal_digest
TDQS
Scored across 8 tools
Each tool targets a distinct source and action: HN feed retrieval vs keyword search vs thread reading; arXiv latest vs search; Lobsters hot; GitHub trending; and an aggregate digest. Overlap is minimal, and the aggregate tool is clearly positioned as a convenience rather than a replacement.
All names use snake_case and mostly follow a source_descriptor convention (hn_, arxiv_, lobsters_, github_). The suffixes vary between verbs and nouns/adjectives (search, discussion, latest, hot), so the pattern is not a strict verb_noun convention.
Eight tools is well-scoped for a four-source signal aggregator: three for HN, two for arXiv, one each for Lobsters and GitHub, plus an aggregator. No tool feels redundant or excessive.
Core workflows are covered: discover recent items per source, search HN and arXiv, read HN discussions, and aggregate across sources. Minor gaps exist (e.g., no keyword search for Lobsters or GitHub beyond hot/trending), but agents can work around them via the digest focus filter.
Maintenance
Related MCP Connectors
Live AI trend radar: trending AI topics, why-trending signals, daily digests. No API key.
Google Trends, Google News, Hacker News, Substack posts and podcasts for research and monitoring.
Read your AI-written daily briefing and retune the filter behind it: feeds, newsletters, topics.
Free cross-lingual news briefings for AI agents across 89 languages. Read-only, hosted.
Related MCP Servers
- FlicenseAqualityDmaintenanceFetches RSS feeds from tech blogs and news sites and returns AI-generated summaries via Claude.15-
- AlicenseAqualityFmaintenanceAggregates and curates AI/tech news from 12 sources every 6 hours, providing pre-summarized and Opus-curated top picks for vibe coders and AI builders via MCP tools.870MIT
- AlicenseNot gradedqualityBmaintenanceEnables fetching daily AI news summaries aggregated from multiple sources including GitHub, Hacker News, Product Hunt, YouTube, Twitter, and Chinese AI sites.MIT
- FlicenseNot gradedqualityAmaintenanceProvides semantic search over a self-building personal technical knowledge base that automatically collects and distills content from GitHub Trending, AI news, and arXiv.-