# Directory Structure ``` ├── .github │ └── workflows │ └── release.yml ├── .gitignore ├── assets │ └── og-image.png ├── eslint.config.mjs ├── LICENSE ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── README.md ├── src │ ├── index.ts │ ├── process.js │ ├── sources.json │ └── typing.ts ├── tsconfig.json └── tsup.config.ts ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` node_modules/ dist/ .vercel .output .vinxi .cache .data .wrangler .env .env.* dev-dist *.tsbuildinfo wrangler.toml imports.app.d.ts package-lock.json ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # newsnow-mcp-server  Official MCP Server for [NewsNow](https://github.com/ourongxing/newsnow), 40+ sources available. ## Usage ```json { "mcpServers": { "newsnow": { "command": "npx", "args": [ "-y", "newsnow-mcp-server" ], "env": { "BASE_URL": "https://newsnow.busiyi.world" } } } } ``` ## License [MIT](./LICENSE) © ourongxing ``` -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- ```yaml onlyBuiltDependencies: - esbuild - simple-git-hooks - unrs-resolver ``` -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- ``` import { ourongxing } from "@ourongxing/eslint-config" export default ourongxing({ type: "lib", ignores: ["public/", ".vscode", "**/*.json"], }) ``` -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- ```typescript import { defineConfig } from "tsup" export default defineConfig({ entry: ["src/index.ts"], format: ["esm"], target: "node18", clean: true, sourcemap: true, dts: false, splitting: false, minify: false, shims: true, treeshake: true, outDir: "dist", }) ``` -------------------------------------------------------------------------------- /src/process.js: -------------------------------------------------------------------------------- ```javascript import sources from "./sources.json" export const description = Object.entries(sources).filter(([_, source]) => { if (source.redirect) { return false } return true }).map(([id, source]) => { return source.title ? `${source.name}-${source.title} id is ${id}` : `${source.name} id is ${id}` }).join(";") ``` -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- ```yaml name: Release on: push: branch: main tags: - 'v*' jobs: release: permissions: contents: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-node@v4 with: node-version: lts/* - run: npx changelogithub env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} ``` -------------------------------------------------------------------------------- /src/typing.ts: -------------------------------------------------------------------------------- ```typescript export type SourceID = string export interface NewsItem { id: string | number // unique title: string url: string mobileUrl?: string pubDate?: number | string extra?: { hover?: string date?: number | string info?: false | string diff?: number icon?: false | string | { url: string scale: number } } } export interface SourceResponse { status: "success" | "cache" id: SourceID updatedTime: number | string items: NewsItem[] } ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "composite": true, "target": "ES2018", "moduleDetection": "force", "useDefineForClassFields": true, "module": "ESNext", "moduleResolution": "bundler", "allowImportingTsExtensions": true, "strict": true, "allowJs": true, "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, "noUnusedParameters": true, "noEmit": true, "esModuleInterop": true, "isolatedModules": true, "skipLibCheck": true, "lib": ["ES2018"], "rootDir": ".", "baseUrl": ".", }, "include": ["src/**/*.ts", "src/**/*.js"] } ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "newsnow-mcp-server", "version": "0.0.10", "description": "NewsNow MCP Server", "keywords": [ "mcp", "newsnow", "mcp-server", "hot-news" ], "author": "Ou RongXing<[email protected]>", "homepage": "https://github.com/ourongxing/newsnow-mcp-server#readme", "repository": { "type": "git", "url": "git+https://github.com/ourongxing/newsnow-mcp-server.git" }, "license": "MIT", "packageManager": "[email protected]", "type": "module", "bin": { "newsnow-mcp-server": "dist/index.js" }, "main": "./dist/index.js", "files": [ "dist" ], "dependencies": { "bumpp": "^10.1.0", "dotenv": "^16.5.0", "fastmcp": "^1.23.0", "ofetch": "^1.4.1", "zod": "^3.24.3" }, "scripts": { "dev": "tsup --watch --onSuccess \"node dist/index.mjs\"", "build": "tsup", "publish": "pnpm publish --access public --no-git-checks", "lint": "eslint", "release": "bumpp", "lint:fix": "eslint --fix" }, "devDependencies": { "@ourongxing/eslint-config": "3.2.3-beta.6", "@types/node": "^22.15.2", "eslint": "^9.21.0", "simple-git-hooks": "^2.11.1", "tsup": "^8.4.0", "tsx": "^4.19.3", "typescript": "^5.8.2", "typescript-eslint": "^8.25.0", "unbuild": "^3.5.0" }, "simple-git-hooks": { "pre-commit": "npx lint-staged" }, "lint-staged": { "*": "eslint --fix" } } ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript #!/usr/bin/env node import process from "node:process" import { FastMCP } from "fastmcp" import { z } from "zod" import { $fetch } from "ofetch" import { config } from "dotenv" import packageJson from "../package.json" import { description } from "./process" import type { SourceResponse } from "./typing" config() if (!process.env.BASE_URL) { console.error("BASE_URL is not set") process.exit(1) } const baseUrl = process.env.BASE_URL const server = new FastMCP({ name: "NewsNow", version: packageJson.version as `${number}.${number}.${number}`, }) server.addTool({ name: "get_hottest_latest_news", description: `get hottest or latest news from source by {id}, return {count: 10} news.`, parameters: z.object({ id: z.string().describe(`source id. e.g. ${description}`), count: z.any().default(10).describe("count of news to return."), }), execute: async ({ id, count }) => { let n = Number(count) if (Number.isNaN(n) || n < 1) { n = 10 } const res: SourceResponse = await $fetch(`${baseUrl}/api/s?id=${id}`, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", }, }) return { content: res.items.slice(0, count).map(item => ({ text: `[${item.title}](${item.url})`, type: "text", })), } }, }) server.start({ transportType: "stdio", }) ``` -------------------------------------------------------------------------------- /src/sources.json: -------------------------------------------------------------------------------- ```json { "v2ex": { "redirect": "v2ex-share", "name": "V2EX", "column": "tech", "home": "https://v2ex.com/", "color": "slate", "interval": 600000, "title": "最新分享" }, "v2ex-share": { "name": "V2EX", "column": "tech", "home": "https://v2ex.com/", "color": "slate", "interval": 600000, "title": "最新分享" }, "zhihu": { "name": "知乎", "type": "hottest", "column": "china", "home": "https://www.zhihu.com", "color": "blue", "interval": 600000 }, "weibo": { "title": "实时热搜", "name": "微博", "type": "hottest", "column": "china", "home": "https://weibo.com", "color": "red", "interval": 120000 }, "zaobao": { "name": "联合早报", "type": "realtime", "desc": "来自第三方网站: 早晨报", "column": "world", "home": "https://www.zaobao.com", "color": "red", "interval": 1800000 }, "coolapk": { "title": "今日最热", "name": "酷安", "type": "hottest", "column": "tech", "home": "https://coolapk.com", "color": "green", "interval": 600000 }, "mktnews": { "redirect": "mktnews-flash", "name": "MKTNews", "column": "finance", "home": "https://mktnews.net", "color": "indigo", "interval": 120000, "title": "快讯" }, "mktnews-flash": { "name": "MKTNews", "column": "finance", "home": "https://mktnews.net", "color": "indigo", "interval": 120000, "title": "快讯" }, "wallstreetcn": { "redirect": "wallstreetcn-quick", "name": "华尔街见闻", "type": "realtime", "column": "finance", "home": "https://wallstreetcn.com/", "color": "blue", "interval": 300000, "title": "实时快讯" }, "wallstreetcn-quick": { "name": "华尔街见闻", "type": "realtime", "column": "finance", "home": "https://wallstreetcn.com/", "color": "blue", "interval": 300000, "title": "实时快讯" }, "wallstreetcn-news": { "name": "华尔街见闻", "column": "finance", "home": "https://wallstreetcn.com/", "color": "blue", "interval": 1800000, "title": "最新资讯" }, "wallstreetcn-hot": { "name": "华尔街见闻", "type": "hottest", "column": "finance", "home": "https://wallstreetcn.com/", "color": "blue", "interval": 1800000, "title": "最热文章" }, "36kr": { "redirect": "36kr-quick", "name": "36氪", "type": "realtime", "disable": "cf", "column": "tech", "home": "https://36kr.com", "color": "blue", "interval": 600000, "title": "快讯" }, "36kr-quick": { "name": "36氪", "type": "realtime", "disable": "cf", "column": "tech", "home": "https://36kr.com", "color": "blue", "interval": 600000, "title": "快讯" }, "douyin": { "name": "抖音", "type": "hottest", "column": "china", "home": "https://www.douyin.com", "color": "gray", "interval": 600000 }, "hupu": { "title": "主干道热帖", "name": "虎扑", "type": "hottest", "column": "china", "home": "https://hupu.com", "color": "red", "interval": 600000 }, "tieba": { "title": "热议", "name": "百度贴吧", "type": "hottest", "column": "china", "home": "https://tieba.baidu.com", "color": "blue", "interval": 600000 }, "toutiao": { "name": "今日头条", "type": "hottest", "column": "china", "home": "https://www.toutiao.com", "color": "red", "interval": 600000 }, "ithome": { "name": "IT之家", "type": "realtime", "column": "tech", "home": "https://www.ithome.com", "color": "red", "interval": 600000 }, "thepaper": { "title": "热榜", "name": "澎湃新闻", "type": "hottest", "column": "china", "home": "https://www.thepaper.cn", "color": "gray", "interval": 1800000 }, "sputniknewscn": { "name": "卫星通讯社", "column": "world", "home": "https://sputniknews.cn", "color": "orange", "interval": 600000 }, "cankaoxiaoxi": { "name": "参考消息", "column": "world", "home": "https://china.cankaoxiaoxi.com", "color": "red", "interval": 1800000 }, "pcbeta": { "redirect": "pcbeta-windows11", "name": "远景论坛", "type": "realtime", "column": "tech", "home": "https://bbs.pcbeta.com", "color": "blue", "interval": 300000, "title": "Windows 11" }, "pcbeta-windows11": { "name": "远景论坛", "type": "realtime", "column": "tech", "home": "https://bbs.pcbeta.com", "color": "blue", "interval": 300000, "title": "Windows 11" }, "cls": { "redirect": "cls-telegraph", "name": "财联社", "type": "realtime", "column": "finance", "home": "https://www.cls.cn", "color": "red", "interval": 300000, "title": "电报" }, "cls-telegraph": { "name": "财联社", "type": "realtime", "column": "finance", "home": "https://www.cls.cn", "color": "red", "interval": 300000, "title": "电报" }, "cls-depth": { "name": "财联社", "column": "finance", "home": "https://www.cls.cn", "color": "red", "interval": 600000, "title": "深度" }, "cls-hot": { "name": "财联社", "type": "hottest", "column": "finance", "home": "https://www.cls.cn", "color": "red", "interval": 600000, "title": "热门" }, "xueqiu": { "redirect": "xueqiu-hotstock", "name": "雪球", "type": "hottest", "column": "finance", "home": "https://xueqiu.com", "color": "blue", "interval": 120000, "title": "热门股票" }, "xueqiu-hotstock": { "name": "雪球", "type": "hottest", "column": "finance", "home": "https://xueqiu.com", "color": "blue", "interval": 120000, "title": "热门股票" }, "gelonghui": { "title": "事件", "name": "格隆汇", "type": "realtime", "column": "finance", "home": "https://www.gelonghui.com", "color": "blue", "interval": 120000 }, "fastbull": { "redirect": "fastbull-express", "name": "法布财经", "type": "realtime", "column": "finance", "home": "https://www.fastbull.cn", "color": "emerald", "interval": 120000, "title": "快讯" }, "fastbull-express": { "name": "法布财经", "type": "realtime", "column": "finance", "home": "https://www.fastbull.cn", "color": "emerald", "interval": 120000, "title": "快讯" }, "fastbull-news": { "name": "法布财经", "column": "finance", "home": "https://www.fastbull.cn", "color": "emerald", "interval": 1800000, "title": "头条" }, "solidot": { "name": "Solidot", "column": "tech", "home": "https://solidot.org", "color": "teal", "interval": 3600000 }, "hackernews": { "name": "Hacker News", "type": "hottest", "column": "tech", "home": "https://news.ycombinator.com/", "color": "orange", "interval": 600000 }, "producthunt": { "name": "Product Hunt", "type": "hottest", "column": "tech", "home": "https://www.producthunt.com/", "color": "red", "interval": 600000 }, "github": { "redirect": "github-trending-today", "name": "Github", "type": "hottest", "column": "tech", "home": "https://github.com/", "color": "gray", "interval": 600000, "title": "Today" }, "github-trending-today": { "name": "Github", "type": "hottest", "column": "tech", "home": "https://github.com/", "color": "gray", "interval": 600000, "title": "Today" }, "bilibili": { "redirect": "bilibili-hot-search", "name": "哔哩哔哩", "type": "hottest", "column": "china", "home": "https://www.bilibili.com", "color": "blue", "interval": 600000, "title": "热搜" }, "bilibili-hot-search": { "name": "哔哩哔哩", "type": "hottest", "column": "china", "home": "https://www.bilibili.com", "color": "blue", "interval": 600000, "title": "热搜" }, "bilibili-hot-video": { "name": "哔哩哔哩", "type": "hottest", "disable": "cf", "column": "china", "home": "https://www.bilibili.com", "color": "blue", "interval": 600000, "title": "热门视频" }, "bilibili-ranking": { "name": "哔哩哔哩", "type": "hottest", "disable": "cf", "column": "china", "home": "https://www.bilibili.com", "color": "blue", "interval": 1800000, "title": "排行榜" }, "kuaishou": { "name": "快手", "type": "hottest", "disable": "cf", "column": "china", "home": "https://www.kuaishou.com", "color": "orange", "interval": 600000 }, "kaopu": { "name": "靠谱新闻", "desc": "不一定靠谱,多看多思考", "column": "world", "home": "https://kaopu.news/", "color": "gray", "interval": 1800000 }, "jin10": { "name": "金十数据", "type": "realtime", "column": "finance", "home": "https://www.jin10.com", "color": "blue", "interval": 600000 }, "baidu": { "name": "百度热搜", "type": "hottest", "column": "china", "home": "https://www.baidu.com", "color": "blue", "interval": 600000 }, "nowcoder": { "name": "牛客", "type": "hottest", "column": "china", "home": "https://www.nowcoder.com", "color": "blue", "interval": 600000 }, "sspai": { "name": "少数派", "type": "hottest", "column": "tech", "home": "https://sspai.com", "color": "red", "interval": 600000 }, "juejin": { "name": "稀土掘金", "type": "hottest", "column": "tech", "home": "https://juejin.cn", "color": "blue", "interval": 600000 }, "ifeng": { "title": "热点资讯", "name": "凤凰网", "type": "hottest", "column": "china", "home": "https://www.ifeng.com", "color": "red", "interval": 600000 }, "chongbuluo": { "redirect": "chongbuluo-latest", "name": "虫部落", "column": "china", "home": "https://www.chongbuluo.com/forum.php?mod=guide&view=newthread", "color": "green", "interval": 1800000, "title": "最新" }, "chongbuluo-latest": { "name": "虫部落", "column": "china", "home": "https://www.chongbuluo.com/forum.php?mod=guide&view=newthread", "color": "green", "interval": 1800000, "title": "最新" }, "chongbuluo-hot": { "name": "虫部落", "type": "hottest", "column": "china", "home": "https://www.chongbuluo.com/forum.php?mod=guide&view=hot", "color": "green", "interval": 1800000, "title": "最热" } } ```