Skip to content

MCP connector

The Zero Hiring MCP connector exposes 13 domain-scoped tools that forward to the local zero CLI. Agents should prefer these tools over shelling out to zero directly.

Each tool takes the same payload shape:

  • command (string, required) — Zero action handled by this tool, e.g. jobs list or --help.
  • args (string[], optional) — positional values, flags, and flag values. Do not put the action in args only.

To discover flags for any action, call get_help with the action and --help:

tool: get_help
command: "candidates list"
args: ["--help"]

For workflows, pricing, and Jenny-specific guidance, see the agent skill reference.

Tools

Getting help

Tool ID get_help
Access Read-only

Inspect the current Zero account and discover available Zero actions. For 'what account / employer / user am I?' use command: "whoami" — it returns email, company, and employer_id from the backend. For 'what commands exist?' use command: "--help" (or command: "<subcommand>", args: ["--help"]). Also handles status show and billing reads.

Also accepts root discovery calls such as --help, --version, and <action> --help.

Allowed commands

help, whoami, status show, billing docs, billing show, billing costs

Managing authentication

Tool ID manage_auth
Access Write

Log in, log out, or update local authentication settings.

Allowed commands

login, logout, internal configure

Reading jobs

Tool ID read_jobs
Access Read-only

List jobs and inspect role details. For RPO accounts, use clients list first when the operator names a client, then pass --client-id <client_uuid> to jobs list or internal jobs list to see jobs under that specific client. When checking whether a role or requisition exists, or when enumerating a customer's roles, never conclude a job does not exist from a single unpaginated page. Prefer jobs list: it already returns every job for the employer in one call, so it needs no pagination flag. --all exists ONLY on internal jobs list (which otherwise returns just the first 20 rows) -- pass it in the same call rather than listing once and re-listing, and never pass --all to jobs list, which rejects it. If a result still carries a truncation warning (JSON: a jobs_list_truncated entry; table: a Showing N of T or use --all footer line), say so rather than presenting the list as complete. When a question is scoped to specific locations (city/state/site), answer from the location fields of the full paginated list, or pass the location as --search; never infer a job's location from its title or from earlier conversation context. Job list rows do NOT contain description text -- they carry description_chars and description_omitted instead, because a few dozen full descriptions exceed this tool's result limit. description_omitted: true means the job HAS a description that was left out; it never means the job lacks one (that is description_chars: 0). To answer 'which roles mention X?', pass internal jobs list --search X, which matches description text server-side. To read one description, use jobs show <job>. Only pass --with-description when you genuinely need every description at once, and expect a very large result.

Allowed commands

jobs list, jobs current, jobs show, internal jobs list, internal jobs get

Managing jobs

Tool ID manage_jobs
Access Write

Create, update, delete, select, or parse roles. hire run --description and internal jobs create/update --description are source material for the hosted JD generator; use --use-description-as-jd only for an explicit polished HTML final JD; plain text is still formatted with AI. Once you've run intake and assembled a structured role summary, pass it as --role-summary-json (a JSON object with at least title) on hire run: it is threaded into both JD generation and the job record so the JD, the job, and the pipeline brief all match the confirmed intake. For RPO accounts, pass --client-id <client_uuid> on hire run or internal jobs create to create the role under that client; omit it only for roles owned directly by the agency. For requests to create, add, set up, update, or load screening questions, interview questions, or custom questions, generate with internal ai generate-questions --structured --json when needed, then save with internal jobs update <job_id> --screening-questions-json <json>; the JSON must be an array of {text,order,source} question objects; source is optional but if set must be one of manual/ai/jenny/ats/legacy (any other value errors before the request is sent — ZER-1154 — instead of silently saving nothing) and the command errors loudly, after re-fetching, if fewer questions persisted than were sent. --custom-questions is legacy fallback only. For Google for Jobs (structured location + salary; ZER-1083), hire run, internal jobs create, and internal jobs update all accept: a remote flag to mark a fully remote role — --remote/--no-remote on hire run (also feeds JD generation), --is-remote/--no-is-remote on internal jobs create/internal jobs update; --location-street, --location-locality, --location-region, --location-postal-code, --location-country (2-letter ISO 3166-1 country code, e.g. US) for the physical address; and --salary-min, --salary-max, --salary-currency (3-letter ISO 4217, e.g. USD), --salary-period (one of YEAR, MONTH, WEEK, DAY, HOUR) for the pay range. All four salary flags are MANDATORY when creating a role with hire run or internal jobs create (ZER-2420): the CLI refuses the call with missing_salary, naming every flag you left out, and the backend rejects it with a 422 -- so settle the range during intake (internal ai suggest-salary computes one) instead of opening the role without it. They stay optional on internal jobs update, which only changes what you pass. The five location flags are optional, but the backend enforces them conditionally and returns a 422 with a clear message on violation: for a non-remote role, once ANY of the five location flags is set, --location-country plus --location-locality or --location-region become required (street/postal stay optional); --is-remote skips this entirely. On internal jobs update, once --salary-min or --salary-max is set, --salary-currency and --salary-period both become required. Surface a 422 or a missing_salary error on these flags to the operator verbatim rather than retrying blindly; it names the missing field. The existing freeform --location still works and keeps being used for display/back-compat.

Allowed commands

hire run, jobs use, jobs delete, internal jobs create, internal jobs update, internal jobs delete, internal jobs parse-jd

Reading candidates

Tool ID read_candidates
Access Read-only

Review candidates, applications, interview results, and transcripts. Use command: "candidates search" with the free-text query as the first arg (e.g. args: ["senior react engineer"], optionally with --source applied|sourced|ats, --page, --limit) when the operator asks something like 'who in my pool has done X', 'do we have any candidates who know Y', or otherwise wants to search ALL of the employer's candidates across every job and integration by name, email, title, skills, or resume content, including semantic matching -- it is not scoped to one job and takes no job id. Use candidates list <job> instead for job-scoped pipelines (rankings, per-job status, resume review for one role's applicants). Use candidates export <applicant_id> to produce a GDPR subject-access / portability bundle for one candidate (Art. 15/20); pass the applicant_id from candidates detail --json. The bundle is sensitive PII — return only what the requester needs and do not retain it.

Allowed commands

candidates list, candidates detail, candidates search, candidates export, results show, results detail, offers list, offers show, internal applications list, internal interviews results

Managing candidates

Tool ID manage_candidates
Access Write

Invite, send offers, reject, discard sourced candidates, rank, create applications or interviews, and upload files. Use candidates discard when the user wants to remove a sourced candidate from active sourcing; it also stops future outreach (the web UI's equivalent control is labeled "Stop outreach" -- same action). Use candidates hire to send a DocuSign offer; billing happens after candidate signature. close run remains a legacy alias. The backend refuses candidates hire/close run with an HTTP 409 hire_requires_candidate_contact when there is no evidence this candidate has ever actually engaged with us (no direct application, no reply to outreach, no interview they engaged with) -- this is a deliberate refusal, not a transient error; explain to the operator that the candidate needs recorded contact evidence first, do not retry the same call, and do not suggest a flag to bypass it (there is none). Use invite cancel <interview_id> to withdraw a pending interview invite before the candidate has started it (rejected with a conflict once started/finished). Use offers cancel <offer_id> to withdraw an in-flight offer letter; it voids the SignWell document and emails the candidate. Use candidates delete <applicant_id> --confirm to permanently erase ALL data for a candidate (GDPR Art. 17 right to erasure) — this is irreversible, requires the explicit --confirm flag, and should only be run to fulfil a verified erasure request. internal applicants create makes a brand-new, standalone applicant with no link to the sourcing pool -- use it only for a genuinely new candidate (a referral or direct apply) who was never sourced. Never call internal applicants create for someone who already has a sourced profile_id (from sourcing search or candidates list --source sourced): that creates a disconnected duplicate that breaks provenance and dedupe against the sourcing pool. Promote that person instead with sourcing add <profile_id> through manage_outreach (ZER-1650: this exact mistake required a hand repair of prod data on 2026-07-20). For invite send, always pass --interview-type explicitly. Use realtime for audio, phone_call for phone call / AI phone call / AI telephony invites, and video for video/avatar interview invites. Example phone args: ["--candidate", candidate, "--interview-type", "phone_call"]; example video/avatar args: ["--candidate", candidate, "--interview-type", "video"] (the CLI defaults video invites to the Jenny Anam avatar, avatar_id 80d32804-14cc-445c-9005-e641b41f8f18, unless an explicit --avatar-id is provided). Ambiguous invite send calls without --interview-type are rejected. For employer preview links after creating a role, use internal interviews test --job-id <job_id> --interview-type video --json; it creates a test interview with the same default Jenny Anam avatar and returns a clickable interview_url. For reference checks: first internal interviews create-reference --application-id <id> --job-id <job_id> --name <name> --email <email> to record who the reference is (returns a reference_id), then internal interviews create --interview-type <type> --applicant-id <id> --job-id <job_id> --use-case reference --reference-id <reference_id> to actually trigger the reference-check call/interview. create-reference alone does not contact anyone. The same candidate-contact-evidence gate as candidates hire applies to the reference-check create call: HTTP 409 reference_request_requires_candidate_contact means this candidate has no recorded engagement with us yet -- explain that plainly, do not retry, there is no override.

Allowed commands

invite send, invite manual, invite cancel, candidates hire, candidates reject, candidates discard, candidates delete, offers cancel, close run, internal applications create, internal applications rank, internal interviews create, internal interviews create-reference, internal interviews test, internal interviews trigger-call, internal applicants create, internal applicants update-phone, internal files upload

Reading outreach

Tool ID read_outreach
Access Read-only

Review saved sourcing searches, shortlists, sequences, enrollments, and outreach stats. Does not run sourcing search; use manage_outreach with command: "sourcing search" for that. Use command: "sourcing status" with args [search_job_id] to check a search that is still running — a search reported as sourcing_search_timeout was NOT cancelled, and this is how you pick it back up. Poll it every ~15s for at most 10 checks; once status is succeeded it returns a search_id, and sourcing show <search_id> returns the ranked results. If it is still running after 10 checks, hand the user the search_job_id and stop polling rather than looping on it. Never respond to a timed-out search by re-running it through manage_outreach: that bills a second provider call for a search already in flight.

Allowed commands

outreach sequences example-steps, outreach sequences list, outreach sequences show, outreach enrollments list, outreach stats, sourcing show, sourcing status, sourcing shortlist list

Managing outreach

Tool ID manage_outreach
Access Write

Run sourcing searches and manage shortlists, outreach sequences, and enrollments. For a normal sourcing search, call this tool once with command: "sourcing search" and args [query, "--job", job_ref, "--limit", limit, "--depth", "cheap", "--wait", "--wait-timeout", "90", "--json"], where job_ref is either the full job UUID or the 8-char short id exactly as shown by jobs list — never construct, pad, or guess a UUID; pass the short id as-is and the CLI resolves it. Always pass --wait-timeout 90: this tool is killed at 120s, and without the cap a slow search lost its search_job_id with the process instead of handing it back (ZER-1687); a --wait search whose --wait-timeout is missing or larger than that ceiling is rejected before any provider call is billed. Use the user's requested count when it is below 50, otherwise use "50" when the user asks for 50+, or gives no count. Run at most one sourcing search per user sourcing request; after one successful result, summarize and ask whether to continue rather than self-initiating another paid search. If the search returns code: "sourcing_search_timeout", the search is STILL RUNNING and was not cancelled — the message carries its search_job_id. Tell the user it is still running, then poll it with read_outreach command: "sourcing status" args [search_job_id] every ~15s and read the results with sourcing show <search_id> once it succeeds. Poll at most 10 times; if it is still running after that, hand the user the search_job_id and stop rather than polling indefinitely. Never re-run the search in that situation: the provider call is already billed and in flight, and a second one double-charges for the same work. For every explicit user request to run a normal sourcing search, you must invoke this tool with command: "sourcing search" — never answer from conversation memory or prior results. The one exception is the timeout case above, where the correct action is to poll the existing search rather than start a new one. This does not apply to explicit fake/demo, safe test-candidate, or experimental paper-mining requests, which use their documented commands below. If sourcing returns code: "sourcing_location_ambiguous" in blocks or warnings, do not retry or broaden the search; ask the user to clarify the location with country or City, Region/State, Country. Use "--source-more" only when the user asks to continue or get more of the exact same search without changing any wording of the criteria; if the new request's wording differs from the previous query in any way — added, removed, or reworded titles, locations, or qualifiers — run a fresh search using the user's exact wording as the query and do not pass "--source-more". Do not add keywords, industries, seniority, or other qualifiers the user did not say. If the user says to use an exact query or ignore job requirements, include "--query-only"; the job is still used for ranking/attribution but provider filters come from the query. If the user explicitly names multiple countries, cities, or regions, do not split them into separate tool calls; append one repeatable --location value per requested location in the same args array so the backend performs one combined search and the limit applies once across all locations. Use --limit 1 only for explicit smoke tests. Read the returned diagnostics before explaining outcomes: distinguish provider_empty from profiles returned but deduped_existing_count > 0, and never claim the provider database has no profiles solely because result_count is 0. If diagnostics include zero_results_reason: "filter_exhausted", tell the user this exact search was already exhausted on the filter_exhausted_since date and that rewording the search or asking again after the backend's TTL will re-check the network; do not report it as "no candidates exist". In dev/staging, normal sourcing returns demo candidates; if the user explicitly asks for real candidates, add --real and the backend caps that live non-production call to 5 profiles. To promote a sourced candidate into the job's applicants list, call command: "sourcing add" with args [profile_id, "--job", job_ref], using the profile_id from a sourcing result -- this is the only correct path from a sourced profile to an applicant and it keeps provenance/dedupe intact against the sourcing pool. Never use internal applicants create (a manage_candidates command) for a candidate who already has a sourced profile_id; that creates a disconnected duplicate instead of promoting the existing sourced record. For fake/demo sourcing, call command: "sourcing demo-candidates add" with args ["--job", job_id, "--json"]; this creates the same five fake sourced candidates, including Muluken and Alan, without calling PDL. For safe demos, create a sourced-style test candidate with command: "sourcing test-candidate add" and args ["--job", job_id, "--email", test_email, "--phone", test_phone, "--json"]; this uses the real sourced application/outreach pipeline without emailing real sourced candidates. For stop-only requests like 'stop outreach for Jane but keep them in sourcing', use command: "outreach enrollments stop-by-candidate" with args [candidate_ref, "--job", job_id, "--json"]; this stops future emails without discarding the candidate. For outreach sequences create in MCP/Jenny, pass steps with --steps-json; the server cannot read Claude-local files or stdin. Use outreach cancel <enrollment_id> (optionally with --reason and --burn-bridges) as the stronger revert primitive: it terminates an active/paused enrollment like stop does, and --burn-bridges additionally blocks that candidate from ever being re-enrolled into the same sequence/job again. For an experimental paper-mining search, call command: "sourcing experimental search" with args ["--job", full_job_uuid, "--limit", "10", "--wait", "--wait-timeout", "90", "--json"] (same 120s ceiling and same sourcing status follow-up as a normal search); it mines recent academic papers for researchers in the role's field, ranks them by citation velocity, and adds them to the job. This is experimental: best for research-heavy roles, and contact-data coverage varies.

Allowed commands

sourcing search, sourcing experimental search, sourcing demo-candidates add, sourcing add, sourcing shortlist add, sourcing test-candidate add, sourcing invite, outreach sequences create, outreach sequences activate, outreach sequences deactivate, outreach enroll, outreach cancel, outreach enrollments pause, outreach enrollments resume, outreach enrollments stop, outreach enrollments stop-by-candidate

Reading pipelines

Tool ID read_pipelines
Access Read-only

Inspect hiring pipeline runs and platform status.

Allowed commands

status show, pipeline status, pipeline list

Managing pipelines

Tool ID manage_pipelines
Access Write

Create, advance, abort, or simulate hiring pipeline work. demo populate <job_id> realistically fills a job's demo pipeline in one call -- candidates across stages, scores, and interview activity -- sized by --scale (one of auto, high, mid, senior; defaults to auto). Distinct from demo seed, which always creates exactly 5 fake candidates and adds none on a re-run; use demo populate when the operator wants a job that looks realistically filled rather than a minimal 5-candidate pipeline.

Allowed commands

pipeline run, pipeline advance, pipeline abort, pipeline autopilot, demo run, demo seed, demo interviews, demo populate

Reading admin data

Tool ID read_admin_data
Access Read-only

Review team members, employer analytics, and RPO clients. Use clients list to see all managed clients; clients show <client_id> for details. In dev/staging only, internal jenny calls|chats ... reads Jenny live-call transcripts and chat logs for debugging (returns 404 in production).

Allowed commands

team list, internal employers analytics, clients list, clients show, internal jenny calls list, internal jenny calls show, internal jenny chats list, internal jenny chats show

Managing admin data

Tool ID manage_admin
Access Write

Invite teammates by email with team invite (team add is a legacy invitation alias), remove members, change employer records, manage RPO clients, or run internal AI helpers. This tool is write-access, so most MCP clients require a human to approve each call before it runs. If a call here (including internal employers create) returns something like "No approval received", that is the MCP client declining to run an unapproved write call -- it is not a Zero backend error, not a permissions or allowlist failure, and not a regression in org creation. Tell the operator to approve the tool call in their client and retry. This is unrelated to the real backend spend-approval gate on payments refund (see the Refunds section of the skill), which requires a live human typing approved_by at an interactive prompt and is genuinely unsatisfiable over MCP by design -- unlike this per-call client approval, that command has no MCP tool at all, on purpose. team invite --email <email> --employer-id <id> (ZER-1185) targets an employer other than your own -- this only works for an allowlisted demo-admin inviting into an employer flagged is_demo=true (e.g. adding a presenter/teammate to a demo org you just created with internal employers create --demo); anyone else passing a non-own --employer-id gets a 403. Omit it to invite into your own employer as before. For RPO client management: clients add --name <name> creates a new client; clients remove <client_id> soft-deletes one. internal ai generate-jd accepts --role-summary-json (a JSON object with at least title) to fold the confirmed structured intake into the generated JD, and --markdown to convert the draft from HTML to Markdown -- use --markdown whenever the draft is going into a chat reply (ZER-2468), since the chat renderer refuses raw HTML; never paste the plain --json job_description field into a reply. Use internal ai generate-questions --structured --json to draft role-specific screening, interview, or custom questions, then save them with manage_jobs internal jobs update --screening-questions-json; do not stop at a draft unless the operator explicitly asked to review one first. internal ai suggest-salary --title <title> --location <location> (ZER-2467) computes a recommended salary range during intake -- salary is never optional and never something to skip asking about. Present the result as an ESTIMATE the operator owns, never as market data Zero measured (there is no benchmark data source behind it, it's an LLM estimate), and offer exactly three options: accept the suggested range, give a different number, or (only when the response's disclosure_required is false) not disclose one. When disclosure_required is true, a range is legally required for that posting -- say so instead of silently dropping salary or treating non-disclosure as available. internal employers create provisions a demo tenant with --demo (plus optional --logo-url/--website-url). demo login create --employer-id <id> (ZER-1192) mints (or rotates) a ready-to-use employer-owner login for that demo org and returns {email, password, login_url} -- hand these directly to the customer as the demo handoff; only works on is_demo=true employers. team invite stays the way to add named internal people (presenters, sales) to a demo org -- it is not the customer handoff. demo mode on|off --employer-id <id> flips demo mode on an existing employer without deleting any data.

Allowed commands

team invite, team add, team remove, clients add, clients remove, internal employers create, internal ai generate-jd, internal ai generate-questions, internal ai extract-skills, internal ai suggest-salary, demo mode, demo login create

Reading the changelog

Tool ID read_changelog
Access Read-only

See what's new on the platform: recent releases merged across the whole product (backend + client), most recent first. Use command: "changelog show" for a 'what's new' / 'what shipped recently' / 'what are your new features' question -- optional --since <ISO date> and --limit <N> (default 10) narrow the window. This always reads the live feed at call time; never answer a changelog question from memory or from your own tool list -- an answer whose items don't trace back to this feed is the failure mode this tool exists to prevent. Do not filter results on a release's highlight field: it only controls whether the in-app What's New modal auto-opens, not whether the release shipped, and most releases have it set to false.

Allowed commands

changelog show

Reading interviews

Tool ID read_interviews
Access Read-only

List and inspect human (hiring-manager) interviews -- the scheduling coordination flow between a candidate's application and an interviewer, distinct from AI screening interviews. interviews list filters with --job-id, --application-id, --status (one of pending_slots, awaiting_candidate, scheduled, completed, cancelled, declined, expired), and --upcoming (only scheduled interviews with a future start time, soonest first; otherwise newest first). Example: command: "interviews list", args: ["--job-id", job_id, "--status", "awaiting_candidate", "--json"]. interviews show <interview_id> returns the full interview including its slots array (each with start_at, end_at, status) plus applicant_name, job_title, interviewer_display_name, and (employer-side) schedule_url -- the public link the candidate uses to pick a time; share the candidate's scheduling status rather than the raw link unless the operator explicitly asks for the URL. interviews availability (--interviewer-user-id or --interviewer-email, plus optional --duration, --days, --timezone) checks the interviewer's connected Google Calendar for free slots -- always call this before proposing times so slots come from real availability; the response's connected field is false when no calendar is linked, in which case propose times manually instead.

Allowed commands

interviews list, interviews show, interviews availability

Managing interviews

Tool ID manage_interviews
Access Write

Create, propose slots for, reschedule, cancel, complete, or leave feedback on a human (hiring-manager) interview. interviews create requires --application-id plus an interviewer: either --interviewer-user-id <uuid> (a platform member/reviewer) or --interviewer-email (optionally with --interviewer-name) for an ad-hoc interviewer. Optional: --title, --duration (minutes, 5-480, default 45), --location-type (one of video, phone, onsite; default video), --location-details (meeting URL / phone note / address), --timezone (IANA, e.g. America/New_York; default UTC), --message (shown to the candidate). Only one active (non-terminal) human interview is allowed per application at a time; the CLI surfaces the backend's conflict error if one already exists. Once that interview reaches a terminal status (completed, cancelled, declined, expired), a further interviews create call succeeds and starts the next round -- the backend auto-increments a round number per application; interviews show/interviews list --json return it, and the job's human_interview stage config may separately carry an advisory expected_rounds -- surface both when relevant rather than presenting a second interview as unexpected. Status machine: pending_slots (created, no slots yet) -> awaiting_candidate (slots proposed, candidate emailed) -> scheduled (candidate picked a time; confirmations + calendar invites sent) -> completed | cancelled | declined (candidate said no times work) | expired (all proposed slots passed unpicked). declined/expired can be re-proposed back to awaiting_candidate with interviews propose-slots. Propose 1-5 --slot values (repeatable) on interviews create or interviews propose-slots <interview_id>; each must be ISO8601 with an explicit UTC offset or Z, e.g. 2026-08-01T15:00:00-04:00 -- times without a timezone are rejected. Creating with --slot proposes immediately and emails the candidate that same call; creating without --slot leaves the interview in pending_slots for a later interviews propose-slots call. Example: command: "interviews create", args: ["--application-id", application_id, "--interviewer-email", "hm@client.com", "--interviewer-name", "Jamie Lee", "--slot", "2026-08-01T15:00:00-04:00", "--slot", "2026-08-02T15:00:00-04:00", "--json"]. interviews reschedule <interview_id> takes new --slot values (1-5, same ISO8601 rule) and an optional --reason; it clears any existing selection and re-emails the candidate, moving the interview back to awaiting_candidate. interviews cancel <interview_id> takes an optional --reason and works from any non-terminal status; notifies the candidate (and the interviewer too, if it had already been scheduled). Confirm with the operator before cancelling an interview that is already scheduled -- the candidate and interviewer have a specific time on their calendar. interviews complete <interview_id> marks a scheduled interview done. interviews feedback <interview_id> takes --rating (1-5) and/or --note; at least one is required. Before proposing slots, call read_interviews interviews availability for the interviewer and propose 2-5 of the returned times -- when the interviewer has Google Calendar connected, scheduling also creates a real calendar invite with an automatic Google Meet link (no need to set --location-details for video interviews in that case).

Allowed commands

interviews create, interviews propose-slots, interviews reschedule, interviews cancel, interviews complete, interviews feedback

Reading the hiring flow

Tool ID read_hiring_flow
Access Read-only

Show a job's hiring flow (stage plan) -- the resume gate, AI interview, and optional reference/human interview checkpoints a candidate moves through -- or draft a new one with AI. hiring-flow show <job_id> returns the saved plan, or the fully-automated smart default (resume gate -> AI interview) if none has been saved yet. hiring-flow draft is stateless -- it does not save anything -- and takes optional --role-summary-json (same shape as hire run --role-summary-json), --job-title, and --feedback to steer a redraft. Call hiring-flow draft right after role intake, before opening the role, so the operator confirms the flow alongside the JD; there are only three stage types -- resume_assessment, ai_interview, human_interview -- and human_interview is present in every draft but disabled by default unless the role warrants it -- say so plainly rather than letting the operator assume every draft includes a live human step. A reference check is NOT a stage type: it is a sibling reference_check config on the plan (enabled, count 1-5), not a fourth stage -- building a reference_interview stage object is rejected by the backend.

Allowed commands

hiring-flow show, hiring-flow draft

Managing the hiring flow

Tool ID manage_hiring_flow
Access Write

Save a hiring flow (stage plan) for a job with hiring-flow set <job_id> --stages-json <json>. Accepts either a bare JSON array of stage objects, or a full {"stages": [...], "source": "...", "reference_check": {...}} object -- pass an edited hiring-flow draft output straight through. --source defaults to manual and should stay that way once the operator has reviewed or edited the draft; only use ai to persist an unedited AI draft as-is. Enabling the resume_assessment stage syncs its config.threshold into the job's real auto-invite gate; disabling it sets the gate to 0 -- confirm the operator actually wants that before saving. To configure the reference check, pass --reference-check-json '{"enabled": true, "count": 2}' (count 1-5) -- it overrides any reference_check already present in --stages-json. If neither is given, the job's existing saved reference_check is preserved as-is; omitting both does NOT disable an already-configured reference check. Prefer threading an approved draft into hire run --stage-plan-json (manage_jobs) at role-creation time instead, so the plan is persisted atomically with the job; use hiring-flow set to change the flow (or just the reference check) on an already-created job. Always confirm the stages with the operator before calling hiring-flow set -- it overwrites the saved plan and can change the live auto-invite gate.

Allowed commands

hiring-flow show, hiring-flow draft, hiring-flow set

Reading the saved company profile

Tool ID read_company_profile
Access Read-only

Check what's already saved on the caller's own employer profile (company name, website, logo, and company_information) before asking the operator for any of it. Use command: "clients profile". Writes nothing. ZER-2462/ZER-2463: never ask the operator to retype a company name or website that's already on file -- call this first, every time first contact comes up, including in a brand-new conversation with an employer you've talked to before; only ask for or generate whatever comes back empty.

Allowed commands

clients profile

Previewing a company profile

Tool ID propose_company_profile
Access Read-only

Preview a company name and website before saving them to the caller's own employer profile. Use command: "clients propose-company-profile" with --company-name and --website-url. Makes no backend call and saves nothing -- it only normalizes and echoes the values back so they can be read out to the operator for confirmation -- it performs no lookup of its own, so it can never fill in a company name you don't already have (resolve that with generate_company_info rather than asking the operator to type it). Check read_company_profile first: only run this for whichever of name/website isn't already saved.

Allowed commands

clients propose-company-profile

Saving a company profile

Tool ID confirm_company_profile
Access Write

Save the company name and website staged by propose_company_profile to the caller's own employer profile. Use command: "clients confirm-company-profile" with the SAME --company-name and --website-url values just previewed -- call this only immediately after propose_company_profile and an explicit, unambiguous yes to that exact preview, in the same exchange. A verbal yes is not itself a save -- nothing is persisted until this call executes. Its response may include a logo_url (whatever is currently on file, if anything); if one comes back, mention it to the operator as part of confirming what was saved, and never ask them for a logo URL -- there is no argument for one here. Also accepts an optional --company-information (see generate_company_info) in the SAME call to save that too. The backend resolves the employer from the caller's own identity -- this tool has no employer-id argument and can only ever write the caller's own profile, never another employer's.

Allowed commands

clients confirm-company-profile

Drafting a company description from the website

Tool ID generate_company_info
Access Read-only

Draft company_information (the company description/FAQ text the AI interviewer uses to answer candidate questions) from the employer's website. Use command: "clients generate-company-info" with --website-url. Makes no write -- pass the result to confirm_company_profile's --company-information to save it. The first-contact profile isn't fully captured until this is saved, not just the name and website. This is ALSO how you resolve the company name from a website instead of asking the operator to type it: the description is written from their real site and names the company, so read the canonical name out of it and pass that to propose_company_profile. So run this whenever company_information is empty OR the company name is still unknown, and run it only once per website -- one call yields both, and both go into the same confirm_company_profile write.

Allowed commands

clients generate-company-info

Install

See the MCP server README for Node requirements, Claude Desktop .mcpb bundles, and HTTP transport setup.