Skip to main content
Glama

NewsDigest MCP

by SomeiLam
index.ts5.06 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { generateMarkdown, normalizeCountries, normalizeLanguages, fetchNews, CountryCode, LanguageCode, CategoryEnum, normalizeKeywords, } from "./fetchNewsUtils"; import dotenv from 'dotenv'; import { sendEmail } from "./sendGmailUtils"; dotenv.config(); const server = new McpServer({ name: "NewsDigest MCP", version: "1.0.0", }); declare const Buffer // Tool #1: fetch headlines server.tool( "get-news-digest", { // union of all fetch params: countries: z.array(CountryCode).optional(), languages: z.array(LanguageCode).optional(), category: CategoryEnum.optional(), keywords: z.union([z.string(), z.array(z.string())]).optional(), from: z.string().optional(), to: z.string().optional(), sortBy: z.enum(["relevancy","popularity","publishedAt"]).optional(), pageSize: z.number().int().min(1).max(100).optional(), }, { title: "Get News Digest", description: "Fetches news (by country/language/category or by keywords) and returns a ready-to-render Markdown digest.", idempotentHint: true, }, async (args) => { // 1) normalize & fetch const cc = args.countries ? normalizeCountries(args.countries) : []; const lc = args.languages ? normalizeLanguages(args.languages) : []; const news = await fetchNews(cc, args.category, lc, args.keywords); // 2) format const md = await generateMarkdown(news); const sentEmail = await sendEmail(md) // 3) return Markdown directly return { content: [ { type: "resource" as const, resource: { text: md, uri: 'data:text/markdown;base64,' + Buffer.from(md).toString('base64'), mimeType: 'text/markdown', }, }, ], }; } ); // Tool #2: summarize & format server.tool( 'format-news-markdown', { newsData: z.any() }, async ({ newsData }) => { const md = await generateMarkdown(newsData); return { content: [ { type: "resource", resource: { text: md, uri: 'data:text/markdown;base64,' + Buffer.from(md).toString('base64'), mimeType: 'text/markdown', }, }, ], }; } ); // Tool #3: search by keywords server.tool( "search-everything", { keywords: z .union([z.string().min(1), z.array(z.string().min(1))]) .describe( "One or more search terms (string or array). Multiple terms will be OR‑joined." ), language: z .string() .optional() .describe( "Optional 2‑letter ISO‑639‑1 code to filter articles by language (e.g. 'en', 'jp')." ), from: z .string() .regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD") .optional() .describe("Optional start date for the time window (inclusive)"), to: z .string() .regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD") .optional() .describe("Optional end date for the time window (inclusive)"), sortBy: z .enum(["relevancy", "popularity", "publishedAt"] as const) .optional() .describe("How to sort results: relevancy, popularity, or publishedAt"), pageSize: z .number() .int() .min(1) .max(100) .optional() .describe("Number of results to return (1–100)"), }, { // ---- annotations object (second overload) ---- title: "Search News by Keywords", description: "Use this tool when the user asks to search news by arbitrary keywords (e.g. “What’s happening with AI?”).", openWorldHint: true, }, async ({ keywords, language, from, to, sortBy, pageSize }) => { const q = normalizeKeywords(keywords); const url = new URL("https://newsapi.org/v2/everything"); url.searchParams.set("q", q); if (language) url.searchParams.set("language", language.toLowerCase()); if (from) url.searchParams.set("from", from); if (to) url.searchParams.set("to", to); if (sortBy) url.searchParams.set("sortBy", sortBy); url.searchParams.set("pageSize", String(pageSize ?? 10)); url.searchParams.set("apiKey", process.env.NEWSAPI_KEY!); const resp = await fetch(url.toString()); if (!resp.ok) { const err = await resp.text(); throw new Error(`NewsAPI error ${resp.status}: ${err}`); } const { articles } = await resp.json(); // wrap as a resource so MCP accepts the JSON const text = JSON.stringify(articles, null, 2); return { content: [ { type: "resource" as const, resource: { text, uri: "data:application/json;base64," + Buffer.from(text).toString("base64"), mimeType: "application/json", }, }, ], }; } ); // const transport = new StdioServerTransport(); // await server.connect(transport);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SomeiLam/news-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server