mcp / app.py
Anish Dahiya
Optimize for CPU basic 2 vCPU 16GB RAM hardware on Hugging Face Spaces
3fb995f
Raw
History Blame Contribute Delete
10.1 kB
import os
# Disable experimental Gradio Node.js SSR proxy on Hugging Face Spaces
os.environ["GRADIO_SSR_MODE"] = "false"
os.environ["GRADIO_SERVER_NAME"] = "0.0.0.0"
os.environ["GRADIO_SERVER_PORT"] = "7860"
import sys
import json
import gradio as gr
import pandas as pd
import plotly.express as px
# Ensure tech_radar package is importable
sys.path.insert(0, os.path.abspath("."))
from tech_radar.db.database import DatabaseManager
from tech_radar.db.vector_store import SemanticVectorStore
from tech_radar.agents.evaluator_agent import EvaluatorAgent
from tech_radar.agents.market_analyst import MarketAnalystAgent
from tech_radar.scrapers.seeder import seed_database
from tech_radar.mcp.tools import (
tool_search_tech_jobs,
tool_analyze_skill_gap,
tool_generate_resume_patch,
tool_get_market_insights,
tool_generate_interview_prep_kit
)
# Initialize DB & Vector Engine
db, vector_store = seed_database(db_path="tech_radar.db")
evaluator = EvaluatorAgent()
market_analyst = MarketAnalystAgent(db)
# Load 3D Cyber Web App HTML
web_html_path = os.path.join("tech_radar", "ui", "web_app.html")
web_html_content = ""
if os.path.exists(web_html_path):
with open(web_html_path, "r", encoding="utf-8") as f:
web_html_content = f.read()
# Gradio Callbacks
def search_jobs_fn(domain, city, query, min_exp):
if query:
semantic_results = vector_store.search_semantic(query=query, domain=domain, city=city, top_k=30)
jobs_list = [j for j, score in semantic_results]
else:
all_jobs = db.search_jobs(domain=domain, city=city, limit=200)
jobs_list = [j for j in all_jobs if j.experience_min_years <= min_exp]
data = []
for j in jobs_list:
data.append({
"Job ID": j.id,
"Title": j.title,
"Company": j.company,
"Domain": j.tech_domain,
"City": j.city,
"Salary (LPA)": f"₹{j.salary_min_lpa}L - ₹{j.salary_max_lpa}L",
"Exp": f"{j.experience_min_years}-{j.experience_max_years} Yrs",
"Work Mode": j.work_mode,
"Tech Stack": ", ".join(j.tech_stack)
})
return pd.DataFrame(data)
def market_analytics_fn(domain, city):
insights = market_analyst.generate_market_report(city=city, domain=domain)
df_frameworks = pd.DataFrame(insights.top_demanded_frameworks)
if not df_frameworks.empty:
fig = px.bar(
df_frameworks,
x="percentage",
y="skill",
orientation="h",
title=f"Top Skill Demand % in {city} ({domain})",
labels={"percentage": "Demand %", "skill": "Framework"},
color="percentage",
color_continuous_scale="Purples"
)
fig.update_layout(template="plotly_dark", yaxis={'categoryorder':'total ascending'})
else:
fig = px.bar(title="No data available")
summary_md = f"""
### 📊 Hiring Summary for {city} ({domain})
- **Total Active Jobs Tracked**: `{insights.total_active_jobs}`
- **Average Salary**: `{insights.avg_salary_lpa} LPA`
- **Salary Range**: `{insights.salary_range}`
- **Market Trend**: {insights.growth_trend}
**Top Employers Hiring**: {', '.join(insights.top_employers)}
"""
return summary_md, fig
def evaluate_ats_fn(resume_text, job_id):
job = db.get_job_by_id(job_id)
if not job:
return "Job ID not found", "", "", "", ""
report = evaluator.evaluate_skill_gap(resume_text=resume_text, candidate_skills=[], job=job)
patch = evaluator.generate_resume_patch(resume_text=resume_text, job=job)
prep = evaluator.generate_interview_prep(job=job)
matched_str = ", ".join(report.matched_skills) or "None"
missing_str = ", ".join(report.missing_skills) or "None"
bullets_md = ""
for b in patch.tailored_bullets:
bullets_md += f"- **Original**: {b['original']}\n - **Tailored**: `{b['tailored']}`\n"
prep_md = ""
for q in prep.technical_questions:
prep_md += f"**Q ({q.category})**: {q.question}\n- *Key Answer Points*: {', '.join(q.ideal_answer_points)}\n\n"
return f"{report.match_percentage}%", matched_str, missing_str, bullets_md, prep_md
def execute_mcp_tool_fn(tool_name, domain, city, query, job_id, resume_text):
if tool_name == "search_tech_jobs":
res = tool_search_tech_jobs(domain=domain, city=city, query=query)
elif tool_name == "analyze_skill_gap":
res = tool_analyze_skill_gap(resume_text=resume_text, target_job_id=job_id)
elif tool_name == "generate_tailored_resume_patch":
res = tool_generate_resume_patch(resume_text=resume_text, target_job_id=job_id)
elif tool_name == "get_market_insights":
res = tool_get_market_insights(domain=domain, city=city)
elif tool_name == "generate_interview_prep_kit":
res = tool_generate_interview_prep_kit(target_job_id=job_id)
else:
res = json.dumps({"error": "Unknown tool"})
try:
return json.dumps(json.loads(res), indent=2)
except:
return res
# Build Gradio UI
theme = gr.themes.Soft(
primary_hue="cyan",
secondary_hue="purple",
neutral_hue="slate"
)
with gr.Blocks(theme=theme, title="TechRadar MCP — Universal Tech Hiring Intelligence") as demo:
gr.Markdown("""
# ⚡ TechRadar MCP — Universal Tech Hiring Intelligence
### Autonomous Model Context Protocol Ecosystem across Indian Tech Hubs & Remote
""")
with gr.Tabs():
with gr.TabItem("📡 Universal Tech Job Radar"):
with gr.Row():
domain_dropdown = gr.Dropdown(["All", "Backend Engineering", "Frontend Engineering", "Full Stack Engineering", "Cloud & DevOps", "Data Engineering", "AI/ML & GenAI", "Mobile Engineering"], value="All", label="Tech Domain")
city_dropdown = gr.Dropdown(["All", "Bengaluru", "Pune", "Hyderabad", "Gurgaon", "Mumbai", "Chennai", "Remote"], value="All", label="City / Region")
query_input = gr.Textbox(placeholder="Search Go, React, vLLM...", label="Semantic Search")
exp_slider = gr.Slider(0, 12, value=10, step=1, label="Max Experience (Yrs)")
search_btn = gr.Button("🔍 Search Tech Jobs", variant="primary")
job_table = gr.Dataframe(label="Matching Active Roles")
search_btn.click(
fn=search_jobs_fn,
inputs=[domain_dropdown, city_dropdown, query_input, exp_slider],
outputs=job_table
)
with gr.TabItem("📊 Market Analytics"):
with gr.Row():
an_domain = gr.Dropdown(["All", "Backend Engineering", "Frontend Engineering", "Cloud & DevOps", "AI/ML & GenAI"], value="All", label="Domain")
an_city = gr.Dropdown(["All", "Bengaluru", "Pune", "Hyderabad", "Gurgaon", "Remote"], value="All", label="City")
an_btn = gr.Button("📊 Generate Market Report", variant="primary")
with gr.Row():
an_summary = gr.Markdown()
an_chart = gr.Plot()
an_btn.click(
fn=market_analytics_fn,
inputs=[an_domain, an_city],
outputs=[an_summary, an_chart]
)
with gr.TabItem("🎯 ATS Resume & Skill Gap Evaluator"):
all_jobs = db.get_all_jobs()
job_ids = [j.id for j in all_jobs]
with gr.Row():
with gr.Column():
resume_input = gr.Textbox(
value="Senior Backend Engineer with 4 years experience building Go microservices, REST APIs, Docker, and PostgreSQL in Bengaluru.",
lines=8,
label="Paste Candidate Resume Text"
)
job_id_select = gr.Dropdown(job_ids, value=job_ids[0] if job_ids else "", label="Select Target Job ID")
eval_btn = gr.Button("🚀 Analyze ATS Match & Skill Gap", variant="primary")
with gr.Column():
match_score_out = gr.Textbox(label="Match Percentage")
matched_skills_out = gr.Textbox(label="Matched Skills")
missing_skills_out = gr.Textbox(label="Missing Dealbreaker Skills")
bullets_out = gr.Markdown(label="ATS Tailored Resume Bullets")
prep_out = gr.Markdown(label="Technical Interview Q&A Prep")
eval_btn.click(
fn=evaluate_ats_fn,
inputs=[resume_input, job_id_select],
outputs=[match_score_out, matched_skills_out, missing_skills_out, bullets_out, prep_out]
)
with gr.TabItem("🧪 FastMCP Server Tester"):
with gr.Row():
with gr.Column():
tool_select = gr.Dropdown(["search_tech_jobs", "analyze_skill_gap", "generate_tailored_resume_patch", "get_market_insights", "generate_interview_prep_kit"], value="search_tech_jobs", label="Select MCP Tool")
mcp_domain = gr.Textbox(value="Backend Engineering", label="domain (if search/insights)")
mcp_city = gr.Textbox(value="Bengaluru", label="city (if search/insights)")
mcp_query = gr.Textbox(value="Go Distributed Systems", label="query (if search)")
mcp_job_id = gr.Textbox(value=job_ids[0] if job_ids else "BLR-BACKEND-101", label="target_job_id")
mcp_resume = gr.Textbox(value="Go & Docker developer", label="resume_text")
exec_mcp_btn = gr.Button("⚡ Execute FastMCP Tool", variant="primary")
with gr.Column():
mcp_output_json = gr.Code(language="json", label="JSON-RPC Tool Response")
exec_mcp_btn.click(
fn=execute_mcp_tool_fn,
inputs=[tool_select, mcp_domain, mcp_city, mcp_query, mcp_job_id, mcp_resume],
outputs=mcp_output_json
)
# Launch Gradio App
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)