freehire
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| FREEHIRE_TOKEN | No | Your freehire API token (fhk_...). Can also be stored in ~/.freehire/creds.json. | |
| FREEHIRE_API_URL | No | Base URL of the freehire API (default: https://freehire.dev). |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| whoamiA | Return the authenticated freehire user (verifies the API key). Call this to confirm auth before other tools. |
| profileA | Return the caller's saved job-search profile: their specializations, target skills, excluded skills, location preferences, and contact-free CV. Call this before searching or tailoring to ground your work in what the candidate already told freehire. |
| facetsA | List the market's filter vocabulary: every facet's live values with a vacancy count each, plus the skills list and numeric ranges. Call this FIRST to discover real values for |
| searchA | Search open jobs by keyword with optional facet filters. Each result carries the job's FULL description as markdown alongside title, company, location and public_slug, plus the total match count — so you can screen a whole result set without calling |
| market_fitA | Score a skill list against the live open-vacancy market for a filtered role: headline coverage (% of vacancies listing ≥1 skill), must-have skills held, and the missing skills that unlock the most vacancies. Here |
| jobA | Fetch a single job's full content by slug (title, company, location, posting URL, description). |
| companyA | Fetch a company and its open jobs by company slug. |
| applyB | Mark a job as applied for the authenticated user. Idempotent. |
| saveA | Bookmark a job for later. Idempotent. |
| unsaveA | Remove a job's bookmark. A no-op if it was not saved. |
| stageA | Set a job's application stage. The server validates the value; valid stages are applied/screening/responded/interview/offer/accepted/rejected/withdrawn. |
| noteA | Attach a free-text note to a tracked job (overwrites the existing note). |
| myA | List the caller's tracked jobs (viewed/saved/applied) with their stage and note. Filter narrows the set. |
| cv_listA | List the caller's tailored CVs, newest edit first, each with the vacancy it was written for. Use this to find the id the other cv_* tools take, or to check whether a vacancy already has a tailored copy. |
| cv_tailorA | Start tailoring a CV to a vacancy, returning the tailored CV's id (plus the base it was copied from and the bound session). Idempotent per vacancy — calling it again for the same slug reopens the copy that already exists, so it is safe when you do not know whether one exists. It spends one of the candidate's AI credits the first time it creates the copy (402 when the balance will not cover it) and 409s when they have no résumé on the site to seed a base CV from — tell them to upload one; you cannot. It does not call a model: the reframing is your work, through cv_context and cv_edit. |
| cv_contextA | Show the cached fit-analysis context a tailored CV should reframe toward: verdict, recommendation, dimension comments, and the requirement split — missing_have (reframe existing evidence) vs missing_gap (ask the candidate before adding). Read this before editing. |
| cv_getA | Fetch a tailored CV with its full document (header, summary, experience bullets, skill groups). |
| cv_editA | Apply a batch of path-addressed edits to a tailored CV, atomically. Read cv_context and cv_get first — indices are counted over the document cv_get returned. THE HONEST WALL: editing with an API key edits as the tailoring agent, and the server enforces it — the candidate's own name, email, phone and links are refused, and anything stating what they DID needs evidence_id, the id of something they asserted themselves. One uncited op rejects the whole batch. For a missing_have requirement, reframe an existing bullet; for a missing_gap, ask the candidate before writing anything. A bad path is a 422 and the CV is untouched. Export with cv_render. |
| cv_renderA | Render a tailored CV to an ATS PDF, returned as a base64 resource (application/pdf). The bytes are large and not human-readable to the model — call it to produce the deliverable, not to inspect content. |
| experience_listA | Read the candidate's whole experience bank: every employment and the achievements attached to it, plus the ones attached to no place under |
| experience_add_employmentA | Record a place where evidence was produced — a job or a side project. At least company or role is required. Use the returned id to attach achievements with experience_add_achievement. |
| experience_add_achievementA | Record one piece of evidence — the sentence a CV bullet would carry. Only record what the CANDIDATE told you; the server stamps anything created here |
| experience_update_employmentA | Correct a place already in the bank. Only the fields you pass change — everything else is carried over from what is banked, because the API replaces the whole row. Use this to fix a name, a role, or dates. |
| experience_update_achievementA | Correct an achievement already in the bank — most often a typo in the claim, which cannot be fixed by re-adding it. Only the fields you pass change; metrics and skills REPLACE the whole list when given. Correcting does NOT change who is held to have said it: an agent_inferred achievement stays agent_inferred and stays uncitable on a CV. Confirming it with the candidate then recording what THEY said is the only way it becomes citable. |
| experience_remove_achievementA | Delete one achievement from the bank. There is no undo, so confirm with the candidate first — read it back to them and remove it only on a clear yes. It takes nothing else with it. This is how a duplicate goes: keep the richer entry, remove the other. |
| experience_remove_employmentA | Delete a place from the bank. It must hold no achievements — removing a place would delete everything recorded under it, and the server refuses that here (409). To retire a duplicate place, move its achievements to the one being kept with experience_update_achievement, then remove the empty shell. There is no undo; confirm with the candidate first. |
| submitB | Submit a vacancy for moderation. The server stores it as pending and returns it. URL (the dedup key), title, and company are required. |
| my_submissionsA | List the caller's own vacancy submissions with their moderation status. |
| contributeA | Hand freehire a link to a vacancy or an ATS company career board (the same intake the website and CLI use). If the posting already exists you get its slug back; if we can read the page it is imported into the catalogue; if the company's board is one we do not crawl yet, it is queued for onboarding. The answer's status is one of found, tracked, imported, review or queued. Check sent links later with my_contributions. |
| my_contributionsA | List the vacancy and company career board links the caller has contributed, with their status: pending (queued for onboarding), review (manual triage), onboarded (now crawled) or rejected. |
| check_jobs_openA | Check whether postings are still open, by the public_slug search results carry — up to 50 per call. Use it before presenting a shortlist or acting on a saved one: each posting answers open, closed or not_found, with when it closed or was last seen live. |
| similar_jobsA | Postings most similar to one posting, by its slug, nearest first — for "show me more like this". Empty for the newest postings, whose neighbours are not computed yet. |
| salary_statsA | Salary percentiles (25th, median, 75th) from open postings that state pay, scoped by role family, seniority and country, with the sample size behind each figure. Currencies and pay periods are reported separately, never converted. |
| job_market_statsA | How many open postings match a set of filters, and their most common role families, seniorities, skills, work modes, countries and regions. Values a filter does not recognise are dropped and named in the answer — fix them before quoting a figure. |
| list_filter_valuesA | The values one job-search filter accepts — skills, categories, seniorities, sources, countries and the rest — each with how many open postings carry it, most common first. A value not listed matches no posting. Use it before filtering on a value you are not sure of, and to fix one a search reported in ignored_values. |
| subscribeA | Create a job alert: a saved search over ANY of the job search's filters whose new postings are delivered to the person on the channels their account has connected (email, Telegram, the mobile app, a webhook) — this conversation is not notified. e.g. remote senior Go roles in the EU paying at least 80k EUR is {"filters":{"work_mode":["remote"],"seniority":["senior"],"skills":["go"],"regions":["eu"],"salary_currency":["EUR"]},"salary_min":80000}. Run the same search with |
| list_subscriptionsA | List the person's job alerts with every setting: id, name, a readable filter summary AND the search as the structured object the alert tools take (query, filters, salary_min/max; |
| update_subscriptionA | Change one of the person's job alerts by its id. Read its current search from list_subscriptions and pass only what changes — every field is optional: name; the search (query, filters, salary_min, salary_max — each replaces that part, an empty filters object clears the facet filters, a salary bound of 0 clears it; website-only filters in |
| unsubscribeA | Stop one job alert by its id, on every channel or on the one named. Deletes the alert's delivery on those channels and their record of matches; the saved search itself stays on the account as a bookmarked search. To hold an alert for a while, pause it with update_subscription instead. |
| subscription_updatesA | List the new postings the person's job alerts have matched — the same matches their alerts are built from, newest first, still-open postings only. Narrow to one alert by id, and to a window by since (RFC 3339) or days (default 7, max 30). An alert only matches postings that appeared after it was created, and matching runs every few minutes, so a new one starts empty. delivered says whether an alert already carried the posting. |
| jobs_addA | Moderator: create a hand-curated job. URL is the dedup key — re-adding the same URL updates the posting. |
| jobs_editA | Moderator: partially update a manual job by slug. Only the provided fields change; the URL identity is not editable. Requires the moderator role (403 otherwise). |
| submissions_pendingA | Moderator: list the pending submission review queue. Requires the moderator role (403 otherwise). |
| submission_approveA | Moderator: approve a pending submission, minting a live job. Requires the moderator role (403 otherwise). |
| submission_rejectA | Moderator: reject a pending submission with an optional reason. Requires the moderator role (403 otherwise). |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 45 tools
Most tools target distinct resources or actions, but `facets` and `list_filter_values` both discover filter vocabulary and could easily be confused. Generic names like `my`, `job`, `company`, and `stage` add minor ambiguity, though descriptions generally clarify intent.
All names use lowercase snake_case, but the conventions are mixed: many tools are bare nouns or verbs (`job`, `company`, `stage`, `note`, `my`) while others use prefixes (`cv_*`, `experience_*`, `list_*`, `update_*`). Plurals and prefixes are inconsistent (`submission_approve` vs `submissions_pending`, `list_subscriptions` vs `subscription_updates`), though names remain readable.
45 tools is heavy for a job-search and CV-tailoring server, spanning search, tracking, CV editing, experience-bank CRUD, alerts, contributions, and moderator actions. The breadth explains the count, but the surface feels bloated and exceeds the recommended range.
The server covers core lifecycles well: job search, tracking, CV tailoring, experience-bank CRUD, alerts, contributions, and moderation. Minor gaps remain, such as no clear way to update the profile, delete a tailored CV, or fully untrack a job beyond unsaving.