from __future__ import annotations from fastmcp import FastMCP from sqlalchemy import select from core.db import SessionLocal from core.models import ApplicationStatus DEFAULT_STAGES = [ "Applied", "Screening", "Shortlisted", "Interview Scheduled", "Technical Round", "Final Interview", "Offered", "Hired", "Rejected", ] def register(mcp: FastMCP) -> None: @mcp.tool() def manage_application_status( mode: str, candidate_id: str | None = None, candidate_name: str | None = None, role_applied: str | None = None, stage: str | None = None, status: str | None = None, notes: str | None = None, ) -> dict: """Create, update, and retrieve application statuses for candidates.""" normalized_mode = mode.strip().lower() with SessionLocal() as db: if normalized_mode in {"upsert", "update"}: if not candidate_id: return {"status": "error", "message": "candidate_id is required for upsert."} existing = db.execute( select(ApplicationStatus).where(ApplicationStatus.candidate_id == candidate_id) ).scalar_one_or_none() if existing is None: existing = ApplicationStatus(candidate_id=candidate_id) db.add(existing) if candidate_name is not None: existing.candidate_name = candidate_name if role_applied is not None: existing.role_applied = role_applied if stage is not None: existing.stage = stage if status is not None: existing.status = status if notes is not None: existing.notes = notes db.commit() db.refresh(existing) return { "status": "success", "message": "Application status upserted.", "record": { "candidate_id": existing.candidate_id, "candidate_name": existing.candidate_name, "role_applied": existing.role_applied, "stage": existing.stage, "status_text": existing.status, "notes": existing.notes, }, } if normalized_mode == "advance": if not candidate_id: return {"status": "error", "message": "candidate_id is required to advance stage."} existing = db.execute( select(ApplicationStatus).where(ApplicationStatus.candidate_id == candidate_id) ).scalar_one_or_none() if existing is None: return {"status": "not_found", "message": "Candidate application not found."} current_stage = existing.stage or "Applied" if current_stage not in DEFAULT_STAGES: existing.stage = "Applied" else: idx = DEFAULT_STAGES.index(current_stage) next_idx = min(idx + 1, len(DEFAULT_STAGES) - 1) existing.stage = DEFAULT_STAGES[next_idx] if notes: existing.notes = notes db.commit() db.refresh(existing) return { "status": "success", "message": "Application stage advanced.", "record": { "candidate_id": existing.candidate_id, "stage": existing.stage, "status_text": existing.status, "notes": existing.notes, }, } if normalized_mode == "get": if not candidate_id: return {"status": "error", "message": "candidate_id is required for get."} existing = db.execute( select(ApplicationStatus).where(ApplicationStatus.candidate_id == candidate_id) ).scalar_one_or_none() if existing is None: return {"status": "not_found", "message": "Candidate application not found."} return { "status": "success", "record": { "candidate_id": existing.candidate_id, "candidate_name": existing.candidate_name, "role_applied": existing.role_applied, "stage": existing.stage, "status_text": existing.status, "notes": existing.notes, }, } if normalized_mode == "list": rows = db.execute( select(ApplicationStatus).order_by(ApplicationStatus.updated_at.desc()).limit(50) ).scalars().all() records = [ { "candidate_id": row.candidate_id, "candidate_name": row.candidate_name, "role_applied": row.role_applied, "stage": row.stage, "status_text": row.status, } for row in rows ] return { "status": "success", "count": len(records), "records": records, } return { "status": "error", "message": "Unsupported mode. Use upsert, update, advance, get, or list.", }