@tmhs/homelab-mcp 0.3.0 → 0.5.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 +9 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/backupDiff.d.ts +3 -0
- package/dist/tools/backupDiff.d.ts.map +1 -0
- package/dist/tools/backupDiff.js +19 -0
- package/dist/tools/backupDiff.js.map +1 -0
- package/dist/tools/backupList.d.ts +3 -0
- package/dist/tools/backupList.d.ts.map +1 -0
- package/dist/tools/backupList.js +28 -0
- package/dist/tools/backupList.js.map +1 -0
- package/dist/tools/backupRestore.d.ts +3 -0
- package/dist/tools/backupRestore.d.ts.map +1 -0
- package/dist/tools/backupRestore.js +41 -0
- package/dist/tools/backupRestore.js.map +1 -0
- package/dist/tools/containerScan.d.ts +3 -0
- package/dist/tools/containerScan.d.ts.map +1 -0
- package/dist/tools/containerScan.js +36 -0
- package/dist/tools/containerScan.js.map +1 -0
- package/dist/tools/fail2banStatus.d.ts +3 -0
- package/dist/tools/fail2banStatus.d.ts.map +1 -0
- package/dist/tools/fail2banStatus.js +23 -0
- package/dist/tools/fail2banStatus.js.map +1 -0
- package/dist/tools/openPorts.d.ts +3 -0
- package/dist/tools/openPorts.d.ts.map +1 -0
- package/dist/tools/openPorts.js +13 -0
- package/dist/tools/openPorts.js.map +1 -0
- package/dist/tools/ufwStatus.d.ts +3 -0
- package/dist/tools/ufwStatus.d.ts.map +1 -0
- package/dist/tools/ufwStatus.js +13 -0
- package/dist/tools/ufwStatus.js.map +1 -0
- package/dist/tools/volumeBackup.d.ts +3 -0
- package/dist/tools/volumeBackup.d.ts.map +1 -0
- package/dist/tools/volumeBackup.js +30 -0
- package/dist/tools/volumeBackup.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Home Lab MCP Server
|
|
2
2
|
|
|
3
|
-
MCP (Model Context Protocol) server for home lab operations. Connects to a Raspberry Pi via SSH and provides
|
|
3
|
+
MCP (Model Context Protocol) server for home lab operations. Connects to a Raspberry Pi via SSH and provides 33 tools for system management, Docker Compose stacks, service monitoring, networking, backups, disaster recovery, and security auditing.
|
|
4
4
|
|
|
5
5
|
## Tools
|
|
6
6
|
|
|
@@ -20,6 +20,10 @@ MCP (Model Context Protocol) server for home lab operations. Connects to a Raspb
|
|
|
20
20
|
| Network | `homelab_networkInfo` | IP addresses, DNS, Tailscale status |
|
|
21
21
|
| Backup | `homelab_backupStatus` | Check latest restic snapshots |
|
|
22
22
|
| Backup | `homelab_backupRun` | Trigger restic backup |
|
|
23
|
+
| Backup | `homelab_backupList` | List all restic snapshots with filtering |
|
|
24
|
+
| Backup | `homelab_backupRestore` | Restore files from a snapshot |
|
|
25
|
+
| Backup | `homelab_backupDiff` | Diff two restic snapshots |
|
|
26
|
+
| Backup | `homelab_volumeBackup` | Back up a Docker volume to restic |
|
|
23
27
|
| Monitoring | `homelab_prometheusQuery` | Run PromQL queries against Prometheus |
|
|
24
28
|
| Monitoring | `homelab_grafanaSnapshot` | Export Grafana dashboard config by UID |
|
|
25
29
|
| Monitoring | `homelab_uptimeKumaStatus` | Get Uptime Kuma monitor statuses |
|
|
@@ -30,6 +34,10 @@ MCP (Model Context Protocol) server for home lab operations. Connects to a Raspb
|
|
|
30
34
|
| DNS/Proxy | `homelab_adguardQueryLog` | Search AdGuard DNS query log |
|
|
31
35
|
| DNS/Proxy | `homelab_npmProxyHosts` | List NPM proxy host configs |
|
|
32
36
|
| DNS/Proxy | `homelab_npmCerts` | List SSL certificates and expiry |
|
|
37
|
+
| Security | `homelab_ufwStatus` | List UFW firewall rules and status |
|
|
38
|
+
| Security | `homelab_fail2banStatus` | List fail2ban jails and banned IPs |
|
|
39
|
+
| Security | `homelab_openPorts` | Scan listening TCP ports and map to processes |
|
|
40
|
+
| Security | `homelab_containerScan` | Scan container images for vulnerabilities via Trivy |
|
|
33
41
|
| SSH | `homelab_sshTest` | Test SSH connectivity |
|
|
34
42
|
|
|
35
43
|
## Setup
|
package/dist/index.js
CHANGED
|
@@ -26,9 +26,17 @@ import { register as registerAdguardFilters } from "./tools/adguardFilters.js";
|
|
|
26
26
|
import { register as registerAdguardQueryLog } from "./tools/adguardQueryLog.js";
|
|
27
27
|
import { register as registerNpmProxyHosts } from "./tools/npmProxyHosts.js";
|
|
28
28
|
import { register as registerNpmCerts } from "./tools/npmCerts.js";
|
|
29
|
+
import { register as registerBackupList } from "./tools/backupList.js";
|
|
30
|
+
import { register as registerBackupRestore } from "./tools/backupRestore.js";
|
|
31
|
+
import { register as registerBackupDiff } from "./tools/backupDiff.js";
|
|
32
|
+
import { register as registerVolumeBackup } from "./tools/volumeBackup.js";
|
|
33
|
+
import { register as registerUfwStatus } from "./tools/ufwStatus.js";
|
|
34
|
+
import { register as registerFail2banStatus } from "./tools/fail2banStatus.js";
|
|
35
|
+
import { register as registerOpenPorts } from "./tools/openPorts.js";
|
|
36
|
+
import { register as registerContainerScan } from "./tools/containerScan.js";
|
|
29
37
|
const server = new McpServer({
|
|
30
38
|
name: "homelab-mcp",
|
|
31
|
-
version: "0.
|
|
39
|
+
version: "0.5.0",
|
|
32
40
|
});
|
|
33
41
|
registerPiStatus(server);
|
|
34
42
|
registerPiReboot(server);
|
|
@@ -55,6 +63,14 @@ registerAdguardFilters(server);
|
|
|
55
63
|
registerAdguardQueryLog(server);
|
|
56
64
|
registerNpmProxyHosts(server);
|
|
57
65
|
registerNpmCerts(server);
|
|
66
|
+
registerBackupList(server);
|
|
67
|
+
registerBackupRestore(server);
|
|
68
|
+
registerBackupDiff(server);
|
|
69
|
+
registerVolumeBackup(server);
|
|
70
|
+
registerUfwStatus(server);
|
|
71
|
+
registerFail2banStatus(server);
|
|
72
|
+
registerOpenPorts(server);
|
|
73
|
+
registerContainerScan(server);
|
|
58
74
|
async function main() {
|
|
59
75
|
const transport = new StdioServerTransport();
|
|
60
76
|
await server.connect(transport);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,QAAQ,IAAI,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,QAAQ,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,QAAQ,IAAI,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,QAAQ,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,QAAQ,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,QAAQ,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,QAAQ,IAAI,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,QAAQ,IAAI,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,QAAQ,IAAI,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,QAAQ,IAAI,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,QAAQ,IAAI,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,QAAQ,IAAI,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,QAAQ,IAAI,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,QAAQ,IAAI,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backupDiff.d.ts","sourceRoot":"","sources":["../../src/tools/backupDiff.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkBhD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
const inputSchema = {
|
|
4
|
+
snapshotA: z.string().min(1).describe("First snapshot ID (older)"),
|
|
5
|
+
snapshotB: z.string().min(1).describe("Second snapshot ID (newer)"),
|
|
6
|
+
};
|
|
7
|
+
export function register(server) {
|
|
8
|
+
server.tool("homelab_backupDiff", "Show the difference between two restic backup snapshots", inputSchema, async (args) => {
|
|
9
|
+
try {
|
|
10
|
+
const repo = process.env.HOMELAB_BACKUP_REPO || "/mnt/backup/restic";
|
|
11
|
+
const output = await execSSH(`sudo restic -r ${repo} diff '${args.snapshotA}' '${args.snapshotB}' 2>&1`);
|
|
12
|
+
return { content: [{ type: "text", text: output }] };
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
return errorResponse(error);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=backupDiff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backupDiff.js","sourceRoot":"","sources":["../../src/tools/backupDiff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,GAAG;IAClB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,2BAA2B,CAAC;IAClE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,4BAA4B,CAAC;CACpE,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,oBAAoB,EACpB,yDAAyD,EACzD,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,oBAAoB,CAAC;YACrE,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,kBAAkB,IAAI,UAAU,IAAI,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,QAAQ,CAC3E,CAAC;YAEF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backupList.d.ts","sourceRoot":"","sources":["../../src/tools/backupList.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAuBhD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
const inputSchema = {
|
|
4
|
+
path: z.string().optional().describe("Filter snapshots containing this path"),
|
|
5
|
+
tag: z.string().optional().describe("Filter snapshots by tag"),
|
|
6
|
+
host: z.string().optional().describe("Filter snapshots by hostname"),
|
|
7
|
+
};
|
|
8
|
+
export function register(server) {
|
|
9
|
+
server.tool("homelab_backupList", "List all restic backup snapshots with optional filtering by path, tag, or host", inputSchema, async (args) => {
|
|
10
|
+
try {
|
|
11
|
+
const repo = process.env.HOMELAB_BACKUP_REPO || "/mnt/backup/restic";
|
|
12
|
+
const flags = [];
|
|
13
|
+
if (args.path)
|
|
14
|
+
flags.push(`--path '${args.path}'`);
|
|
15
|
+
if (args.tag)
|
|
16
|
+
flags.push(`--tag '${args.tag}'`);
|
|
17
|
+
if (args.host)
|
|
18
|
+
flags.push(`--host '${args.host}'`);
|
|
19
|
+
const extra = flags.length > 0 ? " " + flags.join(" ") : "";
|
|
20
|
+
const output = await execSSH(`sudo restic -r ${repo} snapshots${extra} --json 2>/dev/null || echo "No backup repo found at ${repo} or restic not configured"`);
|
|
21
|
+
return { content: [{ type: "text", text: output }] };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return errorResponse(error);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=backupList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backupList.js","sourceRoot":"","sources":["../../src/tools/backupList.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAC7E,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IAC9D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;CACrE,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,oBAAoB,EACpB,gFAAgF,EAChF,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,oBAAoB,CAAC;YACrE,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;YACnD,IAAI,IAAI,CAAC,GAAG;gBAAE,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;YAChD,IAAI,IAAI,CAAC,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;YACnD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,kBAAkB,IAAI,aAAa,KAAK,wDAAwD,IAAI,4BAA4B,CACjI,CAAC;YAEF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backupRestore.d.ts","sourceRoot":"","sources":["../../src/tools/backupRestore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqBzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4BhD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
const inputSchema = {
|
|
4
|
+
snapshot: z
|
|
5
|
+
.string()
|
|
6
|
+
.min(1)
|
|
7
|
+
.describe('Snapshot ID to restore (short hash or "latest")'),
|
|
8
|
+
target: z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.describe("Destination path on the Pi to restore files into"),
|
|
12
|
+
include: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Only restore files matching this pattern (e.g. /opt/homelab/docker/)"),
|
|
16
|
+
confirm: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.describe("Must be true to proceed with restore. Safety check."),
|
|
19
|
+
};
|
|
20
|
+
export function register(server) {
|
|
21
|
+
server.tool("homelab_backupRestore", "Restore files from a restic backup snapshot to a target path on the Pi", inputSchema, async (args) => {
|
|
22
|
+
try {
|
|
23
|
+
if (!args.confirm) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: "Restore cancelled. Set confirm=true to proceed.",
|
|
28
|
+
}],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const repo = process.env.HOMELAB_BACKUP_REPO || "/mnt/backup/restic";
|
|
32
|
+
const includeFlag = args.include ? ` --include '${args.include}'` : "";
|
|
33
|
+
const output = await execSSH(`sudo restic -r ${repo} restore '${args.snapshot}' --target '${args.target}'${includeFlag} 2>&1`);
|
|
34
|
+
return { content: [{ type: "text", text: output }] };
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return errorResponse(error);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=backupRestore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backupRestore.js","sourceRoot":"","sources":["../../src/tools/backupRestore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,GAAG;IAClB,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,iDAAiD,CAAC;IAC9D,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,kDAAkD,CAAC;IAC/D,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,sEAAsE,CAAC;IACnF,OAAO,EAAE,CAAC;SACP,OAAO,EAAE;SACT,QAAQ,CAAC,qDAAqD,CAAC;CACnE,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,wEAAwE,EACxE,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,iDAAiD;yBACxD,CAAC;iBACH,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,oBAAoB,CAAC;YACrE,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,kBAAkB,IAAI,aAAa,IAAI,CAAC,QAAQ,eAAe,IAAI,CAAC,MAAM,IAAI,WAAW,OAAO,CACjG,CAAC;YAEF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"containerScan.d.ts","sourceRoot":"","sources":["../../src/tools/containerScan.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkChD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
const inputSchema = {
|
|
4
|
+
image: z
|
|
5
|
+
.string()
|
|
6
|
+
.optional()
|
|
7
|
+
.describe("Specific container image to scan. Scans all running images if omitted"),
|
|
8
|
+
};
|
|
9
|
+
export function register(server) {
|
|
10
|
+
server.tool("homelab_containerScan", "Scan running container images for HIGH/CRITICAL vulnerabilities using Trivy", inputSchema, async (args) => {
|
|
11
|
+
try {
|
|
12
|
+
const trivyCheck = await execSSH("command -v trivy >/dev/null 2>&1 && echo 'installed' || echo 'missing'");
|
|
13
|
+
if (trivyCheck.trim() === "missing") {
|
|
14
|
+
return {
|
|
15
|
+
content: [{
|
|
16
|
+
type: "text",
|
|
17
|
+
text: "Trivy is not installed on the Pi. Install it with:\n\n" +
|
|
18
|
+
"sudo apt install -y apt-transport-https gnupg\n" +
|
|
19
|
+
"wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null\n" +
|
|
20
|
+
'echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee /etc/apt/sources.list.d/trivy.list\n' +
|
|
21
|
+
"sudo apt update && sudo apt install -y trivy",
|
|
22
|
+
}],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const cmd = args.image
|
|
26
|
+
? `trivy image --severity HIGH,CRITICAL --format table '${args.image}' 2>&1`
|
|
27
|
+
: "docker ps --format '{{.Image}}' | sort -u | while read img; do echo \"=== $img ===\"; trivy image --severity HIGH,CRITICAL --format table \"$img\" 2>&1; echo; done";
|
|
28
|
+
const output = await execSSH(cmd);
|
|
29
|
+
return { content: [{ type: "text", text: output }] };
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return errorResponse(error);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=containerScan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"containerScan.js","sourceRoot":"","sources":["../../src/tools/containerScan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,uEAAuE,CAAC;CACrF,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,6EAA6E,EAC7E,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,OAAO,CAC9B,wEAAwE,CACzE,CAAC;YAEF,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;gBACpC,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,wDAAwD;gCAC5D,iDAAiD;gCACjD,4IAA4I;gCAC5I,iKAAiK;gCACjK,8CAA8C;yBACjD,CAAC;iBACH,CAAC;YACJ,CAAC;YAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK;gBACpB,CAAC,CAAC,wDAAwD,IAAI,CAAC,KAAK,QAAQ;gBAC5E,CAAC,CAAC,qKAAqK,CAAC;YAC1K,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fail2banStatus.d.ts","sourceRoot":"","sources":["../../src/tools/fail2banStatus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiBhD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
const inputSchema = {
|
|
4
|
+
jail: z
|
|
5
|
+
.string()
|
|
6
|
+
.optional()
|
|
7
|
+
.describe("Specific jail name to query (e.g. sshd). Returns all jails if omitted"),
|
|
8
|
+
};
|
|
9
|
+
export function register(server) {
|
|
10
|
+
server.tool("homelab_fail2banStatus", "List fail2ban jails, banned IPs, and ban counts", inputSchema, async (args) => {
|
|
11
|
+
try {
|
|
12
|
+
const cmd = args.jail
|
|
13
|
+
? `sudo fail2ban-client status '${args.jail}' 2>&1`
|
|
14
|
+
: "sudo fail2ban-client status 2>&1";
|
|
15
|
+
const output = await execSSH(cmd);
|
|
16
|
+
return { content: [{ type: "text", text: output }] };
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return errorResponse(error);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=fail2banStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fail2banStatus.js","sourceRoot":"","sources":["../../src/tools/fail2banStatus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,uEAAuE,CAAC;CACrF,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,iDAAiD,EACjD,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI;gBACnB,CAAC,CAAC,gCAAgC,IAAI,CAAC,IAAI,QAAQ;gBACnD,CAAC,CAAC,kCAAkC,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openPorts.d.ts","sourceRoot":"","sources":["../../src/tools/openPorts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAchD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
2
|
+
export function register(server) {
|
|
3
|
+
server.tool("homelab_openPorts", "Scan for listening TCP ports and map them to processes", {}, async () => {
|
|
4
|
+
try {
|
|
5
|
+
const output = await execSSH("sudo ss -tlnp 2>&1");
|
|
6
|
+
return { content: [{ type: "text", text: output }] };
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
return errorResponse(error);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=openPorts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openPorts.js","sourceRoot":"","sources":["../../src/tools/openPorts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,wDAAwD,EACxD,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;YACnD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ufwStatus.d.ts","sourceRoot":"","sources":["../../src/tools/ufwStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAchD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
2
|
+
export function register(server) {
|
|
3
|
+
server.tool("homelab_ufwStatus", "List UFW firewall rules and status", {}, async () => {
|
|
4
|
+
try {
|
|
5
|
+
const output = await execSSH("sudo ufw status numbered 2>&1");
|
|
6
|
+
return { content: [{ type: "text", text: output }] };
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
return errorResponse(error);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=ufwStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ufwStatus.js","sourceRoot":"","sources":["../../src/tools/ufwStatus.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,oCAAoC,EACpC,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,+BAA+B,CAAC,CAAC;YAC9D,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volumeBackup.d.ts","sourceRoot":"","sources":["../../src/tools/volumeBackup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4BhD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
const inputSchema = {
|
|
4
|
+
volume: z.string().min(1).describe("Docker volume name to back up"),
|
|
5
|
+
confirm: z
|
|
6
|
+
.boolean()
|
|
7
|
+
.describe("Must be true to trigger backup. Safety check."),
|
|
8
|
+
};
|
|
9
|
+
export function register(server) {
|
|
10
|
+
server.tool("homelab_volumeBackup", "Back up a specific Docker volume to the restic repository", inputSchema, async (args) => {
|
|
11
|
+
try {
|
|
12
|
+
if (!args.confirm) {
|
|
13
|
+
return {
|
|
14
|
+
content: [{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: "Volume backup cancelled. Set confirm=true to proceed.",
|
|
17
|
+
}],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const repo = process.env.HOMELAB_BACKUP_REPO || "/mnt/backup/restic";
|
|
21
|
+
const volumePath = `/var/lib/docker/volumes/${args.volume}/_data`;
|
|
22
|
+
const output = await execSSH(`sudo restic -r ${repo} backup '${volumePath}' --tag docker-volume --tag '${args.volume}' 2>&1`);
|
|
23
|
+
return { content: [{ type: "text", text: output }] };
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return errorResponse(error);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=volumeBackup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volumeBackup.js","sourceRoot":"","sources":["../../src/tools/volumeBackup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IACnE,OAAO,EAAE,CAAC;SACP,OAAO,EAAE;SACT,QAAQ,CAAC,+CAA+C,CAAC;CAC7D,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,sBAAsB,EACtB,2DAA2D,EAC3D,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,OAAO;oBACL,OAAO,EAAE,CAAC;4BACR,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,uDAAuD;yBAC9D,CAAC;iBACH,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,oBAAoB,CAAC;YACrE,MAAM,UAAU,GAAG,2BAA2B,IAAI,CAAC,MAAM,QAAQ,CAAC;YAClE,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,kBAAkB,IAAI,YAAY,UAAU,gCAAgC,IAAI,CAAC,MAAM,QAAQ,CAChG,CAAC;YAEF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tmhs/homelab-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for home lab operations via SSH -
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "MCP server for home lab operations via SSH - 33 tools for system status, Docker Compose management, service health, monitoring, DNS, reverse proxy, networking, backups, disaster recovery, security auditing, and administration on a Raspberry Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|