PROJECT_PLAN.md Loading last commit info...
PROJECT_PROPOSAL.md
README.md
README.md

AI Job Match — MCP Server + Dashboard

An AI-powered job search system made of two parts:

  1. MCP Server (search-jobs-mcp) — exposes job-search tools (web search, storage, listing, deletion) to any MCP-compatible LLM client.
  2. Client / Dashboard App — where a user uploads a resume, gets an AI-generated job profile, and sees ranked job matches they can apply to.

The LLM sits in the middle: it reads the resume, builds a profile, calls the MCP tools to search and store jobs, and scores each job against the profile.


1. How It Works (End-to-End Flow)

flowchart TD A[User uploads resume] --> B[Resume Parser<br/>PDF/DOCX to text] B --> C[LLM: Build Job Profile<br/>title, skills, seniority, location, salary] C --> D[LLM: Extract Search Keywords<br/>from profile] D --> E[MCP Tool: search_jobs<br/>role, location, limit, freshness] E --> F[LLM: Score Each Job<br/>vs profile 0-100] F --> G[MCP Tool: store_job<br/>save good matches] G --> H[MCP Tool: list_jobs<br/>fetch saved jobs] H --> I[Dashboard: Ranked Job Listing] I --> J[User clicks Apply] J --> K[Redirect to job posting /<br/>track application status]

Step by step:

  1. Upload — user uploads a resume (PDF/DOCX) through the dashboard.
  2. Parse — backend extracts raw text from the resume.
  3. Profile generation — the LLM reads the resume text and produces a structured job_profile (skills, years of experience, target roles, location preference, seniority, etc.). This is exposed back through the MCP job_profile tool so any MCP client can fetch the current profile.
  4. Keyword extraction — the LLM derives a short list of high-signal search keywords from the profile (e.g. "senior backend engineer", "Node.js", "remote").
  5. Job search — the LLM calls search_jobs (LangSearch Web Search API under the hood) using those keywords, plus location/limit/freshness filters.
  6. Scoring — the LLM compares each returned job posting against the profile and assigns a match score (skills overlap, seniority fit, location fit, etc.).
  7. Storage — jobs above a score threshold are saved via store_job as structured JSON.
  8. Listing — the dashboard calls list_jobs to show saved jobs, most recently updated first, with their score.
  9. Apply — user clicks "Apply," which opens the original posting URL (and optionally logs an "applied" status locally).
  10. Cleanupdelete_job lets the user (or the LLM) remove stale/irrelevant postings.

2. MCP Server — search-jobs-mcp

A small FastMCP server that gives an LLM access to job-search tools. It searches the web for current job postings via the LangSearch Web Search API, stores useful job records in a local JSON file, and supports listing/deleting saved jobs.

Tool Overview

ToolPurpose
job_profileReturns the candidate profile the LLM uses for matching and ranking jobs.
search_jobsSearches the web for current job postings by role, location, limit, and freshness.
store_jobSaves a job posting as structured JSON in local storage.
list_jobsLists saved jobs, sorted by most recently updated first.
delete_jobDeletes a saved job by id.

Suggested Tool Schemas

// job_profile — no input, returns current profile
{
  "name": "job_profile",
  "output": {
    "target_titles": ["string"],
    "skills": ["string"],
    "years_experience": "number",
    "seniority": "junior | mid | senior | lead",
    "location_preference": "string",
    "remote_ok": "boolean",
    "salary_expectation": "string | null",
    "summary": "string"
  }
}

// search_jobs
{
  "name": "search_jobs",
  "input": {
    "role": "string",
    "location": "string | null",
    "limit": "number (default 10)",
    "freshness": "day | week | month"
  },
  "output": [
    {
      "title": "string",
      "company": "string",
      "location": "string",
      "url": "string",
      "posted_at": "string",
      "description_snippet": "string"
    }
  ]
}

// store_job
{
  "name": "store_job",
  "input": {
    "id": "string",
    "title": "string",
    "company": "string",
    "location": "string",
    "url": "string",
    "score": "number",
    "matched_keywords": ["string"],
    "status": "new | applied | rejected | saved"
  }
}

// list_jobs — no required input
{
  "name": "list_jobs",
  "output": [ /* array of stored job objects, sorted by updated_at desc */ ]
}

// delete_job
{
  "name": "delete_job",
  "input": { "id": "string" }
}

Local Storage

Jobs are stored in a local JSON file (e.g. data/jobs.json) as a simple array or map keyed by job id. This is fine for MVP; see the Roadmap section for moving to a real database.

Environment Variables

LANGSEARCH_API_KEY=your_key_here
JOBS_STORAGE_PATH=./data/jobs.json
MCP_SERVER_PORT=8000

Running the Server

# install dependencies
pip install -r requirements.txt   # or: uv sync

# run the FastMCP server
python server.py

3. Client / Dashboard App

A web app the user interacts with directly.

Features

  • Resume upload — drag-and-drop PDF/DOCX upload.
  • AI job profile — auto-generated profile shown to the user (editable, since AI extraction won't always be perfect).
  • Keyword view — shows the keywords the AI is using to search, editable before running a search.
  • Job listing dashboard — ranked list of matching jobs with score, company, location, and a short "why it matches" explanation.
  • Apply flow — one-click apply (opens posting or an internal application tracker).
  • Saved / applied / rejected states — simple status tracking per job.
  • Re-run search — refresh matches on demand or on a schedule.

Suggested Tech Stack

LayerOption
FrontendReact (Next.js) or plain React + Tailwind
Backend / APINode.js (Express/Fastify) or Python (FastAPI)
LLM orchestrationAnthropic API (Claude) as MCP client
MCP transportHTTP/SSE to search-jobs-mcp
Resume parsingpdf-parse / mammoth (docx) or a hosted parser
Storage (MVP)Local JSON (matches MCP server)
Storage (production)PostgreSQL or SQLite + object storage for resumes
AuthClerk/Auth0/simple JWT, depending on scale

Suggested API Routes (Backend)

POST   /api/resume/upload        -> parses resume, triggers profile generation
GET    /api/profile               -> returns current job_profile (proxies MCP tool)
PUT    /api/profile               -> lets user edit the AI-generated profile
POST   /api/jobs/search           -> triggers search_jobs + scoring, then store_job
GET    /api/jobs                  -> proxies list_jobs for the dashboard
POST   /api/jobs/:id/apply        -> marks job as "applied", opens posting URL
DELETE /api/jobs/:id               -> proxies delete_job

4. Repository Structure (Suggested)

project-root/
├── mcp-server/
│   ├── server.py
│   ├── tools/
│   │   ├── job_profile.py
│   │   ├── search_jobs.py
│   │   ├── store_job.py
│   │   ├── list_jobs.py
│   │   └── delete_job.py
│   ├── data/
│   │   └── jobs.json
│   └── requirements.txt
├── dashboard/
│   ├── src/
│   │   ├── pages/ (or app/)
│   │   ├── components/
│   │   └── lib/mcp-client.ts
│   └── package.json
├── docs/
│   ├── README.md
│   └── PROJECT_PLAN.md
└── .env.example

5. Roadmap (Post-MVP)

  • Move from JSON file storage to Postgres/SQLite.
  • Add authentication and multi-user support.
  • Add background job for scheduled re-searches (daily/weekly).
  • Add email/notification alerts for new high-score matches.
  • Add application tracking with status pipeline (Applied → Interview → Offer → Rejected).
  • Add resume improvement suggestions based on job description gaps.
  • Add analytics (application-to-response rate, score accuracy over time).

6. Disclaimer

This project searches and aggregates publicly available job postings. Always confirm postings are legitimate and current before applying, and respect the terms of service of any source site the search results link to.

Please wait...
Connection lost or session expired, reload to recover
Page is in error, reload to recover