npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@expander/mcp-server

v0.34.0

Published

Model Context Protocol server for the Exmachine External API

Downloads

950

Readme

@expander/mcp-server

Model Context Protocol (MCP) server for the Exmachine External API. Lets you query Exmachine data from Claude Desktop using your API key.

Prerequisites

  1. An Exmachine account with access to the data you need.
  2. An API key from Settings → API Keys in the Exmachine dashboard.
  3. Node.js 20+ (for npx).

Claude custom connector (remote)

For claude.ai / Cowork, use the hosted MCP server with OAuth.

Add connector in Claude

  1. Customize → Connectors → Add custom connector
  2. URL:
https://mcp.exmachine.io/mcp
  1. In a chat: + → Connectors → enable Exmachine
  2. Click Connect → paste your Exmachine API key (exp_live_… from Settings → API Keys)
  3. Ask Claude: “List my marketplaces and brands.”

OAuth endpoints (auto-discovered by Claude):

| Endpoint | Purpose | |----------|---------| | /.well-known/oauth-authorization-server | OAuth metadata | | /authorize | Connect flow — API key consent page | | /token | Token exchange | | /register | Dynamic client registration (Claude) |

Claude Desktop setup

  1. Open Claude Desktop → Settings → Developer → Edit Config.
  2. Add the expander server under mcpServers:
{
  "mcpServers": {
    "expander": {
      "command": "npx",
      "args": ["-y", "@expander/mcp-server@latest"],
      "env": {
        "EXMACHINE_API_KEY": "exp_live_your_key_here",
        "EXMACHINE_API_BASE": "https://api.exmachine.io/api/v1"
      }
    }
  }
}

Config file paths

| OS | Path | |----|------| | macOS | ~/Library/Application Support/Claude/claude_desktop_config.json | | Windows | %APPDATA%\Claude\claude_desktop_config.json |

For local development against your gateway:

"EXMACHINE_API_BASE": "http://localhost:8080/api/v1"

Quit and reopen Claude Desktop after saving.

Environment variables

| Variable | Required | Default | Description | |----------|----------|---------|-------------| | EXMACHINE_API_KEY | stdio mode | — | Your exp_live_… API key | | EXMACHINE_API_BASE | No | https://api.exmachine.io/api/v1 | API root (includes /api/v1) | | MCP_TOKEN_SECRET | HTTP mode | — | Seals OAuth tokens and client ids. See below | | MCP_PUBLIC_URL | No | https://mcp.exmachine.io | Public origin used for OAuth metadata |

MCP_TOKEN_SECRET

HTTP mode issues OAuth tokens and client_ids that carry their own state, sealed with AES-256-GCM under this secret — nothing about a connected client is stored in memory, so deploys and cold starts are invisible to users.

Two consequences:

  • It must be stable. Changing it invalidates every issued token and client registration; every user has to reconnect the connector.
  • It must be secret. It decrypts users' Exmachine API keys. In production it comes from Secret Manager (mcp-token-secret), not from a plain env var.

Generate with openssl rand -hex 32. The server refuses to start without it — falling back to a random per-boot value would silently log everyone out on every restart, which is the bug this design removes.

Tools

Feedback

| Tool | Calls | |------|--------| | create_ticket | POST /external/tickets | | list_my_tickets | GET /external/tickets |

These two are the reason the server's instructions tell Claude to offer to file a bug or feature request unprompted. The ticket is filed as the owner of the API key, lands on the team's Monday board, and shows up under Settings → My tickets in the dashboard.

Unlike every other tool here, these two paths are served by the gateway rather than proxied to the external-api microservice — creating a ticket means talking to Monday, and that client lives in the gateway. Key validation is still external-api's (POST /api-keys/resolve), so there is one definition of a valid key.

Project requests (personal tool projects)

| Tool | Calls | |------|--------| | request_project | POST /external/project-requests | | my_project_requests | GET /external/project-requests | | resend_setup_zip | POST /external/project-requests/:prq/zip | | set_project_request_github_username | POST /external/project-requests/:prq/github | | approve_project_request | POST /external/project-requests/:prq/approve (Management edit) | | reject_project_request | POST /external/project-requests/:prq/reject (Management edit) | | activate_project_request | POST /external/project-requests/:prq/activate (Management edit) |

A person asks their Claude for a tool project; the request lands in the team's Slack channel with Approve / Reject buttons; the project-provisioner job in database-service builds everything (GitHub repository from the kit template, Vercel project and variables, a Neon database, the setup ZIP) and messages the person. request_project is refused unless the person acknowledged the monthly cost sentence (cost_acknowledged=true); it accepts no GitHub username when the person has no account yet, and the build waits until set_project_request_github_username is called. Design: the monorepo's docs/prd-project-requests.md.

Catalog (discover ids first)

| Tool | Description | |------|-------------| | list_marketplaces | Active marketplaces → use _id as marketplace_id | | list_brands | Brands for a marketplace → use id as brand_id | | list_business_partners | Suppliers, forwarders, or warehouses (type param) |

Data

| Tool | Calls | |------|--------| | update_shipment_status | PATCH /external/shipments/{ex_number}/status | | get_shipment_by_ex_number | GET /external/shipments/{ex_number} | | list_shipments | GET /external/shipments | | list_products | GET /external/products | | list_parent_groups | GET /external/parent-groups | | get_forecast | GET /external/forecast | | get_targets | GET /external/targets | | get_cash_flow_summary | GET /external/cash-flow/summary | | list_cash_flow | GET /external/cash-flow | | list_sellerboard | GET /external/sellerboard | | list_fba_orders | GET /external/amazon-reports/fba-orders | | list_data_kiosk_economics | GET /external/amazon-reports/data-kiosk-economics | | list_data_kiosk_sales_traffic | GET /external/amazon-reports/data-kiosk-sales-traffic | | list_data_kiosk_sales_traffic_totals | GET /external/amazon-reports/data-kiosk-sales-traffic-totals | | … | Other Amazon report tools |

Orders, KPI Insights & fulfillment rate cards

| Tool | Calls | |------|--------| | list_purchase_demands | GET /external/purchase-demands | | get_purchase_demand_by_pd_number | GET /external/purchase-demands/{pd_number} | | list_payments | GET /external/payments | | get_supplier_analytics | GET /external/supplier-analytics | | list_transactions | GET /external/transactions | | list_documents | GET /external/documents | | get_document_content | GET /external/documents/{document_id}/content | | get_payment_forecast | GET /external/payments/forecast | | get_landed_cost_report | GET /external/landed-cost-report | | list_purchase_orders | GET /external/purchase-orders | | get_purchase_order_by_po_number | GET /external/purchase-orders/{po_number} | | list_conversion_orders | GET /external/conversion-orders | | get_conversion_order_by_co_number | GET /external/conversion-orders/{co_number} | | get_kpi_insights_cards | GET /external/kpi-insights/cards | | get_kpi_insights_drill | GET /external/kpi-insights/drill/{kpi_id} | | get_landed_cost_grid | GET /external/kpi-insights/landed-cost-grid | | get_kpi_insights_shipments | GET /external/kpi-insights/shipments | | get_kpi_insights_stock_value | GET /external/kpi-insights/stock-value | | get_maya_summary | GET /external/maya/summary | | list_fba_fulfillment_costs | GET /external/fulfillment-costs/fba | | list_mcf_fulfillment_costs | GET /external/fulfillment-costs/mcf |

PD/PO/CO numbers are normalized client-side (po-000901 → PO000901).

get_document_content is the one tool whose result is not JSON-as-text: a PDF with a text layer, a spreadsheet or a text file comes back as text; a PNG/JPEG comes back as an image block; a scanned PDF comes back as one image block per rendered page (three per call, page continues). Long text is windowed — the result says when it is truncated and which offset to pass to continue. Word, TIFF and archive files are refused with a reason the user can act on.

The four chain surfaces — purchase demands, purchase orders, conversion orders and shipments — all accept sku plus a date_field + from + to window, so "what is on the way for this SKU" and "what lands next month" are one call each instead of paging every order. get_supplier_analytics rolls spend, payments and outstanding balance up per supplier server-side, because summing order money client-side after grouping by SKU counts an order once per line it contains.

Two tools take marketplace_id without brand_id, for different reasons: the fulfillment rate cards are priced per marketplace, not per brand; and list_payments is scoped that way because a payment carries no brand of its own — the brand comes from the order it references, and is reported per row.

Most data tools require marketplace_id and brand_id. Call catalog tools first.

Archived data

Every list surface takes active: active (live only), archived (archived only) or all. Defaults differ and each tool states its own — the catalogs and shipments default to active, the procurement orders to all — because the parameter was added without changing what any existing key already receives.

Archived rows matter most for history: a supplier that is no longer active is still the supplier on last year's orders, so reading those orders back needs active=all on list_business_partners.

Example prompts (in Claude)

  • “List marketplaces and brands for Amazon.ca, then show shipments in BOOKING status.”
  • “List forwarders and filter OCEAN shipments for brand Pelegon.”
  • “Pull Sellerboard data for last month for marketplace X and brand Y.”

Development

cd expander-mcp
npm install
npm run build
EXMACHINE_API_KEY=exp_live_... EXMACHINE_API_BASE=http://localhost:8080/api/v1 npm start

HTTP mode (Cloud Run / Claude remote connector)

npm run build
PORT=8080 \
  EXMACHINE_API_BASE=https://api.exmachine.io/api/v1 \
  MCP_TOKEN_SECRET=$(openssl rand -hex 32) \
  npm run start:http
curl http://localhost:8080/health

MCP endpoint: POST /mcp with header Authorization: Bearer <EXMACHINE_API_KEY>.

Deploy to Google Cloud Run

Pushes to main build the Docker image and deploy expander-mcp-production to Cloud Run (europe-west1), same pattern as server_dashboard.

One-time setup

  1. GitHub secrets on expandertech/expander-mcp (same as other repos):
    • GCR_PROJECT_ID
    • GCR_SERVICE_ACCOUNT_KEY
    • SLACK_BOT_TOKEN (optional — deployment Slack notification)
  2. Artifact Registry repo cloud-run-source-deploy must exist in GCP (already used by dashboard/gateway).
  3. Token secret in Secret Manager — required, the container refuses to boot without it:
    openssl rand -hex 32 | gcloud secrets create mcp-token-secret --data-file=-
    # let the Cloud Run runtime service account read it
    gcloud secrets add-iam-policy-binding mcp-token-secret \
      --member="serviceAccount:$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')[email protected]" \
      --role=roles/secretmanager.secretAccessor
    Create it once and never rotate casually — a new value disconnects every user.
  4. First deploy creates the Cloud Run service. After deploy, note the *.run.app URL from the GCP console.

Custom domain (mcp.exmachine.io)

  1. Cloud Run → expander-mcp-production → Manage custom domains → add mcp.exmachine.io.
  2. In GoDaddy DNS, add CNAME mcp → value from GCP (often ghs.googlehosted.com).
  3. Claude connector URL: https://mcp.exmachine.io/mcp

Release flow

  1. Merge to main.
  2. GitHub Action .github/workflows/deploy-production.yml runs automatically.
  3. Verify: curl https://<your-run-url>/health

Publishing to npm

Pushes to main publish automatically when package.json version is new (already-published versions are skipped).

One-time setup

  1. npm account — sign up at npmjs.com (use a team account if you prefer).
  2. Create org @expander — npm → avatar → Add new organization → name expander → Unlimited public packages (free).
  3. Automation token (required for CI) — npm → Access Tokens → Generate New Token:
    • Type must be Automation (not Publish, not Classic).
    • Automation tokens skip 2FA / OTP — required for GitHub Actions.
    • If CI fails with EOTP / “one-time password”, delete the old secret and create a new Automation token.
  4. GitHub secret — repo expandertech/expander-mcp → Settings → Secrets and variables → Actions:
    • Update secret NPM_TOKEN with the Automation token (replace any Publish/Classic token).
  5. Merge workflow — commit .github/workflows/publish-npm.yml to main (included in this repo).

Release flow

  1. Bump version in package.json (e.g. 0.1.0 → 0.1.1).
  2. Commit, merge to main.
  3. GitHub Action builds and runs npm publish. Users can then npx @expander/mcp-server.

If you merge a change under src/ without bumping the version, the publish job fails on purpose. Cloud Run redeploys on every push while npm only publishes on a bump, so skipping quietly leaves Claude Desktop users on an older tool list than the remote connector — which is exactly what happened between 0.4.0 and 0.5.0, where nine tools shipped to Cloud Run and never reached npm.

The version in package.json is the single source of truth: createServer.ts reads it, so the version reported in the MCP handshake is always the version that was published.

Security

  • Never commit API keys. Use env vars in the Claude config only.
  • The MCP server runs locally and forwards your key to the Exmachine API on each tool call.
  • Rate limit: 60 requests/minute per key (same as REST).
  • Dashboard permissions apply — denied endpoints return 403 forbidden.

License

MIT