UNPKG

@burtthecoder/mcp-shodan

Version:

A Model Context Protocol server for Shodan API queries.

72 lines (71 loc) 3.22 kB
import { z } from "zod"; import { queryShodan } from "../helpers.js"; export function registerShodanSearch(server) { server.addTool({ name: "shodan_search", description: "Search Shodan's database of internet-connected devices. Returns detailed information about matching devices including services, vulnerabilities, and geographic distribution. Supports advanced search filters and returns country-based statistics.", parameters: z.object({ query: z.string().describe("Search query for Shodan."), max_results: z .number() .optional() .default(10) .describe("Maximum results to return."), }), annotations: { readOnlyHint: true, openWorldHint: true, }, execute: async (args) => { const result = await queryShodan("/shodan/host/search", { query: args.query, limit: args.max_results, }); const formattedResult = { "Search Summary": { Query: args.query, "Total Results": result.total, "Results Returned": result.matches.length, }, "Country Distribution": result.facets?.country?.map((country) => ({ Country: country.value, Count: country.count, Percentage: `${((country.count / result.total) * 100).toFixed(2)}%`, })) || [], Matches: result.matches.map((match) => ({ "Basic Information": { "IP Address": match.ip_str, Organization: match.org, ISP: match.isp, ASN: match.asn, "Last Update": match.timestamp, }, Location: { Country: match.location.country_name, City: match.location.city || "Unknown", Region: match.location.region_code || "Unknown", Coordinates: `${match.location.latitude}, ${match.location.longitude}`, }, "Service Details": { Port: match.port, Transport: match.transport, Product: match.product || "Unknown", Version: match.version || "Unknown", CPE: match.cpe || [], }, "Web Information": match.http ? { Server: match.http.server, Title: match.http.title, "Robots.txt": match.http.robots ? "Present" : "Not found", Sitemap: match.http.sitemap ? "Present" : "Not found", } : "No HTTP information", Hostnames: match.hostnames, Domains: match.domains, })), }; return JSON.stringify(formattedResult, null, 2); }, }); }