File size: 4,589 Bytes
0a4b4e5
f77befc
8ae67db
bfad4de
3c1606d
8ae67db
 
 
0a4b4e5
bfad4de
a9d13df
5d20b78
d628d4c
bfad4de
a7b320c
3c1606d
bfad4de
 
8f4df5d
3c1606d
971a339
a7b320c
 
 
 
 
 
064e5aa
bfad4de
a7b320c
69d8214
bfad4de
f77befc
bfad4de
3c1606d
99376ba
3c1606d
a8d9610
 
 
a7b320c
a8d9610
 
 
 
bfad4de
 
 
 
 
 
a8d9610
bfad4de
 
3c1606d
 
 
 
 
 
 
 
 
 
f77befc
064e5aa
f77befc
8ae67db
f77befc
 
8ae67db
f77befc
 
8ae67db
f77befc
064e5aa
8ae67db
f77befc
69d8214
3c1606d
bfad4de
3c1606d
0a4b4e5
091e154
 
5883ab3
091e154
 
3c1606d
0a4b4e5
3c1606d
f77befc
48a6e57
 
 
 
 
f77befc
48a6e57
 
 
 
 
f77befc
48a6e57
f77befc
091e154
0a4b4e5
3c1606d
8f4df5d
3c1606d
 
0a4b4e5
467f662
4ba0c26
3c1606d
8f4df5d
f77befc
4ba0c26
f77befc
3c1606d
 
f77befc
0a4b4e5
 
 
bfad4de
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import gradio as gr
from fastmcp import FastMCP
import logging
import requests
from datetime import datetime

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

mcp = FastMCP("Jobicy Canada Remote Jobs Agent")

@mcp.tool(name="search_jobs")
def search_jobs_tool(query: str = "", location: str = "", limit: int = 20) -> dict:
    base_url = "https://jobicy.com/api/v2/remote-jobs"
    params = {}

    if query.strip():
        params["tag"] = query.strip()
    if location.strip() and location.strip().lower() != "anywhere":
        params["geo"] = location.strip().lower()

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/126.0.0.0 Safari/537.36"
    }

    try:
        logger.info(f"Requesting Jobicy API with params: {params}")
        response = requests.get(base_url, params=params, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json()

        jobs_raw = data.get("jobs", [])

        jobs = []
        for job in jobs_raw:
            salary_min = job.get("annualSalaryMin")
            salary_max = job.get("annualSalaryMax")
            currency = job.get("salaryCurrency", "")
            if salary_min and salary_min != 0 and salary_max and salary_max != 0:
                salary_str = f"{salary_min} - {salary_max} {currency}".strip()
            else:
                salary_str = "Not specified"

            jobs.append({
                "title": job.get("jobTitle", "No Title"),
                "company": job.get("companyName", "Unknown Company"),
                "location": job.get("jobGeo", "Remote"),
                "url": job.get("url", "#"),
                "pubDate": job.get("pubDate", "Unknown Date"),
                "salary": salary_str
            })

        def parse_date(job):
            try:
                return datetime.fromisoformat(job["pubDate"].replace("Z", "+00:00"))
            except Exception:
                return datetime.min

        jobs.sort(key=parse_date, reverse=True)
        jobs = jobs[:limit]

        logger.info(f"Found {len(jobs)} jobs from Jobicy after sorting.")
        return {"jobs": jobs}

    except requests.exceptions.HTTPError as http_err:
        logger.error(f"HTTP error: {http_err}")
        return {"error": f"HTTP error occurred: {http_err}"}
    except requests.exceptions.Timeout:
        logger.error("Request timed out.")
        return {"error": "Request timed out. Please try again later."}
    except requests.exceptions.RequestException as req_err:
        logger.error(f"Request error: {req_err}")
        return {"error": f"Request error: {req_err}"}
    except Exception as e:
        logger.error(f"Unexpected error: {str(e)}")
        return {"error": f"Unexpected error: {str(e)}"}

def search_jobs_ui(query="", location="", limit=10):
    limit = int(max(1, min(limit, 50)))
    result = search_jobs_tool(query, location, limit)

    if "error" in result:
        return f"❌ Error: {result['error']}"

    jobs = result.get("jobs", [])
    if not jobs:
        return "No jobs found for your search. Try different keywords or location."

    output = "# Jobicy Remote Job Search Results\n\n"
    for i, job in enumerate(jobs, 1):
        try:
            posted_date = job['pubDate'][:10]
        except Exception:
            posted_date = job['pubDate']

        output += (
            f"### {i}. {job['title']}\n\n"
            f"Company: {job['company']}\n\n"
            f"Location: {job['location']}\n\n"
            f"Salary: {job['salary']}\n\n"
            f"Posted On: {posted_date}\n\n"
            f"[Apply Here]({job['url']})\n\n"
            "---\n\n"
        )
    return output

country_choices = [
    "Anywhere", "canada", "united states", "united kingdom", "australia", "germany", "france", "india"
]

app = gr.Interface(
    fn=search_jobs_ui,
    inputs=[
        gr.Textbox(label="Job Title / Keyword (optional)", placeholder="e.g., Software Engineer"),
        gr.Dropdown(label="Country (optional)", choices=country_choices, value="Anywhere", interactive=True),
        gr.Slider(minimum=1, maximum=50, value=10, step=1, label="Number of Results"),
    ],
    outputs=gr.Markdown(),
    title="Jobicy Remote Job Search",
    description="Search remote jobs by optional job title and country, sorted by most recent postings.",
    theme="huggingface"
)

if __name__ == "__main__":
    app.launch(mcp_server=True)