Skip to main content
Glama

job-radar-mcp

A local MCP server that lets your AI assistant screen job offers against a detailed profile of your skills, and keeps a running radar of the skills the market keeps asking for and you do not have yet. Everything lives in plain Markdown files on your own machine.

Why

Companies already use AI to filter candidates. It makes sense for candidates to use AI to filter offers too: not against a generic idea of "a good job", but against their own profile, their own goal and their own deal-breakers.

Reading an offer and asking "is this worth my time?" is easy to get wrong when you are tired, hopeful or in a hurry. This project gives your AI assistant the context to give you an honest answer, every time, and it remembers what each offer asked for that you are missing. After twenty offers you no longer guess what to learn next: the radar tells you.

Related MCP server: Job Application MCP

How it works

Two files sit at the heart of it, and they never overlap:

  • skills.md: what you already have. Each skill is [solid] (you can defend it in an interview with evidence) or [partial] (you have used it, you do not master it). Gaps never live here.

  • gaps.md: what offers ask for and you do not have. Each term carries a counter of how many offers asked for it. The higher the count, the more urgent it is to learn.

When you analyse an offer, every missing skill goes to the radar: a new term is created with count 1, a known term goes up by one. When you actually acquire a skill (a course, a project, real experience), you graduate it: it leaves the radar and enters your profile with its level and the evidence. That is the only way from gap to profile.

flowchart LR
    O["Job offer<br/>(you paste it)"] --> A["analyze_offer<br/>verdict + gaps"]
    A -->|"missing skill"| R["gaps.md<br/>[count: N] Term"]
    R -->|"asked again"| R
    R -->|"you learn it<br/>confirm_skill"| P["skills.md<br/>[solid] / [partial]"]
    P -->|"your profile today"| A

Each analysis ends with one of four verdicts (good fit, reasonable reach, too demanding, not worth the time), concrete evidence from your CV, the cheapest gap to close, the terms you should be able to define in an interview, and what to highlight in your CV for that specific offer. The analysis is saved in offers/ and you can track each offer through its lifecycle (analyzed, applied, interview, offer, accepted, or the side exits rejected and discarded).

It can also render a one-page CV tailored to an offer (render_cv), choosing only from content that already exists in your cv-data.json. It reorders and trims, it never invents.

What it does NOT do

  • No scraping. It does not crawl job boards. You paste the offer text into your AI client.

  • No network, no database. The server reads and writes Markdown files in a folder you choose. That is all.

  • No AI of its own. The server does not think. Your AI client does the reasoning; this server is its memory and its hands.

Your data stays yours

The idea and the data are personal. Each user builds their own vault: their skills, their goal, their criteria, their offers. Nothing leaves your machine except what your AI client sends to its own model when you use it.

This repository only contains fictional data: the example-vault/ folder belongs to Alex Morgan, a made-up junior developer from a made-up city. Copy it, then replace everything with your own. Keep your real vault outside the repo (the default location is ~/job-radar-vault), and .gitignore will block the usual names (vault/, *-vault/, *.local.md) if you put one inside by mistake.

Why MCP

MCP (Model Context Protocol) is an open standard for connecting AI assistants to tools and data. Building this as an MCP server means anyone can plug it into the AI client they already use (Claude Desktop, Claude Code, or any other MCP-compatible client) without depending on one specific app. Swap the client, keep your vault.


Quick start

1. Requirements

  • Node.js 22 or newer.

  • An MCP-compatible AI client, such as Claude Desktop or Claude Code.

  • Optional: Obsidian, to browse and edit your vault comfortably. The vault is a plain folder of Markdown, so any editor works.

2. Install

git clone https://github.com/JonniThorpe/job-radar-mcp.git
cd job-radar-mcp
npm install
npm run build

3. Create your vault from the example

# macOS / Linux
cp -r example-vault ~/job-radar-vault
# Windows (PowerShell)
Copy-Item -Recurse example-vault "$HOME\job-radar-vault"

Then open the copy and make it yours. Start with profile/criteria.md (your career goal goes under ## Goal) and profile/skills.md. Empty profile/gaps.md down to its headings: the radar fills itself as you analyse offers.

If the folder does not exist when the server starts, it tells you so on stderr, keeps running, and every tool answers with instructions instead of silently creating an empty vault.

4. Register the server in your client

It is a stdio server: the client launches it as a child process. Use the absolute path to build/index.js and to your vault.

Claude Code

claude mcp add job-radar --scope user \
  -e JOB_MCP_DATA_DIR=/absolute/path/to/job-radar-vault \
  -- node /absolute/path/to/job-radar-mcp/build/index.js

Claude Desktop: add this block to claude_desktop_config.json (Settings > Developer > Edit Config):

{
  "mcpServers": {
    "job-radar": {
      "command": "node",
      "args": ["/absolute/path/to/job-radar-mcp/build/index.js"],
      "env": { "JOB_MCP_DATA_DIR": "/absolute/path/to/job-radar-vault" }
    }
  }
}

On Windows, write paths with double backslashes (C:\\Users\\you\\...). If Claude Desktop cannot find node, use the absolute path to the Node executable as command: desktop apps do not always inherit your terminal's PATH.

Other clients: any MCP client that supports stdio servers takes the same three things: the command (node), the argument (the path to build/index.js) and the environment variable JOB_MCP_DATA_DIR.

5. First use

  1. Ask your assistant to run the ping tool. It answers with the data folder it is using.

  2. Start the analyze_offer prompt. In Claude Code, type / and pick /mcp__job-radar__analyze_offer. In Claude Desktop, open the + menu and look for it under the job-radar server. Other clients list MCP prompts in their own way.

  3. Paste the offer text below it and send.

  4. Read the verdict. The assistant then saves the analysis with save_offer and records each missing skill with record_gap.

  5. Open profile/gaps.md and watch the counters grow over the next offers.

Every npm run build requires restarting your client. An MCP server over stdio is launched once, when the client starts, and that process stays alive. There is no hot reload. In Claude Desktop, closing the window is not enough: quit it from the system tray or menu bar. Typical symptom: you ask for a new tool and the client insists it does not exist.


Reference

Tools (the model calls them)

Tool

What it does

record_gap(term, category, source)

Bumps a gap's count by one and adds the source. Creates it with count 1 if it is new. Never duplicates.

list_gaps()

Lists gaps sorted by count, most urgent first.

confirm_skill(term, level, evidence, category?)

Graduation: moves a skill from the radar to your profile. Only the evidence travels, not the sources.

update_skill_level(term, level)

Changes solid / partial for a skill already in your profile.

save_offer(id, title, company, markdown, score?, salary?, url?, tags?)

Creates or updates an offer. On update it keeps the status and interview notes.

set_offer_status(id, status)

Moves an offer along its lifecycle and logs the change with a date.

add_offer_note(id, note)

Adds a dated note under "Interview notes".

render_cv(offerId, ...)

Writes output/cv-<id>.html, a one-page CV tailored to an offer, using only content from cv-data.json. Warns if the page overflows.

ping()

Health check: says which data folder it uses, or what is wrong with it.

Resources (you attach them)

URI

Content

profile://skills

skills.md

profile://gaps

gaps.md, sorted by count when served

profile://cv

cv.md

profile://cv-data

cv-data.json, the catalogue render_cv chooses from

profile://criteria

criteria.md

offers://list

The pipeline: status, company, title, score

offers://{id}

One full offer: analysis, status log and notes

Prompt (you start it)

Prompt

What it does

analyze_offer

Returns the analysis template filled in with your skills, CV and criteria read from disk at that moment, including your goal from criteria.md (if it is missing, the assistant asks you for it first). Paste the offer below it.

Vault layout

job-radar-vault/
  profile/
    skills.md          - [solid|partial] Term (evidence)        under '## Category'
    gaps.md            - [count: N] Term (Category). Sources: a; b
    criteria.md        ## Goal, deal-breakers and scoring axes, in free prose
    cv.md              your CV in Markdown: evidence for the analysis
    cv-data.json       your CV split into ids: what render_cv may use
    interview-prep.md  blank template for STAR stories (not read by the server)
  offers/
    YYYY-MM-company.md YAML frontmatter + analysis + status log + notes
  output/
    cv-<id>.html       generated, never edit by hand

The server never regenerates a file. It parses only the lines it recognises (by pattern, such as - [count: N] ) and edits that exact line, leaving every other byte untouched. That is why you can write your own notes, rules and prose around the data lines, in Obsidian or any editor. The format is yours, not the server's.

Obsidian (optional)

The vault works well as an Obsidian vault. Two community plugins pair nicely with it: Dataview (live tables of the radar and of offers by status, from the frontmatter) and Kanban (the offer pipeline as a board).

Configuration

Variable

Default

JOB_MCP_DATA_DIR

~/job-radar-vault


Development

npm run build   # tsc -> build/
npm test        # tsc + node --test
npm run watch   # rebuild on save
src/
  index.ts             wiring only: resources + tools + prompts + stdio transport
  config.ts            data folder and derived paths
  resources.ts         the 7 resources
  tools.ts             the 9 tools, with their Zod schemas
  prompts.ts           analyze_offer
  lib/
    analysis-prompt.ts the analysis template and the goal reader
    markdown.ts        line-level primitives: headings, sections, EOL
    skills.ts          skills.md
    radar.ts           gaps.md and its counters
    offers.ts          frontmatter, statuses and notes
    graduate.ts        confirm_skill: the operation that spans both files
    cv.ts              render_cv: selection, height estimate, HTML
    files.ts           the ONLY module that touches the disk
templates/
  cv.html              CV design
example-vault/         fictional data, also used by the tests

src/lib/* are pure functions shaped like (content, args) -> { content, changed, message }, so the tests never touch your vault and run in a fraction of a second. Comments in the source are in Spanish; everything user-facing is in English.

Never use console.log in the server. stdout is the JSON-RPC channel and writing to it breaks the protocol. Logs go to console.error (stderr).

You can talk to the server without any client by piping raw JSON-RPC:

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"cli","version":"0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | JOB_MCP_DATA_DIR=./example-vault node build/index.js

Running it against example-vault writes to it, so try things on a copy.

Contributing

Issues and pull requests are welcome. Ideas that are already on the table live in ROADMAP.md; open an issue to discuss before building a large change. Please keep npm test green, add tests for new behaviour in src/lib, and never commit real personal data: test fixtures and example-vault/ must stay fictional.

License

MIT

Available Tools

9 tools
add_offer_noteAdd a note to an offerA

Adds a dated note to the 'Interview notes' section of an offer: who interviewed the user, what they asked, feedback received, or anything worth rereading before the next round. Notes accumulate, they are never overwritten.

NameRequiredDescriptionDefault
idYesOffer id.
noteYesNote text. Multiple lines and Markdown are fine.

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotentHint=false and destructiveHint=false; the description adds a meaningful behavioral fact not derivable from them - notes accumulate and are never overwritten - which reassures the agent that repeated calls append rather than replace. It also implies the timestamp is applied server-side ('dated note'). It stops short of saying what is returned, which keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose and target are front-loaded in the first clause, and the second sentence about accumulation is short and earns its place. The middle content list is slightly padded but still useful guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter append tool with no output schema, the description covers what is created, where it goes, and the append-only behavior, which is most of what an agent needs. It omits any mention of permissions or the response shape, but neither is critical to invoking it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% with only two required parameters (id, note), so the schema already documents both; the description adds the fact that the note becomes 'dated' and lives in a specific section. That is minor added meaning beyond the schema, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (adds), resource (note), and target (the 'Interview notes' section of an offer) in one clause, plus the note's expected content. No sibling tool in the list adds notes, so the agent can identify this tool unambiguously.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives content guidance ('who interviewed the user, what they asked, feedback received') which implies when this tool is appropriate, but it never states an explicit when-to-use condition or names an alternative among siblings such as save_offer or record_gap. Usage must be inferred from the content examples.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

confirm_skillGraduate a skillA

Graduation: confirms the user now has a skill that was in the radar. Removes it from gaps.md and adds it to skills.md with its level and evidence. It is the ONLY path from gap to profile. Use it only when the user confirms they have acquired it (certification, project or real experience), never on your own initiative.

NameRequiredDescriptionDefault
termYesTerm exactly as it appears in the radar.
levelYessolid = can defend it in an interview with evidence from a project. partial = has used it, does not master it.
categoryNoOnly if the gap had no category in the radar.
evidenceYesWhy the user can defend it: certification, project or experience. Stored in the profile as a note. The offer that created the gap is NOT kept.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only cover idempotentHint=false and destructiveHint=false, so the description carries real weight: it discloses the mutation of two files, what gets written, and notably what gets dropped (the offer that created the gap is NOT kept). This is exactly the side-effect context a mutation tool needs beyond its annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the action and its effects, then the precondition. No filler; every clause carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema needed and full param documentation in the schema, the description supplies everything else an agent requires: precondition, side effects, and the exclusivity of this workflow path.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so term, level, category and evidence are already documented with format and enum meaning in the schema. The description only echoes 'level and evidence' and adds no syntax or format detail beyond that — baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('confirms the user now has a skill that was in the radar') and names the concrete side effects (removes from gaps.md, adds to skills.md). This clearly distinguishes it from siblings like record_gap and update_skill_level.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use ('only when the user confirms they have acquired it via certification, project or real experience'), explicit when-not ('never on your own initiative'), and states it is the ONLY path from gap to profile. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_gapsList gapsA
Read-only

Returns every gap in the radar sorted by count, highest first: the top entries are what most offers have asked for, and therefore the most urgent to learn.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true already establishes this is a safe read, so the description only needs to add context. It does: the result is exhaustive ('every gap'), ordered by count descending, and the ordering carries meaning about urgency. It does not describe pagination behavior or the fields of a gap record.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with the operation and its ordering rule front-loaded, followed by a short interpretive clause. There is no filler and nothing is buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-argument read-only listing tool this covers what an agent needs to select and call it. The only omission is the shape of a returned gap entry, which is a minor gap given there is no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the schema is trivially fully covered and the baseline of 4 applies. Nothing about parameter meaning is left unclear because there is nothing to parametrize.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb and resource ('Returns every gap in the radar') and adds the ordering rule, so an agent immediately knows this is the enumeration counterpart to record_gap. It never names a sibling explicitly, so it stops short of the 5 criterion for sibling differentiation, but the intent is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The clause 'the top entries are what most offers have asked for, and therefore the most urgent to learn' implies the tool is for prioritization, but there is no explicit when-to-use, when-not, or named alternative. Usage is inferred rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pingPingA
Read-only

Checks that the server is alive and which data folder it is working on.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true is consistent with a health probe, and the description adds real value beyond the annotation by disclosing the second piece of information returned (the active data folder), which lets an agent use it as a configuration check, not just a liveness check.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One short sentence, front-loaded with the primary behavior and appending only the secondary return detail. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a no-param, no-output-schema tool the description is sufficient: it covers both the liveness result and the data-folder result, which is the entirety of what a caller needs. Minor omissions such as version or latency reporting are not required here.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are zero parameters, so the schema carries no burden and the baseline is 4; nothing in the description is needed to explain inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (checks), the resource (server), and even an extra facet of the result (which data folder it is working on). It is trivially distinguishable from the domain siblings, though it doesn't need to differentiate itself from any of them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied — an agent would call this to verify connectivity or diagnose availability — but the description never says when to reach for it (startup, before a batch, after a failure) or that it is the only liveness probe available.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

record_gapRecord a gapA

Records that a job offer asks for a skill the user does NOT have. If the term is already in the radar its count goes up by one and the source is added; otherwise it is created with count 1. It never duplicates. Call it once per skill the offer asks for that is missing from the user's profile, including terms the user could not define in an interview. Never call it for something already in skills.md.

NameRequiredDescriptionDefault
termYesThe missing skill or concept, named the way an engineer would. E.g. 'Kubernetes'.
sourceYesOffer that asks for it, so it can be traced. E.g. 'Acme Backend Engineer, Remote'.
categoryYesCategory it belongs to, used to place it under the right '##' heading in the profile once it graduates. E.g. 'Infrastructure'.

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotentHint=false and destructiveHint=false; the description adds the meaningful part by explaining the upsert semantics -- count increments and source is appended if the term exists, otherwise it is created with count 1 -- and that duplicates are never produced. It does not cover permissions, the response shape, or how the added source affects the existing entry, but the mutation consequences are well disclosed and consistent with the non-idempotent hint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose and mutation behavior are front-loaded in the first three sentences, with usage rules following. Five sentences is on the long side for a three-parameter tool, and 'It never duplicates' partially overlaps with the later 'Never call it for something already in skills.md', but every sentence still carries actionable information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with no output schema and only partial annotation coverage (idempotency/destructiveness), the description supplies the core context an agent needs: the missing-skill scope, the create-vs-increment behavior, and the exclusion for skills already in the profile. Remaining gaps -- return value and any auth/permission requirement -- are minor for this operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so term, source, and category are all already documented with examples in the schema. The description only implicitly reinforces what 'source' does (it is added to an existing entry), adding no format, naming, or placement detail beyond the schema. Baseline 3 applies when the schema carries the parameter burden.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb+resource+condition: it records that a job offer asks for a skill the user does NOT have. It also discloses the underlying data behavior (radar entry incremented vs. created with count 1), which distinguishes it clearly from siblings like list_gaps or confirm_skill.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use ('call it once per skill the offer asks for that is missing from the user's profile, including terms the user could not define in an interview') and an explicit when-not ('Never call it for something already in skills.md'). However, it never names the alternative sibling tools an agent should route to for already-known skills (e.g. confirm_skill or update_skill_level), so the exclusion lacks a destination.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

render_cvRender a CV tailored to an offerA
DestructiveIdempotent

Writes output/cv-.html, a version of the CV tailored to one offer: it chooses which projects, which lines of each project, which bullets and which tags go in, and in what order. If not everything fits, trim lines inside each project before dropping a whole project. It CANNOT invent content: everything must already exist in profile/cv-data.json, and the whole call fails if you cite an id or tag that is not there. Only headline, summary and location accept free text, because they are framing. Omit any section to keep it complete as is. Do NOT call it on your own after an analysis: propose the tailoring and wait for the user to ask for it.

NameRequiredDescriptionDefault
noteNoWhy these choices for this offer. Stored as a comment inside the HTML, invisible when printed.
stackNoTech Stack groups to show, in order. Omit it to include them all unchanged.
offerIdYesId of the offer this CV is tailored to, format YYYY-MM-company. It names the output file.
summaryNoSidebar summary. Free text, but it may only reorder and emphasise what is already true in cv-data.json: do not add technologies or experience that are not there. About 300 characters; longer pushes the rest of the column off the page.
headlineNoHeadline under the name. Free text (accepts <br>): it is framing, not a factual claim. Never claim a job title the person has not held. Omit it to keep the one in cv-data.json.
locationNoLocation line in the contact block. Free text: where the person lives and where they can work. E.g. 'Northbridge · Open to remote'. Omit it to keep the one in cv-data.json.
projectsNoProjects to show, in order: the bare id for the whole project, or {id, lines} to keep only the lines that matter for this offer. This is where tailoring pays off most: put first the ones that touch the offer and, if it does not fit, trim lines before dropping a whole project. Omit it to include them all.
educationNoEducation ids, in order. Omit it to include them all.
experienceNoJobs to show, in order. Omit it to include them all.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds substantial behavior beyond the idempotentHint/destructiveHint annotations: content provenance (everything must already exist in profile/cv-data.json), an all-or-nothing failure mode when citing a missing id or tag, and the free-text boundary limited to headline/summary/location. It does not explicitly state that it overwrites an existing output/cv-<id>.html file, which is the main destructive consequence implied by destructiveHint=true.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the write action and output path, then layers constraints and the do-not-auto-call rule. Dense but every sentence carries a rule; slightly long-winded in the projects paragraph where the schema already repeats the trim-before-drop guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description covers the return artifact (file path, invisible stored comment), the validation failure mode, and the invocation workflow. An agent has everything needed to call it correctly and safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (baseline 3), but the description adds real meaning: which fields accept free text versus must reference existing ids, the omit-to-keep-unchanged convention, and the rationale that projects is where tailoring pays off most. That goes beyond the schema's own property descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and artifact ('Writes output/cv-<id>.html, a version of the CV tailored to one offer') and immediately scopes it to selecting projects, lines, bullets and tags. No sibling (record_gap, save_offer, list_gaps, etc.) operates on CV rendering, so the distinction is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when NOT to call it ('Do NOT call it on your own after an analysis: propose the tailoring and wait for the user to ask for it') and gives a fallback ordering rule ('trim lines inside each project before dropping a whole project'). This is a clear workflow constraint an agent would otherwise violate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

save_offerSave an offer analysisA
Idempotent

Creates or updates an offer file with its analysis. If the offer already exists ONLY the analysis is replaced: status and interview notes are kept.

NameRequiredDescriptionDefault
idYesIdentifier and file name, format YYYY-MM-company. E.g. '2026-07-acme'.
urlNoLink to the original offer.
tagsNoShort labels. E.g. ['backend','remote'].
scoreNoScore following the scoring rules in criteria.md, if it defines any.
titleYesJob title exactly as the offer states it.
salaryNoRange or figure exactly as the offer publishes it.
companyYesCompany.
markdownYesThe full analysis in Markdown, with the six points of the template.

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare idempotentHint=true and destructiveHint=false, and the description meaningfully extends that by spelling out the upsert behavior and exactly what survives an overwrite (status and interview notes). It does not cover permissions, file location, or what happens to url/tags/score/salary on update.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with the create-or-update behavior front-loaded and the preservation rule immediately after. No filler, and the most decision-relevant fact (only the analysis is replaced) is stated early.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and 8 parameters, the description covers the central upsert risk well but leaves a real ambiguity: it says ONLY the analysis is replaced, yet the schema accepts url, tags, score, and salary, so an agent cannot tell whether those are updated or ignored on the update path. That gap matters for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so all eight parameters are already documented in the schema with formats and examples. The description adds no parameter-level detail beyond calling the markdown payload 'the analysis', which is the baseline expectation when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Creates or updates an offer file with its analysis') and immediately pins down the upsert semantics. It implicitly separates itself from set_offer_status and add_offer_note by stating that status and interview notes are preserved, though it never names those siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description tells you what the tool does on both the create and update paths, but gives no explicit when-to-use guidance, no prerequisites, and no routing to alternatives such as add_offer_note or set_offer_status for the fields it deliberately preserves. Usage is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_offer_statusChange an offer statusA
Idempotent

Moves an offer along its lifecycle and logs the change with a date. Lifecycle: analyzed -> applied -> interview -> offer -> accepted. Side exits from any point: rejected (the company says no) and discarded (the user decides to stop).

NameRequiredDescriptionDefault
idYesOffer id. E.g. '2026-07-acme'.
statusYesNew status, one of the seven in the lifecycle.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare idempotentHint=true and destructiveHint=false, so the safety profile is covered. The description adds genuinely new behavioral context beyond that: the operation writes a dated log entry and follows a fixed lifecycle with terminal side exits, which matters when reasoning about state changes. It stops short of stating what happens on an invalid transition or whether exits are reversible.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the core action before the lifecycle detail. Every clause carries information (action, log side effect, transition order, exit semantics) with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With only two well-documented parameters, no output schema, and annotations covering safety, the description supplies the one thing the schema cannot: the lifecycle semantics needed to pick a valid status. It leaves open edge behavior such as invalid transitions or reversibility of 'rejected'/'discarded', which is a minor gap for this tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the enum is already enumerated, so the baseline is 3. The description goes further by giving the enum values meaning — the progression analyzed -> applied -> interview -> offer -> accepted, plus the two exit states and which of them is company-driven vs user-driven — context the bare enum list does not convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — moving an offer along its lifecycle and logging the change — and then enumerates the exact state machine, which distinguishes it from siblings like save_offer (creation) and add_offer_note (annotation). An agent can tell what this tool does without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: describing the lifecycle and the two side exits ('rejected', 'discarded') tells the agent which statuses are legitimate, but there is no explicit when-to-use or when-not-to-use guidance relative to save_offer or add_offer_note. The transition model is helpful context but not routing advice.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_skill_levelChange a skill levelA
Idempotent

Changes the level of a term that is ALREADY in skills.md. For something that is not in the profile yet, use confirm_skill.

NameRequiredDescriptionDefault
termYesTerm exactly as it appears in skills.md.
levelYesNew level: solid or partial.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare idempotentHint=true and destructiveHint=false, so safety and repeatability are covered structurally. The description adds the important precondition that the term must already exist, which is behavioral context beyond the annotations, though it stops short of saying what happens if the term is absent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the core action and the key precondition, with the routing alternative second. Every clause carries information; no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter mutation with no output schema and annotations already covering idempotency and non-destructiveness, the description supplies the precondition and sibling routing an agent needs. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both parameters (term, level) are already fully documented, including the enum values. The description reinforces that the term must already exist but adds no syntax or format detail beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (changes) and resource (level of a term in skills.md), and explicitly distinguishes itself from the sibling confirm_skill. An agent can tell immediately that this mutates an existing entry rather than creating one.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit when: the term is ALREADY in skills.md. It also gives an explicit when-not with the named alternative: 'For something that is not in the profile yet, use confirm_skill.' Nothing is left to inference.

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.

  1. 9 tool updatesv0.1.0
    • First observedadd_offer_note
    • First observedconfirm_skill
    • First observedlist_gaps
    • First observedping
    • First observedrecord_gap
    • First observedrender_cv
    • First observedsave_offer
    • First observedset_offer_status
    • First observedupdate_skill_level

TDQS

A4.1/5.0

Scored across 9 tools

Disambiguation5/5

Each tool targets a distinct resource+action: skill-gap recording (record_gap), gap graduation (confirm_skill), level edits on existing skills (update_skill_level), offer persistence (save_offer), lifecycle (set_offer_status), notes (add_offer_note), and CV rendering (render_cv). The three skill-related tools are the closest, but their descriptions explicitly disambiguate (missing vs. acquired vs. already-profiled), so misselection is unlikely.

Naming Consistency4/5

Almost every name follows a clean verb_noun pattern (record_gap, confirm_skill, update_skill_level, list_gaps, save_offer, set_offer_status, add_offer_note, render_cv) with no casing mixups. The lone exception is the health check 'ping', which breaks the pattern but is a conventional outlier, so the set stays highly predictable.

Tool Count5/5

Nine tools is a tight, well-scoped set for a job-search assistant spanning three coherent areas (skills/gaps, offers, CV). Every tool earns its place with no redundant operations, falling squarely in the ideal 3-15 range.

Completeness3/5

The skill side covers create/graduate/update/list and offers cover save/status/notes, plus CV rendering, but there is no read/list operation for offers (no list_offers or get_offer) and no list_skills, leaving agents without a way to enumerate tracked offers or the full profile. These are notable read-side gaps even though the write workflows are solid.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    A 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
  • A
    license
    A
    quality
    C
    maintenance
    A privacy-first MCP server for locally managing job, fellowship, and graduate-school applications. It offers tools for tracking application status, analyzing role fit, generating LaTeX CV/cover letters, interview prep, and discovering public jobs from ATS APIs.
    8
    1
    MIT