File size: 4,935 Bytes
0a4b4e5
f77befc
8ae67db
bfad4de
3c1606d
8ae67db
 
 
ded9789
 
e9525f9
8a5fe3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0f28047
8a5fe3d
 
 
 
 
 
0a4b4e5
5d20b78
e9525f9
2a710fe
10adf53
2a710fe
 
10adf53
 
e9525f9
90365b1
2a710fe
 
10adf53
2a710fe
10adf53
90365b1
ded9789
090b8ec
ded9789
 
e9525f9
090b8ec
ded9789
10adf53
064e5aa
10adf53
ded9789
 
064e5aa
ded9789
 
10adf53
ded9789
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10adf53
ded9789
e9525f9
 
ded9789
 
 
10adf53
 
ded9789
 
 
 
e80edff
 
 
 
ded9789
f77befc
ded9789
3c1606d
0a4b4e5
467f662
4ba0c26
e80edff
 
 
90365b1
4ba0c26
f77befc
3c1606d
e9525f9
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
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 Remote Jobs Agent")

# Remaining filter options
COMPANY_INDUSTRIES = [
    ("", ""),
    ("Business Development", "business"),
    ("Content & Editorial", "copywriting"),
    ("Creative & Design", "design-multimedia"),
    ("Customer Success", "supporting"),
    ("Data Science & Analytics", "data-science"),
    ("DevOps & Infrastructure", "admin"),
    ("Finance & Accounting", "accounting-finance"),
    ("HR & Recruiting", "hr"),
    ("Legal & Compliance", "legal"),
    ("Marketing & Sales", "marketing"),
    ("Product & Operations", "management"),
    ("Programming", "dev"),
    ("Sales", "seller"),
    ("SEO", "seo"),
    ("Social Media Marketing", "smm"),
    ("Software Engineering", "engineering"),
    ("Technical Support", "technical-support"),
    ("Web & App Design", "web-app-design"),
]
COUNTRY_CHOICES = [
    "", "APAC", "EMEA", "LATAM", "Argentina", "Australia", "Austria", "Belgium", "Brazil", "Bulgaria",
    "Canada", "China", "Costa Rica", "Croatia", "Cyprus", "Czechia", "Denmark", "Estonia", "Europe",
    "Finland", "France", "Germany", "Greece", "Hungary", "Ireland", "Israel", "Italy", "Japan",
    "Latvia", "Lithuania", "Mexico", "Netherlands", "New Zealand", "Norway", "Philippines", "Poland",
    "Portugal", "Romania", "Singapore", "Slovakia", "Slovenia", "South Korea", "Spain", "Sweden",
    "Switzerland", "Thailand", "Türkiye", "UAE", "UK", "USA", "Vietnam"
]

@mcp.tool(name="search_jobs")
def search_jobs_tool(industry: str = "", country: str = "", keyword: str = "", limit: int = 20) -> dict:
    """
    Search remote jobs via Jobicy API, supports filters.

    Args:
      industry (str): Company industry filter.
      country (str): Region filter (e.g., usa, canada, ...).
      keyword (str): Free-form keyword tag.
      limit (int): Number of results (1–50).

    Returns:
      dict: {"jobs": [...]} or {"error": "..." }
    """
    base = "https://jobicy.com/api/v2/remote-jobs"
    params = {"count": max(1, min(limit, 50))}
    if industry:
        params["industry"] = industry.lower().replace(" & ", "-").replace(" ", "-")
    if country and country.lower() != "anywhere":
        params["geo"] = country.lower()
    if keyword:
        params["tag"] = keyword.strip()

    logger.info(f"Requesting Jobicy API with params: {params}")
    try:
        resp = requests.get(base, params=params, headers={"User-Agent": "Mozilla/5.0"}, timeout=10)
        resp.raise_for_status()
        jobs_raw = resp.json().get("jobs", [])
    except Exception as e:
        return {"error": f"Fetch error: {e}"}

    def fmt(j):
        posted = j.get("pubDate", "")[:10]
        sal_min = j.get("annualSalaryMin")
        sal_max = j.get("annualSalaryMax")
        cur = j.get("salaryCurrency", "")
        salary = f"{sal_min}{sal_max} {cur}".strip() if sal_min or sal_max else "Not specified"
        return {
            "title": j.get("jobTitle", "No Title"),
            "company": j.get("companyName", ""),
            "location": j.get("jobGeo", ""),
            "url": j.get("url", "#"),
            "pubDate": posted,
            "salary": salary
        }

    sorted_jobs = sorted(jobs_raw, key=lambda x: x.get("pubDate", ""), reverse=True)[:params["count"]]
    return {"jobs": [fmt(j) for j in sorted_jobs]}

def search_jobs_ui(industry, country, keyword, limit):
    res = search_jobs_tool(industry=industry, country=country, keyword=keyword, limit=limit)
    if "error" in res:
        return f"❌ {res['error']}"
    if not res["jobs"]:
        return "No jobs found with these filters."

    md = "# Remote Jobs Results\n\n"
    for i, j in enumerate(res["jobs"], 1):
        md += (
            f"### {i}. {j['title']}\n\n"
            f"**Company:** {j['company']}\n\n"
            f"**Location:** {j['location']}\n\n"
            f"**Salary:** {j['salary']}\n\n"
            f"**Posted On:** {j['pubDate']}\n\n"
            f"[Apply Here]({j['url']})\n\n---\n\n"
        )
    return md

app = gr.Interface(
    fn=search_jobs_ui,
    inputs=[
        gr.Dropdown(label="Company Industry (optional) (Empty = All)", choices=COMPANY_INDUSTRIES, value=""),
        gr.Dropdown(label="Country / Region (optional) (Empty = Anywhere)", choices=COUNTRY_CHOICES, value=""),
        gr.Textbox(label="Keyword / Tag (optional)", placeholder="e.g., python, data, web"),
        gr.Slider(minimum=1, maximum=50, value=20, step=1, label="Number of Results"),
    ],
    outputs=gr.Markdown(),
    title="Jobicy Remote Job Search",
    description="Search remote jobs by industry, region, and keyword. Results sorted by most recent.",
    theme="huggingface"
)

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