@burtthecoder/mcp-shodan 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +101 -39
  2. package/build/index.js +264 -47
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Shodan MCP Server
2
2
 
3
- A Model Context Protocol (MCP) server for querying the [Shodan API](https://shodan.io) and [Shodan CVEDB](https://cvedb.shodan.io). This server provides tools for IP lookups, device searches, DNS lookups, vulnerability queries, CPE lookups, and more. It is designed to integrate seamlessly with MCP-compatible applications like [Claude Desktop](https://claude.ai).
3
+ A Model Context Protocol (MCP) server for querying the [Shodan API](https://shodan.io) and [Shodan CVEDB](https://cvedb.shodan.io). This server provides comprehensive access to Shodan's network intelligence and security services, including IP reconnaissance, DNS operations, vulnerability tracking, and device discovery. All tools provide structured, formatted output for easy analysis and integration.
4
4
 
5
5
  ## Quick Start (Recommended)
6
6
 
@@ -58,45 +58,78 @@ npm run build
58
58
 
59
59
  ## Features
60
60
 
61
- - **IP Lookup**: Retrieve detailed information about an IP address
62
- - **Search**: Search for devices on Shodan matching specific queries
63
- - **Ports**: Get a list of ports that Shodan is scanning
64
- - **CVE Lookup**: Fetch detailed information about specific CVEs using Shodan's CVEDB
65
- - **CPE Lookup**: Search for Common Platform Enumeration (CPE) entries by product name
66
- - **CVEs by Product**: Search for all CVEs affecting a specific product or CPE
67
- - **DNS Lookup**: Resolve hostnames to IP addresses
61
+ - **Network Reconnaissance**: Query detailed information about IP addresses, including open ports, services, and vulnerabilities
62
+ - **DNS Operations**: Forward and reverse DNS lookups for domains and IP addresses
63
+ - **Vulnerability Intelligence**: Access to Shodan's CVEDB for detailed vulnerability information, CPE lookups, and product-specific CVE tracking
64
+ - **Device Discovery**: Search Shodan's database of internet-connected devices with advanced filtering
68
65
 
69
66
  ## Tools
70
67
 
71
68
  ### 1. IP Lookup Tool
72
69
  - Name: `ip_lookup`
73
- - Description: Retrieve detailed information about an IP address
70
+ - Description: Retrieve comprehensive information about an IP address, including geolocation, open ports, running services, SSL certificates, hostnames, and cloud provider details if available
74
71
  - Parameters:
75
72
  * `ip` (required): IP address to lookup
76
-
77
- ### 2. Search Tool
78
- - Name: `search`
79
- - Description: Search for devices on Shodan
73
+ - Returns:
74
+ * IP Information (address, organization, ISP, ASN)
75
+ * Location (country, city, coordinates)
76
+ * Services (ports, protocols, banners)
77
+ * Cloud Provider details (if available)
78
+ * Associated hostnames and domains
79
+ * Tags
80
+
81
+ ### 2. Shodan Search Tool
82
+ - Name: `shodan_search`
83
+ - Description: Search Shodan's database of internet-connected devices
80
84
  - Parameters:
81
85
  * `query` (required): Shodan search query
82
86
  * `max_results` (optional, default: 10): Number of results to return
87
+ - Returns:
88
+ * Search summary with total results
89
+ * Country-based distribution statistics
90
+ * Detailed device information including:
91
+ - Basic information (IP, organization, ISP)
92
+ - Location data
93
+ - Service details
94
+ - Web server information
95
+ - Associated hostnames and domains
83
96
 
84
97
  ### 3. CVE Lookup Tool
85
98
  - Name: `cve_lookup`
86
- - Description: Fetch detailed information about CVEs using Shodan's CVEDB
99
+ - Description: Query detailed vulnerability information from Shodan's CVEDB
87
100
  - Parameters:
88
101
  * `cve` (required): CVE identifier in format CVE-YYYY-NNNNN (e.g., CVE-2021-44228)
89
102
  - Returns:
90
- * CVE details including:
91
- - CVSS v2 and v3 scores
92
- - EPSS score and ranking
103
+ * Basic Information (ID, published date, summary)
104
+ * Severity Scores:
105
+ - CVSS v2 and v3 with severity levels
106
+ - EPSS probability and ranking
107
+ * Impact Assessment:
93
108
  - KEV status
94
- - Proposed action
95
- - Ransomware campaign information
96
- - Affected products (CPEs)
97
- - References
109
+ - Proposed mitigations
110
+ - Ransomware associations
111
+ * Affected products (CPEs)
112
+ * References
113
+
114
+ ### 4. DNS Lookup Tool
115
+ - Name: `dns_lookup`
116
+ - Description: Resolve domain names to IP addresses using Shodan's DNS service
117
+ - Parameters:
118
+ * `hostnames` (required): Array of hostnames to resolve
119
+ - Returns:
120
+ * DNS resolutions mapping hostnames to IPs
121
+ * Summary of total lookups and queried hostnames
98
122
 
99
- ### 4. CPE Lookup Tool
123
+ ### 5. Reverse DNS Lookup Tool
124
+ - Name: `reverse_dns_lookup`
125
+ - Description: Perform reverse DNS lookups to find hostnames associated with IP addresses
126
+ - Parameters:
127
+ * `ips` (required): Array of IP addresses to lookup
128
+ - Returns:
129
+ * Reverse DNS resolutions mapping IPs to hostnames
130
+ * Summary of total lookups and results
131
+
132
+ ### 6. CPE Lookup Tool
100
133
  - Name: `cpe_lookup`
101
134
  - Description: Search for Common Platform Enumeration (CPE) entries by product name
102
135
  - Parameters:
@@ -108,9 +141,9 @@ npm run build
108
141
  * When count is true: Total number of matching CPEs
109
142
  * When count is false: List of CPEs with pagination details
110
143
 
111
- ### 5. CVEs by Product Tool
144
+ ### 7. CVEs by Product Tool
112
145
  - Name: `cves_by_product`
113
- - Description: Search for CVEs affecting a specific product or CPE
146
+ - Description: Search for vulnerabilities affecting specific products or CPEs
114
147
  - Parameters:
115
148
  * `cpe23` (optional): CPE 2.3 identifier (format: cpe:2.3:part:vendor:product:version)
116
149
  * `product` (optional): Name of the product to search for CVEs
@@ -125,14 +158,13 @@ npm run build
125
158
  * Must provide either cpe23 or product, but not both
126
159
  * Date filtering uses published time of CVEs
127
160
  - Returns:
128
- * When count is true: Total number of matching CVEs
129
- * When count is false: List of CVEs with pagination details and query parameters
130
-
131
- ### 6. DNS Lookup Tool
132
- - Name: `dns_lookup`
133
- - Description: Resolve hostnames to IP addresses
134
- - Parameters:
135
- * `hostnames` (required): Array of hostnames to resolve
161
+ * Query information
162
+ * Results summary with pagination details
163
+ * Detailed vulnerability information including:
164
+ - Basic information
165
+ - Severity scores
166
+ - Impact assessments
167
+ - References
136
168
 
137
169
  ## Requirements
138
170
 
@@ -143,15 +175,44 @@ npm run build
143
175
 
144
176
  ### API Key Issues
145
177
 
146
- If you see API key related errors:
178
+ If you see API key related errors (e.g., "Request failed with status code 401"):
147
179
 
148
180
  1. Verify your API key:
149
- - Should be a valid Shodan API key
150
- - No extra spaces or quotes around the key
151
- - Must be from your Shodan account settings
152
- 2. After any configuration changes:
153
- - Save the config file
154
- - Restart Claude Desktop
181
+ - Must be a valid Shodan API key from your [account settings](https://account.shodan.io/)
182
+ - Ensure the key has sufficient credits/permissions for the operation
183
+ - Check for extra spaces or quotes around the key in the configuration
184
+ - Verify the key is correctly set in the SHODAN_API_KEY environment variable
185
+
186
+ 2. Common Error Codes:
187
+ - 401 Unauthorized: Invalid API key or missing authentication
188
+ - 402 Payment Required: Out of query credits
189
+ - 429 Too Many Requests: Rate limit exceeded
190
+
191
+ 3. Configuration Steps:
192
+ a. Get your API key from [Shodan Account](https://account.shodan.io/)
193
+ b. Add it to your configuration file:
194
+ ```json
195
+ {
196
+ "mcpServers": {
197
+ "shodan": {
198
+ "command": "mcp-shodan",
199
+ "env": {
200
+ "SHODAN_API_KEY": "your-actual-api-key-here"
201
+ }
202
+ }
203
+ }
204
+ }
205
+ ```
206
+ c. Save the config file
207
+ d. Restart Claude Desktop
208
+
209
+ 4. Testing Your Key:
210
+ - Try a simple query first (e.g., dns_lookup for "google.com")
211
+ - Check your [Shodan account dashboard](https://account.shodan.io/) for credit status
212
+ - Verify the key works directly with curl:
213
+ ```bash
214
+ curl "https://api.shodan.io/dns/resolve?hostnames=google.com&key=your-api-key"
215
+ ```
155
216
 
156
217
  ### Module Loading Issues
157
218
 
@@ -180,6 +241,7 @@ The server includes comprehensive error handling for:
180
241
 
181
242
  ## Version History
182
243
 
244
+ - v1.0.12: Added reverse DNS lookup and improved output formatting
183
245
  - v1.0.7: Added CVEs by Product search functionality and renamed vulnerabilities tool to cve_lookup
184
246
  - v1.0.6: Added CVEDB integration for enhanced CVE lookups and CPE search functionality
185
247
  - v1.0.0: Initial release with core functionality
package/build/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
4
  import { CallToolRequestSchema, ListToolsRequestSchema, InitializeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
@@ -32,7 +33,7 @@ function logToFile(message) {
32
33
  const IpLookupArgsSchema = z.object({
33
34
  ip: z.string().describe("The IP address to query."),
34
35
  });
35
- const SearchArgsSchema = z.object({
36
+ const ShodanSearchArgsSchema = z.object({
36
37
  query: z.string().describe("Search query for Shodan."),
37
38
  max_results: z
38
39
  .number()
@@ -48,6 +49,9 @@ const CVELookupArgsSchema = z.object({
48
49
  const DnsLookupArgsSchema = z.object({
49
50
  hostnames: z.array(z.string()).describe("List of hostnames to resolve."),
50
51
  });
52
+ const ReverseDnsLookupArgsSchema = z.object({
53
+ ips: z.array(z.string()).describe("List of IP addresses to perform reverse DNS lookup on."),
54
+ });
51
55
  const CpeLookupArgsSchema = z.object({
52
56
  product: z.string().describe("The name of the product to search for CPEs."),
53
57
  count: z.boolean().optional().default(false).describe("If true, returns only the count of matching CPEs."),
@@ -150,7 +154,14 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
150
154
  name: "shodan-mcp",
151
155
  version: "1.0.0",
152
156
  },
153
- instructions: "This server provides tools for querying Shodan, including IP lookups, searches, CVE lookups, CPE lookups, and CVE searches by product/CPE.",
157
+ instructions: `This MCP server provides comprehensive access to Shodan's network intelligence and security services:
158
+
159
+ - Network Reconnaissance: Query detailed information about IP addresses, including open ports, services, and vulnerabilities
160
+ - DNS Operations: Forward and reverse DNS lookups for domains and IP addresses
161
+ - Vulnerability Intelligence: Access to Shodan's CVEDB for detailed vulnerability information, CPE lookups, and product-specific CVE tracking
162
+ - Device Discovery: Search Shodan's database of internet-connected devices with advanced filtering
163
+
164
+ Each tool provides structured, formatted output for easy analysis and integration.`,
154
165
  };
155
166
  });
156
167
  // Register Tools
@@ -158,34 +169,39 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
158
169
  const tools = [
159
170
  {
160
171
  name: "ip_lookup",
161
- description: "Retrieve information about an IP address.",
172
+ description: "Retrieve comprehensive information about an IP address, including geolocation, open ports, running services, SSL certificates, hostnames, and cloud provider details if available. Returns service banners and HTTP server information when present.",
162
173
  inputSchema: zodToJsonSchema(IpLookupArgsSchema),
163
174
  },
164
175
  {
165
- name: "search",
166
- description: "Search for devices on Shodan.",
167
- inputSchema: zodToJsonSchema(SearchArgsSchema),
176
+ name: "shodan_search",
177
+ 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.",
178
+ inputSchema: zodToJsonSchema(ShodanSearchArgsSchema),
168
179
  },
169
180
  {
170
181
  name: "cve_lookup",
171
- description: "Retrieve vulnerability information for a CVE. Use format: CVE-YYYY-NNNNN (e.g., CVE-2021-44228)",
182
+ description: "Query detailed vulnerability information from Shodan's CVEDB. Returns comprehensive CVE details including CVSS scores (v2/v3), EPSS probability and ranking, KEV status, proposed mitigations, ransomware associations, and affected products (CPEs).",
172
183
  inputSchema: zodToJsonSchema(CVELookupArgsSchema),
173
184
  },
174
185
  {
175
186
  name: "dns_lookup",
176
- description: "Perform DNS lookups using Shodan.",
187
+ description: "Resolve domain names to IP addresses using Shodan's DNS service. Supports batch resolution of multiple hostnames in a single query. Returns IP addresses mapped to their corresponding hostnames.",
177
188
  inputSchema: zodToJsonSchema(DnsLookupArgsSchema),
178
189
  },
179
190
  {
180
191
  name: "cpe_lookup",
181
- description: "Search for Common Platform Enumeration (CPE) entries by product name.",
192
+ description: "Search for Common Platform Enumeration (CPE) entries by product name in Shodan's CVEDB. Supports pagination and can return either full CPE details or just the total count. Useful for identifying specific versions and configurations of software and hardware.",
182
193
  inputSchema: zodToJsonSchema(CpeLookupArgsSchema),
183
194
  },
184
195
  {
185
196
  name: "cves_by_product",
186
- description: "Search for CVEs affecting a specific product or CPE. Provide either product name or CPE 2.3 identifier.",
197
+ description: "Search for vulnerabilities affecting specific products or CPEs. Supports filtering by KEV status, sorting by EPSS score, date ranges, and pagination. Can search by product name or CPE 2.3 identifier. Returns detailed vulnerability information including severity scores and impact assessments.",
187
198
  inputSchema: zodToJsonSchema(CVEsByProductArgsSchema),
188
199
  },
200
+ {
201
+ name: "reverse_dns_lookup",
202
+ description: "Perform reverse DNS lookups to find hostnames associated with IP addresses. Supports batch lookups of multiple IP addresses in a single query. Returns all known hostnames for each IP address, with clear indication when no hostnames are found.",
203
+ inputSchema: zodToJsonSchema(ReverseDnsLookupArgsSchema),
204
+ },
189
205
  ];
190
206
  logToFile("Registered tools.");
191
207
  return { tools };
@@ -202,17 +218,55 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
202
218
  throw new Error("Invalid ip_lookup arguments");
203
219
  }
204
220
  const result = await queryShodan(`/shodan/host/${parsedIpArgs.data.ip}`, {});
221
+ // Format the response in a user-friendly way
222
+ const formattedResult = {
223
+ "IP Information": {
224
+ "IP Address": result.ip_str,
225
+ "Organization": result.org,
226
+ "ISP": result.isp,
227
+ "ASN": result.asn,
228
+ "Last Update": result.last_update
229
+ },
230
+ "Location": {
231
+ "Country": result.country_name,
232
+ "City": result.city,
233
+ "Coordinates": `${result.latitude}, ${result.longitude}`,
234
+ "Region": result.region_code
235
+ },
236
+ "Services": result.ports.map((port) => {
237
+ const service = result.data.find((d) => d.port === port);
238
+ return {
239
+ "Port": port,
240
+ "Protocol": service?.transport || "unknown",
241
+ "Service": service?.data?.trim() || "No banner",
242
+ ...(service?.http ? {
243
+ "HTTP": {
244
+ "Server": service.http.server,
245
+ "Title": service.http.title,
246
+ }
247
+ } : {})
248
+ };
249
+ }),
250
+ "Cloud Provider": result.data[0]?.cloud ? {
251
+ "Provider": result.data[0].cloud.provider,
252
+ "Service": result.data[0].cloud.service,
253
+ "Region": result.data[0].cloud.region
254
+ } : "Not detected",
255
+ "Hostnames": result.hostnames || [],
256
+ "Domains": result.domains || [],
257
+ "Tags": result.tags || []
258
+ };
205
259
  return {
206
260
  content: [
207
261
  {
208
262
  type: "text",
209
- text: JSON.stringify(result, null, 2),
263
+ text: JSON.stringify(formattedResult, null, 2),
210
264
  },
211
265
  ],
212
266
  };
213
267
  }
214
- case "search": {
215
- const parsedSearchArgs = SearchArgsSchema.safeParse(args);
268
+ case "shodan_search": {
269
+ const parsedSearchArgs = ShodanSearchArgsSchema.safeParse(args);
216
270
  if (!parsedSearchArgs.success) {
217
271
  throw new Error("Invalid search arguments");
218
272
  }
@@ -220,11 +274,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
220
274
  query: parsedSearchArgs.data.query,
221
275
  limit: parsedSearchArgs.data.max_results,
222
276
  });
277
+ // Format the response in a user-friendly way
278
+ const formattedResult = {
279
+ "Search Summary": {
280
+ "Query": parsedSearchArgs.data.query,
281
+ "Total Results": result.total,
282
+ "Results Returned": result.matches.length
283
+ },
284
+ "Country Distribution": result.facets?.country?.map(country => ({
285
+ "Country": country.value,
286
+ "Count": country.count,
287
+ "Percentage": `${((country.count / result.total) * 100).toFixed(2)}%`
288
+ })) || [],
289
+ "Matches": result.matches.map(match => ({
290
+ "Basic Information": {
291
+ "IP Address": match.ip_str,
292
+ "Organization": match.org,
293
+ "ISP": match.isp,
294
+ "ASN": match.asn,
295
+ "Last Update": match.timestamp
296
+ },
297
+ "Location": {
298
+ "Country": match.location.country_name,
299
+ "City": match.location.city || "Unknown",
300
+ "Region": match.location.region_code || "Unknown",
301
+ "Coordinates": `${match.location.latitude}, ${match.location.longitude}`
302
+ },
303
+ "Service Details": {
304
+ "Port": match.port,
305
+ "Transport": match.transport,
306
+ "Product": match.product || "Unknown",
307
+ "Version": match.version || "Unknown",
308
+ "CPE": match.cpe || []
309
+ },
310
+ "Web Information": match.http ? {
311
+ "Server": match.http.server,
312
+ "Title": match.http.title,
313
+ "Robots.txt": match.http.robots ? "Present" : "Not found",
314
+ "Sitemap": match.http.sitemap ? "Present" : "Not found"
315
+ } : "No HTTP information",
316
+ "Hostnames": match.hostnames,
317
+ "Domains": match.domains
318
+ }))
319
+ };
223
320
  return {
224
321
  content: [
225
322
  {
226
323
  type: "text",
227
- text: JSON.stringify(result, null, 2),
324
+ text: JSON.stringify(formattedResult, null, 2),
228
325
  },
229
326
  ],
230
327
  };
@@ -238,24 +335,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
238
335
  logToFile(`Looking up CVE: ${cveId}`);
239
336
  try {
240
337
  const result = await queryCVEDB(cveId);
338
+ // Helper function to format CVSS score severity
339
+ const getCvssSeverity = (score) => {
340
+ if (score >= 9.0)
341
+ return "Critical";
342
+ if (score >= 7.0)
343
+ return "High";
344
+ if (score >= 4.0)
345
+ return "Medium";
346
+ if (score >= 0.1)
347
+ return "Low";
348
+ return "None";
349
+ };
350
+ // Format the response in a user-friendly way
351
+ const formattedResult = {
352
+ "Basic Information": {
353
+ "CVE ID": result.cve_id,
354
+ "Published": new Date(result.published_time).toLocaleString(),
355
+ "Summary": result.summary
356
+ },
357
+ "Severity Scores": {
358
+ "CVSS v3": result.cvss_v3 ? {
359
+ "Score": result.cvss_v3,
360
+ "Severity": getCvssSeverity(result.cvss_v3)
361
+ } : "Not available",
362
+ "CVSS v2": result.cvss_v2 ? {
363
+ "Score": result.cvss_v2,
364
+ "Severity": getCvssSeverity(result.cvss_v2)
365
+ } : "Not available",
366
+ "EPSS": result.epss ? {
367
+ "Score": `${(result.epss * 100).toFixed(2)}%`,
368
+ "Ranking": `Top ${(result.ranking_epss * 100).toFixed(2)}%`
369
+ } : "Not available"
370
+ },
371
+ "Impact Assessment": {
372
+ "Known Exploited Vulnerability": result.kev ? "Yes" : "No",
373
+ "Proposed Action": result.propose_action || "No specific action proposed",
374
+ "Ransomware Campaign": result.ransomware_campaign || "No known ransomware campaigns"
375
+ },
376
+ "Affected Products": result.cpes?.length > 0 ? result.cpes : ["No specific products listed"],
377
+ "Additional Information": {
378
+ "References": result.references?.length > 0 ? result.references : ["No references provided"]
379
+ }
380
+ };
241
381
  return {
242
382
  content: [
243
383
  {
244
384
  type: "text",
245
- text: JSON.stringify({
246
- cve_id: result.cve_id,
247
- summary: result.summary,
248
- cvss_v3: result.cvss_v3,
249
- cvss_v2: result.cvss_v2,
250
- epss: result.epss,
251
- ranking_epss: result.ranking_epss,
252
- kev: result.kev,
253
- propose_action: result.propose_action,
254
- ransomware_campaign: result.ransomware_campaign,
255
- published: result.published_time,
256
- references: result.references,
257
- affected_products: result.cpes
258
- }, null, 2),
385
+ text: JSON.stringify(formattedResult, null, 2),
259
386
  },
260
387
  ],
261
388
  };
@@ -277,20 +404,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
277
404
  if (!parsedDnsArgs.success) {
278
405
  throw new Error("Invalid dns_lookup arguments");
279
406
  }
280
- // Ensure proper formatting of hostnames for the API request
407
+ // Join hostnames with commas for the API request
281
408
  const hostnamesString = parsedDnsArgs.data.hostnames.join(",");
282
- // Log the request parameters for debugging
283
- logToFile(`DNS lookup request parameters: ${JSON.stringify({ hostnames: hostnamesString })}`);
284
409
  const result = await queryShodan("/dns/resolve", {
285
410
  hostnames: hostnamesString
286
411
  });
287
- // Log the raw response for debugging
288
- logToFile(`DNS lookup raw response: ${JSON.stringify(result)}`);
412
+ // Format the response in a user-friendly way
413
+ const formattedResult = {
414
+ "DNS Resolutions": Object.entries(result).map(([hostname, ip]) => ({
415
+ "Hostname": hostname,
416
+ "IP Address": ip
417
+ })),
418
+ "Summary": {
419
+ "Total Lookups": Object.keys(result).length,
420
+ "Queried Hostnames": parsedDnsArgs.data.hostnames
421
+ }
422
+ };
289
423
  return {
290
424
  content: [
291
425
  {
292
426
  type: "text",
293
- text: JSON.stringify(result, null, 2)
427
+ text: JSON.stringify(formattedResult, null, 2)
294
428
  },
295
429
  ],
296
430
  };
@@ -354,22 +488,74 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
354
488
  start_date: parsedArgs.data.start_date,
355
489
  end_date: parsedArgs.data.end_date
356
490
  });
491
+ // Helper function to format CVSS score severity
492
+ const getCvssSeverity = (score) => {
493
+ if (score >= 9.0)
494
+ return "Critical";
495
+ if (score >= 7.0)
496
+ return "High";
497
+ if (score >= 4.0)
498
+ return "Medium";
499
+ if (score >= 0.1)
500
+ return "Low";
501
+ return "None";
502
+ };
357
503
  // Format the response based on whether it's a count request or full CVE list
358
504
  const formattedResult = parsedArgs.data.count
359
- ? { total_cves: result.total }
360
- : {
361
- cves: result.cves,
362
- skip: parsedArgs.data.skip,
363
- limit: parsedArgs.data.limit,
364
- total_returned: result.cves.length,
365
- query_params: {
366
- cpe23: parsedArgs.data.cpe23,
367
- product: parsedArgs.data.product,
368
- is_kev: parsedArgs.data.is_kev,
369
- sort_by_epss: parsedArgs.data.sort_by_epss,
370
- start_date: parsedArgs.data.start_date,
371
- end_date: parsedArgs.data.end_date
505
+ ? {
506
+ "Query Information": {
507
+ "Product": parsedArgs.data.product || "N/A",
508
+ "CPE 2.3": parsedArgs.data.cpe23 || "N/A",
509
+ "KEV Only": parsedArgs.data.is_kev ? "Yes" : "No",
510
+ "Sort by EPSS": parsedArgs.data.sort_by_epss ? "Yes" : "No"
511
+ },
512
+ "Results": {
513
+ "Total CVEs Found": result.total
372
514
  }
515
+ }
516
+ : {
517
+ "Query Information": {
518
+ "Product": parsedArgs.data.product || "N/A",
519
+ "CPE 2.3": parsedArgs.data.cpe23 || "N/A",
520
+ "KEV Only": parsedArgs.data.is_kev ? "Yes" : "No",
521
+ "Sort by EPSS": parsedArgs.data.sort_by_epss ? "Yes" : "No",
522
+ "Date Range": parsedArgs.data.start_date ?
523
+ `${parsedArgs.data.start_date} to ${parsedArgs.data.end_date || 'now'}` :
524
+ "All dates"
525
+ },
526
+ "Results Summary": {
527
+ "Total CVEs Found": result.total,
528
+ "CVEs Returned": result.cves.length,
529
+ "Page": `${Math.floor(parsedArgs.data.skip / parsedArgs.data.limit) + 1}`,
530
+ "CVEs per Page": parsedArgs.data.limit
531
+ },
532
+ "Vulnerabilities": result.cves.map((cve) => ({
533
+ "Basic Information": {
534
+ "CVE ID": cve.cve_id,
535
+ "Published": new Date(cve.published_time).toLocaleString(),
536
+ "Summary": cve.summary
537
+ },
538
+ "Severity Scores": {
539
+ "CVSS v3": cve.cvss_v3 ? {
540
+ "Score": cve.cvss_v3,
541
+ "Severity": getCvssSeverity(cve.cvss_v3)
542
+ } : "Not available",
543
+ "CVSS v2": cve.cvss_v2 ? {
544
+ "Score": cve.cvss_v2,
545
+ "Severity": getCvssSeverity(cve.cvss_v2)
546
+ } : "Not available",
547
+ "EPSS": cve.epss ? {
548
+ "Score": `${(cve.epss * 100).toFixed(2)}%`,
549
+ "Ranking": `Top ${(cve.ranking_epss * 100).toFixed(2)}%`
550
+ } : "Not available"
551
+ },
552
+ "Impact Assessment": {
553
+ "Known Exploited Vulnerability": cve.kev ? "Yes" : "No",
554
+ "Proposed Action": cve.propose_action || "No specific action proposed",
555
+ "Ransomware Campaign": cve.ransomware_campaign || "No known ransomware campaigns"
556
+ },
557
+ "References": cve.references?.length > 0 ? cve.references : ["No references provided"]
558
+ }))
373
559
  };
374
560
  return {
375
561
  content: [
@@ -392,6 +578,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
392
578
  };
393
579
  }
394
580
  }
581
+ case "reverse_dns_lookup": {
582
+ const parsedArgs = ReverseDnsLookupArgsSchema.safeParse(args);
583
+ if (!parsedArgs.success) {
584
+ throw new Error("Invalid reverse_dns_lookup arguments");
585
+ }
586
+ // Join IPs with commas for the API request
587
+ const ipsString = parsedArgs.data.ips.join(",");
588
+ const result = await queryShodan("/dns/reverse", {
589
+ ips: ipsString
590
+ });
591
+ // Format the response in a user-friendly way
592
+ const formattedResult = {
593
+ "Reverse DNS Resolutions": Object.entries(result).map(([ip, hostnames]) => ({
594
+ "IP Address": ip,
595
+ "Hostnames": hostnames.length > 0 ? hostnames : ["No hostnames found"]
596
+ })),
597
+ "Summary": {
598
+ "Total IPs Queried": parsedArgs.data.ips.length,
599
+ "IPs with Results": Object.keys(result).length,
600
+ "Queried IP Addresses": parsedArgs.data.ips
601
+ }
602
+ };
603
+ return {
604
+ content: [
605
+ {
606
+ type: "text",
607
+ text: JSON.stringify(formattedResult, null, 2)
608
+ },
609
+ ],
610
+ };
611
+ }
395
612
  default:
396
613
  throw new Error(`Unknown tool: ${name}`);
397
614
  }
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@burtthecoder/mcp-shodan",
4
- "version": "1.0.11",
4
+ "version": "1.0.13",
5
5
  "description": "A Model Context Protocol server for Shodan API queries.",
6
6
  "main": "build/index.js",
7
7
  "bin": {
8
8
  "mcp-shodan": "build/index.js"
9
9
  },
10
10
  "scripts": {
11
- "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
11
+ "build": "tsc && chmod +x build/index.js",
12
12
  "prepublishOnly": "npm run build"
13
13
  },
14
14
  "dependencies": {