@burtthecoder/mcp-shodan 1.0.4
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.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/build/index.js +232 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Burt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Shodan MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server for querying the [Shodan API](https://shodan.io). This server provides tools for IP lookups, device searches, DNS lookups, vulnerability queries, and more. It is designed to integrate seamlessly with MCP-compatible applications like [Claude Desktop](https://claude.ai).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **IP Lookup**: Retrieve detailed information about an IP address
|
|
8
|
+
- **Search**: Search for devices on Shodan matching specific queries
|
|
9
|
+
- **Ports**: Get a list of ports that Shodan is scanning
|
|
10
|
+
- **Vulnerabilities**: Fetch information about known vulnerabilities (CVE)
|
|
11
|
+
- **DNS Lookup**: Resolve hostnames to IP addresses
|
|
12
|
+
|
|
13
|
+
## Tools
|
|
14
|
+
|
|
15
|
+
### 1. IP Lookup Tool
|
|
16
|
+
- Name: `ip_lookup`
|
|
17
|
+
- Description: Retrieve detailed information about an IP address
|
|
18
|
+
- Parameters:
|
|
19
|
+
* `ip` (required): IP address to lookup
|
|
20
|
+
|
|
21
|
+
### 2. Search Tool
|
|
22
|
+
- Name: `search`
|
|
23
|
+
- Description: Search for devices on Shodan
|
|
24
|
+
- Parameters:
|
|
25
|
+
* `query` (required): Shodan search query
|
|
26
|
+
* `max_results` (optional, default: 10): Number of results to return
|
|
27
|
+
|
|
28
|
+
### 3. Vulnerabilities Tool
|
|
29
|
+
- Name: `vulnerabilities`
|
|
30
|
+
- Description: Fetch information about known vulnerabilities
|
|
31
|
+
- Parameters:
|
|
32
|
+
* `cve` (required): CVE identifier
|
|
33
|
+
|
|
34
|
+
### 4. DNS Lookup Tool
|
|
35
|
+
- Name: `dns_lookup`
|
|
36
|
+
- Description: Resolve hostnames to IP addresses
|
|
37
|
+
- Parameters:
|
|
38
|
+
* `hostnames` (required): Array of hostnames to resolve
|
|
39
|
+
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- Node.js (v18 or later)
|
|
43
|
+
- A valid [Shodan API Key](https://account.shodan.io/)
|
|
44
|
+
|
|
45
|
+
## Setup Guide
|
|
46
|
+
|
|
47
|
+
### 1. Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone <repository_url>
|
|
51
|
+
cd mcp-shodan
|
|
52
|
+
npm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. Configuration
|
|
56
|
+
|
|
57
|
+
Create a `.env` file in the root directory:
|
|
58
|
+
```
|
|
59
|
+
SHODAN_API_KEY=your_shodan_api_key
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. Build and Run
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run build
|
|
66
|
+
npm start
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 4. Configure Claude Desktop
|
|
70
|
+
|
|
71
|
+
There are two ways to configure the Shodan MCP server in Claude Desktop:
|
|
72
|
+
|
|
73
|
+
#### Option 1: Direct Node Execution (Local Development)
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"mcpServers": {
|
|
77
|
+
"shodan-mcp": {
|
|
78
|
+
"command": "node",
|
|
79
|
+
"args": ["path/to/mcp-shodan/build/index.js"],
|
|
80
|
+
"env": {
|
|
81
|
+
"SHODAN_API_KEY": "your_shodan_api_key",
|
|
82
|
+
"DEBUG": "*"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Option 2: NPX Installation (Recommended for Users)
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"shodan-mcp": {
|
|
94
|
+
"command": "npx",
|
|
95
|
+
"args": ["@burtthecoder/mcp-shodan"],
|
|
96
|
+
"env": {
|
|
97
|
+
"SHODAN_API_KEY": "your_shodan_api_key",
|
|
98
|
+
"DEBUG": "*"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The npx method automatically downloads and runs the latest version of the package from npm.
|
|
106
|
+
|
|
107
|
+
Configuration file location:
|
|
108
|
+
|
|
109
|
+
Windows: %APPDATA%\Claude\claude_desktop_config.json
|
|
110
|
+
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
111
|
+
|
|
112
|
+
## Usage
|
|
113
|
+
|
|
114
|
+
1. Start the MCP server:
|
|
115
|
+
```bash
|
|
116
|
+
npm start
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
2. Launch Claude Desktop and ensure the Shodan MCP server is detected
|
|
120
|
+
3. Use any of the available tools through the Claude interface
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
To run in development mode with hot reloading:
|
|
125
|
+
```bash
|
|
126
|
+
npm run dev
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Error Handling
|
|
130
|
+
|
|
131
|
+
The server includes comprehensive error handling for:
|
|
132
|
+
- Invalid API keys
|
|
133
|
+
- Rate limiting
|
|
134
|
+
- Network errors
|
|
135
|
+
- Invalid input parameters
|
|
136
|
+
|
|
137
|
+
## Version History
|
|
138
|
+
|
|
139
|
+
- v1.0.0: Initial release with core functionality
|
|
140
|
+
|
|
141
|
+
## Contributing
|
|
142
|
+
|
|
143
|
+
1. Fork the repository
|
|
144
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
145
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
146
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
147
|
+
5. Open a Pull Request
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
package/build/index.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, InitializeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import axios from "axios";
|
|
6
|
+
import dotenv from "dotenv";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
dotenv.config();
|
|
11
|
+
const logFilePath = "E:/dev/shodan-mcp/server.log";
|
|
12
|
+
const SHODAN_API_KEY = process.env.SHODAN_API_KEY;
|
|
13
|
+
if (!SHODAN_API_KEY) {
|
|
14
|
+
throw new Error("SHODAN_API_KEY environment variable is required.");
|
|
15
|
+
}
|
|
16
|
+
const API_BASE_URL = "https://api.shodan.io";
|
|
17
|
+
// Logging Helper Function
|
|
18
|
+
function logToFile(message) {
|
|
19
|
+
const timestamp = new Date().toISOString();
|
|
20
|
+
const formattedMessage = `[${timestamp}] ${message}\n`;
|
|
21
|
+
fs.appendFileSync(logFilePath, formattedMessage, "utf8");
|
|
22
|
+
console.error(formattedMessage.trim()); // Use stderr for logging to avoid interfering with stdout
|
|
23
|
+
}
|
|
24
|
+
// Tool Schemas
|
|
25
|
+
const IpLookupArgsSchema = z.object({
|
|
26
|
+
ip: z.string().describe("The IP address to query."),
|
|
27
|
+
});
|
|
28
|
+
const SearchArgsSchema = z.object({
|
|
29
|
+
query: z.string().describe("Search query for Shodan."),
|
|
30
|
+
max_results: z
|
|
31
|
+
.number()
|
|
32
|
+
.optional()
|
|
33
|
+
.default(10)
|
|
34
|
+
.describe("Maximum results to return."),
|
|
35
|
+
});
|
|
36
|
+
const VulnerabilitiesArgsSchema = z.object({
|
|
37
|
+
cve: z.string().describe("The CVE identifier to query."),
|
|
38
|
+
});
|
|
39
|
+
const DnsLookupArgsSchema = z.object({
|
|
40
|
+
hostnames: z.array(z.string()).describe("List of hostnames to resolve."),
|
|
41
|
+
});
|
|
42
|
+
// Helper Function to Query Shodan API
|
|
43
|
+
async function queryShodan(endpoint, params) {
|
|
44
|
+
try {
|
|
45
|
+
const response = await axios.get(`${API_BASE_URL}${endpoint}`, {
|
|
46
|
+
params: { ...params, key: SHODAN_API_KEY },
|
|
47
|
+
timeout: 10000,
|
|
48
|
+
});
|
|
49
|
+
return response.data;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const errorMessage = error.response?.data?.error || error.message;
|
|
53
|
+
logToFile(`Shodan API error: ${errorMessage}`);
|
|
54
|
+
throw new Error(`Shodan API error: ${errorMessage}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Server Setup
|
|
58
|
+
const server = new Server({
|
|
59
|
+
name: "shodan-mcp",
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
}, {
|
|
62
|
+
capabilities: {
|
|
63
|
+
tools: {
|
|
64
|
+
listChanged: true,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
// Handle Initialization
|
|
69
|
+
server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
70
|
+
logToFile("Received initialize request.");
|
|
71
|
+
return {
|
|
72
|
+
protocolVersion: "2024-11-05",
|
|
73
|
+
capabilities: {
|
|
74
|
+
tools: {
|
|
75
|
+
listChanged: true,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
serverInfo: {
|
|
79
|
+
name: "shodan-mcp",
|
|
80
|
+
version: "1.0.0",
|
|
81
|
+
},
|
|
82
|
+
instructions: "This server provides tools for querying Shodan, including IP lookups, searches, and vulnerabilities.",
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
// Register Tools
|
|
86
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
87
|
+
const tools = [
|
|
88
|
+
{
|
|
89
|
+
name: "ip_lookup",
|
|
90
|
+
description: "Retrieve information about an IP address.",
|
|
91
|
+
inputSchema: zodToJsonSchema(IpLookupArgsSchema),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "search",
|
|
95
|
+
description: "Search for devices on Shodan.",
|
|
96
|
+
inputSchema: zodToJsonSchema(SearchArgsSchema),
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "vulnerabilities",
|
|
100
|
+
description: "Retrieve vulnerability information for a CVE.",
|
|
101
|
+
inputSchema: zodToJsonSchema(VulnerabilitiesArgsSchema),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "dns_lookup",
|
|
105
|
+
description: "Perform DNS lookups using Shodan.",
|
|
106
|
+
inputSchema: zodToJsonSchema(DnsLookupArgsSchema),
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
logToFile("Registered tools.");
|
|
110
|
+
return { tools };
|
|
111
|
+
});
|
|
112
|
+
// Handle Tool Calls
|
|
113
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
114
|
+
logToFile(`Tool called: ${request.params.name}`);
|
|
115
|
+
try {
|
|
116
|
+
const { name, arguments: args } = request.params;
|
|
117
|
+
switch (name) {
|
|
118
|
+
case "ip_lookup": {
|
|
119
|
+
const parsedIpArgs = IpLookupArgsSchema.safeParse(args);
|
|
120
|
+
if (!parsedIpArgs.success) {
|
|
121
|
+
throw new Error("Invalid ip_lookup arguments");
|
|
122
|
+
}
|
|
123
|
+
const result = await queryShodan(`/shodan/host/${parsedIpArgs.data.ip}`, {});
|
|
124
|
+
return {
|
|
125
|
+
content: [
|
|
126
|
+
{
|
|
127
|
+
type: "text",
|
|
128
|
+
text: JSON.stringify(result, null, 2),
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
case "search": {
|
|
134
|
+
const parsedSearchArgs = SearchArgsSchema.safeParse(args);
|
|
135
|
+
if (!parsedSearchArgs.success) {
|
|
136
|
+
throw new Error("Invalid search arguments");
|
|
137
|
+
}
|
|
138
|
+
const result = await queryShodan("/shodan/host/search", {
|
|
139
|
+
query: parsedSearchArgs.data.query,
|
|
140
|
+
limit: parsedSearchArgs.data.max_results,
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: JSON.stringify(result, null, 2),
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
case "vulnerabilities": {
|
|
152
|
+
const parsedVulnArgs = VulnerabilitiesArgsSchema.safeParse(args);
|
|
153
|
+
if (!parsedVulnArgs.success) {
|
|
154
|
+
throw new Error("Invalid vulnerabilities arguments");
|
|
155
|
+
}
|
|
156
|
+
const result = await queryShodan(`/shodan/cve/${parsedVulnArgs.data.cve}`, {});
|
|
157
|
+
return {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: "text",
|
|
161
|
+
text: JSON.stringify(result, null, 2),
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
case "dns_lookup": {
|
|
167
|
+
const parsedDnsArgs = DnsLookupArgsSchema.safeParse(args);
|
|
168
|
+
if (!parsedDnsArgs.success) {
|
|
169
|
+
throw new Error("Invalid dns_lookup arguments");
|
|
170
|
+
}
|
|
171
|
+
// Ensure proper formatting of hostnames for the API request
|
|
172
|
+
const hostnamesString = parsedDnsArgs.data.hostnames.join(",");
|
|
173
|
+
// Log the request parameters for debugging
|
|
174
|
+
logToFile(`DNS lookup request parameters: ${JSON.stringify({ hostnames: hostnamesString })}`);
|
|
175
|
+
const result = await queryShodan("/dns/resolve", {
|
|
176
|
+
hostnames: hostnamesString
|
|
177
|
+
});
|
|
178
|
+
// Log the raw response for debugging
|
|
179
|
+
logToFile(`DNS lookup raw response: ${JSON.stringify(result)}`);
|
|
180
|
+
return {
|
|
181
|
+
content: [
|
|
182
|
+
{
|
|
183
|
+
type: "text",
|
|
184
|
+
text: JSON.stringify(result, null, 2)
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
default:
|
|
190
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
195
|
+
logToFile(`Error handling tool call: ${errorMessage}`);
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: `Error: ${errorMessage}`,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
isError: true,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
// Start the Server
|
|
208
|
+
async function runServer() {
|
|
209
|
+
logToFile("Starting Shodan MCP Server...");
|
|
210
|
+
try {
|
|
211
|
+
const transport = new StdioServerTransport();
|
|
212
|
+
await server.connect(transport);
|
|
213
|
+
logToFile("Shodan MCP Server is running.");
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
logToFile(`Error connecting server: ${error.message}`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Handle process events
|
|
221
|
+
process.on('uncaughtException', (error) => {
|
|
222
|
+
logToFile(`Uncaught exception: ${error.message}`);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
});
|
|
225
|
+
process.on('unhandledRejection', (reason) => {
|
|
226
|
+
logToFile(`Unhandled rejection: ${reason}`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
});
|
|
229
|
+
runServer().catch((error) => {
|
|
230
|
+
logToFile(`Fatal error: ${error.message}`);
|
|
231
|
+
process.exit(1);
|
|
232
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@burtthecoder/mcp-shodan",
|
|
4
|
+
"version": "1.0.4",
|
|
5
|
+
"description": "A Model Context Protocol server for Shodan API queries.",
|
|
6
|
+
"main": "./build/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-shodan": "build/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node build/index.js",
|
|
12
|
+
"dev": "ts-node src/index.ts",
|
|
13
|
+
"build": "tsc"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^0.6.0",
|
|
17
|
+
"axios": "^1.7.8",
|
|
18
|
+
"dotenv": "^16.4.5",
|
|
19
|
+
"zod": "^3.22.2",
|
|
20
|
+
"zod-to-json-schema": "^3.23.5"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "^5.3.3",
|
|
24
|
+
"@types/node": "^20.11.24"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"build"
|
|
28
|
+
],
|
|
29
|
+
"author": "BurtTheCoder",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/BurtTheCoder/mcp-shodan.git"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"mcp",
|
|
37
|
+
"shodan",
|
|
38
|
+
"mcp-shodan",
|
|
39
|
+
"cybersecurity",
|
|
40
|
+
"agents",
|
|
41
|
+
"network-reconnaissance",
|
|
42
|
+
"security-tools",
|
|
43
|
+
"shodan-api",
|
|
44
|
+
"shodan-integraton"
|
|
45
|
+
],
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/BurtTheCoder/mcp-shodan/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/BurtTheCoder/mcp-shodan#readme"
|
|
50
|
+
}
|