Stu Mason
Stu Mason

Activity

Pull Request Opened

PR #158 opened: fix(client): listApplicationDeployments returns envelope, defaults to essential projection

Symptom

listApplicationDeployments(uuid) can return responses larger than 1 MB on apps with a typical deployment history (20–40 deployments). When called from an MCP client (e.g. Claude Code), the response can blow past the model's context window or simply exhaust the MCP token budget on a single tool call. Smoke-tested live against Coolify 4.0.0-beta.472: a 35-deployment app returned ~1 MB JSON, of which >99% was raw build-log blobs embedded in each deployment.

Root cause

Two issues, same fix:

  1. Type lie. The current return type Promise<Deployment[]> does not match the real Coolify response. GET /api/v1/deployments/applications/{uuid} actually returns an envelope:

    { "count": 35, "deployments": [ /* Deployment[] */ ] }
    

    Verified live against Coolify 4.0.0-beta.472. The existing type causes any caller using .length, .map(), etc. to blow up at runtime — only no-op callers were unaffected.

  2. No projection. Each Deployment carries a full logs field (the entire build-log stream, often 30–100 KB per deployment). Returning all of them by default is a footgun for LLM/MCP callers.

Fix

  • Return the envelope shape { count, deployments } honestly.
  • Default deployments to DeploymentEssential[] (no logs blob, sets logs_available: true breadcrumb so callers know to refetch a single one if needed).
  • Add options.includeLogs?: boolean opt-in for callers that genuinely want the raw build logs.
  • Tolerate malformed envelopes ({}{ count: 0, deployments: [] }).
  • Update diagnoseApplication (the only internal caller) to flatten the envelope before iterating.
  • Plumb a matching include_logs knob through the deployment MCP tool's list_for_app action.

Smoke test (real Coolify 4.0.0-beta.472)

ModeBytes
Old (Deployment[] w/ logs)~1,000,000+
New default (DeploymentEssential[])4,194
New includeLogs: trueunchanged from old

That's a ~250× reduction by default, and the raw-log path is still available behind a flag.

Tests

  • Replaced should list application deployments with three cases:
    • default essential projection (no logs, has logs_available: true, drops id)
    • includeLogs: true returns full deployments unchanged
    • tolerates malformed envelope ({})
  • Updated 4 mockResponse(...) calls in diagnoseApplication tests to wrap their input in the new envelope shape.
  • npm test: 264/264 pass. Coverage stays at 97.7% statements / 83.9% branches / 99.4% functions / 98.7% lines.
  • npm run lint: clean (4 pre-existing warnings, 0 new).
  • npm run build: clean.

Breaking change

Yes, but signaled by the signature change: Promise<Deployment[]>Promise<{ count: number; deployments: Deployment[] | DeploymentEssential[] }>. Any caller currently treating the result as an array would have already been broken at runtime against real Coolify (since the API doesn't actually return an array), so in practice this only fixes broken callers and gives them a typed migration path.


Generated with Claude Code.

+76
additions
-13
deletions
3
files changed