Skip to main content
Glama
freehire

freehire

by strelov1

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
FREEHIRE_TOKENNoYour freehire API token (fhk_...). Can also be stored in ~/.freehire/creds.json.
FREEHIRE_API_URLNoBase 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

CapabilityDetails
tools
{
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
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 search and market_fit — do not invent facet values. A filter key the API does not recognise is ignored rather than refused, so a result wrapped as {data, ignored} counted a broader market than the one asked for — retry with the suggested name before quoting any number from it. A VALUE it does not recognise comes back in ignored_values (e.g. {"skills": ["golangg"]}): it matched nothing, so fix the spelling from this list rather than reporting an empty market.

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 job per hit. Use the returned slug with job, apply, save, etc. How to search well: a role has several names, so join two or three in one query with an uppercase OR, e.g. title:SRE OR title:"site reliability". Location, work mode, seniority and skills go in their filters, never in the query. If suspiciously few results come back, loosen one constraint at a time and say which. Present results as a table of title, company, location, posted date and link, naming the source. Two traps worth knowing. Geography (region/country/city) is ONE OR-group: passing region AND country widens rather than narrows, so drop the region to search a single country. And a filter param the API does not recognize is ignored rather than refused — the search still runs, just broader — so when a result carries an ignored list, its total answers a wider question than the one asked; retry with the suggested name before reporting the number. A value the API does not recognise (a misspelt skill, say) is reported in ignored_values and matches nothing — a total of 0 beside it is a typo, not an empty market: take the right value from list_filter_values and retry.

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 skills is the MEASURED set, not a filter — use facet params to define the role. One skill probes that skill's demand. A result wrapped as {data, ignored, ignored_values} measured something other than what was asked: fix the named param or value and retry before quoting it.

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 unplaced. Each achievement carries a PROVENANCE — cv_import, stated_in_chat and manual mean the candidate asserted it and it may be cited on a CV; agent_inferred means a model read it into the record and it may NOT. Take evidence_id for cv_edit from here.

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 manual, which means they asserted it, and that stamp is what lets it be cited on a CV. A claim already in the bank, under any spelling, is refused as 409 rather than duplicated — so correct the existing one with experience_update_achievement instead of re-adding it.

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 search first so the person sees what they will get. The search must narrow the catalogue. Values not in a filter's vocabulary are dropped and named in ignored_params. Leave channels empty for the default (email when alert mail is on, otherwise the first connected channel). The answer's alert.id addresses the alert from then on. Saves the search on the account (or reuses the one already selecting the same postings); nothing existing is changed.

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; other holds website-only filters an update keeps), whether it is paused, and each delivery channel with its active flag, pending matches and last delivery. delivery_schedule is the account-wide digest timing every alert shares.

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 other are kept); channels (the full set to deliver on — added ones must be connected, missing ones stop); paused (true stops every channel, false resumes them — prefer it to unsubscribe for holding an alert). The new search must still narrow the catalogue, and unknown values are dropped and named in ignored_params. 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}. Digest timing is account-wide and not changed here. A removed channel's record of matches is deleted with it.

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. description is stored and rendered as HTML. Requires the moderator role (403 otherwise).

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

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A3.6/5.0

Scored across 45 tools

Disambiguation4/5

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.

Naming Consistency3/5

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.

Tool Count2/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues