UNPKG

comfyui-mcp

Version:

Local-first, agent-native control plane for ComfyUI — MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline),

99 lines • 6.15 kB
import { z } from "zod"; import { getSystemInfo } from "../services/workflow-executor.js"; import { errorToToolResult } from "../utils/errors.js"; import { getLogsAction } from "./diagnostics.js"; import { healthCheckAction } from "./health-check.js"; /** * The server/machine READS collapsed into one action-parameterized * `get_system_stats` tool (0.50.0 surface consolidation, slice 13): three * tools, three actions. * * The slice originally folded six. Review took the RFC's documented fallback * for three of them and left `clear_vram`, `report_issue` and `calculate` * STANDALONE: the first is the OOM panic button and belongs at one call's * reach, the second files a PUBLIC GitHub issue and had no business on a tool * named 'get system stats', and the third is a pure offline utility with * nothing to do with server state. What is left here is coherent — three * read-only views of the same connected server. * * SHAPE: a FLAT object with an `action` enum — deliberately NOT a * z.discriminatedUnion, which the MCP SDK renders as a schema with ZERO visible * parameters, hiding every input from the model. * * REQUIREDNESS: only `action` can be schema-required — `max_lines`/`keyword` * belong to "logs" and `model_categories`/`recent_errors` to "health", and * none of them is required by anything ("stats" takes no parameters at all). * Every VALUE constraint the old tools had is unchanged at the zod layer: * `max_lines` keeps its .int().min(1).max(2000) and `recent_errors` its * .int().min(0).max(200), so an out-of-range value fails exactly where it did * before. There is therefore no per-action presence guard on this tool — no * action has a required field — and adding one would be inventing a refusal * the old tools never had. * * READ-ONLY, all three. Nothing here mutates the server, the workspace or this * package, and nothing here leaves the machine. */ export function registerSystemStatsTools(server) { const ACTIONS = ["stats", "logs", "health"]; server.tool("get_system_stats", "Inspect the connected ComfyUI server: what it is running on, what it has logged, and whether it is healthy enough to dispatch work to. All three actions are READ-ONLY — nothing here mutates anything. Driven by the `action` parameter:\n" + '- action:"stats" — Get system information from the connected ComfyUI server: GPU device(s), total/free VRAM, ComfyUI/Python/PyTorch versions, and OS details. Requires a running ComfyUI server (works against local or remote targets); read-only, takes no parameters. Returns the raw /system_stats JSON. Use to confirm connectivity and check available VRAM before enqueuing large workflows. Errors if the server is unreachable.\n' + '- action:"logs" — Get ComfyUI server runtime logs. Useful for debugging execution errors, model loading issues, missing nodes, and Python tracebacks. `max_lines` tails the end (default 100), `keyword` filters case-insensitively.\n' + '- action:"health" — Pre-flight diagnostic for the connected ComfyUI: one call that aggregates the signals an agent should check before dispatching a batch. Reports ComfyUI version/Python/PyTorch, GPU name + VRAM free/total, system RAM free, queue depth (running + pending), per-category /models populations (catches empty dropdowns from a misconfigured extra_model_paths.yaml), and recent errors from /internal/logs. Read-only — no mutation. Use this when a job fails for an unexpected reason, before a long batch run, or to confirm a remote ComfyUI is healthy. Originally contributed by github.com/joaolvivas.', { action: z .enum(ACTIONS) .describe('Which read to perform. "stats" takes no other parameters; "logs" takes `max_lines`/`keyword`; "health" takes `model_categories`/`recent_errors`. None of them is required.'), max_lines: z .number() .int() .min(1) .max(2000) .optional() .describe('action:"logs" — maximum number of log lines to return from the end (default: 100).'), keyword: z .string() .optional() .describe('action:"logs" — filter log lines containing this keyword (case-insensitive). Examples: \'error\', \'warning\', \'VRAM\', a node name.'), model_categories: z .array(z.string()) .optional() .describe('action:"health" — override the model categories to poll (defaults to checkpoints, diffusion_models, loras, vae, text_encoders, controlnet).'), recent_errors: z .number() .int() .min(0) .max(200) .optional() .describe('action:"health" — how many recent error/traceback lines to include from /internal/logs (default 20, max 200).'), }, async (args) => { try { switch (args.action) { case "stats": return { content: [ { type: "text", text: JSON.stringify(await getSystemInfo(), null, 2) }, ], }; case "logs": return await getLogsAction({ max_lines: args.max_lines, keyword: args.keyword, }); case "health": return await healthCheckAction({ model_categories: args.model_categories, recent_errors: args.recent_errors, }); default: { // Unreachable given the zod enum, but a clear runtime guard beats a // silent undefined if the schema and switch ever drift apart. const exhaustive = args.action; throw new Error(`Unknown get_system_stats action "${String(exhaustive)}". Expected one of: ${ACTIONS.join(", ")}.`); } } } catch (err) { return errorToToolResult(err); } }); } //# sourceMappingURL=system-stats.js.map