# MCPHire — skill for AI agents

This is the canonical instruction document for any AI agent joining MCPHire. A human gives their agent a short prompt pointing here; the agent reads this file and follows the steps. Single source of truth — do not split these instructions across llms.txt / server.json; start and finish here.

## Your job as the agent

When your human user asks you to register them on MCPHire (or to hire candidates on their company's behalf), do the following end-to-end. Never skip the approval screen before any `register_*` call.

## Always — hard rules (apply to every flow)

These three rules are non-negotiable and apply whether the user is a job seeker or an employer:

1. Ask the user's permission before reading any local files; wait for an affirmative reply.
2. Show the user all collected answers and get explicit consent before any register_ call.
3. Never fabricate data — if a value is unknown, leave it empty.

## Which track

- User asked you to find work / register them as a job seeker → follow **Track A** (candidate).
- User asked you to hire / register their company → follow **Track B** (employer).
- A single user may be both — pick the track per request.

## 0. Connect

**Preferred: MCP over Streamable HTTP.**

The MCP endpoint is `https://mcp.mcphire.com/mcp`. If your client (Claude Desktop / Cursor / Cline / anything else) supports MCP, configure this entry once and restart the client:

```json
{
  "mcpServers": {
    "mcphire": {
      "type": "http",
      "url": "https://mcp.mcphire.com/mcp"
    }
  }
}
```

On macOS `~/Library/Application Support/Claude/claude_desktop_config.json`, on Windows `%APPDATA%/Claude/claude_desktop_config.json`. Full quit the client (⌘Q or File → Quit, not window close). Or run `curl -fsSL https://mcphire.com/install.sh | bash` for the same thing automated.

Once connected you'll see 22 tools: `search_jobs`, `get_job_details`, `get_salary_stats`, `apply_to_job`, `get_candidate_applications`, `get_candidate_questions`, `register_candidate_profile`, `get_candidate_verification_status`, `list_candidate_matches`, `get_candidate_cv`, `delete_candidate_profile`, `get_employer_questions`, `register_employer_profile`, `post_vacancy`, `publish_vacancy`, `get_employer_vacancies`, `search_candidates`, `get_applicants`, `shortlist_candidate`, `send_interview_invite`, `unpublish_vacancy`, `delete_employer_profile`.

**Fallback: REST.**

If your runtime can't speak MCP, most of the flow — registration, verification, matches, CV, the catalog, and the whole employer track — is available as a plain HTTP API with a unified response envelope. These endpoints exist and are supported:

```
# Candidate track (register → verify → read)
GET    https://api.mcphire.com/api/v1/candidate/questions       # step 1 of Track A over REST
POST   https://api.mcphire.com/api/v1/candidate/register
GET    https://api.mcphire.com/api/v1/candidate/verify?token=...
GET    https://api.mcphire.com/api/v1/candidate/matches?session_token=...
GET    https://api.mcphire.com/api/v1/candidate/cv?session_token=...
DELETE https://api.mcphire.com/api/v1/candidate/profile
POST   https://api.mcphire.com/api/v1/candidate/stealth         # visibility / catalog opt-in / blocklist

# Job search (NOTE the /v1 prefix, not /api/v1)
GET    https://api.mcphire.com/v1/jobs?q=python&perPage=20&directOnly=true

# Public catalog + employer-side candidate search
GET    https://api.mcphire.com/api/v1/candidates                # consented candidates
POST   https://api.mcphire.com/api/v1/employer/candidates/search # body: {employer_session_token, stack, ...}

# Employer track
GET    https://api.mcphire.com/api/v1/employer/questions        # step 1 of Track B over REST
POST   https://api.mcphire.com/api/v1/employer/register
POST   https://api.mcphire.com/api/v1/employer/vacancy
GET    https://api.mcphire.com/api/v1/employer/{employer_id}/vacancies
GET    https://api.mcphire.com/api/v1/employer/{employer_id}/vacancies/{vacancy_id}/applicants
```

**Not available over REST — MCP-only:** `apply_to_job` and `get_candidate_applications` have no REST twin yet. If your user needs to *apply* to a job (not just browse), you must use the MCP transport above. Everything else works over plain HTTP.

Every response is `{"success": bool, "data"?: obj, "error"?: string, "hint"?: string}`. On 429 rate-limit responses check `Retry-After` and `X-RateLimit-Remaining` headers.

## Track A — register a candidate (your human is a job seeker)

> **Prerequisite — a GitHub account is required.** MCPHire verifies candidate identity by having the user place a one-time token into a public GitHub artefact (profile bio, a pinned Gist, or a repo README). `proof_url` is accepted ONLY from `github.com` / `gist.github.com`. If your user has no GitHub, they cannot publish a resume here yet — tell them upfront before you spend time on the ~150 questions. Their CV and job matches unlock ONLY after this GitHub verification step (7) completes — until then the CV page shows an 'awaiting verification' stub, so do not promise the resume is live before that.

1. **Get questions.** Call `get_candidate_questions()` (optional args: `section`, `language="ru"|"en"` — default "ru"). You get ~150 questions in 11 sections.
2. **Ask permission, then answer from the user's local context.** First ask the user which files or directories they want to share and wait for an affirmative reply (see hard rule 1). Read only what the user explicitly confirmed: an existing CV/resume file, MD files and git log in project directories the user names, shared public profiles — or simply ask the questions in chat. Never read agent memory or unrelated files. Use the `hint_for_agent` on each question as guidance. If data is missing, leave the answer `null` — do not fabricate.
3. **Provenance on critical fields.** For these 8 fields you MUST attach `{source_excerpt (≤140 chars), source_file, confidence: high|medium|low|unknown}`:
    - `q_experience_commercial_years_total`
    - `q_experience_seniority_self`
    - `q_experience_managed_team_size_max`
    - `q_experience_shipped_products`
    - `q_experience_biggest_prod_system_users`
    - `q_experience_first_paid_coding`
    - `q_comp_floor_usd_monthly`
    - `q_comp_last_total_comp_usd`

   The server rejects a non-null answer to any of these without provenance.
4. **Collect observed_facts.** As a side-channel while reading context, emit SPO triples: `{subject, predicate, object, source_excerpt?, confidence}`. Predicates like `worked_at`, `contributed_to`, `shipped`, `mentored`, `studied_at`, `designed`, `refactored`. Start with `approved_by_user=false`; user flips to `true` during approval.
5. **SHOW AN APPROVAL SCREEN.** Present all answers + facts organised by section. The user corrects / confirms / rejects items. To confirm, the user must type the exact consent phrase (either language is accepted):
    - RU: `я согласен отправить анкету в MCPHire` (or `я согласна отправить анкету в MCPHire`)
    - EN: `I agree to submit my profile to MCPHire`

    **Do not call `register_candidate_profile`** until the user types one of those phrases. Pass it verbatim as `consent_phrase`. Any other input — update the screen and wait again.
6. **Register.** Call:

   ```
   register_candidate_profile(
     answers={...},
     consent_granted=true,
     provenance={q_id: {source_excerpt, source_file, confidence}},
     observed_facts=[{..., approved_by_user: true}, ...],
     questions_version="0.3.3"
   )
   ```

   **Required field:** `answers.q_artifacts_proof_url_primary` must be a public GitHub URL the user controls — `github.com/<user>` profile/bio, a pinned `gist.github.com` Gist, or a repo README — the claim-verifier `curl`s it to confirm ownership. A non-GitHub URL is rejected with **400 `GITHUB_REQUIRED`**; a missing one with **400**. Collect a valid GitHub URL before calling `register_candidate_profile`.

   **Public catalog opt-in (`q_catalog_visibility`).** Default is `no`: the profile stays reachable by its direct `/cv/<slug>` link and appears in employers' private `search_candidates`, but is NOT listed in the public catalog at mcphire.com/candidates. Set `q_catalog_visibility="yes"` only if the user explicitly wants to be discoverable by anyone browsing the catalog. Ask; don't assume.

   **The CV page carries no personal name by design.** Its title comes from `q_artifacts_tagline_one_liner` (role/one-liner) — anonymity until the candidate opts into contact via the shortlist flow. You may additionally pass `q_identity_display_name` (a free-form public name): it is NOT shown on the CV page, but is displayed on the catalog card at mcphire.com/candidates if the user opted in.

   You receive: `profile_id`, `session_token` (persistent bearer — store this), `claim_token`, `claim_instructions`, `expires_at`, `cv_url`.
7. **Claim ownership (GitHub).** Ask the user to paste the `claim_token` (shape: `mcphire-verify-XXXXXXXX`) into a public GitHub artefact they control — GitHub profile bio, a pinned Gist, or a repo README (the same URL they gave as `proof_url`). You can edit the bio for them if you have an appropriate MCP and explicit consent — always show the exact diff first. The claim verifier cron picks it up within 15 minutes. For instant re-check: `get_candidate_verification_status(claim_token)`.
8. **Share the CV.** Show the user their `cv_url`. That is their auto-generated public resume.
9. **Enable Telegram pushes.** Ask the user to DM `@mcphire_match_bot` with `/link <claim_token>`. After that, every new matching vacancy triggers an instant Telegram push with 3 inline buttons: 👀 View, ✅ Apply, 🙈 Hide.

### Applying and tracking applications

`apply_to_job(job_id, cover_letter, claim_token=<stored>)` — always pass the candidate's `claim_token` so the application is linked to their profile (the employer then sees it with full CV context). `get_candidate_applications(session_token=<stored>)` returns **only that candidate's own** applications — it requires the `session_token` (or `claim_token`); without an identity token it returns nothing. Applications sent without linking a profile are anonymous and cannot be listed back.

### Heartbeat

On every new conversation with the MCPHire skill, call `list_candidate_matches(session_token=<stored>, since=<last_check_iso>, limit=10)`. It returns a union of delivered pushes + pending queue, joined with jobs + companies. Each match carries `score`, `reasons`, `matched_at`, `source`. Empty list → say nothing (no spam).

## Track B — register an employer (your human is a hiring manager or founder)

1. **Get employer questions.** `get_employer_questions(section?, language?)` — ~40 questions in 7 sections (`company`, `team`, `culture`, `hiring_now`, `comp_band`, `process`, `integrations`).
2. **Answer from the company's public context.** GitHub org, website, careers page, README, team page. Focus on business facts, not internal employee PII.
3. **Approval screen, then register.** Show all answers. To confirm, the user must type the exact consent phrase (either language is accepted):
    - RU: `я согласен зарегистрировать компанию в MCPHire` (or `я согласна зарегистрировать компанию в MCPHire`)
    - EN: `I agree to register the company on MCPHire`

    Then call `register_employer_profile(answers, consent_granted=true, consent_phrase="<verbatim phrase>")`. Requires `q_consent_company_representation=true` plus the minimum set: `q_company_legal_name`, `q_company_slug` (URL-safe, 2-64 chars), `q_team_size`, `q_team_remote_split`, `q_team_lang`, `q_comp_currency`. Returns `employer_id` + `employer_url`.
4. **Post vacancies.** For each opening:

   ```
   post_vacancy(
     employer_id=...,
     title="...",
     description="...",
     skills=["..."],
     salary_from=..., salary_to=...,
     currency="RUB" | "USD" | "EUR" | ... any ISO-4217,
     level="junior"|"middle"|"senior"|"lead",
     work_format="office"|"remote"|"hybrid",
     city="..." or null,
     requirements=["..."]
   )
   ```

   **Vacancy goes live immediately.** As of 2026-07-10 there is NO verification gate for employers: `post_vacancy` creates the vacancy with `status=active` — it is instantly visible at its public `/jobs/<slug>` URL, enters the matching loop right away (matching candidates get a Telegram push), and needs no founder review. `publish_vacancy` is kept as an idempotent no-op for backwards compatibility — calling it on an active vacancy simply returns `already_active: true`. Per-employer anti-spam caps apply (default 5 vacancies/hour, 20/day) — exceeding them returns 429. So after `post_vacancy`, tell the user the vacancy is **live** and share the `job_url`. Check anytime with `get_employer_vacancies(employer_session_token=...)` — the session token alone is enough for it and for `get_applicants(vacancy_id, employer_session_token=...)`; the legacy `employer_id` parameter is deprecated and may be omitted. Currency codes are case-insensitive (`rub` and `RUB` both work; stored upper-case).

   Vacancies posted directly through MCPHire (yours) carry a **direct** badge and rank above aggregator imports in search and listings — so a freshly posted vacancy surfaces on the first screen, not buried under scraped jobs.

5. **Find candidates for a vacancy.** `search_candidates(employer_session_token, stack?, seniority?, timezone?, limit?)` searches the whole pool of verified candidates and returns masked cards: `profile_id_masked`, `cv_url` (public CV), `stack_summary`, `seniority`, `timezone`, `headline`. When the user says "find me a python engineer" / "найди мне питониста", call this with `stack=["python"]`. Contact details are never returned here — read the `cv_url`, then use `shortlist_candidate` to reach out; the candidate's contacts unlock only after they respond "interested" to the shortlist push. This tool is employer-only (needs a valid `employer_session_token`). The public, consent-only version of this pool is the catalog at **mcphire.com/candidates**.

## Privacy rules — server-enforced

The following field names are rejected in `answers` even if the user says yes: `age`, `gender`, `sex`, `nationality`, `citizenship`, `marital_status`, `children`, `religion`, `political_views`, `health`, `disability`, `visa_status`, `ethnicity`, `sexual_orientation`. Don't ask about them; don't fill them in with proxies.

Employer registration has its own blacklist — no employee PII in company answers.

Local MD files never leave the user's machine. Only the approved answers are sent to the server.

## Languages and currencies

- Tool-facing text supports `ru` (default) and `en`. Pass `language="en"` to `get_candidate_questions` / `get_employer_questions` for English wording of questions and hints.
- Salary fields accept any ISO-4217 currency. No server-side conversion; whatever currency you pass is preserved.
- Default currency in historical data is RUB because that's what most imports were denominated in, not because the service is locked to one market.

## Error recovery

Every REST error body is `{"success": false, "error": "...", "hint": "..."}`. MCP tool calls return a dict; errors surface as `{"error": "...", "message": "..."}`. If `hint` is present, use it to correct the request before re-trying.

Specific non-obvious failure modes:
- `register_candidate_profile` returns 400 if `proof_url` is missing — it's required.
- `claim_token` expires in 48 hours; if the user was slow, call `register_candidate_profile` again.
- `apply_to_job` without `claim_token` still works but the application is sentinel-only — the employer will NOT see the candidate's profile. Always pass the user's `claim_token` if they already have one.
- `list_candidate_matches` now prefers `session_token` over `profile_id`; use `profile_id` only if you still have code paths that stored it.

## Discovery & machine-readable manifests

- `https://mcphire.com/skill.md` — this file (start here).
- `https://mcphire.com/.well-known/mcp/server.json` — manifest with tool schemas, rate limits, supported languages, usage patterns.
- `https://mcphire.com/llms.txt` — compact agent overview.
- `https://mcp.mcphire.com/mcp` — live MCP endpoint (Streamable HTTP transport, JSON-RPC 2.0).
- `https://api.mcphire.com/api/v1/` — REST fallback base.

## Contact

- Founder: Tim Zinin — `@timofeyzinin` on Telegram, [timzinin.com](https://timzinin.com).
- Issues / partnership: open a GitHub issue on `TimmyZinin/mcphire-mcp` (private — request access).
