Skip to main content
Glama
itsyashvardhan

Job Listings MCP Server

README.md
# MCP Server

A standalone Python microservice that **scrapes fresh job listings** using [Jobspy](https://github.com/Bunsly/JobSpy), stores them in SQLite with deduplication, and exposes a **`/jobs` REST endpoint** for embedding in a portfolio site as a live feed.

---

## Features

- **Multi-site scraping** 
- **Tiered role search** 
- **Smart deduplication**
- **APScheduler**
- **Query filtering** 
- **CORS-enabled**
- **Deploy-ready**

---

## Architecture

```
APScheduler (1hr)  →  Jobspy Scraper  →  SQLite (deduped)  ←  FastAPI /jobs
                                                                    ↕
                                                          Portfolio Site (fetch)
```

## Quick Start

### 1. Clone & Install

```bash
cd jobs-mcp-server
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install -r requirements.txt
```

### 2. Configure

```bash
cp .env.example .env
# Edit .env as needed
```

### 3. Run

```bash
python main.py
```

The server starts at `http://localhost:8000`. An initial scrape runs automatically in the background.

---

## API Endpoints

### `GET /` — Health Check

```json
{
  "status": "healthy",
  "service": "Job Listings MCP Server",
  "total_jobs_in_db": 142,
  "scrape_interval_hours": 1
}
```

### `GET /jobs` — List Job Listings

**Query Params:**

| Param      | Type   | Description                                  |
|------------|--------|----------------------------------------------|
| `location` | string | Filter by location (substring, case-insensitive) |
| `keyword`  | string | Filter by keyword in job title               |
| `hours`    | int    | Only jobs scraped within the last N hours     |
| `limit`    | int    | Max results (default 100, max 500)           |
| `offset`   | int    | Pagination offset                            |

**Example:**

```bash
curl "http://localhost:8000/jobs?location=San%20Francisco&keyword=AI&hours=24"
```

**Response:**

```json
{
  "count": 5,
  "filters": {
    "location": "San Francisco",
    "keyword": "AI",
    "hours": 24
  },
  "jobs": [
    {
      "id": 1,
      "job_title": "AI Solutions Engineer",
      "company": "Acme Corp",
      "location": "San Francisco, CA",
      "salary": "USD 120,000–160,000/yearly",
      "apply_link": "https://linkedin.com/jobs/...",
      "date_posted": "2025-01-15",
      "date_scraped": "2025-01-15T12:00:00+00:00",
      "source_site": "linkedin",
      "role_tier": "T2 — Secondary"
    }
  ]
}
```

### `POST /scrape` — Manual Trigger

Triggers a scrape run in the background.

```bash
curl -X POST http://localhost:8000/scrape
```

### `GET /status` — Last Scrape Status

```bash
curl http://localhost:8000/status
```

### `GET /roles` — Configured Role Tiers

```bash
curl http://localhost:8000/roles
```

---

## Deployment

### Railway

1. Fork the `mcp-server` repo to a new GitHub repo (or subdirectory).
2. Connect Railway to the repo.
3. Railway auto-detects the Dockerfile.
4. Add a **Volume** at `/data` to persist the SQLite DB.
5. Set environment variables in the Railway dashboard.

### Render

1. Create a new **Web Service**.
2. Point to the repo/directory.
3. Set **Build Command:** `pip install -r requirements.txt`
4. Set **Start Command:** `python main.py`
5. Add a **Disk** at `/data` and set `DATA_DIR=/data`.

---

## 🔗 Portfolio Integration

In your Next.js portfolio, fetch from the deployed URL:

```tsx
// In a Next.js API route or client component
const API_URL = process.env.NEXT_PUBLIC_JOBS_API_URL || 'https://your-jobs-server.up.railway.app';

async function fetchJobs(filters?: { location?: string; keyword?: string; hours?: number }) {
  const params = new URLSearchParams();
  if (filters?.location) params.set('location', filters.location);
  if (filters?.keyword) params.set('keyword', filters.keyword);
  if (filters?.hours) params.set('hours', String(filters.hours));

  const res = await fetch(`${API_URL}/jobs?${params.toString()}`);
  return res.json();
}
```


## License

MIT

Maintenance

ActivityInactive
ResponsivenessNo issues