port-scanner-mcp 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 ADDED
@@ -0,0 +1,21 @@
1
+ # Port Scanner MCP
2
+
3
+ Scan local or remote hosts for open TCP ports. Runs locally with no external service. Only use on hosts you own or have permission to scan.
4
+
5
+ This file is self contained. It reads public data only and never writes to the machine. All output is bounded and honest about what could not be fetched.
6
+
7
+ ## Tools
8
+
9
+
10
+ * `scan_host` Scan a list of ports.
11
+ * `scan_common` Scan common ports.
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ npm install
17
+ npm run build
18
+ node dist/index.js
19
+ ```
20
+
21
+ Scans are bounded to 50 ports per call and 2 seconds per port. Scanning systems you do not own may be illegal where you live.
package/dist/api.js ADDED
@@ -0,0 +1,52 @@
1
+ import { connect } from "node:net";
2
+ const UA = "mrfentmen-port-scanner-mcp/1.0";
3
+ export class PortError extends Error {
4
+ }
5
+ function check(host, port, timeoutMs) {
6
+ return new Promise((resolve) => {
7
+ const sock = connect({ host, port });
8
+ let done = false;
9
+ const finish = (open) => {
10
+ if (done)
11
+ return;
12
+ done = true;
13
+ sock.destroy();
14
+ resolve(open);
15
+ };
16
+ sock.setTimeout(timeoutMs);
17
+ sock.once("connect", () => finish(true));
18
+ sock.once("timeout", () => finish(false));
19
+ sock.once("error", () => finish(false));
20
+ });
21
+ }
22
+ export async function scanHost(args) {
23
+ const host = (args.host ?? "").trim();
24
+ if (!host)
25
+ throw new PortError("Provide a host name or IP");
26
+ const timeoutMs = Math.min(Math.max(args.timeout_ms ?? 1000, 100), 2000);
27
+ const ports = (args.ports ?? "80,443,22,21,25,53,8080,3306,5432,6379")
28
+ .split(",").map((p) => parseInt(p.trim(), 10)).filter((p) => p >= 1 && p <= 65535).slice(0, 50);
29
+ if (!ports.length)
30
+ throw new PortError("Provide a valid port list");
31
+ const results = [];
32
+ for (const p of ports) {
33
+ const open = await check(host, p, timeoutMs);
34
+ results.push(`${p}: ${open ? "OPEN" : "closed"}`);
35
+ }
36
+ return `Scan of ${host}:\n${results.join("\n")}`;
37
+ }
38
+ export async function scanCommon(args) {
39
+ const host = (args.host ?? "").trim();
40
+ if (!host)
41
+ throw new PortError("Provide a host name or IP");
42
+ const common = [21, 22, 23, 25, 53, 80, 110, 143, 443, 445, 993, 995, 1433, 1521, 3306, 3389, 5432, 5900, 6379, 8080, 8443, 9000, 27017];
43
+ const results = [];
44
+ for (const p of common) {
45
+ const open = await check(host, p, 800);
46
+ if (open)
47
+ results.push(`${p}: OPEN`);
48
+ }
49
+ return results.length
50
+ ? `Open ports on ${host}:\n${results.join("\n")}`
51
+ : `No common ports open on ${host}`;
52
+ }
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2
+ import { createServer } from "./server.js";
3
+ const main = async () => { const server = createServer(); await server.connect(new StdioServerTransport()); };
4
+ main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });
package/dist/server.js ADDED
@@ -0,0 +1,38 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { scanCommon } from "./api.js";
4
+ import { scanHost } from "./api.js";
5
+ const text = (value) => ({ content: [{ type: "text", text: value }] });
6
+ const textError = (t) => ({ content: [{ type: "text", text: t }], isError: true });
7
+ const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
8
+ const error = (e) => `Error: ${e instanceof Error ? e.message : String(e)}`;
9
+ export function createServer() {
10
+ const server = new McpServer({ name: "port-scanner-mcp", version: "1.0.0" });
11
+ server.registerTool("scan_host", {
12
+ title: "Scan host",
13
+ description: "Scan a host for open ports.",
14
+ inputSchema: z.object({ host: z.string().describe("Host name or IP."), ports: z.string().describe("Comma separated port list.").optional(), timeout_ms: z.number().describe("Per port timeout in milliseconds.").optional() }),
15
+ annotations: READ_ONLY,
16
+ }, async (args) => {
17
+ try {
18
+ return text(await scanHost(args));
19
+ }
20
+ catch (e) {
21
+ return textError(error(e));
22
+ }
23
+ });
24
+ server.registerTool("scan_common", {
25
+ title: "Scan common",
26
+ description: "Scan a host against common ports.",
27
+ inputSchema: z.object({ host: z.string().describe("Host name or IP.") }),
28
+ annotations: READ_ONLY,
29
+ }, async (args) => {
30
+ try {
31
+ return text(await scanCommon(args));
32
+ }
33
+ catch (e) {
34
+ return textError(error(e));
35
+ }
36
+ });
37
+ return server;
38
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "type": "module",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/mrfentmen/port-scanner-mcp.git"
7
+ },
8
+ "bin": {
9
+ "port-scanner-mcp": "./dist/index.js"
10
+ },
11
+ "main": "./dist/index.js",
12
+ "files": [
13
+ "dist",
14
+ "server.json",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.json",
19
+ "start": "node dist/index.js",
20
+ "dev": "npm run build && node dist/index.js"
21
+ },
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.0.4",
25
+ "zod": "^3.23.8"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.0.0",
29
+ "typescript": "^5.6.0"
30
+ },
31
+ "name": "port-scanner-mcp",
32
+ "description": "Scan local or remote hosts for open TCP ports. Runs locally.",
33
+ "mcpName": "io.github.mrfentmen/port-scanner-mcp",
34
+ "keywords": [
35
+ "mcp",
36
+ "network",
37
+ "ports",
38
+ "scan",
39
+ "tcp"
40
+ ],
41
+ "engines": {
42
+ "node": ">=20"
43
+ }
44
+ }
package/server.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.mrfentmen/port-scanner-mcp",
4
+ "description": "Scan local or remote hosts for open TCP ports. Runs locally.",
5
+ "repository": {
6
+ "url": "https://github.com/mrfentmen/port-scanner-mcp",
7
+ "source": "github"
8
+ },
9
+ "version": "1.0.0",
10
+ "packages": [
11
+ {
12
+ "registryType": "npm",
13
+ "identifier": "port-scanner-mcp",
14
+ "version": "1.0.0",
15
+ "transport": {
16
+ "type": "stdio"
17
+ }
18
+ }
19
+ ]
20
+ }