Stu Mason · Folkestone, Kent
One entryClosed issue· StuMason/coolify-mcp· TypeScript

Issue #232 closed: deployment get with `lines` bypasses the essential projection — leaks server secrets and blows token budgets

What happens

deployment {action: get, uuid, lines: N} switches from the slim toDeploymentEssential() projection to the raw upstream payload (getDeployment(uuid, { includeLogs: true })), and that raw Coolify response embeds the entire application graph: rendered docker_compose, custom_labels, webhook secrets, the destination server object with proxy config, and server.settings including logdrain_custom_config (which contains a live bearer token for the log drain) and sentinel_token.

Observed in production use (2026-07-02): deployment {action: get, uuid: ..., lines: 5} returned a ~78KB payload — 5 log lines wrapped in the full application + server + proxy + log-drain config. It blew the calling agent's tool-result cap, and the secrets above were in the body.

Why it matters

  1. Security: v2.12.0 deliberately masked webhook secrets in list_resources — this path undoes that class of protection. Any agent asking for deployment logs gets log-drain credentials and the sentinel token in context (and potentially into transcripts/telemetry).
  2. Token budget: the whole point of toDeploymentEssential (coolify-client.ts ~line 300) is defeated the moment you ask for logs, which is the most common reason to call get at all.

Where

  • Handler: src/lib/mcp-server.ts:1155 — the lines branch calls getDeployment(uuid, { includeLogs: true }) and returns the raw object.
  • Projection helper: src/lib/coolify-client.ts ~300 (toDeploymentEssential).

Suggested fix

Always project through toDeploymentEssential, and when lines is set attach logs (via the existing truncateLogs() in mcp-server.ts:70) onto the projection — never return the raw upstream object. If any raw field is genuinely needed later, add it to the projection explicitly. Belt-and-braces: run the projection output through the same secret-masking used by list_resources so future upstream fields can't reintroduce a leak.

Acceptance

  • deployment get with lines returns: essential fields + logs (truncated) only; no application.*, no server.*, no compose, no tokens.
  • A test asserting the response for a logs-included get contains no logdrain, sentinel_token, manual_webhook_secret*, or docker_compose keys.