Skip to content

fix(mcp)!: apply the review and live-test fixes from #433 - #440

Merged
dodeja merged 17 commits into
mainfrom
mcp-fixes
Oct 17, 2026
Merged

dodeja merged 17 commits into
mainfrom
mcp-fixes

Conversation

@dodeja

@dodeja dodeja commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Why

Live runs found that track_container sent the container owner prefix as the SCAC. They also found that it hid why a request was not created, and that list_tracking_requests accepted statuses the API rejects. The MCP docs also showed fields the tools never return.

What changed

  • test(mcp): run get_container_route through the real SDK client
  • fix(mcp): stop sending the container owner prefix as track_container's SCAC
  • fix(mcp)!: limit list_tracking_requests status to the API's filter values
  • fix(mcp)!: report why track_container could not create a request
  • fix(mcp): give the track-shipment prompt a check-digit-valid example
  • docs: describe the MCP tracking request statuses and track_container
  • docs: list the real list_tracking_requests parameters in the MCP guide
  • docs: list the real list_shipments and list_containers inputs
  • docs: show search_container's tracking_requests and snake_case fields
  • docs: list summarize_containers, list_parties, and search_docs in the MCP guides
  • fix(mcp): return the created request's id and number type from track_container
  • fix(mcp): create a tracking request when the matching container is no longer tracked
  • docs: say track_container returns only a container that is still tracked
  • docs: show the real get_container and transport events output in the MCP guide
  • docs: use valid shipment filters in the MCP guide's SDK example
  • fix(mcp): say track_container returns container details only for a tracked container
  • docs: show the container number and pickup fields the SDK returns in the MCP guide

Scope

packages/mcp, docs/mcp, and the MCP in-depth guide. #418's summarize_containers behavior is unchanged.

Tradeoffs

track_container no longer returns an error for duplicates or pending requests. It returns tracking_request_created: false with the reason, so agents can tell "already tracked" from a failure.

Blast Radius

Breaking changes for MCP clients:

  • list_tracking_requests no longer accepts status succeeded. Use created.
  • search_container gains a tracking_requests array.
  • track_container's duplicate result changes shape.

Verification

  • vp run check and vp run test pass at every commit and at the tip.
  • Live MCP stdio harness against staging 51711be: 43/44 pass. Every tool was called, and every structuredContent matched its output schema. The one failure is DEV-13514, a backend 422 shape.

Base: #439.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

RetriggerView in GreptileConfidence Score: 4/5

The PR appears safe to merge, with a non-blocking inconsistency in the details returned after creating a request.

Fix All in CodexFindings

  1. P2 Created request details disappear ▶
Fix with agent prompt
### Issue 1
packages/mcp/src/tools/track-container.ts:511-515
When a newly created request links to a readable container, the successful response drops `tracking_request`. Callers lose the request's ID and confirmed number type, despite the new tool description promising both. Include `tracking_request: created` alongside the container details so callers do not need another search.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This PR corrects MCP carrier selection, tracking status filters, duplicate responses, and tracking-request search results.

  • Tracking requests now use the inferred carrier and return clearer results.
  • Search results now include matching tracking requests.
  • Tracking request filters now use the API’s accepted statuses.
  • MCP guides now show the tools’ actual inputs and returned fields.
  • The route tool now has a test that uses the real SDK client.

Known verification failure: dodeja identified DEV-13514, a backend 422 shape, as the one failure in the live MCP checks.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[track_container] --> B[Search the account]
  B --> C{In-progress request?}
  C -->|Yes| D[Return existing request]
  C -->|No| E{Exact container still tracked?}
  E -->|Yes| F[Return container details]
  E -->|No| G[Infer carrier and create request]
  G --> H{API result}
  H -->|Duplicate| I[Return existing request ID]
  H -->|Refused| J[Explain the refusal]
  H -->|Created| K{Linked container readable?}
  K -->|No| L[Return created request details]
  K -->|Yes| M[Return container details without request details]
Loading

Reviews (1) · Last reviewed commit: "docs: show the container number and pick..." · Reviewed by Greptile

@vercel

vercel Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
api Ready Ready Preview Oct 17, 2026 6:40am UTC

Request Review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T06:41:37.938032Z 5ebebb4 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4911dbf123

ℹ️ 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".

Comment on lines +366 to +369
const container = result.containers.find(
(candidate) =>
normalizeTrackingNumber(candidate.container_number) === number,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inspect all exact container matches before retracking

When /search returns multiple records for a reused container number, this selects only the first exact match even though search_container explicitly supports duplicate container numbers. If that first record belongs to a stopped shipment but a later exact match is still active, the subsequent stopped-tracking check falls through and attempts to create another tracking request rather than returning the active container. Preserve all exact matches and check them until an actively tracked record is found before deciding to retrack.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. track_container now reads every container whose number matches exactly, in search order, and returns the first one whose shipment is still tracked. It retracks only when none is tracked. The new test failed before the fix.

Comment on lines +511 to +515
const created = createdRequest(trackingRequest, {
number,
numberType: inferredNumberType,
scac: requestedScac,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Created request details disappear

When a newly created request links to a readable container, the successful response drops tracking_request. Callers lose the request's ID and confirmed number type, despite the new tool description promising both. Include tracking_request: created alongside the container details so callers do not need another search.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/mcp/src/tools/track-container.ts
Line: 511-515

Comment:
**Created request details disappear**

When a newly created request links to a readable container, the successful response drops `tracking_request`. Callers lose the request's ID and confirmed number type, despite the new tool description promising both. Include `tracking_request: created` alongside the container details so callers do not need another search.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The success path that also returns the linked container now includes tracking_request with its id, request_number, number_type and scac.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd4a73ed1f

ℹ️ 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".

unavailableId ??= match.id;
continue;
}
if (!containerDetails.shipment?.line_tracking_stopped_at) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat a stopped reason as stopped tracking

When a shipment has line_tracking_stopped_reason set but line_tracking_stopped_at is absent, this condition treats the old container as actively tracked and returns it instead of creating the requested tracking request. The same module already treats either field as evidence that tracking stopped when calculating demurrage, and both fields are independently nullable in the API schema, so expose/check the reason here as well.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dismissing. The backend sets the stopped reason in the same transaction as the tracking_stopped transition, and that transition stamps line_tracking_stopped_at. The API's duplicate check keys on that state. So a shipment with a reason but no stopped_at is still tracked as far as a new request is concerned, and checking the reason too would only turn this into a duplicate rejection.

Comment on lines +218 to +222
errors.some(
(entry) =>
IDENTIFIER_POINTER.test(entry.source?.pointer ?? '') &&
(!entry.code || IDENTIFIER_CODES.has(entry.code)),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve actionable SCAC refusal details

When the API returns code: "blank" at /data/attributes/scac for a nonblank but unsupported or unrecognized carrier—the repository's documented 422 examples use exactly that shape—this predicate classifies the response as an invalid identifier. The caller consequently receives the generic “identifier is invalid” message instead of the API's actionable explanation that the carrier is unsupported; distinguish an actually blank value from these carrier refusals or pass their detail through.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dismissing. The API takes the JSON:API code from the validation's error type. An unknown carrier returns not_recognized and an unintegrated one returns no_integration. no_integration passes its detail through, and not_recognized tells the caller to check the SCAC. Code blank only appears when the SCAC was blank. The blank examples in openapi.json are out of date and we'll fix them separately.

Comment thread packages/mcp/src/server.ts Outdated
Comment on lines +1088 to +1089
'A new request returns the request (id, request_number, number_type, scac), not container details: ' +
'poll list_tracking_requests for its status, then use search_container or get_shipment_details. ' +

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Describe the linked-container success response accurately

When a newly created tracking request already contains a container relationship and that container is readable, executeTrackContainer returns the full container fields together with tracking_request and tracking_request_created: true. This description instead tells MCP clients that a new request never returns container details and instructs them to poll, which can make agents ignore the usable snapshot and issue unnecessary follow-up calls.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The description now says container details come back only when the create response links a container, which a new request normally does not. It still tells clients to poll list_tracking_requests.

Base automatically changed from sdk-fixes to main October 17, 2026 06:28
@mintlify

mintlify Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
terminal49 🟢 Ready View Preview Oct 17, 2026, 6:39 AM

dodeja and others added 12 commits October 16, 2026 23:39
The tool tests stub the SDK, so nothing checked that the summary the
tool builds matches what containers.route() returns from
GET /containers/{id}/map_geojson. A test now gives the real SDK client
the map_geojson fixture and asserts the request path and the route
summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s SCAC

A container prefix (MSKU, ZZQU) names the equipment owner, not the
carrier. track_container passed it to createTrackingRequestFromInfer as
an explicit scac, which overrode the carrier that infer_number selected
(MSKU4330011 went out as MSKU instead of MAEU) and sent an invalid SCAC
when infer had no prediction (ZZQU4330056 got a 422 reported as an
invalid identifier).

Only a caller-supplied scac now reaches the API. When neither inference
nor the caller provides a carrier or number type, the tool returns a
TrackContainerInputError that tells the model what to pass, instead of
the old hint that formatToolError replaced with a generic message. The
pending and 404 results report the SCAC the API recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lues

The status enum offered succeeded, which the API does not recognize. Its
filter[status] handler treats anything other than created, failed, or
cancelled as pending, so status: succeeded silently returned pending
requests. A tracking request that succeeded has status created.

The enum is now pending, created, failed, and cancelled, typed against
the generated filter[status] parameter so it fails to compile if the
spec adds or drops a value. The descriptions say that created means the
request succeeded and that each item's shipment.id (mapped from
tracked_object) feeds get_shipment_details.

BREAKING CHANGE: list_tracking_requests rejects status: 'succeeded'.
Pass 'created' for requests that succeeded. The new 'cancelled' value
selects cancelled requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
formatToolError turned every 422 from POST /tracking_requests into
"Tracking identifier X is invalid". The live run hit the account's
tracking request limit and told the user the number was invalid, and
the backend's duplicate, tracking_slot_limit_reached, and
insufficient_credits 422s were reported the same way.

track_container now parses the 422 body and classifies it by code
before pointer. A duplicate returns a non-error result with the
existing tracking_request id and shipment_id. Limit and credit refusals
pass the API's message through. Errors that point at request_number,
request_type, number, or scac with an identifier code (blank,
invalid_format, not_recognized, and the like), and infer_number's bare
detail with no code or pointer, say the identifier is invalid. Any
other refusal, such as container_number_tracking_disabled,
no_container_request_type_support, or a party that does not belong to
the account, passes its detail through.

search_container also dropped tracking_request search hits, so a number
with a pending request was tracked again and refused as a duplicate. It
now returns tracking_requests, with shipment_id on a request whose
tracking was stopped, and track_container answers with a request that is still in
progress (pending or awaiting_manifest, the statuses the API treats as
blocking) instead of posting, and reports its real status. The API
scopes its duplicate check by carrier, so when the caller names a SCAC,
an in-progress request under another SCAC (or with none yet) is ignored
and the new request is sent.

BREAKING CHANGE: search_container's output gains a required
tracking_requests array, and total_results now counts containers,
shipments, and tracking requests. track_container returns a result with
tracking_request_created: false and source 'duplicate' or
'search_match' for a number that is already tracked or has an
in-progress request, where it used to fail with an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
track_container rejects CAIU1234567 on the ISO 6346 check digit, so the
prompt's example number now uses MSKU4330011, which passes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
list_tracking_requests accepts pending, created, failed, or cancelled.
created matches requests that succeeded and are still tracking; a
stopped request has status tracking_stopped, which the filter cannot
select. track_container needs scac when the carrier cannot be inferred
and never uses the container prefix as the SCAC. It returns an existing
container or in-progress (pending or awaiting_manifest) request with
tracking_request_created false before it creates one.

The examples use MSKU4330011, which passes the ISO 6346 check digit;
CAIU1234567 fails it and never reaches the API. The overview examples
drop the request_type argument the tool does not accept and use number
instead of the deprecated containerNumber alias.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MCP guide's tool table listed filters and request_type, which the
strict input schema rejects, and left out request_number and scac.
Match the server.ts schema and docs/mcp/home.mdx.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MCP guide and TOOLS_OVERVIEW still showed status, port, carrier,
updated_after, and a comma-separated include string. Both tools use
strict input schemas, so those calls fail validation. The guide table
now lists the inputs the schemas accept, and the examples use
voyage_status, current_status, pod_code, shipping_line_scac,
pickup_lfd, and an include array.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The example response used camelCase keys the tool never emits and had
no tracking_requests array. Show the real snake_case output, add
pending and failed tracking requests, say that only a request whose
tracking was stopped carries shipment_id, and say total_results counts
all three result types. TOOLS_OVERVIEW.md's Returns line says the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…i in the MCP guides

The MCP guide's tools table, TOOLS_OVERVIEW.md, and the package README
left out tools the server registers. Add summarize_containers,
list_parties, and search_docs to each, add whoami to the package
README, which now counts all 14 tools, and add a search_docs section
to the MCP tools reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…container

A new request was described by the submitted number and the locally
inferred type, without the request's id. Every path that creates a
request now returns tracking_request with the id, request_number,
number_type, and scac the API sent back, falling back to the submitted
values for an attribute the response leaves out. That includes the
path that also returns the linked container's details.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… longer tracked

track_container returned early on any container search hit for the
number. Search returns every container with a shipment, including one
whose shipment stopped tracking, and the API accepts a new request for
such a container because container numbers are reused on later voyages.
It also treated a lone search hit with a different number as a match,
although search matches substrings and reference numbers.

track_container now checks for an in-progress tracking request first.
It then reads every container whose number matches exactly, in search
order, because a reused number can match a stopped container and a
tracked one. It returns the first whose shipment has no
line_tracking_stopped_at. Otherwise it sends the request
and lets the API's duplicate check decide. get_container's shipment
summary now includes line_tracking_stopped_at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dodeja and others added 5 commits October 16, 2026 23:39
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…MCP guide

The get_container and get_container_transport_events examples showed
camelCase fields that the tools never return. Both examples now hold
actual tool output with snake_case keys, and track_container says that
an existing container comes back in the get_container shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The example passed status and carrier to client.shipments.list, which
the SDK rejects before sending a request, and said the call was not
available through MCP although list_shipments is an MCP tool. It now
filters by voyage_status and pod_code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…acked container

The tool description promised detailed container information after it
creates a tracking request. The create response carries the request,
not a container, so a new request returns its id, request_number,
number_type, and scac. The description now says container details come
back only for a container that is still being tracked, and tells the
caller to poll list_tracking_requests and then use search_container or
get_shipment_details.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the MCP guide

The Raw vs Mapped example used attributes.container_number and a mapped
containerNumber and availableForPickup at the top level. The API
serializes the container number as number, and mapContainer returns
number and nests availableForPickup under location.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

2 active deployments
Preview — 5ebebb47 Deployed Oct 17, 2026 by vercel[bot]
staging - docs — 5ebebb47 Deployed Oct 17, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant