Spaces:
Running
Running
File size: 7,537 Bytes
0a4b4e5 f77befc 8ae67db bfad4de 3c1606d 8ae67db ded9789 8a5fe3d c77afe4 8a5fe3d c77afe4 8a5fe3d 0a4b4e5 5d20b78 e9525f9 01c0c63 10adf53 90365b1 ded9789 090b8ec ded9789 e9525f9 090b8ec ded9789 10adf53 bbcda92 064e5aa bbcda92 ded9789 064e5aa ded9789 10adf53 ded9789 10adf53 ded9789 01c0c63 bbcda92 e9525f9 ded9789 10adf53 ded9789 d39b588 ded9789 e80edff d39b588 f77befc ded9789 3c1606d 0a4b4e5 467f662 4ba0c26 e80edff 90365b1 4ba0c26 f77befc 3c1606d 5d5017c 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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
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")
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 = [
"", "USA", "Canada", "UK", "China", "APAC", "EMEA", "LATAM", "Argentina", "Australia", "Austria", "Belgium", "Brazil", "Bulgaria",
"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", "Vietnam"
]
@mcp.tool(name="search_jobs")
def search_jobs_tool(industry: str = "", country: str = "", keyword: str = "", limit: int = 20) -> dict:
"""
Search remote jobs from the Jobicy API with optional filters.
Parameters:
industry (str): Company industry to filter jobs by (e.g., 'business', 'design-multimedia').
Leave empty to include all industries.
country (str): Region or country to filter jobs by (e.g., 'usa', 'canada').
Leave empty or 'anywhere' to include all locations.
keyword (str): Keyword or tag to search for in job listings (e.g., 'python', 'data').
Leave empty for no keyword filtering.
limit (int): Number of job results to return, between 1 and 50. Defaults to 20.
Returns:
dict: A dictionary with a "jobs" key containing a list of job dictionaries with:
- title: Job title
- company: Company name
- location: Job location
- url: Application or job posting URL
- pubDate: Date posted (YYYY-MM-DD)
- salary: Salary range or 'Not specified'
Or an "error" key with an error message if the fetch fails.
"""
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}")
session = requests.Session()
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0 Safari/537.36",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Referer": "https://jobicy.com/remote-jobs",
"Origin": "https://jobicy.com",
})
try:
resp = session.get(base, params=params, 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):
"""
Search remote jobs from the Jobicy API with optional filters.
Parameters:
industry (str): Company industry to filter jobs by (e.g., 'business', 'design-multimedia').
Leave empty to include all industries.
country (str): Region or country to filter jobs by (e.g., 'usa', 'canada').
Leave empty or 'anywhere' to include all locations.
keyword (str): Keyword or tag to search for in job listings (e.g., 'python', 'data').
Leave empty for no keyword filtering.
limit (int): Number of job results to return, between 1 and 50. Defaults to 20.
Returns:
dict: A dictionary with a "jobs" key containing a list of job dictionaries with:
- title: Job title
- company: Company name
- location: Job location
- url: Application or job posting URL
- pubDate: Date posted (YYYY-MM-DD)
- salary: Salary range or 'Not specified'
Or an "error" key with an error message if the fetch fails.
"""
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):
search_query = f"{j['title']} {j['company']}"
search_url = f"https://www.google.com/search?q={requests.utils.quote(search_query)}"
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 / More Info]({j['url']}) \n"
f"🔍 [Google Search]({search_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.
The Apply Now in the Jobicy is only for Paid accounts.
If you don't have one, just click the Google Search to search the job link in Google and apply there.""",
theme="huggingface"
)
if __name__ == "__main__":
app.launch(mcp_server=True)
|