Skip to main content
Glama

MCP-RSS-Crawler

by mshk
mcp-server.ts20.9 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import rssManager from './rss-manager'; // MCPサーバーインスタンスの作成 export const server = new McpServer({ name: "RssFeedMCP", version: "0.1.0", }); // RSS Managerからフィードを取得するツール server.tool( "fetchRssFeeds", "Fetch articles from configured RSS feeds", { limit: z.number().min(1).max(50).default(10).describe("Number of articles to retrieve") }, async ({ limit }) => { try { // RSS Managerからフィードを取得 const feeds = await rssManager.crawlFeed(limit); return { content: [ { type: "text", text: JSON.stringify(feeds, null, 2), }, ], }; } catch (error: any) { console.error("Error fetching RSS feeds:", error.message); return { content: [ { type: "text", text: `Error fetching RSS feeds: ${error.message}`, }, ], isError: true }; } } ); // RSS Managerから最新のフィードを取得するツール server.tool( "getLatestRssFeeds", "Get the latest articles from configured RSS feeds", { limit: z.number().min(1).max(50).default(10).describe("Number of articles to retrieve") }, async ({ limit }) => { try { // RSS Managerから最新のフィードを取得 const feeds = await rssManager.getLatestArticles(limit); return { content: [ { type: "text", text: JSON.stringify(feeds, null, 2), }, ], }; } catch (error: any) { console.error("Error retrieving latest RSS feeds:", error.message); return { content: [ { type: "text", text: `Error retrieving latest RSS feeds: ${error.message}`, }, ], isError: true }; } } ); // RSS Managerからカテゴリ別のフィードを取得するツール server.tool( "fetchRssFeedsByCategory", "Fetch articles from configured RSS feeds by category", { category: z.string().describe("Category name to filter feeds by"), limit: z.number().min(1).max(50).default(10).describe("Number of articles to retrieve") }, async ({ category, limit }) => { try { // RSS Managerからカテゴリ別のフィードを取得 const feeds = await rssManager.getFeedsByCategory(category, limit); return { content: [ { type: "text", text: JSON.stringify(feeds, null, 2), }, ], }; } catch (error: any) { console.error("Error fetching RSS feeds by category:", error.message); return { content: [ { type: "text", text: `Error fetching RSS feeds by category: ${error.message}`, }, ], isError: true }; } } ); // RSS Managerでフィードを検索するツール server.tool( "searchRssFeeds", "Search for articles in configured RSS feeds", { query: z.string().describe("Search query"), limit: z.number().min(1).max(50).default(10).describe("Number of articles to retrieve") }, async ({ query, limit }) => { try { // RSS Managerでフィードを検索 const feeds = await rssManager.searchFeeds(query, limit); return { content: [ { type: "text", text: JSON.stringify(feeds, null, 2), }, ], }; } catch (error: any) { console.error("Error searching RSS feeds:", error.message); return { content: [ { type: "text", text: `Error searching RSS feeds: ${error.message}`, }, ], isError: true }; } } ); // RSSフィードの一覧を取得するツール server.tool( "listRssFeeds", "Get a list of all configured RSS feeds", {}, async () => { try { // RSS Managerからフィード一覧を取得 const feeds = await rssManager.getFeeds(); return { content: [ { type: "text", text: JSON.stringify(feeds, null, 2), }, ], }; } catch (error: any) { console.error("Error listing RSS feeds:", error.message); return { content: [ { type: "text", text: `Error listing RSS feeds: ${error.message}`, }, ], isError: true }; } } ); // RSSフィードを追加するツール server.tool( "addRssFeed", "Add a new RSS feed to the database", { url: z.string().url().describe("URL of the RSS feed"), name: z.string().describe("Name of the RSS feed"), category: z.string().optional().describe("Category of the RSS feed (optional)") }, async ({ url, name, category }) => { try { // RSS Managerにフィードを追加 const success = await rssManager.addFeed({ url, name, category }); if (success) { return { content: [ { type: "text", text: `Successfully added RSS feed: ${name} (${url})`, }, ], }; } else { return { content: [ { type: "text", text: `Failed to add RSS feed: ${name} (${url}). The feed may already exist.`, }, ], isError: true }; } } catch (error: any) { console.error("Error adding RSS feed:", error.message); return { content: [ { type: "text", text: `Error adding RSS feed: ${error.message}`, }, ], isError: true }; } } ); // RSSフィードを削除するツール server.tool( "removeRssFeed", "Remove an RSS feed from the database", { url: z.string().url().describe("URL of the RSS feed to remove") }, async ({ url }) => { try { // RSS Managerからフィードを削除 const success = await rssManager.removeFeed(url); if (success) { return { content: [ { type: "text", text: `Successfully removed RSS feed: ${url}`, }, ], }; } else { return { content: [ { type: "text", text: `Failed to remove RSS feed: ${url}. The feed may not exist.`, }, ], isError: true }; } } catch (error: any) { console.error("Error removing RSS feed:", error.message); return { content: [ { type: "text", text: `Error removing RSS feed: ${error.message}`, }, ], isError: true }; } } ); // ユーザーの興味キーワード一覧を取得するツール server.tool( "listKeywords", "Get a list of all user interest keywords", {}, async () => { try { // RSS Managerからキーワード一覧を取得 const keywords = await rssManager.getKeywords(); return { content: [ { type: "text", text: JSON.stringify(keywords, null, 2), }, ], }; } catch (error: any) { console.error("Error listing keywords:", error.message); return { content: [ { type: "text", text: `Error listing keywords: ${error.message}`, }, ], isError: true }; } } ); // ユーザーの興味キーワードを追加するツール server.tool( "addKeyword", "Add a new interest keyword to the database", { keyword: z.string().min(1).describe("Interest keyword to add") }, async ({ keyword }) => { try { // RSS Managerにキーワードを追加 const success = await rssManager.addKeyword(keyword); if (success) { return { content: [ { type: "text", text: `Successfully added interest keyword: "${keyword}"`, }, ], }; } else { return { content: [ { type: "text", text: `Failed to add interest keyword: "${keyword}". The keyword may already exist.`, }, ], isError: true }; } } catch (error: any) { console.error("Error adding keyword:", error.message); return { content: [ { type: "text", text: `Error adding keyword: ${error.message}`, }, ], isError: true }; } } ); // ユーザーの興味キーワードを削除するツール server.tool( "removeKeyword", "Remove an interest keyword from the database", { keyword: z.string().min(1).describe("Interest keyword to remove") }, async ({ keyword }) => { try { // RSS Managerからキーワードを削除 const success = await rssManager.removeKeyword(keyword); if (success) { return { content: [ { type: "text", text: `Successfully removed interest keyword: "${keyword}"`, }, ], }; } else { return { content: [ { type: "text", text: `Failed to remove interest keyword: "${keyword}". The keyword may not exist.`, }, ], isError: true }; } } catch (error: any) { console.error("Error removing keyword:", error.message); return { content: [ { type: "text", text: `Error removing keyword: ${error.message}`, }, ], isError: true }; } } ); // ユーザーの興味キーワードに一致する記事を取得するツール server.tool( "getArticlesByKeywords", "Get articles matching user interest keywords", { limit: z.number().min(1).max(50).default(10).describe("Number of articles to retrieve") }, async ({ limit }) => { try { // RSS Managerからキーワードに一致する記事を取得 const articles = await rssManager.getArticlesByKeywords(limit); return { content: [ { type: "text", text: JSON.stringify(articles, null, 2), }, ], }; } catch (error: any) { console.error("Error getting articles by keywords:", error.message); return { content: [ { type: "text", text: `Error getting articles by keywords: ${error.message}`, }, ], isError: true }; } } ); // Add a tool to fetch articles from a specified URL using firecrawl server.tool( "fetchArticle", "Fetch an article from a specified URL using firecrawl", { url: z.string().url().describe("URL of the article to fetch") }, async ({ url }) => { try { console.error(`Fetching article from URL: ${url}`); // Use the RSS manager to fetch the article const article = await rssManager.fetchArticleFromUrl(url); if (!article) { return { content: [ { type: "text", text: "Failed to fetch article from the URL." } ], isError: true }; } // Format the article content for the response return { content: [ { type: "text", text: `# ${article.title || 'Article'}\n\n${article.content || ''}` } ] }; } catch (error) { console.error(`Error in fetchArticle tool: ${error}`); return { content: [ { type: "text", text: `Error fetching article: ${error}` } ], isError: true }; } } ); // Add a tool to get all articles from the database server.tool( "getArticles", "Get all articles from the database", { limit: z.number().optional().describe("Maximum number of articles to return (default: 10)") }, async ({ limit = 10 }) => { try { console.error(`Getting articles with limit: ${limit}`); // Use the RSS manager to get articles const articles = await rssManager.getArticles(limit); if (!articles || articles.length === 0) { return { content: [ { type: "text", text: "No articles found in the database." } ] }; } // Format the articles for the response let responseText = "# Articles\n\n"; articles.forEach((article, index) => { responseText += `## ${index + 1}. ${article.title || 'Untitled Article'}\n`; responseText += `- URL: ${article.url}\n`; if (article.author) responseText += `- Author: ${article.author}\n`; if (article.published_date) responseText += `- Published: ${article.published_date}\n`; if (article.summary) responseText += `\n${article.summary}\n\n`; responseText += `---\n\n`; }); return { content: [ { type: "text", text: responseText } ] }; } catch (error) { console.error(`Error in getArticles tool: ${error}`); return { content: [ { type: "text", text: `Error getting articles: ${error}` } ], isError: true }; } } ); // Add a tool to search articles in the database server.tool( "searchArticles", "Search articles in the database", { query: z.string().describe("Search query"), limit: z.number().optional().describe("Maximum number of articles to return (default: 10)") }, async ({ query, limit = 10 }) => { try { console.error(`Searching articles with query: ${query}, limit: ${limit}`); // Use the RSS manager to search articles const articles = await rssManager.searchArticles(query, limit); if (!articles || articles.length === 0) { return { content: [ { type: "text", text: `No articles found matching the query: "${query}"` } ] }; } // Format the articles for the response let responseText = `# Search Results for "${query}"\n\n`; articles.forEach((article, index) => { responseText += `## ${index + 1}. ${article.title || 'Untitled Article'}\n`; responseText += `- URL: ${article.url}\n`; if (article.author) responseText += `- Author: ${article.author}\n`; if (article.published_date) responseText += `- Published: ${article.published_date}\n`; if (article.summary) responseText += `\n${article.summary}\n\n`; responseText += `---\n\n`; }); return { content: [ { type: "text", text: responseText } ] }; } catch (error) { console.error(`Error in searchArticles tool: ${error}`); return { content: [ { type: "text", text: `Error searching articles: ${error}` } ], isError: true }; } } ); // Add a tool to crawl a website using Firecrawl server.tool( "crawlWebsite", "Crawl a website and save all pages to the database", { url: z.string().url().describe("URL of the website to crawl"), limit: z.number().min(1).max(500).optional().describe("Maximum number of pages to crawl (default: 100)") }, async ({ url, limit = 100 }) => { try { console.error(`Crawling website: ${url} with limit: ${limit}`); // Use the RSS manager to crawl the website const result = await rssManager.crawlWebsite(url, limit); if (!result.success) { return { content: [ { type: "text", text: `Failed to crawl website: ${result.error || "Unknown error"}` } ], isError: true }; } // Format the result for the response return { content: [ { type: "text", text: `Successfully crawled website: ${url}\n\nTotal pages: ${result.totalPages}\nSaved pages: ${result.savedPages}` } ] }; } catch (error) { console.error(`Error in crawlWebsite tool: ${error}`); return { content: [ { type: "text", text: `Error crawling website: ${error}` } ], isError: true }; } } ); // Add a tool to asynchronously crawl a website using Firecrawl server.tool( "asyncCrawlWebsite", "Start an asynchronous crawl of a website", { url: z.string().url().describe("URL of the website to crawl"), limit: z.number().min(1).max(500).optional().describe("Maximum number of pages to crawl (default: 100)") }, async ({ url, limit = 100 }) => { try { console.error(`Starting async crawl of website: ${url} with limit: ${limit}`); // Use the RSS manager to start an async crawl const result = await rssManager.asyncCrawlWebsite(url, limit); if (!result.success) { return { content: [ { type: "text", text: `Failed to start async crawl: ${result.error || "Unknown error"}` } ], isError: true }; } // Format the result for the response return { content: [ { type: "text", text: `Successfully started async crawl of website: ${url}\n\nCrawl ID: ${result.id}\nStatus: ${result.status}\n\nYou can check the status of this crawl using the checkCrawlStatus tool with the ID.` } ] }; } catch (error) { console.error(`Error in asyncCrawlWebsite tool: ${error}`); return { content: [ { type: "text", text: `Error starting async crawl: ${error}` } ], isError: true }; } } ); // Add a tool to check the status of an asynchronous crawl server.tool( "checkCrawlStatus", "Check the status of an asynchronous crawl", { id: z.string().describe("ID of the crawl job to check") }, async ({ id }) => { try { console.error(`Checking status of crawl: ${id}`); // Use the RSS manager to check the crawl status const result = await rssManager.checkCrawlStatus(id); if (!result.success) { return { content: [ { type: "text", text: `Failed to check crawl status: ${result.error || "Unknown error"}` } ], isError: true }; } // Format the result for the response return { content: [ { type: "text", text: `Crawl Status for ID: ${id}\n\nStatus: ${result.status}\nPages Processed: ${result.pagesProcessed}\nTotal Pages: ${result.totalPages}` } ] }; } catch (error) { console.error(`Error in checkCrawlStatus tool: ${error}`); return { content: [ { type: "text", text: `Error checking crawl status: ${error}` } ], isError: true }; } } ); // Add a tool to cancel an asynchronous crawl server.tool( "cancelCrawl", "Cancel an asynchronous crawl job", { id: z.string().describe("ID of the crawl job to cancel") }, async ({ id }) => { try { console.error(`Cancelling crawl: ${id}`); // Use the RSS manager to cancel the crawl const result = await rssManager.cancelCrawl(id); if (!result.success) { return { content: [ { type: "text", text: `Failed to cancel crawl: ${result.error || "Unknown error"}` } ], isError: true }; } // Format the result for the response return { content: [ { type: "text", text: `Successfully cancelled crawl: ${id}` } ] }; } catch (error) { console.error(`Error in cancelCrawl tool: ${error}`); return { content: [ { type: "text", text: `Error cancelling crawl: ${error}` } ], isError: true }; } } );

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/mshk/mcp-rss-crawler'

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