Skip to main content
Glama

MCP-RSS-Crawler

by mshk
index.ts9.16 kB
#!/usr/bin/env node import { Hono } from 'hono'; import { serve } from '@hono/node-server'; import rssManager from './rss-manager'; import dotenv from 'dotenv'; // Load environment variables from .env file dotenv.config(); const app = new Hono(); // Status endpoint app.get('/status', (c) => { return c.json({ status: 'ok', service: 'mcp-rss-manager' }); }); // MCP endpoint app.post('/mcp', async (c) => { try { const request = await c.req.json(); // Process the request using the MCP server // Since we don't have direct HTTP handling in the MCP SDK, // we'll manually process the request and format the response // The request should contain a method and params const { method, params } = request; let response; switch (method) { case 'fetchRssFeeds': const { limit } = params || { limit: 10 }; const feeds = await rssManager.crawlFeed(limit); response = { result: feeds }; break; case 'fetchArticle': if (!params?.url) { response = { error: { code: -32602, message: 'Invalid params: url is required' } }; } else { const article = await rssManager.fetchArticleFromUrl(params.url); response = { result: article }; } break; case 'crawlWebsite': if (!params?.url) { response = { error: { code: -32602, message: 'Invalid params: url is required' } }; } else { const result = await rssManager.crawlWebsite(params.url, params.limit || 100); response = { result }; } break; case 'asyncCrawlWebsite': if (!params?.url) { response = { error: { code: -32602, message: 'Invalid params: url is required' } }; } else { const result = await rssManager.asyncCrawlWebsite(params.url, params.limit || 100); response = { result }; } break; case 'checkCrawlStatus': if (!params?.id) { response = { error: { code: -32602, message: 'Invalid params: id is required' } }; } else { const result = await rssManager.checkCrawlStatus(params.id); response = { result }; } break; case 'cancelCrawl': if (!params?.id) { response = { error: { code: -32602, message: 'Invalid params: id is required' } }; } else { const result = await rssManager.cancelCrawl(params.id); response = { result }; } break; default: response = { error: { code: -32601, message: 'Method not found' } }; } return c.json(response); } catch (error) { console.error('Error processing MCP request:', error); return c.json({ error: { code: -32603, message: 'Internal error', data: error instanceof Error ? error.message : String(error) } }, 500); } }); // API endpoint to get latest feeds app.get('/api/feeds', async (c) => { try { // Get the limit parameter from the query string, default to 10 const limitParam = c.req.query('limit'); const limit = limitParam ? parseInt(limitParam) : 10; // Get the latest feeds from RSS Manager const feeds = await rssManager.getLatestArticles(limit); return c.json({ status: 'success', count: feeds.items.length, feeds }); } catch (error: any) { console.error('Error fetching feeds:', error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // API endpoint to get feeds by category app.get('/api/feeds/category/:category', async (c) => { try { const category = c.req.param('category'); const limitParam = c.req.query('limit'); const limit = limitParam ? parseInt(limitParam) : 10; const feeds = await rssManager.getFeedsByCategory(category, limit); return c.json({ status: 'success', category, count: feeds.items.length, feeds }); } catch (error: any) { console.error(`Error fetching feeds for category ${c.req.param('category')}:`, error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // API endpoint to search feeds app.get('/api/feeds/search', async (c) => { try { const query = c.req.query('q'); if (!query) { return c.json({ status: 'error', message: 'Search query is required' }, 400); } const limitParam = c.req.query('limit'); const limit = limitParam ? parseInt(limitParam) : 10; const feeds = await rssManager.searchFeeds(query, limit); return c.json({ status: 'success', query, count: feeds.items.length, feeds }); } catch (error: any) { console.error(`Error searching feeds:`, error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // API endpoint to fetch an article from a URL app.post('/api/articles/fetch', async (c) => { try { const { url } = await c.req.json(); if (!url) { return c.json({ status: 'error', message: 'URL is required' }, 400); } const article = await rssManager.fetchArticleFromUrl(url); if (!article) { return c.json({ status: 'error', message: 'Failed to fetch article' }, 500); } return c.json({ status: 'success', article }); } catch (error: any) { console.error(`Error fetching article:`, error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // API endpoint to crawl a website app.post('/api/crawl', async (c) => { try { const { url, limit } = await c.req.json(); if (!url) { return c.json({ status: 'error', message: 'URL is required' }, 400); } const result = await rssManager.crawlWebsite(url, limit || 100); return c.json({ status: result.success ? 'success' : 'error', ...result }); } catch (error: any) { console.error(`Error crawling website:`, error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // API endpoint to start an asynchronous crawl app.post('/api/crawl/async', async (c) => { try { const { url, limit } = await c.req.json(); if (!url) { return c.json({ status: 'error', message: 'URL is required' }, 400); } const result = await rssManager.asyncCrawlWebsite(url, limit || 100); return c.json({ status: result.success ? 'success' : 'error', ...result }); } catch (error: any) { console.error(`Error starting async crawl:`, error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // API endpoint to check crawl status app.get('/api/crawl/status/:id', async (c) => { try { const id = c.req.param('id'); if (!id) { return c.json({ status: 'error', message: 'Crawl ID is required' }, 400); } const result = await rssManager.checkCrawlStatus(id); return c.json({ status: result.success ? 'success' : 'error', ...result }); } catch (error: any) { console.error(`Error checking crawl status:`, error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // API endpoint to cancel a crawl app.post('/api/crawl/cancel/:id', async (c) => { try { const id = c.req.param('id'); if (!id) { return c.json({ status: 'error', message: 'Crawl ID is required' }, 400); } const result = await rssManager.cancelCrawl(id); return c.json({ status: result.success ? 'success' : 'error', ...result }); } catch (error: any) { console.error(`Error cancelling crawl:`, error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // API endpoint to list all configured feeds app.get('/api/feeds/list', async (c) => { try { const feeds = await rssManager.getFeeds(); return c.json({ status: 'success', count: feeds.length, feeds }); } catch (error: any) { console.error('Error listing feeds:', error); return c.json({ status: 'error', message: error.message, error: error.toString() }, 500); } }); // Start the server serve({ fetch: app.fetch, port: process.env.PORT ? parseInt(process.env.PORT) : 5556, }, (info) => { console.error(`RSS Manager server is running at http://localhost:${info.port}`); console.error(`MCP endpoint available at http://localhost:${info.port}/mcp`); console.error(`API endpoints available at http://localhost:${info.port}/api/feeds`); });

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