markov-exa-mcp-server
Version:
A Model Context Protocol server with Exa for web search, academic paper search, and Twitter/X.com search. Provides real-time web searches with configurable tool selection, allowing users to enable or disable specific search capabilities. Supports customiz
148 lines (147 loc) • 7.31 kB
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Import tool implementations
import { registerWebSearchTool } from "./tools/webSearch.js";
import { registerResearchPaperSearchTool } from "./tools/researchPaperSearch.js";
import { registerCompanyResearchTool } from "./tools/companyResearch.js";
import { registerCrawlingTool } from "./tools/crawling.js";
import { registerCompetitorFinderTool } from "./tools/competitorFinder.js";
import { registerLinkedInSearchTool } from "./tools/linkedInSearch.js";
import { registerWikipediaSearchTool } from "./tools/wikipediaSearch.js";
import { registerGithubSearchTool } from "./tools/githubSearch.js";
import { registerResearchTaskTools } from "./tools/researchTasks.js";
import { registerContentsTool } from "./tools/contents.js";
import { registerWebsetsTools } from "./tools/websets.js";
import { registerOpenAIDeepResearchTools } from "./tools/openaiDeepResearch.js";
import { registerOpenAISparringTools } from "./tools/openaiSparring.js";
import { log } from "./utils/logger.js";
// Configuration schema for the EXA API key and tool selection
export const configSchema = z.object({
exaApiKey: z.string().optional().describe("Exa AI API key for search operations"),
enabledTools: z.array(z.string()).optional().describe("List of tools to enable (if not specified, all tools are enabled)"),
debug: z.boolean().default(false).describe("Enable debug logging")
});
// Tool registry for managing available tools
const availableTools = {
'web_search_exa': { name: 'Web Search (Exa)', description: 'Real-time web search using Exa AI', enabled: true },
'research_paper_search_exa': { name: 'Research Paper Search', description: 'Search academic papers and research', enabled: true },
'company_research_exa': { name: 'Company Research', description: 'Research companies and organizations', enabled: true },
'crawling_exa': { name: 'Web Crawling', description: 'Crawl and extract content from web pages', enabled: true },
'competitor_finder_exa': { name: 'Competitor Finder', description: 'Find competitors for companies', enabled: true },
'linkedin_search_exa': { name: 'LinkedIn Search', description: 'Search LinkedIn profiles and content', enabled: true },
'wikipedia_search_exa': { name: 'Wikipedia Search', description: 'Search Wikipedia articles', enabled: true },
'github_search_exa': { name: 'GitHub Search', description: 'Search GitHub repositories and code', enabled: true },
'research_tasks_exa': { name: 'Research Tasks', description: 'Create and manage AI research tasks', enabled: true },
'contents_exa': { name: 'Contents Retrieval', description: 'Retrieve content from specific URLs', enabled: true },
'websets_exa': { name: 'Websets', description: 'Create and manage websets for continuous data collection', enabled: true },
'openai_deep_research': { name: 'OpenAI Deep Research', description: 'Execute actual research tasks with web search using o3/o4-mini deep research models', enabled: true },
'openai_sparring': { name: 'OpenAI Sparring', description: 'Refine research methodology through Q&A with o3 (creates research plans, not results)', enabled: true }
};
// Process ENABLED_TOOLS environment variable
const enabledToolsEnv = process.env.ENABLED_TOOLS;
if (enabledToolsEnv) {
// Disable all tools first
Object.keys(availableTools).forEach(toolKey => {
availableTools[toolKey].enabled = false;
});
// Enable only specified tools
const enabledToolsList = enabledToolsEnv.split(',').map(tool => tool.trim());
enabledToolsList.forEach(toolName => {
if (availableTools[toolName]) {
availableTools[toolName].enabled = true;
log(`Enabled tool: ${toolName}`);
}
else {
log(`Warning: Unknown tool specified in ENABLED_TOOLS: ${toolName}`);
}
});
}
else {
log("All tools enabled by default");
}
// Main server setup
async function main() {
const args = process.argv.slice(2);
const transport = args.find(arg => arg.startsWith('--transport='))?.split('=')[1] || 'stdio';
log(`Starting MCP server with transport: ${transport}`);
const server = new McpServer({
name: "exa-mcp-server",
version: "2.0.0",
description: "MCP server for Exa AI search capabilities"
});
// Note: The MCP server handles configuration through the client's connection
// API key should be set via environment variable EXA_API_KEY
// Register enabled tools
const registerTools = () => {
if (availableTools.web_search_exa.enabled) {
registerWebSearchTool(server);
log("Registered Web Search tool");
}
if (availableTools.research_paper_search_exa.enabled) {
registerResearchPaperSearchTool(server);
log("Registered Research Paper Search tool");
}
if (availableTools.company_research_exa.enabled) {
registerCompanyResearchTool(server);
log("Registered Company Research tool");
}
if (availableTools.crawling_exa.enabled) {
registerCrawlingTool(server);
log("Registered Crawling tool");
}
if (availableTools.competitor_finder_exa.enabled) {
registerCompetitorFinderTool(server);
log("Registered Competitor Finder tool");
}
if (availableTools.linkedin_search_exa.enabled) {
registerLinkedInSearchTool(server);
log("Registered LinkedIn Search tool");
}
if (availableTools.wikipedia_search_exa.enabled) {
registerWikipediaSearchTool(server);
log("Registered Wikipedia Search tool");
}
if (availableTools.github_search_exa.enabled) {
registerGithubSearchTool(server);
log("Registered GitHub Search tool");
}
if (availableTools.research_tasks_exa.enabled) {
registerResearchTaskTools(server);
log("Registered Research Tasks tools");
}
if (availableTools.contents_exa.enabled) {
registerContentsTool(server);
log("Registered Contents Retrieval tool");
}
if (availableTools.websets_exa.enabled) {
registerWebsetsTools(server);
log("Registered Websets tools");
}
if (availableTools.openai_deep_research.enabled) {
registerOpenAIDeepResearchTools(server);
log("Registered OpenAI Deep Research tools");
}
if (availableTools.openai_sparring.enabled) {
registerOpenAISparringTools(server);
log("Registered OpenAI Sparring tools");
}
};
// Register tools initially
registerTools();
// Connect based on transport
if (transport === 'stdio') {
const stdioTransport = new StdioServerTransport();
await server.connect(stdioTransport);
log("Connected via stdio");
}
else {
throw new Error(`Unsupported transport: ${transport}`);
}
}
// Run the server
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});