freehire.me: search 3.3M+ IT jobs from 294K company boards, track applications, tailor CVs.
Strong tool definitions with comprehensive descriptions and well-structured schemas. All 18 tools have explicit registrations with descriptions (10-392 chars, median ~180). Input schemas use Zod with type constraints and descriptions. Tool annotations (readOnlyHint) present on read-only tools. Key strengths: facets/search/market_fit have exceptionally detailed descriptions explaining API quirks (OR-group geography, ignored filters, discovery-first patterns). Weaknesses: some parameter descriptions are generic (e.g., 'Free-text note to store on the job'); cv_patch lacks examples of valid JSON Patch operations; no output schemas documented; error handling relies on ApiError wrapping but lacks recovery guidance for specific failure modes.
Mark a job as applied for the authenticated user. Idempotent.
Fetch a company and its open jobs by company slug.
Fetch the cached fit-analysis context a tailored CV was seeded with: the job's description, the candidate's base CV, and the market fit for the role. Use this to understand what the tailoring was optimizing for, or to seed a follow-up tailoring in a different tool.
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.
Edit a tailored CV's content (markdown). The edit is a JSON Patch (RFC 6902) applied to the document: a list of {op, path, value} objects. `op` is add/remove/replace/move/copy/test; `path` is a JSON Pointer (RFC 6901) into the document, e.g. /sections/0/content; `value` is the new content (for add/replace) or the source path (for move/copy). Patches are atomic: either all operations succeed or none do. A patch that would leave the document invalid (e.g. removing a required section) is rejected with a 400 and a hint.
Output schemas not documented. Tools return raw API JSON (ok() function) but LLMs cannot plan downstream calls without knowing field structure. E.g., search returns 'total', 'ignored', 'data' but this is inferred, not declared.
cv_patch parameter 'patch' lacks examples and format guidance. RFC 6902 JSON Patch is complex; LLMs need concrete examples (e.g., {"op": "replace", "path": "/sections/0/content", "value": "new text"}) to avoid malformed patches.
Error handling lacks recovery guidance. ApiError wrapping returns status codes and messages, but tools do not guide LLMs on retry logic, user-fixable vs fatal errors, or next steps. E.g., 402 on cv_tailor (insufficient credits) should suggest 'purchase credits' or 'use existing CV'.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 62 | 2026-07-28+ | v2 |
Fetch a tailored CV's current content (markdown). Use this to review what was written before editing or rendering.
Render a tailored CV as a PDF (base64-encoded). The PDF is a snapshot of the current content; edits after rendering are not reflected in the PDF.
Start tailoring a CV to a job: returns the tailored CV's id, the base it was copied from, and the bound agent session. Idempotent per vacancy: calling it again for the same slug returns the copy that already exists rather than making a second one, so it is safe to call when you do not know whether one exists. It debits an AI credit the first time it creates the copy (402 when the balance will not cover it) and 409s when the candidate has no résumé to seed a base CV from. It never calls a model itself.
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.
Fetch a single job's full content by slug (title, company, location, posting URL, description).
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.
List the caller's tracked jobs (viewed/saved/applied) with their stage and note. Filter narrows the set.
Attach a free-text note to a tracked job (overwrites the existing note).
Bookmark a job for later. Idempotent.
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. 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.
Set a job's application stage. The server validates the value; valid stages are applied/screening/responded/interview/offer/accepted/rejected/withdrawn.
Remove a job's bookmark. A no-op if it was not saved.
Return the authenticated freehire user (verifies the API key). Call this to confirm auth before other tools.
Generic parameter descriptions on write tools. 'Free-text note to store on the job' (note tool) and 'Application stage, e.g. interview, offer, rejected' (stage tool) lack constraints. stage should enumerate valid values; note should specify length limits or formatting rules.
Tool composition: cv_tailor, cv_context, cv_read, cv_patch, cv_render form a chain but cv_tailor description does not mention that cv_context must be called first to understand the tailoring context. Undocumented dependency increases misuse risk.