Romanian Job Search MCP

Romanian Job Search MCP

Cache-first MCP server for Romanian job search with Jooble integration, enabling local job search, quota management, and structured detail extraction.

Category
Visit Server

README

Romanian Job Search MCP

Cache-first MCP server for Romanian job search. The first provider is Jooble, with a database ledger so Hermes can search locally most of the time and spend Jooble API requests only when asked.

Design

  • search_logged_jobs searches the local DB only. It never calls Jooble.
  • search_and_log_jooble_jobs can spend one Jooble API request, then upserts every returned job.
  • start_job_search_session lets Hermes pass a Jooble key from the agent-owned .env.
  • Raw API keys are not stored in the database. Request logs store only key_id and a fingerprint.
  • mark_job_interest stores interest/application state so the LLM does not need to remember.
  • get_job_detail_schema defines the stable structure Hermes should fill after researching a job page.
  • populate_job_details persists that LLM-filled structured detail record.

Key Ownership

Recommended deployment: keep Jooble keys in the Hermes/agent .env, not in the MCP container/appdata. Hermes should read its fallback keys and pass the selected key as api_key when it calls start_job_search_session.

The MCP keeps that raw key only in process memory for the session. The database stores request counts with a key id/fingerprint, not the secret. Named MCP-side keys via JOOBLE_API_KEYS still exist as an optional mode, mainly for standalone testing or an MCP-owned scheduled worker.

MCP Tools

  • start_job_search_session(api_key, request_budget?)
  • quota_status(session_id?, key_id?)
  • search_logged_jobs(keywords?, location?, marked_status?)
  • search_and_log_jooble_jobs(session_id, keywords, location?, radius?, salary?, page?, result_on_page?)
  • get_logged_job(job_id?, source_job_id?, include_raw?)
  • get_job_detail_schema()
  • populate_job_details(job_id, details, extracted_by?)
  • mark_job_interest(job_id, status, notes?)
  • list_marked_jobs(status?)
  • create_saved_search(...)
  • refresh_saved_search(session_id, saved_search_id)

Hermes Usage Pattern

  1. Start a session with a key from the Hermes .env:

    {"api_key": "${JOOBLE_RO_PRIMARY}", "request_budget": 3}
    

    If the primary key hits quota, Hermes can start a new session with its fallback key.

  2. Search locally first:

    {"keywords": "python backend", "location": "Bucuresti"}
    
  3. Spend Jooble quota only if local results are missing or stale:

    {
      "session_id": "returned-session-id",
      "keywords": "python backend",
      "location": "Bucuresti",
      "result_on_page": 50
    }
    
  4. Mark jobs instead of remembering them in the LLM context:

    {"job_id": 1, "status": "interested", "notes": "Remote-friendly Python API role"}
    
  5. For deeper details, Hermes should inspect the public job page itself, then save a structured extraction:

    {
      "job_id": 1,
      "details": {
        "job_title": "Python Developer",
        "company_name": "Example SRL",
        "company_website": null,
        "location": "Bucuresti",
        "work_mode": "hybrid",
        "employment_type": "full_time",
        "seniority": "mid",
        "contract_type": "CIM",
        "salary_text": null,
        "salary_min": null,
        "salary_max": null,
        "salary_currency": null,
        "description": "Concise role description extracted from the public page.",
        "responsibilities": ["Build APIs", "Maintain integrations"],
        "requirements": ["Python", "REST APIs"],
        "nice_to_have": [],
        "benefits": [],
        "technologies": ["Python", "FastAPI"],
        "languages": ["English"],
        "application_url": "https://ro.jooble.org/jdp/...",
        "source_url": "https://ro.jooble.org/jdp/...",
        "contact": null,
        "evidence": {
          "work_mode": "Short supporting note or snippet"
        },
        "missing_fields": ["salary_text"],
        "confidence": 82
      },
      "extracted_by": "hermes"
    }
    

    Use get_job_detail_schema when Hermes needs the exact current field contract. Unknown fields are rejected; unknown values should be null or [] and listed in missing_fields.

Local Development

py -m venv .venv
.\.venv\Scripts\python.exe -m pip install -e ".[test]"
.\.venv\Scripts\python.exe -m pytest
.\.venv\Scripts\python.exe -m uvicorn ro_job_search_mcp.main:app --reload

The default Docker Compose MCP endpoint is http://localhost:8012/mcp, and health is http://localhost:8012/health.

Docker Compose / Unraid

  1. Copy .env.example to .env.

  2. Do not put Jooble keys in this .env when Hermes owns them.

  3. On Unraid, set APPDATA_PATH=/mnt/user/appdata/ro-job-search-mcp.

  4. Start:

    docker compose up -d --build
    

The MCP server listens on host port ${MCP_PORT:-8012} and container port 8000.

The worker service is behind the Docker Compose worker profile because it needs an MCP-side key. In the agent-owned key model, let Hermes call refresh_saved_search instead of running the worker.

Publishing Docker Images

GitHub Actions publishes the Docker image to GitHub Container Registry:

ghcr.io/ispas-catalin/ro-job-search-mcp

Publish by pushing a version tag:

git tag v0.1.0
git push origin v0.1.0

You can also run the Docker workflow manually from the GitHub Actions tab.

Jooble Quota Policy

Default limits are set for a 500-request key:

  • monthly hard limit: 500
  • monthly soft warning: 430
  • daily limit: 16
  • default cache TTL: 12h

Cached searches do not spend quota. Forced refreshes and stale cache refreshes do.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured