@rog0x/mcp-network-tools 1.0.0
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/README.md +82 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +192 -0
- package/dist/tools/dns-lookup.d.ts +20 -0
- package/dist/tools/dns-lookup.js +132 -0
- package/dist/tools/ip-info.d.ts +20 -0
- package/dist/tools/ip-info.js +53 -0
- package/dist/tools/ping-tool.d.ts +19 -0
- package/dist/tools/ping-tool.js +80 -0
- package/dist/tools/ssl-checker.d.ts +23 -0
- package/dist/tools/ssl-checker.js +109 -0
- package/dist/tools/whois-lookup.d.ts +16 -0
- package/dist/tools/whois-lookup.js +156 -0
- package/package.json +20 -0
- package/src/index.ts +222 -0
- package/src/tools/dns-lookup.ts +131 -0
- package/src/tools/ip-info.ts +71 -0
- package/src/tools/ping-tool.ts +122 -0
- package/src/tools/ssl-checker.ts +112 -0
- package/src/tools/whois-lookup.ts +147 -0
- package/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# mcp-network-tools
|
|
2
|
+
|
|
3
|
+
Network diagnostics tools for AI agents, exposed via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io).
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
| Tool | Description |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| `dns_lookup` | Resolve domains to DNS records (A, AAAA, MX, TXT, NS, CNAME, SOA). Reverse DNS. Propagation check across public DNS servers. |
|
|
10
|
+
| `ip_info` | Geolocation and network info for an IP address — country, city, ISP, ASN, coordinates. IPv4 and IPv6. |
|
|
11
|
+
| `ssl_check` | Inspect SSL/TLS certificates — validity, issuer, expiry, SANs, certificate chain, cipher, protocol. |
|
|
12
|
+
| `whois_lookup` | WHOIS data for a domain — registrar, creation/expiry dates, nameservers, status. |
|
|
13
|
+
| `http_ping` | Measure HTTP response time over N requests. Returns avg, min, max, p95 latency and success rate. |
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage with Claude Desktop
|
|
23
|
+
|
|
24
|
+
Add to your `claude_desktop_config.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"network-tools": {
|
|
30
|
+
"command": "node",
|
|
31
|
+
"args": ["D:/products/mcp-servers/mcp-network-tools/dist/index.js"]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Dependencies
|
|
38
|
+
|
|
39
|
+
- **@modelcontextprotocol/sdk** — MCP server framework
|
|
40
|
+
- **Node.js built-in modules** — `dns`, `tls`, `net`, `https`, `http` (no external runtime dependencies beyond MCP SDK)
|
|
41
|
+
- **ip-api.com** — free geolocation API (no API key required)
|
|
42
|
+
|
|
43
|
+
## Examples
|
|
44
|
+
|
|
45
|
+
### DNS Lookup
|
|
46
|
+
```
|
|
47
|
+
dns_lookup({ domain: "example.com", record_types: ["A", "MX"] })
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Reverse DNS
|
|
51
|
+
```
|
|
52
|
+
dns_lookup({ domain: "8.8.8.8", mode: "reverse" })
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### DNS Propagation Check
|
|
56
|
+
```
|
|
57
|
+
dns_lookup({ domain: "example.com", mode: "propagation" })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### IP Info
|
|
61
|
+
```
|
|
62
|
+
ip_info({ ip: "1.1.1.1" })
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### SSL Check
|
|
66
|
+
```
|
|
67
|
+
ssl_check({ host: "example.com" })
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### WHOIS Lookup
|
|
71
|
+
```
|
|
72
|
+
whois_lookup({ domain: "example.com" })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### HTTP Ping
|
|
76
|
+
```
|
|
77
|
+
http_ping({ url: "https://example.com", count: 10 })
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
5
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
7
|
+
const dns_lookup_js_1 = require("./tools/dns-lookup.js");
|
|
8
|
+
const ip_info_js_1 = require("./tools/ip-info.js");
|
|
9
|
+
const ssl_checker_js_1 = require("./tools/ssl-checker.js");
|
|
10
|
+
const whois_lookup_js_1 = require("./tools/whois-lookup.js");
|
|
11
|
+
const ping_tool_js_1 = require("./tools/ping-tool.js");
|
|
12
|
+
const server = new index_js_1.Server({
|
|
13
|
+
name: "mcp-network-tools",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
}, {
|
|
16
|
+
capabilities: {
|
|
17
|
+
tools: {},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
21
|
+
tools: [
|
|
22
|
+
{
|
|
23
|
+
name: "dns_lookup",
|
|
24
|
+
description: "Resolve a domain name to DNS records (A, AAAA, MX, TXT, NS, CNAME, SOA). Supports reverse DNS and propagation checking.",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
domain: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Domain name to look up (e.g. example.com)",
|
|
31
|
+
},
|
|
32
|
+
record_types: {
|
|
33
|
+
type: "array",
|
|
34
|
+
items: { type: "string" },
|
|
35
|
+
description: "Record types to query (default: all). Options: A, AAAA, MX, TXT, NS, CNAME, SOA",
|
|
36
|
+
},
|
|
37
|
+
mode: {
|
|
38
|
+
type: "string",
|
|
39
|
+
enum: ["resolve", "reverse", "propagation"],
|
|
40
|
+
description: "Mode: 'resolve' (default) for forward DNS, 'reverse' for reverse DNS (pass IP in domain), 'propagation' to check across public DNS servers",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: ["domain"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "ip_info",
|
|
48
|
+
description: "Get geolocation and network information for an IP address: country, city, ISP, ASN, coordinates. Supports IPv4 and IPv6.",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
ip: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "IPv4 or IPv6 address to look up",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ["ip"],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "ssl_check",
|
|
62
|
+
description: "Check the SSL/TLS certificate of a host: validity, issuer, expiry date, days until expiry, subject alternative names, certificate chain, cipher, and protocol.",
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: "object",
|
|
65
|
+
properties: {
|
|
66
|
+
host: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "Hostname to check (e.g. example.com)",
|
|
69
|
+
},
|
|
70
|
+
port: {
|
|
71
|
+
type: "number",
|
|
72
|
+
description: "Port number (default: 443)",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
required: ["host"],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "whois_lookup",
|
|
80
|
+
description: "Look up WHOIS information for a domain: registrar, creation date, expiry date, nameservers, and domain status.",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
domain: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "Domain name to look up (e.g. example.com)",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
required: ["domain"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "http_ping",
|
|
94
|
+
description: "Measure HTTP response time to a URL over multiple requests. Returns per-request latency and statistics: average, min, max, p95, and success rate.",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
type: "object",
|
|
97
|
+
properties: {
|
|
98
|
+
url: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "URL to ping (e.g. https://example.com)",
|
|
101
|
+
},
|
|
102
|
+
count: {
|
|
103
|
+
type: "number",
|
|
104
|
+
description: "Number of requests to send (default: 5, max: 20)",
|
|
105
|
+
},
|
|
106
|
+
timeout_ms: {
|
|
107
|
+
type: "number",
|
|
108
|
+
description: "Timeout per request in milliseconds (default: 10000)",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
required: ["url"],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
}));
|
|
116
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
117
|
+
const { name, arguments: args } = request.params;
|
|
118
|
+
try {
|
|
119
|
+
switch (name) {
|
|
120
|
+
case "dns_lookup": {
|
|
121
|
+
const domain = args?.domain;
|
|
122
|
+
const mode = args?.mode || "resolve";
|
|
123
|
+
const recordTypes = args?.record_types;
|
|
124
|
+
let result;
|
|
125
|
+
switch (mode) {
|
|
126
|
+
case "reverse":
|
|
127
|
+
result = await (0, dns_lookup_js_1.reverseDns)(domain);
|
|
128
|
+
break;
|
|
129
|
+
case "propagation":
|
|
130
|
+
result = await (0, dns_lookup_js_1.checkPropagation)(domain, recordTypes?.[0] || "A");
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
result = await (0, dns_lookup_js_1.dnsLookup)(domain, recordTypes);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
case "ip_info": {
|
|
141
|
+
const result = await (0, ip_info_js_1.getIpInfo)(args?.ip);
|
|
142
|
+
return {
|
|
143
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
case "ssl_check": {
|
|
147
|
+
const port = args?.port || 443;
|
|
148
|
+
const result = await (0, ssl_checker_js_1.checkSsl)(args?.host, port);
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
case "whois_lookup": {
|
|
154
|
+
const result = await (0, whois_lookup_js_1.whoisLookup)(args?.domain);
|
|
155
|
+
// Return parsed data without the full raw WHOIS by default to save tokens
|
|
156
|
+
const { raw, ...summary } = result;
|
|
157
|
+
return {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: "text",
|
|
161
|
+
text: JSON.stringify({ ...summary, rawLength: raw.length }, null, 2),
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
case "http_ping": {
|
|
167
|
+
const count = args?.count || 5;
|
|
168
|
+
const timeoutMs = args?.timeout_ms || 10000;
|
|
169
|
+
const result = await (0, ping_tool_js_1.httpPing)(args?.url, count, timeoutMs);
|
|
170
|
+
return {
|
|
171
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
default:
|
|
175
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
180
|
+
return {
|
|
181
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
182
|
+
isError: true,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
async function main() {
|
|
187
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
188
|
+
await server.connect(transport);
|
|
189
|
+
console.error("MCP Network Tools server running on stdio");
|
|
190
|
+
}
|
|
191
|
+
main().catch(console.error);
|
|
192
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface DnsLookupResult {
|
|
2
|
+
domain: string;
|
|
3
|
+
records: Record<string, unknown>;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
}
|
|
6
|
+
interface ReverseDnsResult {
|
|
7
|
+
ip: string;
|
|
8
|
+
hostnames: string[];
|
|
9
|
+
timestamp: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function dnsLookup(domain: string, recordTypes?: string[]): Promise<DnsLookupResult>;
|
|
12
|
+
export declare function reverseDns(ip: string): Promise<ReverseDnsResult>;
|
|
13
|
+
export declare function checkPropagation(domain: string, recordType?: string): Promise<{
|
|
14
|
+
domain: string;
|
|
15
|
+
recordType: string;
|
|
16
|
+
servers: Record<string, unknown>;
|
|
17
|
+
consistent: boolean;
|
|
18
|
+
timestamp: string;
|
|
19
|
+
}>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.dnsLookup = dnsLookup;
|
|
37
|
+
exports.reverseDns = reverseDns;
|
|
38
|
+
exports.checkPropagation = checkPropagation;
|
|
39
|
+
const dns = __importStar(require("node:dns"));
|
|
40
|
+
const node_util_1 = require("node:util");
|
|
41
|
+
const resolve4 = (0, node_util_1.promisify)(dns.resolve4);
|
|
42
|
+
const resolve6 = (0, node_util_1.promisify)(dns.resolve6);
|
|
43
|
+
const resolveMx = (0, node_util_1.promisify)(dns.resolveMx);
|
|
44
|
+
const resolveTxt = (0, node_util_1.promisify)(dns.resolveTxt);
|
|
45
|
+
const resolveNs = (0, node_util_1.promisify)(dns.resolveNs);
|
|
46
|
+
const resolveCname = (0, node_util_1.promisify)(dns.resolveCname);
|
|
47
|
+
const resolveSoa = (0, node_util_1.promisify)(dns.resolveSoa);
|
|
48
|
+
const reverseLookup = (0, node_util_1.promisify)(dns.reverse);
|
|
49
|
+
async function safeResolve(fn) {
|
|
50
|
+
try {
|
|
51
|
+
return await fn();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function dnsLookup(domain, recordTypes) {
|
|
58
|
+
const types = recordTypes?.map((t) => t.toUpperCase()) ?? [
|
|
59
|
+
"A",
|
|
60
|
+
"AAAA",
|
|
61
|
+
"MX",
|
|
62
|
+
"TXT",
|
|
63
|
+
"NS",
|
|
64
|
+
"CNAME",
|
|
65
|
+
"SOA",
|
|
66
|
+
];
|
|
67
|
+
const records = {};
|
|
68
|
+
const resolvers = {
|
|
69
|
+
A: () => resolve4(domain),
|
|
70
|
+
AAAA: () => resolve6(domain),
|
|
71
|
+
MX: () => resolveMx(domain),
|
|
72
|
+
TXT: () => resolveTxt(domain),
|
|
73
|
+
NS: () => resolveNs(domain),
|
|
74
|
+
CNAME: () => resolveCname(domain),
|
|
75
|
+
SOA: () => resolveSoa(domain),
|
|
76
|
+
};
|
|
77
|
+
await Promise.all(types.map(async (type) => {
|
|
78
|
+
const resolver = resolvers[type];
|
|
79
|
+
if (resolver) {
|
|
80
|
+
const result = await safeResolve(resolver);
|
|
81
|
+
if (result !== null) {
|
|
82
|
+
records[type] = result;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
return {
|
|
87
|
+
domain,
|
|
88
|
+
records,
|
|
89
|
+
timestamp: new Date().toISOString(),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async function reverseDns(ip) {
|
|
93
|
+
const hostnames = await reverseLookup(ip);
|
|
94
|
+
return {
|
|
95
|
+
ip,
|
|
96
|
+
hostnames,
|
|
97
|
+
timestamp: new Date().toISOString(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function checkPropagation(domain, recordType = "A") {
|
|
101
|
+
const dnsServers = {
|
|
102
|
+
"Google": "8.8.8.8",
|
|
103
|
+
"Cloudflare": "1.1.1.1",
|
|
104
|
+
"OpenDNS": "208.67.222.222",
|
|
105
|
+
"Quad9": "9.9.9.9",
|
|
106
|
+
};
|
|
107
|
+
const resolver = new dns.Resolver();
|
|
108
|
+
const resolveWithServer = (0, node_util_1.promisify)(resolver.resolve.bind(resolver));
|
|
109
|
+
const results = {};
|
|
110
|
+
const allRecords = [];
|
|
111
|
+
for (const [name, server] of Object.entries(dnsServers)) {
|
|
112
|
+
try {
|
|
113
|
+
resolver.setServers([server]);
|
|
114
|
+
const records = await resolveWithServer(domain, recordType);
|
|
115
|
+
results[name] = { server, records };
|
|
116
|
+
allRecords.push(JSON.stringify(records));
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
120
|
+
results[name] = { server, error: message };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const uniqueResults = new Set(allRecords);
|
|
124
|
+
return {
|
|
125
|
+
domain,
|
|
126
|
+
recordType,
|
|
127
|
+
servers: results,
|
|
128
|
+
consistent: uniqueResults.size <= 1,
|
|
129
|
+
timestamp: new Date().toISOString(),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=dns-lookup.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface IpInfoResult {
|
|
2
|
+
ip: string;
|
|
3
|
+
country: string;
|
|
4
|
+
countryCode: string;
|
|
5
|
+
region: string;
|
|
6
|
+
regionName: string;
|
|
7
|
+
city: string;
|
|
8
|
+
zip: string;
|
|
9
|
+
lat: number;
|
|
10
|
+
lon: number;
|
|
11
|
+
timezone: string;
|
|
12
|
+
isp: string;
|
|
13
|
+
org: string;
|
|
14
|
+
as: string;
|
|
15
|
+
asn: string;
|
|
16
|
+
query: string;
|
|
17
|
+
timestamp: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function getIpInfo(ip: string): Promise<IpInfoResult>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getIpInfo = getIpInfo;
|
|
7
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
8
|
+
function httpGet(url) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const req = node_http_1.default.get(url, { timeout: 10000 }, (res) => {
|
|
11
|
+
let data = "";
|
|
12
|
+
res.on("data", (chunk) => {
|
|
13
|
+
data += chunk.toString();
|
|
14
|
+
});
|
|
15
|
+
res.on("end", () => resolve(data));
|
|
16
|
+
res.on("error", reject);
|
|
17
|
+
});
|
|
18
|
+
req.on("error", reject);
|
|
19
|
+
req.on("timeout", () => {
|
|
20
|
+
req.destroy();
|
|
21
|
+
reject(new Error("Request timed out"));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function getIpInfo(ip) {
|
|
26
|
+
const url = `http://ip-api.com/json/${encodeURIComponent(ip)}?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,org,as,query`;
|
|
27
|
+
const raw = await httpGet(url);
|
|
28
|
+
const data = JSON.parse(raw);
|
|
29
|
+
if (data.status === "fail") {
|
|
30
|
+
throw new Error(`IP lookup failed: ${data.message}`);
|
|
31
|
+
}
|
|
32
|
+
const asString = data.as || "";
|
|
33
|
+
const asnMatch = asString.match(/^AS(\d+)/);
|
|
34
|
+
return {
|
|
35
|
+
ip,
|
|
36
|
+
country: data.country ?? "",
|
|
37
|
+
countryCode: data.countryCode ?? "",
|
|
38
|
+
region: data.region ?? "",
|
|
39
|
+
regionName: data.regionName ?? "",
|
|
40
|
+
city: data.city ?? "",
|
|
41
|
+
zip: data.zip ?? "",
|
|
42
|
+
lat: data.lat ?? 0,
|
|
43
|
+
lon: data.lon ?? 0,
|
|
44
|
+
timezone: data.timezone ?? "",
|
|
45
|
+
isp: data.isp ?? "",
|
|
46
|
+
org: data.org ?? "",
|
|
47
|
+
as: asString,
|
|
48
|
+
asn: asnMatch ? `AS${asnMatch[1]}` : "",
|
|
49
|
+
query: data.query ?? ip,
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=ip-info.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface PingResult {
|
|
2
|
+
url: string;
|
|
3
|
+
requests: number;
|
|
4
|
+
results: Array<{
|
|
5
|
+
attempt: number;
|
|
6
|
+
statusCode: number;
|
|
7
|
+
latencyMs: number;
|
|
8
|
+
}>;
|
|
9
|
+
stats: {
|
|
10
|
+
avgMs: number;
|
|
11
|
+
minMs: number;
|
|
12
|
+
maxMs: number;
|
|
13
|
+
p95Ms: number;
|
|
14
|
+
successRate: number;
|
|
15
|
+
};
|
|
16
|
+
timestamp: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function httpPing(url: string, count?: number, timeoutMs?: number): Promise<PingResult>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.httpPing = httpPing;
|
|
7
|
+
const node_https_1 = __importDefault(require("node:https"));
|
|
8
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
9
|
+
const node_url_1 = require("node:url");
|
|
10
|
+
function singlePing(url, timeoutMs) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const parsed = new node_url_1.URL(url);
|
|
13
|
+
const client = parsed.protocol === "https:" ? node_https_1.default : node_http_1.default;
|
|
14
|
+
const start = performance.now();
|
|
15
|
+
const req = client.get(url, {
|
|
16
|
+
timeout: timeoutMs,
|
|
17
|
+
headers: {
|
|
18
|
+
"User-Agent": "mcp-network-tools/1.0",
|
|
19
|
+
},
|
|
20
|
+
}, (res) => {
|
|
21
|
+
const latencyMs = Math.round((performance.now() - start) * 100) / 100;
|
|
22
|
+
// Consume response data to free memory
|
|
23
|
+
res.resume();
|
|
24
|
+
res.on("end", () => {
|
|
25
|
+
resolve({
|
|
26
|
+
statusCode: res.statusCode || 0,
|
|
27
|
+
latencyMs,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
req.on("error", (err) => {
|
|
32
|
+
reject(err);
|
|
33
|
+
});
|
|
34
|
+
req.on("timeout", () => {
|
|
35
|
+
req.destroy();
|
|
36
|
+
reject(new Error("Request timed out"));
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function percentile(sorted, p) {
|
|
41
|
+
const index = Math.ceil((p / 100) * sorted.length) - 1;
|
|
42
|
+
return sorted[Math.max(0, index)];
|
|
43
|
+
}
|
|
44
|
+
async function httpPing(url, count = 5, timeoutMs = 10000) {
|
|
45
|
+
const safeCount = Math.min(Math.max(1, count), 20);
|
|
46
|
+
const results = [];
|
|
47
|
+
for (let i = 0; i < safeCount; i++) {
|
|
48
|
+
try {
|
|
49
|
+
const { statusCode, latencyMs } = await singlePing(url, timeoutMs);
|
|
50
|
+
results.push({ attempt: i + 1, statusCode, latencyMs });
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
54
|
+
results.push({ attempt: i + 1, statusCode: 0, latencyMs: -1 });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const successfulLatencies = results
|
|
58
|
+
.filter((r) => r.latencyMs >= 0)
|
|
59
|
+
.map((r) => r.latencyMs)
|
|
60
|
+
.sort((a, b) => a - b);
|
|
61
|
+
const successCount = successfulLatencies.length;
|
|
62
|
+
const stats = {
|
|
63
|
+
avgMs: successCount > 0
|
|
64
|
+
? Math.round((successfulLatencies.reduce((a, b) => a + b, 0) / successCount) *
|
|
65
|
+
100) / 100
|
|
66
|
+
: -1,
|
|
67
|
+
minMs: successCount > 0 ? successfulLatencies[0] : -1,
|
|
68
|
+
maxMs: successCount > 0 ? successfulLatencies[successCount - 1] : -1,
|
|
69
|
+
p95Ms: successCount > 0 ? percentile(successfulLatencies, 95) : -1,
|
|
70
|
+
successRate: Math.round((successCount / safeCount) * 10000) / 100,
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
url,
|
|
74
|
+
requests: safeCount,
|
|
75
|
+
results,
|
|
76
|
+
stats,
|
|
77
|
+
timestamp: new Date().toISOString(),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=ping-tool.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface CertificateInfo {
|
|
2
|
+
host: string;
|
|
3
|
+
valid: boolean;
|
|
4
|
+
issuer: Record<string, string>;
|
|
5
|
+
subject: Record<string, string>;
|
|
6
|
+
validFrom: string;
|
|
7
|
+
validTo: string;
|
|
8
|
+
daysUntilExpiry: number;
|
|
9
|
+
serialNumber: string;
|
|
10
|
+
fingerprint: string;
|
|
11
|
+
fingerprint256: string;
|
|
12
|
+
subjectAltNames: string[];
|
|
13
|
+
protocol: string;
|
|
14
|
+
cipher: string;
|
|
15
|
+
chain: Array<{
|
|
16
|
+
subject: string;
|
|
17
|
+
issuer: string;
|
|
18
|
+
validTo: string;
|
|
19
|
+
}>;
|
|
20
|
+
timestamp: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function checkSsl(host: string, port?: number): Promise<CertificateInfo>;
|
|
23
|
+
export {};
|