@tmhs/homelab-mcp 0.1.0 → 0.2.1
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 +31 -31
- package/README.md +84 -71
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/alertList.d.ts +3 -0
- package/dist/tools/alertList.d.ts.map +1 -0
- package/dist/tools/alertList.js +35 -0
- package/dist/tools/alertList.js.map +1 -0
- package/dist/tools/grafanaSnapshot.d.ts +3 -0
- package/dist/tools/grafanaSnapshot.d.ts.map +1 -0
- package/dist/tools/grafanaSnapshot.js +49 -0
- package/dist/tools/grafanaSnapshot.js.map +1 -0
- package/dist/tools/prometheusQuery.d.ts +3 -0
- package/dist/tools/prometheusQuery.d.ts.map +1 -0
- package/dist/tools/prometheusQuery.js +37 -0
- package/dist/tools/prometheusQuery.js.map +1 -0
- package/dist/tools/speedtestResults.d.ts +3 -0
- package/dist/tools/speedtestResults.d.ts.map +1 -0
- package/dist/tools/speedtestResults.js +37 -0
- package/dist/tools/speedtestResults.js.map +1 -0
- package/dist/tools/uptimeKumaStatus.d.ts +3 -0
- package/dist/tools/uptimeKumaStatus.d.ts.map +1 -0
- package/dist/tools/uptimeKumaStatus.js +27 -0
- package/dist/tools/uptimeKumaStatus.js.map +1 -0
- package/package.json +49 -49
package/LICENSE
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 TM Hospitality Strategies
|
|
4
|
-
|
|
5
|
-
SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
|
6
|
-
|
|
7
|
-
You are free to:
|
|
8
|
-
|
|
9
|
-
Share - copy and redistribute the material in any medium or format.
|
|
10
|
-
|
|
11
|
-
The licensor cannot revoke these freedoms as long as you follow the
|
|
12
|
-
license terms.
|
|
13
|
-
|
|
14
|
-
Under the following terms:
|
|
15
|
-
|
|
16
|
-
Attribution - You must give appropriate credit, provide a link to
|
|
17
|
-
the license, and indicate if changes were made. You may do so in
|
|
18
|
-
any reasonable manner, but not in any way that suggests the licensor
|
|
19
|
-
endorses you or your use.
|
|
20
|
-
|
|
21
|
-
NonCommercial - You may not use the material for commercial purposes.
|
|
22
|
-
|
|
23
|
-
NoDerivatives - If you remix, transform, or build upon the material,
|
|
24
|
-
you may not distribute the modified material.
|
|
25
|
-
|
|
26
|
-
No additional restrictions - You may not apply legal terms or
|
|
27
|
-
technological measures that legally restrict others from doing
|
|
28
|
-
anything the license permits.
|
|
29
|
-
|
|
30
|
-
Full license text:
|
|
31
|
-
https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode
|
|
1
|
+
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TM Hospitality Strategies
|
|
4
|
+
|
|
5
|
+
SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
|
6
|
+
|
|
7
|
+
You are free to:
|
|
8
|
+
|
|
9
|
+
Share - copy and redistribute the material in any medium or format.
|
|
10
|
+
|
|
11
|
+
The licensor cannot revoke these freedoms as long as you follow the
|
|
12
|
+
license terms.
|
|
13
|
+
|
|
14
|
+
Under the following terms:
|
|
15
|
+
|
|
16
|
+
Attribution - You must give appropriate credit, provide a link to
|
|
17
|
+
the license, and indicate if changes were made. You may do so in
|
|
18
|
+
any reasonable manner, but not in any way that suggests the licensor
|
|
19
|
+
endorses you or your use.
|
|
20
|
+
|
|
21
|
+
NonCommercial - You may not use the material for commercial purposes.
|
|
22
|
+
|
|
23
|
+
NoDerivatives - If you remix, transform, or build upon the material,
|
|
24
|
+
you may not distribute the modified material.
|
|
25
|
+
|
|
26
|
+
No additional restrictions - You may not apply legal terms or
|
|
27
|
+
technological measures that legally restrict others from doing
|
|
28
|
+
anything the license permits.
|
|
29
|
+
|
|
30
|
+
Full license text:
|
|
31
|
+
https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode
|
package/README.md
CHANGED
|
@@ -1,71 +1,84 @@
|
|
|
1
|
-
# Home Lab MCP Server
|
|
2
|
-
|
|
3
|
-
MCP (Model Context Protocol) server for home lab operations. Connects to a Raspberry Pi via SSH and provides
|
|
4
|
-
|
|
5
|
-
## Tools
|
|
6
|
-
|
|
7
|
-
| Category | Tool | Description |
|
|
8
|
-
|----------|------|-------------|
|
|
9
|
-
| System | `homelab_piStatus` | CPU temp, memory, disk, uptime, throttle state |
|
|
10
|
-
| System | `homelab_piReboot` | Safe reboot with pre-checks |
|
|
11
|
-
| System | `homelab_diskUsage` | Disk usage breakdown by directory |
|
|
12
|
-
| System | `homelab_aptUpdate` | Run apt update, list upgradable packages |
|
|
13
|
-
| Containers | `homelab_serviceHealth` | Docker container health status |
|
|
14
|
-
| Containers | `homelab_serviceLogs` | Tail container logs |
|
|
15
|
-
| Containers | `homelab_serviceRestart` | Restart a container |
|
|
16
|
-
| Compose | `homelab_composeUp` | Start compose stacks |
|
|
17
|
-
| Compose | `homelab_composeDown` | Stop compose stacks |
|
|
18
|
-
| Compose | `homelab_composePull` | Pull latest images |
|
|
19
|
-
| Compose | `homelab_composePs` | List running containers |
|
|
20
|
-
| Network | `homelab_networkInfo` | IP addresses, DNS, Tailscale status |
|
|
21
|
-
| Backup | `homelab_backupStatus` | Check latest restic snapshots |
|
|
22
|
-
| Backup | `homelab_backupRun` | Trigger restic backup |
|
|
23
|
-
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
1
|
+
# Home Lab MCP Server
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for home lab operations. Connects to a Raspberry Pi via SSH and provides 20 tools for system management, Docker Compose stacks, service monitoring, networking, and backups.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
| Category | Tool | Description |
|
|
8
|
+
|----------|------|-------------|
|
|
9
|
+
| System | `homelab_piStatus` | CPU temp, memory, disk, uptime, throttle state |
|
|
10
|
+
| System | `homelab_piReboot` | Safe reboot with pre-checks |
|
|
11
|
+
| System | `homelab_diskUsage` | Disk usage breakdown by directory |
|
|
12
|
+
| System | `homelab_aptUpdate` | Run apt update, list upgradable packages |
|
|
13
|
+
| Containers | `homelab_serviceHealth` | Docker container health status |
|
|
14
|
+
| Containers | `homelab_serviceLogs` | Tail container logs |
|
|
15
|
+
| Containers | `homelab_serviceRestart` | Restart a container |
|
|
16
|
+
| Compose | `homelab_composeUp` | Start compose stacks |
|
|
17
|
+
| Compose | `homelab_composeDown` | Stop compose stacks |
|
|
18
|
+
| Compose | `homelab_composePull` | Pull latest images |
|
|
19
|
+
| Compose | `homelab_composePs` | List running containers |
|
|
20
|
+
| Network | `homelab_networkInfo` | IP addresses, DNS, Tailscale status |
|
|
21
|
+
| Backup | `homelab_backupStatus` | Check latest restic snapshots |
|
|
22
|
+
| Backup | `homelab_backupRun` | Trigger restic backup |
|
|
23
|
+
| Monitoring | `homelab_prometheusQuery` | Run PromQL queries against Prometheus |
|
|
24
|
+
| Monitoring | `homelab_grafanaSnapshot` | Export Grafana dashboard config by UID |
|
|
25
|
+
| Monitoring | `homelab_uptimeKumaStatus` | Get Uptime Kuma monitor statuses |
|
|
26
|
+
| Monitoring | `homelab_alertList` | List Alertmanager alerts by state |
|
|
27
|
+
| Monitoring | `homelab_speedtestResults` | Get recent Speedtest Tracker results |
|
|
28
|
+
| SSH | `homelab_sshTest` | Test SSH connectivity |
|
|
29
|
+
|
|
30
|
+
## Setup
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd mcp-server
|
|
34
|
+
npm install
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
Set environment variables:
|
|
41
|
+
|
|
42
|
+
| Variable | Default | Description |
|
|
43
|
+
|----------|---------|-------------|
|
|
44
|
+
| `HOMELAB_PI_HOST` | `raspi5.local` | Pi hostname or IP |
|
|
45
|
+
| `HOMELAB_PI_USER` | `tmhs` | SSH username |
|
|
46
|
+
| `HOMELAB_PI_KEY_PATH` | (empty) | Path to SSH private key |
|
|
47
|
+
| `HOMELAB_COMPOSE_DIR` | `/opt/homelab/docker` | Compose project directory on Pi |
|
|
48
|
+
| `HOMELAB_BACKUP_REPO` | `/mnt/backup/restic` | Restic backup repo path on Pi |
|
|
49
|
+
| `HOMELAB_GRAFANA_TOKEN` | (empty) | Grafana API token (preferred auth method) |
|
|
50
|
+
| `HOMELAB_GRAFANA_USER` | `admin` | Grafana basic auth username |
|
|
51
|
+
| `HOMELAB_GRAFANA_PASSWORD` | (empty) | Grafana basic auth password (falls back to admin/admin) |
|
|
52
|
+
| `HOMELAB_PROMETHEUS_PORT` | `9090` | Prometheus port override |
|
|
53
|
+
| `HOMELAB_GRAFANA_PORT` | `3000` | Grafana port override |
|
|
54
|
+
| `HOMELAB_ALERTMANAGER_PORT` | `9093` | Alertmanager port override |
|
|
55
|
+
| `HOMELAB_UPTIME_KUMA_PORT` | `3001` | Uptime Kuma port override |
|
|
56
|
+
| `HOMELAB_SPEEDTEST_PORT` | `8765` | Speedtest Tracker port override |
|
|
57
|
+
|
|
58
|
+
## Usage with Cursor
|
|
59
|
+
|
|
60
|
+
Add to `.cursor/mcp.json`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"mcpServers": {
|
|
65
|
+
"homelab": {
|
|
66
|
+
"command": "node",
|
|
67
|
+
"args": ["path/to/Home-Lab-Developer-Tools/mcp-server/dist/index.js"],
|
|
68
|
+
"env": {
|
|
69
|
+
"HOMELAB_PI_HOST": "raspi5.local",
|
|
70
|
+
"HOMELAB_PI_USER": "tmhs",
|
|
71
|
+
"HOMELAB_PI_KEY_PATH": "~/.ssh/id_ed25519_pi"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm run dev # Watch mode with tsx
|
|
82
|
+
npm test # Run Vitest tests
|
|
83
|
+
npm run build # Compile TypeScript
|
|
84
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -16,9 +16,14 @@ import { register as registerBackupStatus } from "./tools/backupStatus.js";
|
|
|
16
16
|
import { register as registerBackupRun } from "./tools/backupRun.js";
|
|
17
17
|
import { register as registerAptUpdate } from "./tools/aptUpdate.js";
|
|
18
18
|
import { register as registerSshTest } from "./tools/sshTest.js";
|
|
19
|
+
import { register as registerPrometheusQuery } from "./tools/prometheusQuery.js";
|
|
20
|
+
import { register as registerGrafanaSnapshot } from "./tools/grafanaSnapshot.js";
|
|
21
|
+
import { register as registerUptimeKumaStatus } from "./tools/uptimeKumaStatus.js";
|
|
22
|
+
import { register as registerAlertList } from "./tools/alertList.js";
|
|
23
|
+
import { register as registerSpeedtestResults } from "./tools/speedtestResults.js";
|
|
19
24
|
const server = new McpServer({
|
|
20
25
|
name: "homelab-mcp",
|
|
21
|
-
version: "0.1
|
|
26
|
+
version: "0.2.1",
|
|
22
27
|
});
|
|
23
28
|
registerPiStatus(server);
|
|
24
29
|
registerPiReboot(server);
|
|
@@ -35,6 +40,11 @@ registerBackupStatus(server);
|
|
|
35
40
|
registerBackupRun(server);
|
|
36
41
|
registerAptUpdate(server);
|
|
37
42
|
registerSshTest(server);
|
|
43
|
+
registerPrometheusQuery(server);
|
|
44
|
+
registerGrafanaSnapshot(server);
|
|
45
|
+
registerUptimeKumaStatus(server);
|
|
46
|
+
registerAlertList(server);
|
|
47
|
+
registerSpeedtestResults(server);
|
|
38
48
|
async function main() {
|
|
39
49
|
const transport = new StdioServerTransport();
|
|
40
50
|
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;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alertList.d.ts","sourceRoot":"","sources":["../../src/tools/alertList.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmBzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6BhD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
import { CommandFailedError } from "../utils/errors.js";
|
|
4
|
+
const DEFAULT_PORT = 9093;
|
|
5
|
+
const SERVICE_NAME = "Alertmanager";
|
|
6
|
+
const inputSchema = {
|
|
7
|
+
state: z
|
|
8
|
+
.enum(["active", "suppressed", "unprocessed"])
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Filter alerts by state. Returns all states if omitted"),
|
|
11
|
+
};
|
|
12
|
+
function getPort() {
|
|
13
|
+
const override = process.env.HOMELAB_ALERTMANAGER_PORT;
|
|
14
|
+
return override ? parseInt(override, 10) : DEFAULT_PORT;
|
|
15
|
+
}
|
|
16
|
+
export function register(server) {
|
|
17
|
+
server.tool("homelab_alertList", "List alerts from Alertmanager, optionally filtered by state", inputSchema, async (args) => {
|
|
18
|
+
const port = getPort();
|
|
19
|
+
try {
|
|
20
|
+
const stateParam = args.state ? `?state=${args.state}` : "";
|
|
21
|
+
const output = await execSSH(`curl -sf 'http://localhost:${port}/api/v2/alerts${stateParam}'`);
|
|
22
|
+
return { content: [{ type: "text", text: output }] };
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error instanceof CommandFailedError) {
|
|
26
|
+
if (error.exitCode === 7) {
|
|
27
|
+
return errorResponse(new Error(`Could not connect to ${SERVICE_NAME} on port ${port}. Is it running? ` +
|
|
28
|
+
`Set HOMELAB_ALERTMANAGER_PORT if using a non-default port.`));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return errorResponse(error);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=alertList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alertList.js","sourceRoot":"","sources":["../../src/tools/alertList.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,cAAc,CAAC;AAEpC,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,CAAC;SACL,IAAI,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;SAC7C,QAAQ,EAAE;SACV,QAAQ,CAAC,uDAAuD,CAAC;CACrE,CAAC;AAEF,SAAS,OAAO;IACd,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IACvD,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,6DAA6D,EAC7D,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,8BAA8B,IAAI,iBAAiB,UAAU,GAAG,CACjE,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,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;gBACxC,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACzB,OAAO,aAAa,CAClB,IAAI,KAAK,CACP,wBAAwB,YAAY,YAAY,IAAI,mBAAmB;wBACrE,4DAA4D,CAC/D,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grafanaSnapshot.d.ts","sourceRoot":"","sources":["../../src/tools/grafanaSnapshot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6BzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsChD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
import { CommandFailedError } from "../utils/errors.js";
|
|
4
|
+
const DEFAULT_PORT = 3000;
|
|
5
|
+
const SERVICE_NAME = "Grafana";
|
|
6
|
+
const inputSchema = {
|
|
7
|
+
dashboard: z.string().min(1).describe("Dashboard UID to export"),
|
|
8
|
+
};
|
|
9
|
+
function getPort() {
|
|
10
|
+
const override = process.env.HOMELAB_GRAFANA_PORT;
|
|
11
|
+
return override ? parseInt(override, 10) : DEFAULT_PORT;
|
|
12
|
+
}
|
|
13
|
+
function buildAuthHeader() {
|
|
14
|
+
const token = process.env.HOMELAB_GRAFANA_TOKEN;
|
|
15
|
+
if (token) {
|
|
16
|
+
return `-H 'Authorization: Bearer ${token}'`;
|
|
17
|
+
}
|
|
18
|
+
const user = process.env.HOMELAB_GRAFANA_USER || "admin";
|
|
19
|
+
const password = process.env.HOMELAB_GRAFANA_PASSWORD;
|
|
20
|
+
if (password) {
|
|
21
|
+
return `-u '${user}:${password}'`;
|
|
22
|
+
}
|
|
23
|
+
return `-u 'admin:admin'`;
|
|
24
|
+
}
|
|
25
|
+
export function register(server) {
|
|
26
|
+
server.tool("homelab_grafanaSnapshot", "Export a Grafana dashboard configuration by UID", inputSchema, async (args) => {
|
|
27
|
+
const port = getPort();
|
|
28
|
+
try {
|
|
29
|
+
const auth = buildAuthHeader();
|
|
30
|
+
const output = await execSSH(`curl -sf ${auth} 'http://localhost:${port}/api/dashboards/uid/${args.dashboard}'`);
|
|
31
|
+
return { content: [{ type: "text", text: output }] };
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error instanceof CommandFailedError) {
|
|
35
|
+
if (error.exitCode === 7) {
|
|
36
|
+
return errorResponse(new Error(`Could not reach ${SERVICE_NAME} API on port ${port}. Is it running? ` +
|
|
37
|
+
`Set HOMELAB_GRAFANA_PORT if using a non-default port.`));
|
|
38
|
+
}
|
|
39
|
+
if (error.exitCode === 22) {
|
|
40
|
+
return errorResponse(new Error(`${SERVICE_NAME} returned an HTTP error. Check authentication -- ` +
|
|
41
|
+
`set HOMELAB_GRAFANA_TOKEN (API key) or HOMELAB_GRAFANA_USER/HOMELAB_GRAFANA_PASSWORD. ` +
|
|
42
|
+
`Also verify the dashboard UID "${args.dashboard}" exists.`));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return errorResponse(error);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=grafanaSnapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grafanaSnapshot.js","sourceRoot":"","sources":["../../src/tools/grafanaSnapshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,SAAS,CAAC;AAE/B,MAAM,WAAW,GAAG;IAClB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,yBAAyB,CAAC;CACjE,CAAC;AAEF,SAAS,OAAO;IACd,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAClD,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AAC1D,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAChD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,6BAA6B,KAAK,GAAG,CAAC;IAC/C,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,OAAO,CAAC;IACzD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IACtD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,OAAO,IAAI,IAAI,QAAQ,GAAG,CAAC;IACpC,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,yBAAyB,EACzB,iDAAiD,EACjD,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,YAAY,IAAI,sBAAsB,IAAI,uBAAuB,IAAI,CAAC,SAAS,GAAG,CACnF,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,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;gBACxC,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACzB,OAAO,aAAa,CAClB,IAAI,KAAK,CACP,mBAAmB,YAAY,gBAAgB,IAAI,mBAAmB;wBACpE,uDAAuD,CAC1D,CACF,CAAC;gBACJ,CAAC;gBACD,IAAI,KAAK,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;oBAC1B,OAAO,aAAa,CAClB,IAAI,KAAK,CACP,GAAG,YAAY,mDAAmD;wBAChE,wFAAwF;wBACxF,kCAAkC,IAAI,CAAC,SAAS,WAAW,CAC9D,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prometheusQuery.d.ts","sourceRoot":"","sources":["../../src/tools/prometheusQuery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoBzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8BhD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
import { CommandFailedError } from "../utils/errors.js";
|
|
4
|
+
const DEFAULT_PORT = 9090;
|
|
5
|
+
const SERVICE_NAME = "Prometheus";
|
|
6
|
+
const inputSchema = {
|
|
7
|
+
query: z.string().min(1).describe("PromQL expression to evaluate"),
|
|
8
|
+
time: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Evaluation timestamp (RFC3339 or Unix). Defaults to current time"),
|
|
12
|
+
};
|
|
13
|
+
function getPort() {
|
|
14
|
+
const override = process.env.HOMELAB_PROMETHEUS_PORT;
|
|
15
|
+
return override ? parseInt(override, 10) : DEFAULT_PORT;
|
|
16
|
+
}
|
|
17
|
+
export function register(server) {
|
|
18
|
+
server.tool("homelab_prometheusQuery", "Run a PromQL query against Prometheus and return the result", inputSchema, async (args) => {
|
|
19
|
+
const port = getPort();
|
|
20
|
+
try {
|
|
21
|
+
const encoded = encodeURIComponent(args.query);
|
|
22
|
+
const timeParam = args.time ? `&time=${encodeURIComponent(args.time)}` : "";
|
|
23
|
+
const output = await execSSH(`curl -sf 'http://localhost:${port}/api/v1/query?query=${encoded}${timeParam}'`);
|
|
24
|
+
return { content: [{ type: "text", text: output }] };
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (error instanceof CommandFailedError) {
|
|
28
|
+
if (error.exitCode === 7) {
|
|
29
|
+
return errorResponse(new Error(`Could not reach ${SERVICE_NAME} on port ${port}. Is it running? ` +
|
|
30
|
+
`Set HOMELAB_PROMETHEUS_PORT if using a non-default port.`));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return errorResponse(error);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=prometheusQuery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prometheusQuery.js","sourceRoot":"","sources":["../../src/tools/prometheusQuery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,YAAY,CAAC;AAElC,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IAClE,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,kEAAkE,CAAC;CAChF,CAAC;AAEF,SAAS,OAAO;IACd,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IACrD,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,yBAAyB,EACzB,6DAA6D,EAC7D,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,8BAA8B,IAAI,uBAAuB,OAAO,GAAG,SAAS,GAAG,CAChF,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,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;gBACxC,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACzB,OAAO,aAAa,CAClB,IAAI,KAAK,CACP,mBAAmB,YAAY,YAAY,IAAI,mBAAmB;wBAChE,0DAA0D,CAC7D,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speedtestResults.d.ts","sourceRoot":"","sources":["../../src/tools/speedtestResults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsBzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4BhD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
3
|
+
import { CommandFailedError } from "../utils/errors.js";
|
|
4
|
+
const DEFAULT_PORT = 8765;
|
|
5
|
+
const SERVICE_NAME = "Speedtest Tracker";
|
|
6
|
+
const inputSchema = {
|
|
7
|
+
count: z
|
|
8
|
+
.number()
|
|
9
|
+
.int()
|
|
10
|
+
.positive()
|
|
11
|
+
.optional()
|
|
12
|
+
.default(5)
|
|
13
|
+
.describe("Number of recent speedtest results to return"),
|
|
14
|
+
};
|
|
15
|
+
function getPort() {
|
|
16
|
+
const override = process.env.HOMELAB_SPEEDTEST_PORT;
|
|
17
|
+
return override ? parseInt(override, 10) : DEFAULT_PORT;
|
|
18
|
+
}
|
|
19
|
+
export function register(server) {
|
|
20
|
+
server.tool("homelab_speedtestResults", "Get recent speedtest results from Speedtest Tracker", inputSchema, async (args) => {
|
|
21
|
+
const port = getPort();
|
|
22
|
+
try {
|
|
23
|
+
const output = await execSSH(`curl -sf 'http://localhost:${port}/api/speedtest/latest?limit=${args.count}'`);
|
|
24
|
+
return { content: [{ type: "text", text: output }] };
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (error instanceof CommandFailedError) {
|
|
28
|
+
if (error.exitCode === 7) {
|
|
29
|
+
return errorResponse(new Error(`Could not connect to ${SERVICE_NAME} on port ${port}. Is it running? ` +
|
|
30
|
+
`Set HOMELAB_SPEEDTEST_PORT if using a non-default port.`));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return errorResponse(error);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=speedtestResults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speedtestResults.js","sourceRoot":"","sources":["../../src/tools/speedtestResults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAEzC,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,OAAO,CAAC,CAAC,CAAC;SACV,QAAQ,CAAC,8CAA8C,CAAC;CAC5D,CAAC;AAEF,SAAS,OAAO;IACd,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;IACpD,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,0BAA0B,EAC1B,qDAAqD,EACrD,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,8BAA8B,IAAI,+BAA+B,IAAI,CAAC,KAAK,GAAG,CAC/E,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,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;gBACxC,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACzB,OAAO,aAAa,CAClB,IAAI,KAAK,CACP,wBAAwB,YAAY,YAAY,IAAI,mBAAmB;wBACrE,yDAAyD,CAC5D,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uptimeKumaStatus.d.ts","sourceRoot":"","sources":["../../src/tools/uptimeKumaStatus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4BhD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { execSSH, errorResponse } from "../utils/ssh-api.js";
|
|
2
|
+
import { CommandFailedError } from "../utils/errors.js";
|
|
3
|
+
const DEFAULT_PORT = 3001;
|
|
4
|
+
const SERVICE_NAME = "Uptime Kuma";
|
|
5
|
+
function getPort() {
|
|
6
|
+
const override = process.env.HOMELAB_UPTIME_KUMA_PORT;
|
|
7
|
+
return override ? parseInt(override, 10) : DEFAULT_PORT;
|
|
8
|
+
}
|
|
9
|
+
export function register(server) {
|
|
10
|
+
server.tool("homelab_uptimeKumaStatus", "Get the status of all Uptime Kuma monitors", {}, async () => {
|
|
11
|
+
const port = getPort();
|
|
12
|
+
try {
|
|
13
|
+
const output = await execSSH(`curl -sf 'http://localhost:${port}/api/status-page/heartbeat/default'`);
|
|
14
|
+
return { content: [{ type: "text", text: output }] };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error instanceof CommandFailedError) {
|
|
18
|
+
if (error.exitCode === 7) {
|
|
19
|
+
return errorResponse(new Error(`Could not connect to ${SERVICE_NAME} on port ${port}. Is it running? ` +
|
|
20
|
+
`Set HOMELAB_UPTIME_KUMA_PORT if using a non-default port.`));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return errorResponse(error);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=uptimeKumaStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uptimeKumaStatus.js","sourceRoot":"","sources":["../../src/tools/uptimeKumaStatus.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,YAAY,GAAG,aAAa,CAAC;AAEnC,SAAS,OAAO;IACd,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IACtD,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,0BAA0B,EAC1B,4CAA4C,EAC5C,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAC1B,8BAA8B,IAAI,qCAAqC,CACxE,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,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;gBACxC,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBACzB,OAAO,aAAa,CAClB,IAAI,KAAK,CACP,wBAAwB,YAAY,YAAY,IAAI,mBAAmB;wBACrE,2DAA2D,CAC9D,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tmhs/homelab-mcp",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "MCP server for home lab operations via SSH -
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"homelab-mcp": "dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"README.md",
|
|
13
|
-
"LICENSE"
|
|
14
|
-
],
|
|
15
|
-
"engines": {
|
|
16
|
-
"node": ">= 20.0.0"
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "tsc",
|
|
20
|
-
"dev": "tsx watch src/index.ts",
|
|
21
|
-
"start": "node dist/index.js",
|
|
22
|
-
"test": "vitest run",
|
|
23
|
-
"test:watch": "vitest",
|
|
24
|
-
"prepublishOnly": "npm run build"
|
|
25
|
-
},
|
|
26
|
-
"dependencies": {
|
|
27
|
-
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
28
|
-
"ssh2": "^1.16.0",
|
|
29
|
-
"zod": "^3.23.0"
|
|
30
|
-
},
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"@types/node": "^25.5.2",
|
|
33
|
-
"@types/ssh2": "^1.15.0",
|
|
34
|
-
"tsx": "^4.19.0",
|
|
35
|
-
"typescript": "^5.7.0",
|
|
36
|
-
"vitest": "^4.1.2"
|
|
37
|
-
},
|
|
38
|
-
"license": "CC-BY-NC-ND-4.0",
|
|
39
|
-
"repository": {
|
|
40
|
-
"type": "git",
|
|
41
|
-
"url": "https://github.com/TMHSDigital/Home-Lab-Developer-Tools.git",
|
|
42
|
-
"directory": "mcp-server"
|
|
43
|
-
},
|
|
44
|
-
"homepage": "https://github.com/TMHSDigital/Home-Lab-Developer-Tools",
|
|
45
|
-
"bugs": {
|
|
46
|
-
"url": "https://github.com/TMHSDigital/Home-Lab-Developer-Tools/issues"
|
|
47
|
-
},
|
|
48
|
-
"author": "TMHSDigital"
|
|
49
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@tmhs/homelab-mcp",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "MCP server for home lab operations via SSH - 20 tools for system status, Docker Compose management, service health, monitoring, networking, backups, and administration on a Raspberry Pi.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"homelab-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">= 20.0.0"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"dev": "tsx watch src/index.ts",
|
|
21
|
+
"start": "node dist/index.js",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
28
|
+
"ssh2": "^1.16.0",
|
|
29
|
+
"zod": "^3.23.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^25.5.2",
|
|
33
|
+
"@types/ssh2": "^1.15.0",
|
|
34
|
+
"tsx": "^4.19.0",
|
|
35
|
+
"typescript": "^5.7.0",
|
|
36
|
+
"vitest": "^4.1.2"
|
|
37
|
+
},
|
|
38
|
+
"license": "CC-BY-NC-ND-4.0",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/TMHSDigital/Home-Lab-Developer-Tools.git",
|
|
42
|
+
"directory": "mcp-server"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/TMHSDigital/Home-Lab-Developer-Tools",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/TMHSDigital/Home-Lab-Developer-Tools/issues"
|
|
47
|
+
},
|
|
48
|
+
"author": "TMHSDigital"
|
|
49
|
+
}
|