search-posts
Search HackerNews content by keyword with filters for tags, points, comments, and dates to find relevant stories and discussions.
Instructions
Search HackerNews for stories, comments, and other content by keyword.
Supports:
Keyword search across titles, text, and authors
Tag filtering (story, comment, poll, show_hn, ask_hn, front_page, author_USERNAME)
Numeric filters for points, comments, and dates
Pagination with customizable results per page
Advanced filtering with OR logic and multiple conditions
Basic Examples:
Search for AI stories: { "query": "AI", "tags": ["story"] }
Find popular posts: { "query": "Python", "numericFilters": ["points>=100"] }
Filter by author: { "query": "startup", "tags": ["author_pg"] }
Date range: { "query": "startup", "numericFilters": ["created_at_i>1640000000"] }
Advanced Filtering Examples:
High engagement posts: { "query": "programming", "numericFilters": ["points>=100", "num_comments>=50"] }
OR logic for tags: { "query": "web", "tags": ["(story,poll)"] } - returns stories OR polls
Author with filters: { "query": "", "tags": ["author_pg", "story"], "numericFilters": ["points>=50"] }
Multiple conditions: { "query": "AI", "tags": ["story"], "numericFilters": ["points>=200", "num_comments>=100"] }
Numeric Filter Operators: < (less than), <= (less than or equal), = (equal), >= (greater than or equal), > (greater than) Numeric Filter Fields: points, num_comments, created_at_i (Unix timestamp)
Tag Syntax:
Single tag: ["story"] - only stories
Multiple tags (AND): ["story", "show_hn"] - stories that are also show_hn
OR logic: ["(story,poll)"] - stories OR polls
Author filter: ["author_USERNAME"] - posts by specific author
Returns paginated results with hits, total count, and page information.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| tags | No | ||
| numericFilters | No | ||
| page | No | ||
| hitsPerPage | No |
Implementation Reference
- src/mcp.ts:15-36 (handler)Core handler logic for all tools including 'search-posts': forwards arguments to remote API https://mcp.xiaobenyang.com/api using toolName='search-posts' in headers, returns text response as MCP content.const calcXiaoBenYangApi = async function (fullArgs: Record<string, any>) { // 发起 POST 请求 let response = await fetch('https://mcp.xiaobenyang.com/api', { method: 'POST', headers: { 'XBY-APIKEY': apiKey, 'func': fullArgs.toolName, 'mcpid': mcpID }, body: new URLSearchParams(fullArgs) }); const apiResult = await response.text(); return { content: [ { type: "text", text: apiResult // 将字符串结果放入 content 中 } ] } as { [x: string]: unknown; content: [{ type: "text"; text: string }] }; };
- src/mcp.ts:39-48 (handler)Intermediate handler called by registered tool handler: adds toolName to args and invokes the API proxy.const handleXiaoBenYangApi = async (args: Record<string, any>, toolName: string) => { // 校验aid是否存在 if (toolName === undefined || toolName === null) { throw new Error("缺少必要参数 'aid'"); } // 合并参数 const fullArgs = {...args, toolName: toolName}; // 调用API return calcXiaoBenYangApi(fullArgs); };
- src/mcp.ts:50-65 (registration)Helper function that registers each tool (including 'search-posts') with dynamic schema, description, and shared handler.const addToolXiaoBenYangApi = function ( name: string, desc: string, params: Record<string, ZodType> ) { server.registerTool( name, { title: name, description: desc, inputSchema: params, } , async (args: Record<string, any>) => handleXiaoBenYangApi(args, name) ) };
- src/mcp.ts:88-132 (registration)Dynamically fetches tool definitions from remote (https://mcp.xiaobenyang.com/getMcpDesc?mcpId=1777316659382275) and registers them, including 'search-posts', with generated Zod schemas.const apiDescList = data.tools; for (const apiDesc of apiDescList) { let inputSchema = JSON.parse(apiDesc.inputSchema); const zodDict: Record<string, z.ZodTypeAny> = {}; Object.entries(inputSchema.properties).forEach(([name, propConfig]) => { let zodType; let pt = (propConfig as { type: string }).type; switch (pt) { case 'string': zodType = z.string(); break; case 'number': zodType = z.number(); break; case 'boolean': zodType = z.boolean(); break; case 'integer': zodType = z.int32(); break; case 'array': zodType = z.array(z.any()); break; case 'object': zodType = z.object(z.any()); break; default: zodType = z.any(); } if (inputSchema.required?.includes(name)) { zodDict[name] = zodType; } else { zodDict[name] = zodType.optional(); } }); addToolXiaoBenYangApi( apiDesc.name, apiDesc.description ? apiDesc.description : apiDesc.name, zodDict); }
- src/mcp.ts:91-127 (schema)Generates Zod input schema for each tool based on remote JSON schema description, mapping types to Zod types and handling required/optional.let inputSchema = JSON.parse(apiDesc.inputSchema); const zodDict: Record<string, z.ZodTypeAny> = {}; Object.entries(inputSchema.properties).forEach(([name, propConfig]) => { let zodType; let pt = (propConfig as { type: string }).type; switch (pt) { case 'string': zodType = z.string(); break; case 'number': zodType = z.number(); break; case 'boolean': zodType = z.boolean(); break; case 'integer': zodType = z.int32(); break; case 'array': zodType = z.array(z.any()); break; case 'object': zodType = z.object(z.any()); break; default: zodType = z.any(); } if (inputSchema.required?.includes(name)) { zodDict[name] = zodType; } else { zodDict[name] = zodType.optional(); } });