job-search-mcp
{"answer": "This job-search MCP server helps you analyze job descriptions against your resume and track fit-analysis results — it does not generate or tailor resumes.\n\n### Key capabilities\n\n- Match a job description to your resume (match_job): Embeds a job description and retrieves the most relevant resume/experience chunks from a vector store (Qdrant) via similarity search. Returns a heuristic retrieval_score (top-match cosine similarity), the retrieved evidence chunks with individual similarity scores, and the original job description. No internal LLM calls — the calling assistant reasons over the evidence using the provided job-fit://rubric resource.\n\n- Push a fit verdict to your tracker (push_to_tracker): Writes a FitVerdict (Strong/Good/Possible/Weak/Not a Fit, with domain match, scope match, preference severity, gate failures, red flags, rationale, and demotion count) to an existing row in your Notion tracking database. Only touches tool-populated fields defined in tracking_schema.yaml, skips misconfigured fields with warnings, and supports dry_run to preview the payload without writing.\n\n- Find or create a tracked application (find_or_create_application): Look up an existing row by exact company+role match, or create a new one, returning {job_id, created}.\n\n- Update application status (update_status): Set the Status field on an existing tracked row for lifecycle events (Applied, Rejected, Interviewing, Offer, etc.), completely decoupled from FitVerdict.\n\n- List tracked applications (list_applications): Retrieve tracked jobs with job_id and tool-populated fields (status, fit rating, notes). Optionally filter by exact case-sensitive status (e.g., \"Not yet applied\") to quickly surface jobs in a specific stage.\n\n- Ingest your resume: Accepts local text/markdown, PDF, or DOCX files, which are split into section/role-sized chunks, embedded with a local sentence-transformers model, and stored in the vector store for retrieval.\n\n### Design highlights\n\n- Schema-driven tracking: Which fields are read/written is controlled by a user-maintained tracking_schema.yaml, not hardcoded to a specific Notion database layout.\n- Adapter-based architecture: Flexible backends for vector storage (Qdrant), tracking stores (Notion), and resume sources.\n- Job ID ownership verification: push_to_tracker and update_status verify the job_id belongs to your configured Notion database before writing.\n- Misconfigured fields are skipped with warnings, not hard failures — check the warnings field in results."}
Planned ResumeSource implementation for retrieving resume/experience content from Google Drive (listed on the roadmap, not yet shipped).
Provides tracking store integration for persisting and listing job fit-analysis results in Notion, including writing FitVerdict data to tracked rows and listing applications with their status and fit ratings.
Provides a zero-dependency local tracking store implementation for persisting fit-analysis results, reading the same configurable tracking field schema as the Notion backend.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@job-search-mcpmatch this job description to my resume and show me the top evidence"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
job-search-mcp
An MCP server that retrieves resume/experience evidence relevant to a job description via RAG, and tracks fit-analysis results in a tracking store.
Analysis and tracking only — this project does not generate or tailor
resumes. See docs/adr/0007-analysis-only-v1-no-resume-generation.md.
Design
The server is adapter-based rather than locked to any specific vendor. Three interfaces define the boundaries:
VectorStore(src/job_search_mcp/vector_store/) — embedded resume/experience chunk storage and similarity search. Default implementation:QdrantVectorStore(Cloud or self-hosted-in-Docker — a connection detail, not an interface difference).TrackingStore(src/job_search_mcp/tracking_store/) — persistence for fit-analysis results.NotionTrackingStoreis the store the server wires up today (see Setup).SQLiteTrackingStoreis a zero-dependency local implementation that also exists but isn't yet selectable via config — see Roadmap. Both read a user-declaredtracking_schema.yaml(docs/adr/0011-configurable-tracking-field-schema.md) for which fields to write and read, rather than a mapping hardcoded to one specific Notion database.ResumeSource(src/job_search_mcp/resume_source/) — retrieval of resume/experience content. Implementation:FileResumeSource(local text/markdown, PDF, and DOCX parsing).
Rationale for each of these decisions is recorded in docs/adr/.
Embeddings (src/job_search_mcp/embeddings/) default to a local
SentenceTransformersEmbedder (all-MiniLM-L6-v2) — see
docs/adr/0006-embeddings-choice-open.md.
Related MCP server: Job Application MCP
Stack
Python, dependency management via
uvjustas the task runner (seejustfile)Qdrant (vector store), Notion (tracking store)
MCP Python SDK for the server itself
MCP tools
match_job(job_description, source_url=None)— embeds the job description, retrieves the most relevant resume/experience chunks from the configuredVectorStore, and returns a heuristicretrieval_score(top-match cosine similarity) plus the retrieved evidence. Namedretrieval_score, notfit_score— it's a retrieval confidence signal, not a fit judgment, and the two can diverge (seedocs/adr/0008-resume-chunking-strategy.md). It does not synthesize a fit verdict itself — no internal LLM call — so the calling assistant is expected to reason over the returned evidence, applying thejob-fit://rubricresource returned alongside it. Sincejob_descriptionis often scraped/pasted web text, the result also includes it wrapped in an explicit data delimiter with a do-not-follow-instructions notice (docs/adr/0017-delimit-job-description-in-match-job-result.md).Note:
docs/adr/0009-caller-agnostic-reversal.mdcalls for this judgment step to move server-side into a newevaluate_fittool, so fit-bucket assignment isn't left to whichever assistant happens to callmatch_job. That tool is designed (docs/evaluate_fit_schema.md,docs/adr/0010-layer-split-design-evaluate-fit.md) but not yet implemented — today, the calling assistant still constructs theFitVerdictpassed topush_to_trackeritself.push_to_tracker(job_id, verdict, dry_run=False)— writes aFitVerdict(seedocs/evaluate_fit_schema.md) to the configuredTrackingStore. Updates an existing tracked row by Notion page ID — never creates a new row or searches for one. Only the fields yourtracking_schema.yamlmarks tool-populated are touched; every manual field on the row (company, comp range, source, work arrangement, etc.) is left as-is (docs/adr/0011-configurable-tracking-field-schema.md). A misconfigured field (e.g. anotion.propertythat no longer exists on your database) is skipped with a warning rather than failing the whole write — check the result'swarnings.dry_run=Truereturns the mapped properties payload without writing.find_or_create_application(company, role, source_url=None, dry_run=False)— finds an existing tracked row by exact company+role text match, or creates a new one if none exists. Requirescompanyandrolefields with anotion.propertydeclared intracking_schema.yaml. Returns{"job_id": ..., "created": bool}. Seedocs/adr/0013-find-or-create-application.md.update_status(job_id, status, dry_run=False)— sets the Status field on an existing tracked row directly, for candidate-reported lifecycle events (Applied, Rejected, Interviewing, Offer, ...). Decoupled fromFitVerdict/push_to_trackerentirely — seedocs/adr/0014-update-status-tool.md.
push_to_tracker and update_status both verify the target job_id
actually belongs to your configured Notion database before writing to it
— see docs/adr/0016-job-id-ownership-check.md.
list_applications(status=None)— lists tracked jobs from the configuredTrackingStore, each withjob_idplus whatever tool-populated fields your schema declares (typicallystatus,fit_rating,notes). Passstatusto filter to an exact (case-sensitive) match, e.g."Not yet applied"— useful for questions like "what am I waiting to hear back on" without opening Notion.
Resume text is split into light, section/role-sized chunks before
embedding (src/job_search_mcp/chunking.py) rather than embedded whole —
see docs/adr/0008-resume-chunking-strategy.md.
Tracking field schema
push_to_tracker and list_applications don't hardcode a field list —
they read tracking_schema.yaml (path overridable via
TRACKING_SCHEMA_PATH, gitignored like .env; copy
tracking_schema.example.yaml to get started). Each field is declared as
either:
manual: true— the tool never reads or writes it (company, comp range, source, work arrangement, ...). Documentation only.derived_from: <status_fixed | fit_rating_from_bucket | key_notes>— a tool-populated field, computed from aFitVerdict. These three are the only values aFitVerdictcan currently be turned into; the schema says which of them your tracker wants and under what property/column name, not how to compute them.
A tool-populated field also needs a backend location:
notion.property/notion.type for Notion, sqlite.column for
SQLiteTrackingStore (which only tracks fields that declare a
sqlite.column at all — it has no concept of Notion's manual fields).
Someone with a simpler tracker than the author's just lists fewer
fields. A misconfigured individual field (an unrecognized
derived_from, or a notion.property that doesn't exist on the live
database) is warned about and skipped rather than failing the whole
write; only an unreadable or structurally invalid schema file itself is
a hard failure. See docs/adr/0011-configurable-tracking-field-schema.md
for the full design.
Setup
just installCopy
.env.exampleto.envand fill in your Qdrant and Notion connection details.Copy
tracking_schema.example.yamltotracking_schema.yamland edit the field list to match your own tracker (see Tracking field schema, above).Ingest a resume:
uv run python -m job_search_mcp.ingest path/to/resume.pdfRegister with Claude Code (project-scoped):
claude mcp add job-search-mcp -- uv run --directory "$(pwd)" job-search-mcpIn a Claude Code session in this project, ask it to call
match_jobwith a real job description, thenpush_to_trackeragainst a row you already track in Notion.
Roadmap
Shipped: match_job (retrieval), push_to_tracker, list_applications,
find_or_create_application, and update_status against
NotionTrackingStore, the YAML-configurable tracking field schema
(docs/adr/0011-configurable-tracking-field-schema.md), and the ingestion
pipeline (ResumeSource → chunking → embedding → VectorStore).
Planned next:
evaluate_fit— move fit-bucket judgment (the rubric indocs/job_fit_scoring_algorithm.md) inside the server via an internal LLM call, so it no longer depends on the calling assistant applying the rubric itself (docs/adr/0009-caller-agnostic-reversal.md,docs/adr/0010-layer-split-design-evaluate-fit.md)Wire
SQLiteTrackingStoreup as a selectable backend (it exists, reads the same tracking schema, and is tested, but the server currently always constructsNotionTrackingStore)Google Drive-backed
ResumeSourceimplementationRevisit the embeddings choice if local sentence-transformers quality proves insufficient (
docs/adr/0006-embeddings-choice-open.md)
Deferred, low priority (no design commitment beyond the note below):
Bulk re-evaluation when
candidate_profile.yamlchanges —target_floor/title_mapping_noteare documented as editable-but-stable, so a value changing after postings are already tracked is a foreseeable case. Open question: whetherlist_applications+ re-runningevaluate_fitis sufficient once that tool exists, or whether the originaljob_descriptiontext needs to be persisted somewhere (it currently isn't) to make re-evaluation possible later.Duplicate/near-duplicate JD detection — companies commonly post near-identical reqs for genuinely different underlying roles (same title, similar boilerplate, different team/contract). Neither
match_job/evaluate_fitnorfind_or_create_application(docs/adr/0013-find-or-create-application.md, which only matches exact company+role text) currently detect this. Direction: a lightweight hash/fuzzy-match check against previously-ingested postings, surfaced as a warning rather than blocking.
Both items above were raised alongside specific anecdotes (a mid-search
target_floor change; two near-duplicate "Skylight" postings) that a
transcript-search check found no independent record of — see
docs/adr/0015-prompt-injection-defense-for-job-description-text.md's
Verification section for the same check applied to a related claim from
the same source. Both items stand on the general reasoning above
regardless of that.
Development
just install # uv sync
just test # uv run pytest (unit tests only; integration needs QDRANT_URL)
just lint # uv run ruff check .
just run # uv run job-search-mcp (stdio MCP server)CI (.github/workflows/ci.yml) runs lint and the unit test suite on
every push to main and every pull request.
Available Tools
3 toolslist_applicationsA
List tracked jobs from the configured tracking store (Notion).
Args: status: If given, only return rows whose Status matches exactly (e.g. "Not yet applied", "Applied"). Case-sensitive — must match the tracking store's status value exactly. Omit to return every tracked row.
Each entry has job_id plus whatever tool-populated fields your tracking_schema.yaml declares (see docs/adr/0011-configurable-tracking-field-schema.md) — typically status, fit_rating, and notes. Statuses beyond "Not yet applied" (Applied, Recruiter screen, ...) are manual edits made in Notion, not something this server drives.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses that status matching is case-sensitive and exact, that returned fields depend on a configurable schema, and clarifies which statuses are server-driven versus manual edits. It explains what gets returned (job_id plus tool-populated fields) and the read-only nature of the operation, providing good behavioral context beyond the minimal schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose in the first sentence, then parameter details, then return-value context. It's reasonably compact for the information conveyed, though the return-fields paragraph is slightly verbose with the documentation reference. No wasted sentences; each paragraph earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-optional-parameter read tool with no annotations, the description covers purpose, filter semantics, return shape (with reference to a configurable schema), and data provenance. An output schema exists to document return structure. It's complete enough for an agent to invoke confidently, though it could note error cases or pagination behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry all parameter meaning. It thoroughly explains the 'status' parameter: exact-match semantics, case-sensitivity, example values, and behavior when omitted. The key insight that filtering requires exact tracking-store values is genuinely useful and not derivable from the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool 'lists tracked jobs from the configured tracking store (Notion)', specifying the verb (list), resource (tracked jobs), and source (Notion tracking store). It distinguishes from siblings by clarifying it reads tracked applications rather than matching or pushing jobs, though it doesn't explicitly name the sibling alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context: the optional 'status' filter for exact matches, instructions to omit it to return all rows, and a warning that certain statuses are manual edits made in Notion rather than server-driven. It gives practical when-to-use guidance via the filter semantics, though it doesn't explicitly state when to use a sibling tool instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
match_jobA
Retrieve resume/experience evidence relevant to a job description.
Args: job_description: The full text of the job description to match against. source_url: Optional URL the job description was pulled from, for reference.
Returns a heuristic retrieval_score (top-match cosine similarity) and the retrieved resume chunks with their individual similarity scores. Does not synthesize strengths/gaps/notes — reason over the retrieved evidence yourself, applying the job-fit://rubric resource linked in this result.
| Name | Required | Description | Default |
|---|---|---|---|
| source_url | No | ||
| job_description | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| source_url | Yes | |
| job_description | Yes | |
| retrieval_score | Yes | |
| retrieved_chunks | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It does disclose a key behavioral trait: the tool returns raw retrieval scores and resumes chunks WITHOUT synthesizing strengths/gaps/notes, explicitly telling the agent to reason over the evidence itself. This is valuable transparency about the tool's computational behavior (heuristic cosine similarity) and its non-analytical nature. It doesn't cover auth/permissions or failure modes, but for a retrieval-style tool the core disclosure is present and helpful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections: purpose sentence, Args list, and Returns paragraph. It's appropriately detailed for a retrieval tool and front-loads the purpose. There is a minor dependency on the job-fit://rubric resource which isn't fully elaborated, and the Args/Returns formatting is somewhat technical, but overall it's concise with no wasted sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With only 2 parameters (1 required), no enums, and an output schema present, the tool is relatively simple. The description explains what the tool returns (retrieval_score, chunks, similarities) and, crucially, what it does NOT do (synthesize strengths/gaps/notes), directing the agent to the job-fit://rubric. The main gap is that the rubric resource is referenced vaguely without stating what it contains, but the output schema and low parameter count reduce the burden on the description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema description coverage is 0%, meaning the schema provides only type names (job_description, source_url) with titles but no descriptive help. The description compensates meaningfully: it explains job_description is 'the full text of the job description to match against' and source_url is 'optional URL the job description was pulled from, for reference.' However, it adds no format details for job_description (e.g., expected length, formatting) or when source_url might matter. This is adequate compensation for low coverage but not exhaustive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource statement: 'Retrieve resume/experience evidence relevant to a job description.' This clearly distinguishes it from its siblings (push_to_tracker, list_applications), which are evident from context to be write/listing operations. The purpose is unambiguous and specifically tied to retrieval matching.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly implies when to use this tool: when you need evidence retrieval against a job description for fit analysis. It explicitly states what the tool does NOT do ('Does not synthesize strengths/gaps/notes') and instructs the agent to reason over retrieved evidence applying the job-fit://rubric resource. This is strong guidance about how to use the output. However, it doesn't explicitly contrast with sibling tools (e.g., when NOT to use this in favor of list_applications), which would push it to a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
push_to_trackerA
Write an evaluate_fit result to the configured tracking store (Notion).
Args: job_id: The Notion page ID of the job you already track. Updates that existing row in place — never creates a new row. verdict: The full evaluate_fit output (see docs/evaluate_fit_schema.md). dry_run: If true, returns the mapped Notion properties payload without writing anything, so you can review it first.
Only the fields your tracking_schema.yaml marks tool-populated are touched — every other, manual field on the row (company, comp range, source, work arrangement, etc.) is left as-is. See docs/adr/0011-configurable-tracking-field-schema.md.
A misconfigured tool-populated field (an unrecognized derived_from,
or — on a real write, not dry_run — a notion.property that doesn't
exist on your database) is skipped rather than failing the whole
write; check warnings in the result. dry_run's warnings only cover
what can be checked without a Notion API call, since dry_run makes
none — a bad property name only shows up in warnings on a real write.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes | ||
| dry_run | No | ||
| verdict | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden and it delivers extensively. It discloses that only tool-populated fields (per tracking_schema.yaml) are touched while manual fields are left intact, that misconfigured fields are skipped rather than failing the whole write, that warnings appear in the result, and that dry_run makes no Notion API call so its warning coverage is limited. This is exceptionally transparent about side effects and failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense and well-organized with Args, behavior, and edge-case handling clearly sectioned. It's longer than minimal but every sentence adds operational value — dry_run semantics, field-preservation behavior, and warning behavior are all genuinely useful. Slightly long but front-loaded with the core operation first.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a write tool with no annotations and no output schema, the description is thorough: it covers the write target, in-place update semantics, field-selection behavior driven by tracking_schema.yaml, error handling for misconfigured fields, and dry_run's limitations. It would benefit from describing the result structure (what warnings/properties are returned) since there's no output schema, but it's largely complete for effective use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does. It explains job_id (Notion page ID of an existing tracked job), verdict (full evaluate_fit output, referencing docs), and dry_run (preview mapped payload without writing). The only minor gap: it references external docs (evaluate_fit_schema.md, adr/0011) rather than describing the verdict structure inline, though reasonable given complexity.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb ('Write'), the resource ('an evaluate_fit result to the configured tracking store (Notion)'), and the key behavior — updating an existing row in place, never creating a new one. This distinguishes it effectively from sibling tools like match_job (which produces the fit result) and list_applications (which reads applications).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly explains when to use this tool (after evaluate_fit produces a result, to persist it) and gives critical usage context: job_id must be an already-tracked Notion page ID, whether to use dry_run to preview mapping before writing, and how misconfigured fields are handled. It distinguishes from siblings by positioning this as the write step following the match/evaluate step.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
list_applications - First observed
match_job - First observed
push_to_tracker
TDQS
Scored across 3 tools
The three tools have clearly distinct purposes: match_job retrieves evidence, push_to_tracker writes results, and list_applications reads tracked jobs. There's no real overlap between them. The only minor confusion is that push_to_tracker's write and list_applications's read both touch the same Notion store, but their actions are opposite and clear.
All three names follow a clear verb_noun pattern: match_job, push_to_tracker, list_applications. Each uses an imperative verb followed by the target. Minor inconsistency: 'push_to_tracker' is a multi-word noun phrase while 'match_job' and 'list_applications' are more compact, but the pattern is consistent enough.
Three tools is on the low side but reasonable for a focused job-search workflow. The surface covers matching, tracking, and listing, which are the core operations. One could argue a remove/cleanup tool is missing, but the scope of an MCP for job search is narrow enough that 3 tools feels appropriately scoped, not thin.
The server covers the core workflow: match a job, push the evaluation to the tracker, and list tracked applications. However, there are notable gaps — no way to create a new job row (push_to_tracker explicitly requires an existing job_id and never creates a row), no update/retract on previous evaluations, and no removal of stale entries. The create-to-track action is a significant missing lifecycle step for a job-search tracking workflow.
Maintenance
Related MCP Connectors
Public MCP server for discovering open jobs. Search, filter, and get application links.
GetJobzi MCP server for job search, application tracking, and career forecasting.
Generate tailored, ATS-optimized resume PDFs and cover letters from a job description, over MCP.
AI job search MCP — fact-checked jobs, application tracker, alerts. ChatGPT, Claude, Cursor.
Related MCP Servers
- AlicenseAqualityAmaintenanceAn MCP server that exposes a perpetual, honest job-application pipeline as typed tools an LLM agent can call, with fit scoring, verified resume building, and a submission planner enforced by code, not prompts.16MIT
- AlicenseNot gradedqualityAmaintenanceA local-first, open-source MCP server that analyzes jobs, matches your CV, tailors documents, and tracks applications — all on your machine with no data uploaded.AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceAn MCP server that exposes job-search and application-management capabilities to compatible AI clients, enabling discovery of vacancies, drafting of tailored application materials, and coordinated human-approved submissions.MIT
- AlicenseNot gradedqualityCmaintenanceMCP server that aggregates and deduplicates job listings from multiple public sources, ranks them against a user's resume, and exposes tools for searching, viewing details, explaining fit, and tracking applications.MIT