# Malaysia MyPriMe Scraper (`speedpolice/malaysia-myprime-scraper`) Actor

- **URL**: https://apify.com/speedpolice/malaysia-myprime-scraper.md
- **Developed by:** [John Tan](https://apify.com/speedpolice) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## MOH MyPriMe Pharmaceutical Claims Auditor MCP Server

A production-ready Model Context Protocol (MCP) server containerized with Docker that queries the **Malaysia Ministry of Health (MOH) MyPriMe (Consumer Price Guide)** database to audit pharmaceutical billing, calculate insurance markup percentages, and detect claims leakage.

***

### 🌟 Key Features

- **MOH MyPriMe Integration**: Connects to the Malaysian Ministry of Health drug database (`https://pharmacy.moh.gov.my/en/apps/drug-pricedatabase`) for benchmark procurement and Recommended Retail Prices (RRP).
- **Asynchronous Scraper & Local Cache**: Built with `httpx` and `BeautifulSoup4`, scraping and caching records in a persistent SQLite database.
- **Fuzzy Search Engine**: Powered by `rapidfuzz` to tolerate common misspelling variants (e.g., `nexum` $\rightarrow$ `Nexium`, `paracetemol` $\rightarrow$ `Paracetamol`).
- **B2B Claims Auditing Tools**:
  - `search_drug_price`: Lookup benchmark prices and dosages.
  - `audit_billed_medication`: Audit single item billings against MOH RRP with customizable markup thresholds.
  - `batch_audit_invoice`: Audit entire hospital medication invoices with consolidated claims leakage metrics.
- **Dual Transport (STDIO & SSE)**: Configurable transport via environment variable (`MCP_TRANSPORT=stdio` or `MCP_TRANSPORT=sse`).
- **Production-Ready Docker**: Hardened multi-stage Docker build, non-root user execution (`appuser`), and persistent volume mounting.

***

### 📂 Project Architecture

```
.
├── Dockerfile              # Multi-stage production build (python:3.11-slim, non-root user)
├── docker-compose.yml      # Deployment compose file with named volumes & healthchecks
├── entrypoint.sh           # Container entrypoint with automatic seed checking
├── database.py             # SQLite schema, connection management & rapidfuzz matching
├── seed_mock_data.py       # Seeds 25 realistic Malaysian private hospital medications
├── scraper.py              # Asynchronous MOH MyPriMe web scraper
├── server.py               # FastMCP server exposing auditing tools (STDIO & SSE)
├── requirements.txt        # Python dependency manifest
└── README.md               # Complete setup, configuration & integration guide
```

***

### 🛠️ MCP Tools Overview

#### 1. `search_drug_price(query: str) -> str`

Searches the MOH MyPriMe price database using fuzzy string matching across generic names, brand names, and strengths.

- **Example query**: `"nexum"`, `"Augmentin"`, `"Pantoprazole 40mg"`.

#### 2. `audit_billed_medication(drug_name: str, quantity: int, total_billed_rm: float, custom_markup_threshold_pct: float = 30.0) -> dict`

Evaluates a billed medication against MOH RRP.

- Calculates billed unit price vs. MOH RRP.
- Computes markup %:
  $$\text{Markup %} = \frac{\text{Billed Unit Price} - \text{MOH RRP}}{\text{MOH RRP}} \times 100$$
- Flags claims exceeding standard Third-Party Administrator (TPA) thresholds (e.g. $>30%$) and computes estimated claims leakage.

#### 3. `batch_audit_invoice(items: list[dict], custom_markup_threshold_pct: float = 30.0) -> dict`

Processes multi-line pharmaceutical invoices, providing:

- Total items audited & flagged item count.
- Total billed amount vs MOH benchmark RRP.
- Net claims leakage in Malaysian Ringgit (RM).
- Line-item breakdown.

***

### 🚀 Getting Started with Docker

#### Option A: Run via Docker Compose (Recommended for SSE Mode)

1. **Start the MCP Server in SSE Mode**:
   ```bash
   docker compose up -d --build
   ```

2. **Verify Server Health & Logs**:

   ```bash
   docker compose logs -f
   ```

   The server will start at `http://localhost:8000/sse`.

3. **Trigger Scraper to Refresh Live MOH Data** (Optional):
   ```bash
   docker compose exec moh-myprime-mcp python3 scraper.py
   ```

***

#### Option B: Build and Run with Docker CLI

##### 1. Build the Docker Image

```bash
docker build -t moh-myprime-mcp:latest .
```

##### 2. Run in STDIO Mode (for CLI integration / Claude Desktop)

```bash
docker run -i --rm \
  -v moh_data:/app/data \
  -e MCP_TRANSPORT=stdio \
  moh-myprime-mcp:latest
```

##### 3. Run in SSE Mode (for remote agents, API gateways, or webhooks)

```bash
docker run -d --name moh-mcp \
  -p 8000:8000 \
  -v moh_data:/app/data \
  -e MCP_TRANSPORT=sse \
  moh-myprime-mcp:latest
```

***

### 🔌 Connecting to AI Clients

#### 1. Claude Desktop (STDIO Mode)

Add the following to your `claude_desktop_config.json`:

- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "moh-myprime-auditor": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-v",
        "moh_data:/app/data",
        "-e",
        "MCP_TRANSPORT=stdio",
        "moh-myprime-mcp:latest"
      ]
    }
  }
}
```

***

#### 2. Cursor / Windsurf / Remote Agents (SSE Mode)

When running the container via `docker compose up -d`:

- **Transport**: `sse`
- **Server URL**: `http://localhost:8000/sse`

In your MCP configuration:

```json
{
  "mcpServers": {
    "moh-myprime-auditor": {
      "url": "http://localhost:8000/sse"
    }
  }
}
```

***

#### 3. API Gateway / Custom Agent Integration (Python Example)

```python
import asyncio
from mcp.client.session import ClientSession
from mcp.client.sse import sse_client

async def run_audit():
    async with sse_client("http://localhost:8000/sse") as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            
            # Audit a hospital bill for Nexium
            result = await session.call_tool(
                "audit_billed_medication",
                arguments={
                    "drug_name": "nexum 40mg",
                    "quantity": 14,
                    "total_billed_rm": 180.0
                }
            )
            print(result)

asyncio.run(run_audit())
```

***

### 📊 Seeded Benchmark Dataset

On first startup, the database is pre-seeded with 25 common Malaysian private hospital medications, including:

- **Pantoprazole 40mg** (Controloc)
- **Atorvastatin 20mg** (Lipitor)
- **Amoxicillin / Clavulanic Acid 625mg** (Augmentin)
- **Ceftriaxone 1g IV** (Rocephin)
- **Metformin 500mg** (Glucophage)
- **Celecoxib 200mg** (Celebrex)
- **Omeprazole 20mg** (Losec)
- **Esomeprazole 40mg** (Nexium)
- **Paracetamol 500mg** (Panadol ActiFast)
- **Amlodipine 5mg** (Norvasc)
- **Rosuvastatin 10mg** (Crestor)
- **Enoxaparin 4000 IU** (Clexane)
- *...and more.*

# Actor input Schema

## `targetUrl` (type: `string`):

Target URL for MOH MyPriMe portal or alternative mirror.

## `maxRecords` (type: `integer`):

Maximum number of records to ingest (0 for unlimited).

## Actor input object example

```json
{
  "targetUrl": "https://pharmacy.moh.gov.my/en/apps/drug-pricedatabase",
  "maxRecords": 0
}
```

# Actor output Schema

## `results` (type: `string`):

Scraped drug benchmark prices and Recommended Retail Prices (RRP) from Malaysia MOH MyPriMe

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("speedpolice/malaysia-myprime-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("speedpolice/malaysia-myprime-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call speedpolice/malaysia-myprime-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,speedpolice/malaysia-myprime-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/0UWUbKyn9moWVCZVC/builds/gNDhZZxTH6Nw64z12/openapi.json
