{
  "markdown": "<!-- mcp-name: com.finerxfinder/finerx -->\n\n# FineRx MCP server\n\nGive an AI assistant live access to **FineRx** drug-price data — the same\ncatalog, package, price-comparison, and nearest-pharmacy data shown on the\nFineRx website — **the free FineRx discount card**, and the reviewed map of\n**foreign medicine brands to their US equivalent** (pages and card text in\n12 languages), through the\n[Model Context Protocol](https://modelcontextprotocol.io).\n\n`finerx-mcp` is a thin client over the FineRx **public REST API**\n(`/api/public/v1`). It has no direct database access and inherits the public\nAPI's authentication and rate limits, so it adds zero extra attack surface.\n\n## Tools\n\n| Tool | What it does |\n|------|--------------|\n| `search_drugs(query, limit=10)` | Find drugs by name/alias → candidates with slugs + from-prices, plus any matching foreign brands |\n| `get_drug(slug, locale?, channel?)` | A drug's stats + strength/form variants + `savingsCard` |\n| `compare_prices(ndc, quantity, locale?, channel?)` | Offer matrix (chain × savings program × price × observation date) + `savingsCard` |\n| `find_nearby_pharmacies(zip, chains?, limit=3)` | Nearest store locations per chain around a ZIP |\n| `get_dataset_info()` | Dataset coverage, freshness, and attribution/disclaimer terms |\n| `get_savings_card(locale?, channel?, drug?)` | The free discount card: codes, how to use it, ways to save it — **plus an image of the card**, and an interactive card in hosts that render MCP Apps |\n| `email_savings_card(email, consent, locale?)` | Email the card to an address the person gave, after they said yes |\n| `get_prescription_options(locale?, drug?)` | What to do when there is no prescription yet, or the brand costs too much |\n| `find_us_equivalent(brand, country?, locale?, channel?)` | A medicine from another country → what it is in the US, the vetted sentence to say, the price, the card |\n| `foreign_brands_for_drug(slug)` | What a US drug is called abroad (the reverse lookup) |\n\nEvery result embeds a one-line `disclaimer` and, wherever a price appears, an\n`observedAt` date. No tool ever returns hidden vendors or an operator reference\nprice.\n\nThe server also ships **`instructions`** (served to the client as system-level\nguidance: quote the observation date, call `find_us_equivalent` first for a\nmedicine from another country, offer the card whenever you quote a price, never\nsay guaranteed/best/cheapest, never give medical advice), three\n**resources** — `finerx://card` (the card as markdown), `finerx://how-it-works`,\nand `ui://widget/savings-card.html` (the card as a UI component, below) — and\nthree **prompts**, `price_and_card(drug)`, `prescription_help(drug?)` and\n`us_equivalent(brand, country?)`.\n\n## US equivalents (the thing nobody else does)\n\nAn immigrant does not search \"atorvastatin\" — they search Но-шпа, Nurofen,\nDolo-Neurobion, 999 Ganmaoling. FineRx holds a reviewed corpus mapping those\nbrands to their US status, and `find_us_equivalent` is how an assistant reaches\nit instead of answering from memory.\n\n```python\nfind_us_equivalent(brand=\"Но-шпа\")\n# → usClass \"rx_alternative\", inn \"drotaverine hydrochloride\",\n#   guidance \"No-Spa (drotaverine hydrochloride) is not sold in the US as the same\n#             product; the closest US options need a prescription. Ask a doctor or\n#             pharmacist which one fits.\"\n#   otherMatches [{\"brand\": \"No-Spa\", \"countries\": [\"Poland\"], ...}]\n\nfind_us_equivalent(brand=\"Nurofen\", country=\"Turkey\")\n# → usClass \"same_inn\", usGeneric \"ibuprofen\", usBrands [\"Advil\", \"Motrin\"],\n#   usDrug {slug, fromPrice, observedAt}, savingsCard, and the guidance sentence\n#   that names ibuprofen as the thing to ask the pharmacist for.\n```\n\nThree classes, and the difference between them is the whole honesty of the\nfeature: **`same_inn`** means the same active ingredient is sold here (not the\nsame product — strength, form and excipients differ, which is what\n`guidanceDisclaimer` says); **`rx_alternative`** means it is not sold here and\nthe closest US options need a prescription, so the answer is \"ask a clinician\",\nnever a named swap; **`no_equivalent`** means nothing here matches, and the\n`components` breakdown states each ingredient's own US status rather than\ninventing a substitute.\n\n`guidance` is written and reviewed server-side. **Quote it; do not paraphrase,\ntranslate or extend it** — a sentence composed by the model is a medical claim\nnobody reviewed. `foreign_brands_for_drug(slug)` is the reverse (\"what is\nlisinopril called in Mexico?\"), and `search_drugs` carries a `foreignBrands`\nlist so a client that only calls search still finds the corpus.\n\n## Handing over the card\n\nThe card is the free LowerMyRx discount card FineRx distributes. It is **not\ninsurance**, needs no signup, and is credited at the pharmacy counter by the\ngroup code — so an assistant can hand it over completely, with no click-through:\n\n- **`get_savings_card`** returns the three counter codes, what the card is and\n  is not, the steps to use it, the sentence to say to the pharmacist, an\n  optional observed price with its date, an FAQ and the legal lines — *and* a\n  PNG of the card as an image content block, so a chat client can show something\n  the person saves to their phone. The PNG is fetched once per process and\n  cached in memory; if that fetch fails the tool still returns the card text,\n  and `imageUrl` is in the JSON either way for clients that cannot render images.\n- **`email_savings_card`** sends one card-only message. It **refuses without\n  `consent=true`** and never calls the API in that case — the assistant has to\n  ask for the address and an explicit yes first. The result masks the address\n  (`j***@example.com`); FineRx does not store it. `503` means email delivery is\n  off (offer the image or the link instead), `429` means the abuse cap was hit.\n- **`compare_prices` / `get_drug`** carry the same card object as `savingsCard`,\n  so the card is present in the same turn as the price. Say \"lowest\" only when\n  `savingsCard.price.isLowest` is true; otherwise repeat the provided `note`.\n- Every URL the tools return carries `src=<channel>` (default `mcp`), so the\n  assistant that sent someone is visible in FineRx's own analytics — nothing\n  about the person is.\n\n## Renders as an app\n\nOn a host that supports UI components — **ChatGPT** (Apps SDK) and any host that\nimplements the standard **MCP Apps** extension, which is how Claude renders one —\n`get_savings_card` does not just return JSON: the host draws the card in the\nconversation.\n\n- The component is the resource **`ui://widget/savings-card.html`**, served with\n  mime type `text/html;profile=mcp-app`. `get_savings_card` points at it from its\n  tool `_meta`, under both the standard key (`ui.resourceUri`) and OpenAI's alias\n  (`openai/outputTemplate`), so it renders in either kind of host.\n- It shows the teal card with the three counter codes (large and selectable), the\n  card price for the drug when one is known (with the date it was observed and\n  the vetted note — never a \"lowest\" badge unless `price.isLowest` is true), the\n  sentence to say to the pharmacist with a Copy button, the steps, buttons to\n  open / print / save / email the card, the FAQ, and the legal lines. Every\n  string comes from the tool result's `labels`, so it speaks the same language\n  and uses the same words as finerxfinder.com.\n- The Email button calls `email_savings_card` from inside the component (which is\n  why that tool is declared `ui.visibility: [\"model\", \"app\"]` /\n  `openai/widgetAccessible`). Consent is a checkbox the person ticks; the address\n  is sent once and never written back into the page.\n- The document is **entirely self-contained** — inline CSS and vanilla JS, no\n  external script, stylesheet, font or image — because hosts serve it under a\n  `default-src 'none'` CSP. Links are opened through the host bridge\n  (`window.openai.openExternal` or `ui/open-link`), never `window.open`.\n- It renders with **no data at all** (a host that sends nothing still shows the\n  three codes), with no `price` block, and with no `labels` (English fallbacks).\n\n**Testing it without a host.** The tool result is the whole input, so a mocked\nbridge is enough:\n\n```bash\nuv run --project packages/finerx-mcp python -c \\\n  \"from finerx_mcp.widget import load_widget_html; open('/tmp/w.html','w').write(load_widget_html())\"\n```\n\nPrepend a `<script>` that defines `window.openai = {toolOutput: <a /card\nresponse>, theme: \"light\", locale: \"en\", callTool: …, openExternal: …}` to that\nfile and open it in a browser for the ChatGPT bridge; or put the widget in an\n`<iframe srcdoc>` and have the parent answer `ui/initialize` and then post a\n`ui/notifications/tool-result` notification to exercise the standard bridge.\n`get_savings_card`'s `structuredContent` is exactly what the component reads, so\n`GET /api/public/v1/card` is a ready-made fixture.\n\n## Prerequisites\n\n- A FineRx developer API key (format `frx_live_...`). Request one by emailing\n  **partners@finerxfinder.com** — see <https://finerxfinder.com/developers>.\n- [`uv`](https://docs.astral.sh/uv/) installed (provides `uvx`).\n\n## Configuration\n\nThe server reads two environment variables:\n\n| Variable | Required | Default | Notes |\n|----------|----------|---------|-------|\n| `FINERX_API_KEY` | yes | — | Your `frx_live_...` key |\n| `FINERX_API_BASE` | no | `https://finerxfinder.com/api/public/v1` | Point at another host for local testing |\n| `FINERX_CARD_IMAGE_URL` | no | `https://www.finerxfinder.com/card.png` | PNG `get_savings_card` returns, when the API response names no `delivery.imageUrl` |\n| `FINERX_MCP_TRANSPORT` | no | `stdio` | `streamable-http` for the remote endpoint |\n| `FINERX_MCP_HOST` / `FINERX_MCP_PORT` | no | `127.0.0.1` / `8000` | Bind address for the HTTP transport |\n| `FINERX_MCP_STATELESS` | no | `1` (on) | `0` restores per-session HTTP transports (see below) |\n\n## Install & run\n\nRun directly with `uvx` (no manual install needed):\n\n```bash\nFINERX_API_KEY=frx_live_xxxxxxxx uvx finerx-mcp\n```\n\nThe server speaks MCP over **stdio** by default.\n\n### Remote (HTTP) mode\n\nThe same tools can be served over **Streamable HTTP** — the transport that\nconnector catalogs (ChatGPT Apps, Claude connectors, Gemini, Grok) consume, so a\nuser adds FineRx by URL with no local install. Set the transport (and, for a\nhosted deployment, the bind host/port):\n\n```bash\nFINERX_API_KEY=frx_live_xxxx \\\nFINERX_MCP_TRANSPORT=streamable-http \\\nFINERX_MCP_HOST=0.0.0.0 FINERX_MCP_PORT=9000 \\\nuvx finerx-mcp\n# → endpoint at http://<host>:9000/mcp\n```\n\nDefaults stay stdio, so existing Claude Desktop/Code/Cursor configs are\nunaffected. The HTTP endpoint calls the same public REST API with the server's\n`FINERX_API_KEY`, so hosting it exposes no data beyond the already-public API.\n\n**HTTP mode is stateless by default.** Keyless one-shot connector calls — a\ncatalog probe, a single tool call from a chat — open a Streamable-HTTP session\nand never DELETE it, so in session mode the transport map only grows (measured\non the hosted endpoint: 3568 sessions over 7 days, ~25 MB of swap a day, until\nthe container restarts). Stateless mode builds a transport per request and drops\nit. It is safe here because no tool keeps per-session state: every call is a\nfresh request against the public API, and the card-image cache is process-level,\nnot per-session. Set `FINERX_MCP_STATELESS=0` if you need SSE resumability;\nstdio ignores the setting entirely.\n\n## Claude Desktop\n\nAdd to your `claude_desktop_config.json`\n(macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"finerx\": {\n      \"command\": \"uvx\",\n      \"args\": [\"finerx-mcp\"],\n      \"env\": {\n        \"FINERX_API_KEY\": \"frx_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n      }\n    }\n  }\n}\n```\n\nRestart Claude Desktop; the FineRx tools appear in the tools menu.\n\n## Claude Code\n\n```bash\nclaude mcp add finerx --env FINERX_API_KEY=frx_live_xxxx -- uvx finerx-mcp\n```\n\nOr add it to `.mcp.json` in your project:\n\n```json\n{\n  \"mcpServers\": {\n    \"finerx\": {\n      \"command\": \"uvx\",\n      \"args\": [\"finerx-mcp\"],\n      \"env\": { \"FINERX_API_KEY\": \"frx_live_xxxxxxxx\" }\n    }\n  }\n}\n```\n\n## Local development\n\nRun against a local FineRx stack (e.g. the dev proxy on `:8080`):\n\n```bash\nexport FINERX_API_KEY=frx_live_...        # a key you created via the CLI\nexport FINERX_API_BASE=http://localhost:8080/api/public/v1\nuv run --project packages/finerx-mcp finerx-mcp\n```\n\nA smoke test that drives one tool call end-to-end lives at\n`scripts/smoke_mcp.py` in the FineRx repo.\n\nRun the unit tests (HTTP is stubbed — no key, no network):\n\n```bash\nuv run --project packages/finerx-mcp --with pytest --with pytest-asyncio \\\n    pytest packages/finerx-mcp/tests -q\n```\n\n## Terms\n\nData is provided under the FineRx public API terms: attribution required\n(\"Prices via FineRx\"), prices are observed estimates (not guaranteed or insured\nprices) and may be out of date, and nothing here is medical advice. Pharmacy\nlocation coordinates are © OpenStreetMap contributors (ODbL).",
  "bytes": 13063,
  "sha": "a4c1ebaf8aa98e5fbe7d0499338b28ac05d11a5aa9bf11aec8900ea50f549143",
  "repo_slug": "",
  "fonte": "pypi",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_com_finerxfinder_finerx_a056f637/readme"
}