MCP Server
Nico exposes a Model Context Protocol (MCP) server that lets AI assistants like Claude interact with Nico directly.What can the MCP server do?
The MCP server provides 13 tools in three groups: job posting search (job_posting_*, discover openings in Nico’s index), job application management (job_application_*, your own tracked applications), and interviews (schedule and manage interviews on your applications).
Job posting search (
job_posting_search, job_posting_get) searches Nico’s shared posting index — not your own applications. To check “have I already saved this?”, use job_application_search. Posting search requires job search to be enabled for your account.Connecting your AI assistant
The server URL for all assistants is:Claude (Desktop and claude.ai)
- Go to Settings → Connectors and choose Add custom connector.
- Enter a name (e.g. “Nico”) and the server URL above. Leave the advanced OAuth fields blank.
- Click Connect and approve access on Nico’s consent screen.
ChatGPT
Custom MCP connectors in ChatGPT require developer mode, which is available on paid plans (Plus, Pro, Business/Enterprise).- Enable developer mode: go to Settings → Apps & Connectors → Advanced settings and turn on Developer mode.
- Go back to Apps & Connectors and choose Create to add a custom connector.
- Enter a name (e.g. “Nico”), the server URL above, and select OAuth as the authentication method. Leave the client ID and secret blank — ChatGPT registers itself automatically.
- Complete the connection and approve access on Nico’s consent screen.
The MCP server requires OAuth authentication with the
mcp scope — the connector requests it automatically. See OAuth for details on the flow, scopes, and revoking access.Job posting search
job_posting_search
Search Nico’s job postings index — the same data behind the in-app job search at/jobs. Returns the most recent matching postings, newest-first, in compact form (no application URL or description — fetch those with job_posting_get).
Parameters:
City radius search:
Pass
city together with country_code (plus region for US/CA) to filter postings within radius_km of that city. Cities are resolved by exact, case-insensitive match against Nico’s existing geocoded-locations index — there is no live geocoding fallback, so unknown or ambiguous names return an error rather than a best-guess match.
"engineering manager", "software engineer" — also matches postings filed under that role whose title says it differently: "Software Engineering Manager", "Manager, Engineering". The widening stops at director level: "engineering manager" never pulls in "Director of Engineering" or "VP Engineering", and "director of engineering" matches only director and VP titles.
A phrase with a level word — "senior software engineer", "staff software engineer" — widens to that role’s titles at the same level only: "Sr. Software Engineer", "Senior Platform Engineer", "Software Engineer III", "Senior Software Developer". Pass title_levels: 1 to take one level up or down as well (mid-level and staff titles for a senior phrase). Pass title_exact: true to keep every phrase literal.
The response’s title_expansions says what widened — one entry per phrase, with level (null for a phrase without a level word) and every other title now included — so you can tell the user the search covered them:
title_exact: true.
Example response:
total_count stops counting at 100 (total_count_capped is then true); walk further pages with next_page, which is null on the last page. sort is newest unless your account has ranked search, in which case a sort: "match" request returns match here and a match_rank on each result. Each result’s job_application is null until you have acted on that posting, otherwise the id and status of the application you already have.
Search results omit url and description to stay compact. Take a result’s id and call job_posting_get to retrieve the application URL and the full description.
Errors:
The tool returns a single-key error object when the request can’t proceed:
country_code is required—country_codeis mandatory on every call.region (state/province) is required for city search in US(orCA) —regionmust be set whencityis given and the country isUSorCA.Location not found: <city>— the city wasn’t found in Nico’s geocoded locations index.Ambiguous location: multiple matches for <city> — please refine— more than one row matched; narrow withregion.Unknown employer: <name>— a name inemployersdidn’t match any employer.Ambiguous employer: multiple matches for <name> — please refine— a name inemployersmatched more than one employer; refine the spelling.Job search is not enabled for this account— the calling account doesn’t have job search enabled.
job_posting_get
Get one job posting by theid returned from job_posting_search. Returns the same fields as a search result, plus the external application url and the full description (the two fields the search list omits).
Parameters:
Example response:
{ "error": "Job posting not found" } for an unknown id. Same feature-flag gate as job_posting_search.
Job application management
job_application_search
Search your job applications by URL or company name (case-insensitive). URL matching is canonical: a tracking parameter, trailing slash, differently-cased host, fragment or email-forward wrapper all resolve to the same application, exactly asjob_application_create’s duplicate check does. The two tools therefore always agree about whether a job is already tracked.
Parameters:
*At least one of
url or company_name is required.
Example response (search by URL):
job_application_list
List your job applications with optional filtering and pagination. Parameters:
Example response:
job_application_get
Get the full details of one job application, including its notes and interviews. Parameters:
Example response:
{ "error": "Job application not found" } for an unknown id.
job_application_parse_url
Extract structured job data from a job posting URL. Nico automatically detects the job board and parses the posting. Parameters:
Example response:
null fields and an error_reason explaining why. no_title_extracted means Nico reached the posting but could not pull anything usable from it; in that case treat company_name as unreliable rather than as a finding, since it may have been derived from the domain.
You can pass a link forwarded by email. Gmail and Outlook rewrite links to their own redirect (
google.com/url?q=…, Outlook SafeLinks); Nico unwraps those to the real posting URL, so a forwarded job parses and de-duplicates exactly as the original link would.job_application_create
Add a job application to your board. It is created as adraft unless you pass status: "proposed", which marks it as a suggestion for the account owner to approve. Those are the only two values accepted — every later status is reached through job_application_transition, which is what records the change and keeps the history correct.
The starting status no longer depends on who is calling. It previously became proposed automatically for anyone who wasn’t the account owner, which was invisible from the caller’s side.
Parameters:
Idempotency: If this job is already tracked, the tool returns the existing application with
"created": false rather than creating a duplicate. This is a success, not an error — the desired end state has been reached, so there’s nothing to retry. Every response carries created, and both cases return the same shape, so there is no need to call job_application_search first just to avoid a duplicate.
Matching is on the canonical URL, so the same posting is recognised through a tracking parameter, a trailing slash, a differently-cased host, or an email-forward wrapper:
status and possible_transitions, an agent that finds a job already tracked can act on it — advancing it with job_application_transition, say — without a follow-up read.
Example response:
job_application_update
Correct the details of an existing application. Only the fields you pass are changed; everything else is left alone. Parameters:status is not editable here. Use job_application_transition — a status change has to go through the application’s lifecycle so the history is recorded.job_application_transition
Move an application to a new status by firing a lifecycle event. Which events are valid depends on where the application currently is — every response includespossible_transitions for exactly this reason.
Parameters:
Events:
Example response:
job_application_add_note
Add a note to an existing job application. Notes are visible to all team members. Parameters:
Example response:
Interviews
Interviews belong to an application. Schedule them withjob_application_add_interview, then read them back with interview_list — either for one application or across your whole account.
Times are ISO 8601. If you leave the UTC offset off —
2026-08-03T14:00 — Nico reads the time on your clock, using the timezone on your account. Include an offset when you want to be explicit.interview_list
List interviews, soonest first. Passjob_application_id to see one application’s interviews; leave it off for every interview on your account.
Parameters:
Example response:
filter value is ignored rather than rejected — you get all interviews.
Returns { "error": "Job application not found" } when job_application_id names an application that doesn’t exist or isn’t yours.
job_application_add_interview
Schedule an interview on an application. Parameters:
Example response:
Adding an interview does not change the application’s status. Move it to
interviewing yourself with job_application_transition.
interview_update
Reschedule or edit an existing interview. Only the fields you send are changed; everything else is left alone. Parameters:
Send at least one field besides
interview_id. The response is the same shape as job_application_add_interview, with the updated values.
Errors: