Skip to main content
Glama
Ispas-Catalin

Romanian Job Search MCP

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.

Related MCP server: j-hunt-mcp

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.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server that exposes job search data from multiple boards, enabling clients to query and manage job listings via natural language.
    7
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    A local job-hunting MCP server for discovering jobs across pluggable web sources, tracking applications through a status lifecycle, and managing profiles/resumes, with geo/map-region search.
    12
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server that aggregates job listings from Remotive and Arbeitnow, with local SQLite caching, application tracking, and saved searches.
    45 npm
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server that aggregates and deduplicates job listings from multiple public sources, ranks them against a user's resume, and exposes tools for searching, viewing details, explaining fit, and tracking applications.
    MIT