Spaces:
Running
Running
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)
|