@moor-sh/mcp 0.10.0 → 0.11.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +41 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moor-sh/mcp",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "MCP server for moor - lets AI agents (Claude Code, Cursor, etc.) manage your moor projects via the moor HTTP API.",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/index.ts CHANGED
@@ -660,6 +660,47 @@ server.registerTool(
660
660
  },
661
661
  );
662
662
 
663
+ server.registerTool(
664
+ "moor_project_stats",
665
+ {
666
+ title: "Project Container Stats (live)",
667
+ description:
668
+ "Live container stats for one project: CPU percent, memory (excluding page cache, same accounting as `docker stats`), network and block I/O totals, PID count. Single Docker stats snapshot — CPU uses the cpu_stats/precpu_stats delta the daemon already includes. Stopped or never-started projects return running=false with zeroed counters (no 404).",
669
+ inputSchema: z.object({
670
+ project: z.string().describe("Project name or ID"),
671
+ }),
672
+ },
673
+ async ({ project }) => {
674
+ const p = await resolveProject(project);
675
+ const res = await apiGet(`/api/projects/${p.id}/container-stats`);
676
+ if (!res.ok) throw new Error(`Failed: ${res.status} ${await res.text()}`);
677
+ const s = (await res.json()) as {
678
+ running: boolean;
679
+ cpu_percent: number;
680
+ memory_bytes: number;
681
+ memory_limit_bytes: number;
682
+ memory_percent: number;
683
+ network_rx_bytes: number;
684
+ network_tx_bytes: number;
685
+ block_read_bytes: number;
686
+ block_write_bytes: number;
687
+ pids: number;
688
+ };
689
+ if (!s.running) {
690
+ return {
691
+ content: [{ type: "text", text: `${p.name}: not running (zeroed counters returned).` }],
692
+ };
693
+ }
694
+ const memLimit = s.memory_limit_bytes > 0 ? formatBytes(s.memory_limit_bytes) : "unlimited";
695
+ const lines = [
696
+ `${p.name}: CPU ${s.cpu_percent}% | Memory ${formatBytes(s.memory_bytes)} / ${memLimit} (${s.memory_percent}%) | PIDs ${s.pids}`,
697
+ `Network: rx ${formatBytes(s.network_rx_bytes)} / tx ${formatBytes(s.network_tx_bytes)}`,
698
+ `Block I/O: read ${formatBytes(s.block_read_bytes)} / write ${formatBytes(s.block_write_bytes)}`,
699
+ ];
700
+ return { content: [{ type: "text", text: lines.join("\n") }] };
701
+ },
702
+ );
703
+
663
704
  function formatBytes(bytes: number): string {
664
705
  if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
665
706
  const units = ["B", "KB", "MB", "GB", "TB"];