hot_news_7x24
Access financial, political, tech, sports, entertainment, military, social, and international news from Tushare API with content deduplication for relevant updates.
Instructions
7x24热点:从Tushare新闻接口获取最新的财经、政治、科技、体育、娱乐、军事、社会、国际等新闻
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | 返回条数,默认100,上限1500。接口按此数量向Tushare请求后再进行内容相似度去重 |
Implementation Reference
- src/tools/hotNews.ts:130-179 (handler)Core handler logic: validates limit, fetches raw news from Tushare API, deduplicates using 80% Jaccard similarity on title+summary bigrams, formats as markdown list with source/day stats, handles errors and empty results.async run(_args?: { limit?: number }) { try { const logs: string[] = []; const rawLimit = typeof _args?.limit === 'number' && isFinite(_args.limit) ? Math.floor(_args.limit) : 100; const limit = Math.min(1500, Math.max(1, rawLimit)); logs.push(`[START] hot_news_7x24 获取最新批次(limit=${limit})`); const raw = await fetchTushareNewsBatch(limit, logs); const deduped = deduplicateByContent(raw, 0.8); logs.push(`[INFO] 去重后条数: ${deduped.length}`); if (deduped.length === 0) { const hint = '可能原因:1) 未配置 Tushare Token;2) 被频控限制;3) 网络/服务异常。'; return { content: [ { type: 'text', text: `# 7x24 热点\n\n暂无数据\n${hint}` }, { type: 'text', text: `## 调用日志\n\n${logs.join('\n')}` } ] }; } // 逐条仅展示摘要(如有标题可作为前缀),不展示来源/时间/分隔线 const formattedList = deduped.map(n => { const title = n.title ? `${n.title}\n` : ''; return `${title}${n.summary}`.trim(); }).join('\n---\n\n'); // 底部统计:来源统计 + 时间范围/日期 const sourceCounts = new Map<string, number>(); const daySet = new Set<string>(); for (const n of deduped) { sourceCounts.set(n.source, (sourceCounts.get(n.source) || 0) + 1); const day = (n.publishTime || '').split(' ')[0] || ''; if (day) daySet.add(day); } const sourceStats = Array.from(sourceCounts.entries()) .sort((a,b) => b[1]-a[1]) .map(([s, c]) => `${s}: ${c}`) .join(','); const uniqueDays = Array.from(daySet.values()).sort(); const dayInfo = uniqueDays.length ? `日期:${uniqueDays.join('、')}` : `日期:未知`; const footer = `\n\n—\n统计:共 ${deduped.length} 条;来源分布:${sourceStats || '无'}\n${dayInfo}\n数据来源:Tushare 新闻快讯 (<https://tushare.pro/document/2?doc_id=143>)`; return { content: [ { type: 'text', text: `# 7x24 热点(按80%相似度降重)\n\n${formattedList}${footer}` } ] }; } catch (error) { return { content: [{ type: 'text', text: `# 7x24 热点 获取失败\n\n错误: ${error instanceof Error ? error.message : '未知错误'}` }] }; } }
- src/tools/hotNews.ts:119-129 (schema)JSON Schema for tool input: optional 'limit' (number, 1-1500, default 100) controlling batch size before deduplication.parameters: { type: 'object', properties: { limit: { type: 'number', description: '返回条数,默认100,上限1500。接口按此数量向Tushare请求后再进行内容相似度去重', minimum: 1, maximum: 1500 } } },
- src/index.ts:401-403 (registration)Tool dispatch registration in stdio MCP server (index.ts): maps 'hot_news_7x24' calls to hotNews.run(), normalizes result.case "hot_news_7x24": { return normalizeResult(await hotNews.run({})); }
- src/httpServer.ts:410-411 (registration)Tool dispatch registration in HTTP MCP server (httpServer.ts): maps 'hot_news_7x24' calls to hotNews.run().case 'hot_news_7x24': return await hotNews.run({});
- src/tools/hotNews.ts:52-114 (helper)Key helper: Fetches latest news batch from Tushare 'news' API endpoint using POST with token, timeout/abort, parses response, maps to NewsItem, logs progress/errors.async function fetchTushareNewsBatch(maxTotal: number, logs?: string[]): Promise<NewsItem[]> { if (!TUSHARE_CONFIG.API_TOKEN) { logs?.push('[WARN] 未配置 TUSHARE_TOKEN,无法从 Tushare 获取数据'); return []; } const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), TUSHARE_CONFIG.TIMEOUT); try { const body = { api_name: 'news', token: TUSHARE_CONFIG.API_TOKEN, // 不传任何筛选参数,直接获取默认的最新数据 params: {}, fields: 'datetime,content,title,channels' } as const; const resp = await fetch(TUSHARE_CONFIG.API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: controller.signal }); clearTimeout(timeoutId); if (!resp.ok) { const msg = `Tushare请求失败: HTTP ${resp.status}`; logs?.push(`[ERROR] ${msg}`); return []; } const data = await resp.json(); if (data.code !== 0) { const msg = `Tushare返回错误: ${data.msg || data.message || '未知错误'}`; logs?.push(`[ERROR] ${msg}`); return []; } const fields: string[] = data.data?.fields ?? []; const items: any[][] = data.data?.items ?? []; const idxDatetime = fields.indexOf('datetime'); const idxContent = fields.indexOf('content'); const idxTitle = fields.indexOf('title'); const results: NewsItem[] = []; for (const row of items) { if (results.length >= maxTotal) break; const title = String(row[idxTitle] ?? '').trim(); const content = String(row[idxContent] ?? '').trim(); const datetime = String(row[idxDatetime] ?? '').trim(); results.push({ title, summary: content, url: '', source: 'Tushare', publishTime: datetime, keywords: [] }); } logs?.push(`[INFO] 从 Tushare 获取原始条数: ${results.length}`); return results; } catch (err) { clearTimeout(timeoutId); const msg = `获取Tushare新闻失败: ${err instanceof Error ? err.message : String(err)}`; console.error(msg); logs?.push(`[ERROR] ${msg}`); return []; } }