server_logs
Fetch server logs and monitor system metrics from Kastell-managed servers via SSH. Retrieve recent log lines or check CPU, RAM, and disk usage for troubleshooting and maintenance.
Instructions
Fetch logs and system metrics from Kastell-managed servers via SSH. Actions: 'logs' retrieves recent log lines from Coolify container (Coolify servers only), Docker service, or system journal. Bare servers: use service 'system' or 'docker' (coolify service not available). 'monitor' fetches CPU, RAM, and disk usage metrics (works for all server modes). Requires SSH access to target server (root@ip). Note: live streaming (--follow) is not available via MCP — use the CLI for live log tailing.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Action: 'logs' fetch recent log lines, 'monitor' fetch CPU/RAM/Disk metrics | |
| server | No | Server name or IP. Auto-selected if only one server exists. | |
| service | No | Log source (only for 'logs' action): 'coolify' container (Coolify servers only), 'docker' service journal, 'system' full journal | coolify |
| lines | No | Number of log lines to fetch (only for 'logs' action, default: 50, max: 500) | |
| containers | No | Include Docker container list in metrics (only for 'monitor' action) |
Implementation Reference
- src/mcp/tools/serverLogs.ts:38-179 (handler)The handler function for the 'server_logs' tool, which processes 'logs' and 'monitor' actions.
export async function handleServerLogs(params: { action: "logs" | "monitor"; server?: string; service?: LogService; lines?: number; containers?: boolean; }): Promise<McpResponse> { try { const servers = getServers(); if (servers.length === 0) { return mcpError("No servers found", undefined, [ { command: "kastell init", reason: "Deploy a server first" }, ]); } const server = resolveServerForMcp(params, servers); if (!server) { if (params.server) { return mcpError( `Server not found: ${params.server}`, `Available servers: ${servers.map((s) => s.name).join(", ")}`, ); } return mcpError( "Multiple servers found. Specify which server to use.", `Available: ${servers.map((s) => s.name).join(", ")}`, ); } switch (params.action) { case "logs": { // Resolve default service from adapter (coolify→"coolify", dokploy→"dokploy", bare→"system") const platform = resolvePlatform(server); const adapter = platform ? getAdapter(platform) : null; const defaultService: LogService = isBareServer(server) || !adapter ? "system" : adapter.defaultLogService; const service: LogService = params.service ?? defaultService; const lines = params.lines ?? 50; // Guard: bare servers cannot read platform service logs if (isBareServer(server) && service !== "system" && service !== "docker") { return mcpError( `'${service}' logs not available on bare servers`, "Use service: 'system' or 'docker' for bare servers", [ { command: `server_logs { action: 'logs', server: '${server.name}', service: 'system' }`, reason: "View system journal instead", }, ], ); } const result = await fetchServerLogs(server.ip, service, lines); if (result.error) { return { content: [{ type: "text", text: JSON.stringify({ server: server.name, ip: server.ip, service, error: result.error, ...(result.hint ? { hint: result.hint } : {}), ...(result.logs ? { partial_logs: result.logs } : {}), suggested_actions: [ { command: `kastell logs ${server.name} --service ${service}`, reason: "Try from CLI for interactive mode" }, { command: `server_info { action: 'health', server: '${server.name}' }`, reason: "Check if server is reachable" }, ], }) }], isError: true, }; } const suggestedActions: SuggestedAction[] = [ { command: `server_logs { action: 'monitor', server: '${server.name}' }`, reason: "Check system metrics" }, ]; if (lines < 200) { suggestedActions.unshift({ command: `server_logs { action: 'logs', server: '${server.name}', service: '${service}', lines: 200 }`, reason: "Fetch more lines", }); } if (service === "coolify") { suggestedActions.push({ command: `server_logs { action: 'logs', server: '${server.name}', service: 'system' }`, reason: "Check system journal", }); } return mcpSuccess({ server: server.name, ip: server.ip, service, lines, logs: result.logs, suggested_actions: suggestedActions, }); } case "monitor": { const includeContainers = isBareServer(server) ? false : (params.containers ?? false); const result = await fetchServerMetrics(server.ip, includeContainers); if (result.error) { return { content: [{ type: "text", text: JSON.stringify({ server: server.name, ip: server.ip, error: result.error, ...(result.hint ? { hint: result.hint } : {}), suggested_actions: [ { command: `server_info { action: 'health', server: '${server.name}' }`, reason: "Check if server is reachable" }, ], }) }], isError: true, }; } const suggestedActions: SuggestedAction[] = [ { command: `server_logs { action: 'logs', server: '${server.name}' }`, reason: "Check recent logs" }, ]; if (!includeContainers) { suggestedActions.unshift({ command: `server_logs { action: 'monitor', server: '${server.name}', containers: true }`, reason: "Include Docker container list", }); } return mcpSuccess({ server: server.name, ip: server.ip, metrics: result.metrics, ...(result.containers ? { containers: result.containers } : {}), suggested_actions: suggestedActions, }); } } } catch (error: unknown) { return mcpError(getErrorMessage(error)); } } - src/mcp/tools/serverLogs.ts:15-31 (schema)Zod schema defining the inputs for the 'server_logs' tool.
export const serverLogsSchema = { action: z.enum(["logs", "monitor"]).describe( "Action: 'logs' fetch recent log lines, 'monitor' fetch CPU/RAM/Disk metrics", ), server: z.string().optional().describe( "Server name or IP. Auto-selected if only one server exists.", ), service: z.enum(["coolify", "docker", "system"]).default("coolify").describe( "Log source (only for 'logs' action): 'coolify' container (Coolify servers only), 'docker' service journal, 'system' full journal", ), lines: z.number().min(1).max(500).default(50).describe( "Number of log lines to fetch (only for 'logs' action, default: 50, max: 500)", ), containers: z.boolean().default(false).describe( "Include Docker container list in metrics (only for 'monitor' action)", ), }; - src/mcp/server.ts:71-83 (registration)Registration of the 'server_logs' tool within the MCP server setup.
server.registerTool("server_logs", { description: "Fetch logs and system metrics from Kastell-managed servers via SSH. Actions: 'logs' retrieves recent log lines from Coolify container (Coolify servers only), Docker service, or system journal. Bare servers: use service 'system' or 'docker' (coolify service not available). 'monitor' fetches CPU, RAM, and disk usage metrics (works for all server modes). Requires SSH access to target server (root@ip). Note: live streaming (--follow) is not available via MCP — use the CLI for live log tailing.", inputSchema: serverLogsSchema, annotations: { title: "Server Logs & Metrics", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, }, async (params) => { return handleServerLogs(params);