index.ts•8.19 kB
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js"
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import { z } from "zod"
import { CallToolRequestSchema, ListToolsRequestSchema, ToolSchema } from "@modelcontextprotocol/sdk/types.js"
import * as si from "systeminformation"
import { exec } from "child_process"
import { promisify } from "util"
const execAsync = promisify(exec)
type Tool = z.infer<typeof ToolSchema>
// Define our tools
const TOOLS: Tool[] = [
{
name: "get_process_count",
description: "Get the total number of running processes",
inputSchema: {
type: "object",
properties: {},
required: [],
},
},
{
name: "get_top_cpu_processes",
description: "Get the top N processes consuming the most CPU",
inputSchema: {
type: "object",
properties: {
limit: {
type: "number",
description: "Number of top processes to return (default: 5)",
},
},
required: [],
},
},
{
name: "get_top_memory_processes",
description: "Get the top N processes consuming the most memory",
inputSchema: {
type: "object",
properties: {
limit: {
type: "number",
description: "Number of top processes to return (default: 5)",
},
},
required: [],
},
},
{
name: "get_process_instances",
description: "Get processes grouped by name with instance counts",
inputSchema: {
type: "object",
properties: {
minInstances: {
type: "number",
description: "Only show processes with at least this many instances (default: 2)",
},
},
required: [],
},
},
{
name: "find_process_by_name",
description: "Find all processes matching a name pattern",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Process name or pattern to search for",
},
},
required: ["name"],
},
},
]
// Create the MCP server
const server = new Server(
{
name: "mcp-process-monitor",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
)
// Handle tool listing
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools: TOOLS }
})
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async request => {
const { name, arguments: args } = request.params
try {
switch (name) {
case "get_process_count": {
const processes = await si.processes()
return {
content: [
{
type: "text",
text: `Total running processes: ${processes.all}`,
},
],
}
}
case "get_top_cpu_processes": {
const limit = (args?.limit as number) || 5
const processes = await si.processes()
const topCpu = processes.list
.sort((a, b) => b.cpu - a.cpu)
.slice(0, limit)
.map(p => ({
name: p.name,
pid: p.pid,
cpu: `${p.cpu.toFixed(1)}%`,
memory: `${p.memRss ? (p.memRss / 1024 / 1024).toFixed(1) : "0"} MB`,
}))
.filter(p => p.pid !== 0)
return {
content: [
{
type: "text",
text: `Top ${limit} CPU consuming processes:\n${JSON.stringify(topCpu, null, 2)}`,
},
],
}
}
case "get_top_memory_processes": {
const limit = (args?.limit as number) || 5
const processes = await si.processes()
const topMem = processes.list
.sort((a, b) => (b.memRss || 0) - (a.memRss || 0))
.slice(0, limit)
.map(p => ({
name: p.name,
pid: p.pid,
memory: `${p.memRss ? (p.memRss / 1024 / 1024).toFixed(1) : "0"} MB`,
cpu: `${p.cpu.toFixed(1)}%`,
}))
.filter(p => p.pid !== 0)
return {
content: [
{
type: "text",
text: `Top ${limit} memory consuming processes:\n${JSON.stringify(topMem, null, 2)}`,
},
],
}
}
case "get_process_instances": {
const minInstances = (args?.minInstances as number) || 2
const processes = await si.processes()
const grouped = processes.list.reduce((acc: any, p) => {
acc[p.name] = (acc[p.name] || 0) + 1
return acc
}, {})
const filtered = Object.entries(grouped)
.filter(([_, count]) => (count as number) >= minInstances)
.sort((a, b) => (b[1] as number) - (a[1] as number))
.map(([name, count]) => ({ process: name, instances: count }))
return {
content: [
{
type: "text",
text: `Processes with ${minInstances}+ instances:\n${JSON.stringify(filtered, null, 2)}`,
},
],
}
}
case "find_process_by_name": {
const searchName = ((args?.name as string) || "").toLowerCase()
const processes = await si.processes()
const matched = processes.list
.filter(p => p.name.toLowerCase().includes(searchName))
.map(p => ({
name: p.name,
pid: p.pid,
cpu: `${p.cpu.toFixed(1)}%`,
memory: `${p.memRss ? (p.memRss / 1024 / 1024).toFixed(1) : "0"} MB`,
command: p.command,
}))
.filter(p => p.pid !== 0)
return {
content: [
{
type: "text",
text:
matched.length > 0
? `Found ${matched.length} processes matching "${searchName}":\n${JSON.stringify(
matched,
null,
2
)}`
: `No processes found matching "${searchName}"`,
},
],
}
}
default:
throw new Error(`Unknown tool: ${name}`)
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
}
}
})
// Start the server
async function main() {
const transport = new StdioServerTransport()
await server.connect(transport)
console.error("MCP Process Monitor Server running on stdio")
}
main().catch(error => {
console.error("Fatal error:", error)
process.exit(1)
})