system-health-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 +142 -0
- package/babel.config.mjs +6 -0
- package/index.ts +213 -0
- package/jest.config.mjs +8 -0
- package/package.json +31 -0
- package/test/index.edge.test.ts +62 -0
- package/test/index.test.ts +80 -0
- package/tsconfig.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# System Health MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server that provides real-time system health monitoring capabilities including CPU load, memory usage, and battery status.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Real-time CPU monitoring**: Get current CPU load percentage
|
|
8
|
+
- **Memory statistics**: Track active and total memory usage
|
|
9
|
+
- **Battery information**: Monitor battery level and charging status
|
|
10
|
+
- **Hardware details**: GPU, network interfaces, disk usage, and Docker containers
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- Node.js (v18 or higher recommended)
|
|
15
|
+
- TypeScript
|
|
16
|
+
- npm or yarn
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
1. Clone this repository:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone <repository-url>
|
|
24
|
+
cd system-health-mcp
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
2. Install dependencies:
|
|
28
|
+
|
|
29
|
+
3. Build the TypeScript code:
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
To use this MCP server with an MCP-compatible client (like Claude Desktop), add the following configuration to your client's MCP settings file:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"system-health": {
|
|
39
|
+
"command": "node",
|
|
40
|
+
"args": ["/path/to/your/project/index.js"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Important**: Replace `/path/to/your/project/index.js` with the absolute path to the built `index.js` file in your cloned repository.
|
|
47
|
+
|
|
48
|
+
### Example Configuration Locations
|
|
49
|
+
|
|
50
|
+
- **Claude Desktop (macOS)**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
51
|
+
- **Claude Desktop (Windows)**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
52
|
+
- **Claude Desktop (Linux)**: `~/.config/Claude/claude_desktop_config.json`
|
|
53
|
+
|
|
54
|
+
After updating the configuration, restart your MCP client for the changes to take effect.
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
This MCP server exposes two tools for comprehensive system monitoring.
|
|
59
|
+
|
|
60
|
+
### Running the Server
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
node index.js
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The server communicates via stdio and is designed to be used with MCP-compatible clients.
|
|
67
|
+
|
|
68
|
+
### Tool: get_system_stats
|
|
69
|
+
|
|
70
|
+
Returns real-time system health information.
|
|
71
|
+
|
|
72
|
+
**Response:**
|
|
73
|
+
|
|
74
|
+
- `cpuLoadPercentage`: Current CPU load as a percentage
|
|
75
|
+
- `memoryUsedGB`: Active memory in gigabytes
|
|
76
|
+
- `memoryTotalGB`: Total system memory in gigabytes
|
|
77
|
+
- `batteryLevel`: Battery percentage (or "No battery" if not applicable)
|
|
78
|
+
- `isCharging`: Boolean indicating if the battery is currently charging
|
|
79
|
+
|
|
80
|
+
**Example Response:**
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"cpuLoadPercentage": "23.45",
|
|
85
|
+
"memoryUsedGB": "8.42",
|
|
86
|
+
"memoryTotalGB": "16.00",
|
|
87
|
+
"batteryLevel": "85%",
|
|
88
|
+
"isCharging": false
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Tool: get_hardware_details
|
|
93
|
+
|
|
94
|
+
Returns detailed hardware information based on the specified category.
|
|
95
|
+
|
|
96
|
+
**Parameters:**
|
|
97
|
+
|
|
98
|
+
- `category` (required): One of `"graphics"`, `"network"`, `"disks"`, or `"docker"`
|
|
99
|
+
|
|
100
|
+
**Categories:**
|
|
101
|
+
|
|
102
|
+
- `graphics`: GPU and graphics card information
|
|
103
|
+
- `network`: Network interface configurations
|
|
104
|
+
- `disks`: Disk usage and filesystem information
|
|
105
|
+
- `docker`: Docker container details
|
|
106
|
+
|
|
107
|
+
**Example Request:**
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"category": "graphics"
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
### Project Structure
|
|
118
|
+
|
|
119
|
+
- `index.ts`: Main server implementation
|
|
120
|
+
- `package.json`: Project dependencies and configuration
|
|
121
|
+
- `tsconfig.json`: TypeScript compiler configuration
|
|
122
|
+
|
|
123
|
+
### Building
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npx tsc
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Type Checking
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npx tsc --noEmit
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Dependencies
|
|
136
|
+
|
|
137
|
+
- `@modelcontextprotocol/sdk`: MCP SDK for building servers
|
|
138
|
+
- `systeminformation`: Cross-platform system information library
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
ISC
|
package/babel.config.mjs
ADDED
package/index.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import si from 'systeminformation';
|
|
5
|
+
import psList from 'ps-list';
|
|
6
|
+
import pidusage from 'pidusage';
|
|
7
|
+
import getFolderSize from 'get-folder-size';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
const server = new Server(
|
|
12
|
+
{ name: "system-health-monitor", version: "1.0.0" },
|
|
13
|
+
{ capabilities: { tools: {} } }
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// Standalone handler for ListToolsRequest
|
|
18
|
+
async function handleListToolsRequest() {
|
|
19
|
+
return {
|
|
20
|
+
tools: [
|
|
21
|
+
{
|
|
22
|
+
name: "get_system_stats",
|
|
23
|
+
description: "Get real-time CPU load, memory usage, and battery status",
|
|
24
|
+
inputSchema: { type: "object", properties: {} }
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "get_hardware_details",
|
|
28
|
+
description: "Get detailed hardware info like GPU, Disk health, and Network config",
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
category: {
|
|
33
|
+
type: "string",
|
|
34
|
+
enum: ["graphics", "network", "disks", "docker"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "get_resource_hogs",
|
|
41
|
+
description: "Get top N resource hog processes by CPU usage",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
topN: { type: "number", default: 5 }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "get_largest_files_folders",
|
|
51
|
+
description: "Get top N largest files/folders in a directory",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
scanPath: { type: "string", default: "/" },
|
|
56
|
+
topN: { type: "number", default: 5 }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
server.setRequestHandler(ListToolsRequestSchema, handleListToolsRequest);
|
|
65
|
+
|
|
66
|
+
// 2. Implement the logic
|
|
67
|
+
// Standalone handler for CallToolRequest
|
|
68
|
+
async function handleCallToolRequest(request: any) {
|
|
69
|
+
// Get top N resource hog processes
|
|
70
|
+
if (request.params.name === "get_resource_hogs") {
|
|
71
|
+
const { topN = 5 } = request.params.arguments || {};
|
|
72
|
+
const processes = await psList();
|
|
73
|
+
const usages = await Promise.all(
|
|
74
|
+
processes.map(async (proc) => {
|
|
75
|
+
try {
|
|
76
|
+
const stats: { cpu: number; memory: number } = await pidusage(proc.pid);
|
|
77
|
+
return {
|
|
78
|
+
...proc,
|
|
79
|
+
cpu: stats.cpu,
|
|
80
|
+
memory: stats.memory,
|
|
81
|
+
};
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
const validUsages = usages.filter((u): u is NonNullable<typeof u> => !!u);
|
|
88
|
+
validUsages.sort((a, b) => ((b && b.cpu) || 0) - ((a && a.cpu) || 0));
|
|
89
|
+
return {
|
|
90
|
+
content: [{ type: "text", text: JSON.stringify(validUsages.slice(0, Number(topN)), null, 2) }]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get top N largest files/folders in a directory
|
|
95
|
+
if (request.params.name === "get_largest_files_folders") {
|
|
96
|
+
const { scanPath = "/", topN = 5 } = request.params.arguments || {};
|
|
97
|
+
const items: { name: string; size: number; isDir: boolean }[] = [];
|
|
98
|
+
let dirents: fs.Dirent[];
|
|
99
|
+
try {
|
|
100
|
+
dirents = fs.readdirSync(scanPath as string, { withFileTypes: true });
|
|
101
|
+
} catch (err) {
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: "text", text: `Error reading directory: ${err}` }]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
for (const dirent of dirents) {
|
|
107
|
+
const fullPath = path.join(scanPath as string, dirent.name);
|
|
108
|
+
try {
|
|
109
|
+
if (dirent.isDirectory()) {
|
|
110
|
+
const size = await getFolderSize(fullPath).then((result: { size: number }) => result.size);
|
|
111
|
+
items.push({ name: fullPath, size, isDir: true });
|
|
112
|
+
} else if (dirent.isFile()) {
|
|
113
|
+
const stats = fs.statSync(fullPath);
|
|
114
|
+
items.push({ name: fullPath, size: stats.size, isDir: false });
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
// Ignore errors
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
items.sort((a, b) => b.size - a.size);
|
|
121
|
+
return {
|
|
122
|
+
content: [{ type: "text", text: JSON.stringify(items.slice(0, Number(topN)), null, 2) }]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (request.params.name === "get_system_stats") {
|
|
126
|
+
const [cpu, mem, battery] = await Promise.all([
|
|
127
|
+
si.currentLoad(),
|
|
128
|
+
si.mem(),
|
|
129
|
+
si.battery()
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const stats = {
|
|
133
|
+
cpuLoadPercentage: cpu.currentLoad.toFixed(2),
|
|
134
|
+
memoryUsedGB: (mem.active / 1024 / 1024 / 1024).toFixed(2),
|
|
135
|
+
memoryTotalGB: (mem.total / 1024 / 1024 / 1024).toFixed(2),
|
|
136
|
+
batteryLevel: battery.hasBattery ? `${battery.percent}%` : "No battery",
|
|
137
|
+
isCharging: battery.isCharging
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
content: [{ type: "text", text: JSON.stringify(stats, null, 2) }]
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (request.params.name === "get_hardware_details") {
|
|
146
|
+
const { category } = request.params.arguments as { category: string };
|
|
147
|
+
let data;
|
|
148
|
+
|
|
149
|
+
switch (category) {
|
|
150
|
+
case "graphics":
|
|
151
|
+
data = { graphics: await si.graphics() };
|
|
152
|
+
break;
|
|
153
|
+
case "network":
|
|
154
|
+
data = await si.networkInterfaces();
|
|
155
|
+
break;
|
|
156
|
+
case "disks":
|
|
157
|
+
data = await si.fsSize();
|
|
158
|
+
break;
|
|
159
|
+
case "docker":
|
|
160
|
+
data = await si.dockerContainers();
|
|
161
|
+
break;
|
|
162
|
+
default:
|
|
163
|
+
data = { error: "Invalid category" };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
throw new Error("Tool not found");
|
|
171
|
+
}
|
|
172
|
+
server.setRequestHandler(CallToolRequestSchema, handleCallToolRequest);
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
// 3. Connect the transport
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
// Single-request mode for testability
|
|
179
|
+
if (process.env.TEST_MODE === '1') {
|
|
180
|
+
process.stdin.setEncoding('utf8');
|
|
181
|
+
let input = '';
|
|
182
|
+
process.stdin.on('data', (chunk) => {
|
|
183
|
+
input += chunk;
|
|
184
|
+
if (input.includes('\n')) {
|
|
185
|
+
process.stdin.pause();
|
|
186
|
+
handleInput(input.trim());
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
async function handleInput(line: string) {
|
|
191
|
+
try {
|
|
192
|
+
const req = JSON.parse(line);
|
|
193
|
+
let result;
|
|
194
|
+
if (req.schema === 'ListToolsRequest') {
|
|
195
|
+
result = await handleListToolsRequest();
|
|
196
|
+
} else if (req.schema === 'CallToolRequest') {
|
|
197
|
+
result = await handleCallToolRequest(req);
|
|
198
|
+
} else {
|
|
199
|
+
throw new Error('Unknown schema');
|
|
200
|
+
}
|
|
201
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
202
|
+
process.exit(0);
|
|
203
|
+
} catch (err: any) {
|
|
204
|
+
process.stderr.write((err && err.message ? err.message : String(err)) + '\n');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
(async () => {
|
|
210
|
+
const transport = new StdioServerTransport();
|
|
211
|
+
await server.connect(transport);
|
|
212
|
+
})();
|
|
213
|
+
}
|
package/jest.config.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "system-health-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"mcp",
|
|
10
|
+
"system",
|
|
11
|
+
"debugging"
|
|
12
|
+
],
|
|
13
|
+
"author": "John Van Wagenen",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"description": "A local MCP server to give your LLM access to hardware information.",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
19
|
+
"get-folder-size": "^5.0.0",
|
|
20
|
+
"pidusage": "^4.0.1",
|
|
21
|
+
"ps-list": "^9.0.0",
|
|
22
|
+
"systeminformation": "^5.30.5"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"repository": "https://github.com/jvdub/system-health-mcp",
|
|
26
|
+
"@types/node": "^25.0.10",
|
|
27
|
+
"@types/pidusage": "^2.0.5",
|
|
28
|
+
"@types/ps-list": "^6.0.0",
|
|
29
|
+
"typescript": "^5.9.3"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { describe, expect, test } from '@jest/globals';
|
|
4
|
+
|
|
5
|
+
describe('System Health MCP Server Edge Cases', () => {
|
|
6
|
+
const serverPath = path.resolve(__dirname, '../out/index.js');
|
|
7
|
+
|
|
8
|
+
function runServerWithInput(input: string): Promise<string> {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const proc = spawn('node', [serverPath], { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, TEST_MODE: '1' } });
|
|
11
|
+
let output = '';
|
|
12
|
+
let error = '';
|
|
13
|
+
proc.stdout.on('data', (data: { toString: () => string; }) => {
|
|
14
|
+
output += data.toString();
|
|
15
|
+
});
|
|
16
|
+
proc.stderr.on('data', (data: { toString: () => string; }) => {
|
|
17
|
+
error += data.toString();
|
|
18
|
+
});
|
|
19
|
+
proc.on('close', () => {
|
|
20
|
+
if (error) reject(error);
|
|
21
|
+
else resolve(output);
|
|
22
|
+
});
|
|
23
|
+
proc.stdin.write(input);
|
|
24
|
+
proc.stdin.end();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
it('should handle invalid tool name', async () => {
|
|
28
|
+
const request = JSON.stringify({
|
|
29
|
+
schema: 'CallToolRequest',
|
|
30
|
+
params: { name: 'nonexistent_tool', arguments: {} }
|
|
31
|
+
}) + '\n';
|
|
32
|
+
await expect(runServerWithInput(request)).rejects.toMatch(/Tool not found/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should handle invalid hardware category', async () => {
|
|
36
|
+
const request = JSON.stringify({
|
|
37
|
+
schema: 'CallToolRequest',
|
|
38
|
+
params: { name: 'get_hardware_details', arguments: { category: 'invalid' } }
|
|
39
|
+
}) + '\n';
|
|
40
|
+
const output = await runServerWithInput(request);
|
|
41
|
+
expect(output).toMatch(/Invalid category/);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle unreadable directory in get_largest_files_folders', async () => {
|
|
45
|
+
const request = JSON.stringify({
|
|
46
|
+
schema: 'CallToolRequest',
|
|
47
|
+
params: { name: 'get_largest_files_folders', arguments: { scanPath: '/root/forbidden', topN: 1 } }
|
|
48
|
+
}) + '\n';
|
|
49
|
+
const output = await runServerWithInput(request);
|
|
50
|
+
expect(output).toMatch(/Error reading directory/);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should default to topN=5 if not provided', async () => {
|
|
54
|
+
const request = JSON.stringify({
|
|
55
|
+
schema: 'CallToolRequest',
|
|
56
|
+
params: { name: 'get_resource_hogs', arguments: {} }
|
|
57
|
+
}) + '\n';
|
|
58
|
+
const output = await runServerWithInput(request);
|
|
59
|
+
// Should return at least one process
|
|
60
|
+
expect(output).toMatch(/cpu/);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { describe, expect, test } from '@jest/globals';
|
|
4
|
+
|
|
5
|
+
describe('System Health MCP Server', () => {
|
|
6
|
+
const serverPath = path.resolve(__dirname, '../out/index.js');
|
|
7
|
+
|
|
8
|
+
function runServerWithInput(input: string): Promise<string> {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const proc = spawn('node', [serverPath], { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, TEST_MODE: '1' } });
|
|
11
|
+
let output = '';
|
|
12
|
+
let error = '';
|
|
13
|
+
proc.stdout.on('data', (data: { toString: () => string; }) => {
|
|
14
|
+
output += data.toString();
|
|
15
|
+
});
|
|
16
|
+
proc.stderr.on('data', (data: { toString: () => string; }) => {
|
|
17
|
+
error += data.toString();
|
|
18
|
+
});
|
|
19
|
+
proc.on('close', () => {
|
|
20
|
+
if (error) reject(error);
|
|
21
|
+
else resolve(output);
|
|
22
|
+
});
|
|
23
|
+
proc.stdin.write(input);
|
|
24
|
+
proc.stdin.end();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
it('should list all tools', async () => {
|
|
28
|
+
const request = JSON.stringify({
|
|
29
|
+
schema: 'ListToolsRequest',
|
|
30
|
+
params: {}
|
|
31
|
+
}) + '\n';
|
|
32
|
+
const output = await runServerWithInput(request);
|
|
33
|
+
expect(output).toContain('get_system_stats');
|
|
34
|
+
expect(output).toContain('get_hardware_details');
|
|
35
|
+
expect(output).toContain('get_resource_hogs');
|
|
36
|
+
expect(output).toContain('get_largest_files_folders');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return system stats', async () => {
|
|
40
|
+
const request = JSON.stringify({
|
|
41
|
+
schema: 'CallToolRequest',
|
|
42
|
+
params: { name: 'get_system_stats', arguments: {} }
|
|
43
|
+
}) + '\n';
|
|
44
|
+
const output = await runServerWithInput(request);
|
|
45
|
+
expect(output).toMatch(/cpuLoadPercentage/);
|
|
46
|
+
expect(output).toMatch(/memoryUsedGB/);
|
|
47
|
+
expect(output).toMatch(/memoryTotalGB/);
|
|
48
|
+
expect(output).toMatch(/batteryLevel/);
|
|
49
|
+
expect(output).toMatch(/isCharging/);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should return hardware details for graphics', async () => {
|
|
53
|
+
const request = JSON.stringify({
|
|
54
|
+
schema: 'CallToolRequest',
|
|
55
|
+
params: { name: 'get_hardware_details', arguments: { category: 'graphics' } }
|
|
56
|
+
}) + '\n';
|
|
57
|
+
const output = await runServerWithInput(request);
|
|
58
|
+
expect(output).toMatch(/graphics/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should return resource hogs', async () => {
|
|
62
|
+
const request = JSON.stringify({
|
|
63
|
+
schema: 'CallToolRequest',
|
|
64
|
+
params: { name: 'get_resource_hogs', arguments: { topN: 2 } }
|
|
65
|
+
}) + '\n';
|
|
66
|
+
const output = await runServerWithInput(request);
|
|
67
|
+
expect(output).toMatch(/cpu/);
|
|
68
|
+
expect(output).toMatch(/memory/);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should return largest files/folders', async () => {
|
|
72
|
+
const request = JSON.stringify({
|
|
73
|
+
schema: 'CallToolRequest',
|
|
74
|
+
params: { name: 'get_largest_files_folders', arguments: { scanPath: '.', topN: 2 } }
|
|
75
|
+
}) + '\n';
|
|
76
|
+
const output = await runServerWithInput(request);
|
|
77
|
+
expect(output).toMatch(/name/);
|
|
78
|
+
expect(output).toMatch(/size/);
|
|
79
|
+
});
|
|
80
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Visit https://aka.ms/tsconfig to read more about this file
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
// File Layout
|
|
5
|
+
// "rootDir": "./src",
|
|
6
|
+
"outDir": "./out",
|
|
7
|
+
|
|
8
|
+
// Environment Settings
|
|
9
|
+
// See also https://aka.ms/tsconfig/module
|
|
10
|
+
"module": "nodenext",
|
|
11
|
+
"target": "esnext",
|
|
12
|
+
"types": ["node", "jest"],
|
|
13
|
+
// For nodejs:
|
|
14
|
+
// "lib": ["esnext"],
|
|
15
|
+
// "types": ["node"],
|
|
16
|
+
// and npm install -D @types/node
|
|
17
|
+
|
|
18
|
+
// Other Outputs
|
|
19
|
+
"sourceMap": true,
|
|
20
|
+
"declaration": true,
|
|
21
|
+
"declarationMap": true,
|
|
22
|
+
|
|
23
|
+
// Stricter Typechecking Options
|
|
24
|
+
"noUncheckedIndexedAccess": true,
|
|
25
|
+
"exactOptionalPropertyTypes": true,
|
|
26
|
+
|
|
27
|
+
// Style Options
|
|
28
|
+
// "noImplicitReturns": true,
|
|
29
|
+
// "noImplicitOverride": true,
|
|
30
|
+
// "noUnusedLocals": true,
|
|
31
|
+
// "noUnusedParameters": true,
|
|
32
|
+
// "noFallthroughCasesInSwitch": true,
|
|
33
|
+
// "noPropertyAccessFromIndexSignature": true,
|
|
34
|
+
|
|
35
|
+
// Recommended Options
|
|
36
|
+
"strict": true,
|
|
37
|
+
"jsx": "react-jsx",
|
|
38
|
+
"verbatimModuleSyntax": true,
|
|
39
|
+
"isolatedModules": true,
|
|
40
|
+
"noUncheckedSideEffectImports": true,
|
|
41
|
+
"moduleDetection": "force",
|
|
42
|
+
"skipLibCheck": true,
|
|
43
|
+
}
|
|
44
|
+
,
|
|
45
|
+
"include": ["index.ts"]
|
|
46
|
+
}
|