TL;DR – get started in 5 minutes
1
Pick your MCP client
Follow the setup guide for your tool:
- Claude (claude.ai, Claude Desktop — available in the Claude Directory)
- Claude Code
- ChatGPT
- Cursor
- Microsoft Copilot (Copilot Studio)
- VS Code (GitHub Copilot)
- Gemini (personal Google accounts)
- Antigravity
- Agent plugins (Claude Code, Cursor, Codex, GitHub Copilot CLI)
- Other MCP clients
2
Add the connector URL
For Claude, add Terminal49 from the Claude Directory. For other clients, point the client at
https://mcp.terminal49.com. No API key needed.3
Sign in to Terminal49
The server supports OAuth 2.1: your client opens a browser window, you sign in with your Terminal49 credentials and approve access. That’s it.
4
Ask a question
“Using the Terminal49 MCP server, search for container CAIU1234567 and summarize its status.”
5
Explore tools
“List the tools available in the Terminal49 MCP server and what they’re for.”
Need test container numbers? See Test Numbers for containers you can use during development.
Transports
Authentication:
- OAuth 2.1 (recommended) – no API key needed. Add the connector URL, sign in with your Terminal49 credentials in the browser window your client opens, and approve access. Clients discover the authorization server (
https://auth.terminal49.com) automatically and use authorization code with PKCE and Dynamic Client Registration. - API key (for clients without OAuth support) – create a key in the developer portal and pass
Authorization: Token YOUR_API_KEY. Use theTokenscheme for API keys; theBearerscheme is used for OAuth access tokens, which OAuth clients obtain automatically. - The local stdio server reads the
T49_API_TOKENenvironment variable instead.
Setup guides
Claude
Add Terminal49 from the Claude Directory
Claude Code
One
claude mcp add command from your terminalChatGPT
Install from the ChatGPT Plugins Directory
Cursor
mcp.json or Cursor Settings → MCPMicrosoft Copilot
Copilot Studio agent tools
VS Code
GitHub Copilot agent mode
Gemini
Gemini app custom apps (personal accounts)
Antigravity
Google Antigravity agent
Agent plugins
Terminal49 plugin for Claude Code, Cursor, Codex, and Copilot CLI
Other MCP clients
Generic OAuth or API-key configuration
Any MCP client
Use the same hosted Streamable HTTP endpoint in any MCP-compatible client. With an OAuth-capable client, no credentials are needed — just the URL:Limit the tools
Two optional request headers reduce the tools your client sees. They can only remove tools.X-T49-MCP-Read-Only: truelists only read-only tools. It removestrack_containerand every write tool.X-T49-MCP-Toolsetstakes a comma-separated list of toolsets.
core: every tool except the document reads, the write tools below, and the trade intelligence tools.whoamireports the toolsets a session serves.documents:list_documentsandget_document.operations:set_party_role,set_custom_fieldandupdate_record. It is in the default toolsets. SendX-T49-MCP-Toolsets: core,documents,tradeto keep every other tool without them.tracking_lifecycle:stop_shipment_tracking,resume_shipment_trackingandcancel_tracking_request. It is not in the default toolsets; a deployment serves it only whenT49_MCP_TOOLSETSlists it.documents_write: no tools yet.developer: no tools yet.trade: the trade intelligence tools.
true, false, 1 or 0, in any case. Any other read-only value, an unknown toolset name, or a toolsets value made only of commas returns 400 Bad Request.
Monitoring
Self-hosted deployments can enable Sentry MCP Monitoring by settingSENTRY_DSN. This captures MCP tool calls, resource reads, prompt usage, performance spans, and errors in Sentry.
Signed URLs, such as a document download link, are replaced with [REDACTED_URL] in logs, Sentry, and PostHog, even when output recording is on. Confirmation tokens are replaced with [REDACTED_CONFIRMATION_TOKEN] in the same places.
Each call to a write tool, track_container included, logs one mcp.write.audit event, adds a Sentry breadcrumb, and sends a PostHog event when the account is known. The event names the tool, its tier, the phase, the outcome, the record type and ID, the account ID, and the credential type. It never carries values, reference numbers, party names, URLs, or tokens.
Tools reference
In the default toolsets, every tool is read-only except
track_container and the write tools in the operations toolset, which are described under Write tools. track_container creates a tracking request. If your account already has an in-progress tracking request for the number, or a container with that number that is still being tracked, track_container returns it instead of creating a new request. A container whose shipment has stopped tracking does not count. A request is in progress while its status is pending or awaiting_manifest. When you pass scac, only an in-progress request under that SCAC counts. Failed or cancelled requests do not block a new one.search_container
Find containers by container number, BL, booking, or your own reference. This is the fastest way to locate containers.
Parameters
query(string, required) – container number, BL, booking, or reference
Example MCP request
Example MCP request
Example response
Example response
total_results counts containers, shipments, and tracking requests. The search API returns no status for a container hit, so status is unknown. Use get_container for the current status. pod_terminal, pol_terminal, and destination appear only when the search API returns them. Only a request whose tracking was stopped (status tracking_stopped) has shipment_id. A failed request has failed_reason. not_found and invalid_number mean the carrier rejected the number, so check the number and carrier before you track it again. retries_exhausted and expired can succeed on a later request with the same values. duplicate means the shipment already exists. See failed reasons for the full list. Requests that succeeded (status created) never appear in tracking_requests. Search returns the tracked container under containers, or the tracked shipment under shipments for a bill of lading or booking number. A tracking request whose number matches a returned shipment’s number is left out.- “Find this container and tell me where it is”
- “Show all containers with reference PO-12345”
track_container
Start tracking a container, bill of lading, or booking number. The tool first searches your account for the number:
- If a tracking request for it is still in progress, with status
pendingorawaiting_manifest, the tool returns that request and its status. When you passscac, only an in-progress request under the same SCAC counts. - If a container with exactly that number is still being tracked, the tool returns that container’s details in the same shape as
get_container, plustracking_request_createdandinfer_result. A container whose shipment has stopped tracking does not count, because container numbers are reused on later voyages. - Otherwise the tool creates a tracking request. If the API reports that the number is already tracked, the tool returns
source: "duplicate", the existingtracking_request.id, andshipment_idwhen the request has a shipment.messageholds the API’s explanation, which can name a different number that the existing request is stored under.
tracking_request_created in the result. It is false when the tool returned an existing container, an in-progress request, or a duplicate. Container numbers are checked against the ISO 6346 check digit before the tool calls the API.
Parameters
number(string, required) – container number, BL, or booking numbernumberType(string, optional) – override inference (container,bill_of_lading,booking_number)scac(string, optional) – shipping line code, e.g.,MAEUfor Maersk. Required when Terminal49 cannot infer the carrier. A container prefix such asMSKUnames the equipment owner, not the carrier, so the tool never uses it as the SCAC.refNumbers(string[], optional) – your reference numbers
Example MCP request
Example MCP request
- “Track container MSKU4330011 with Maersk”
- “Start tracking this new shipment”
get_container
Get detailed container information with flexible data loading. Choose what to include based on your question.
Parameters
id(uuid, required) – Terminal49 container UUIDinclude(string[], optional) – what to load:shipment– routing, BOL, line, ref numbers (lightweight)pod_terminal– terminal name, location (lightweight)transport_events– addsevents.count,events.rail_events_count, andevents.latest_event; useget_container_transport_eventsfor the full timelinecustom_fields– account-defined fields such as PO number or project manager (lightweight; needs a signed-in user, not an API key)party_roles– companies assigned to the container by role, such ascustomerorpickup_dray_carrier. Each row has the party roleid,role,party_id, andparty_name.party_nameis the company name, else the party’s name, else its nickname. If the credential cannot read party roles,party_rolesisnullandparty_roles_noteexplains why.
_metadata with factual details such as includes_loaded.
Example MCP request
Example MCP request
Example response
Example response
- “What’s the status of this container?”
- “Is it available for pickup? Any holds?”
- “When does demurrage start?”
get_container_transport_events
Get the full event timeline for a container’s journey. Each event has a vessel_id, or null, that you can pass to get_vessel.
Parameters
id(uuid, required) – Terminal49 container UUID
Example response
Example response
- “Show me the journey timeline”
- “What happened to this container?”
- “How long was the rail portion?”
get_shipment_details
Get shipment-level information including routing, BOL, and all containers.
Parameters
id(uuid, required) – Terminal49 shipment UUIDinclude_containers(boolean, optional) – include container list (default: true)include_custom_fields(boolean, optional) – include account-defined custom fields such as PO number or project manager (default: false; needs a signed-in user, not an API key)include_party_roles(boolean, optional) – include the companies assigned to the shipment by role, in the same shape asget_container(default: false)
- “Tell me about this shipment”
- “What containers are on this BL?”
- “Show me the routing”
get_supported_shipping_lines
List carriers supported by Terminal49 with their SCAC codes.
Parameters
search(string, optional) – filter by name or SCAC
- “What carriers do you support?”
- “What’s the SCAC code for CMA CGM?”
get_container_route
Get a container’s route: the ports in order with their arrival and departure times, and the vessel carrying the container while it is at sea.
The tool returns:
ports: each port’s sequence, label (POL,TS1,POD), name, country code,inboundETA and ATA, andoutboundETD and ATD.current_vessel: name, IMO, voyage, position, heading, speed in knots, and the departure and arrival ports. It isnullwhen the container is not at sea.total_ports: the number of ports on the route.
NoRouteData result with a suggested alternative instead of an error.
Parameters
id(uuid, required) – Terminal49 container UUID
- “What’s the routing for this container?”
- “Which transshipment ports?”
- “What vessel is it on, and where is it now?”
list_shipments
Return one page of shipments with verified API filters. Common inputs include exact shipment number, tracking_stopped, actively_tracked, voyage_status, pod_code, pol_code, pod_arrival, created_at, and tags.
Use the typed advanced_filters object for the full shipment filter catalog. Use sort for server ordering, include_containers for container relationships, and page and page_size for pagination. Container relationships default to excluded. Pages default to 25 rows and cannot exceed 25.
Parameters
number(string or string[], optional) - exact shipment number. Arrays match any supplied number.tracking_stopped(boolean, optional) - shipping-line tracking stopped state.actively_tracked(boolean, optional) - active tracking state.voyage_status(string, optional) -arrivedoron_ship.pod_code(string or string[], optional) - Port of Discharge UN/LOCODE.pol_code(string or string[], optional) - Port of Lading UN/LOCODE.pod_arrival(string or string[], optional) - arrival date expressions or bounds.created_at(string or string[], optional) - creation timestamps with a timezone offset.tags(string or string[], optional) - account shipment tags.advanced_filters(object, optional) - complete typed shipment filter catalog.sort(string, optional) - supported server sort token or tokens.include_containers(boolean, optional) - include related containers. Default false.include_stopped_tracking(boolean, optional) - include records whose tracking stopped. Default false: like the dashboard, only actively tracked records are returned unlessactively_trackedis set. Set true for history over a past period.page(number, optional) - page number, starting at 1.page_size(number, optional) - page size. Default 25, maximum 25.
- “Show actively tracked shipments arriving at Los Angeles this week.”
- “Find shipments by an exact bill of lading number.”
list_containers
Return one page of containers with verified API filters. Common inputs include number, current_status, pod_code, pol_code, shipping_line_scac, has_holds, has_fees, requires_attention, actively_tracked, arrival, pickup_lfd, and tags.
Use the typed advanced_filters object for the full container filter catalog. Use sort for server ordering and include to add shipment, pod_terminal, or both. Pages default to 25 rows and cannot exceed 25.
Parameters
number(string or string[], optional) - exact container number. Comma-separated numbers match any; arrays require all.current_status(string or string[], optional) - container status from the schema’s finite vocabulary.pod_code(string or string[], optional) - Port of Discharge UN/LOCODE.pol_code(string or string[], optional) - Port of Lading UN/LOCODE.shipping_line_scac(string or string[], optional) - carrier Standard Carrier Alpha Code.has_holds(boolean, optional) - reported terminal hold state.has_fees(boolean, optional) - reported terminal fees.requires_attention(boolean, optional) - attention selection. False is not the exact complement of true.actively_tracked(boolean, optional) - related shipment tracking state.arrival(string or string[], optional) - arrival date expressions or bounds.pickup_lfd(string or string[], optional) - pickup Last Free Day date expressions or bounds.tags(string or string[], optional) - related shipment tags.advanced_filters(object, optional) - complete typed container filter catalog.sort(string, optional) - supported server sort token or tokens.include(string[], optional) -shipment,pod_terminal, or both.include_stopped_tracking(boolean, optional) - include records whose tracking stopped. Default false: like the dashboard, only actively tracked records are returned unlessactively_trackedis set. Set true for history over a past period.view(string, optional) -compact(default) returns number, status, availability, POD terminal, key dates, LFD, holds, fees and the shipment’s BL, carrier and POD ETA;fullreturns every attribute.page(number, optional) - page number, starting at 1.page_size(number, optional) - page size. Default 25, maximum 25.
- “Show available containers at Los Angeles with reported terminal holds.”
- “List actively tracked containers approaching their pickup Last Free Day.”
summarize_containers
Count containers grouped by one dimension, using the same filters as list_containers. Use it for “how many” and “break down by” questions; use list_containers for “which ones”. Like the dashboard, only actively tracked containers are counted unless include_stopped_tracking is true.
Parameters
- The filters accepted by
list_containers(includingadvanced_filtersandinclude_stopped_tracking). group_by(string, required) -pod_terminal,current_status,shipping_line,port_of_discharge,hold_type,pickup_lfd_dateorpod_arrival_date.
GET /v2/containers/summary in one request. hold_type counts each distinct current POD hold name, including pending holds, so a container with two holds counts twice; dates follow dashboard display timezones, falling back to UTC when missing or invalid. Groups are ordered by count and capped at 200; truncated is true when there are more.
Good for
- “How many containers are at risk at each terminal?”
- “Break down containers on hold by hold type.”
list_parties
Find the companies on the account’s shipments (customers, shippers, consignees, customs brokers, freight forwarders, dray carriers) by name and return their IDs. Filter a customer with advanced_filters.customer_id on list_containers, summarize_containers, or list_shipments. Filter other roles with advanced_filters.parties on list_containers or summarize_containers, keyed by role, such as { "pickup_dray_carrier": "<id>" }. A customer created before party roles is listed under its customer account ID, which customer_id accepts and advanced_filters.parties does not.
Parameters
id(uuid, optional) - one party to read, such asparty_idfrom a party role. When you passid, the tool ignoressearchandlimitand returns the party under the ID you passed. An unknown ID returns no parties. A party with no company name comes back with its name, else its nickname, else its ID, asname.search(string, optional) - company name or part of one; case and punctuation are ignored.limit(number, optional) - most parties to return. Default 25, maximum 50.
- “How many containers does Acme have at the terminal?” (find Acme, then filter by
customer_id) - “Which containers is Ray Drayage picking up?” (find the carrier, then filter by
pickup_dray_carrier)
set_party_role
Attach a party to containers or shipments in a role, replace the party in that role, or detach it. For a party already on a record, use the party_id shown in that record’s party roles (get_container with party_roles, or get_shipment_details with include_party_roles). list_parties can list a customer under an account ID that party roles do not accept. Use list_parties only for a party that is on no record yet.
add, and a replace that detaches nobody, run at once. remove, and a replace that detaches another party, ask you to approve a preview of every record that changes, as described under Write tools. One approval covers the whole call.
Parameters
container_idsorshipment_ids(array of IDs) - 1 to 50 records; pass one, not both.role(string) - containers supportpickup_dray_carrier; shipments also supportshipper,consignee,notify_party,customs_broker,customer, andfreight_forwarder.party_id(string) - the party’s ID, as shown in a record’s party roles.action(string) -add,replace, orremove.addattaches the party beside any others in the role. A record holds onepickup_dray_carrierand onecustomer, soaddfails on a record where a different party already holds one of those; usereplaceto swap it.replacemakes the party the only one in the role and detaches the rest. It does not replace a shipment’scustomer: remove the current customer, then add the new one.removedetaches the party.confirmation_token(string, optional) - only on a client that cannot ask you directly, after you approve the preview.
current) and after the change (proposed), counts the records that already match, and names records that cannot be changed with the reason. When Terminal49 has no party with party_id, the call returns party_not_found before it reads any record. A remove whose party_id matches no role on a record that holds another party in that role fails that record and says which party it has.
Returns the records that changed, those that already matched, and any that failed. A call stops after about 20 seconds; on the hosted server that time starts when the request arrives, so checking your credentials counts toward it. Records it had not started saving by then are listed in not_attempted, unchanged, so you can call again with them. A record that was still being saved when time ran out is listed in failed, and so is a save that failed without a clear answer from Terminal49, such as a dropped connection. The change may have been applied to these records, so check them before you call again.
Good for
- “Set the drayage carrier to Ray Drayage on these containers.” (
replace)
set_custom_field
Set or clear one of your account’s custom fields on containers or shipments. Filling fields that are empty runs at once. Overwriting or clearing a value on any record in the call asks you to approve a preview of every record that changes, as described under Write tools. One approval covers the whole call.
Parameters
entity(string) -containerorshipment.ids(array of IDs) - 1 to 50 records.api_slug(string) - the custom field’s API slug.value(string, number, boolean, array of strings, or null) - the new value; pass an array of option values for a multi-select field, andnullto clear the field.confirmation_token(string, optional) - only on a client that cannot ask you directly, after you approve the preview.
current and proposed value and counts the records that already match.
Returns the records that changed, those that already matched, and any that failed. A call stops after about 20 seconds; on the hosted server that time starts when the request arrives, so checking your credentials counts toward it. Records it had not started saving by then are listed in not_attempted, unchanged, so you can call again with them. A record that was still being saved when time ran out is listed in failed, and so is a save that failed without a clear answer from Terminal49, such as a dropped connection. The change may have been applied to these records, so check them before you call again.
Good for
- “Set Sales Rep to Sofia Cortina on these containers.”
update_record
Add or remove reference numbers, such as PO or job numbers, on a container, shipment, or tracking request, and tags on a shipment. Adding runs at once and keeps everything already there. Removing any value asks you to approve a preview first, because searches, filters, and integrations that use it stop finding the record.
Parameters
entity_type:container,shipment, ortracking_request.entity_id: the record’s Terminal49 ID.ref_numbers:{ add, remove }, up to 20 values each.tags:{ add, remove }, shipments only.
list_tracking_requests
List tracking requests with optional filters and pagination.
Parameters
request_number(string, optional) – tracking request identifierstatus(string, optional) –pending,created,failed, orcancelled.createdmatches requests that succeeded and are still tracking. This filter cannot select two statuses:awaiting_manifest, a request that is still being processed while the carrier manifest is pending, andtracking_stopped, a succeeded request whose tracking was stopped. To find requests in those statuses, passrequest_numberwithoutstatus.scac(string, optional) – four-letter shipping line SCACpage(number, optional) – page number, starting at 1page_size(number, optional) – results per page (default: 25; maximum: 25)
- “Show failed tracking requests”
- “List latest tracking activity”
get_tracking_request
Get one tracking request: its number, carrier SCAC, status, failure reason, retry state, and the shipment it created.
Parameters
id(uuid, required) – tracking request UUID, such as theidfromlist_tracking_requestsinclude_custom_fields(boolean, optional) – include account-defined custom fields set on the request (default: false; needs a signed-in user, not an API key)
request_number,request_type,scac,status,failed_reason,ref_numbers,tags,is_retrying, andretry_countshipment_idonce the request has succeeded (statuscreated), otherwisenull
- “Did my tracking request go through?”
- “Why did tracking fail for this number?”
identify_tracking_number
Identify a container, bill of lading, or booking number without tracking it. The tool creates nothing and adds no tracking volume.
Parameters
number(string, required) – one number, 4 to 64 characters
number_type(container,bill_of_lading, orbooking_number),is_valid,check_digit_passed, andinvalid_reasoncarrier– thedecision(auto_select,needs_confirmation, orno_prediction), theselectedcarrier, and thecandidates, each withscac,name, andconfidencealready_tracked– the account’scontainersandshipmentswith exactly this number, and anyin_progress_request. It isnullwhen none is found.already_tracked_note– set when the check could not run. Thenalready_trackedisnull, but that does not mean the number is untracked.
error: "rate_limited" with a message instead of failing.
Good for
- “Which carrier is MAEU123456789?”
- “Are we already tracking CAIU2885402?”
get_location
Get a port, terminal, or metro area.
Parameters
kind(string, required) –port,terminal, ormetro_areaid(string, required) – the Terminal49 UUID. A port or metro area also accepts its UN/LOCODE, such asUSLAX. A terminal needs its UUID.
- Every kind:
nameandtime_zone - A port or metro area:
code(UN/LOCODE),state_abbr,country_code,latitude, andlongitude - A terminal:
nickname,firms_code,smdg_code,bic_facility_code,address, and itsport. The terminal’stime_zoneis its port’s time zone.
- “What is the FIRMS code for this terminal?”
- “Which time zone is the last free day in?”
get_vessel
Get a vessel’s name, IMO, MMSI, and last reported position. Optionally project its track to a port and estimate when it arrives.
Parameters
id(uuid, required) – vessel UUID, such asvessel_idfromget_container_transport_eventseta_to(object, optional) – project the track to a port:port_id(uuid, required) – the port the vessel is heading toprevious_port_id(uuid, required) – the port it left before thatlatitudeandlongitude(numbers, optional) – start from these coordinates instead of the last reported position. Give both or neither.
position–latitude,longitude,speed_knots,heading_degrees, andreported_at, ornulleta– witheta_to:estimated_arrival_at,total_positions, and up to 120 evenly spacedpositions
position.
The projection needs Routing Data, a paid feature. Without it, eta is null and eta_note explains why. If the API cannot project the port pair, eta is also null and eta_note asks you to check port_id and previous_port_id.
If the account does not have vessel data, the tool returns error: "FeatureNotEnabled" with a message and an alternative instead of the vessel.
Good for
- “Where is the vessel now?”
- “When will the vessel reach Los Angeles?”
list_custom_field_definitions
List the account’s custom fields, such as PO number or project manager.
Parameters
entity_type(string, optional) –containerorshipment. Omit it for both.include_archived(boolean, optional) – also list archived fields (default: false)
definitions– each withapi_slug,display_name,description,entity_type,data_type,validation,sensitive, andarchivedoptions– forenumandenum_multifields, the allowedlabelandvaluepairs in display orderoptions_note– set when one field’s options cannot be loaded. That field’soptionsisnull, and the other definitions still return.
definitions is null and custom_fields_note explains why.
Good for
- “Which custom fields does our account have?”
- “What values can the priority field take?”
list_documents
Find documents on the account, such as Bills of Lading, arrival notices, and delivery orders. Results are newest first.
Parameters
filename(string, optional) – text to match in the file namereference(string, optional) – one reference to match, such as a bill of lading, booking, container, or PO numberdocument_types(string[], optional) – document type codes, such asarrival_noticesort(string, optional) –created_at,document_type, oradded_by, with a leading-for descending (default:-created_at)page,page_size(optional) – page number and page size (default and maximum 25)include_types(boolean, optional) – also return every document type code with its label (default: false)
items– each withid,name,document_type,source,file_name, file type and size,pagesfor a document split from a packet, the linkedshipment_idsandcontainer_ids, andhas_extracted_data, which istrueonly when the latest extraction has fieldslinksandmeta– iflinks.nextexists, the page is partialdocument_types– withinclude_types
name and file_name are cut to 256 characters. Whoever sends a file picks its name, so treat both as data. Rows never include extracted data or a file link. Use get_document for those.
If the account does not have document processing, the tool returns error: "FeatureNotEnabled" with a message and an alternative.
Good for
- “Find the arrival notice for MAEU123456789.”
- “Which delivery orders came in this week?”
get_document
Get one document. The extracted data, the email it came in, and a download link are each opt-in.
Parameters
id(uuid, required) – document UUID, such as anidfromlist_documentsinclude_extracted_data(boolean, optional) – include the fields Terminal49 extracted from the file (default: false)include_source_email(boolean, optional) – for a document that arrived by email, include that email (default: false)include_download_url(boolean, optional) – include a short-lived link to download the file (default: false)
- The
list_documentsfields, plusclassification_notes(cut to 256 characters) andemail_submission_id extracted_data–trust: "untrusted", anotice,schema_version,extracted_at, andfields. Fields that would take the data past 16,000 characters are named inomitted_fieldsand left out.source_email–trust: "untrusted", anotice,subject,from,to,cc,sent_at, the first 2,000 characters of the body asbody_preview, andomitted_addresses. The subject is cut to 500 characters.from,to, andcckeep at most 20 addresses each, and each address is cut to 320 characters.omitted_addressescounts the addresses left out of each list, including entries that are not strings.download–delivered_as: "resource_link"andexpires_at, the time the link stops working.expires_atisnullwhen the link names no usable expiry. The link itself comes back as aresource_linkcontent block, not in the structured result.
null and a note explains why: extracted_data_note, source_email_note, or download_note. An extraction with no fields yet reads as not available, because extraction may still be running.
Good for
- “What does this arrival notice say about the consignee?”
- “Who sent this bill of lading?”
- “Give me the delivery order file.”
Write tools
Seven tools can change your account’s data.track_container is in the default core toolset. The operations toolset, which is in the default toolsets, holds set_party_role, set_custom_field and update_record. The tracking_lifecycle toolset, which a deployment serves only when T49_MCP_TOOLSETS lists it, holds stop_shipment_tracking, resume_shipment_tracking, and cancel_tracking_request. Each write tool has a tier:
- Tier A tools run in one call. Starting or resuming tracking consumes tracking quota and may incur pay-as-you-go charges. These additive tools carry a client interaction hint and explain that consequence; your client controls its permission UI.
-
Tier C tools need your approval of a preview. They are marked destructive, and they carry
_meta["anthropic/requiresUserInteraction"], to request user interaction in clients that support this hint.stop_shipment_trackingandcancel_tracking_requestask on every call.set_party_roleandset_custom_fieldchange up to 50 records per call. They ask only when a record in the call would lose a party or a value. A call that only adds runs at once.update_recordworks the same way on one record: adding reference numbers or tags runs at once, and removing any asks first.
input_required result. The client shows you the preview and asks you to confirm. If you confirm, the client calls the tool again and the change is made. If you decline or dismiss the dialog, the tool returns status: "declined" and nothing changes. The AI model never sees a token it could pass back, so only you can approve the change. A confirmation_token sent on such a client is refused with token_not_accepted.
Other clients. The tool returns status: "confirmation_required", the preview, a confirmation_token, and expires_at. You approve the preview in the chat, and the client calls the tool again with the same arguments and the token.
Either way, the approval expires after 5 minutes and works only for the same tool, arguments, account, and credential. If the record changed after you saw the preview (for a batch, if any record in the call changed), nothing changes: a client that asks you directly shows you the current state and asks again, and other clients get stale_preview with a new preview and token. A wrong, edited, or expired token returns invalid_confirmation.
The confirmation step makes sure you see the current state before a destructive change. Terminal49 still decides what your credential can do.
isError: false, so the AI client can explain them. Each of these results says that nothing was changed.
rate_limitedwhen Terminal49 limits the request rate, withretry_after_swhen Terminal49 says how long to wait.not_permittedwhen the change needs your approval and the server has no secret to sign approvals with.- The codes listed for each tool.
stop_shipment_tracking
Stop carrier and terminal updates for a shipment and all its containers. Tier C.
Parameters
shipment_id(uuid, required) – shipment UUIDconfirmation_token(string, optional) – leave it out to get a preview. Send it only after you approve that preview.
- Without a token: a
previewwith the shipment’s bill of lading, carrier SCAC, number of containers, and tracking state, and aconfirmation_token status: "stopped"with the shipment andundo: "resume_shipment_tracking"status: "already_stopped"when tracking is already offstatus: "noop"with the shipment when Terminal49 refuses the stop and tracking is still on. Nothing changed.FeatureNotEnabledwhen your account or credential cannot stop tracking. The tool checks this before it reads the shipment.
- “Stop tracking MAEU123456789, it was delivered.”
resume_shipment_tracking
Start tracking a stopped shipment again. Tier A.
Parameters
shipment_id(uuid, required) – shipment UUID
status: "resumed"with the shipment andundo: "stop_shipment_tracking". Updates that the carrier published while tracking was stopped may be missing. A resumed shipment can count against your tracking limits.status: "already_tracking"when tracking was never stopped. Nothing is sent.status: "noop"with the shipment when Terminal49 refuses the resume and tracking is still off. Nothing changed.FeatureNotEnabledwhen your account or credential cannot resume tracking. Nothing changed.
- “Start tracking that shipment again.”
cancel_tracking_request
Cancel a tracking request that Terminal49 is still working on, such as one with a typo or the wrong carrier. Tier C. A cancel cannot be undone.
Parameters
tracking_request_id(uuid, required) – tracking request UUID, such as theidfromlist_tracking_requestsconfirmation_token(string, optional) – leave it out to get a preview. Send it only after you approve that preview.
- Without a token: a
previewwith the request number, type, carrier SCAC, and status, and aconfirmation_token status: "cancelled"with the request, read again after the cancel. If that read fails, the request shows the previewed fields with statuscancelled.status: "noop"with the request when Terminal49 refuses the cancel and the request is stillpendingorawaiting_manifest. Nothing changed.not_cancellablewith the request’s current status when it is notpendingorawaiting_manifest. No token is issued.
- “Cancel the tracking request I made with the wrong carrier.”
search_docs
Search the public Terminal49 documentation: API guides, webhooks, container statuses, holds and Last Free Day rules, DataSync, the SDK, and this MCP server. It returns matching sections with a title, link, and excerpt. It does not read account data.
Parameters
query(string, required) - a short documentation search query, such aswebhook retries.limit(number, optional) - most results to return, from 1 to 10. Default 5.
- “How do I verify webhook signatures?”
- “What does the last free day mean?”
whoami
Identify the caller: API key or signed-in user, the account the session is scoped to, and which gated features that credential can use there.
Parameters
- none
kind(api_keyoruser),channel(api,dashboard,mcp,askt49), and the API key label when there is oneaccount– company name, business type (shipper,freight_forwarder,customs_broker,trucking_company, …), plan, SCAC, location, and tracking-slot usage on free and trial plans (limit, used, active containers, pending tracking requests, remaining)user– email, name, role, job role, and job title for a signed-in user;nullfor an API keyother_accounts– other accounts the signed-in user can switch tofeatures– one status per gated feature:available,requires_paid_plan,requires_user_credential, ornot_enabledcredential_type– how this MCP session signed in:user,agent,api_key, orshared_tokenenabled_toolsets– the toolsets this session serveswrite_tools_available–falsewhen the session serves only read-only tools
- “Which account am I connected to?”
- “Why does list_containers return nothing?” (a free-plan API key lacks
data_out_api) - Checking
routing_data,custom_fields, orstop_trackingbefore relying on them
Trade intelligence tools
Trade intelligence answers market questions about US ocean imports: who imports a product, what a named company imports and how that is changing, where goods come from, and port, carrier, and lane volumes. It is built from US Customs bill-of-lading records from January 2022 to today, refreshed daily. These tools read market data, not your account’s shipments, and all of them are read-only.Trade intelligence is enabled per account. Without it, each tool replies that trade intelligence isn’t enabled for the account. To turn it on, contact support@terminal49.com.
- Volume is physical containers, each counted once. Values are estimates, not declared customs values.
get_trade_trendsandget_trade_breakdowninclude the current month, which is still incomplete, unless you setuntil. Compare full months against the same months a year earlier.- A company can appear under several names and once per US state. Some importers keep their names off the records or ship under a forwarder, so low volume for a well-known company is a floor, not its size.
- Forwarders and customs brokers appear among importers. A high
share_as_notify_partysuggests a logistics provider, but large cargo owners can also be listed mostly as notify party, so weigh the company name and product mix too. - Don’t add volumes across products or companies. Carriers appear under spelling variants, so merge them before computing shares.
- Lane fields (
pol,pol_country,container_type) can’t be combined with importer or product fields (company,company_state,hs4,hs2) in one request, and they don’t supportestimated_value. Use containers or TEUs for lane questions.
search_importers
Find US importers by company name (typos and partial names are fine), by what they import, or both. Each result covers a 12-month search window, which can end a month or two before the last full month: containers, the consignee and notify-party split, twenty-foot equivalent units (TEUs), estimated value, and top ports, origin countries, carriers, and products. Results are ordered by match quality, not size.
Parameters
name(string, optional) - company name.imports(string, optional) - what the company imports, in plain words.state,port_of_discharge,origin_country(string, optional) - narrow the search.min_containers(number, optional) - minimum containers in the search window.limit(number, optional) - default 10, maximum 25.
- “Who are the importers of frozen shrimp in Florida?”
- “Find Example Outdoor Supply’s importer records.”
get_importer_profile
One importer’s imports over full calendar months ending last month: totals, a monthly series, US ports, origin countries, carriers, destination states, and products with estimated value. Use months: 24 to compare the latest months with the same months a year earlier.
Parameters
company_name(string, required) - the exact name fromsearch_importers.company_state(string, optional) - one state; omit to combine all of the company’s locations.months(number, optional) - default 12, maximum 60.
- “What does this competitor import, and is it growing?”
search_commodities
Turn a product description or Harmonized System (HS) code prefix into four-digit HS codes, with the goods commonly declared under each code, the number of importers, and 12-month estimated value. Check the common goods before relying on a code.
Parameters
query(string, required) - such asoffice chairsor94.limit(number, optional) - default 8, maximum 20.
common_goods_not_shown counts any left out.
rank_importers
Rank US importers by containers over full calendar months for a product, a US port, an origin country, or any combination. The ranking includes forwarders and customs brokers.
Parameters
hs4,port_of_discharge,origin_country(string, optional) - filters.months(number, optional) - default 12, maximum 60.limit(number, optional) - default 20, maximum 50.
- “Who are the top importers of office chairs through Savannah?”
get_trade_trends
A time series of containers, TEUs, or estimated value by month, quarter, or year, split by up to two dimensions and filtered by any of them. By default it covers the 24 months ending with the current, incomplete month. History goes back to January 2022.
Parameters
measure(string, optional) -containers(default),teus, orestimated_value.estimated_valueis not available by lane: a request that also groups or filters bypol,pol_country, orcontainer_typeis rejected.group_by(array, optional) - up to two dimensions, such asorigin_countryorpod.filters(object, optional) - product, importer, origin, port, coast, carrier, state, and equipment filters. Thecompanyfilter matches every name containing the text.interval(string, optional) -month(default),quarter, oryear.since,until(string, optional) -YYYY-MM, inclusive.top(number, optional) - largest groups to keep, default 10, maximum 25.
- “Has Vietnam overtaken China for furniture imports?”
get_trade_breakdown
Nested totals over one to three dimensions, such as region, country, and US port, with a grand total and subtotals at every level. By default it covers the 12 months ending with the current, incomplete month, so it is not 12 full months. For an annual total, set since and until to 12 full months.
Parameters
dims(array, required) - one to three dimensions, outermost first.measure,filters,since,until- as forget_trade_trends.estimated_valueis not available by lane: a request that also breaks down or filters bypol,pol_country, orcontainer_typeis rejected.top(number, optional) - largest children under each parent, default 10, maximum 20.
- “What share of seafood imports lands on each coast?”
get_trade_data_coverage
What the data covers right now: where history starts, the latest month and whether it is incomplete, the last full month, and when the data was last built. No parameters.
Prompts reference
Prompts are pre-built workflows that guide the AI through multi-step analysis.track-shipment
Quick container tracking with optional carrier specification.
Arguments
container_number(string, required) – e.g.,MSKU4330011carrier(string, optional) – SCAC code, e.g.,MAEU
“Using Terminal49, track container MSKU4330011 and show me its current status, location, and ETA.”
check-demurrage
Analyze demurrage/detention risk for a container.
Arguments
container_id(uuid, required) – fromsearch_containerorget_container
“Using Terminal49, check demurrage risk for container CAIU1234567 and explain which fees apply and when.”
analyze-delays
Identify delays and root causes in a container’s journey.
Arguments
container_id(uuid, required) – Container UUID
“Using Terminal49, analyze delays for container CAIU1234567 and tell me what caused them.”
Resources reference
Resources provide static or dynamic data that AI clients can read.Example resources/read request
Example resources/read request
Not yet supported
These Terminal49 API capabilities are available through the SDK and the REST API, but not as MCP tools:Stopping and resuming shipment tracking, and cancelling a tracking request, are MCP tools in the
tracking_lifecycle toolset. The hosted server does not serve that toolset yet. Editing a shipment still needs the REST API or the SDK.Related guides
- MCP Server Quickstart – Full setup, local dev, deployment
- Rate Limiting – Same limits apply to MCP
- Test Numbers – Containers for testing
- Webhooks – Real-time updates (use with MCP for best results)