Skip to main content
Glama
meskhetian

linkedin-applicants-mcp

by meskhetian

linkedin-applicants-mcp

Export the applicants of your LinkedIn job posts (lists, application details, resume files and full profiles) through your own logged-in Chrome, paced like a person. Drive it from Claude, Codex, Cursor or any MCP client, browse and search the results in a local dashboard, and export them to CSV or JSON.

CI License: MIT Node >= 22.13 Read-only on LinkedIn

What it does

linkedin-applicants-mcp is a Model Context Protocol server for LinkedIn job posters, company-page admins and hiring collaborators using the free hiring dashboard, not Recruiter seats. It drives your own installed Google Chrome, in a dedicated profile you sign in to once, through the dashboard you already have access to: posted jobs, applicant lists, each application (contact details, screening answers, rating, resume file) and the applicant's full LinkedIn profile. Everything lands in a local SQLite database and a folder of files that you query instantly from Claude, browse in a local dashboard, or export to CSV/JSON.

Why it exists

  • LinkedIn's job-poster dashboard has no bulk export: applications and resumes can only be opened one at a time.

  • Popular postings collect thousands of applicants across open and closed jobs; downloading each resume by hand does not scale.

  • Any burst of automation gets a LinkedIn account restricted. This server does the slow, careful thing instead: one visible tab, human-like input, working hours, daily and hourly caps that ramp up and vary from day to day like a person's workload (67 applications one day, 43 the next, never the same number twice), random breaks, shuffled order, and a resumable queue that keeps going for as many days as a job needs.

  • It is strictly read-only on LinkedIn: it never rates, shortlists, messages or connects with anyone.

Automating LinkedIn is against its User Agreement. Read Compliance and privacy before using this.

Related MCP server: linkedin-toolkit

Dashboard

npm run dashboard        # → http://127.0.0.1:4173

A local, read-mostly web page over the same SQLite database the MCP server and worker use. It shows:

  • Overview tiles: jobs, applicants, details, resumes, profiles, applicants with an email; worker state and work-window pills; today's usage against the effective caps; auto-refresh.

  • Jobs table with progress meters per job: applicants listed vs the total LinkedIn reports, details, resumes and profiles fetched, list-sync state.

  • Applicants table with the same filters as applicants_list (job, full-text search, has resume / profile / details, rating), pagination, and links to the stored resume file, profile.json and the LinkedIn profile.

  • A detail drawer per applicant: contact details, screening answers, qualifications, resume text, profile.

  • Export CSV / JSON buttons (the only write the dashboard performs: files under <data>/exports/).

  • Recent worker events (checkpoints, rate limits, cool-downs, failures).

It binds to 127.0.0.1 only, applicant data is personal information, serves GET requests only, and never touches LinkedIn. Change the port with LINKEDIN_MCP_DASHBOARD_PORT.

Dashboard

The dashboard with sample data: overview tiles, per-job progress meters and the applicant table.

Roadmap

  • Next version: ATS importers. Push exported applicants, resumes and profiles into Ashby, Lever, Workable, Greenhouse and similar applicant tracking systems through their APIs, so LinkedIn applicants land in the pipeline you already use.

  • OCR for scanned/image resumes (text extraction currently covers PDF, DOCX and plain text).

  • More dashboard filters and saved views.

  • Test fixtures for the legacy (Ember) dashboard so both UI generations are covered by CI.

  • Download-free capture of LinkedIn's own "Save to PDF" per profile (savePdf is reserved on the fetch tools and skipped with a warning while Chrome downloads are denied, see the Troubleshooting entry on the Chrome 154 download crash).

How it works

┌───────────────────────────┐   stdio (MCP)   ┌───────────────────────────────────┐
│ Claude Desktop /          │ ◄─────────────► │ MCP server  (node dist/index.js)  │
│ Claude Code  (MCP client) │                 │ 27 tools · review_applicants      │
└───────────────────────────┘                 └───────────────┬───────────────────┘
                                                 enqueue tasks │ read results
                                                               ▼
┌───────────────────────────┐   reads         ┌───────────────────────────────────┐
│ Local dashboard           │ ──────────────► │ SQLite  (node:sqlite)             │
│ http://127.0.0.1:4173     │                 │ jobs · applicants (+FTS5) · tasks │
└───────────────────────────┘                 │ counters · settings · events      │
                                              └───────────────┬───────────────────┘
                                                  claims tasks │ writes rows + files
                                                               ▼
                                              ┌───────────────────────────────────┐
                                              │ Background worker + scheduler     │
                                              │ in-process, or `npm run worker`   │
                                              │ work hours · caps · breaks · lock │
                                              └───────────────┬───────────────────┘
                                                               │ patchright
                                                               ▼
                                              ┌───────────────────────────────────┐
                                              │ Your Google Chrome                │
                                              │ dedicated profile · visible tab   │
                                              └───────────────┬───────────────────┘
                                                               │ https
                                                               ▼
                                              ┌───────────────────────────────────┐
                                              │ linkedin.com hiring dashboard     │
                                              │ posted jobs → applicants →        │
                                              │ applications → profiles           │
                                              └───────────────────────────────────┘
  1. Tools only enqueue. Every tool that touches LinkedIn (jobs_sync, applicants_sync, applicants_fetch_details, applicants_fetch_profiles) writes tasks to SQLite and returns immediately; the background worker does the browsing. Tasks survive restarts, and the progress of a long applicant list is saved after every page.

  2. The scheduler decides when the worker may act: inside working hours and days (start and end jittered every day), under the rolling hourly cap and the daily caps, and never during a break. Daily caps ramp up from a small day-1 value so a fresh setup does not start at full speed, and every day's caps and every hour's cap are drawn afresh within a spread around the configured value, so no two days look alike. An owner lock in the database makes sure only one process drives the browser.

  3. Input looks human. Bézier mouse paths with occasional overshoot, uneven wheel scrolling with back-scrolls, variable typing rhythm, and clipped log-normal pauses (long right tail) after clicks, while "reading" a page, between applicants and between profiles. Same-priority tasks are shuffled and the worker occasionally drops by the feed.

  4. It stops when LinkedIn asks for a human. Verification, CAPTCHA, "unusual activity" and login pages are detected from the URL and strong page-text signals. The task is requeued without burning an attempt, the queue is flagged needsHuman, and nothing runs until you solve the page in the Chrome window and call queue_resume. HTTP 429/999 back the queue off for hours; three unexpected failures in a row trigger a 20–40 minute cool-down.

  5. Both LinkedIn hiring dashboards are supported, detected per job from the URL LinkedIn lands on. The classic Ember list (/hiring/jobs/<id>/applicants/?r=<BUCKET>&sort_by=APPLIED_DATE&start=N, 25 per page) is crawled per rating bucket, Unrated, Good fit, Maybe and the hidden Not a fit. The 2026 "Hiring Pro" server-driven UI (/hiring/applicants/?jobId=<id>&rating=ALL&sort=DateApplied, 25 per page) uses numbered page buttons, a start= offset in the URL and resume downloads that open a signed URL in a new tab. The classic list is sorted by applied date, so its pagination stays stable across days. The Hiring Pro list ignores sort parameters and orders by qualification match with unstable ties, so a first pass ends a few percent short; a complete list that is more than 2 percent short gets one sweep through LinkedIn's table view, which does honour date order (see Troubleshooting). Raw page snapshots are kept next to the data so parsers can be repaired offline after LinkedIn deploys.

Quick start

Requirements

Node.js

>= 22.13 (tested on Node 26). SQLite comes from Node's built-in node:sqlite; no native build step.

Browser

Google Chrome installed. The server launches your Chrome and never downloads a browser. Edge and Chromium work via LINKEDIN_MCP_CHROME_CHANNEL.

LinkedIn

An account that posted the jobs, or was added as a hiring collaborator.

OS

macOS, Linux or Windows.

1. Install

git clone https://github.com/meskhetian/linkedin-applicants-mcp.git
cd linkedin-applicants-mcp
npm install
npm run build

2. Sign in once

npm run login

Chrome opens with a dedicated profile (~/.linkedin-applicants-mcp/chrome-profile). Sign in to LinkedIn yourself in that window, nothing is ever typed on your behalf. Expect LinkedIn's new-device check (an email PIN) the first time. Cookies persist in that profile, so you will not need to do this again unless LinkedIn logs you out. You can also do this later from Claude with the browser_open_login tool.

3. Register the server

Claude Desktop, ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "linkedin-applicants": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/linkedin-applicants-mcp/dist/index.js"],
      "env": {
        "LINKEDIN_MCP_DATA_DIR": "/Users/you/.linkedin-applicants-mcp"
      }
    }
  }
}

Claude Code:

claude mcp add linkedin-applicants \
  -e LINKEDIN_MCP_DATA_DIR="$HOME/.linkedin-applicants-mcp" \
  -- node /ABSOLUTE/PATH/TO/linkedin-applicants-mcp/dist/index.js

Add --scope user to make it available in every project. All other settings are optional environment variables (see Configuration).

Use with other MCP clients

The server speaks standard MCP over stdio, so any client that can launch a local command works. Every client needs the same three things: the command node, the absolute path to dist/index.js, and the LINKEDIN_MCP_DATA_DIR environment variable (optional; defaults to ~/.linkedin-applicants-mcp). Replace /ABSOLUTE/PATH/TO and /Users/you below.

Client

Where to put it

Claude Desktop

claude_desktop_config.json, see above

Claude Code

claude mcp add ..., see above

OpenAI Codex CLI

~/.codex/config.toml, or codex mcp add linkedin-applicants -- node /ABSOLUTE/PATH/TO/linkedin-applicants-mcp/dist/index.js

Cursor

~/.cursor/mcp.json (global) or .cursor/mcp.json in a project

Windsurf

~/.codeium/windsurf/mcp_config.json

VS Code (GitHub Copilot agent mode)

.vscode/mcp.json in a workspace, or the user-level mcp.json via "MCP: Open User Configuration"

Cline, Roo Code

MCP Servers panel, "Configure MCP Servers" (cline_mcp_settings.json)

Zed

settings.json, context_servers

Gemini CLI

~/.gemini/settings.json, mcpServers

Continue

~/.continue/config.yaml, mcpServers

JetBrains AI Assistant / Junie

Settings, Tools, AI Assistant, Model Context Protocol, add server as JSON

Anything else

If it accepts a stdio command: same command, args and env. If it only accepts remote HTTP servers (for example ChatGPT connectors), run a stdio-to-HTTP bridge such as supergateway or mcp-remote in front of node dist/index.js on your own machine.

Codex CLI (~/.codex/config.toml):

[mcp_servers.linkedin-applicants]
command = "node"
args = ["/ABSOLUTE/PATH/TO/linkedin-applicants-mcp/dist/index.js"]

[mcp_servers.linkedin-applicants.env]
LINKEDIN_MCP_DATA_DIR = "/Users/you/.linkedin-applicants-mcp"

Cursor, Windsurf, Cline, Roo Code, Gemini CLI, JetBrains (the common mcpServers shape):

{
  "mcpServers": {
    "linkedin-applicants": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/linkedin-applicants-mcp/dist/index.js"],
      "env": { "LINKEDIN_MCP_DATA_DIR": "/Users/you/.linkedin-applicants-mcp" }
    }
  }
}

VS Code (.vscode/mcp.json):

{
  "servers": {
    "linkedin-applicants": {
      "type": "stdio",
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/linkedin-applicants-mcp/dist/index.js"],
      "env": { "LINKEDIN_MCP_DATA_DIR": "/Users/you/.linkedin-applicants-mcp" }
    }
  }
}

Zed (settings.json):

{
  "context_servers": {
    "linkedin-applicants": {
      "source": "custom",
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/linkedin-applicants-mcp/dist/index.js"],
      "env": { "LINKEDIN_MCP_DATA_DIR": "/Users/you/.linkedin-applicants-mcp" }
    }
  }
}

Continue (~/.continue/config.yaml):

mcpServers:
  - name: linkedin-applicants
    command: node
    args:
      - /ABSOLUTE/PATH/TO/linkedin-applicants-mcp/dist/index.js
    env:
      LINKEDIN_MCP_DATA_DIR: /Users/you/.linkedin-applicants-mcp

Test any setup without a client: npx @modelcontextprotocol/inspector node dist/index.js opens the MCP Inspector, where you can call browser_status and queue_status by hand.

Two notes that apply to every client:

  • Clients that start the server only while a chat is open also stop the embedded worker when they close it. For multi-day exports run the worker as its own process with npm run worker and set LINKEDIN_MCP_AUTOSTART_WORKER=false in the client config; the tools keep working, the queue keeps draining.

  • Several clients may run their own server instance at the same time. That is fine: they share one database, and an owner lock guarantees only one process drives Chrome.

4. First workflow

Step

Ask Claude to call

What happens

1

browser_status

Is Chrome connected and LinkedIn logged in? If not, browser_open_login.

2

jobs_sync with wait: true

Crawls Posted jobs: the default tab plus every jobState= tab LinkedIn renders (Closed is always included). Blocks up to 2 minutes and returns the jobs.

3

jobs_list

Jobs stored locally with progress counters. Pick a jobId.

4

applicants_sync with jobId

Queues the applicant-list crawl: 25 per page, progress saved after every page, hidden "Not a fit" applicants included.

5

applicants_fetch_details with jobId

Queues, per applicant, the application page plus resume download and then the full profile (includeProfile defaults to true). Returns how many were queued and a day estimate.

6

queue_status

Poll it: task counts, today's usage vs caps, whether you are inside the work window, per-job list progress, any checkpoint LinkedIn raised.

7

applicants_list, applicant_get, resume_text, applicants_export

Read and export locally, instant, no LinkedIn traffic. Or run the review_applicants prompt with your criteria.

Steps 2–5 only enqueue work; the worker does the browsing during working hours.

Tools

Session

Tool

Description

browser_status

Is Chrome connected, is LinkedIn logged in, current URL, any checkpoint. Never launches a browser.

browser_open_login

Launches (or connects to) your Chrome with the dedicated profile and opens LinkedIn so you can sign in; waits up to timeoutSeconds.

browser_close

Stops the worker and closes the Chrome window the server launched (cdp mode: only disconnects). Queued tasks stay in the database.

Jobs

Tool

Description

jobs_sync

Queue a crawl of /my-items/posted-jobs/ (open and, by default, closed). wait: true blocks up to 2 minutes and returns the jobs.

jobs_list

Jobs stored locally with per-job counters: applicants stored, details fetched, resumes stored, profiles fetched, list-sync progress.

Applicants (LinkedIn work, queued)

Tool

Description

applicants_sync

Crawl the applicant list of one job or allJobs: name, headline, location, applied date, application id, profile link. Runs in chunks of pagesPerRun, resumes across restarts; restart ignores saved progress.

applicants_fetch_details

Open each application page (email/phone when shared, screening answers, rating), capture the resume, then optionally the full profile (includeProfile, profileDepth). onlyMissing and limit control scope. Best-matching applicants go first (orderByFit, from LinkedIn's must-have counters), and profileMinMustHave limits profile visits to strong matches so a long queue spends its daily budget where it matters.

applicants_fetch_profiles

Full profiles only, for applicants whose profile URL is already known. The most rate-sensitive action on LinkedIn.

Data (local, instant)

Tool

Description

applicants_list

Page through stored applicants with filters (jobId, rating, hasResume, hasProfile, hasDetail) and full-text search over name, headline, resume text and profile text (prefix matching).

applicant_get

The full local record of one applicant: list fields, application details, resume text (clipped to maxChars), structured profile.

resume_text

Plain text extracted from the downloaded resume (PDF/DOCX). Empty for scanned PDFs.

applicants_export

Stream all applicants (or one job) to CSV, JSON or JSONL on disk. CSV is Excel-friendly (UTF-8 BOM) and guards against formula injection.

Queue and pacing

Tool

Description

queue_status

Worker state (running / paused / needsHuman), task counts, today's counters vs effective caps, work window and next window start, per-job list progress, browser status, recent events.

queue_start

Start or resume the worker in this process. No-op if another process (npm run worker) owns the queue.

queue_pause

Pause after the current task; tasks stay queued.

queue_resume

Clear the paused flag and, by default, the needsHuman flag; then start the worker. Call it after solving a checkpoint.

queue_cancel

Cancel tasks by type, jobId or applicationId (no filter cancels every queued task). Pending tasks leave the queue; a running list sync or sweep stops after its current page, a running application or profile fetch finishes the page it is on, stays cancelled and queues nothing further. Done work is untouched.

queue_retry_failed

Put failed tasks back with a fresh attempt budget.

queue_tasks

Recent tasks with status, attempts and last error.

pacing_get

Current speed, working hours/days, caps, ramp, breaks, delay table.

pacing_set

Change any of those; persisted and wins over environment variables. Warns above ~100 profiles/day or ~60 actions/hour.

Debug (for when LinkedIn changes its markup)

These operate on the same single tab the worker uses, queue_pause first.

Tool

Description

debug_snapshot

Save a full-page screenshot, the HTML, the visible text and all captured network payloads of the current tab into <data>/debug/.

debug_navigate

Navigate the tab to a linkedin.com URL the human way, then report URL, title and checkpoint state. Refuses other domains.

debug_page_text

The innerText of <main> (or body) of the current tab, clipped.

debug_find

Count and describe the elements matching a CSS/Playwright selector (text, href, aria-label, data-view-name, componentkey, class).

debug_click

Click the first visible match with the worker's paced mouse movement. Refuses selectors that look like a write action (rate, shortlist, message, reject, …).

debug_captures

The JSON/RSC responses LinkedIn's own web app fetched while browsing (Voyager REST, GraphQL, flagship-web), how you discover its internal endpoints.

Prompt

review_applicants(jobId, criteria), pages through every stored applicant of a job, reads resume text, structured profile and screening answers with applicant_get, scores each 1–10 against your criteria, and returns a ranked Markdown table (rank, name, headline, location, score, strengths, gaps, applicationId, profileUrl) plus the list of applicants whose details are still missing. It works on local data only and is told not to contact anyone or invent facts.

Pacing

Everything below is the normal speed. Slower is safer; the defaults mimic one recruiter reviewing applicants during office hours.

Mechanism

Default

Adjust with pacing_set

Working hours and days

09:00–19:00 local time (or timezone), Mon–Fri, start/end jittered per day

workHoursStart, workHoursEnd, workDays, timezone

Daily cap: application pages

120, ramped: 25 on day 1, +10 per day until the cap; each day draws its own number between 65 % and 100 % of that (67 one day, 43 the next), fixed for the day; the configured cap is never exceeded

dailyApplicantCap, rampStart, rampPerDay, dailyCapVariance

Daily cap: full profile views

80, ramped at 70 % of the applicant ramp, varied per day the same way

dailyProfileCap

Hourly cap: LinkedIn page actions

40 per rolling hour, varied per hour the same way

hourlyActionCap

Long breaks

every 25–60 actions, 5–20 minutes

breakEveryActions, breakMinutes

Order

same-priority tasks shuffled; 8 % chance of a warm-up visit to the feed

randomizeOrder, warmupProbability

LinkedIn "Save to PDF"

at most 150 profiles per month (LinkedIn's own limit is 200)

LINKEDIN_MCP_SAVE_PDF_MONTHLY_CAP

The speed preset scales the three caps (slow × 0.6, brisk × 1.4) and picks a delay table. Medians of the clipped log-normal pauses:

Pause

slow

normal

brisk

After a click

3 s

2 s

1.2 s

Reading a page

18 s

11 s

6 s

Between list pages

25 s

15 s

8 s

Between applicants

90 s

55 s

25 s

Between profiles

120 s

75 s

40 s

Expected throughput at normal, once ramped: roughly 80–120 application pages and 60–80 profiles per work day. A job with 2,000 applicants is ~80 list pages (a few hours inside working hours), then ~17 work days of application pages and ~25 work days of profiles running interleaved, about five work weeks plus the ramp. If you need it faster, add work days or hours before raising caps, and never raise dailyProfileCap much above 100.

Start slow after a new-device login. The first sign-in to the dedicated profile looks like a new device to LinkedIn (expect an email PIN). Do not start a crawl the same evening. Begin the next work day with jobs_sync and a small applicants_sync, consider LINKEDIN_MCP_SPEED=slow with a gentler ramp (for example LINKEDIN_MCP_RAMP_START=15, LINKEDIN_MCP_RAMP_PER_DAY=8) for the first week, and only then move to normal.

What LinkedIn actually limits. Its help pages document no cap on opening applications in your own hiring dashboard. What is limited is viewing profiles of people you are not connected to: a daily "data security" limit (undisclosed, temporary block on non-connection profile views) and the monthly commercial use limit on people search and non-connection browsing (undisclosed, resets on the 1st). Detection is behavioural: round-the-clock activity, uniform intervals and request sequences that do not look like a person reading, more than raw daily counts. Community numbers ("80 profile views a day", "300 searches a month") are guesses; treat 40 to 60 non-connection profile views per working day as the conservative band.

A plan for a job with about 1,000 applicants (roughly 11 days instead of three weeks, while still looking like one busy recruiter):

  • Work 08:30 to 18:30 local time, seven days or six; never around the clock. A recruiter reading resumes at 03:00 is exactly the outlier the model catches.

  • Applications: 40 the first day, then +15 per day up to 120 (pacing_set with rampStart, rampPerDay, dailyApplicantCap), hourlyActionCap 30, the built-in breaks, warmupProbability 0.15 so feed visits break the cycle.

  • Profiles: dailyProfileCap 50, and only for applicants who meet the must-have qualifications (applicants_fetch_details with profileMinMustHave, for example 0.8). Contact details are on the application when the applicant shared them and almost always in the resume text, so most profiles are not needed for an export.

  • Same machine, same home network, no VPN, no other LinkedIn extensions in the dedicated profile, no second-device logins while the queue runs. After a new-device login, keep the ramp.

  • Closed jobs: fetch details for the best matches first (orderByFit, limit), review the export, then decide whether the long tail is worth opening at all.

Warning signs and what to do. "We noticed unusual activity" (not restricted yet): stop for the day and halve every cap for a week. "Your ability to view profiles has been temporarily restricted" or a commercial use warning: includeProfile: false until the stated date, dashboard reads at half volume, then profiles at 25 per day. A CAPTCHA, a sign-in PIN or needsHuman in queue_status: solve it by hand in the Chrome window, pause a day or two, resume at half caps; two in a week means stop for the month. A formal restriction notice: stop the tool and wait out the stated date; second offences are where permanent restrictions are reported.

Profile strategy. Profile views are the most rate-sensitive action on LinkedIn. With LINKEDIN_MCP_PROFILE_STRATEGY=auto (default) the worker visits the profile page like a person and then makes one request (1–3 with top-ups for truncated sections) to LinkedIn's internal profile API from inside the page, structured data with dates, instead of ~8 page views through the detail sections, and falls back to the rendered text if that fails. dom never touches the API and reads the profile and detail pages as text, at the cost of more page views per applicant. voyager uses the API only and fails if LinkedIn retires the endpoint.

Data layout

~/.linkedin-applicants-mcp/                (LINKEDIN_MCP_DATA_DIR)
  db.sqlite                     jobs, applicants (+ resume/profile text, FTS5 index), tasks, counters, settings, events
  chrome-profile/               the dedicated Chrome profile, your LinkedIn cookies live here
  downloads/                    Chrome's default download directory (unused: resumes are read from the network)
  files/jobs/<jobId>/
    raw/                        raw applicant-list pages + the JSON LinkedIn's own app fetched
    <applicationId>_<Name>/     resume.<pdf|docx>, profile.json, profile.png, raw-application.json,
                                raw-voyager.json or raw-profile-*.json, profile-linkedin.pdf (savePdf)
  exports/                      CSV / JSON / JSONL exports
  debug/                        debug_snapshot output; every captured payload when LINKEDIN_MCP_CAPTURE_RAW=true
  logs/                         JSON-lines logs (stderr is mirrored here)

Resume text is extracted from PDF (unpdf) and DOCX (mammoth) and indexed for full-text search; the original file is always kept.

Configuration

All settings are environment variables (see .env.example). Pacing values can also be changed at runtime with pacing_set; persisted overrides win over the environment.

Variable

Default

Meaning

LINKEDIN_MCP_DATA_DIR

~/.linkedin-applicants-mcp

Data directory (database, Chrome profile, files, exports, logs)

LINKEDIN_MCP_BROWSER_MODE

persistent

persistent or cdp (see below)

LINKEDIN_MCP_CDP_URL

http://127.0.0.1:9222

DevTools endpoint for cdp mode

LINKEDIN_MCP_CHROME_CHANNEL

chrome

chrome, msedge or chromium, must be installed

LINKEDIN_MCP_PROFILE_STRATEGY

auto

auto, voyager or dom

LINKEDIN_MCP_SPEED

normal

slow, normal or brisk

LINKEDIN_MCP_WORK_HOURS

09:00-19:00

Local working window

LINKEDIN_MCP_WORK_DAYS

1,2,3,4,5

0 = Sunday … 6 = Saturday

LINKEDIN_MCP_TIMEZONE

system

IANA zone, e.g. Europe/Berlin

LINKEDIN_MCP_DAILY_APPLICANT_CAP

120

Application pages per day

LINKEDIN_MCP_DAILY_PROFILE_CAP

80

Full profile views per day

LINKEDIN_MCP_HOURLY_ACTION_CAP

40

LinkedIn page actions per rolling hour

LINKEDIN_MCP_RAMP_START / LINKEDIN_MCP_RAMP_PER_DAY

25 / 10

Warm-up ramp; 0 disables it

LINKEDIN_MCP_DAILY_CAP_VARIANCE

0.35

Day-to-day spread of the caps: each day and hour draws up to this fraction below the configured value, never above it; 0 = exact numbers

LINKEDIN_MCP_SAVE_PDF_MONTHLY_CAP

150

Cap for LinkedIn "Save to PDF"

LINKEDIN_MCP_AUTOSTART_WORKER

true

Start the worker inside the MCP server process

LINKEDIN_MCP_CAPTURE_RAW

false

Persist every captured LinkedIn payload under <data>/debug/raw (heavy)

LINKEDIN_MCP_LOG_LEVEL

info

debug, info, warn or error

LINKEDIN_MCP_DASHBOARD_PORT

4173

Port for npm run dashboard

Browser modes

persistent (default) launches your installed Chrome with the dedicated profile directory through patchright's launchPersistentContext: a visible window at its real size, the Chrome sandbox left on, no user-agent override, no init scripts, and only three flags (--disable-blink-features=AutomationControlled, --no-first-run, --no-default-browser-check). The profile's Chrome preferences keep PDFs in Chrome's viewer and silence the download prompt; resume files are read from the network response, never downloaded, because Chrome 154 crashes when an automation-triggered download starts.

cdp attaches to a Chrome you started yourself:

./scripts/launch-chrome.sh              # then set LINKEDIN_MCP_BROWSER_MODE=cdp

Chrome 136+ refuses --remote-debugging-port on its default profile, so the script starts Chrome with the same dedicated profile directory. In this mode the server only disconnects on exit and never closes your Chrome.

The two modes launch Chrome with different flags and therefore encrypt the profile's cookies differently: do not open the dedicated profile directory with a plain Chrome launch while using persistent mode, and expect to sign in again if you switch modes.

Why no proxies

The whole point of using your own browser is that IP, cookies, TLS fingerprint and login history all match your normal recruiter activity. Routing the session through a proxy changes the IP mid-session, one of the most common triggers for LinkedIn's "verify it's you" checkpoint. The risk to manage is volume and rhythm on one account, and the levers for that are pacing, caps and working hours, not IPs. If you always work through a company VPN, keep using that same VPN consistently; never rotate.

Running while Claude is closed

The MCP server runs the queue in-process, so it stops when Claude Desktop quits. To keep going:

npm run worker

This drives the same SQLite queue: enqueue work from Claude, let the CLI worker do the browsing. Only one process owns the browser at a time (an owner lock with a heartbeat in the database); the MCP server notices and steps back. The worker still honours working hours, so it waits for the next window rather than crawling at night unless you widen workHours.

Troubleshooting

Symptom

What to do

"Could not find posted job cards / applicant cards"

LinkedIn changed its markup (it ships two DOM generations side by side and rotates class names). queue_pause, then debug_snapshot, debug_find to try selectors, debug_captures to see the JSON the page fetched, debug_click to explore pagination, and update src/linkedin/selectors.ts. Everything parsed so far is kept; raw snapshots let you re-parse offline.

Names or headlines look wrong after an update (badge text such as "new applicant" next to a name, "Name at headline")

The list parser was fixed but your rows were stored by an older version. The server re-parses stored rows once at startup; to do it by hand, run npm run reparse (optionally with a job id). Nothing is fetched from LinkedIn.

"Google Chrome quit unexpectedly" while an application was being fetched

Older versions let Chrome download the resume; Chrome 154 crashes its browser process when an automation-triggered download starts. This is a known Chromium 152 to 154 issue (microsoft/playwright#42506): once the profile holds download history, any download start crashes the browser process. Update: resume files are now captured passively from the viewer's own responses (nothing is downloaded or replayed), downloads are denied, and the profile's download history is cleared before every launch. If Chrome is still lost during an application, the retry captures the details without the resume (event resume-skipped); run applicants_fetch_details for the job again later, its default selection includes applicants whose resume is still missing.

Applicants listed is a few percent below LinkedIn's count ("955 / 1,005, list complete")

LinkedIn's date order shifts between page loads: the same applicant shows up on two pages and displaces one that never renders, so a first pass ends about 4 to 5 percent short. LinkedIn ignores sort parameters on the list view, but its table view honours date order, so when a complete Hiring Pro list is more than 2 percent short the worker sweeps the table view once (30 rows per page, stable order), matches rows to stored applicants by name and opens only the rows nobody matched. The table carries no ids, so for each such row the worker opens the detail drawer (closing the previous one like a person would), uses Share, "Copy application URL" and reads the URL from the system clipboard: Chrome grants the LinkedIn tab clipboard access for that moment, the grant is cleared afterwards, and whatever text your clipboard held before is put back (avoid copying things in the same moment). Each opened row is an application view and counts against the daily applicant cap and the hourly cap, and the sweep pauses for working hours, breaks and queue_cancel between rows. A row whose drawer shows another name, whose id is already stored, or whose id belongs to another job is skipped rather than guessed, and a run that learns no ids stops and retries later. A list stored below 80 percent of LinkedIn's count is not swept (that is a broken crawl; re-run applicants_sync). The table's last column ("Top fit", "Maybe", "Not a fit") is LinkedIn's match label, kept in the raw data and never stored as a rating. Blank table pages are retried in later runs like list pages. The dashboard shows the remaining gap. Whatever is still missing after the sweep is usually applicants who withdrew or whose accounts are gone; LinkedIn keeps counting them.

needsHuman in queue_status

LinkedIn raised a verification, CAPTCHA, "unusual activity" or login page. Solve it in the Chrome window (or run browser_open_login), then queue_resume.

"Could not launch Chrome with profile …"

Another Chrome window is using the dedicated profile. Close it, or switch to cdp mode.

List stops before the reported total (stoppedEarly)

LinkedIn may cap deep pagination or hide buckets. Re-run applicants_sync; if it stops at the same offset, narrow the list with the dashboard's ratings/sort filters and sync again (rows are deduplicated by application id). filterParamIgnored means LinkedIn ignored the rating-bucket parameter and the crawler fell back to one pass with the UI filter.

Resume saved but no text

A scanned/image PDF. The file is still on disk; OCR is not built in yet (see Roadmap).

Yellow "You are using an unsupported command-line flag" bar in Chrome

Expected and harmless. --disable-blink-features=AutomationControlled is on Chrome's bad-flags list; the bar is browser UI, invisible to page JavaScript, and cannot be hidden with a flag (--disable-infobars was removed from Chrome in 2019).

A resume shows hasResume but no file

Open the applicant's raw-application.json and look at resume.debug (trapSeen, documentUrls, downloadsSeen, tabs, frames): it records what the Resume control did. If downloadsSeen is not empty, the control started a download (denied and cancelled) instead of opening the viewer; add the file URL pattern to RESUME_URL_RE in src/linkedin/urls.ts so it is fetched from the page instead.

Nothing runs

Check queue_status: outside working hours (nextWindowStart)? Cap reached (nextEligibleAt)? Paused? Another process holding the owner lock (npm run worker)?

Where are the logs?

stderr (Claude Desktop writes it to ~/Library/Logs/Claude/mcp*.log on macOS) and <data>/logs/.

FAQ

Is this against LinkedIn's terms? Yes. LinkedIn's User Agreement prohibits scraping and automation of its site. This tool reads only what you, as the job poster, can already see in your own hiring dashboard, only for your own postings, and behaves like a person doing the same work by hand, but that does not make it permitted. Use at your own risk.

Will my account get restricted? Nobody can promise it will not. The levers that matter are volume and rhythm: keep the default caps, keep working hours, start slow after a new-device login, and never rotate IPs. The server pauses itself the moment LinkedIn shows a checkpoint.

Does it message, rate or shortlist anyone? No. It is strictly read-only on LinkedIn: no ratings, notes, messages, connection requests or status changes. Even debug_click refuses selectors that look like a write action.

Does it need proxies? No, and it should not use any. See Why no proxies.

Where is my data? On your disk, under LINKEDIN_MCP_DATA_DIR, unencrypted. Nothing is sent anywhere other than LinkedIn itself. The dashboard binds to localhost only.

Can I run it overnight? Use npm run worker to keep the queue running while Claude Desktop is closed. It still respects working hours and caps by design; widen workHours/workDays with pacing_set if you really want it to work outside office hours.

Can I use Edge or Chromium instead of Chrome? Yes: LINKEDIN_MCP_CHROME_CHANNEL=msedge or chromium. The browser must already be installed.

Contributing

Issues and pull requests are welcome, see CONTRIBUTING.md. Please keep the project read-only on LinkedIn and keep pacing defaults conservative.

npm run dev          # run the MCP server from source (tsx)
npm test             # build + unit tests + stdio smoke test (no browser, no LinkedIn)
npm run typecheck

The verified research behind the URL patterns, selectors, internal endpoints and pacing numbers lives in docs/research.

Acknowledgements

Compliance and privacy

  • LinkedIn's User Agreement prohibits scraping and automation of its site. This tool only reads data that you, as the job poster, can already see in your own hiring dashboard, only for your own postings, and it behaves like a person doing the same work by hand. Accounts can still be restricted. Use at your own risk.

  • Applicants shared their data with you for a hiring decision. Store it accordingly (the data directory is unencrypted on your disk), delete it when the process is over, and follow your local data-protection law (GDPR, CCPA and similar) regarding retention, access and purpose limitation.

  • The server never writes anything on LinkedIn: no ratings, notes, messages or connection requests.

License

MIT

Available Tools

27 tools
applicant_getGet one applicantA
Read-onlyIdempotent

Full local record for one applicant: list fields, application details (email/phone when shared, screening answers, rating), resume text (clipped to maxChars) and the structured LinkedIn profile. Local database only.

NameRequiredDescriptionDefault
maxCharsNoClip resume text to this many characters
applicationIdYes
includeProfileNo
includeResumeTextNo

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already signal readOnlyHint, idempotentHint, and openWorldHint=false, so the bar is lower. The description adds non-obvious behavioral details: resume text is clipped to maxChars, email/phone appear only when shared, and the source is a local database only. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences front-load the core purpose and then enumerate the delivered data. Every phrase earns its place, and the qualifiers 'local,' 'one applicant,' 'when shared,' and 'clipped to maxChars' are meaningful rather than filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only, idempotent tool with rich annotations, the description covers the return contents, data source, and clipping behavior. However, with no output schema and low parameter coverage, the includeProfile/includeResumeText switches and the exact response shape are left somewhat to inference.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 25%, and the description compensates only for maxChars via 'clipped to maxChars.' It leaves applicationId's role implicit and does not explain the effect of includeProfile or includeResumeText, relying on parameter names alone. This is insufficient for a required string ID and two behavior-controlling booleans.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: returns one applicant's full local record, enumerating the exact content categories (list fields, application details, resume text, LinkedIn profile). The added 'Local database only' scoping distinguishes it from fetch/sync siblings such as applicants_fetch_profiles and applicants_fetch_details.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides clear context: it is the local, single-applicant, full-record read. The 'Local database only' qualifier alerts the agent not to expect remote data and implies fetch/sync tools for non-local sources, but the description does not explicitly name alternatives or give when-not-to-use conditions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

applicants_exportExport applicants to CSV / JSONA

Write all stored applicants (optionally one job) to a CSV, JSON or JSONL file on disk and return the path. Streams, so thousands of rows are fine. CSV is Excel-friendly (UTF-8 BOM) and guards against formula injection.

NameRequiredDescriptionDefault
jobIdNo
formatNocsv
outPathNoAbsolute output path; default under the data directory /exports
includeProfileNoInclude the structured profile (JSON string in CSV)
includeResumeTextNo

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations indicate a non-read-only, non-idempotent operation, and the description adds meaningful behavioral detail: it writes to disk, returns the path, streams large result sets, uses an Excel-friendly UTF-8 BOM for CSV, and guards against formula injection. These are useful traits beyond what the structured annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tightly written sentences, each adding a distinct useful fact: core behavior, scalability/streaming, and CSV safety. The most important information is front-loaded and there is no filler or repetition of schema content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The definition covers the essential invocation contract: what is exported, optional job filtering, supported formats, destination, and the return value. The main gap is parameter-level detail for includeResumeText and path behavior, but the schema partially covers outPath and includeProfile, so an agent can still call the tool correctly with reasonable inference.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 40%, and the description only loosely covers jobId ('optionally one job') and format ('CSV, JSON or JSONL'). It does not explain includeResumeText, clarify includeProfile beyond its schema note, or describe outPath behavior. With low schema coverage, the description should compensate but leaves several parameters under-documented.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific action ('Write all stored applicants ... to a file on disk and return the path'), a clear resource (applicants), and supported formats (CSV, JSON, JSONL). This clearly distinguishes it from siblings like applicants_list or applicants_fetch_details, which retrieve data rather than export to a file.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the main use case: creating a full or job-filtered export file, and notes that streaming makes it suitable for thousands of rows. However, it does not explicitly explain when to prefer this over applicants_fetch_details, applicants_list, or other retrieval tools, nor does it state any exclusions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

applicants_fetch_detailsFetch application details, resumes and profilesA
Idempotent

Queue the deep fetch for applicants: open each application page (contact email/phone when shared, screening answers, rating, applied date), download the resume, and (includeProfile) then visit the LinkedIn profile for the full structured profile. One applicant at a time, paced like a recruiter, within working hours and daily caps, so thousands of applicants take days to weeks by design. Defaults to only applicants not fetched yet. Returns how many were queued and a time estimate. Track with queue_status; read results with applicants_list / applicant_get / applicants_export.

NameRequiredDescriptionDefault
jobIdNoFetch all applicants of this job (subject to onlyMissing/limit)
limitNoQueue at most this many applicants now
savePdfNoReserved: LinkedIn "Save to PDF" is a Chrome download, which crashes Chrome 154 under automation, so it is currently skipped with a warning; the structured profile is stored instead
orderByFitNoFetch the applicants who match the job's must-have qualifications best first (LinkedIn's list counters), so the strong candidates are done in the first days of a long queue
onlyMissingNoSkip applicants whose details were already fetched (with downloadResume, applicants whose resume is still missing are included)
profileDepthNo'full' visits detail sections when Voyager is unavailable; 'basic' reads only the main profile pagefull
applicationIdsNoExplicit application ids instead of a whole job
downloadResumeNo
includeProfileNoAlso queue the full LinkedIn profile after the application page
profileMinMustHaveNoWith includeProfile: only visit the LinkedIn profile of applicants meeting at least this fraction of the must-have qualifications (for example 0.8). Others get details and resume only; queue their profiles later with applicants_fetch_profiles

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses the asynchronous queueing nature, the side-effect of interacting with external sites (openWorldHint), the idempotent behavior via onlyMissing, the savePdf Chrome crash warning, and the pacing constraints. This goes well beyond the annotations (readOnlyHint false, idempotentHint true) and provides essential expectations for a tool that takes days to complete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single dense paragraph that front-loads the purpose, then covers pacing, defaults, and return value. Every sentence adds information, but it is a bit long and could benefit from bullet points for scannability. Still, it is well-organized and efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 10 parameters and no output schema, it covers the essential context: what it does, how it behaves (async, paced), what it returns (count and estimate), how to track progress, and how to read results. It also notes important caveats (savePdf, profileDepth, onlyMissing). Nothing critical for an agent to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already covers 90% of parameters with descriptions, so the description doesn't need to repeat them. It adds global context (pacing, defaults, interaction between includeProfile and profileMinMustHave) that enriches parameter interpretation. However, it doesn't explicitly explain the profileMinMustHave logic in the description, relying on the schema, so it's a slight gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Queue the deep fetch') and resource ('applicants'), enumerates the exact actions (open application page, download resume, visit LinkedIn profile), and explicitly names the sibling tool applicants_fetch_profiles as the alternative for later profile-only fetches. This unambiguously distinguishes it from related tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit guidance: it describes the pacing (one at a time, working hours, daily caps), the default behavior (only missing), and when to defer profile fetching via profileMinMustHave and applicants_fetch_profiles. It also directs users to queue_status for tracking and applicants_list/applicant_get/export for reading results, covering both usage and alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

applicants_fetch_profilesFetch full LinkedIn profiles onlyA
Idempotent

Queue full-profile fetches for applicants whose profile URL is known (from the list or application page) without re-opening their application. Use when details were fetched with includeProfile=false. Paced like a person; profile views are the most rate-sensitive action.

NameRequiredDescriptionDefault
depthNofull
jobIdNo
limitNo
savePdfNoReserved: currently skipped with a warning (Chrome download crash), the structured profile is stored instead
onlyMissingNoSkip applicants whose profile was already fetched
applicationIdsNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description adds meaningful behavioral context: it is paced like a human, profile views are the most rate-sensitive action, and it queues rather than performing synchronous opening. The idempotentHint is consistent with queuing, and openWorldHint fits the known-URL precondition. It does not detail queue mechanics or failure behavior, but the rate-limiting disclosure is valuable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, all purposeful and front-loaded with the core action. The usage condition and pacing warning are stated without filler, making it easy for an agent to parse and act on.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with six parameters, no output schema, and required fields entirely absent, the description provides a good usage trigger and rate-sensitivity warning but omits how applicants are selected via parameters. It also does not clarify what the caller should expect after queueing, which is more pressing given there is no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 33%, so the description carries heavy responsibility, but it fails to explain key parameters like applicationIds, jobId, limit, and depth. It mentions the profile URL is known 'from the list or application page' without mapping that to the actual parameters. savePdf and onlyMissing are documented in the schema, but the remaining parameters are left ambiguous.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource—'Queue full-profile fetches for applicants'—and clarifies scope by requiring a known profile URL and explicitly noting it does not re-open the application. This clearly distinguishes it from applicants_fetch_details, especially with the title 'Fetch full LinkedIn profiles only.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger condition: 'Use when details were fetched with includeProfile=false.' It also implies the alternative is not needed when profiles are already at full depth. However, it does not name a sibling tool or state a when-not-to-use condition as explicitly as it could.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

applicants_listList / search applicantsA
Read-onlyIdempotent

Page through applicants stored locally, with filters and full-text search over name, headline, resume text and profile text (prefix matching, e.g. "kubern django"). Rows omit resume/profile bodies; use applicant_get for one applicant. Reads the local database only (no LinkedIn traffic).

NameRequiredDescriptionDefault
jobIdNo
limitNo
orderNodesc
offsetNo
ratingNo
searchNoFull-text query over name, headline, resume text, profile text
orderByNoappliedAt
hasDetailNoWhether the application page was fetched (contact info, screening answers)
hasResumeNo
hasProfileNo

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and idempotentHint, so the description's added value is the local-only read (no LinkedIn traffic) and the omission of resume/profile bodies. This enriches the agent's understanding beyond annotations without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no fluff, with the core purpose front-loaded and the caveat about applicant_get placed right after. Every clause earns its place, making it efficient and easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only list/search tool with 10 parameters and no output schema, the description covers the essential context: local data, paging, filters, search fields, and the alternative for full details. It doesn't enumerate every filter, but the schema provides enums and some descriptions. The description is sufficient for an agent to decide when to use it and how to structure a basic query.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 20%, so the description must compensate. It explains the search parameter in detail (prefix matching, example) and mentions filters generally, but does not clarify the semantics of other parameters like rating, hasDetail, or orderBy. It partially compensates but leaves many parameters to be inferred from the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (page through) and resource (applicants) with clear scope: filters and full-text search over defined fields. It also distinguishes itself from the sibling applicant_get by noting rows omit resume/profile bodies. This makes the tool's purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly directs the agent to applicant_get for a single applicant's full details and clarifies that it reads only the local database (no LinkedIn traffic), implying sync/fetch tools are for live data. This gives clear when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

applicants_syncSync the applicant list of a jobA
Idempotent

Queue a crawl of the applicant LIST for one job (or every known job): names, headlines, locations, applied dates, application ids and profile links, 25 per page. Thousands of applicants mean many pages, so the crawl runs in chunks of pagesPerRun pages per worker turn, persists progress after every page, and resumes automatically across restarts and working-hours windows. Hidden "Not a fit" applicants are included by default. This does NOT open individual applications (use applicants_fetch_details for contact info, resumes and profiles). Progress: queue_status or jobs_list.

NameRequiredDescriptionDefault
jobIdNoLinkedIn job id (from jobs_list). Omit with allJobs=true to sync every known job.
allJobsNoSync every job known locally
restartNoIgnore saved progress and start from the first page again
pagesPerRunNoList pages (25 applicants each) per worker turn before the scheduler re-checks caps
includeNotAFitNoEnable the Ratings filter so applicants marked "Not a fit" are included

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint=false (mutation), openWorldHint=true, idempotentHint=true. The description adds significant context: it's a queued crawl that persists progress, resumes automatically, and includes hidden 'Not a fit' applicants by default. It also mentions that it does not open individual applications. This enriches the annotation-provided idempotence and open-world nature.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense paragraph but well-organized, starting with the core action, then data fields, then behavior nuances, then exclusions, then progress tracking. While it is somewhat long, it is front-loaded with the purpose and each sentence adds necessary detail (pages, progress, alternatives). No redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (queued crawl, pagination, persistence) and lack of output schema, the description explains key behaviors: chunking, progress persistence, resume, inclusion of not-a-fit, and where to check progress (queue_status/jobs_list). It also clarifies the scope (list vs details). Minor omissions might include response behavior when crawling completes, but the description is sufficient for an agent to safely invoke and monitor.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so each parameter is described in the schema. The description adds conceptual meaning by explaining that jobId can be omitted with allJobs=true, and that pagesPerRun controls chunks of pages per worker turn. This goes beyond simple definitions and helps an agent understand relationship between parameters. However, since schema is already detailed, the added value is moderate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool queues a crawl of applicant lists for one or all jobs, enumerating the exact data fields (names, headlines, locations, applied dates, application ids, profile links). It distinguishes itself from applicants_fetch_details for individual applications. It doesn't explicitly name the alternative for fetching details within the main description, but does in the usage section, so it's clear enough.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'use applicants_fetch_details for contact info, resumes and profiles' for opening individual applications, and notes that to sync every job you use allJobs=true. It also explains the pagination behavior (chunks of pagesPerRun, progress persistence, resume across restarts) guiding when to be patient. This gives clear when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_closeClose the browser sessionA

Stops the background worker and closes the Chrome window this server launched (in cdp mode it only disconnects; your Chrome keeps running). Queued tasks stay in the database; queue_start relaunches when needed.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Explains side effects beyond annotations: stops the worker, closes the Chrome window (or only disconnects in cdp), and confirms queued tasks persist. The description gives concrete behavioral context that annotations (readOnlyHint=false, destructiveHint=false) do not fully convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, information-dense, with the primary action front-loaded and the cdp caveat and persistence detail following. No filler or redundant phrasing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a no-parameter tool with no output schema, the description fully covers what it does, its side effects, and follow-up behavior (queue persistence and relaunch). Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so schema coverage is trivially 100% with an empty object. The description doesn't need to explain parameters; the baseline of 4 applies. It correctly adds no param-related noise.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action: 'Stops the background worker and closes the Chrome window this server launched', with a specific resource (browser session) and a distinction for cdp mode. This differentiates it from siblings like browser_status or debug_navigate.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides context on behavior in cdp mode and notes that queued tasks remain, implying it's safe to close. It mentions queue_start as the relaunch mechanism, but doesn't explicitly list when not to use this tool or compare with siblings. Still clear enough for an agent to decide.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_open_loginOpen LinkedIn login in your ChromeA
Idempotent

Launches (or connects to) your own Google Chrome with the dedicated profile and opens LinkedIn so YOU can sign in manually in the visible window. Nothing is typed on your behalf. Waits up to timeoutSeconds for the login to complete, then returns the status. Use before the first sync and whenever browser_status shows loggedIn=false or a checkpoint. After solving a checkpoint call queue_resume.

NameRequiredDescriptionDefault
timeoutSecondsNoHow long to wait for you to finish signing in

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses key behavioral traits: it launches or connects to the user's own Chrome, opens LinkedIn, does not type anything on the user's behalf, waits up to timeoutSeconds for login to complete, and returns the status. This goes beyond the annotations (readOnlyHint=false, openWorldHint=true, idempotentHint=true) by explaining the interactive nature and the wait behavior. It doesn't detail what the returned status looks like, but the core behavior is well covered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured. It front-loads the core action (launch Chrome, open LinkedIn, manual sign-in), then explains the wait behavior, and finally provides usage guidance. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete for an interactive login tool. It explains the purpose, the manual nature, the timeout behavior, and when to use it. It doesn't describe the exact return status format, but since there is no output schema, a bit more detail on the returned status could be helpful. However, the description covers the essential context an agent needs to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so the schema already documents the timeoutSeconds parameter. The description mentions 'waits up to timeoutSeconds' which reinforces the parameter's meaning but doesn't add significant new detail beyond the schema. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: it launches or connects to the user's own Chrome with a dedicated profile and opens LinkedIn for manual sign-in. It distinguishes itself from siblings by emphasizing that nothing is typed on the user's behalf and that the user signs in manually in a visible window.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says when to use this tool: before the first sync and whenever browser_status shows loggedIn=false or a checkpoint. It also names the next step after solving a checkpoint: call queue_resume. This provides clear usage context and alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

browser_statusBrowser statusA
Read-onlyIdempotent

Report whether the Chrome session is connected, whether LinkedIn is logged in, the current URL and any checkpoint (verification / login wall). Never launches a browser. Call this first, and whenever the queue reports needsHuman.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and idempotentHint; the description meaningfully adds that it never launches a browser and enumerates exactly what status information is reported. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The core purpose is front-loaded, and the rare critical usage instruction ('Call this first') is placed at the end of the second sentence without drifting into unrelated detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-input, idempotent status tool, the description fully defines what the agent should expect: connectivity, login state, current URL, and checkpoint presence. The absence of an output schema is compensated by this explicit enumerated report contract.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and schema coverage is 100%, so the baseline is 4. The description adds no parameter-specific detail, but none is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Report') and resource (Chrome session, LinkedIn login, URL, checkpoint), making it distinct from sibling tools like browser_open_login or debug_navigate. The 'Never launches a browser' clause sharpens the boundary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs to call this first and whenever the queue reports needsHuman, giving clear context for use. It does not name alternatives or exclusions, so it stops short of a full when-not/alternative comparison.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

debug_capturesCaptured network payloadsA
Read-only

List the JSON / RSC responses LinkedIn's own web app fetched while we browsed (Voyager REST, GraphQL, flagship-web). This is how you discover the endpoints and shapes behind the hiring dashboard. Bodies clipped to 2000 chars.

NameRequiredDescriptionDefault
kindNo
limitNo
urlContainsNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, and the description adds useful behavioral context beyond that: payloads are captured 'while we browsed' and bodies are clipped to 2000 characters. This tells the agent what to expect and prevents surprise from truncated output.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, no filler, with the main action and resource front-loaded. The body truncation note is placed early and earns its place because it materially affects how the agent interprets results.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description gives a clear purpose, a usage cue, and one important output trait (truncation), but it leaves parameter semantics and return structure unexplained. For a no-output-schema tool with 0% parameter coverage, this is usable but not fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description needed to compensate by explaining kind, limit, and urlContains, but it does not name or define any of them. The enum values are partially self-explanatory and the phrase 'Bodies clipped to 2000 chars' relates to output, not parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('List') with a concrete resource ('JSON / RSC responses LinkedIn's own web app fetched') and names the relevant endpoint families (Voyager REST, GraphQL, flagship-web). This clearly distinguishes it from sibling tools like debug_page_text or debug_find, which operate on page text rather than captured network payloads.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'This is how you discover the endpoints and shapes behind the hiring dashboard' gives a clear, actionable use case. It does not explicitly state when not to use it or name alternative tools, but the intended discovery workflow is strongly implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

debug_clickClick an element (human-like)A

Click the first visible element matching a selector on the current tab, with the same paced mouse movement the worker uses, then report the resulting URL, title, checkpoint state and the number of matches. For exploring pagination / menus while adapting selectors. Read-only actions only: never use it to rate, shortlist, move or message applicants.

NameRequiredDescriptionDefault
waitMsNoSettle time after the click
selectorYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds meaningful behavioral context beyond the annotations: it explains the human-like pacing, that only the first visible match is clicked, and exactly what will be reported after the click. It also adds a caution about not using it for rating or messaging. The 'Read-only actions only' phrasing is a usage constraint rather than a direct contradiction of readOnlyHint=false, since clicking can still navigate or alter page state.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences deliver the core behavior, the output report, the intended use case, and an exclusion guard. Every phrase earns its place, and the most important behavioral details are front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 2-parameter tool with no output schema, the description provides enough to call it correctly: the required selector, optional waitMs, and the expected results. It could be even more complete by describing what happens if no element matches or if the selector is invalid, but those are not critical gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The selector parameter lacks its own schema description, but the tool description partially compensates by explaining that selector selects the first visible matching element and that the match count is reported. waitMs is well described in the schema. The description does not clarify selector syntax (CSS, XPath, etc.), so compensation is only partial.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific action ('click'), a specific resource ('first visible element matching a selector on the current tab'), and adds concrete behavioral details (paced mouse movement, reported URL/title/checkpoint state/match count). This clearly distinguishes it from siblings like debug_navigate or debug_find.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says when to use it ('For exploring pagination / menus while adapting selectors') and gives a 'never use' exclusion for applicant-changing actions. It does not name alternative sibling tools explicitly, so it is slightly below a perfect when/when-not/alternatives statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

debug_findTry a CSS/Playwright selectorA
Read-only

Count and describe elements matching a selector on the current tab: text, href, aria-label, data-view-name, componentkey, class. Use to repair src/linkedin/selectors.ts when LinkedIn changes markup.

NameRequiredDescriptionDefault
limitNo
selectorYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool as read-only, so no contradiction exists. The description adds useful behavioral context beyond annotations by specifying the operation is scoped to the current tab and by disclosing which element properties are inspected. It does not describe error behavior or match limits, but the read-only annotation lowers the burden.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two tight sentences. The first front-loads the action, scope, and output fields; the second gives a concrete repair use case. There is no filler or repetition of schema/annotation information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only debugging tool with just two parameters and no output schema, the description covers what it does, what it returns, and when to use it. It could mention whether it waits for elements or what happens on zero matches, but those are minor gaps given the tool's debugging purpose.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It clarifies that 'selector' is a CSS/Playwright selector used to find matching elements on the current tab. The 'limit' parameter is not explained in the description, though its name, default, minimum, and maximum in the schema make its meaning reasonably inferable.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb-resource pair: 'Count and describe elements matching a selector on the current tab' and enumerates the exact fields returned. The title 'Try a CSS/Playwright selector' further clarifies the selector type, making it clearly distinct from siblings like debug_click and debug_page_text.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides an explicit use case: 'Use to repair src/linkedin/selectors.ts when LinkedIn changes markup.' This tells the agent when the tool is appropriate. It does not explicitly name alternative tools or exclusions, but the targeted repair scenario is strong contextual guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

debug_navigateNavigate the tab (LinkedIn only)A

Navigate the working tab to a linkedin.com URL the human way (paced), then report the final URL, title and checkpoint state. Pause the queue first so you do not fight the worker for the tab.

NameRequiredDescriptionDefault
urlYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false (mutation) and openWorldHint=true. The description adds meaningful behavioral context: it navigates 'the human way (paced)', pauses the queue, and reports final URL, title, and checkpoint state. This goes beyond the annotations by explaining the pacing behavior and the queue interaction, which is valuable for an agent deciding whether to invoke this tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with zero waste. The core action is front-loaded, the LinkedIn-only constraint is stated immediately, and the queue-pausing instruction is a critical operational detail that earns its place. Every word contributes to correct invocation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter tool with no output schema, the description covers the essential context: what it does, the scope constraint, the pacing behavior, the queue interaction, and what it reports back. The only minor gap is not explaining what 'checkpoint state' means or what the output format looks like, but with no output schema and a simple parameter, this is reasonably complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. The description mentions the URL must be a linkedin.com URL, which adds meaning beyond the schema's 'uri' format. However, it doesn't explain details like whether the URL must be absolute, whether redirects are followed, or what happens with non-LinkedIn URLs. The single parameter is simple enough that the description's LinkedIn-only constraint provides adequate but not complete semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('navigate'), a specific resource ('the working tab'), and a specific scope ('LinkedIn only'). It also distinguishes itself from generic navigation by noting the 'human way (paced)' approach and the reporting of final URL, title, and checkpoint state. This clearly separates it from siblings like debug_page_text or debug_find.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says to pause the queue first to avoid fighting the worker for the tab, which is a clear usage guideline. It also implies this is for LinkedIn URLs only. However, it doesn't explicitly state when NOT to use this tool versus alternatives like debug_page_text or debug_find, though the LinkedIn-only scope provides some implicit exclusion.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

debug_page_textVisible text of the current pageB
Read-only

Return the innerText of (or body) of the current tab, clipped. Cheap way to see what LinkedIn is showing.

NameRequiredDescriptionDefault
maxCharsNo

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the read-only nature is known. The description adds that the result is 'clipped' and comes from 'innerText', which is useful. It does not disclose performance implications or edge cases, but given the simple read-only nature, the bar is lower. No contradictions with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no fluff. The action is front-loaded, and the contextual note ('Cheap way...') is brief. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only tool with one optional parameter and no output schema, the description covers the core behavior. However, it does not explicitly state that maxChars limits the returned text length, nor does it describe the return type (string implied by 'innerText'). It also lacks mention of what happens when the limit is exceeded. These gaps are minor but present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has a single parameter maxChars with no description (coverage 0%). The description mentions 'clipped' but does not explicitly link this to maxChars. An agent cannot tell from the description that maxChars controls the clipping limit, its default, or its bounds. The description fails to compensate for the lack of schema documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Return the innerText of <main> (or body)') and scope ('current tab, clipped'). It distinguishes this from other debug tools by focusing on visible text extraction. However, it does not explicitly name any sibling alternative, so it lacks strong differentiation against debug_snapshot or debug_find.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'Cheap way to see what LinkedIn is showing' implies a quick inspection use case, but it does not provide explicit guidance on when to use this instead of other tools like debug_snapshot or debug_navigate. No alternatives are mentioned, and no exclusions are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

debug_snapshotSnapshot the current pageA
Read-only

Save a full-page screenshot, the HTML, the visible text and all captured network payloads of the current tab into the debug directory. Use when a task fails with "Could not find …" so selectors can be updated.

NameRequiredDescriptionDefault
nameNoLabel for the snapshot folder

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, so the agent knows this is safe. The description adds useful detail about what exactly gets saved, going beyond the annotations. It does not contradict them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The action and scope are front-loaded, and the usage hint follows immediately. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple, read-only snapshot tool with one optional parameter and no output schema, the description provides all essential information: what is saved, where (debug directory), and when to use it. No critical details are missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema covers the single parameter 'name' with a description ('Label for the snapshot folder'), and schema coverage is 100%. The tool description does not add any extra meaning to the parameter beyond what the schema provides, so a baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (Save) with a precise resource (current tab) and enumerates exactly what is captured (full-page screenshot, HTML, visible text, network payloads). This clearly distinguishes it from siblings like debug_page_text or debug_captures, which capture subsets.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides a concrete trigger scenario: 'Use when a task fails with "Could not find …"' and explains the purpose (so selectors can be updated). It does not explicitly name alternatives or state when not to use it, but the context is clear enough.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

jobs_listList known jobsA
Read-onlyIdempotent

List the posted jobs stored locally (from jobs_sync) with per-job progress counters: applicants stored, details fetched, resumes stored, profiles fetched, and applicant-list sync progress.

NameRequiredDescriptionDefault
statusNoFilter by job statusall

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and idempotentHint=true, so the safety profile is covered. The description adds useful context by specifying that data is local and that per-job progress counters are included, but it does not disclose other behavioral details like result ordering, pagination, or the shape of the returned list.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence that front-loads the core action and resource, then adds the important detail about progress counters. No wasted words and no redundant restatement of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list tool with one optional parameter and no output schema, the description gives enough context about what is listed and what counters are included. It would be slightly stronger with a note about how the status filter affects the result, but the schema already documents that parameter.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the only parameter (status) is fully documented with an enum and default in the schema. The description itself does not mention filtering, so it adds no extra parameter meaning, but the baseline of 3 is appropriate because the schema carries the full burden.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('List') and resource ('jobs stored locally from jobs_sync'), and names the exact per-job progress counters included in the result. This clearly distinguishes the tool from the sibling jobs_sync, which is presumably the operation that fetches/syncs the jobs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'stored locally (from jobs_sync)' implies this tool is for inspecting the local synced snapshot rather than triggering a sync, but it never explicitly says when to prefer this tool over jobs_sync or any other sibling. There is no when/if guidance or exclusion of alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

jobs_syncSync posted jobs from LinkedInA
Idempotent

Queue a crawl of your posted jobs (/my-items/posted-jobs/, open and optionally closed) and store them locally. Runs in the background paced like a person (roughly 1–3 minutes). With wait=true the call blocks up to 2 minutes and returns the jobs found. Afterwards use jobs_list.

NameRequiredDescriptionDefault
waitNoBlock until the sync finishes (max 120 s)
includeClosedNoAlso crawl the Closed tab

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false, openWorldHint=true, idempotentHint=true. The description adds behavioral context beyond annotations: it runs 'in the background paced like a person (roughly 1–3 minutes)' and that wait=true blocks. It also notes the side effect of storing locally. This is valuable context that isn't in the annotations. It doesn't contradict any annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no wasted words. The first sentence states the core action and location, the second explains behavior and options. It front-loads the primary purpose and keeps the alternative usage compact. Perfectly structured for quick comprehension.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with two optional parameters and no output schema, the description covers everything an agent needs: what it does, how it behaves (background, paced), how to get results (wait=true or use jobs_list), and the specific path. The annotations already cover open-world and idempotency. No critical information is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents both parameters. The description adds interpretative value: it explains the effect of wait=true ('blocks up to 2 minutes and returns the jobs found') and mentions 'open and optionally closed' which maps to includeClosed. This goes beyond the schema's brief descriptions, so it earns above the baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Queue a crawl'), the resource ('your posted jobs'), and the storage effect ('store them locally'). It also mentions the specific path '/my-items/posted-jobs/' and distinguishes itself from the sibling 'jobs_list' by saying 'Afterwards use jobs_list'. This makes the tool's purpose unambiguous and distinct.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly explains the two modes of use: background crawl (non-blocking) and with wait=true (blocks up to 2 minutes and returns jobs). It also directs the agent to the next appropriate tool (jobs_list) after the sync. This gives clear when-to-use and follow-up guidance, though it doesn't explicitly state when not to use it, the context is sufficient.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pacing_getGet pacing settingsA
Read-onlyIdempotent

Current human-pacing settings: speed, working hours/days, daily caps (with warm-up ramp), hourly cap, break pattern, delay table.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile with readOnlyHint=true and idempotentHint=true. The description adds value by listing what is returned, including the nuance of daily caps with a warm-up ramp, which is useful given there is no output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One front-loaded sentence with no filler. The scope appears first, and every listed item communicates a distinct aspect of the returned settings.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter read-only tool, the description is largely sufficient: annotations cover safety and the field enumeration compensates for the missing output schema. It could add exact response formatting or freshness semantics, but an agent can invoke it correctly without those.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

This tool has zero parameters and schema coverage is 100%, so the schema fully resolves parameter questions. The description does not need to add parameter semantics, and the baseline of 4 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The phrase 'Current human-pacing settings' names the resource and scope, and the verb 'Get' communicates the action precisely. The enumerated components distinguish it from siblings like pacing_set.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The word 'Current' implies this is for reading existing settings, and the sibling pacing_set implies the write counterpart. However, there is no explicit when-to-use guidance or direct mention of alternatives, so usage is inferred rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pacing_setChange pacing settingsA
Idempotent

Adjust how fast/when the worker acts. Persisted. Slower is safer: the defaults mimic a recruiter reviewing applicants during office hours. Raising dailyProfileCap above ~100 or hourlyActionCap above ~60 materially increases the chance LinkedIn restricts the account.

NameRequiredDescriptionDefault
speedNo
timezoneNoIANA time zone, e.g. Europe/Berlin; empty string = system
workDaysNo0=Sunday … 6=Saturday
rampStartNoDay-1 cap; 0 disables the warm-up ramp
rampPerDayNo
workHoursEndNo
randomizeOrderNo
workHoursStartNo
dailyProfileCapNo
hourlyActionCapNo
dailyApplicantCapNo
warmupProbabilityNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses that changes are persisted ('Persisted'), which is valuable behavioral context beyond the idempotentHint annotation. It also warns about risk of account restriction, which is critical behavioral information. The annotation idempotentHint=true is consistent with the description's implication that setting the same values again is safe. No contradiction found.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded with the core purpose. The risk warning is placed after the main purpose, and every sentence adds value. It's appropriately sized for a tool with 12 parameters, focusing on the most critical behavioral guidance without unnecessary detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has 12 parameters, no output schema, and only 25% schema description coverage, the description provides essential context about persistence and risk. It explains the two most safety-critical parameters. However, it doesn't cover the meaning of several other parameters (ramp, workDays, warmupProbability), which an agent might need to set correctly. The risk warning is a strong addition that improves completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 25%, so the description must compensate for undocumented parameters. The description explains the meaning of dailyProfileCap and hourlyActionCap with specific thresholds, adding value beyond the schema. However, it doesn't explain other parameters like rampStart, rampPerDay, workDays, or warmupProbability, leaving gaps. The description partially compensates but not fully.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool adjusts pacing settings for the worker, with a specific verb ('Adjust') and resource ('pacing settings'). It distinguishes itself from siblings like pacing_get by implying this is the setter counterpart, though it doesn't explicitly name the sibling. The title 'Change pacing settings' reinforces the purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context on when to use this tool: when adjusting how fast/when the worker acts. It also gives practical guidance on safe values (e.g., raising caps above certain thresholds increases risk), which helps an agent decide appropriate parameter values. However, it doesn't explicitly state when NOT to use it or name alternatives like pacing_get for reading current settings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

queue_cancelCancel pending tasksA
Destructive

Cancel PENDING tasks matching the filters (type / jobId / applicationId). Without filters cancels every pending task. Running tasks finish; done work is untouched.

NameRequiredDescriptionDefault
typeNo
jobIdNo
applicationIdNo

TDQS

A3.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes beyond the destructiveHint annotation by specifying exactly what is affected: only PENDING tasks, with running tasks allowed to finish and completed work untouched. It also discloses the default behavior when no filters are provided (cancels all pending). This is rich behavioral context that an agent needs to anticipate side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured, with two sentences. The main action is front-loaded, followed by essential clarifications about scope and side effects. Every sentence adds value, and there is no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a cancellation tool with no required parameters and no output schema, the description covers the critical aspects: what it does, the filters, the default behavior, and what it does not affect. It could optionally mention error handling or return values, but these are not essential for correct invocation given the annotations and the clear scope. Minor gaps remain, but overall it is sufficiently complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage, so the description must compensate. It lists the three filter fields (type, jobId, applicationId) but does not explain their semantics, combination logic, or constraints (e.g., how multiple filters interact, whether they are OR/AND). The description essentially repeats the parameter names without adding meaning beyond what the schema already shows.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (cancel), the target (pending tasks), and the filtering mechanism (type/jobId/applicationId). It also distinguishes the scope: without filters it cancels every pending task, and explicitly notes that running tasks finish and done work is untouched. This differentiates it from sibling tools like queue_pause or queue_retry_failed.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no explicit guidance on when to use this tool versus alternatives. It does not mention queue_pause, queue_retry_failed, or any other sibling, nor does it state conditions for choosing this over them. The intended use is implied by the name and description, but there is no direct comparison or exclusion criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

queue_pausePause the queueA
Idempotent

Pause processing after the current task finishes. Tasks stay queued. Resume with queue_resume.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds behavioral detail beyond the annotations: processing halts only after the current task finishes, and tasks remain queued. It also references the resume path. The idempotentHint annotation covers repeat-invocation behavior, so the description does not need to restate it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences deliver the core behavior, the state preservation guarantee, and the resume counterpart with no filler. The most important fact, when the pause takes effect, is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-argument state-change tool, the description is complete: it states what happens, when it happens, and how to undo it. The output schema is absent but not needed for such a simple operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parametersasi, so parameter semantics are trivially complete. The baseline for zero-parameter tools is 4, and the description appropriately focuses on behavior rather than parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool pauses queue processing, with the precise timing detail 'after the current task finishes.' It also clarifies 'Tasks stay queued,' which distinguishes it from a cancel operation. This is specific and immediately understandable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when to use the tool: to pause processing temporarily while retaining queued tasks. It mentions queue_resume as the natural counterpart. However, it does not explicitly contrast with queue_cancel, leaving the main alternative usage boundary implicit rather than fully explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

queue_resumeResume the queueA
Idempotent

Clear the paused flag and, by default, the needsHuman flag set after a LinkedIn checkpoint or login wall (call after you solved it in the Chrome window), then start the worker.

NameRequiredDescriptionDefault
clearNeedsHumanNo

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare it is not read-only and is idempotent. The description adds that it clears the paused flag and, by default, the needsHuman flag, and then starts the worker, disclosing the side effects. It also explains the reason for the flags (checkpoint/login wall), providing useful context beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence that packs the action, condition, and parameter default. It is slightly long but still efficient. The key information is front-loaded, with the action stated first.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with one optional boolean parameter and no output schema, the description covers the purpose, the trigger condition, and the default behavior. It is sufficient for an agent to know when and how to call it. It does not describe error handling or worker behavior, but that is beyond the scope for this simple tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has one boolean parameter with no description. The description mentions 'by default' clearing the needsHuman flag, which implies the parameter controls that behavior. This adds meaning beyond the schema, but it does not explicitly state what happens when clearNeedsHuman is false. Given 0% schema coverage, the description partially compensates but could be more explicit.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool clears the paused flag and, by default, the needsHuman flag, then starts the worker. It specifies the exact context (LinkedIn checkpoint/login wall) and distinguishes it from sibling tools like queue_start, which likely only starts the queue without clearing flags.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly provides a trigger condition: 'call after you solved it in the Chrome window.' This gives clear guidance on when to use it. However, it does not explicitly contrast it with alternatives like queue_start, so it lacks a full 'when-not-to-use' statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

queue_retry_failedRetry failed tasksA
Idempotent

Put failed tasks back into the queue with a fresh attempt budget (optionally filtered by type / jobId). Useful after fixing a login problem or when LinkedIn had a bad day.

NameRequiredDescriptionDefault
typeNo
jobIdNo

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already signal a mutating, idempotent operation, and the description adds meaningful behavioral context: tasks are put back 'with a fresh attempt budget' and can be filtered by type/jobId. This goes beyond the annotations without contradicting them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences, with the core action and key qualifier front-loaded. The optional filter and usage context are stated without any redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a low-complexity tool with two optional parameters and no output schema, the description covers the main behavior and gives a clear situational trigger. It does not detail defaults for the attempt budget or what happens to existing queue entries, but this is a minor gap given the tool's simplicity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, and the description only says the operation is 'optionally filtered by type / jobId'. This names both parameters and hints at their role, but does not explain the enum values or what jobId refers to, leaving the description to carry more weight than it actually does.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Put failed tasks back into the queue') and a specific resource, clearly identifying the tool as a retry operation for failed tasks. It also distinguishes itself from queue_status/queue_start/queue_cancel by focusing on re-enqueueing failed tasks with a fresh attempt budget.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'Useful after fixing a login problem or when LinkedIn had a bad day' provides clear situational context for when the tool is appropriate. It does not explicitly name alternatives or state when not to use it, so it falls just short of the highest tier.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

queue_startStart the workerA
Idempotent

Start (or resume) the background worker in this process. It only acts when tasks are pending and the scheduler allows it. No-op if another process (npm run worker) owns the queue.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate mutating (readOnlyHint=false), external side effects (openWorldHint=true), and idempotency (idempotentHint=true). The description adds meaningful nuance beyond these: it discloses the conditional behavior (only acts when tasks pending and scheduler allows) and the no-op scenario when another process owns the queue. This enriches the agent's understanding of when side effects actually occur.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, directly front-loaded with the primary action. Each clause earns its place: the action, the conditional triggers, and the ownership caveat. No filler, no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no parameters and no output schema, the description is nearly complete. It covers what it does, when it acts, and when it is a no-op. Subtle points like asynchronous execution or post-condition state are not mentioned, but these are minor given the annotations and context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and an empty schema (100% coverage). Per the rubric, a baseline of 4 is appropriate when there are no parameters to document. The description doesn't need to add parameter details, and it doesn't.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action: 'Start (or resume) the background worker in this process.' It identifies the resource (background worker) and the scope (this process), and adds a distinguishing condition (no-op if another process owns the queue). While it doesn't explicitly name sibling tools like queue_resume, the 'in this process' qualifier effectively differentiates it from process-wide queue operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides context on when the tool actually acts: 'It only acts when tasks are pending and the scheduler allows it. No-op if another process owns the queue.' This tells the agent the conditions under which it should expect an effect. It does not explicitly compare with queue_resume or queue_pause, but it gives enough guidance for the current process context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

queue_statusQueue and worker statusA
Read-onlyIdempotent

Everything about the background work: worker state (running / paused / needsHuman), task counts by type and status, today's counters vs effective daily caps, whether we are inside the working-hours window and when the next window starts, per-job applicant-list sync progress, browser status and recent events. Poll this to follow long exports.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and idempotentHint=true, so the safe, repeatable nature is known. The description adds behavioral value by saying this tool is meant for polling during long exports and by naming live status dimensions such as running/paused/needsHuman and working-hours windows. There is no contradiction with the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but compact, using a single list to cover many distinct status dimensions and ending with a concrete usage directive. Every listed item adds a different aspect of the status. It is not overly verbose, though the opening phrase 'Everything about the background work' is slightly broad and could be more specific.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only, idempotent tool, the description supplies the key context an agent needs: what data is available, what state categories exist, and when to poll. Since there is no output schema, the description carries the burden of describing return semantics at a category level, which it does adequately. It could be more explicit about response structure or event formatting, but nothing essential is missing for correct selection and invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the schema coverage is trivially 100%, so there is no per-parameter meaning to add. For a zero-parameter tool, a baseline of 4 is appropriate; the description instead compensates by telling the agent what status categories to expect, which is useful given the absence of an output schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies a read-only status resource by enumerating the exact categories it covers: worker state, task counts, daily caps, working-hours window, sync progress, browser status, and events. The directive 'Poll this to follow long exports' gives a specific verb and use, and the read-only nature distinguishes it from sibling control tools like queue_start, queue_pause, and queue_retry_failed.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The final sentence provides an explicit trigger condition: poll this to follow long exports. This makes the primary use case clear and separates it from queue-control siblings. However, it does not explicitly mention when to prefer queue_tasks for detailed task-level information or browser_status for browser-only status, so it lacks formal exclusions or alternative routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

queue_tasksList tasksA
Read-onlyIdempotent

Recent tasks with status, attempts and last error (filter by type / status / jobId). Use to see why something failed.

NameRequiredDescriptionDefault
typeNo
jobIdNo
limitNo
statusNo

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and idempotentHint=true, so the safety profile is covered. The description adds that results include status, attempts, and last error, which is useful behavioral context beyond the schema. However, it doesn't disclose default ordering, time window, or whether 'recent' is a fixed window, which would be relevant for interpreting results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with no filler. The core purpose and the primary use case are front-loaded, and every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only list tool with no output schema, the description covers the main purpose and filter dimensions. However, it lacks details on result ordering, pagination behavior beyond the limit parameter, and what 'recent' means. Given the tool's diagnostic purpose, an agent might need to know whether results are sorted by recency or whether failed tasks are highlighted.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the burden of explaining parameters. It mentions filtering by type/status/jobId, which maps to three of the four parameters, but it doesn't explain the 'limit' parameter or provide any format/semantics beyond what the schema enum names already convey. The description adds some meaning but doesn't fully compensate for the 0% schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('List') and resource ('tasks'), and adds the key attributes shown (status, attempts, last error) plus filter dimensions. It distinguishes itself from siblings like queue_status and queue_retry_failed by focusing on listing tasks with failure diagnostics, though it doesn't explicitly name a sibling alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'Use to see why something failed' gives clear context for when to call this tool, implying it is the diagnostic entry point for failed tasks. It doesn't explicitly state when not to use it or name alternatives like queue_status, but the intended use case is clear enough for an agent to select it appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resume_textResume textA
Read-onlyIdempotent

Plain text extracted from the downloaded resume (PDF/DOCX) of one applicant, clipped to maxChars. Empty when the resume was a scanned image or was not downloaded.

NameRequiredDescriptionDefault
maxCharsNo
applicationIdYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already mark the tool as readOnly and idempotent, and the description adds meaningful behavior beyond that: text is clipped to maxChars and will be empty for scanned images or missing downloads. This is useful context that the annotations do not provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One tight sentence that front-loads the core behavior and adds the important empty-case caveat. Every word earns its place, with no redundant phrasing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter read-only tool, the description covers the core behavior, the clip behavior, and the empty-case scenario. Without an output schema, a bit more detail on the exact return shape (e.g., whether empty is an empty string or null) would be useful, but the description is largely sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It maps applicationId to 'one applicant' and mentions maxChars as the clip limit, but it does not fully define either parameter's semantics or format. The schema provides the required/default values, so the description provides only partial added meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states exactly what the tool returns: plain text extracted from one applicant's downloaded resume (PDF/DOCX), clipped to maxChars. This clearly distinguishes it from broader tools like applicant_get or debug_page_text by scoping it to resume text for a single application.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool is for retrieving resume text for one applicant, but it does not explicitly say when to prefer it over sibling tools such as applicant_get or applicants_fetch_details. The empty-when-scanned/no-download note helps an agent interpret results, but no explicit when-to-use or alternative guidance is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 27 tool updatesv0.1.0
    • First observedapplicant_get
    • First observedapplicants_export
    • First observedapplicants_fetch_details
    • First observedapplicants_fetch_profiles
    • First observedapplicants_list
    • First observedapplicants_sync
    • First observedbrowser_close
    • First observedbrowser_open_login
    • First observedbrowser_status
    • First observeddebug_captures
    • First observeddebug_click
    • First observeddebug_find
    • First observeddebug_navigate
    • First observeddebug_page_text
    • First observeddebug_snapshot
    • First observedjobs_list
    • First observedjobs_sync
    • First observedpacing_get
    • First observedpacing_set
    • First observedqueue_cancel
    • First observedqueue_pause
    • First observedqueue_resume
    • First observedqueue_retry_failed
    • First observedqueue_start
    • First observedqueue_status
    • First observedqueue_tasks
    • First observedresume_text

TDQS

A3.7/5.0

Scored across 27 tools

Disambiguation4/5

Tools are grouped by clear resource prefixes (browser_, debug_, jobs_, applicants_, queue_, pacing_) and the descriptions sharply separate crawling stages—list sync vs detail fetch vs profile fetch. A couple of controls could be confused (queue_start vs queue_resume, debug_click vs debug_navigate), but the descriptions resolve those boundaries.

Naming Consistency4/5

Most names follow a domain_prefix + verb pattern (queue_pause, pacing_set, applicants_export), so the set is predictable. Deviations include 'browser_open_login' (resource+verb+object), noun-only names like 'resume_text' and 'queue_tasks', and mixed applicant/applicants singular/plural, but these are minor.

Tool Count2/5

At 27 tools, this exceeds a comfortable agent surface and falls in the 'too many' band, especially since 6 debug_* and 9 queue_* tools are operational rather than core applicant functions. The browser-automation domain justifies some of them, but an agent will still face a large selection problem.

Completeness4/5

The core read/extract lifecycle is well covered: jobs sync/list, applicant list/detail/profile/resume fetch, local search/get, and export. Gaps are mostly write-side (no shortlist, rate, or status changes) and there is no one-shot 'sync everything', but those appear to be out of scope or handled via manual browser work.

Maintenance

ActivityMaintained
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables comprehensive LinkedIn profile search, data extraction, and contact enrichment using Google Search, Apollo.io, and AI-powered analysis. Includes automated data mining workflows with database storage and CSV export capabilities.
    3
    ISC
  • A
    license
    Not graded
    quality
    B
    maintenance
    LinkedIn from the user's own logged-in Chrome session for AI agents: search, profiles, post engagers, company employees, lists, sequences, inbox and a Research Pack, with hard caps and a human approval queue. Local-first, no headless browser.
    13
    MIT
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to automate recruiter-side candidate sourcing and outreach by searching with multiple keywords, viewing and screenshotting resumes to extract share links, maintaining a persistent candidate database, filtering and scoring candidates, exporting Markdown reports, and sending greetings through a logged-in Chrome session.
    -
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to automate recruiter workflows by batch-searching candidates, capturing and OCR-ing resume screenshots, scoring and filtering applicants, extracting share links, greeting candidates, and exporting Markdown reports.
    -