Metadata-Version: 2.4
Name: salary-mcp
Version: 0.1.2
Summary: Model Context Protocol (MCP) server providing access to public Ukrainian/European IT salary benchmarks from Djinni and DOU.
License-File: LICENSE
Author: Vadym Burylo
Author-email: gaenday12@gmail.com
Requires-Python: >=3.11,<4.0
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: beautifulsoup4 (>=4.12.0,<5.0.0)
Requires-Dist: httpx (>=0.27.0,<0.28.0)
Requires-Dist: mcp (>=1.3.0,<2.0.0)
Requires-Dist: platformdirs (>=4.2.0,<5.0.0)
Requires-Dist: pydantic (>=2.6.0,<3.0.0)
Description-Content-Type: text/markdown

# Salary MCP Server (`salary-mcp`)

[![CI](https://github.com/propsi4/salary-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/propsi4/salary-mcp/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/salary-mcp.svg)](https://pypi.org/project/salary-mcp/)
[![Python Version](https://img.shields.io/pypi/pyversions/salary-mcp.svg)](https://pypi.org/project/salary-mcp/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server providing LLMs with direct, programmatic access to actual public IT market salary benchmarks from **Djinni (`djinni.co`)** and **DOU (`jobs.dou.ua/salaries/`)**.

---

## ⚡ Quick Start (Published PyPI Package)

`salary-mcp` is published on [PyPI](https://pypi.org/project/salary-mcp/) and can be run instantly without manual repository cloning.

### 1. Run over Stdio (Default)
Standard input/output communication for desktop AI clients (Claude Desktop, Cursor, Antigravity, Zed):
```bash
# Instant run with uvx (no installation needed)
uvx salary-mcp

# Or with pipx
pipx run salary-mcp

# Or install via pip
pip install salary-mcp
salary-mcp
```

### 2. Run over HTTP / SSE (Remote Server)
Server-Sent Events (SSE) mode for remote deployments, containers, and web clients:
```bash
# Start SSE HTTP server on port 8000
uvx salary-mcp --transport sse --host 0.0.0.0 --port 8000
```
Your MCP client can connect to: `http://localhost:8000/sse`

---

## 🔌 MCP Client Configurations

### Claude Desktop (`claude_desktop_config.json`)

#### Stdio Mode (Recommended):
```json
{
  "mcpServers": {
    "salary-mcp": {
      "command": "uvx",
      "args": ["salary-mcp"]
    }
  }
}
```

#### HTTP / SSE Mode:
```json
{
  "mcpServers": {
    "salary-mcp": {
      "url": "http://localhost:8000/sse"
    }
  }
}
```

### Cursor (`~/.cursor/mcp.json`)
```json
{
  "mcpServers": {
    "salary-mcp": {
      "command": "uvx",
      "args": ["salary-mcp"]
    }
  }
}
```

---

## 🌐 Data Sources & Extraction Architecture

The server fetches data **exclusively** from the live official web portals of Djinni and DOU:

### 1. Djinni (`https://djinni.co/salaries/`)
* **Endpoint Format**: `https://djinni.co/salaries/?category={category}&exp={exp}&english_level={level}`
* **Extraction Method**: Live on-demand scraping of Djinni's rolling 30-day platform hiring metrics.
* **Extracted Data**:
  * **Candidate Expectations**: 25th–75th percentile salary expectations and calculated median.
  * **Company Vacancies**: Active job posting salary offer ranges.
  * **Market Activity**: Real-time counters of active candidates online and open vacancies.
  * **Salary Distribution**: Full salary bin histogram parsed directly from embedded chart data.

### 2. DOU (`https://jobs.dou.ua/salaries/`)
* **Endpoint Source**: Master widget dataset loaded directly by [`https://jobs.dou.ua/salaries/`](https://jobs.dou.ua/salaries/) (`https://s.dou.ua/files/lenta/salary-widget_jun_2026_v3/data/swd-medians.csv`).
* **Extraction Method**: Slices official statistical quartiles ($q1$, $median$, $q3$), respondent sample sizes ($count$), and seniority title levels ($title$).
* **Historical Support**: Supports querying specific historical survey waves via the `as_of_date` parameter (e.g. `'2025-12'`, `'2026-06'`), defaulting to the latest available wave.

---

## ❓ Why DOU Provider Data May Differ from Website UI Views

When querying DOU via `salary-mcp`, you might occasionally notice subtle differences between the returned statistics and what is rendered in the interactive UI of `jobs.dou.ua/salaries/`:

1. **Frontend Sample Size Thresholds**:
   * On the public website, DOU's charting scripts often apply a minimum sample size threshold (typically $\ge 15-20$ respondents).
   * When a specific experience bracket has fewer respondents (e.g. $11$ respondents for 9 years of experience in Data Science), the website chart suppresses or greys out the bar as *"Недостатньо анкет"* (Insufficient data).
   * The underlying DOU analytics dataset preserves the exact calculated median for those respondents, which `salary-mcp` returns accurately.
2. **Category Aggregations vs. Specific Title Filtering**:
   * Selecting a broad category (e.g. "Data & Analytics" or "Management") on the web interface aggregates all sub-roles together.
   * Specific title queries (e.g. `Middle Data Scientist` or `Junior HR Specialist`) match the specific title tier within the dataset.
3. **Survey Wave Releases**:
   * By default, `salary-mcp` always selects the most recent official survey wave (e.g. `2026-06`). If the website user interface is displaying an earlier wave or a different article, specifying `as_of_date` ensures identical alignment.

---

## 🛠️ MCP Tools Reference

### `get_djinni_salaries`
Fetch real-time candidate salary expectations and vacancy offer distributions from Djinni.
* **Arguments**:
  * `role` (*string, required*): Target job role (e.g. `"Software Engineer"`, `"QA"`, `"DevOps"`, `"HR"`).
  * `specialization` (*string, optional*): Technology or domain (e.g. `"Python"`, `"React"`, `"HR"`).
  * `experience_years` (*integer, optional*): Years of experience (e.g. `0`, `2`, `5`).
  * `english_level` (*string, optional*): English proficiency (e.g. `"intermediate"`, `"advanced"`).

### `get_dou_salaries`
Fetch official salary survey benchmarks and percentiles from DOU.
* **Arguments**:
  * `role` (*string, required*): Job role or category (e.g. `"Software Engineer"`, `"Data Science"`).
  * `specialization` (*string, optional*): Language or sub-role (e.g. `"Python"`, `"Data Scientist"`).
  * `experience_years` (*integer, optional*): Years of professional experience.
  * `seniority` (*string, optional*): Seniority tier (`"Junior"`, `"Middle"`, `"Senior"`, `"Lead"`, `"Architect"`).
  * `city` (*string, optional*): Location filter (e.g. `"Kyiv"`, `"Lviv"`, `"Remote"`).
  * `as_of_date` (*string, optional*): Survey date in `YYYY-MM` format (e.g. `"2025-12"`, `"2026-06"`). Defaults to latest.

### `compare_salaries`
Compare salary benchmarks between Djinni and DOU side-by-side with differential analysis.
* **Arguments**:
  * `role` (*string, required*): Target job role.
  * `specialization` (*string, optional*): Technology or specialization.
  * `experience_years` (*integer, optional*): Years of experience.
  * `seniority` (*string, optional*): Seniority level for DOU matching.
  * `as_of_date` (*string, optional*): Target survey date for DOU comparison.

### `list_specializations`
List available roles, technologies, seniorities, locations, and historical survey dates.
* **Arguments**:
  * `provider` (*string, optional*): Scope of choices (`"all"`, `"djinni"`, `"dou"`). Defaults to `"all"`.

---

## 🛠️ Local Development

```bash
# Clone and install dependencies
git clone https://github.com/propsi4/salary-mcp.git
cd salary-mcp
poetry install

# Run test suite
poetry run pytest

# Run linter and type checks
poetry run ruff check . --fix
poetry run ruff format .
poetry run mypy src tests
```

---

## 📄 License

MIT License. See [LICENSE](LICENSE) for details.

