io.github.pipeworx-io/drug-prices is an MCP server that US drug price benchmarks — what a pharmacy PAYS and what Medicare PAYS OUT. Its tool list has not been published yet over http, requires no API key, and scores 60/100 on MCPpedia's security, maintenance and efficiency rubric.
Config is the same across clients - only the file and path differ.
{
"mcpServers": {
"drug-prices": {
"url": "https://gateway.pipeworx.io/drug-prices/mcp"
}
}
}Are you the author?
Add this badge to your README to show your security score and help users find safe servers.
US drug price benchmarks — what a pharmacy PAYS and what Medicare PAYS OUT.
This server supports HTTP transport. Be the first to test it - help the community know if it works.
Five weighted categories - click any category to see the underlying evidence.
No known CVEs.
Checked @pipeworx/mcp-drug-prices against OSV.dev.
Be the first to review
Have you used this server?
Share your experience - it helps other developers decide.
Sign in to write a review.
Others in health
MCP server for the ClinicalTrials.gov v2 API. Search trials, retrieve study details and results, and match patients to eligible trials.
Search ClinicalTrials.gov: find studies, retrieve results, match patients to candidate trials.
Control FL Studio with Claude: in-DAW mixing, diagnosis, routing, and composition.
MCP server providing seamless access to FHIR APIs for AI tools and healthcare applications
MCP Security Weekly
Get CVE alerts and security updates for io.github.pipeworx-io/drug-prices and similar servers.
Start a conversation
Ask a question, share a tip, or report an issue.
Sign in to join the discussion.
US drug price benchmarks: what a pharmacy pays to acquire a drug, and what Medicare Part B pays out for one administered by a provider. Keyless.
Part of Pipeworx — an MCP gateway connecting AI agents to 1863+ live data sources. This is an independent, unofficial integration — not affiliated with, endorsed by, or published by the upstream provider.
These are two different numbers that are routinely confused for each other, and keeping them apart is most of what this pack does.
| NADAC | ASP payment limit | |
|---|---|---|
| Answers | what a retail pharmacy pays a wholesaler | what Medicare pays a clinic |
| Cadence | weekly | quarterly |
| Keyed by | NDC | HCPCS (J-code) |
| Typical drug | generic tablets, retail products | oncology, biologics, infusions, vaccines |
| Source | CMS via data.medicaid.gov | CMS ASP Pricing Files (ZIP) |
A generic tablet has a NADAC and no ASP. An infused oncology drug has an ASP and
usually no retail NADAC. drug_price_compare reports whichever exist and says
why the other is missing, rather than returning an empty result.
nadac_price(drug | ndc, as_of?, limit?) — current acquisition cost per unitnadac_history(ndc, weeks?) — weekly price series and % changeasp_payment_limit(hcpcs | drug, quarter?, limit?) — Part B limit per dosage unitdrug_price_compare(drug, ndc?) — both benchmarks side by side, with vintagesNADAC is read live — data.medicaid.gov exposes a real query API (filter,
sort, paginate) and the dataset holds ~20 months, so there is nothing to mirror.
ASP has no API at all: CMS publishes one ZIP per quarter, so it is ingested
into asp_payment_limits by scripts/ingest-asp-payment-limits.mjs and read
from Supabase.
node scripts/ingest-asp-payment-limits.mjs # current quarter
node scripts/ingest-asp-payment-limits.mjs --back=5 # backfill
Every one of these was hit while building the pack, and each produced a confident wrong answer rather than an error.
The newest NADAC week is usually a correction file, not a week. NADAC
publishes the full weekly file plus tiny off-cycle corrections sharing the same
effective_date column. Measured 2026-08-28: 2026-08-26 → 77 rows,
2026-08-19 → 58,163 rows, 2026-08-12 → 6 rows. The obvious design — take
max(effective_date), then filter — lands on the 77-row file and reports "no
price found" for almost every drug, which is indistinguishable from a drug NADAC
does not cover. Filter for the drug first, then take the newest date in the
matched set.
The datastore API silently ignores unknown parameters. ?sort=-effective_date
returns HTTP 200 with unsorted rows — byte-identical to ?zzznonsense=x. The
only spelling that sorts is sorts[0][property] + sorts[0][order]. A typo here
does not fail; it quietly answers with an arbitrary week's price.
The NADAC dataset id rotates every January, and the year in the title is not the range it covers. The dataset called "NADAC … 2026" holds effective dates from 2025-01-01 to 2026-08-26. The id is resolved by title at call time, with the last known id as a fallback, so January does not break the pack.
NADAC carries two dates. effective_date is the week the price applies to;
as_of_date is when CMS published it. Prices key on the first; both are
returned.
The ASP payment limit's vintage is not its quarter. The CMS file states it: the file effective 2026-07-01 is "based on 1Q26 ASP data". The limit in force is derived from ASP data roughly two quarters earlier, so comparing it against this week's NADAC spans about six months. Both quarters are always returned.
The ASP CSV is windows-1252, not UTF-8, and carries a 0xA0 inside a real
value ("1 million\xa0PFU"). Reading it as UTF-8 throws — and worse, grep
silently aborts on the illegal byte, which is how J9271 first appeared to be
missing from a file that contains it.