In-depth architectural comparison of the Chelaile MCP and Freightutils MCP MCP servers. Compare execution transports, security boundaries, tool capabilities, quality scores, and ready-to-paste client installation snippets for Claude, Cursor, Windsurf, and VS Code.
At a Glance & Executive Verdict
Chelaile MCP
Travel & Transportation · Local stdio
Quality: 63/100 (Good) | Auth: No auth required
Freightutils MCP
Travel & Transportation · Local stdio
Quality: 65/100 (Great) | Auth: No auth required
Verdict Summary: Choose Chelaile MCP if you need specialized Travel & Transportation tools running via a local process. Choose Freightutils MCP if your workspace requires Travel & Transportation integration with local subprocess execution. Both servers can be configured concurrently in your client's mcpServers manifest.
Which MCP Server Should You Choose?
Choose Chelaile MCP when:
You need dedicated capabilities in the Travel & Transportation domain.
You prefer local stdio subprocess transport architecture.
Your security boundary fits: No auth required (Free / Open Source).
A Chelaile-powered MCP server that lets LLMs query realtime bus and metro data in China — arrival times, vehicle positions, timetables, nearby stops, and transit routing. No login required.
17 freight calculation and reference tools — ADR dangerous goods, HS codes, LDM/CBM/chargeable weight calculators, duty estimation, airline codes, UN/LOCODE, and more. Free REST APIs + MCP server.
Category & Scope
Tools & Capabilities Breakdown
Chelaile MCP Tools (15)
bus_list_cities
List cities supported by the realtime bus data service.
Args:
- hot_only (boolean, default true): return only the upstream's curated 'hot' set (~20 cities). Set false to dump the full ~480-city list (token-heavy, use sparingly).
- response_format ('markdown' | 'json'): defaults to 'markdown'
Returns (json):
{
"cities": [
{ "cityId": "034", "cityName": "上海", "pinyin": "ShangHai", "supportSubway": true, "hot": true },
...
]
}
Use when: the user mentions a city name and you don't have its ID. The hot set covers the top-tier cities the user almost certainly means.
bus_get_city_config
Get a city's runtime config: max poll interval and "arriving" time threshold.
This is mostly relevant if you are deciding how aggressively to refresh — not for end-user questions about lines or stops.
Args:
- city_id (string, required): e.g. '034' (Shanghai), '027' (Beijing)
- response_format ('markdown' | 'json')
Returns (json):
{
"maxInterval": 30,
"arrivingStationLimitSeconds": 180,
"busDisplayConfig": { "lineDetail": "time#order#distance", "other": "time#order" }
}
bus_reverse_geocode
Convert WGS-84 lat/lng to a Chinese postal address (province, city, district, township, formatted address).
Useful when you have raw GPS coordinates and need a human-readable place name, or the citycode/adcode to pass to other tools.
Args:
- lat (string, required): WGS-84 latitude, decimal, e.g. '31.230416'
- lng (string, required): WGS-84 longitude, decimal, e.g. '121.473701'
- response_format ('markdown' | 'json')
Returns (json):
{
"formatted": "上海市黄浦区...",
"province": "上海市",
"city": "上海市",
"district": "黄浦区",
"township": "南京东路街道",
"citycode": "021",
"adcode": "310101"
}
For municipalities (Shanghai/Beijing/Tianjin/Chongqing) the upstream emits an empty 'city' value; this tool back-fills it with 'province' so the field is always a usable string.
Ready-to-Paste Client Configurations
Paste either (or both) of these JSON server blocks into your client config file (e.g. claude_desktop_config.json or ~/.cursor/mcp.json).
Chelaile MCP is categorized under Travel & Transportation and uses a local stdio subprocess. In contrast, Freightutils MCP belongs to Travel & Transportation using local stdio subprocess. Select Chelaile MCP when you need capabilities focused on travel & transportation and Freightutils MCP when you require tools for travel & transportation.
Resolve the caller's approximate location from their public IP — useful when the user asks something like "what's near me" without providing coordinates.
**Precision is city-level (typically a few kilometres).** Good enough to identify the city and seed bus_get_nearby_stops with a starting guess. NOT precise enough to find the user's actual bus stop — for that, ask for a landmark/address and resolve it via bus_search.
Caveats:
- Resolves via ip-api.com (free tier; rate-limited but no auth).
- VPN / corporate proxy → result reflects the proxy exit IP, not the user.
- Cellular IPs often land on a provincial centroid.
Args:
- ip (string, optional): a specific IPv4/IPv6 to look up. Omit to use the mcp server process's own outbound IP (= the caller's machine when running locally).
- response_format ('markdown' | 'json')
Returns (json):
{
"lat": 31.2222,
"lng": 121.4581,
"gpsType": "wgs",
"city": "上海",
"region": "上海市",
"country": "中国",
"ip": "116.236.0.1",
"isp": "China Telecom",
"precision": "city-level (~10 km); not suitable for stop-level queries"
}
**Suggested workflow**:
1. Call this tool to identify the user's city (match 'city' field against bus_list_cities to get a cityId).
2. Pass lat/lng into bus_get_nearby_stops for a rough nearby list, OR ask the user to confirm a landmark and use bus_search.pois for sharper coordinates.
bus_search
Search inside a city by keyword. Returns matching lines, stations, and POIs in one call.
Use this as the primary entry point when the user gives a line number, station name, or destination name without IDs.
**Keyword tip**: plain "71", "71路", "地铁2号线", "陆家嘴" all work — the upstream is reasonably forgiving. If a short numeric returns empty, try appending "路".
**Coordinate systems**:
- 'pois' coords are GCJ-02 (use directly with bus_plan_transit)
- 'stations' coords are WGS-84 (use with bus_get_nearby_stops / bus_get_line_realtime)
Both are also marked with a 'gpsType' field.
Args:
- city_id (string, required): e.g. '034'
- keyword (string, required)
- response_format ('markdown' | 'json')
Returns (json):
{
"highlightKey": "71路",
"lines": [
{
"name": "71", "lineNo": "r95817", "isSubway": false,
"directions": [
{ "direction": 0, "lineId": "21283603183", "startSn": "延安东路外滩", "endSn": "申昆路枢纽站" },
{ "direction": 1, "lineId": "21283603182", "startSn": "申昆路枢纽站", "endSn": "延安东路外滩" }
],
// Compat top-level fields mirror directions[0] (or first available).
"lineId": "21283603183", "direction": 0, "startSn": "延安东路外滩", "endSn": "申昆路枢纽站"
},
...
],
"stations": [{ "sId":"...", "sn":"西藏中路", "lat":31.231006, "lng":121.474316, "gpsType":"wgs", "physicalStId":"...", "namesakeStId":"...", "isSubway":false }, ...],
"pois": [{ "name":"71路", "address":"...", "tag":"公交线路", "district":"黄浦区", "lat":31.233021, "lng":121.49073, "gpsType":"gcj" }, ...]
}
**Line folding**: each entry in 'lines' is one logical line (e.g. "71路"). The two travel directions live in 'directions[]'. Pick the lineId matching your desired direction.
**Subway hint**: when 'isSubway' is true, the entry carries a 'hint' field — bus_get_line_detail will return empty for these lineIds. Use bus_get_stop_detail (metros field) or bus_plan_transit instead.
**Follow-ups**:
- directions[i].lineId → bus_get_line_detail (full stop list, first/last/price) — non-subway only
- stations[*].physicalStId + namesakeStId → bus_get_stop_detail
(NOTE: a few stations lack physicalStId — typically metro-only entries with subwayV2=1. For those, use bus_get_nearby_stops to resolve the bus platform IDs nearby.)
- pois[*].lat/lng (GCJ) → bus_plan_transit as origin/destination
bus_search_more
Paginated 'see more' for one category from bus_search.
Args:
- city_id (string, required)
- keyword (string, required): same keyword used in bus_search
- type ('1'|'2'|'3'): 1=more lines, 2=more stations, 3=more POIs (default '1')
- response_format ('markdown' | 'json')
Returns: same shape as bus_search but only the requested category is populated.
bus_get_nearby_stops
List bus stops near a WGS-84 GPS coordinate, each annotated with the lines that pass through and the realtime buses approaching.
**If you don't have coordinates**: call bus_get_my_location first (city-level precision via IP), or ask the user for a landmark and resolve it via bus_search.pois — the resulting lat/lng goes into this tool's lat/lng args.
Args:
- city_id (string, required): e.g. '034'
- lat / lng (string, required): WGS-84 decimal coordinates
- limit (number, default 5): how many of the closest stops to return (max 20)
- response_format ('markdown' | 'json')
Returns (json):
{
"stops": [
{
"sId": "021-15232", "sn": "西藏中路", "distance": 87, "isSubway": false,
"physicalStId": "...", "namesakeStId": "...", "firstLineId": "...",
"lines": [
{
"lineId": "...", "name": "71", "direction": 0, "endSn": "...",
"status": "等待发车" | "不在运营时间" | "" (running),
"preArrivalTime": "10:10" | undefined,
"targetOrder": 2, "targetStationId": "021-15232",
"buses": [
{ "busId": "...", "order": 2, "arrivalTime": 1779070466055, "travelTime": 25, "distanceToDest": 90, "capacity": 0 }
]
}
],
"subwayLines": [ { "name": "地铁2号线", "shortName": "2号线", "color": "140,194,32", "directions": [{ "destName": "...", "firstTime": "05:31", "lastTime": "23:24" }] } ]
}
]
}
Field notes:
- buses[].arrivalTime is a ms timestamp; -1 = unknown
- buses[].travelTime is seconds remaining; -1 = unknown
- buses[].capacity: 0=light, 1=moderate, 2=crowded
- If no realtime buses but the line is starting soon, 'preArrivalTime' will hold the next predicted dispatch ("10:12")
bus_get_stop_detail
Full detail for a stop: precise WGS-84 coordinates, every line that passes through (with first/last/price), realtime buses, and nearby metro lines.
Args:
- city_id (string, required)
- physical_st_id (string, required): from bus_get_nearby_stops / bus_search
- namesake_st_id (string, optional): recommended; from the same source
- first_line_id (string, optional): a line to highlight
- lat / lng (string, optional): caller's WGS-84 location, used to populate 'distance'
- response_format ('markdown' | 'json')
Returns (json):
{
"stations": [
{ "sId": "...", "sn": "...", "lat": ..., "lng": ..., "distance": ...,
"lines": [{ "lineId": "...", "name": "71", "direction": 0, "startSn": "...", "endSn": "...", "firstTime": "05:30", "lastTime": "23:30", "price": "2元", "targetOrder": 2, "buses": [...] }],
"metros": [{ "name": "地铁14号线", "lineNo": "14号线", "color": "97,96,32" }] }
]
}
Multiple entries in stations[] mean the stop name maps to several physical platforms.
bus_get_line_detail
Full info for a line: rider-facing fields (name, first/last/price, stationsNum), the full ordered station list, the reverse-direction lineId, and every bus currently on the line.
**Use this — not bus_get_timetable — to answer "is line X still running" or "first/last bus time" questions.** The timetable tool only has data for a small minority of lines.
**Subway lines are NOT supported.** If bus_search returned a line with isSubway=true (e.g. lineId=1057 for 地铁2号线), this endpoint returns an empty payload — the response will carry `empty: true` and a `hint` field pointing at bus_get_nearby_stops / bus_plan_transit. Don't retry; route to those tools instead.
Args:
- city_id (string, required)
- line_id (string, required): from bus_search.lines[*].lineId
- lat / lng (string, optional): caller's WGS-84 coordinates
- response_format ('markdown' | 'json')
Returns (json):
{
"line": { "lineId":"...", "name":"71", "lineNo":"r95817", "direction":0, "startSn":"...", "endSn":"...", "firstTime":"05:30", "lastTime":"23:30", "price":"2元", "stationsNum":24 },
"stations": [{ "order":1, "sId":"...", "sn":"...", "wgsLat":..., "wgsLng":..., "physicalStId":"...", "namesakeStId":"...", "metros":[{"name":"地铁14号线","lineNo":"14号线","color":"97,96,32"}] }, ...],
"buses": [{ "busId":"...", "order":2, "lat":..., "lng":..., "speed":5.7, "capacity":0, "distanceToWaitStn":...}],
"reverseDirection": { "lineId":"...", "startSn":"...", "endSn":"...", "firstTime":"04:30", "lastTime":"22:30", "price":"2元" } | null,
"depDesc": "...", "preArrivalTime": "...", "targetOrder": 24,
"empty": true, "hint": "..." // present only when upstream returned no data (subway / retired line)
}
Each station carries:
- 'order' → feed into bus_get_line_realtime / bus_list_line_buses as target_order
- 'sId' → feed into bus_get_line_realtime as station_id (NOT into bus_get_stop_detail!)
- 'physicalStId' + 'namesakeStId' → feed into bus_get_stop_detail to see every line through that stop
bus_get_line_route
Polyline coordinates for drawing a line on a map. Points with 'stopOrder' are actual stops; others are shape points between stops.
Args:
- city_id (string, required)
- line_id (string, required)
- include_shape (boolean, default false): false returns only stop markers (~25 points); true returns all shape points (~400-500). Skip unless you actually need to draw the line.
- response_format ('markdown' | 'json')
Returns (json):
{
"pointCount": 480, // total shape points upstream returned
"stopCount": 23, // stop markers among them
"points": [{ "lat":..., "lng":..., "stopOrder":1 }, ...]
}
'points' is the filtered list — stops only by default, full polyline when include_shape=true.
**Known caveat**: upstream sometimes omits the terminus stop from the polyline, so 'stopCount' may be one less than bus_get_line_detail's 'stationsNum' (e.g. 23 vs 24). Trust bus_get_line_detail for the authoritative station list; line_route is just for drawing.
Markdown mode only summarises counts; request JSON to read coordinates.
bus_get_line_realtime
Canonical "when will my bus arrive" tool. Returns every bus currently on the line, with the nearest one carrying an ETA to the waiting stop.
**Important**: the upstream predicts an ETA for only the nearest bus heading to your stop. Buses farther up the route are returned (with position/speed/capacity) but their `eta` field is null. That's not a bug.
**Args**:
- city_id (string, required)
- line_id (string, required): from bus_search
- target_order (string, required): the waiting stop's order on the line. Source: bus_get_line_detail.stations[i].order, or bus_get_nearby_stops.stops[].lines[].targetOrder.
- station_id (string, required): sId of the waiting stop
- lat / lng (string, required): WGS-84 — the user's location is best; if unavailable, use the waiting stop's wgsLat/wgsLng (from line_detail.stations[i]).
- response_format ('markdown' | 'json')
Returns (json):
{
"line": { "lineId":"...", "name":"71", "direction":0, "endSn":"..." },
"targetOrder": 2,
"realData": true,
"buses": [
{ "busId":"...", "licence":"...", "order":2, "lat":..., "lng":..., "speed":5.7, "capacity":0, "distanceToWaitStn":90, "eta":{"travelTime":25,"arrivalTime":1779070466055,"displayTime":"10:14"} },
{ "busId":"...", "order":3, "lat":..., "lng":..., "speed":3, "capacity":0, "eta":null },
...
],
"note": "..."
}
Field notes:
- The 'line' sub-object intentionally omits startSn — upstream does not return it on this endpoint. Read it from bus_get_line_detail if needed.
- eta.travelTime is seconds remaining
- eta.arrivalTime is a ms timestamp
- eta.displayTime is a "HH:MM" hint from upstream
- capacity: 0=light, 1=moderate, 2=crowded
bus_list_line_buses
Returns the nearest bus heading to the anchor stop, with ETA and the bus's next stop name.
**This is narrower than the name suggests.** Despite the upstream endpoint being called "busList", in practice it returns at most 1-2 buses (the imminent ones). For the FULL roster of every bus currently on the line, call **bus_get_line_detail** — its 'buses' array lists all live vehicles with positions.
Use this tool when you want a quick "what's about to arrive" answer for a specific stop.
Args:
- city_id (string, required)
- line_id (string, required)
- target_order (string, required): the waiting stop's order on the line
- station_name (string, required): display name of that anchor stop
- response_format ('markdown' | 'json')
Returns (json):
{
"targetOrder": 2,
"buses": [
{ "busId":"...", "licence":"...", "order":2, "lat":..., "lng":..., "speed":8.2, "capacity":0, "nextStop":"西藏中路", "eta":{"travelTime":214,"arrivalTime":..., "displayTime":"10:14"} }
]
}
+3 more tools listed on main page
Freightutils MCP Tools (25)
cbm_calculator
Calculate cubic metres (CBM) for a shipment from per-piece dimensions. CBM is the standard volume unit in international shipping: 1 CBM = 1m x 1m x 1m = 1,000 litres, and ocean freight prices per "freight tonne" (1 CBM or 1,000 kg, whichever is greater).
Behavior: deterministic — identical inputs always return identical figures; total volume = pieces x per-piece CBM, with conversions to cubic feet, cubic inches and litres included. Missing or non-positive dimensions error with a validation message naming the parameter. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: cbm_per_piece, total_cbm, cubic_feet, litres, cubic_inches and pieces under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Related: chargeable_weight_calculator (air billing weight from the same dims), consignment_calculator (multi-line totals), unit_converter (single conversions), shipment_summary (full composite analysis).
chargeable_weight_calculator
Calculate air freight chargeable weight — the greater of actual gross weight and volumetric weight, which is what airlines bill. Volumetric weight (kg) = (L x W x H in cm) / divisor; the IATA-standard divisor is 6,000 (1 CBM = 166.67 kg), while express integrators (DHL, FedEx, UPS) typically use 5,000.
Behavior: deterministic; per-piece volumetric weight is rounded to 2 decimal places before totalling; basis reports which weight governs ("volumetric" = cargo is light for its size, "actual" = dense). Air mode only — sea W/M (1 CBM = 1,000 kg) is covered by consignment_calculator with mode=sea. Missing or non-positive inputs error with the failing parameter named. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: chargeable_weight_kg, basis, volumetric_weight_kg (total and per piece), gross_weight_kg, cbm, ratio, factor and pieces under result; normalized_input echoes the interpreted inputs and any defaults applied; plus confidence, _source and citation (the FreightUtils v1 response envelope).
Related: cbm_calculator (volume only), consignment_calculator (multi-line, all modes), uld_lookup (the equipment the freight flies in).
ldm_calculator
Calculate loading metres (LDM) for European road freight — how much trailer length a pallet load occupies. 1 LDM = 1 linear metre of a 2.4m-wide trailer; a standard artic is 13.6 LDM.
Provide a pallet preset OR custom length_mm + width_mm — omitting both errors with a usage hint. Behavior: deterministic; stackable=true with stack_height 2 or 3 divides the floor footprint accordingly; fits reports whether the load fits the chosen vehicle's LENGTH (give weight_kg to also see total_weight_kg against the vehicle's max payload); utilisation_percent is of the vehicle's length. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: ldm, vehicle (name, length_m, max_payload_kg), utilisation_percent, pallet_spaces (used/available), total_weight_kg, fits and warnings under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Related: vehicle_lookup (the trailer specs behind the vehicle presets), pallet_fitting_calculator (boxes onto one pallet), consignment_calculator (mixed lines including LDM).
adr_lookup
Look up European road dangerous-goods (ADR 2025) reference data for a substance: hazard class, classification code, packing group, labels, special provisions, limited/excepted quantities, transport category, tunnel restriction code and Kemler (hazard identification) number. Covers 2,939 entries across all 9 hazard classes, from UNECE ADR 2025 (ECE/TRANS/352).
Provide exactly ONE of: un_number (exact lookup — returns every packing-group variant of that UN number), search (case-insensitive partial match on the proper shipping name), or hazard_class (all entries in a class or division). un_number is normalised — "1203", "UN1203" and "un 1203" are equivalent, and normalized_input reports the correction; explosives keep their leading zero ("0004").
Behavior: read-only reference lookup; name searches return up to 50 entries, class filters up to 100. An unknown UN number or a search with no hits errors with the API's NOT_FOUND body and a retry hint. 28 Table A rows carry a scope remark instead of a packing group: those return packing_group null plus not_subject_to_adr (with conditions_ref, e.g. "5.5.3" for UN 1845 dry ice) or carriage_prohibited, and table_a_remark preserves the verbatim Table A text. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: count and results[] — per entry: un_number, proper_shipping_name, class, classification_code, packing_group, labels, special_provisions, limited_quantity, excepted_quantity, transport_category, tunnel_restriction_code, hazard_identification_number, variant_index/variant_count and, on scope-flagged rows, not_subject_to_adr/carriage_prohibited/conditions_ref/table_a_remark — under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Limitations: a factual compilation of the ADR table, not legal or compliance advice; classification remains the consignor's responsibility — verify against the current UNECE ADR text.
Related: adr_lq_eq_check (checks quantities against the LQ/EQ values returned here), adr_exemption_calculator (1.1.3.6 small-load points), consignment_calculator (flags dangerous-goods lines by UN number).
adr_exemption_calculator
Calculate ADR 1.1.3.6 "small load" exemption points for a dangerous-goods load. Each substance's transport category (0-4) sets a points multiplier (category 1 x50, 2 x3, 3 x1, 4 x0; the nine ADR 1.1.3.6.3 note-a entries UN 0081/0082/0084/0241/0331/0332/0482/1005/1017 are x20 with a 50 kg per-transport-unit cap); points = quantity x multiplier, and a load totalling 1,000 points or less qualifies for reduced ADR requirements. Transport category 0 substances can NEVER use this exemption — has_category_zero flags them.
Provide un_number + quantity for a single substance, or items[] for a mixed load (items takes precedence if both are given). Quantities are in kg or litres per the substance's ADR unit.
Multi-variant UNs: a UN number with more than one ADR Table A row (packing group / concentration variant — e.g. UN 1789 PG II vs PG III have different transport categories) needs packing_group (I|II|III) or variant_index (from adr_lookup) to pin one row. Without a disambiguator the tool returns blocking_errors[AMBIGUOUS_UN_VARIANT] + human_review_required + candidates[] (each candidate's variant_index, packing_group, proper_shipping_name, transport_category, multiplier) and NO verdict, rather than silently guessing a row. Single-row UNs are unchanged.
Scope verdicts: Table A rows listed "NOT SUBJECT TO ADR" or "CARRIAGE PROHIBITED" never enter the points math. An all-not-subject load (e.g. UN 1845 dry ice) returns not_subject_to_adr true with a dedicated message ("Not subject to ADR (road). Section 5.5.3 applies: ...") and, for dry ice, conditions[] quoting the ADR 2025 section 5.5.3 requirements verbatim (ventilation, package marking, warning mark, documentation, training). A load containing a CARRIAGE PROHIBITED entry returns exempt false with carriage_prohibited true. In a mixed load, not-subject items are excluded from the points and the exclusion is stated in warnings.
Behavior: deterministic points arithmetic over ADR 2025 reference data; a UN that cannot be found returns blocking_errors (NOT_FOUND); exempt is the overall verdict. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: items[] (each with packing_group, variant_index, transport_category, multiplier, points, quantity_unit/quantity_basis/expected_unit when a unit or basis was declared, and scope flags where applicable), total_points (NULL when no verdict was reached), threshold (1000), exempt (NULL when no verdict was reached — never false as a stand-in), has_category_zero, has_quantity_exceedance, warnings, message and — on scope verdicts — not_subject_to_adr/conditions_ref/conditions[]/carriage_prohibited under result — or, when a UN is ambiguous, human_review_required + candidates[] with blocking_errors, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Limitations: a deterministic calculation over reference data, not legal advice — even exempt loads keep core duties (packaging, marking, documentation), and mixed-packing rules still apply; verify against the current UNECE ADR text.
Related: adr_lookup (per-substance data incl. transport category + variant_index), adr_lq_eq_check (the LQ/EQ relief routes instead of 1.1.3.6).
adr_lq_eq_check
Check whether dangerous goods qualify for ADR Limited Quantity (LQ, ADR 3.4) or Excepted Quantity (EQ, ADR 3.5) relief. LQ compares each item's per-inner-packaging quantity against that substance's LQ maximum; EQ resolves the substance's E-code (E0-E5) and checks the per-inner limit, plus the per-outer limit when inner_packaging_qty is given.
Provide mode ("lq" or "eq") and 1-20 items, each with un_number, quantity and unit — ml or L for liquids, g or kg for solids; quantity is per INNER packaging, not the whole load.
Unit families: column (7a) states the limit in ONE dimension — a mass for some entries, a volume for others — and ADR supplies no density, so a mass quantity against a volume limit (or the reverse) CANNOT be compared. Those items return status 'inconclusive' with the dimension named, never a pass or a fail, and a batch holding any inconclusive item never reads overall_status 'qualifies'. Send the quantity in the unit given by lq_limit_unit to get a verdict. Multi-variant UNs: a UN number with more than one ADR Table A row (packing group / concentration variant — e.g. UN 1789 PG II LQ 1 L vs PG III LQ 5 L) needs packing_group (I|II|III) or variant_index (from adr_lookup) on that item to pin one row. Without a disambiguator the tool returns blocking_errors[AMBIGUOUS_UN_VARIANT] + human_review_required + candidates[] (each candidate's variant_index, packing_group, proper_shipping_name, limited_quantity, excepted_quantity) and NO verdict, rather than silently checking the wrong packing group. Single-row UNs are unchanged.
Behavior: deterministic reference check; each item gets a status and reason (an LQ value of "0" or code E0 means the relief is not permitted for that substance), with overall_status and summary counts across the batch. Table A rows listed "NOT SUBJECT TO ADR" (e.g. UN 1845 dry ice) get item status not_subject — outside ADR scope, neither a pass nor a fail — and an all-not-subject batch returns overall_status not_applicable; "CARRIAGE PROHIBITED" rows are not_permitted with the prohibition stated in reason. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: mode, overall_status (qualifies | does_not_qualify | partial | not_applicable | inconclusive), items[] (un_number, variant_index, substance, class, packing_group, lq_limit or eq_code, quantity_entered, status, reason, and scope flags where applicable), summary {total_items, qualifying, exceeding, not_permitted, not_subject?, inconclusive?} and the ADR chapter references under result — or, when a UN is ambiguous, human_review_required + candidates[] with blocking_errors, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Limitations: a quantity-threshold check only — LQ/EQ relief also requires packaging, marking and documentation conformity that this tool does not assess; not legal advice, verify against the current UNECE ADR text.
Related: adr_lookup (the per-substance LQ/EQ values + variant_index), adr_exemption_calculator (the 1.1.3.6 load-points route instead).
airline_lookup
Search 6,357 airlines by name, IATA code, ICAO code, AWB prefix, or country. AWB prefixes are the first 3 digits of an air waybill number and identify the issuing carrier (e.g. 176 = Emirates).
Provide ONE parameter: query is a ranked fuzzy search across names and codes; iata / icao / prefix / country are exact filters.
Behavior: read-only; fuzzy query hits report their match quality through the envelope's confidence (basis match_quality, score 0-1) with a FUZZY_BEST_MATCH advisory naming the matched field; a query with no hits returns count 0 with a NO_MATCH advisory rather than an error. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: count and results[] — per airline: airline_name, iata_code, icao_code, awb_prefix[], callsign, country, has_cargo, aliases and per-record verification fields — under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Limitations: this dataset's provenance is pending independent verification (the envelope's provenance_status says so) — confirm operationally critical codes with IATA/ICAO or the carrier.
Related: airport_lookup (searches AIRPORTS, not carriers), validate (checks an AWB number's check digit and names its airline from this dataset).
container_lookup
Get ISO shipping-container specifications, with optional load-fit maths. Covers 10 types: 20ft/40ft standard, 40ft and 45ft high-cube, 20ft/40ft reefer, 20ft/40ft open-top and 20ft/40ft flat-rack.
Provide type as a slug (e.g. "20ft-standard", "40ft-high-cube") for one container's record; omit it to list all 10. Add item dimensions (item_length_cm/width_cm/height_cm, optional item_weight_kg and item_quantity) to also compute how many such items fit.
Behavior: read-only reference data with per-record provenance (sources, audited_at, decision_rationale); an unknown type errors with the valid slug list. Fit calculations are geometric best-effort — they do not model load distribution, securing or mixed cargo. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: the container record — internal/external/door dimensions (cm), capacity_cbm, tare_weight_kg, max_gross_kg, max_payload_kg and euro/GMA pallet counts — under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Limitations: manufacturer-typical specs, provenance pending independent verification (the envelope's provenance_status says so) — actual equipment varies by lessor and line; confirm against the carrier's equipment guide.
Related: validate (checks a container NUMBER's ISO 6346 check digit — not specs), cbm_calculator / consignment_calculator (the cargo volume to fill it), uld_lookup (the air-freight equivalent).
hs_code_lookup
Search 6,940 WCO Harmonized System (HS 2022) commodity codes — the 6-digit international customs classification layer. The first 2 digits are the chapter, 4 the heading, 6 the subheading.
Provide ONE of: query (free-text description search, min 2 chars), code (2-6 digit lookup, returns the code plus its hierarchy), or section (Roman numeral I-XXI to browse a section).
Behavior: read-only; description search is keyword-based against official HS descriptions, so everyday product words can return zero rows — count 0 with an empty results[] is a valid answer (e.g. "laptop" and "computers" find nothing; "automatic data" matches the official phrasing "automatic data processing machines"); prefer the formal tariff wording. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: the query/code echo, count and results[] (hscode, description and hierarchy context) under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Limitations: the 6-digit international level only — national tariff lines (8-10 digits) and duty rates are set per country; classification here is indicative, not a binding ruling.
Related: uk_duty_calculator (duty/VAT for a code found here), ics2_check (EU ENS goods-description quality — a different check entirely).
incoterms_lookup
Look up the 11 Incoterms 2020 trade rules — who pays for transport, insurance and customs clearance, and where risk transfers from seller to buyer. 7 rules work for any transport mode (EXW, FCA, CPT, CIP, DAP, DPU, DDP); 4 are sea/inland-waterway only (FAS, FOB, CFR, CIF).
Provide code for one rule, category (any_mode | sea_only) for a filtered list, or neither to list all 11. Behavior: read-only reference; an unknown code errors with the valid code list. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: the rule record — name, category, summary, seller_responsibility, buyer_responsibility, risk_transfer, cost_transfer, insurance, export/import clearance, best_for and watch_out — under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Limitations: summarised guidance on ICC Incoterms 2020; the ICC publication is the binding text and specific contract wording prevails.
Related: uk_duty_calculator (accepts an incoterm when composing the CIF value), shipment_summary (composite analysis).
pallet_fitting_calculator
Calculate how many identical boxes fit on a pallet: boxes per layer (trying 90-degree rotation when allowed), layer count within the max height, totals, volume utilisation and weight capping.
Behavior: deterministic geometric packing of one box size in aligned rows and columns — it does not model interlocked or mixed-orientation patterns; weight_limited reports when max_payload_kg caps the count below the geometric fit; pallet_deck_height_cm defaults to 15. Missing or non-positive dimensions error naming the parameter. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: boxes_per_layer, layers, total_boxes, orientation, boxes_per_row/col, usable_height_cm, utilisation_percent, total_box_volume_cbm, wasted_space_cbm and the weight fields under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Limitations: a theoretical best-effort fit — real stacking obeys carton strength, overhang and load-stability rules it does not model.
Related: ldm_calculator (pallets into trailer length), vehicle_lookup (pallet capacity per vehicle), container_lookup (pallets into containers).
unit_converter
Convert freight and logistics units: weight (kg, lbs, oz, tonnes, short_tons, long_tons), volume (cbm, cuft, cuin, litres, gal_us, gal_uk), length (cm, inches, m, feet, mm), plus two freight-specific targets valid only FROM cbm — chargeable_kg (air volumetric weight at the IATA 6,000 divisor, 1 CBM = 166.67 kg) and freight_tonnes (sea W/M, 1 CBM = 1 freight tonne).
Behavior: deterministic; the response names both units and states the formula used. Cross-dimension conversions (e.g. kg to litres) and freight targets from a non-cbm source error with the accepted-unit list. Note: short ton (US) = 2,000 lb, long ton (UK) = 2,240 lb, metric tonne = 2,204.6 lb. Rate-limited (anonymous use: 25 requests/day per IP): a 429 error body carries retry_after_seconds and a Retry-After header — back off and retry, or call get_subscribe_link for higher limits.
Returns: input {value, unit, name}, result {value, unit, name}, formula and note under result, plus confidence, _source and citation (the FreightUtils v1 response envelope).
Related: cbm_calculator (dimensions to volume first), chargeable_weight_calculator (proper air billing weight with pieces and a custom divisor).