apex-data-for-seo
Version:
A comprehensive MCP server for DataForSEO API
171 lines (153 loc) • 5.12 kB
JavaScript
const { program } = require('commander');
const readline = require('readline');
const axios = require('axios');
const fs = require('fs');
const { version } = require('../package.json');
program
.name('apex-data-for-seo')
.description('A comprehensive stdio MCP server for DataForSEO API')
.version(version)
.option('--dataforseo-login <login>', 'DataForSEO login')
.option('--dataforseo-password <password>', 'DataForSEO password');
program.parse();
const options = program.opts();
// Set environment variables
const env = { ...process.env };
if (options.dataforseoLogin) env.DATAFORSEO_LOGIN = options.dataforseoLogin;
if (options.dataforseoPassword) env.DATAFORSEO_PASSWORD = options.dataforseoPassword;
// Validate required credentials
if (!env.DATAFORSEO_LOGIN || !env.DATAFORSEO_PASSWORD) {
console.error('Error: DataForSEO API credentials are required');
console.error('Please provide them via environment variables or command line arguments:');
console.error(' --dataforseo-login YOUR_LOGIN --dataforseo-password YOUR_PASSWORD');
console.error(' or');
console.error(' DATAFORSEO_LOGIN=your_login DATAFORSEO_PASSWORD=your_password npx apex-data-for-seo');
process.exit(1);
}
// Create API client
const apiClient = axios.create({
baseURL: 'https://api.dataforseo.com/v3',
auth: {
username: env.DATAFORSEO_LOGIN,
password: env.DATAFORSEO_PASSWORD
},
headers: {
'Content-Type': 'application/json'
}
});
// Set up readline interface for stdin/stdout
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
// MCP tools implementation
const tools = {
// SERP API tools
'DataForSEO_GoogleSearch': async (params) => {
try {
const response = await apiClient.post('/serp/google/organic/live', [{
keyword: params.keyword,
location_name: params.location_name || 'United States',
language_name: params.language_name || 'English',
device: params.device || 'desktop'
}]);
return response.data;
} catch (error) {
return { error: error.message };
}
},
'DataForSEO_KeywordData': async (params) => {
try {
const response = await apiClient.post('/keywords_data/google/search_volume/live', [{
keywords: [params.keyword],
location_name: params.location_name || 'United States',
language_name: params.language_name || 'English'
}]);
return response.data;
} catch (error) {
return { error: error.message };
}
},
'DataForSEO_DomainOverview': async (params) => {
try {
const response = await apiClient.post('/domain_analytics/domain_overview/live', [{
target: params.domain,
location_name: params.location_name || 'United States',
language_name: params.language_name || 'English'
}]);
return response.data;
} catch (error) {
return { error: error.message };
}
}
};
// Process each line from stdin
rl.on('line', async (line) => {
try {
// Parse the incoming JSON request
const request = JSON.parse(line);
let response;
// Handle different request types
if (request.type === 'invoke') {
const { name, parameters } = request;
if (tools[name]) {
const result = await tools[name](parameters);
response = {
type: 'invoke_response',
id: request.id,
status: 'success',
name,
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} else {
response = {
type: 'invoke_response',
id: request.id,
status: 'error',
error: `Tool ${name} not found`
};
}
} else if (request.type === 'capabilities') {
// MCP capabilities request
response = {
type: 'capabilities_response',
id: request.id,
capabilities: {
tools: Object.keys(tools).map(name => ({
name,
description: `DataForSEO ${name.replace('DataForSEO_', '')} API`
}))
}
};
} else {
response = {
type: 'error',
id: request.id,
error: 'Unsupported request type'
};
}
// Send response back to stdout
console.log(JSON.stringify(response));
} catch (error) {
// Handle errors
console.error(`Error processing request: ${error.message}`);
const errorResponse = {
type: 'error',
error: error.message
};
console.log(JSON.stringify(errorResponse));
}
});
// Log startup information to stderr (won't affect the protocol)
console.error('DataForSEO MCP Server started and waiting for requests...');
console.error(`Using credentials: ${env.DATAFORSEO_LOGIN}`);
// Process SIGINT (Ctrl+C)
process.on('SIGINT', () => {
console.error('Shutting down...');
process.exit(0);
});