Skip to main content
Glama

JobApply MCP Server

by Sakshee5
main.py7.4 kB
from mcp.server.fastmcp import FastMCP import requests from bs4 import BeautifulSoup from docx import Document import PyPDF2 import os from duckduckgo_search import DDGS # Create the MCP server mcp = FastMCP("JobApply") @mcp.tool() def scrape_job_posting(url: str) -> str: """ Extracts job description text from the given job posting URL by stripping out scripts/styles and returning the raw textual content of the page. """ try: response = requests.get(url) response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') for tag in soup(['script', 'style']): tag.decompose() text = soup.get_text(separator=' ', strip=True) return text[:5000] # cap length for LLMs except requests.exceptions.RequestException as e: return f"Error fetching URL: {e}" @mcp.tool() def read_resume_pdf(file_path: str) -> str: """ Reads and extracts plain text from a PDF resume. Useful for downstream processing like ATS scoring and cover letter generation. """ try: with open(file_path, 'rb') as file: reader = PyPDF2.PdfReader(file) text = "".join(page.extract_text() or "" for page in reader.pages) return text[:5000] except Exception as e: return f"Error reading PDF: {str(e)}" @mcp.tool() def read_document(file_path: str) -> str: """ Reads .txt or .docx documents and returns the content as plain text. Unsupported formats are rejected. """ try: file_ext = os.path.splitext(file_path)[1].lower() if file_ext == '.txt': with open(file_path, 'r', encoding='utf-8') as file: return file.read() elif file_ext == '.docx': doc = Document(file_path) return '\n'.join([paragraph.text for paragraph in doc.paragraphs]) else: return f"Unsupported file format: {file_ext}" except Exception as e: return f"Error reading document: {str(e)}" @mcp.tool() def save_cover_letter(content: str, output_path: str) -> str: """ Saves a generated cover letter string to a Word (.docx) file at the specified path. """ try: doc = Document() doc.add_paragraph(content) doc.save(output_path) return f"Cover letter saved successfully to {output_path}" except Exception as e: return f"Error saving cover letter: {str(e)}" @mcp.tool() def calculate_ats_score(job_desc: str, resume_text: str) -> dict: """ Performs a basic ATS-style keyword match between the job description and resume. Returns a score and a list of missing keywords. """ import re from collections import Counter job_keywords = re.findall(r'\b\w+\b', job_desc.lower()) resume_keywords = re.findall(r'\b\w+\b', resume_text.lower()) job_counts = Counter(job_keywords) resume_counts = Counter(resume_keywords) matched = sum((min(job_counts[k], resume_counts[k]) for k in job_counts)) total = sum(job_counts.values()) coverage = matched / total if total else 0 missing = [k for k in job_counts if k not in resume_counts] return { "ats_score": round(coverage * 100, 2), "missing_keywords": missing[:20] } @mcp.tool() def company_research(search_query: str, max_results=10) -> str: results = [] with DDGS() as ddgs: for r in ddgs.text(search_query, max_results=max_results): results.append({ "title": r.get("title"), "href": r.get("href"), "body": r.get("body") }) return results @mcp.prompt() def generate_search_query(job_desc: str) -> str: """ Generates targeted search queries to help gather insights about the company's mission, recent updates, team structure, and relevant projects or products. These queries are intended to enrich a tailored cover letter. """ return f"""You are writing a cover letter and want to include relevant and insightful information about the company and team to demonstrate alignment and genuine interest. Given the job description below, write 3–5 concise and relevant search queries that would help you find: - The company’s mission, recent news, and culture - Details about the team or department mentioned - Notable products, projects, or initiatives relevant to the role Make sure the queries are phrased in a way that would return valuable results on Google or Brave Search. Avoid generic queries or anything about interview experiences. Job Description: {job_desc} Example queries: - "OpenAI product roadmap 2024" - "OpenAI research scientist team culture" - "OpenAI recent news site:techcrunch.com" - "OpenAI transformer interpretability team" - "OpenAI employee blog OR careers page" Now write similar search queries for the company in the job description above. """ @mcp.prompt() def resume_enhancer(job_desc: str, resume_content: str) -> str: """ Prompt for the LLM to suggest edits to the resume that improve alignment with the job description while staying truthful and not overly exaggerated. """ return f"""Improve the resume by aligning it with the job description below. Ensure edits remain truthful and relevant without over-exaggeration. Focus on measurable impact, ATS-friendly phrasing, and logical alignment. Job Description: {job_desc} Resume: {resume_content} """ @mcp.prompt() def gap_finder(job_desc: str, resume_content: str) -> str: """ Prompt for finding skill or experience gaps between resume and job description. Useful for suggesting upskilling paths or additional context in applications. """ return f"""Compare the following job description and resume. Identify specific gaps in skills, experience, or qualifications. Output a list of gaps and possible ways the user can bridge them (e.g., certifications, side projects, learning goals): Job Description: {job_desc} Resume: {resume_content} """ @mcp.prompt() def generate_cover_letter(job_desc: str, resume_context: str = "", profile_context: str = "", company_context: str = "") -> str: """ Prompt to generate a short, logically structured, personalized cover letter that integrates resume highlights, personal context, and company-specific insights. """ return f"""Write a concise, compelling cover letter for the job described below. Use the user's resume context, profile background, and any company-specific research to personalize the letter. Make the tone professional yet conversational. Job Description: {job_desc} Resume Highlights: {resume_context} User Profile / Goals: {profile_context} Company Context / News / Team: {company_context} """ def main(): print("Starting JobApply MCP Server...") # mcp.run() jd = scrape_job_posting(url='https://ats.rippling.com/chartmetric/jobs/1656afd4-46d4-4478-a881-1cdfbac03819') print(f"Job Desc:/n{jd}\n\n") resume = read_resume_pdf(file_path="C:/Users/saksh/Downloads/Sakshee_Patil_Resume.pdf") print(f"Resume:\n{resume}\n\n") ats_score = calculate_ats_score(jd, resume) print(f"ATS Score:\n{ats_score}\n\n") cr = company_research("Chartmetric AI Initiatives") print(f"Company Research:\n{cr}\n\n") if __name__ == "__main__": main()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Sakshee5/JobApply-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server