salary-mcp
by Propsi4
README.md
# Salary MCP Server (`salary-mcp`)
[](https://github.com/propsi4/salary-mcp/actions/workflows/ci.yml)
[](https://pypi.org/project/salary-mcp/)
[](https://pypi.org/project/salary-mcp/)
[](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.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues