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
JavaScript
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