#!/usr/bin/env node
/**
* MCP Domain Checker Price Server
*
* Model Context Protocol server for domain pricing and affiliate purchase links.
* Scrapes pricing from Joker.com and generates affiliate or regular purchase URLs.
*/
import { config } from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
// Load .env from project root
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const projectRoot = resolve(__dirname, '../../..');
config({ path: resolve(projectRoot, '.env') });
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 { getVersion } from './tools/get-version.js';
import { getDomainPricing } from './tools/get-domain-pricing.js';
import { openPurchaseLink } from './tools/open-purchase-link.js';
/**
* Tool definitions
*/
const TOOLS = [
{
name: 'get_version',
description: 'Get version information for this MCP server. Returns package version, capabilities, and usage notes. Safe to call anytime - read-only operation.',
inputSchema: {
type: 'object',
properties: {},
required: [],
},
},
{
name: 'get_domain_pricing',
description: 'Get domain registration pricing from Joker.com. Uses static pricing by default (fast, no auth). Set use_api=true for real-time pricing via DMAPI (requires credentials). Results are cached for 6 hours.',
inputSchema: {
type: 'object',
properties: {
domain: {
type: 'string',
description: 'The domain name to check pricing for (e.g., "example.com")',
},
force_refresh: {
type: 'boolean',
description: 'Bypass cache and fetch fresh pricing (default: false)',
default: false,
},
use_api: {
type: 'boolean',
description: 'Use real-time DMAPI pricing instead of static data (requires Joker.com credentials). Default: false',
default: false,
},
},
required: ['domain'],
},
},
{
name: 'open_purchase_link',
description: 'Generate Joker.com purchase link(s) for domain(s). Returns URLs with suggestion to open via mcp__opener__open_browser. Includes affiliate tracking if MCP_AFFILIATE_JOKER_ID is configured.',
inputSchema: {
type: 'object',
properties: {
domain: {
type: 'string',
description: 'Single domain to purchase (mutually exclusive with domains)',
},
domains: {
type: 'array',
items: { type: 'string' },
description: 'Multiple domains to purchase (mutually exclusive with domain)',
},
open_all: {
type: 'boolean',
description: 'If multiple domains: open all URLs (true) or just first one (false). Default: false',
default: false,
},
},
},
},
];
/**
* Initialize MCP server
*/
async function main() {
const server = new Server(
{
name: 'domain-checker-price-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: TOOLS,
}));
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'get_version': {
return await getVersion();
}
case 'get_domain_pricing': {
const domain = (args as { domain?: string })?.domain;
const force_refresh = (args as { force_refresh?: boolean })?.force_refresh;
const use_api = (args as { use_api?: boolean })?.use_api;
if (!domain) {
throw new Error('domain parameter is required');
}
return await getDomainPricing({ domain, force_refresh, use_api });
}
case 'open_purchase_link': {
const domain = (args as { domain?: string })?.domain;
const domains = (args as { domains?: string[] })?.domains;
const open_all = (args as { open_all?: boolean })?.open_all;
return await openPurchaseLink({ domain, domains, open_all });
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: 'text',
text: `Error: ${message}`,
},
],
isError: true,
};
}
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
// Keep process alive
process.stdin.resume();
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});