MCP tool reference

What an AI assistant connected to First can actually do — every tool it can call, and what each one changes.

The First MCP server: connect an AI assistant to First to research a talent market, run the full role lifecycle (create, configure, edit the application process, publish, pause, close, archive), source candidates from the organisation's own candidate database and from the public web, and report on hiring.

Point your assistant at the First MCP server and sign in. Assistants that support it will sign you in with your First account; for tools that need a static credential instead, generate a personal API key in First under Profile, then Personal access tokens.

{
  "mcpServers": {
    "first": {
      "url": "https://api.firststage.co/mcp",
      "headers": { "Authorization": "Bearer fst_..." }
    }
  }
}

Every tool below is authorised as you: it can only reach the organisations your account belongs to, roles you are allowed to see are the only ones it can read, and changing a role needs the same admin rights it would in the app. Tools marked CHANGES DATA alter or withdraw something people can see — a good assistant will ask you before running one.

How your assistant is told to use these tools

The server hands every assistant these instructions when it connects. They are worth reading: they are what makes an assistant source against agreed criteria rather than guessing, and they explain the sourcing workspace that ties a run of searches together.

First is a recruitment platform. These tools research the talent market, manage roles, source candidates from the organisation's own candidate database and from the public web, and report on hiring.

Every tool but one takes an organisationId. Never ask the user for it — call list_organisations and use what comes back. Most people belong to exactly one organisation, so use it without checking; ask which they mean only when there are several.

The hiring workflow, when someone is hiring rather than asking a one-off question. Offer it, do not force it — and never walk someone through it who only wanted a number or a database search:

1. RESEARCH the role and market — research_talent_market for demand, pay and who else is hiring. Then suggest a role, with their go-ahead before you create anything.
2. CAPTURE the intake — ask for the hiring manager's brief in whatever form it exists: notes, an interview transcript, a job description. There is no form to fill in, so take it as it comes.
3. GENERATE the criteria — draft the role from that brief and generate_suggested_criteria from it. These are the same criteria that later score applicants.
4. SOURCE candidates — search the database against those exact criteria, then present matches for the user to add.
5. PUBLISH — once they are happy, create the role, apply the same criteria, add the candidates and publish. Only then are those candidates assessed, against the very same criteria.

The thread through 3, 4 and 5 is the point: one set of criteria, agreed once, used to source and then to assess. Steps can be skipped when the user already knows what they want, but do not skip the criteria.

At step 4 there are two places to look for people, and they are alternatives rather than stages — say which one you are using:

- The organisation's OWN database (source_candidates_from_draft_role). Fast, free, and everyone it returns has applied before, so First already holds their CV and assessment history. Look here first: re-engaging a known candidate beats cold-sourcing a stranger.
- The PUBLIC WEB (search_web_for_candidates). Finds people who have never applied. Slower, quota-limited, and it saves nothing on its own — pick the profiles worth keeping and pass those to save_web_candidates, which enriches them into the same workspace the database searches use. Reach for it when the database is thin or the user asks for the web, LinkedIn, or "outside our database".

The rest of this section is about the database search. The web search is driven by a query string instead of criteria, so it is described entirely in its own tool description.

Sourcing the database always goes through source_candidates_from_draft_role — no role needs to exist.

Never search the database on a job title alone. A title-only search matches anyone whose CV contains those words, so every candidate comes back looking strong and the band tells the user nothing. Every database search goes out with criteria behind it.

Before the first search, gather two things — usually one question:

- Seniority, or years of experience.
- A few must-have skills or technologies.

Then call generate_suggested_criteria and search with the result as criteriaChanges. That is the normal path, not the thorough one.

If the user will not be drawn — "just search", "I don't know yet" — do not block them and do not fall back to a bare title. Infer sensible seniority and skills from the title and whatever context you have, pass your inference as the title and keywords, generate criteria from it, run the search, and say plainly what you assumed: "I've assumed senior-level and a TypeScript/React stack — tell me if that's wrong and I'll redo it." An assumption the user can correct beats a broad search they cannot interpret.

How much the user invests changes only how good the criteria are:

- FAST: seniority and a few skills, or your stated inference.
- BETTER: a job description too, giving the generator far more to work with — and the criteria are the same set you later apply to the role, so the people you source are matched against the requirements they will later be assessed on.

Show the criteria when the user is shaping the role; keep them in the background when they just want candidates. Either way, they exist.

A match band is a fast skill-overlap signal, not a preview of the assessment score: sourcing compares a candidate's skills, past roles and previously-met criteria, while assessment evaluates their CV and application against every criterion. Present a band as "looks a strong fit to source", never as the score they will assess at.

Each addition — more skills, a job description, refined criteria — improves the SAME search; you do not switch tools. Sharpening is always worth offering after the first page.

Sourcing workspaces are a session you carry:

- Omit `reference` on the first search. The response returns one — reuse that exact value for every later search in the same piece of hiring work, and pass it to add_sourced_candidates.
- Reusing the reference is how you get MORE people: each search excludes candidates already saved to that workspace, so repeating it returns the next page. Inventing a new reference starts over and re-returns the same candidates.
- A search only returns the page it sourced. get_sourced_candidates lists everything saved under a reference, so use it if you have lost track of earlier results or want to show the user everything found so far before adding anyone.

Searching a location: 'radius' needs coordinates and 'country' needs an ISO code, so resolve the name first with geocode_place or geocode_country. If either returns found: false, fall back to searchType 'everywhere' rather than guessing coordinates.

Once the user wants to commit: create_role, then update_role_process with the same criteria you sourced on, then add_sourced_candidates with the reference, then get_role to check readiness and publish_role. Sourced candidates are only assessed once the role is published.

Before creating a role, gather what it needs: a title, a job description and at least one location are required to publish. Employment type, workplace type and compensation are not required but make the listing usable. Ask the user for anything missing rather than guessing.
1 toolOrganisations

The organisations a credential may act on — where the organisationId every other tool needs comes from.

list_organisations

List organisations

The organisations you can act on, with their ids. Every other tool needs an organisationId — call this first to find it rather than asking the user for one. Returns exactly one organisation for most people: use it without asking.

This tool takes no arguments.

12 toolsRoles

Role lifecycle: create, configure, edit the application process, publish, pause, close, archive.

list_roles

List roles

List an organisation's roles, optionally filtered by status. Only returns roles the token owner is allowed to see. Use status 'imported' to find ATS-synced roles awaiting adoption.

FieldTypeDescription
organisationId*string
statusesarray of one of: imported, draft, open, paused, closed, archived
get_role

Get a role

A role's current state: its details, criteria and questions, and readiness — which required details are still missing before it can be published.

FieldTypeDescription
organisationId*string
roleId*string
create_role

Create a role

Create a new draft role. The token owner becomes the hiring manager. Location and category names that don't exist in the organisation are created automatically.

FieldTypeDescription
organisationId*string
name*stringThe role title
internalNamestring
shortDescriptionstring
descriptionstring
workplaceTypeone of: on-site, hybrid, remote, remote-first, flexible
employmentTypeone of: full-time, part-time, contractor, internship, volunteer
roleWebsiteUrlone of 2 shapes
compensationobject
locationNamesarray of string
categoryNamesarray of string
templateIdsarray of stringRole template ids to seed criteria and questions from
duplicate_role

Duplicate a role

Duplicate an existing role into a new draft, copying its details, settings, criteria and questions. Returns the new role's id.

FieldTypeDescription
organisationId*string
roleId*stringThe role to copy
import_role

Adopt an imported role

Adopt a role synced from the ATS: moves it from 'imported' to a draft the team can configure and publish. Use list_roles with status 'imported' to find adoptable roles.

FieldTypeDescription
organisationId*string
roleId*string
update_role_details

Update a role's details

Update a role's details. Provide only the fields to change; everything else keeps its current value. Location and category names replace the existing sets. The response lists any fields locked to a connected ATS which kept their synced values.

FieldTypeDescription
organisationId*string
roleId*string
namestring
internalNamestring
shortDescriptionstring
descriptionstring
workplaceTypeone of: on-site, hybrid, remote, remote-first, flexible
employmentTypeone of: full-time, part-time, contractor, internship, volunteer
roleWebsiteUrlone of 2 shapes
compensationobject
locationNamesarray of string
categoryNamesarray of string
publish_role

Publish a role

Publish a role: opens it for applications, putting it live on the job board and apply page and approving any criteria still awaiting review. Also resumes a paused role and reopens a closed one, restarting progress towards any pause goal. Fails with the missing fields when the role's details are incomplete.

FieldTypeDescription
organisationId*string
roleId*string
pause_role

Pause a role

Pause an open role: candidates can still apply, but new applications wait to be assessed until the role is resumed with publish_role. Fails when the role is not open.

FieldTypeDescription
organisationId*string
roleId*string
close_roleCHANGES DATA

Close a role

Close an open or paused role: candidates can no longer apply. Reopen it with publish_role. Fails when the role is not open or paused.

FieldTypeDescription
organisationId*string
roleId*string
archive_roleCHANGES DATA

Archive a role

Archive a role: hides it from active lists and stops new applications. Archived roles can be restored in First. Fails when the role is already archived.

FieldTypeDescription
organisationId*string
roleId*string
update_role_processCHANGES DATA

Update a role's application process

Apply changes to a role's application process: its criteria and questions. Each change is one item of the shared process-change contract, applied in order. Criteria changed on a live role may await review and are approved when the role is next published. Use get_role first to see the current criteria and questions.

FieldTypeDescription
organisationId*string
roleId*string
changes*array of one of 18 shapesThe process changes to apply, in order
remove_role_process_componentCHANGES DATA

Remove a criterion or question from a role

Remove a criterion or question from a role's application process by process component type and subtype. For stage criteria (type 'assess-stage'), pass the specific criterionId — otherwise every stage criterion on the role would be removed.

FieldTypeDescription
organisationId*string
roleId*string
type*one of: collect-basic-details, check-requirements, check-skills-and-experience, test-knowledge-and-behaviour, ask-video-question, survey, assess-stage, legacy-criterion, legacy-question
itemType*one of 7 shapes
questionIdstring
criterionIdstringRequired for stage criteria (type 'assess-stage'): without it, every stage criterion on the role would be removed
2 toolsApplications

Search the organisation's applications.

search_applications

Search applications

Search the organisation's applications by name or email across every role. Only returns applications for a role the token owner is allowed to see. Use this to find a specific person by name or email address. Omit `query` to list rather than search: `list: "hired"` with no query lists hires, which is how you answer "who have we hired recently" — there is no search term that returns them all. Each row carries `hiredAt`, `shortlistedAt` and `rejectedAt`, so filter to a date window on those rather than on `createdAt`, which is when the application arrived rather than when the outcome happened. **Read `truncated` before quoting any total.** When it is true this is a partial answer — `found` is a lower bound and paging to the end will not reach the rest, so say the count is at least that rather than stating it as the number. Only when `truncated` is false is `found` the whole answer.

FieldTypeDescription
organisationId*string
querystringName or email to search for. Omit to list the whole of `list` instead — there is no term that returns every hire.
fieldone of: email, fullNameRestrict matching to one field; omit to match either
listone of: inbox, shortlisted, rejected, hired, all
pageinteger
get_applicationREAD ONLY

Get an application

One application's record: which stage it has reached, when it was hired, shortlisted or rejected, where the candidate came from, and its assessment score with the criteria version behind it. Search finds the person; this says what happened to them. Returns not found for a role you are not allowed to see. Their work and education history comes back too, taken from the CV already held against the application — use this rather than the web-sourcing tools for anyone who has applied before: it costs nothing and it is their own CV, not a third-party guess at it. Pass includeProfile: false to leave it out.

FieldTypeDescription
organisationId*string
applicationId*stringThe application's id, as returned by search_applications.
includeProfilebooleanSet false to leave out the candidate's CV history. Included by default.
9 toolsSourcing

Source candidates from the organisation's own indexed candidate pool — for an existing role, or against a draft before any role exists — and from the public web, and add them as applications.

source_candidates

Source candidates for a role

Search the organisation's own indexed candidate pool for people matching a role's live criteria, and save a page of matches to the role's sourcing workspace. Choose the scope with searchType: 'radius' (near a location), 'country' (a whole country by ISO code) or 'everywhere'. Each call excludes candidates already saved for the role, so repeating it returns the next page. Add the returned candidates with add_sourced_candidates.

FieldTypeDescription
organisationId*string
roleId*string
searchType*one of: radius, country, everywhereThe scope of the search: 'radius' (near a location), 'country' (a whole country by ISO code) or 'everywhere' (the whole organisation).
locationobjectOnly for searchType 'radius': the coordinates to source within a commute radius of. Ignored for 'country' and 'everywhere'.
radiusKmnumberOnly for searchType 'radius': commute radius in km around the location. Defaults to 50km.
countrystringOnly for searchType 'country': the country to restrict to, as an ISO 3166-1 alpha-2 code (e.g. 'GB'). Ignored for the other scopes.
limitintegerHow many new candidates to source, save and return — one page of results. Defaults to 4. Each search excludes everyone already saved for this role, so a follow-up search returns the next page.
add_sourced_candidates

Add sourced candidates to a role

Add previously sourced candidates to a role as applications. The contactIds are the ids returned by a source tool. For candidates sourced from a draft role, pass the same `reference` that search used. The response lists the ids that were added; ids that no longer resolve to a saved candidate are simply absent.

FieldTypeDescription
organisationId*string
roleId*string
contactIds*array of stringThe ids of the saved sourced candidates to add to the role as applications — the `id` values from a prior source-candidates response.
referencestringThe workspace the candidates were saved under. Pass the `reference` from a draft-role search; omit for candidates sourced directly for this role.
source_candidates_from_draft_role

Source candidates from a draft role

Search the organisation's own indexed candidate pool against a draft role — no role required. Pass `criteriaChanges` from generate_suggested_criteria: that is what makes the search discriminate, and gathering a seniority and a few skills first is enough to generate them. A job description sharpens them further. A title on its own is a last resort — it matches anyone whose CV carries those words, so everyone scores strongly and the bands stop meaning anything. Omit `reference` on the first search and reuse the one returned for every later call — each search excludes candidates already saved to that workspace, so repeating it returns the next page. Choose the scope with searchType: 'radius', 'country' or 'everywhere'.

FieldTypeDescription
organisationId*string
title*stringThe draft role's job title.
jobDescriptionstringThe draft role's job description, once written. Sharpens the discipline match and pulls the role's own skills into the search.
keywordsarray of stringSkills or keywords to aim the search at when there is no job description or criteria yet, e.g. ['React', 'Node.js']. The quickest but least precise input.
criteriaChangesarray of one of 18 shapesThe draft's agreed criteria — the `changes` from generating suggested criteria. The most accurate input: apply the same set to the role on creation and sourced candidates match on the requirements they are later assessed against. Only skills-and-experience changes feed the match.
referencestringThe sourcing workspace to save matches into. Omit on the first search and reuse the `reference` returned in the response for every later call, so follow-up searches page through results rather than repeating them. Pass it to the add endpoint to promote saved candidates into a role.
searchType*one of: radius, country, everywhereThe scope of the search: 'radius' (near a location), 'country' (a whole country by ISO code) or 'everywhere' (the whole organisation).
locationobjectOnly for searchType 'radius': the coordinates to source within a commute radius of. Ignored for 'country' and 'everywhere'.
radiusKmnumberOnly for searchType 'radius': commute radius in km around the location. Defaults to 50km.
countrystringOnly for searchType 'country': the country to restrict to, as an ISO 3166-1 alpha-2 code (e.g. 'GB'). Ignored for the other scopes.
limitintegerHow many new candidates to source, save and return — one page of results. Defaults to 4. Each search excludes everyone already saved for this role, so a follow-up search returns the next page.
get_sourced_candidates

List a sourcing workspace

List every candidate saved under a sourcing `reference`. Searches only return the page they sourced, so use this to see everything accumulated so far — after losing track of a page, or before deciding who to add to a role. An empty or unknown reference returns an empty list.

FieldTypeDescription
organisationId*string
reference*stringThe sourcing workspace to list.
clear_sourced_candidatesCHANGES DATA

Empty a sourcing workspace

Remove every candidate saved under a sourcing `reference`. This also resets what searches exclude, so a later search on the same reference can return people it had already saved. Starting a fresh reference achieves the same without discarding anything, so prefer that unless the user asks to start this one over.

FieldTypeDescription
organisationId*string
reference*stringThe sourcing workspace to empty.
search_web_for_candidatesREAD ONLY

Search the web for candidates

Search the PUBLIC WEB for candidate profiles with a Google X-Ray query (e.g. site:linkedin.com/in with OR-grouped job titles and must-have skills). This is the ONLY tool that finds people who have never applied to this organisation — every other search reads its own candidate database. It saves nothing: pass the profiles worth keeping to save_web_candidates. There is no location parameter — put the location into the query text.

FieldTypeDescription
organisationId*string
query*stringA Google X-Ray query, e.g. site:linkedin.com/in with OR-grouped job titles and must-have skills. There is no location parameter — put the location scope into the query text.
numintegerHow many results to return, 1-10. Defaults to 3.
startintegerThe 1-based index of the first result, for paging. Defaults to 1; step by `num` to page.
dateRestrictstringRestrict results by age: a unit letter followed by a number — "d7" (past 7 days), "w2" (past 2 weeks), "m6" (past 6 months), "y5" (past 5 years). The letter comes first.
save_web_candidates

Save web-sourced candidates

Enrich up to 10 web-sourced profiles and save them to a sourcing workspace. This is the save step for web results — the database searches already save their own. Pass the `reference` from an earlier search to keep everything in one workspace, or omit it to start one and reuse the reference that comes back.

FieldTypeDescription
organisationId*string
contacts*array of objectThe profiles worth keeping, built from a prior web search. Enrichment is billed per contact, so send only the ones the caller has decided on.
referencestringThe workspace to save into. Pass the `reference` from an earlier search to keep everything together; omit to start a new one, whose reference comes back in the response.
geocode_place

Resolve a place to coordinates

Turn a city or place name into the coordinates a 'radius' search needs. Call this before sourcing near somewhere, then pass the coordinates as `location`. Returns found: false when the name is not an actual city (a whole country, or unrecognisable) — use geocode_country for a country, or fall back to searchType 'everywhere'.

FieldTypeDescription
organisationId*string
name*stringThe place to resolve, e.g. 'London' or 'Leeds, UK'.
geocode_country

Resolve a country to its ISO code

Turn a country a recruiter named — 'United Kingdom', 'UK' or 'GB' — into the ISO 3166-1 alpha-2 code a 'country' search needs. Call this before sourcing country-wide, then pass the code as `country`. Returns found: false when the value is not a recognisable country.

FieldTypeDescription
organisationId*string
name*stringThe country to resolve, e.g. 'United Kingdom'.
1 toolCriteria

Generate a role's assessment criteria from its title and job description.

generate_suggested_criteria

Generate suggested role criteria

Generate a role's assessment criteria from its title and job description. Returns process changes ready to source against (pass them as `criteriaChanges` to source_candidates_from_draft_role) and to apply to a role (update_role_process) — using the same set for both is what makes sourced candidates match on the requirements they are later assessed against. Nothing is created or changed: show the criteria to the user, adjust if they want, then source. `requirementsPreview` shows what the matcher will score against. ALWAYS pass `roleId` when the role already exists — when you are working on a role rather than drafting one. That is what pulls in its intake call transcripts and note-taker summaries, which say what the hiring manager actually asked for; a job description is often boilerplate. Omitting it silently generates from the description alone. Omit it only while the role is still a draft.

FieldTypeDescription
organisationId*string
title*stringThe role title.
jobDescriptionstringThe role's job description. The richer this is, the better the criteria.
keywordsarray of stringSkills the hiring manager called out, when there is no job description yet.
roleIdstringAn existing role to draw intake material from. Its saved intake call transcripts and note-taker summaries are read in alongside the job description. Omit when the role does not exist yet.
1 toolResearch

Research the talent market for a role — pool depth, pay and who else is hiring. The only tools here that reach the web.

research_talent_market

Research a talent market

Research the talent market for a role — how deep the pool is, what it pays, and who else is hiring for it. Use it before committing to a hire, to sanity-check a salary, or to tell the user whether a location is realistic. Searches the web, not the organisation's candidate database (source_candidates_from_draft_role does that). Nothing is created or changed. `location` is a place as the recruiter would say it — 'London', 'the UK' — not coordinates or an ISO code. A market it finds nothing for returns found: false; say so rather than retrying.

FieldTypeDescription
organisationId*string
roleTitle*stringThe role to research, e.g. 'Software Engineer'.
location*stringWhere to research it, as the recruiter would say it — 'London', 'the UK', 'Berlin'. Not a coordinate or ISO code.
seniorityLevelstringThe seniority to narrow to, e.g. 'senior', 'lead'.
additionalContextstringAnything else that shapes the market, e.g. an industry or a required technology.
3 toolsReporting

Monthly usage reporting.

get_monthly_reporting

Get monthly recruitment reporting

Monthly reporting for an organisation: one row per role with application volumes (in period, total, last 7/30 days, rolling daily averages), hires and time to hire (average/median days), completion funnel percentages (started, complete, shortlisted), stalled and aged application counts, role status and whether the role uses AI assessment. Defaults to the latest available month. Use this to compare roles; use get_reporting_summary for org-wide trends and get_stage_reporting for pipeline bottlenecks.

FieldTypeDescription
organisationId*stringThe organisation to report on.
monthstringReport month in YYYY-MM format. Omit for the latest available month.
get_stage_reporting

Get pipeline stage reporting

Pipeline funnel reporting for an organisation: one row per role per pipeline stage, in pipeline order, with how many applications reached each stage and the average/median days spent in it. Use this to find where candidates drop off or wait the longest (pass-through rates, bottlenecks). Defaults to the latest available month. Months before stage metrics existed return no rows.

FieldTypeDescription
organisationId*stringThe organisation to report on.
monthstringReport month in YYYY-MM format. Omit for the latest available month.
get_reporting_summary

Get organisation reporting summary

Organisation-level trend reporting: one row per month with totals across every role — application volumes, hires and time to hire (average/median days), completion funnel percentages, aged applications and the share of roles using AI assessment. Use this first for topline questions and month-on-month trends. from/to are inclusive YYYY-MM bounds (at most 24 months); omit both for the trailing 12 months. Months without a report are omitted.

FieldTypeDescription
organisationId*stringThe organisation to report on.
fromstringFirst month in YYYY-MM format, inclusive.
tostringLast month in YYYY-MM format, inclusive.

The same operations are available over HTTP — see the REST API reference.

First.cx logo
Copyright © OpenDigital Limited 2026

We use cookies to analyse site traffic and improve your experience. By clicking "Accept", you consent to our use of analytics cookies. See our Privacy Policy for details.