> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nico-jobagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server

> Connect AI assistants to Nico via the Model Context Protocol

# MCP Server

Nico exposes a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that lets AI assistants like Claude interact with Nico directly.

## What can the MCP server do?

The MCP server provides 12 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).

| Tool                            | Group            | Type  | Description                                                                      |
| ------------------------------- | ---------------- | ----- | -------------------------------------------------------------------------------- |
| `job_posting_search`            | Posting search   | Read  | Search Nico's job postings index                                                 |
| `job_posting_get`               | Posting search   | Read  | Get one posting's full detail, incl. the application URL                         |
| `job_application_search`        | Application mgmt | Read  | Search your saved applications by URL or company name                            |
| `job_application_list`          | Application mgmt | Read  | List your applications with filtering and pagination                             |
| `job_application_get`           | Application mgmt | Read  | Get full details of one application, incl. notes and interviews                  |
| `job_application_parse_url`     | Application mgmt | Read  | Extract structured data from a job posting URL                                   |
| `job_application_create`        | Application mgmt | Write | Add a new proposed application to your board                                     |
| `job_application_add_note`      | Application mgmt | Write | Add a note to an existing application                                            |
| `interview_list`                | Interviews       | Read  | List all your interviews, with time filters (today, next 7 days, upcoming, past) |
| `job_application_interviews`    | Interviews       | Read  | List the interviews for one application                                          |
| `job_application_add_interview` | Interviews       | Write | Schedule an interview for an application                                         |
| `interview_update`              | Interviews       | Write | Reschedule or edit an existing interview                                         |

<Note>
  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.
</Note>

## Connecting your AI assistant

The server URL for all assistants is:

```
https://mcp.nico-jobagent.com/mcp
```

Authentication is handled via OAuth — you never paste a token, API key, or client ID. Nico supports [dynamic client registration](/integrations/oauth#client-registration), so your assistant registers itself automatically. When you add the connector, you're redirected to Nico's consent screen; click **Authorize** and you're connected.

### Claude (Desktop and claude.ai)

1. Go to **Settings → Connectors** and choose **Add custom connector**.
2. Enter a name (e.g. "Nico") and the server URL above. Leave the advanced OAuth fields blank.
3. 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).

1. Enable developer mode: go to **Settings → Apps & Connectors → Advanced settings** and turn on **Developer mode**.
2. Go back to **Apps & Connectors** and choose **Create** to add a custom connector.
3. 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.
4. Complete the connection and approve access on Nico's consent screen.

For a visual walkthrough of enabling remote MCP servers in ChatGPT, see [Enable Remote MCP Servers in ChatGPT (Step-by-Step)](https://www.youtube.com/watch?v=wn9ZtQf5qto).

<Note>
  The MCP server requires OAuth authentication with the `mcp` scope — the connector requests it automatically. See [OAuth](/integrations/oauth) for details on the flow, scopes, and revoking access.
</Note>

## 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:**

| Parameter      | Type      | Required | Description                                                                                                                                                                                                             |
| -------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `country_code` | string    | **Yes**  | ISO 3166-1 alpha-2 country code, e.g. `US` or `NL`. Always required.                                                                                                                                                    |
| `title`        | string\[] | No       | Title phrases ORed together. Each phrase is a case-insensitive substring match. Example: `["backend engineer", "staff engineer"]`                                                                                       |
| `region`       | string    | No       | State/province name (case-insensitive). Required when `city` is set and `country_code` is `US` or `CA`.                                                                                                                 |
| `city`         | string    | No       | City name for radius search. Looked up exactly against Nico's geocoded locations index — no external geocoding is performed. Returns an error if the city is unknown or ambiguous.                                      |
| `radius_km`    | integer   | No       | Search radius in km around the resolved `city` (default `25`, max `250`). Only used when `city` is set.                                                                                                                 |
| `work_mode`    | string\[] | No       | One or more of `remote`, `onsite`. Hybrid postings surface under either filter.                                                                                                                                         |
| `employers`    | string\[] | No       | Filter to postings at these employers. Pass employer names — lookup is case-insensitive. Each name must resolve to exactly one employer; unknown or ambiguous names return an error. Example: `["Anthropic", "Stripe"]` |
| `limit`        | integer   | No       | Results per page (default `20`, max `100`).                                                                                                                                                                             |
| `page`         | integer   | No       | Page number (default `1`). Use the response's `pagination` block to walk further pages.                                                                                                                                 |

<Warning>
  Don't combine a geographic filter (`region` or `city`) with `work_mode: ["remote"]`. Remote postings aren't pinned to a location, so that combination almost always returns nothing. Search remote roles by country, and use `region`/`city` for on-site or hybrid roles.
</Warning>

**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.

```json theme={null}
{
  "city": "Amsterdam",
  "country_code": "NL",
  "radius_km": 50
}
```

```json theme={null}
{
  "city": "Springfield",
  "region": "Illinois",
  "country_code": "US",
  "radius_km": 50
}
```

**Example response:**

```json theme={null}
{
  "job_postings": [
    {
      "id": "019e1e37-ff01-7870-a56a-7b316b2d2373",
      "title": "Senior Backend Engineer",
      "company_name": "Acme Co",
      "company_id": "019e1e37-aa01-7870-a56a-7b316b2d2374",
      "location": "San Francisco, CA / Hybrid",
      "work_mode": "hybrid",
      "employment_type": "full-time",
      "salary_min": 150000,
      "salary_max": 200000,
      "salary_currency": "USD",
      "salary_period": "annual",
      "posted_at": "2026-05-12T08:00:00Z",
      "effective_posted_at": "2026-05-12T08:00:00Z"
    }
  ],
  "count": 20,
  "pagination": {
    "current_page": 1,
    "total_pages": 5,
    "total_count": 99,
    "per_page": 20
  }
}
```

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:

```json theme={null}
{ "error": "country_code is required" }
```

Other error messages you may see:

* `country_code is required` — `country_code` is mandatory on every call.
* `region (state/province) is required for city search in US` (or `CA`) — `region` must be set when `city` is given and the country is `US` or `CA`.
* `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 with `region`.
* `Unknown employer: <name>` — a name in `employers` didn't match any employer.
* `Ambiguous employer: multiple matches for <name> — please refine` — a name in `employers` matched 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 the `id` 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:**

| Parameter | Type   | Required | Description                                         |
| --------- | ------ | -------- | --------------------------------------------------- |
| `id`      | string | Yes      | The posting `id` from `job_posting_search` results. |

**Example response:**

```json theme={null}
{
  "id": "019e1e37-ff01-7870-a56a-7b316b2d2373",
  "title": "Senior Backend Engineer",
  "company_name": "Acme Co",
  "company_id": "019e1e37-aa01-7870-a56a-7b316b2d2374",
  "location": "San Francisco, CA / Hybrid",
  "work_mode": "hybrid",
  "employment_type": "full-time",
  "salary_min": 150000,
  "salary_max": 200000,
  "salary_currency": "USD",
  "salary_period": "annual",
  "posted_at": "2026-05-12T08:00:00Z",
  "effective_posted_at": "2026-05-12T08:00:00Z",
  "url": "https://acme.test/jobs/be-eng",
  "description": "About the role…"
}
```

Returns `{ "error": "Job posting not found" }` for an unknown `id`. Same feature-flag gate as `job_posting_search`.

<Tip>
  After finding a relevant posting, call `job_posting_get` for its `url`, then pass that URL to `job_application_parse_url` to extract richer structured fields, and finally `job_application_create` to add it to your board.
</Tip>

## Job application management

### job\_application\_search

Search your job applications by URL (exact match) or company name (case-insensitive).

**Parameters:**

| Parameter      | Type   | Required | Description                                                                                       |
| -------------- | ------ | -------- | ------------------------------------------------------------------------------------------------- |
| `url`          | string | No\*     | Search by exact job posting URL                                                                   |
| `company_name` | string | No\*     | Search by company name (case-insensitive)                                                         |
| `status`       | string | No       | Filter results by status group: `draft`, `applied`, `interviewing`, `offer`, `finished`, `active` |

\*At least one of `url` or `company_name` is required.

**Example response (search by URL):**

```json theme={null}
{
  "exists": true,
  "count": 1,
  "job_applications": [
    {
      "id": "019e1e40-1234-7000-9abc-1111aaaa2222",
      "title": "Senior Product Manager",
      "status": "applied",
      "company": { "name": "Acme Corp" }
    }
  ]
}
```

***

### job\_application\_list

List your job applications with optional filtering and pagination.

**Parameters:**

| Parameter  | Type    | Required | Description                                                                               |
| ---------- | ------- | -------- | ----------------------------------------------------------------------------------------- |
| `status`   | string  | No       | Filter by status group: `draft`, `applied`, `interviewing`, `offer`, `finished`, `active` |
| `page`     | integer | No       | Page number (default `1`)                                                                 |
| `per_page` | integer | No       | Items per page (default `25`, max `100`)                                                  |

**Example response:**

```json theme={null}
{
  "job_applications": [
    {
      "id": "019e1e40-1234-7000-9abc-1111aaaa2222",
      "title": "Senior Product Manager",
      "url": "https://acme.com/careers/pm-senior",
      "status": "applied",
      "work_mode": "hybrid",
      "location": "Berlin, Germany",
      "company_name": "Acme Corp",
      "created_at": "2026-03-15T10:30:00Z",
      "updated_at": "2026-03-16T09:00:00Z"
    }
  ],
  "pagination": {
    "current_page": 1,
    "per_page": 25,
    "total_count": 52,
    "total_pages": 3
  }
}
```

***

### job\_application\_get

Get the full details of one job application, including its notes and interviews.

**Parameters:**

| Parameter | Type   | Required | Description              |
| --------- | ------ | -------- | ------------------------ |
| `id`      | string | Yes      | The job application `id` |

**Example response:**

```json theme={null}
{
  "id": "019e1e40-1234-7000-9abc-1111aaaa2222",
  "title": "Senior Product Manager",
  "url": "https://acme.com/careers/pm-senior",
  "status": "applied",
  "work_mode": "hybrid",
  "location": "Berlin, Germany",
  "salary_min": 80000,
  "salary_max": 100000,
  "salary_currency": "EUR",
  "salary_period": "annual",
  "company": { "id": "019e1e37-aa01-7870-a56a-7b316b2d2374", "name": "Acme Corp", "url": "https://acme.com" },
  "notes": [],
  "interviews": [],
  "created_at": "2026-03-15T10:30:00Z"
}
```

Returns `{ "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:**

| Parameter | Type   | Required | Description                     |
| --------- | ------ | -------- | ------------------------------- |
| `url`     | string | Yes      | URL of the job posting to parse |

**Example response:**

```json theme={null}
{
  "title": "Senior Product Manager",
  "company": "Acme Corp",
  "location": "Berlin, Germany",
  "work_mode": "hybrid",
  "salary_min": 80000,
  "salary_max": 100000,
  "currency": "EUR",
  "period": "annual",
  "employment_type": "full-time"
}
```

<Tip>
  Use `job_application_parse_url` before `job_application_create` to extract job details automatically — then pass the parsed data to the create tool.
</Tip>

***

### job\_application\_create

Create a new job application in `proposed` status. The account owner must approve it before it moves forward.

**Parameters:**

| Parameter      | Type   | Required | Description                                              |
| -------------- | ------ | -------- | -------------------------------------------------------- |
| `title`        | string | Yes      | Job title                                                |
| `company_name` | string | Yes      | Company name (creates the company if it doesn't exist)   |
| `url`          | string | No       | Job posting URL                                          |
| `location`     | string | No       | Job location                                             |
| `work_mode`    | string | No       | One of: `remote`, `remote-optional`, `hybrid`, `on-site` |

**Idempotency:** If an application with the same `url` already exists, the tool returns `{ "error": "A job application with this URL already exists", "existing_job_application_id": "<id>" }` instead of creating a duplicate.

**Example response:**

```json theme={null}
{
  "job_application": {
    "id": "019e1e41-5678-7000-9abc-3333bbbb4444",
    "title": "Senior Product Manager",
    "url": "https://acme.com/careers/pm-senior",
    "status": "proposed",
    "work_mode": "hybrid",
    "location": "Berlin, Germany",
    "company_name": "Acme Corp",
    "created_at": "2026-03-15T14:20:00Z"
  }
}
```

***

### job\_application\_add\_note

Add a note to an existing job application. Notes are visible to all team members.

**Parameters:**

| Parameter            | Type   | Required | Description                           |
| -------------------- | ------ | -------- | ------------------------------------- |
| `job_application_id` | string | Yes      | The job application `id`              |
| `body`               | string | Yes      | The note text (max 10,000 characters) |

**Example response:**

```json theme={null}
{
  "note": {
    "id": "019e1e42-9abc-7000-9abc-5555cccc6666",
    "body": "Recruiter replied — call scheduled for Friday.",
    "author": "Alex",
    "job_application_id": "019e1e41-5678-7000-9abc-3333bbbb4444",
    "created_at": "2026-03-15T14:20:00Z"
  }
}
```
