hn-mcp-server
Browse Hacker News feeds, threads, and user profiles with full-text search.
使うべきか
品質と安全性
検出事項(2)
- HIGH
- INFOhn_search_content 内
ツール定義とプロトコルへの準拠に関する自動分析に基づいています。
コンテキストコスト
これは、サーバーのツールがモデルのコンテキストに読み込まれるたびに消費されるおおよそのトークン数です。数が多いほど、ほかのタスクに使える注意が減ります。
インストール
ワンクリックインストール
これを `claude_desktop_config.json` ファイルに追加してください:
{
"mcpServers": {
"hn-mcp-server": {
"command": "bun",
"args": [
"@cyanheads/hn-mcp-server"
]
}
}
}実行可能なパッケージ
0.5.20streamable-httpリモートエンドポイント
https://hn.caseyjhand.com/mcpstreamable-httpできること
ツール一覧
ツール(4)
🟢hn_get_stories(feed, count, offset)
Fetch stories from an HN feed (top, new, best, ask, show, jobs), with title, URL, score, author, and comment count for each story.
入力スキーマ
{
"type": "object",
"properties": {
"feed": {
"type": "string",
"enum": [
"top",
"new",
"best",
"ask",
"show",
"jobs"
],
"description": "Which HN feed to fetch. \"top\" includes jobs. \"ask\" and \"show\" are Ask HN / Show HN posts."
},
"count": {
"default": 30,
"description": "Number of stories to return. Larger counts take longer.",
"type": "integer",
"minimum": 1,
"maximum": 100
},
"offset": {
"default": 0,
"description": "Number of stories to skip from the start of the feed. Use with count for pagination.",
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"feed"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}出力スキーマ
{
"type": "object",
"properties": {
"stories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Item ID — use with hn_get_thread to read comments."
},
"type": {
"type": "string",
"description": "Item type (story, job)."
},
"title": {
"description": "Story title when provided by HN. Omitted when unknown.",
"type": "string"
},
"url": {
"description": "External link URL. Absent for Ask HN / text posts.",
"type": "string"
},
"domain": {
"description": "Bare hostname derived from url (e.g. \"github.com\", with leading \"www.\" stripped). Absent when url is missing or unparseable.",
"type": "string"
},
"score": {
"description": "Upvote count when provided by HN. Omitted when unknown.",
"type": "number"
},
"by": {
"description": "Author username when provided by HN. Omitted when unknown.",
"type": "string"
},
"time": {
"description": "Unix timestamp when provided by HN. Omitted when unknown.",
"type": "number"
},
"descendants": {
"description": "Total comment count. Absent for jobs.",
"type": "number"
},
"text": {
"description": "Body text for Ask HN / text posts. Use hn_get_thread for full discussion.",
"type": "string"
}
},
"required": [
"id",
"type"
],
"additionalProperties": false,
"description": "A single story or job posting."
},
"description": "Stories from the feed, ordered by HN ranking."
},
"feed": {
"type": "string",
"description": "Which feed was fetched."
},
"total": {
"type": "number",
"description": "Total items in the feed (up to 500 for top/new/best, 200 for ask/show/jobs)."
},
"offset": {
"type": "number",
"description": "Offset that was applied to this page."
},
"hasMore": {
"type": "boolean",
"description": "Whether more stories are available beyond this page."
},
"truncated": {
"description": "True when more stories remain beyond this page (hasMore). Absent on the last page of the feed.",
"type": "boolean"
},
"shown": {
"description": "Number of stories returned on this page.",
"type": "number"
},
"cap": {
"description": "The count cap that was applied.",
"type": "number"
},
"failedIds": {
"description": "IDs on this page whose fetch failed after retries. They are not deleted or missing and may load on a later call: retry the page with the same offset, or pass an ID to hn_get_thread to fetch that story alone. Absent when every item on the page loaded.",
"type": "array",
"items": {
"type": "number"
}
},
"notice": {
"description": "Agent guidance: the offset to pass for the next page while more stories remain, the IDs that failed to load and how to retry them, or why a page came back empty — offset past the end of the feed, an empty feed, or every item on the page deleted or flagged. Absent on the last page of a non-empty result with no failed IDs.",
"type": "string"
},
"error": {
"description": "Present when the call failed. Absent on success.",
"type": "object",
"properties": {
"code": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Machine-readable failure mode. Declared by this tool: `upstream_rejected`: The HN API answered with a 4xx status other than 429 — it rejected the request as built. `upstream_rate_limited`: The HN API answered with HTTP 429. `upstream_unavailable`: The HN API answered with a 5xx status. `upstream_html`: The HN API served an HTML error page with a 200 status, which it does under rate limiting or maintenance. `upstream_malformed`: The HN API answered with a 200 status and a body that is not JSON. Other values are possible when a failure originates below the handler.",
"examples": [
"upstream_rejected",
"upstream_rate_limited",
"upstream_unavailable",
"upstream_html",
"upstream_malformed"
]
},
"recovery": {
"description": "Actionable next step for the caller.",
"type": "object",
"properties": {
"hint": {
"type": "string"
}
},
"required": [
"hint"
],
"additionalProperties": {}
},
"retryable": {
"description": "Whether retrying may succeed.",
"type": "boolean"
}
},
"additionalProperties": {}
}
},
"required": [
"code",
"message"
],
"additionalProperties": {}
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"stories",
"feed",
"total",
"offset",
"hasMore"
]
},
{
"required": [
"error"
]
}
]
}🟢hn_get_thread(itemId, depth, maxComments, cursor)
Get an item and its comment tree as a threaded discussion, with child comments resolved recursively. Use depth 0 for an item-only lookup. A long thread comes back in pages: pass the returned nextCursor as cursor to continue.
入力スキーマ
{
"type": "object",
"properties": {
"itemId": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "ID of the story, comment, job, poll, or poll option to fetch the thread for."
},
"depth": {
"default": 3,
"description": "How many levels of replies to resolve. 0 = just the item, no comments. 1 = direct replies only. Replies below this depth are never fetched — to read them, raise depth or call again with a specific comment's itemId to drill into its subtree. Ignored when cursor is set: the depth the cursor was issued with applies.",
"type": "integer",
"minimum": 0,
"maximum": 10
},
"maxComments": {
"default": 50,
"description": "Maximum comments in one response, across all depth levels. Highest-ranked top-level comments resolve first; replies fill in only after the level above is exhausted. A response also stops at a fixed 64,000-byte text budget. When either limit stops the traversal, nextCursor continues it.",
"type": "integer",
"minimum": 1,
"maximum": 200
},
"cursor": {
"description": "nextCursor from a previous call, passed with the same itemId, to continue the traversal at the next unseen comment without repeating or skipping any. Omit to start from the top.",
"type": "string"
}
},
"required": [
"itemId"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}出力スキーマ
{
"type": "object",
"properties": {
"item": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Item ID."
},
"type": {
"type": "string",
"description": "Item type: story | comment | job | poll | pollopt."
},
"by": {
"description": "Author username.",
"type": "string"
},
"time": {
"description": "Unix timestamp.",
"type": "number"
},
"title": {
"description": "Story/job/poll title.",
"type": "string"
},
"url": {
"description": "External link URL.",
"type": "string"
},
"text": {
"description": "Body text (HTML stripped).",
"type": "string"
},
"score": {
"description": "Upvote count.",
"type": "number"
},
"descendants": {
"description": "Total comment count.",
"type": "number"
},
"parent": {
"description": "For a comment root, the ID of the story or comment it replies to — pass it as itemId for the context above.",
"type": "number"
},
"poll": {
"description": "For a poll-option root, the ID of its poll.",
"type": "number"
},
"parts": {
"description": "For a poll root, its option IDs in HN order.",
"type": "array",
"items": {
"type": "number"
}
},
"deleted": {
"description": "Present and `true` when HN reports the root deleted; its author and text are gone, but its replies are still walked. Omitted otherwise.",
"type": "boolean",
"const": true
},
"dead": {
"description": "Present and `true` when the root is dead (flagged or killed); its replies are still walked. Omitted otherwise.",
"type": "boolean",
"const": true
},
"options": {
"description": "For a poll root, its options with text and votes in parts order, resolved at every depth including 0. Deleted and dead options are kept and marked; an option whose fetch failed is listed in failedIds instead. Options do not count toward maxComments or totalLoaded.",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Poll option ID."
},
"text": {
"description": "Option text (HTML stripped). Absent once deleted.",
"type": "string"
},
"score": {
"description": "Votes for this option.",
"type": "number"
},
"deleted": {
"description": "Present and `true` when the option was deleted. Omitted otherwise.",
"type": "boolean",
"const": true
},
"dead": {
"description": "Present and `true` when the option is dead (flagged or killed). Omitted otherwise.",
"type": "boolean",
"const": true
}
},
"required": [
"id"
],
"additionalProperties": false,
"description": "One poll option."
}
}
},
"required": [
"id",
"type"
],
"additionalProperties": false,
"description": "The root item: a story, comment, job, poll, or poll option."
},
"comments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Comment ID."
},
"by": {
"description": "Author username.",
"type": "string"
},
"time": {
"description": "Unix timestamp.",
"type": "number"
},
"text": {
"description": "Comment text (HTML stripped).",
"type": "string"
},
"depth": {
"type": "number",
"description": "Nesting level (0 = direct reply to root)."
},
"parentId": {
"type": "number",
"description": "Parent item ID."
},
"childCount": {
"type": "number",
"description": "Number of direct child comments (may exceed what was resolved)."
},
"isOp": {
"description": "Present and `true` when the comment author matches the root item author (OP replying within their own thread). Omitted otherwise — including when either author is missing. Most threads carry no OP replies, so absence is the common case; treat missing as \"not OP\" rather than unknown.",
"type": "boolean",
"const": true
}
},
"required": [
"id",
"depth",
"parentId",
"childCount"
],
"additionalProperties": false,
"description": "A single comment in the thread with its tree position."
},
"description": "Flat comment list ordered breadth-first by rank: highest-ranked top-level comments first, then their replies. Use depth/parentId to reconstruct nesting. With cursor, the page continues the same order."
},
"totalLoaded": {
"type": "number",
"description": "Number of comments in this response (this page, when paging with cursor)."
},
"totalAvailable": {
"description": "HN's total comment count for the root (descendants), which also counts dead and deleted comments. Absent for comment and job roots.",
"type": "number"
},
"truncated": {
"description": "True when comments remain unread: reachable through nextCursor (truncationReason count, size, or rate_limited), or below the depth limit (depth). Absent on a terminal page — including a thread that loads in full at exactly maxComments.",
"type": "boolean"
},
"truncationReason": {
"description": "Why the traversal stopped short: count = maxComments (or the 1,000-item ceiling per call) was reached; size = the 64,000-byte response budget was reached; rate_limited = the HN API throttled the fetches; depth = replies lie below the depth limit, which nextCursor does not reach — raise depth or pass a comment id as itemId. When a cursor reason and a depth cut both apply, the cursor reason is reported.",
"type": "string",
"enum": [
"count",
"size",
"depth",
"rate_limited"
]
},
"nextCursor": {
"description": "Pass as cursor, with the same itemId, to continue from the next unseen comment. Present when truncationReason is count, size, or rate_limited; absent on the last page.",
"type": "string"
},
"shown": {
"description": "Number of comments returned.",
"type": "number"
},
"cap": {
"description": "The maxComments cap that was applied.",
"type": "number"
},
"failedIds": {
"description": "Comment and poll-option IDs whose fetch failed after retries, so neither they nor their replies are in this response. They are not deleted or missing and may load on a later call. Failed comments ride in nextCursor and are retried when you continue; without one, call again, or pass an ID as itemId to fetch that comment and its replies. Absent when everything the traversal reached loaded.",
"type": "array",
"items": {
"type": "number"
}
},
"notice": {
"description": "Traversal context: counts of deleted/dead comments dropped, the IDs that failed to load and how to retry them, then what stopped the traversal and how to continue — nextCursor for a count, size, or rate-limit stop, or a larger depth or a comment id as itemId for a depth cut. Absent when nothing was dropped, failed, or left unread.",
"type": "string"
},
"error": {
"description": "Present when the call failed. Absent on success.",
"type": "object",
"properties": {
"code": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Machine-readable failure mode. Declared by this tool: `item_not_found`: HN reports no item exists for the given itemId. `invalid_cursor`: The cursor was issued for a different itemId than the one passed with it. `upstream_rejected`: The HN API answered with a 4xx status other than 429 — it rejected the request as built. `upstream_rate_limited`: The HN API answered with HTTP 429. `upstream_unavailable`: The HN API answered with a 5xx status. `upstream_html`: The HN API served an HTML error page with a 200 status, which it does under rate limiting or maintenance. `upstream_malformed`: The HN API answered with a 200 status and a body that is not JSON. Other values are possible when a failure originates below the handler.",
"examples": [
"item_not_found",
"invalid_cursor",
"upstream_rejected",
"upstream_rate_limited",
"upstream_unavailable",
"upstream_html",
"upstream_malformed"
]
},
"recovery": {
"description": "Actionable next step for the caller.",
"type": "object",
"properties": {
"hint": {
"type": "string"
}
},
"required": [
"hint"
],
"additionalProperties": {}
},
"retryable": {
"description": "Whether retrying may succeed.",
"type": "boolean"
}
},
"additionalProperties": {}
}
},
"required": [
"code",
"message"
],
"additionalProperties": {}
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"item",
"comments",
"totalLoaded"
]
},
{
"required": [
"error"
]
}
]
}🟢hn_get_user(username, includeSubmissions, submissionCount, submissionOffset)
Get an HN user profile with karma, about, and optionally their most recent submissions resolved into full items.
入力スキーマ
{
"type": "object",
"properties": {
"username": {
"type": "string",
"minLength": 1,
"description": "HN username. Case-sensitive. Trimmed; blank or whitespace-only input is rejected."
},
"includeSubmissions": {
"default": false,
"description": "Resolve the user's most recent submissions into full items. Without this, only the submission count is available.",
"type": "boolean"
},
"submissionCount": {
"default": 10,
"description": "Page size — how many submissions to resolve per call. Only used when includeSubmissions is true.",
"type": "integer",
"minimum": 1,
"maximum": 50
},
"submissionOffset": {
"default": 0,
"description": "How many submissions to skip before resolving, counting back from the most recent. Use with submissionCount to page through a long history: request offset 0, then offset submissionCount, and so on. The enrichment block echoes submissionOffset and, when more remain, the offset to send next. Only used when includeSubmissions is true.",
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"username"
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}出力スキーマ
{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Username."
},
"karma": {
"type": "number",
"description": "Karma score."
},
"created": {
"type": "number",
"description": "Account creation time (Unix timestamp)."
},
"about": {
"description": "Self-description (HTML stripped).",
"type": "string"
},
"totalSubmissions": {
"type": "number",
"description": "Total number of submissions."
}
},
"required": [
"id",
"karma",
"created",
"totalSubmissions"
],
"additionalProperties": false,
"description": "User profile."
},
"submissions": {
"description": "One page of submissions, most recent first, starting at submissionOffset. Absent when includeSubmissions is false or the user has never submitted. Empty when the page holds no live items — either the offset is past the end, or every item in the window was deleted, flagged, or failed to load (failedIds lists the failures).",
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Item ID — use with hn_get_thread to read comments."
},
"type": {
"type": "string",
"description": "Item type (story, comment, job, poll, pollopt)."
},
"parent": {
"description": "For a comment, the ID of the story or comment it replied to — pass it to hn_get_thread for the context.",
"type": "number"
},
"poll": {
"description": "For a poll option, the ID of its poll.",
"type": "number"
},
"title": {
"description": "Title (stories/jobs/polls).",
"type": "string"
},
"url": {
"description": "External link URL.",
"type": "string"
},
"text": {
"description": "Body text (HTML stripped).",
"type": "string"
},
"score": {
"description": "Score/upvotes.",
"type": "number"
},
"time": {
"description": "Unix timestamp.",
"type": "number"
},
"descendants": {
"description": "Comment count (stories/polls).",
"type": "number"
}
},
"required": [
"id",
"type"
],
"additionalProperties": false,
"description": "A single submission by the user (story, comment, job, poll, or poll option)."
}
},
"submissionOffset": {
"description": "The offset this page started at. Absent when includeSubmissions is false or the user has never submitted.",
"type": "number"
},
"truncated": {
"description": "True when submissions remain beyond this page.",
"type": "boolean"
},
"shown": {
"description": "Number of submissions returned.",
"type": "number"
},
"cap": {
"description": "The submissionCount cap that was applied.",
"type": "number"
},
"failedIds": {
"description": "Submission IDs in this window whose fetch failed after retries. They are not deleted or missing and may load on a later call: retry with the same submissionOffset, or pass an ID to hn_get_thread to fetch that item alone. Absent when every submission in the window loaded.",
"type": "array",
"items": {
"type": "number"
}
},
"notice": {
"description": "Pagination context — which window of the history this page covers and the submissionOffset to send next, the submission IDs that failed to load and how to retry them, or a warning that the offset is past the end. Absent when the page reaches the end of the history with no failed IDs, or when no submissions were resolved.",
"type": "string"
},
"error": {
"description": "Present when the call failed. Absent on success.",
"type": "object",
"properties": {
"code": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Machine-readable failure mode. Declared by this tool: `user_not_found`: HN reports no user account exists for the given username. `upstream_rejected`: The HN API answered with a 4xx status other than 429 — it rejected the request as built, which a username outside HN’s charset can cause. `upstream_rate_limited`: The HN API answered with HTTP 429. `upstream_unavailable`: The HN API answered with a 5xx status. `upstream_html`: The HN API served an HTML error page with a 200 status, which it does under rate limiting or maintenance. `upstream_malformed`: The HN API answered with a 200 status and a body that is not JSON. Other values are possible when a failure originates below the handler.",
"examples": [
"user_not_found",
"upstream_rejected",
"upstream_rate_limited",
"upstream_unavailable",
"upstream_html",
"upstream_malformed"
]
},
"recovery": {
"description": "Actionable next step for the caller.",
"type": "object",
"properties": {
"hint": {
"type": "string"
}
},
"required": [
"hint"
],
"additionalProperties": {}
},
"retryable": {
"description": "Whether retrying may succeed.",
"type": "boolean"
}
},
"additionalProperties": {}
}
},
"required": [
"code",
"message"
],
"additionalProperties": {}
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"user"
]
},
{
"required": [
"error"
]
}
]
}🟢hn_search_content(query, tags, author, storyId, sort, ...)
Search Hacker News stories, comments, polls, and jobs via Algolia — by keyword, by filters alone, or both. Filterable by content type, author, parent story, date range, and minimum points.
入力スキーマ
{
"type": "object",
"properties": {
"query": {
"description": "Search terms. Supports simple keywords — Algolia handles stemming and relevance. Trimmed before searching; blank or whitespace-only input is rejected. Omit for a filter-only search, which needs at least one of tags, author, storyId, minPoints, or a dateRange bound.",
"type": "string",
"minLength": 1
},
"tags": {
"description": "Filter results by content type: \"story\", \"comment\", \"poll\", or \"job\", or the story subsets \"ask_hn\", \"show_hn\", and \"front_page\". Omit to search all types.",
"type": "string",
"enum": [
"story",
"comment",
"poll",
"job",
"ask_hn",
"show_hn",
"front_page"
]
},
"author": {
"description": "Filter results to a specific author. Useful for finding a user's posts on a topic (hn_get_user only shows recent submissions). Trimmed before filtering; omit the field to search all authors rather than passing a blank string.",
"type": "string",
"minLength": 1
},
"storyId": {
"description": "Restrict results to one discussion: the id of a story or poll root, combined with the other filters. Pair with tags \"comment\" to search within a thread. Take it from hits[].storyId or the root item of hn_get_thread — a comment id matches nothing.",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
},
"sort": {
"default": "relevance",
"description": "Sort order. \"relevance\" for best match, \"date\" for most recent first.",
"type": "string",
"enum": [
"relevance",
"date"
]
},
"dateRange": {
"description": "Filter to a creation-time window with a start, an end, or both. An empty object is rejected — omit dateRange instead.",
"type": "object",
"properties": {
"start": {
"description": "Exclusive lower bound — only items created strictly after this instant match. ISO 8601: YYYY, YYYY-MM, YYYY-MM-DD, or YYYY-MM-DDThh:mm[:ss[.sss]] with an optional Z or ±hh:mm offset. Reduced and date-only forms mean UTC midnight at the start of that period; a date-time without an offset is read as UTC.",
"type": "string",
"pattern": "^(\\d{4})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d{1,3})?)?(?:Z|[+-](\\d{2}):(\\d{2}))?)?)?)?$"
},
"end": {
"description": "Exclusive upper bound — only items created strictly before this instant match. Same formats and UTC reading as start, and must be later than start. A date-only end excludes that whole UTC day: to include it, pass the next day or a full timestamp.",
"type": "string",
"pattern": "^(\\d{4})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d{1,3})?)?(?:Z|[+-](\\d{2}):(\\d{2}))?)?)?)?$"
}
}
},
"minPoints": {
"description": "Minimum score. Applies to stories and polls, including the ask_hn, show_hn, and front_page subsets. Comments and jobs carry no points in the search index, so any minPoints excludes them — combining it with tags \"comment\" or \"job\" is rejected.",
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"count": {
"default": 30,
"description": "Number of results to return.",
"type": "integer",
"minimum": 1,
"maximum": 50
},
"page": {
"default": 0,
"description": "Page number for pagination (0-indexed).",
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"view": {
"default": "full",
"description": "How much of each hit to return. \"full\" includes every field. \"compact\" omits the two body-text fields — `text` and `highlights.text` — which together can repeat a long comment twice per hit; everything else (id, title, url, domain, author, points, comment count, timestamp, parent story, title highlight, matchedWords) is unchanged. Use \"compact\" to scan many results, then pass a hit id to hn_get_thread to read the body you skipped.",
"type": "string",
"enum": [
"full",
"compact"
]
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}出力スキーマ
{
"type": "object",
"properties": {
"hits": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "HN item ID — use with hn_get_thread to read the discussion."
},
"title": {
"description": "Item title (present for stories, polls, and jobs).",
"type": "string"
},
"url": {
"description": "External link URL.",
"type": "string"
},
"domain": {
"description": "Bare hostname derived from url (e.g. \"github.com\", with leading \"www.\" stripped). Absent when url is missing or unparseable.",
"type": "string"
},
"author": {
"type": "string",
"description": "Author username."
},
"points": {
"description": "Score/upvotes.",
"type": "number"
},
"numComments": {
"description": "Comment count.",
"type": "number"
},
"createdAt": {
"type": "string",
"description": "Creation time (ISO 8601)."
},
"storyTitle": {
"description": "Parent story title (present for comment results).",
"type": "string"
},
"storyId": {
"description": "Parent story ID for comment hits; equals `id` for story hits.",
"type": "number"
},
"text": {
"description": "Comment or story body text (HTML stripped). Absent when the hit has no body, and always absent under view \"compact\" — call hn_get_thread with this id to read it.",
"type": "string"
},
"highlights": {
"description": "Algolia per-field highlight metadata showing which terms matched and where. Absent when no fields produced a match.",
"type": "object",
"properties": {
"title": {
"description": "Title snippet with matched terms wrapped in `<em>…</em>`. Titles are not HTML, though some arrive entity-encoded; entities are decoded and no tags are stripped, so a literal `<em>` in the title is indistinguishable from a marker. Absent when the title did not match.",
"type": "string"
},
"text": {
"description": "Body snippet (comment_text or story_text), HTML stripped, with matched terms wrapped in `<em>…</em>`. A literal `<em>` typed into the body is indistinguishable from a marker. Absent when the body did not match, and always absent under view \"compact\" — matchedWords still lists what matched.",
"type": "string"
},
"matchedWords": {
"type": "array",
"items": {
"type": "string"
},
"description": "Deduplicated union of matched terms across all searchable fields (title, url, author, comment_text, story_text, story_title)."
}
},
"required": [
"matchedWords"
],
"additionalProperties": false
}
},
"required": [
"id",
"author",
"createdAt"
],
"additionalProperties": false,
"description": "A single Algolia search hit (story, comment, poll, or job)."
},
"description": "Search results ranked by sort order."
},
"query": {
"description": "The query that was searched. Absent for a filter-only search.",
"type": "string"
},
"totalHits": {
"type": "number",
"description": "Total matching results across all pages."
},
"page": {
"type": "number",
"description": "Current page number (0-indexed)."
},
"totalPages": {
"type": "number",
"description": "Number of pages Algolia will actually serve for this query. Not derived from totalHits — broad queries report a totalHits far larger than the reachable page range, so paginate against this value."
},
"truncated": {
"description": "True when more pages remain after this one (page + 1 < totalPages). Absent on the last page Algolia serves.",
"type": "boolean"
},
"shown": {
"description": "Number of hits returned.",
"type": "number"
},
"cap": {
"description": "The count cap that was applied.",
"type": "number"
},
"notice": {
"description": "Agent guidance: the next page to request while more pages remain, the last valid page when the requested page is past the end, or the filters to relax when a first page comes back empty. Absent on the last page of a non-empty result.",
"type": "string"
},
"error": {
"description": "Present when the call failed. Absent on success.",
"type": "object",
"properties": {
"code": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Machine-readable failure mode. Declared by this tool: `missing_query_or_filter`: Neither a query nor any filter was supplied, so there is nothing to search by. `invalid_date_range`: dateRange was supplied with neither bound, or with start not before end. `min_points_unscored_type`: minPoints was combined with tags \"comment\" or \"job\", record types Algolia stores without points. `upstream_rejected`: Algolia answered with a 4xx status other than 429 — it rejected the request as built. `upstream_rate_limited`: Algolia answered with HTTP 429. `upstream_unavailable`: Algolia answered with a 5xx status. `upstream_html`: Algolia served an HTML error page with a 200 status, which it does under rate limiting or maintenance. `upstream_malformed`: Algolia answered with a 200 status and a body that is not JSON. Other values are possible when a failure originates below the handler.",
"examples": [
"missing_query_or_filter",
"invalid_date_range",
"min_points_unscored_type",
"upstream_rejected",
"upstream_rate_limited",
"upstream_unavailable",
"upstream_html",
"upstream_malformed"
]
},
"recovery": {
"description": "Actionable next step for the caller.",
"type": "object",
"properties": {
"hint": {
"type": "string"
}
},
"required": [
"hint"
],
"additionalProperties": {}
},
"retryable": {
"description": "Whether retrying may succeed.",
"type": "boolean"
}
},
"additionalProperties": {}
}
},
"required": [
"code",
"message"
],
"additionalProperties": {}
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"hits",
"totalHits",
"page",
"totalPages"
]
},
{
"required": [
"error"
]
}
]
}コミュニティ
エビデンス