Skip to main content
Glama
FastBound MCP

FastBound MCP

by xuanji86

FastBound MCP

An MCP server for the FastBound firearms Acquisition & Disposition (A&D) API — the electronic bound book used by US Federal Firearms Licensees (FFLs). It lets an MCP client (Claude Desktop, Claude Code, Codex CLI, Codex Desktop, etc.) search inventory, record acquisitions and dispositions, manage contacts, and pull reports through natural language.

⚠️ Compliance disclaimer. This tool writes to ATF-regulated records (27 CFR Part 478). You — the FFL/operator — are solely responsible for the accuracy and legality of every record. Test against a FastBound TEST account before touching production data. Writes are disabled by default (see Write safety).

Unofficial. A community-built integration — not affiliated with, endorsed by, or sponsored by FastBound. "FastBound" is a trademark of its respective owner, used here nominatively to describe API compatibility. See NOTICE.

Features

  • 53 tools covering account/reference, items, acquisitions, dispositions, contacts, reports, webhooks, and inventory — comprehensive coverage of the FastBound v1 Account API.

  • Multiple accounts in one server. Configure several bound books (a live store, a sibling store, a dev sandbox); switch the active one with use_account, or target another for a single call with account:"sandbox". Every result says which account it hit.

  • Guarded writes with dry-run preview. Writes are off unless you opt in; committing/destructive operations preview exactly what they will send and require an explicit confirm:true.

  • Rate-limit aware (60 req/min token bucket + 429 backoff) and surfaces FastBound's side-effect headers (multiple-sale reports, auto-acquisitions on FFL transfers, contact dedupe).

Related MCP server: Amazon Seller MCP Server

Install

The server runs over stdio and is configured entirely through environment variables.

Claude Code

claude mcp add fastbound \
  -e FASTBOUND_ACCOUNT_NUMBER=12345 \
  -e FASTBOUND_API_KEY=your-api-key \
  -e FASTBOUND_AUDIT_USER=you@ffl.com \
  -e FASTBOUND_ALLOW_WRITES=false \
  -- npx -y fastbound-mcp

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "fastbound": {
      "command": "npx",
      "args": ["-y", "fastbound-mcp"],
      "env": {
        "FASTBOUND_ACCOUNT_NUMBER": "12345",
        "FASTBOUND_API_KEY": "your-api-key",
        "FASTBOUND_AUDIT_USER": "you@ffl.com",
        "FASTBOUND_ALLOW_WRITES": "false"
      }
    }
  }
}

Codex (CLI & Desktop)

Codex loads MCP servers over stdio, and both the Codex CLI and the Codex desktop / IDE app read the same config file: ~/.codex/config.toml. This is a standard stdio MCP server, so a single entry works for both:

[mcp_servers.fastbound]
command = "node"
args = ["/absolute/path/to/fastbound-mcp/dist/index.js"]
env = { FASTBOUND_ACCOUNT_NUMBER = "12345", FASTBOUND_API_KEY = "your-api-key", FASTBOUND_AUDIT_USER = "you@ffl.com", FASTBOUND_ALLOW_WRITES = "false" }
  • Codex CLI — run npm run build first so dist/ exists, then start codex; the /mcp command lists the connected fastbound server and its tools.

  • Codex Desktop / IDE extension — shares the same ~/.codex/config.toml. Add the block above (or use the app's MCP settings panel), then restart the app to load it.

If you npm i -g . (or publish the package), replace the node + absolute-path form with command = "fastbound-mcp".

Compatibility is verified end-to-end: all tool schemas are standard JSON Schema (draft-07, additionalProperties: false, no $ref/anyOf); tool names stay within OpenAI's function-name limits; the server emits only clean newline-delimited JSON-RPC on stdout (no log pollution) and negotiates MCP protocol 2025-06-18. Codex's client accepts the tools without modification.

Configuration

Single account

Variable

Required

Default

Purpose

FASTBOUND_ACCOUNT_NUMBER

yes

—

Account number (the numeric id in your cloud.fastbound.com URL). Used as the Basic-auth username.

FASTBOUND_API_KEY

yes

—

API key from Settings → Account. Basic-auth password.

FASTBOUND_AUDIT_USER

recommended

—

Email recorded as X-AuditUser on writes and on the document downloads (download_*), which refuse without one (ATF audit trail). Must be an active account user. Per-call auditUser overrides it.

FASTBOUND_ALLOW_WRITES

no

false

Master write switch. When false, every write tool refuses and sends nothing.

FASTBOUND_BASE_URL

no

https://cloud.fastbound.com

API root override.

FASTBOUND_API_VERSION

no

—

Optional x-api-version header.

The account is exposed under the alias default.

Multiple accounts

List the aliases in FASTBOUND_ACCOUNTS, then configure each with the same variables prefixed by its alias (uppercased, dashes → underscores). Per-account values fall back to the un-prefixed ones, so shared settings can stay global.

FASTBOUND_ACCOUNTS="main,sibling,sandbox"
FASTBOUND_DEFAULT_ACCOUNT="main"          # optional; defaults to the first alias

FASTBOUND_MAIN_LABEL="My Shop (PROD)"     # optional human name
FASTBOUND_MAIN_ACCOUNT_NUMBER="12345"
FASTBOUND_MAIN_API_KEY="…"                # API keys are account-bound: one per account
FASTBOUND_MAIN_AUDIT_USER="you@ffl.com"   # must be a user ON that account
FASTBOUND_MAIN_ALLOW_WRITES="true"        # per-account write switch

FASTBOUND_SANDBOX_ACCOUNT_NUMBER="67890"
FASTBOUND_SANDBOX_API_KEY="…"
FASTBOUND_SANDBOX_ALLOW_WRITES="true"

Choosing an account, in order of precedence:

  1. Per call — every API tool takes an optional account (alias or account number): search_items({ serial: "ABC", account: "sandbox" }). Nothing is remembered.

  2. Active account — use_account({ account: "sandbox" }) moves it for the rest of the session; list_accounts shows every account, its write switch, and which is active.

  3. Default — FASTBOUND_DEFAULT_ACCOUNT, else the first alias listed.

An unknown alias is an error listing the configured accounts — it never silently falls back to the active one. Results carry the account they hit on the first line (OK [sandbox #67890] — …), and a write's DRY RUN preview names it explicitly.

Get a free TEST account at fastbound.com to build and validate integrations without affecting real records. Generate the API key in Settings → Account, and find the account number in your dashboard URL.

Write safety

This server treats writes as dangerous by default:

  1. Off by default, per account. With that account's write switch unset/false, every write tool returns BLOCKED and sends nothing — so a live bound book can stay read-only while a sandbox accepts writes in the same server.

  2. Audit user required. Writes (and the download_* reads) need a valid X-AuditUser email (from the account's default audit user or a per-call auditUser).

  3. Dry-run by default for the dangerous ones. Committing or destructive tools (acquire, dispose, commit_*, delete_item, undispose_item, merge_contacts, update_*, theft-loss/destroyed/NFA, …) return a DRY RUN preview showing the exact method, path, and request body. Re-call with confirm:true to execute. The preview is built from the same code that sends the live request, so it can't drift.

  4. Staging tools execute directly. Creating pending (uncommitted) records or adding items to them carries no ATF effect, so those run without a confirm step (but still require the write switch + audit user).

Tool results are tagged OK / DRY RUN / BLOCKED / ERROR on the first line, followed by the account the call hit (OK [main #12345] — …).

Tools

  • Accounts: list_accounts, use_account (server-local; they pick which bound book the other tools talk to)

  • Reference: get_account, list_smartlists, list_users

  • Items: search_items, get_item, update_item, set_item_external_id, set_item_acquisition_contact, delete_item, undispose_item

  • Acquisitions: search_acquisitions, get_acquisition, get_acquisition_item, acquire, create_pending_acquisition, add_acquisition_items, update_acquisition, update_acquisition_item, attach_acquisition_contact, commit_acquisition, delete_acquisition, delete_acquisition_item

  • Dispositions: search_dispositions, get_disposition, list_disposition_items, list_4473_dispositions, dispose, create_pending_disposition, add_disposition_items, update_disposition, edit_disposition_item_price, attach_disposition_contact, remove_disposition_items, commit_disposition, lock_disposition, dispose_theft_loss, dispose_destroyed, dispose_nfa, delete_disposition

  • Contacts: search_contacts, get_contact, create_contact, update_contact, manage_contact_licenses, merge_contacts

  • Reports: download_bound_book, download_4473, download_attachment, download_multiple_sale_report

  • Webhooks / Inventory: manage_webhooks, bulk_verify_inventory

Firearm classification fields (caliber, manufacturer, type, condition, location) are account-configurable — use list_smartlists to discover valid values before writing.

Development

npm install
npm run build        # tsc → dist/
npm test             # vitest (offline unit tests)
npm run typecheck

The unit tests are fully mocked and offline. An opt-in live smoke test (test/smoke.account.test.ts) runs a read-only get_account against every configured account when credentials are present, and skips otherwise — handy right after adding an account:

FASTBOUND_ENV_FILE=./.env npx vitest run test/smoke.account.test.ts

License

MIT

Available Tools

53 tools
acquireAcquire (create + commit)A

Record an acquisition and commit it to the A&D book in one step (the recommended intake path). A supplier contact is REQUIRED — provide contactId, contactExternalId, or an inline contact. This commits a regulated record: dry-run by default, pass confirm:true to execute. Write.

Inputs: type (required), items (required), date, note, +11 more.

add_acquisition_itemsAdd item to acquisitionB

Add a single firearm to a pending acquisition. Executes directly (the acquisition is still uncommitted). Write.

Inputs: type (required), model (required), serial (required), caliber (required), +18 more.

add_disposition_itemsAdd items to dispositionA

Add inventory items (by GUID, optionally with a price) to a pending disposition. Executes directly. Write.

Inputs: items (required), dispositionId (required), account, auditUser.

attach_acquisition_contactAttach acquisition contactA
Idempotent

Assign an existing supplier contact to a pending acquisition. Executes directly. Write.

Inputs: id (required), contactId (required), account, auditUser.

attach_disposition_contactAttach disposition contactB
Idempotent

Assign an existing recipient/buyer contact to a pending disposition. Executes directly. Write.

Inputs: id (required), contactId (required), account, auditUser.

bulk_verify_inventoryBulk verify inventoryB
Idempotent

Mark a list of serial numbers as physically verified during a cycle count, optionally updating their location. Write.

Inputs: serials (required), account, location, auditUser, +3 more.

commit_acquisitionCommit acquisitionA

Commit a pending acquisition to the A&D book — the point of no return. Dry-run by default; pass confirm:true to commit. Write.

Inputs: id (required), account, confirm, auditUser.

commit_dispositionCommit dispositionA

Commit a pending disposition to the A&D book — the point of no return. May trigger a Multiple Sale report and, for FFL transfers, auto-create the recipient's acquisition (reported via headers). Dry-run by default; pass confirm:true. Write.

Inputs: id (required), account, confirm, auditUser, +1 more.

create_contactCreate contactA

Create a contact (FFL, organization, or individual). Provide fflNumber for an FFL, organizationName for an org, or first/last name for an individual, plus premise address fields. Executes directly (a contact is not an A&D record). If FastBound matches an existing contact it is reported via headers. Write.

Inputs: fax, sotein, suffix, account, +22 more.

create_pending_acquisitionCreate pending acquisitionA

Create a PENDING (uncommitted) acquisition draft. A supplier contact is REQUIRED (contactId, contactExternalId, or inline contact). Has no ATF effect until committed, so it executes directly (no dry-run). Add items with add_acquisition_items, then finalise with commit_acquisition. Write.

Inputs: type (required), date, note, items, +10 more.

create_pending_dispositionCreate pending dispositionA

Create a PENDING (uncommitted) disposition draft. No ATF effect until committed, so it executes directly. Add items with add_disposition_items, then finalise with commit_disposition. Write.

Inputs: date (required), requestType (required), note, otsn, +13 more.

delete_acquisitionDelete pending acquisitionA
Destructive

Delete a PENDING (uncommitted) acquisition draft. Dry-run by default; pass confirm:true. Destructive write.

Inputs: id (required), account, confirm, auditUser.

delete_acquisition_itemDelete acquisition itemA
Destructive

Remove an item from a pending acquisition. Dry-run by default; pass confirm:true. Destructive write.

Inputs: id (required), acquisitionItemId (required), account, confirm, +1 more.

delete_dispositionDelete pending dispositionA
Destructive

Delete a PENDING (uncommitted) disposition draft. Dry-run by default; pass confirm:true. Destructive write.

Inputs: id (required), account, confirm, auditUser.

delete_itemDelete itemA
Destructive

Delete a firearm record. ATF permits deletion only for a Duplicate or Error; a deleteType and an explanatory deleteNote are required and form an immutable audit entry. Dry-run by default; pass confirm:true to delete. Destructive write.

Inputs: id (required), deleteNote (required), deleteType (required), account, +2 more.

disposeDispose (create + commit)A

Record a disposition and commit it in one step (sale, FFL transfer, etc.). Items reference firearms already in inventory by GUID. For FFL transfers set requestType=Regular and otherTransfereeEmails; FastBound may auto-create the receiving FFL's acquisition (reported back via headers). Commits a regulated record: dry-run by default, pass confirm:true. Write.

Inputs: date (required), items (required), requestType (required), note, +14 more.

dispose_destroyedDispose as destroyedA

Record and commit a destroyed disposition for inventory items. Provide destruction date, description, and witnesses. Dry-run by default; pass confirm:true. Write.

Inputs: date (required), items (required), note, account, +7 more.

dispose_nfaDispose (NFA)B

Record and commit an NFA disposition for inventory items. Dry-run by default; pass confirm:true. Write.

Inputs: date (required), type (required), items (required), note, +14 more.

dispose_theft_lossDispose as theft/lossA

Record and commit a theft/loss disposition for inventory items. Provide discovery date and incident numbers. Dry-run by default; pass confirm:true. Write.

Inputs: date (required), items (required), note, account, +7 more.

download_4473Download 4473B
Read-only

Download a completed ATF Form 4473 PDF by its id. Requires an auditUser email (recorded by FastBound). Returns the file base64-encoded. Read.

Inputs: form4473Id (required), account, auditUser.

download_attachmentDownload attachmentA
Read-only

Download an attachment file by its id. Requires an auditUser email (recorded by FastBound). Returns the file base64-encoded. Read.

Inputs: attachmentId (required), account, auditUser.

download_bound_bookDownload bound bookA
Read-only

Generate and download the A&D bound book export. Requires an auditUser email (recorded by FastBound). Returns the file base64-encoded (large exports are summarised instead). Read.

Inputs: account, auditUser.

download_multiple_sale_reportDownload multiple-sale reportA
Read-only

Download a Multiple Sale report attachment (ATF Form 3310.4/5300.9) by report id and attachment id. Requires an auditUser email (recorded by FastBound). Returns the file base64-encoded. Read.

Inputs: attachmentId (required), multipleSaleReportId (required), account, auditUser.

edit_disposition_item_priceEdit disposition item priceA
Idempotent

Set the sale price of an item on a pending disposition. Executes directly. Write.

Inputs: id (required), price (required), itemId (required), account, +1 more.

get_accountGet accountA
Read-only

Read FastBound account properties and settings (number, name, items in inventory, owner). Useful as a connectivity/credential check. Read-only.

Inputs: account.

get_acquisitionGet acquisitionA
Read-only

Retrieve a single acquisition by FastBound GUID id or your externalId. Read-only.

Inputs: id (required), idType, account.

get_acquisition_itemGet acquisition itemB
Read-only

Retrieve a single item within an acquisition. Read-only.

Inputs: id (required), acquisitionItemId (required), account.

get_contactGet contactA
Read-only

Retrieve a single contact by FastBound GUID id or your externalId. Read-only.

Inputs: id (required), idType, account.

get_dispositionGet dispositionA
Read-only

Retrieve a single disposition by FastBound GUID id or your externalId. Read-only.

Inputs: id (required), idType, account.

get_itemGet itemA
Read-only

Retrieve a single firearm record by FastBound GUID id or by your externalId. Read-only.

Inputs: id (required), idType, account.

list_4473_dispositionsList 4473 dispositionsB
Read-only

List dispositions that have an associated ATF Form 4473. Read-only.

Inputs: skip, take, account.

list_accountsList configured accountsA
Read-only

List every FastBound account this server is configured for, showing each alias, label, account number, whether writes are enabled for it, its default audit user, and which one is currently active. Pass an alias (or account number) as account on any tool to target it for that one call, or use use_account to change the active account. Server-local: makes no API call.

Inputs: no named parameters.

list_disposition_itemsList disposition itemsC
Read-only

List the items on a disposition. Read-only.

Inputs: id (required), account.

list_smartlistsList a SmartListA
Read-only

Return one of FastBound's reference lists (allowed values). Use this to discover valid caliber/manufacturer/type/condition/location/importer values before creating or editing records. Read-only.

Inputs: list (required), account.

list_usersList usersA
Read-only

List users on the account. Use this to find valid emails for the X-AuditUser header required on write operations and document downloads. Read-only.

Inputs: account.

lock_dispositionLock dispositionA
Idempotent

Lock a disposition to prevent further edits. Dry-run by default; pass confirm:true. Write.

Inputs: id (required), account, confirm, auditUser.

manage_contact_licensesManage contact licensesA

Add, update, or delete a license on a contact. For add provide type+number; for update/delete provide licenseId. Executes directly. Write.

Inputs: action (required), contactId (required), type, number, +5 more.

manage_webhooksManage webhooksA

List available webhook event types, get a webhook, or create/update/delete a webhook subscription. action=list_events|get reads; create|update|delete are writes (require FASTBOUND_ALLOW_WRITES). For create provide name, url, and events.

Inputs: action (required), url, name, events, +3 more.

merge_contactsMerge contactsA
Destructive

Merge two duplicate contacts: keep winningContactId, discard losingContactId (its references move to the winner). Dry-run by default; pass confirm:true. Destructive write.

Inputs: losingContactId (required), winningContactId (required), account, confirm, +1 more.

remove_disposition_itemsRemove item from dispositionA
Destructive

Remove an item from a pending disposition by its inventory item GUID. Dry-run by default; pass confirm:true. Write.

Inputs: itemId (required), dispositionId (required), account, confirm, +1 more.

search_acquisitionsSearch acquisitionsC
Read-only

Search acquisition records (intake events). Filter by type, PO/invoice/tracking number, supplier contact, item, and manufacturing flag. Read-only.

Inputs: id, skip, take, type, +10 more.

search_contactsSearch contactsB
Read-only

Search contacts (individuals, organizations, and FFLs). Filter by name, FFL number, trade name, or organization name. Read-only.

Inputs: skip, take, suffix, account, +7 more.

search_dispositionsSearch dispositionsB
Read-only

Search disposition records (firearms leaving inventory: sales, FFL transfers, theft/loss, destroyed). Filter by type, TTSN/OTSN, PO/invoice/tracking, recipient contact, item. Read-only.

Inputs: id, OTSN, TTSN, skip, +13 more.

search_itemsSearch itemsB
Read-only

Search the firearm inventory (the A&D book). Filter by serial, manufacturer, model, type, caliber, status, date ranges, TTSN/OTSN, and more. Returns a page of items plus the total record count. Read-only.

Inputs: mpn, sku, upc, otsn, +25 more.

set_item_acquisition_contactSet item acquisition contactB
Idempotent

Set the acquisition (source) contact on an item. Executes directly. Write.

Inputs: id (required), contactId (required), account, auditUser.

set_item_external_idSet item externalIdA
Idempotent

Set or change only the externalId of an item (links it to your system's record) without touching other fields. Write.

Inputs: id (required), externalId (required), account, auditUser.

undispose_itemUndispose itemA
Destructive

Reverse a disposition: return a disposed firearm to available inventory. Dry-run by default; pass confirm:true to apply. Write.

Inputs: id (required), note, account, confirm, +1 more.

update_acquisitionUpdate acquisitionA
Idempotent

Edit header fields (type, date, note, PO/invoice/tracking, externalId) on a PENDING acquisition. Read-merge-write so unspecified fields keep their current values. Dry-run by default; pass confirm:true. Write.

Inputs: id (required), date, note, type, +7 more.

update_acquisition_itemUpdate acquisition itemA
Idempotent

Replace an item within a pending acquisition with corrected firearm data. Supply the full item (PUT replaces it). Dry-run by default; pass confirm:true. Write.

Inputs: id (required), type (required), model (required), serial (required), +20 more.

update_contactUpdate contactA
Idempotent

Edit fields on an existing contact. Read-merge-write so unspecified fields keep their current values (including the required status). Dry-run by default (preview shows the merged body); pass confirm:true. Write.

Inputs: id (required), fax, sotein, status, +27 more.

update_dispositionUpdate dispositionA
Idempotent

Edit header fields on a PENDING disposition (type, date, note, TTSN/OTSN, PO/invoice/tracking, theft-loss/destroyed details). Read-merge-write so unspecified fields keep their values. Dry-run by default; pass confirm:true. Write.

Inputs: id (required), date, note, otsn, +19 more.

update_itemUpdate itemA
Idempotent

Edit fields on an existing firearm record. Performs a read-merge-write so unspecified fields keep their current values. Dry-run by default (preview shows the merged body); pass confirm:true to apply. Write.

Inputs: id (required), mpn, sku, upc, +25 more.

use_accountSwitch active accountA

Set which FastBound account subsequent tool calls use by default, by alias or account number. Affects only this server session and only calls that do not pass their own account argument. Server-local: makes no API call and changes nothing in FastBound — follow with get_account to confirm the credentials reach the expected bound book.

Inputs: account (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.3/5.0

    Scored across 53 tools

    Disambiguation3/5

    Most tools map to distinct resource+action pairs with clear read/write and pending-vs-committed distinctions spelled out in descriptions. However, the dispose family overlaps heavily (dispose vs. dispose_theft_loss/dispose_destroyed/dispose_nfa), as do acquire vs. create_pending_acquisition, and set_item_acquisition_contact vs. attach_acquisition_contact require careful reading to separate. Descriptions resolve most of this, but an agent still has meaningful misselection risk.

    Naming Consistency3/5

    The dominant pattern is snake_case verb_noun (get_item, update_item, create_pending_disposition, search_contacts), which is good. But there are bare-verb outliers (acquire, dispose), multi-action manage_* tools (manage_webhooks, manage_contact_licenses), an 'edit' that breaks the 'update' convention (edit_disposition_item_price), and inconsistent plurality (add_acquisition_items adds a single firearm while delete_acquisition_item removes one). Readable but not fully predictable.

    Tool Count2/5

    53 tools is heavy for any single server and pushes well past a comfortable surface, especially since several are thin convenience variants of broader tools (dispose_theft_loss, dispose_destroyed, dispose_nfa, set_item_external_id, bulk_verify_inventory). The domain is genuinely complex, but the set could be consolidated substantially (e.g., one dispose tool with a type parameter) without losing capability.

    Completeness4/5

    Coverage is strong: full lifecycle for acquisitions, dispositions, items, and contacts, plus 4473 handling, document/attachment downloads, bound book export, webhooks, multi-account switching, and a bulk verification helper. Minor gaps remain — no way to list multiple-sale reports (only download by id), no contact deletion, and no disposition unlock — but these are narrow and workaroundable.

    Maintenance

    ActivityMaintained
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      C
      maintenance
      Enables interaction with Appfolio Property Manager through the Reporting API, allowing property management tasks and data retrieval via natural language commands.
      47
      11 npm
      10
      ISC
    • A
      license
      Not graded
      quality
      Not graded
      maintenance
      Exposes Amazon Selling Partner API tools for sellers to manage orders, inventory, listings, pricing, analytics, and reports via natural language.
      14 npm
      1
      AGPL 3.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables interaction with the SkuVault inventory management system API for product management, inventory control, warehouse operations, and analytics through natural language.
      1
      MIT