@cyanheads/usitc-tariffs-mcp-server
Search USITC tariff schedules, inspect heading trees, and look up published duty rates via MCP. STDIO or Streamable HTTP.
6 Tools • 0 Resources • 0 Prompts
Overview
The U.S. Harmonized Tariff Schedule from the USITC. Search commodity descriptions, inspect classification ancestry, read published base duty columns, and investigate partial Chapter 99 evidence. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above; no upstream account or API key is required.
Tools
Capability reference
usitc_search_hts tool
- Required
query: commodity text or a code prefix. limit defaults to 25, with at most 100 items per page; follow page.nextCursor with the same arguments.
- Returns
context: "flat_search_matches" and rows with ratesResolved: false. Use heading lookup for ancestry and duty lookup for resolved base columns; no matches is a successful empty result.
usitc_get_heading tool
- Required
code: four, six, or eight digits, bare or conventionally dotted. Optional returned nodeId selects an exact node; limit defaults to 25, at most 100 per page.
- Returns flat source-order
nodes with parentId, executable parentFollowUp, and resolvedRates states of published, inherited, or not_published. Unnumbered superior rows remain part of the hierarchy; published subchapter titles are preserved separately in sections. A missing classification returns found: false.
usitc_browse_chapter tool
- Required
chapter: 01–99, accepting one or two digits. limit defaults to 25, at most 100 entries per page.
- Returns complete
rateLineCount and statisticalLineCount per published subtree, counting a unique combined row's eight-digit relationship once. Published subchapter markers group following headings. Chapters 98 and 99 retain their actual numbered entries; grouping stays absent without a verified marker, and reserved chapters can return an empty collection.
usitc_lookup_duty_rate tool
- Required eight- or ten-digit
code; optional ISO alpha-2 countryCode. Identifiers retain leading zeros and accept conventional dots. An absent eight-digit row resolves only through its unique published ten-digit row, preserving the source number; ambiguous suffixes remain unresolved. Ten-digit requests require their exact published number.
- Returns General, Special, and Column 2 cells with rate-source provenance, or
found: false. Blank statistical cells inherit only from the matching eight-digit legal ancestor; blank never means zero or Free.
specialPrograms preserves unmatched text and parseCoverage; Special-program membership remains unknown, and eligibility and column selection remain not_determined. With countryCode, countryAssessment.column2Listing reports membership in the reviewed General Note 3(b) list for its pinned edition, or unknown when editions differ. Country input does not select a rate or establish preference qualification.
usitc_check_additional_duties tool
- Required exact eight- or ten-digit base commodity
code, excluding Chapter 99; optional countryCode. view is candidates (default), evidence (requires returned candidateCode), or unmapped. limit defaults to 25, at most 100 per page.
coverage.status is always partial; inspect supportedGrammar, limitations, and unresolved evidence. Candidate legalStatus is not_determined. Unknown and different-country evidence remains visible; an empty candidate list never establishes clearance.
- Explicit “following” clauses can link directly owned numbered code lists to their governing heading, with separate passage follow-ups. Descriptive items remain qualified context. Reviewed single-country “product(s) of China” or “product(s) of Canada” scopes can label country relations; exceptions, conditions and conflicting scopes remain unknown.
- Follow returned
nextCall and evidence followUp objects. For passages, supply noteId and optional noteCursor with the original code/country selectors, omitting view, candidateCode, and collection cursor; concatenate all notesPage.fragments across pages and inspect governing context.
usitc_list_reference tool
- Required
topic: identifiers, spi, chapters, units, or releases. limit defaults to 25, at most 100 entries per page.
- Program and unit decoders carry their verification edition and citations. SPI country membership remains unknown; release records describe published editions without enabling historical tariff lookup.
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
USITC-specific:
- Keyless access to the HTS application's undocumented interfaces, with paced requests and bounded retries.
- Published descriptions, rate strings, units, sparse fields, and footnotes preserved alongside legal ancestry.
- Edition-pinned Special Program Indicator decoding and partial Chapter 99 evidence research.
- No conclusive classification, preference qualification, duty stacking, combined-duty calculation, or current-enforceability determination. Confirm legal applicability against legal notes and current CBP/Federal Register guidance.
Agent-friendly output:
- Provenance — every result carries
release, source, and notices; edition checks prevent detected cross-edition mixing, but do not prove atomic publication or current law.
- Complete continuation — pages expose
total, returned, effectiveLimit, and nextCursor; a 24,000-byte complete-reply budget can reduce page size with budgetLimited: true. Cursors bind the original arguments and source snapshot, expire within 15 minutes, and can expire earlier after eviction or restart.
- Explicit uncertainty —
found, rate states, partial coverage, and typed error reasons support branching without guessing. Restart the original call without cursors after cursor_expired; fix selectors after invalid_cursor or invalid_selector.
- Matching response surfaces —
content[] and structuredContent carry the same selected data; published source text is framed as data rather than instructions.
Getting started
Connect to the hosted instance, no install:
Or run it locally:
For other clients, add this to the MCP config file:
No API key is required. For Docker or a source build, see Running the server.
Configuration
No server-specific environment variables are required. See .env.example for common framework overrides.
Running the server
Local development
Requires Bun v1.4.0 or higher (or Node.js v24+ for the published package) and network access to hts.usitc.gov.
No server-specific variables are required; adjust framework overrides as needed. For Streamable HTTP, run MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http after building; the endpoint is http://localhost:3010/mcp.
Docker
The image serves HTTP on port 3010 with stateless sessions and logs under /var/log/usitc-tariffs-mcp-server; configure authentication before exposing it publicly. For a stdio MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/usitc-tariffs-mcp-server:latest"]. To build locally, run docker build -t usitc-tariffs-mcp-server .; add --build-arg OTEL_ENABLED=false to omit optional telemetry peers.
Project structure
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catch in tool logic
- Use
ctx.log for logging and pass ctx.signal through to upstream work
- Register new tools in the
createApp() array in src/index.ts
- Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
- Keep partial-coverage and country-qualification limits intact when extending the Chapter 99 evidence grammar
Contributing
Issues are welcome. Run checks and tests before submitting:
License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.