UNPKG

allthingsdev-mcp-server

Version:

🚀 Official MCP server for AllThingsDev API marketplace. Discover, search, and integrate 600+ APIs directly through Claude Desktop, Cursor IDE, and other AI assistants. Features real-time API search, pricing comparison, endpoint documentation, and seamles

449 lines • 18.6 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { readFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { DataDecoder } from './data-decoder.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); class AllThingsDevMCPServer { server; apiData = []; faqData = []; constructor() { this.server = new Server({ name: "allthingsdev-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, }); this.loadData(); this.setupHandlers(); } loadData() { try { // Load morphed API data const apiDataPath = join(__dirname, '../API data.json'); const apiDataRaw = readFileSync(apiDataPath, 'utf-8'); const morphedData = JSON.parse(apiDataRaw); // Decode morphed structure back to usable format this.apiData = DataDecoder.extractAPIs(morphedData); // Load FAQ data const faqDataPath = join(__dirname, "../faq's.json"); const faqDataRaw = readFileSync(faqDataPath, 'utf-8'); this.faqData = JSON.parse(faqDataRaw); console.error(`Loaded ${this.apiData.length} APIs and ${this.faqData.length} FAQ sections`); } catch (error) { console.error('Error loading data:', error); } } setupHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "search_apis", description: "Search for APIs in the AllThingsDev marketplace by keywords, categories, or functionality", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query (keywords, API name, functionality, etc.)" }, limit: { type: "number", description: "Maximum number of results to return (default: 10)", default: 10 } }, required: ["query"] } }, { name: "get_api_details", description: "Get detailed information about a specific API including endpoints, pricing, and documentation", inputSchema: { type: "object", properties: { apiId: { type: "string", description: "API ID or API name to get details for" } }, required: ["apiId"] } }, { name: "list_api_categories", description: "Get a list of API categories and popular APIs in each category", inputSchema: { type: "object", properties: { showCount: { type: "boolean", description: "Whether to show count of APIs in each category", default: true } } } }, { name: "get_pricing_info", description: "Get pricing information for APIs, optionally filtered by price range", inputSchema: { type: "object", properties: { maxPrice: { type: "number", description: "Maximum price filter (optional)" }, planType: { type: "string", description: "Specific plan type (basic, pro, ultra, mega)", enum: ["basic", "pro", "ultra", "mega"] } } } }, { name: "search_faq", description: "Search AllThingsDev FAQ for answers to common questions", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query for FAQ content" } }, required: ["query"] } }, { name: "get_api_endpoints", description: "Get endpoint information for a specific API", inputSchema: { type: "object", properties: { apiId: { type: "string", description: "API ID or name to get endpoints for" } }, required: ["apiId"] } } ] }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (!args) { return { content: [ { type: "text", text: "Error: No arguments provided" } ] }; } try { switch (name) { case "search_apis": return await this.searchAPIs(args.query, args.limit || 10); case "get_api_details": return await this.getAPIDetails(args.apiId); case "list_api_categories": return await this.listAPICategories(args.showCount !== false); case "get_pricing_info": return await this.getPricingInfo(args.maxPrice, args.planType); case "search_faq": return await this.searchFAQ(args.query); case "get_api_endpoints": return await this.getAPIEndpoints(args.apiId); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` } ] }; } }); } async searchAPIs(query, limit) { const searchTerm = query.toLowerCase(); const matchedAPIs = this.apiData .filter(api => { const searchableContent = `${api.title} ${api.content} ${api.apiName}`.toLowerCase(); return searchableContent.includes(searchTerm); }) .slice(0, limit) .map(api => { // Extract key information from content const shortDesc = this.extractShortDescription(api.content); const keywords = this.extractKeywords(api.content); const pricing = this.extractPricing(api.content); return { id: api.id, name: api.apiName, title: api.title, shortDescription: shortDesc, keywords: keywords.slice(0, 5), // Show first 5 keywords pricing: pricing, endpointUrl: api.endpointUrl, documentationUrl: api.documentationUrl }; }); const resultText = matchedAPIs.length > 0 ? `Found ${matchedAPIs.length} APIs matching "${query}":\n\n` + matchedAPIs.map(api => `**${api.name}**\n` + `${api.shortDescription}\n` + `Keywords: ${api.keywords.join(', ')}\n` + `Pricing: ${api.pricing.map(p => `${p.name} ($${p.price})`).join(', ')}\n` + `Documentation: ${api.documentationUrl}\n`).join('\n---\n\n') : `No APIs found matching "${query}". Try different keywords or browse categories.`; return { content: [ { type: "text", text: resultText } ] }; } async getAPIDetails(apiId) { const api = this.apiData.find(a => a.id === apiId || a.apiId === apiId || a.apiName.toLowerCase() === apiId.toLowerCase() || a.title.toLowerCase() === apiId.toLowerCase()); if (!api) { return { content: [ { type: "text", text: `API not found: ${apiId}. Try searching for APIs first to get the correct ID.` } ] }; } const shortDesc = this.extractShortDescription(api.content); const longDesc = this.extractLongDescription(api.content); const keywords = this.extractKeywords(api.content); const endpoints = this.extractEndpoints(api.content); const pricing = this.extractPricing(api.content); const documentation = this.extractDocumentation(api.content); const detailText = `# ${api.apiName}\n\n` + `**ID:** ${api.id}\n` + `**Short Description:** ${shortDesc}\n\n` + `**Full Description:**\n${longDesc}\n\n` + `**Keywords:** ${keywords.join(', ')}\n\n` + `**Endpoints:**\n${endpoints.map(e => `- ${e.method} ${e.pathURL} - ${e.name}`).join('\n')}\n\n` + `**Pricing Plans:**\n${pricing.map(p => `- ${p.name}: $${p.price}/month (${p.rateLimit} requests)`).join('\n')}\n\n` + `**Links:**\n` + `- Documentation: ${api.documentationUrl}\n` + `- Endpoints: ${api.endpointUrl}\n` + `- Tutorial: ${api.tutorialUrl}\n` + `- Pricing: ${api.pricingUrl}\n` + `- Discussion: ${api.discussionUrl}\n\n` + `**API Documentation:**\n${documentation}`; return { content: [ { type: "text", text: detailText } ] }; } async listAPICategories(showCount) { // Group APIs by keywords to create categories const categories = new Map(); this.apiData.forEach(api => { const keywords = this.extractKeywords(api.content); keywords.forEach(keyword => { const key = keyword.toLowerCase(); if (!categories.has(key)) { categories.set(key, []); } categories.get(key).push(api); }); }); // Sort categories by API count const sortedCategories = Array.from(categories.entries()) .sort((a, b) => b[1].length - a[1].length) .slice(0, 20); // Top 20 categories const categoryText = `# API Categories in AllThingsDev Marketplace\n\n` + sortedCategories.map(([category, apis]) => { const count = showCount ? ` (${apis.length} APIs)` : ''; const examples = apis.slice(0, 3).map(api => api.apiName).join(', '); return `**${category}**${count}\nExamples: ${examples}`; }).join('\n\n'); return { content: [ { type: "text", text: categoryText } ] }; } async getPricingInfo(maxPrice, planType) { let filteredAPIs = this.apiData; if (maxPrice !== undefined || planType) { filteredAPIs = this.apiData.filter(api => { const pricing = this.extractPricing(api.content); if (planType) { return pricing.some(p => p.name.toLowerCase() === planType.toLowerCase()); } if (maxPrice !== undefined) { return pricing.some(p => p.price <= maxPrice); } return true; }); } const pricingData = filteredAPIs.slice(0, 20).map(api => { const pricing = this.extractPricing(api.content); return { name: api.apiName, pricing: pricing }; }); const pricingText = `# API Pricing Information\n\n` + (maxPrice ? `Filtered by max price: $${maxPrice}\n` : '') + (planType ? `Filtered by plan type: ${planType}\n` : '') + `\nShowing ${pricingData.length} APIs:\n\n` + pricingData.map(api => `**${api.name}**\n` + api.pricing.map(p => `- ${p.name}: $${p.price}/month (${p.rateLimit} requests)`).join('\n')).join('\n\n'); return { content: [ { type: "text", text: pricingText } ] }; } async searchFAQ(query) { const searchTerm = query.toLowerCase(); const matchedFAQs = []; this.faqData.forEach(section => { section.content.forEach(item => { if (item.question.toLowerCase().includes(searchTerm) || item.answer.toLowerCase().includes(searchTerm)) { matchedFAQs.push({ question: item.question, answer: item.answer, category: section.title }); } }); }); const faqText = matchedFAQs.length > 0 ? `# FAQ Results for "${query}"\n\n` + matchedFAQs.map(faq => `**Category:** ${faq.category}\n` + `**Q:** ${faq.question}\n` + `**A:** ${faq.answer}`).join('\n\n---\n\n') : `No FAQ entries found matching "${query}". Try different keywords.`; return { content: [ { type: "text", text: faqText } ] }; } async getAPIEndpoints(apiId) { const api = this.apiData.find(a => a.id === apiId || a.apiId === apiId || a.apiName.toLowerCase() === apiId.toLowerCase()); if (!api) { return { content: [ { type: "text", text: `API not found: ${apiId}` } ] }; } const endpoints = this.extractEndpoints(api.content); const endpointText = `# Endpoints for ${api.apiName}\n\n` + endpoints.map(endpoint => `**${endpoint.name}**\n` + `- Method: ${endpoint.method}\n` + `- Path: ${endpoint.pathURL}\n`).join('\n'); return { content: [ { type: "text", text: endpointText } ] }; } // Helper methods to extract information from API content extractShortDescription(content) { const match = content.match(/shortDescription - (.*?)(?:\n|longDescription)/s); return match ? match[1].trim() : 'No description available'; } extractLongDescription(content) { const match = content.match(/longDescription - (.*?)(?:\n|keyword)/s); return match ? match[1].trim() : this.extractShortDescription(content); } extractKeywords(content) { const match = content.match(/keyword - (.*?)(?:\n|Endpoints)/s); if (!match) return []; return match[1].split(',').map(k => k.trim()).filter(k => k.length > 0); } extractEndpoints(content) { const endpoints = []; const endpointMatches = content.matchAll(/name - (.*?)\nmethod - (.*?)\npathURL - (.*?)(?:\n|$)/g); for (const match of endpointMatches) { endpoints.push({ name: match[1].trim(), method: match[2].trim(), pathURL: match[3].trim() }); } return endpoints; } extractPricing(content) { const pricing = []; const pricingMatches = content.matchAll(/name - (.*?)\nrateLimit - (.*?)\nprice - (.*?)(?:\n|$)/g); for (const match of pricingMatches) { pricing.push({ name: match[1].trim(), rateLimit: match[2].trim(), price: parseInt(match[3].trim()) || 0 }); } return pricing; } extractDocumentation(content) { const match = content.match(/Documentation -(.*?)(?:\nPricing Plans|$)/s); return match ? match[1].trim() : 'No documentation available'; } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("AllThingsDev MCP server running on stdio"); } } const server = new AllThingsDevMCPServer(); server.run().catch(console.error); //# sourceMappingURL=index.js.map