Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This is a public, cross-platform plugin marketplace for Terminal49.

## Shared source of truth

- `plugins/terminal49/skills/terminal49-mcp/SKILL.md` is the shared behavioral
- `plugins/terminal49/skills/container-tracking/SKILL.md` is the shared behavioral
guidance for all clients.
- `plugins/terminal49/.mcp.json` and `plugins/terminal49/mcp.json` are the MCP
adapters for Claude/Codex and Cursor respectively. Keep their server entries
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ Thanks for improving the Terminal49 agent plugins.
4. Test installation in any client affected by the change.

Platform-specific behavior belongs in its adapter. Guidance that applies to all
clients belongs in `plugins/terminal49/skills/terminal49-mcp/SKILL.md`.
clients belongs in `plugins/terminal49/skills/container-tracking/SKILL.md`.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This marketplace currently publishes one plugin:
## What the plugin provides

- OAuth connection to `https://mcp.terminal49.com`
- A shared `terminal49-mcp` skill for choosing and sequencing MCP tools
- A shared `container-tracking` skill for choosing and sequencing MCP tools
- Guidance for status, pickup readiness, holds, ETAs, routes, delays, and
demurrage-risk questions
- Guardrails around tracking-request creation, credentials, dates, and missing
Expand Down Expand Up @@ -84,7 +84,7 @@ to the authenticated Terminal49 account.
├── mcp.json # Cursor MCP adapter
├── README.md
├── assets/logo.svg # Referenced by the Cursor manifest
└── skills/terminal49-mcp/SKILL.md
└── skills/container-tracking/SKILL.md
```

The skill is shared. Each platform-specific manifest and MCP file is a thin
Expand Down
8 changes: 7 additions & 1 deletion plugins/terminal49/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ API keys, access tokens, or one-time codes into chat.

## Included skill

`terminal49-mcp` covers:
`container-tracking` is written for logistics operators and covers:

- finding an existing container or shipment from a business identifier
- starting a new tracking request when the user intends to do so
- inspecting container, shipment, terminal, event, and route details
- state-dependent answers: which attributes matter at each lifecycle stage,
from booking through empty return
- surfacing risk signals: ETA changes, long terminal dwell, last-free-day
pressure, active holds, rolls, and detention
- determining where pickup happens — the discharge port or an inland rail
destination
- querying fleets with filters and pagination
- presenting dates, holds, missing information, and paid-feature boundaries

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
---
name: terminal49-mcp
description: Use Terminal49 MCP tools to find, track, and investigate ocean container shipments. Use when a user asks about container status, shipment details, pickup readiness, terminal holds, ETAs, transport events, routes, delays, tracking requests, carrier support, or demurrage risk.
name: container-tracking
description: Find, track, and investigate ocean container shipments with Terminal49 MCP tools, written for logistics operators. Use when a user provides a container number, bill of lading, booking, or reference number, or asks about container status, location, pickup readiness, terminal holds, last free day, ETAs or ETA changes, dwell time, rail moves, inland destinations, transport events, routes, delays, tracking requests, carrier support, or demurrage and detention risk.
---

# Terminal49 MCP
# Container tracking (Terminal49 MCP)

Use the Terminal49 MCP server for container and shipment questions. Prefer the
smallest tool sequence that answers the question, and preserve the difference
between observed data and inference.
Use the Terminal49 MCP server for container and shipment questions. The user is
usually a logistics operator, not a developer: answer in plain operational
language, lead with what they should do or know, and prefer the smallest tool
sequence that answers the question. Preserve the difference between observed
data and inference.

A bare identifier with no question ("CAIU1234567", "where is MSCU…") means:
find it and report current status, location, and anything that needs attention.

## Before using tools

Expand Down Expand Up @@ -57,7 +62,7 @@ reference number. Detail tools require a Terminal49 UUID.
| Pickup readiness, terminal availability, holds, or LFD | `search_container`, then `get_container` with `shipment` and `pod_terminal` |
| What happened, where it moved, or delay analysis | `search_container`, then `get_container_transport_events` |
| Full shipment and its containers | `search_container`, then `get_shipment_details` |
| Multi-leg route, transshipment, vessels, or itinerary | `search_container`, then `get_container_route` |
| Multi-leg route, rail leg, transshipment, or itinerary | `search_container`, then `get_container_route` |
| Containers or shipments matching operational filters | `list_containers` or `list_shipments` |
| Tracking-request status or audit | `list_tracking_requests` |
| Supported carrier or SCAC lookup | `get_supported_shipping_lines` |
Expand All @@ -66,6 +71,57 @@ Use the `terminal49://docs/mcp-query-guidance` resource when available for the
server's current intent-to-tool playbooks. Use
`terminal49://docs/milestone-glossary` when event terminology needs explanation.

## What matters at each state

Which attributes are worth reporting depends on where the container is in its
lifecycle. Lead with the fields for the current state; skip fields that are not
yet meaningful.

| State | Lead with |
| --- | --- |
| Booked, before departure | ETD, POL, vessel and voyage, whether carrier data has started flowing |
| On the water | POD ETA and any change to it, current vessel, transshipment ports |
| Arrived at POD, not yet discharged | Actual arrival vs the last ETA, discharge status, holds already visible |
| Discharged at the POD terminal | Availability, holds, last free day, days since discharge, terminal name |
| Moving inland by rail | Rail departure and arrival events, destination ramp and its ETA, that pickup happens inland |
| Available for pickup | Pickup location, LFD and remaining free days, confirmation that holds are cleared |
| Picked up (full out) | Delivery status, empty-return deadline, whether the empty has been returned |

## Risk signals to surface

Check for these whenever the data is already in hand, and lead the answer with
any that apply — even if the user only asked "where is it":

- **ETA change** — the current ETA differs from earlier estimates in the
transport events. Report old vs new and the cumulative drift.
- **Long dwell** — discharged 3 or more days ago and not picked up. State the
day count; treat it as urgent when the LFD is past or unknown.
- **LFD pressure** — last free day is today, tomorrow, or past. This outranks
everything else in the summary.
- **Active holds** — customs, freight, or terminal holds block pickup even when
the container shows as available. Name each hold type.
- **Rolled or transshipped** — events show discharge and reload at an
intermediate port, or the vessel changed mid-route.
- **Detention risk** — picked up but the empty has not been returned and the
return deadline is near or past.

Label anything inferred (for example, dwell computed from a discharge date) as
computed, and never present a risk as a confirmed charge.

## Where pickup happens

Do not assume pickup is at the port of discharge. Check the shipment
destination and route:

- If the shipment has an inland destination or the route/events show a rail
leg, pickup happens at the inland ramp or destination terminal — report that
location and the rail arrival ETA, not the POD availability.
Comment on lines +116 to +118

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 Require the rail leg to reach the inland destination

For shipments with an origin-side or other intermediate rail leg but a final destination at the POD, this or treats any rail movement as proof of inland pickup and instructs every supported client to suppress the relevant POD availability, holds, and LFD. Require the destination to be inland and the rail leg to connect the POD to that destination before reporting an inland pickup location.

AGENTS.md reference: AGENTS.md:L18-L19

Useful? React with 👍 / 👎.

- If the container terminates at the POD, use `pod_terminal` data for
availability, holds, and LFD.
- When it is unclear which applies, say so and check
`get_container_route` or the transport events for rail milestones before
answering.

## Efficient detail loading

For `get_container`, request only the related data needed:
Expand All @@ -88,10 +144,13 @@ leaves a material question unanswered.
and `updated_after` in ISO 8601 form.
- Use pagination instead of silently assuming the first page is complete.
- State the filters and time boundary used in the answer.
- For "what needs attention" requests, rank results by the risk signals above:
LFD pressure first, then holds, then dwell, then ETA changes.

## Interpret and present results

1. Lead with the current answer: status, location, readiness, or risk.
1. Lead with the operational answer: status, location, readiness, or risk —
then supporting detail.
2. Separate actual timestamps from estimated timestamps and label each clearly.
3. Preserve source time zones. If converting a time, state the target time zone.
4. Call out terminal holds, customs issues, and pickup blockers explicitly.
Expand All @@ -110,8 +169,10 @@ leaves a material question unanswered.

1. Resolve the identifier with `search_container`.
2. Call `get_container` with `shipment` and `pod_terminal`.
3. Summarize current status, terminal, availability, holds, and relevant dates.
4. If the state is unclear, call `get_container_transport_events` for context.
3. Determine the lifecycle state and where pickup happens (POD or inland).
4. Summarize status, location, availability, holds, relevant dates, and any
risk signals.
5. If the state is unclear, call `get_container_transport_events` for context.

### Delay investigation

Expand All @@ -124,8 +185,9 @@ leaves a material question unanswered.
### Demurrage-risk review

1. Use `list_containers` with the narrowest known operational filters.
2. Fetch `get_container` with `pod_terminal` for the candidates that need detail.
3. Prioritize known pickup LFDs, active terminal holds, discharge state, and
availability.
2. Fetch `get_container` with `pod_terminal` for the candidates that need
detail.
3. Prioritize known pickup LFDs, active terminal holds, discharge state,
availability, and dwell days since discharge.
4. Do not calculate fees unless the required tariff and timing inputs are
present. Label any risk assessment separately from an actual charge.
4 changes: 2 additions & 2 deletions scripts/validate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const paths = {
codexPlugin: "plugins/terminal49/.codex-plugin/plugin.json",
mcp: "plugins/terminal49/.mcp.json",
cursorMcp: "plugins/terminal49/mcp.json",
skill: "plugins/terminal49/skills/terminal49-mcp/SKILL.md",
skill: "plugins/terminal49/skills/container-tracking/SKILL.md",
};

async function readJson(relativePath) {
Expand Down Expand Up @@ -153,7 +153,7 @@ for (const docPath of ["README.md", "CONTRIBUTING.md", "AGENTS.md", "plugins/ter
try {
const skill = await readFile(resolve(root, paths.skill), "utf8");
if (!skill.startsWith("---\n")) errors.push("skill must start with YAML frontmatter");
if (!/^name: terminal49-mcp$/m.test(skill)) errors.push("skill name must be terminal49-mcp");
if (!/^name: container-tracking$/m.test(skill)) errors.push("skill name must be container-tracking");
if (!/^description: .+$/m.test(skill)) errors.push("skill must have a description");
if (!skill.includes("search_container")) errors.push("skill must explain search_container");
if (!skill.includes("track_container")) errors.push("skill must explain track_container");
Expand Down
Loading