feat(mcp): apps.logs — compose-aware runtime logs

Adds apps.logs MCP tool + session REST endpoint for tailing runtime
container logs. Unblocks cold-start debugging for agent-deployed
compose apps (Twenty, Cal.com, Plane, etc.) where Coolify's own
/applications/{uuid}/logs endpoint returns empty.

Architecture:
  - dockerfile / nixpacks / static apps → Coolify's REST logs API
  - dockercompose apps                  → SSH into Coolify host,
                                          `docker logs` per service

New SSH path uses a dedicated `vibn-logs` user (docker group, no
sudo, no pty, no port-forwarding, single ed25519 key). Private key
lives in COOLIFY_SSH_PRIVATE_KEY_B64 on the vibn-frontend Coolify
app; authorized_key is installed by scripts/setup-vibn-logs-user.sh
on the Coolify host.

Tool shape:
  params:   { uuid, service?, lines? (default 200, max 5000) }
  returns:  { uuid, buildPack, source: 'coolify_api'|'ssh_docker'|'empty',
              services: { [name]: { container, lines, bytes, logs, status? } },
              warnings: string[], truncated: boolean }

Made-with: Cursor
This commit is contained in:
2026-04-23 13:21:52 -07:00
parent 9959eaeeaa
commit d86f2bea03
7 changed files with 541 additions and 0 deletions

View File

@@ -59,6 +59,7 @@ export interface CoolifyApplication {
environment_id?: number;
environment_name?: string;
environment?: { id?: number; project_uuid?: string; project?: { uuid?: string } };
build_pack?: string;
}
/**
@@ -548,6 +549,19 @@ export async function getDeploymentLogs(deploymentUuid: string): Promise<{ logs:
return coolifyFetch(`/deployments/${deploymentUuid}/logs`);
}
/**
* Coolify's "runtime logs" endpoint. Returns `{ logs: "…" }` for simple
* Dockerfile/nixpacks apps; returns an empty string for `dockercompose`
* apps (Coolify v4 doesn't know which of the compose services to tail).
* Use coolify-logs.getApplicationRuntimeLogs for the compose-aware path.
*/
export async function getApplicationRuntimeLogsFromApi(
uuid: string,
lines = 200,
): Promise<{ logs: string }> {
return coolifyFetch(`/applications/${uuid}/logs?lines=${Math.max(1, Math.min(lines, 5000))}`);
}
export async function listApplicationDeployments(uuid: string): Promise<CoolifyDeployment[]> {
// Coolify v4 nests this under /deployments/applications/{uuid}
// and returns { count, deployments }. Normalize to a flat array.