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
112 lines (111 loc) • 4.14 kB
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import dotenv from "dotenv";
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
// Import the tool registry system
import { toolRegistry } from "./tools/index.js";
import { log } from "./utils/logger.js";
dotenv.config();
// Parse command line arguments to determine which tools to enable
const argv = yargs(hideBin(process.argv))
.option('tools', {
type: 'string',
description: 'Comma-separated list of tools to enable (if not specified, all enabled-by-default tools are used)',
default: ''
})
.option('list-tools', {
type: 'boolean',
description: 'List all available tools and exit',
default: false
})
.help()
.argv;
// Convert comma-separated string to Set for easier lookups
const argvObj = argv;
const toolsString = argvObj['tools'] || '';
const specifiedTools = new Set(toolsString ? toolsString.split(',').map((tool) => tool.trim()) : []);
// List all available tools if requested
if (argvObj['list-tools']) {
console.log("Available tools:");
Object.entries(toolRegistry).forEach(([id, tool]) => {
console.log(`- ${id}: ${tool.name}`);
console.log(` Description: ${tool.description}`);
console.log(` Enabled by default: ${tool.enabled ? 'Yes' : 'No'}`);
console.log();
});
process.exit(0);
}
// Check for API key after handling list-tools to allow listing without a key
const API_KEY = process.env.EXA_API_KEY;
if (!API_KEY) {
throw new Error("EXA_API_KEY environment variable is required");
}
/**
* Exa AI Web Search MCP Server
*
* This MCP server integrates Exa AI's search capabilities with Claude and other MCP-compatible clients.
* Exa is a search engine and API specifically designed for up-to-date web searching and retrieval,
* offering more recent and comprehensive results than what might be available in an LLM's training data.
*
* The server provides tools that enable:
* - Real-time web searching with configurable parameters
* - Research paper searches
* - And more to come!
*/
class ExaServer {
server;
constructor() {
this.server = new McpServer({
name: "exa-search-server",
version: "0.3.10"
});
log("Server initialized");
}
setupTools() {
// Register tools based on specifications
const registeredTools = [];
Object.entries(toolRegistry).forEach(([toolId, tool]) => {
// If specific tools were provided, only enable those.
// Otherwise, enable all tools marked as enabled by default
const shouldRegister = specifiedTools.size > 0
? specifiedTools.has(toolId)
: tool.enabled;
if (shouldRegister) {
this.server.tool(tool.name, tool.description, tool.schema, tool.handler);
registeredTools.push(toolId);
}
});
return registeredTools;
}
async run() {
try {
// Set up tools before connecting
const registeredTools = this.setupTools();
log(`Starting Exa MCP server with ${registeredTools.length} tools: ${registeredTools.join(', ')}`);
const transport = new StdioServerTransport();
// Handle connection errors
transport.onerror = (error) => {
log(`Transport error: ${error.message}`);
};
await this.server.connect(transport);
log("Exa Search MCP server running on stdio");
}
catch (error) {
log(`Server initialization error: ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
}
// Create and run the server with proper error handling
(async () => {
try {
const server = new ExaServer();
await server.run();
}
catch (error) {
log(`Fatal server error: ${error instanceof Error ? error.message : String(error)}`);
process.exit(1);
}
})();