Skip to main content
Glama
adamwardecki

JobHunt Tracker

by adamwardecki

JobHunt

A Claude Code (and Hermes Agent) plugin that runs a disciplined, low-volume job search for one person. Every weekday it finds roles that fit your hard filters, verifies each one on the employer's own posting, dedupes against everything you have touched, and prepares tailored applications. It submits only through the employer's official apply flow, and only after you approve. It tracks replies and interviews and writes one short daily report.

The design follows one principle: fewer, better applications.

Website: adamwardecki.github.io/job-hunt-mcp

Status: early (v0.1). It grew out of one person's real job search and is used daily by that one person. Expect rough edges, and expect the source adapters to break when job sites change. Issues and pull requests are welcome.

What it will never do

  • Send email or LinkedIn messages. It writes drafts only, and a hook blocks every send tool.

  • Submit an application without your /j-hunt:approve <role_id> in the same session.

  • Create accounts, enter passwords, solve visible CAPTCHAs, tick consent boxes, or fill personal-data fields (passport, nationality, age, gender, marital status, ID numbers).

  • Auto-apply at volume.

Related MCP server: job-search-copilot

Requirements

  • Claude Code 2.1.295 or newer (the Desktop app or the CLI), or Hermes Agent.

  • Node.js 22.18 or newer. If it is missing, /j-hunt:setup offers to install it on macOS and Windows: it downloads the official installer from nodejs.org and checks its checksum and signature (on Windows it uses winget when available). The tracker uses the built-in node:sqlite, and Node runs the plugin's TypeScript directly. There are no runtime npm dependencies.

  • Recommended connectors: Gmail and Google Calendar, for recruiter replies, interview invites and reply drafts.

  • Recommended browser: Claude in Chrome or the host's built-in browser. You need one for logged-in job boards, some employer pages and every application form. Without a browser the daily run still works and queues browser work for later.

  • Optional: a LinkedIn MCP server (read-only use: job search and inbox).

Usage. A sweep runs several subagents (one per source, one per role it scores), so a daily run uses a noticeable share of your Claude plan or API budget. Start with /j-hunt:sweep --limit 5 to see what a run costs you.

Install

Claude Code:

/plugin marketplace add adamwardecki/job-hunt-mcp
/plugin install j-hunt@job-hunt-mcp
/j-hunt:setup

Setup walks through CV import, hard filters, preferences, writing rules, accounts (including which calendar your invites actually land on), connectors, browser check, sources, autonomy and the daily schedule, then runs a 5-result calibration sweep. Every "no, because..." becomes a rule. You can stop at any point and run /j-hunt:setup again to resume.

Already tracking applications in a markdown table? /j-hunt:import <folder> brings in an applied-log.md (| date | company | role | source | action | status |), a preferences file, queue notes and your profile and stories. See commands/import.md for the expected files.

Hermes Agent

The same checkout is also a native Hermes plugin:

git clone https://github.com/adamwardecki/job-hunt-mcp.git
cd job-hunt-mcp
scripts/hermes-install.sh

Restart Hermes and type /j-hunt:setup. Details and the differences from Claude Code are in docs/hermes.md.

What a day looks like

The scheduled sweep ends with a report like this one (the format is real, rendered by the tracker's render_report; the companies are made up):

# Daily report · 2026-10-18

**46 roles checked. 2 worth a look.**

## Needs you

1. **Halyard**, Senior Platform Engineer (role 1): Cover note saved. Apply with /j-hunt:apply?
2. **Pinecrest Health**, Staff Engineer (role 2): The posting asks for 2 days a month in Berlin. OK?

## Interviews

- 2026-10-20 14:00 (Europe/Berlin): **Juniper**, technical screen with Head of Engineering

## Applied

- **Ostra**, Backend Engineer, Payments. Confirmation: "Thanks for applying to Ostra! We will be in touch within two weeks."

## Dropped

- location: Northwind (ATS says hybrid in Austin)
- pay: Ledgerly (Band €55–72k is below the floor)
- industry: Blockwise (Crypto exchange)

## Sources

- wellfound: now degraded (Login expired)

You answer the "Needs you" items, run /j-hunt:apply for the roles you want, and approve each submit.

Commands

Command

What it does

/j-hunt:setup

Guided setup; resumable.

/j-hunt:sweep [--limit N]

The daily run (what the scheduled task runs). Inbox, sourcing, verify, score, prepare, report, audit. Never submits.

/j-hunt:apply [role-id|company|url]

Interactive apply: verify, write, fill, read back, then wait for your approval.

/j-hunt:approve <role-id>

You type this on the filled form to allow one submit attempt for that role and page (up to 2 hours).

/j-hunt:allow-consent <role-id>

You type this on the form to allow ticking consent boxes for that role and page.

/j-hunt:triage <source>

Reconcile and clean a tracker board (JobCopilot-style) with the delete guard.

/j-hunt:prep <company>

Interview prep from your stories.

/j-hunt:status

Pipeline summary and open items.

/j-hunt:cover-letter <role-id> [--pdf]

Cover letter, optionally as a one-page PDF.

/j-hunt:correct

Turn your override into a rule with a reason and example.

/j-hunt:import [path]

One-off import of an existing markdown log.

How it fits together

commands, scheduled task
  └─ skills (setup, source-roles, verify-employer, score-role, write-note, fill-form,
     triage-tracker, inbox-and-calendar, interview-prep) and agents (sourcer, scorer, auditor)
       └─ hooks: hard gates on every MCP call, user prompt and stop
            └─ MCP: tracker (this repo), Gmail, Calendar, LinkedIn, browser
                 └─ storage: tracker DB in ~/.job-hunt (never inside the plugin directory)
  • Tracker (servers/tracker/): the only writer to the database. Narrow tools, no raw SQL. An append-only event log with a state machine (found → verified → scored → queued → applied → interviewing → offer, plus dropped and closed). Drops need an employer quote, verified needs the ATS URL and location line, applied needs the confirmation text. Tool reference: docs/tracker-api.md.

  • Hooks (hooks/): see below.

  • Skills, commands, agents, templates: the operating rules, including the incidents that shaped them. The original design document, with the reasoning behind each rule, is docs/SPEC.md.

Your data

The tracker runs locally, always. .mcp.json starts a stdio server on ~/.job-hunt/tracker.db. There is no network listener and no token. The directory is created owner-only (0700) and the database and session files are 0600, because they hold your CV summary, pay floor and application history. Tightening is best effort: on a filesystem that cannot chmod, the tracker logs a warning and still starts.

Local storage does not mean nothing leaves your machine. Like any Claude Code or Hermes session, what the model reads (your profile, postings, emails it classifies) goes to your model provider, and each connector you add has its own data policy.

The daily sweep is a local scheduled task (Claude Desktop or Claude Code on your machine, or a Hermes cron job). It needs the browser for logged-in boards, employer pages and forms, and the browser is only reachable from a local session, so cloud scheduled tasks are not supported. The task runs only while the machine is awake and the app is open.

Hooks (hard gates)

Gate

Rule

Send

Any Gmail/email send, reply or forward, and any LinkedIn message or connection request, is denied in every session. Browser clicks on "Send" are denied in job-hunt sessions.

Drafts

Gmail create_draft/update_draft bodies are linted; em/en dashes, "not X but Y", fake-candid openers and volunteered gaps are denied.

Submit

A click on a submit/apply element (resolved from the page's accessibility tree) or a scripted submit needs your /j-hunt:approve on that role's current form and a passing gate_check (verified, not no_cold_apply, no live application at the company). Approval is consumed by one submit attempt. No identifiable page or reachable tracker means no submit.

Board delete

Needs a fresh JOBHUNT_TRIAGE_GUARD with ok: true and nothing but the delete control and its confirm button clicked since.

Personal data and consent

form_input into passport, nationality, age, DOB, gender, marital, ID and similar fields is denied; consent boxes stay unticked unless you allow them.

Read-back

verify_artifact is denied unless a real get_draft/list_drafts result in the session contains the draft id.

Stop

A /j-hunt:sweep session cannot end until every role it touched has an event and the report was rendered.

Browser gates apply only in sessions that used a job-hunt command or the tracker; send blocking applies everywhere.

Known limits. Gates see tool calls, not the screen. A click by raw coordinates has no element label, so the submit gate cannot classify it; the skills require ref-based clicks for submits and the auditor checks every applied role for captured confirmation text. Pressing Enter in a field is not gated. Hooks require a host that runs plugin hooks (Claude Code, Claude Desktop, Hermes); if approval notes never appear, the approve command tells the model not to submit. If Node.js is missing, or the gate cannot start or times out, mail, LinkedIn and browser tools are blocked (onFailure: "block"); other MCP tools still run, and the prompt hook stays fail open so you can still ask for help.

Use it responsibly

  • You are the applicant. Everything submitted goes out in your name. Read what it prepared before you type /j-hunt:approve.

  • Site terms. Some job boards (LinkedIn in particular) restrict automated access in their terms of service. The plugin keeps LinkedIn read-only and low-volume, but whether to connect a source is your call and your account.

  • Employer AI policies. If a form says not to use AI, the writing skill leaves those fields for you.

  • Models make mistakes. Verification quotes the employer's own page so you can check every drop and every match, and /j-hunt:correct turns each mistake into a rule.

Development

npm run setup   # dev only: TypeScript, the Node types and Biome, installed into dev/
npm test        # tsc, then the Node and Python tests

The code is TypeScript that Node runs directly (type stripping), so there is no build step and nothing to install at runtime. npm run typecheck runs tsc on its own; npm run test:node skips it.

Node tests cover tracker logic, migrations and locking, the MCP protocol, every hook gate, the triage guard run against a synthetic board in headless Chrome (skipped when Chrome is not installed), the hook config and matcher partition, the Node.js installer scripts, the runtime instructions in setup and the commands, the Hermes host adapter, the tools/list schemas, and the shared helpers. Python tests cover the native Hermes plugin wiring, including fail-closed rules and the Windows Node path.

Acceptance criterion (spec §14)

Covered by

1. Setup completes with connectors only

setup_status test: no browser is amber with a recordable decision

2. Report names missing capabilities and what was queued

report test

3. Alias dedupe ("Flex Living" vs "The Flex")

alias, fuzzy and ATS-slug tests

4. "Remote" tag vs ATS "Hybrid (8 days/month)" dropped with the quote

state-machine test

5. €42–78K dropped, €79–97K kept and flagged

check_pay test

6. Send tools blocked

hook test

7. Submit on an unverified role blocked

hook test

8. Triage never changes Applied; identical markup across columns

guard tests (Chrome) and delete-gate tests

9. Drafts reported only after read-back

tracker and hook tests

10. Em dash or gap sentence fails the auditor

lint and audit tests

11. Legacy import reproduces the pipeline summary

fixture test

See CONTRIBUTING.md before opening a pull request.

Design choices

  1. Local only. The tracker is SQLite behind a stdio server in ~/.job-hunt. A remote mode was built and removed: the daily run needs a local browser anyway.

  2. The daily run is a local scheduled task. Every run still detects capabilities and queues browser work with blocked_by: browser when no browser is connected.

  3. LinkedIn is read-only and optional. Send and connect tools are blocked by a hook.

  4. Voice. write-note uses an installed humanizer or writing-style skill when present, else its own rules.

  5. Templates ship neutral. Your own values go in through setup.

License

MIT

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A local-first MCP server for tracking job applications, companies, and vacancies, storing everything in a single SQLite file and exposing 19 tools for managing the job search from clients like Claude.
    2
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables an MCP-capable coding agent to run a local-first job search: recording verified opportunities in a private SQLite tracker, tracking follow-ups and outcomes, and preparing evidence-based resumes, application answers, and outreach drafts. It keeps all personal data on the user's machine and stops before submitting applications, uploading documents, or contacting anyone.
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to manage job applications and companies in a local SQLite-backed tracker, including searching, creating, updating, and deleting postings and companies, plus reading and updating shared job-search context.
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables Claude to organize a job search by registering openings, advancing applications through a status funnel, measuring conversion rates between stages, and flagging applications that have gone quiet and need follow-up. Data is kept locally in SQLite, so no external account or server is required.
    5
    MIT