Uh oh!
There was an error while loading. Please reload this page.
fix(mcp): get_container_transport_events false-empty timeline (fall back to include path) - #273
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:d23a46b744
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
dodeja
commented
Jun 26, 2026
Triaged the automated review comments and pushed
Nothing skipped — no nitpicks, duplicates, or out-of-scope suggestions in the set. Gate: SDK build/type-check/test (51 pass / 2 skip) and MCP build/type-check/lint/test (80 pass) all green. No SDK public surface change, so no docs regen. Did not run |
The dedicated /containers/{id}/transport_events sub-resource can 404 even
when the container exists and has events. Previously the tool swallowed
that NotFound into a success-shaped empty timeline whose _metadata.error
secretly carried "Not Found" — an LLM could not tell a real empty timeline
from a failed fetch or a bad container id.
Now on a NotFound from the sub-resource we fall back to
GET /containers/{id}?include=transport_events and map the included
transport_event records. A genuinely-missing container (the fallback also
404s) surfaces as a real tool error instead of a false-empty. An
empty-but-valid container returns an empty timeline tagged
_metadata.container_found:true with no error, so it is unambiguous.
Also drops the duplicate `mapped` payload this tool used to emit and
returns the timeline summary as a flat object.
DEV-10660
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Triage of PR review comments on get_container_transport_events: - Preserve movement locations on the include fallback. The fallback path fetches only include=transport_events, so the related port/metro_area resource is not side-loaded. Canonical transport_event payloads carry location_locode (and usually no location_name), so resolveLocation now surfaces a location whenever location_locode/port_locode is present instead of dropping every fallback location. Test fixture updated to the realistic location_locode shape with a guard assertion. - Log a structured tool.execute.fallback entry when the primary 404 triggers the include fallback, so operators can correlate fallback traffic (previously the 404 was silently swallowed). - Set container_found on the primary success path too, so an empty but valid primary timeline (200 with zero events) is consistent with the fallback path and never read as a missing container. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9645ad6 to
4c37fdaComparedodeja
commented
Jul 1, 2026
Rebased onto latest All three inline automated-review comments (Codex + Greptile) were already fixed in the prior commit
No new issues found on re-review; nothing skipped. Checks after rebase:
Force-pushed the rebase to |
Uh oh!
There was an error while loading. Please reload this page.
Summary
get_container_transport_eventsreads the dedicated/containers/{id}/transport_eventssub-resource. That sub-resource can return 404 even when the container exists and has events (it isn't enabled/populated for every container). The old tool swallowed that NotFound into a success-shaped empty timeline whose_metadata.errorsecretly carried"Not Found". The result: an LLM could not distinguish a genuinely empty timeline from a failed fetch, and a bad container id looked identical to a valid container with no events.This PR makes the failure modes unambiguous:
GET /containers/{id}?include=transport_eventsand maps theincludedtransport_eventrecords, so it returns the real events instead of a false-empty._metadata.container_found: truewith noerror, clearly separating "nothing happened yet" from "bad id"._metadata.error: "Not Found".mappedpayload — the tool now returns a single flat timeline summary (total_events,event_categories,timeline,milestones,_metadata) instead of{ mapped, summary }. Non-NotFound errors (e.g. upstream 5xx) still propagate unchanged — the fallback only triggers on NotFound._metadata.sourceis set totransport_events_subresourceorcontainer_include_fallbackso callers/operators can see which path served the data.Issues
Closes DEV-10660
Green gate
Whole-repo gate is green (baseline was SDK 51 pass / 2 skip, MCP 77 pass):
npm run build --workspace @terminal49/sdk— cleannpm run build --workspace @terminal49/mcp— cleannpm run type-check --workspace @terminal49/sdk— cleannpm run type-check --workspace @terminal49/mcp— cleannpm test --workspace @terminal49/sdk -- --run— 51 pass / 2 skipnpm run test --workspace @terminal49/mcp -- --run— 80 pass (added: fallback returns real events; genuinely-missing container raises a real error; empty-but-valid timeline; non-NotFound errors do not fall back; updated the happy-path to assert the flat, no-duplicate shape)Notes
packages/mcp/src/tools/contracts.test.tswere written first (red), then the tool was implemented to green.packages/mcp/src/tools/get-container-transport-events.tsand the events region ofcontracts.test.tswere touched. The repo baseline is notoxfmt-clean (the pristineHEADversions of these files already failoxfmt --check), so a repo-wideoxfmt --writewas intentionally avoided — it rewrites files owned by other PRs and even mangles an import. The diff matches the surrounding committed style and stays confined to the owned files.This is an AI-drafted PR for human review.
🤖 Generated with Claude Code
Greptile Summary
This PR fixes a subtle but impactful bug where
get_container_transport_eventswould silently convert a 404 from the dedicated sub-resource into a success-shaped empty timeline — making a missing sub-resource look identical to "no events" to an LLM consumer. The fix adds a two-step fallback: a NotFound from the primary endpoint triggers aGET /containers/{id}?include=transport_eventsfetch, a second 404 (genuine missing container) is re-thrown as a real error, and a new_metadata.sourcefield tells callers which path served the data._metadata.errorfield.{ mapped, summary }wrapper is removed; the tool now returns a single flat object (total_events,timeline,milestones,_metadata).Confidence Score: 4/5
The change is safe to merge; it replaces a proven-buggy silent-swallow pattern with a well-tested two-step fallback, and the new tests cover all four distinct outcomes.
The fallback logic and error propagation are clearly correct and backed by new tests. The two observations — no log entry emitted at the moment of fallback, and the absent container_found flag on the primary zero-event path — are non-blocking quality concerns, not defects in the happy path.
get-container-transport-events.ts around the silent 404 catch block and the FormatOptions / containerFound branching logic in formatTransportEventsResponse.
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Tool as get_container_transport_events participant Sub as GET /containers/{id}/transport_events participant Inc as GET /containers/{id}?include=transport_events Tool->>Sub: "events(id, {format:'raw'})" alt 200 OK Sub-->>Tool: raw event list Tool-->>Tool: "formatTransportEventsResponse(raw, source='transport_events_subresource')" else 404 NotFound Sub-->>Tool: NotFoundError Note over Tool: silent – no log entry for the 404 Tool->>Inc: "get(id, ['transport_events'], {format:'raw'})" alt 200 OK (container exists) Inc-->>Tool: container + included events Tool-->>Tool: "extractIncludedTransportEvents → format(source='container_include_fallback', containerFound=true)" else 404 NotFound (container missing) Inc-->>Tool: NotFoundError Tool-->>Tool: logError + rethrow end else Other error Sub-->>Tool: Error Tool-->>Tool: logError + rethrow end%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Tool as get_container_transport_events participant Sub as GET /containers/{id}/transport_events participant Inc as GET /containers/{id}?include=transport_events Tool->>Sub: "events(id, {format:'raw'})" alt 200 OK Sub-->>Tool: raw event list Tool-->>Tool: "formatTransportEventsResponse(raw, source='transport_events_subresource')" else 404 NotFound Sub-->>Tool: NotFoundError Note over Tool: silent – no log entry for the 404 Tool->>Inc: "get(id, ['transport_events'], {format:'raw'})" alt 200 OK (container exists) Inc-->>Tool: container + included events Tool-->>Tool: "extractIncludedTransportEvents → format(source='container_include_fallback', containerFound=true)" else 404 NotFound (container missing) Inc-->>Tool: NotFoundError Tool-->>Tool: logError + rethrow end else Other error Sub-->>Tool: Error Tool-->>Tool: logError + rethrow endPrompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix(mcp): get_container_transport_events..." | Re-trigger Greptile