Shodan MCP Server

by X3r0K
Verified
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import axios from 'axios'; const API_KEY = process.env.SHODAN_API_KEY; // provided by MCP config if (!API_KEY) { throw new Error('SHODAN_API_KEY environment variable is required'); } const shodanApi = axios.create({ baseURL: 'https://api.shodan.io', timeout: 5000, params: { key: API_KEY }, }); class ShodanServer { private server: Server; constructor() { this.server = new Server({ name: 'shodan-mcp-server', version: '0.1.0', capabilities: { resources: {}, tools: {}, }, } ); this.setupToolHandlers(); this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'get_ip_info', description: 'Get information about a specific IP address', inputSchema: { type: 'object', properties: { ip: { type: 'string', description: 'The IP address to query' }, }, required: ['ip'], }, }, { name: 'dns_lookup', description: 'Perform DNS lookups for a given domain', inputSchema: { type: 'object', properties: { hostname: { type: 'string', description: 'The hostname to resolve' }, }, required: ['hostname'], }, }, { name: 'get_vulnerabilities', description: 'Track vulnerabilities associated with a specific IP address', inputSchema: { type: 'object', properties: { ip: { type: 'string', description: 'The IP address to query for vulnerabilities' }, }, required: ['ip'], }, }, { name: 'cve_info', description: 'Retrieve information about a specific CVE ID', inputSchema: { type: 'object', properties: { cve: { type: 'string', description: 'The CVE ID to query' }, }, required: ['cve'], }, }, { name: 'search', description: 'Search Shodan for devices matching a query', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'The search query' }, }, required: ['query'], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { switch (request.params.name) { case 'get_ip_info': return this.getIpInfo(String(request.params.arguments?.ip)); case 'dns_lookup': return this.dnsLookup(String(request.params.arguments?.hostname)); case 'get_vulnerabilities': return this.getVulnerabilities(String(request.params.arguments?.ip)); case 'cve_info': return this.getCveInfo(String(request.params.arguments?.cve)); case 'search': return this.search(String(request.params.arguments?.query)); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`); } } catch (error) { console.error(`Tool execution failed: ${error}`); return { content: [{ type: 'text', text: `Tool execution failed: ${error}` }], isError: true, }; } }); } private async getIpInfo(ip: string) { try { const response = await shodanApi.get(`/shodan/host/${ip}`); return { content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }] }; } catch (error: any) { throw new Error(`Shodan API error: ${error.message}`); } } private async dnsLookup(hostname: string) { try { const response = await shodanApi.get(`/dns/resolve`, { params: { hostnames: hostname } }); return { content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }] }; } catch (error: any) { throw new Error(`Shodan API error: ${error.message}`); } } private async getVulnerabilities(ip: string) { try { const response = await shodanApi.get(`/shodan/host/${ip}/vulns`); return { content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }] }; } catch (error: any) { throw new Error(`Shodan API error: ${error.message}`); } } private async getCveInfo(cve: string) { try { const response = await shodanApi.get(`/vulnerability/${cve}`); return { content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }] }; } catch (error: any) { throw new Error(`Shodan API error: ${error.message}`); } } private async search(query: string) { try { const response = await shodanApi.get(`/shodan/host/search`, { params: { query } }); return { content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }] }; } catch (error: any) { throw new Error(`Shodan API error: ${error.message}`); } } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Shodan MCP server running on stdio'); } } const server = new ShodanServer(); server.run().catch(console.error);