linkedin-applicants-mcp
Export and manage LinkedIn job applicants from your own logged-in Chrome, with local data browsing, exports, and human-like pacing.
Check Chrome and LinkedIn session status, and open a manual login window.
Sync your posted jobs (open and closed) and list stored jobs with progress counters.
Crawl applicant lists per job or all jobs, including hidden Not a fit applicants, resumable across restarts.
Queue detailed fetches per applicant: application pages, contact info, screening answers, resumes, and full LinkedIn profiles.
Fetch full profiles only for applicants whose profile URLs are already known.
Read, search, filter, and page through locally stored applicants; get one applicant's full record; extract resume text.
Export applicants to CSV, JSON, or JSONL files with Excel-friendly CSV and formula-injection protection.
Monitor and control the background queue: status, start, pause, resume, cancel pending tasks, retry failed tasks.
Adjust pacing: speed, working hours and days, timezone, daily and hourly caps, ramp-up, breaks, randomization.
Use debug tools to snapshot pages, navigate LinkedIn, read visible text, test selectors, click read-only elements, and inspect captured network payloads.
Run the
review_applicantsprompt to score and rank applicants against criteria using local data.Run a local dashboard for overview tiles, job progress, applicant tables, detail drawer, exports, and worker events.
Keep all LinkedIn activity read-only: no rating, messaging, shortlisting, or connecting.
Enables pushing exported applicants, resumes, and profiles into Greenhouse through its API for applicant tracking.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@linkedin-applicants-mcpexport applicants for my latest job post to CSV"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
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:4173A 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.jsonand 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.

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 (
savePdfis 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 │
└───────────────────────────────────┘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.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.
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.
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 callqueue_resume. HTTP 429/999 back the queue off for hours; three unexpected failures in a row trigger a 20–40 minute cool-down.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, astart=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 |
Browser | Google Chrome installed. The server launches your Chrome and never downloads a browser. Edge and Chromium work via |
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 build2. Sign in once
npm run loginChrome 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.jsAdd --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 Code |
|
OpenAI Codex CLI |
|
Cursor |
|
Windsurf |
|
VS Code (GitHub Copilot agent mode) |
|
Cline, Roo Code | MCP Servers panel, "Configure MCP Servers" ( |
Zed |
|
Gemini CLI |
|
Continue |
|
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 |
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-mcpTest 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 workerand setLINKEDIN_MCP_AUTOSTART_WORKER=falsein 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 |
| Is Chrome connected and LinkedIn logged in? If not, |
2 |
| Crawls Posted jobs: the default tab plus every |
3 |
| Jobs stored locally with progress counters. Pick a |
4 |
| Queues the applicant-list crawl: 25 per page, progress saved after every page, hidden "Not a fit" applicants included. |
5 |
| Queues, per applicant, the application page plus resume download and then the full profile ( |
6 |
| 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 |
| Read and export locally, instant, no LinkedIn traffic. Or run the |
Steps 2–5 only enqueue work; the worker does the browsing during working hours.
Tools
Session
Tool | Description |
| Is Chrome connected, is LinkedIn logged in, current URL, any checkpoint. Never launches a browser. |
| Launches (or connects to) your Chrome with the dedicated profile and opens LinkedIn so you can sign in; waits up to |
| Stops the worker and closes the Chrome window the server launched (cdp mode: only disconnects). Queued tasks stay in the database. |
Jobs
Tool | Description |
| Queue a crawl of |
| Jobs stored locally with per-job counters: applicants stored, details fetched, resumes stored, profiles fetched, list-sync progress. |
Applicants (LinkedIn work, queued)
Tool | Description |
| Crawl the applicant list of one job or |
| Open each application page (email/phone when shared, screening answers, rating), capture the resume, then optionally the full profile ( |
| Full profiles only, for applicants whose profile URL is already known. The most rate-sensitive action on LinkedIn. |
Data (local, instant)
Tool | Description |
| Page through stored applicants with filters ( |
| The full local record of one applicant: list fields, application details, resume text (clipped to |
| Plain text extracted from the downloaded resume (PDF/DOCX). Empty for scanned PDFs. |
| 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 |
| Worker state (running / paused / |
| Start or resume the worker in this process. No-op if another process ( |
| Pause after the current task; tasks stay queued. |
| Clear the paused flag and, by default, the |
| Cancel tasks by |
| Put failed tasks back with a fresh attempt budget. |
| Recent tasks with status, attempts and last error. |
| Current speed, working hours/days, caps, ramp, breaks, delay table. |
| 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 |
| Save a full-page screenshot, the HTML, the visible text and all captured network payloads of the current tab into |
| Navigate the tab to a |
| The |
| Count and describe the elements matching a CSS/Playwright selector (text, href, aria-label, |
| Click the first visible match with the worker's paced mouse movement. Refuses selectors that look like a write action (rate, shortlist, message, reject, …). |
| 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 |
Working hours and days | 09:00–19:00 local time (or |
|
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 |
|
Daily cap: full profile views | 80, ramped at 70 % of the applicant ramp, varied per day the same way |
|
Hourly cap: LinkedIn page actions | 40 per rolling hour, varied per hour the same way |
|
Long breaks | every 25–60 actions, 5–20 minutes |
|
Order | same-priority tasks shuffled; 8 % chance of a warm-up visit to the feed |
|
LinkedIn "Save to PDF" | at most 150 profiles per month (LinkedIn's own limit is 200) |
|
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 |
|
|
|
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_setwithrampStart,rampPerDay,dailyApplicantCap),hourlyActionCap30, the built-in breaks,warmupProbability0.15 so feed visits break the cycle.Profiles:
dailyProfileCap50, and only for applicants who meet the must-have qualifications (applicants_fetch_detailswithprofileMinMustHave, 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 |
|
| Data directory (database, Chrome profile, files, exports, logs) |
|
|
|
|
| DevTools endpoint for |
|
|
|
|
|
|
|
|
|
|
| Local working window |
|
| 0 = Sunday … 6 = Saturday |
| system | IANA zone, e.g. |
|
| Application pages per day |
|
| Full profile views per day |
|
| LinkedIn page actions per rolling hour |
|
| Warm-up ramp; |
|
| Day-to-day spread of the caps: each day and hour draws up to this fraction below the configured value, never above it; |
|
| Cap for LinkedIn "Save to PDF" |
|
| Start the worker inside the MCP server process |
|
| Persist every captured LinkedIn payload under |
|
|
|
|
| Port for |
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=cdpChrome 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 workerThis 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). |
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 |
"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 |
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 |
| LinkedIn raised a verification, CAPTCHA, "unusual activity" or login page. Solve it in the Chrome window (or run |
"Could not launch Chrome with profile …" | Another Chrome window is using the dedicated profile. Close it, or switch to |
List stops before the reported total ( | LinkedIn may cap deep pagination or hide buckets. Re-run |
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. |
A resume shows | Open the applicant's |
Nothing runs | Check |
Where are the logs? | stderr (Claude Desktop writes it to |
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 typecheckThe verified research behind the URL patterns, selectors, internal endpoints and pacing numbers lives in docs/research.
Acknowledgements
patchright, the Playwright build that removes the automation leaks of Chrome DevTools Protocol.
Model Context Protocol TypeScript SDK, unpdf and mammoth for the server, PDF and DOCX text extraction.
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
Available Tools
27 toolsapplicant_getGet one applicantARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| maxChars | No | Clip resume text to this many characters | |
| applicationId | Yes | ||
| includeProfile | No | ||
| includeResumeText | No |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| jobId | No | ||
| format | No | csv | |
| outPath | No | Absolute output path; default under the data directory /exports | |
| includeProfile | No | Include the structured profile (JSON string in CSV) | |
| includeResumeText | No |
TDQS
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.
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.
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.
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.
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.
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 profilesAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| jobId | No | Fetch all applicants of this job (subject to onlyMissing/limit) | |
| limit | No | Queue at most this many applicants now | |
| savePdf | No | Reserved: 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 | |
| orderByFit | No | Fetch 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 | |
| onlyMissing | No | Skip applicants whose details were already fetched (with downloadResume, applicants whose resume is still missing are included) | |
| profileDepth | No | 'full' visits detail sections when Voyager is unavailable; 'basic' reads only the main profile page | full |
| applicationIds | No | Explicit application ids instead of a whole job | |
| downloadResume | No | ||
| includeProfile | No | Also queue the full LinkedIn profile after the application page | |
| profileMinMustHave | No | With 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
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.
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.
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.
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.
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.
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 onlyAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| depth | No | full | |
| jobId | No | ||
| limit | No | ||
| savePdf | No | Reserved: currently skipped with a warning (Chrome download crash), the structured profile is stored instead | |
| onlyMissing | No | Skip applicants whose profile was already fetched | |
| applicationIds | No |
TDQS
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.
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.
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.
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.
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.
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 applicantsARead-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).
| Name | Required | Description | Default |
|---|---|---|---|
| jobId | No | ||
| limit | No | ||
| order | No | desc | |
| offset | No | ||
| rating | No | ||
| search | No | Full-text query over name, headline, resume text, profile text | |
| orderBy | No | appliedAt | |
| hasDetail | No | Whether the application page was fetched (contact info, screening answers) | |
| hasResume | No | ||
| hasProfile | No |
TDQS
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.
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.
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.
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.
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.
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 jobAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| jobId | No | LinkedIn job id (from jobs_list). Omit with allJobs=true to sync every known job. | |
| allJobs | No | Sync every job known locally | |
| restart | No | Ignore saved progress and start from the first page again | |
| pagesPerRun | No | List pages (25 applicants each) per worker turn before the scheduler re-checks caps | |
| includeNotAFit | No | Enable the Ratings filter so applicants marked "Not a fit" are included |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 ChromeAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| timeoutSeconds | No | How long to wait for you to finish signing in |
TDQS
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.
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.
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.
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.
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.
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 statusARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 payloadsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | ||
| limit | No | ||
| urlContains | No |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| waitMs | No | Settle time after the click | |
| selector | Yes |
TDQS
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.
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.
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.
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.
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.
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 selectorARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| selector | Yes |
TDQS
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.
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.
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.
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.
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.
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_page_textVisible text of the current pageBRead-only
Return the innerText of (or body) of the current tab, clipped. Cheap way to see what LinkedIn is showing.
| Name | Required | Description | Default |
|---|---|---|---|
| maxChars | No |
TDQS
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.
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.
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.
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.
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.
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 pageARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Label for the snapshot folder |
TDQS
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.
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.
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.
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.
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.
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 jobsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Filter by job status | all |
TDQS
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.
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.
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.
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.
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.
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 LinkedInAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Block until the sync finishes (max 120 s) | |
| includeClosed | No | Also crawl the Closed tab |
TDQS
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.
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.
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.
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.
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.
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 settingsARead-onlyIdempotent
Current human-pacing settings: speed, working hours/days, daily caps (with warm-up ramp), hourly cap, break pattern, delay table.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 settingsAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| speed | No | ||
| timezone | No | IANA time zone, e.g. Europe/Berlin; empty string = system | |
| workDays | No | 0=Sunday … 6=Saturday | |
| rampStart | No | Day-1 cap; 0 disables the warm-up ramp | |
| rampPerDay | No | ||
| workHoursEnd | No | ||
| randomizeOrder | No | ||
| workHoursStart | No | ||
| dailyProfileCap | No | ||
| hourlyActionCap | No | ||
| dailyApplicantCap | No | ||
| warmupProbability | No |
TDQS
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.
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.
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.
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.
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.
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 tasksADestructive
Cancel PENDING tasks matching the filters (type / jobId / applicationId). Without filters cancels every pending task. Running tasks finish; done work is untouched.
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | ||
| jobId | No | ||
| applicationId | No |
TDQS
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.
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.
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.
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.
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.
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 queueAIdempotent
Pause processing after the current task finishes. Tasks stay queued. Resume with queue_resume.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 queueAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| clearNeedsHuman | No |
TDQS
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.
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.
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.
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.
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.
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 tasksAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | ||
| jobId | No |
TDQS
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.
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.
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.
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.
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.
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 workerAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 statusARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 tasksARead-onlyIdempotent
Recent tasks with status, attempts and last error (filter by type / status / jobId). Use to see why something failed.
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | ||
| jobId | No | ||
| limit | No | ||
| status | No |
TDQS
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.
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.
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.
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.
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.
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 textARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| maxChars | No | ||
| applicationId | Yes |
TDQS
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.
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.
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.
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.
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.
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.
27 tool updates
v0.1.0- First observed
applicant_get - First observed
applicants_export - First observed
applicants_fetch_details - First observed
applicants_fetch_profiles - First observed
applicants_list - First observed
applicants_sync - First observed
browser_close - First observed
browser_open_login - First observed
browser_status - First observed
debug_captures - First observed
debug_click - First observed
debug_find - First observed
debug_navigate - First observed
debug_page_text - First observed
debug_snapshot - First observed
jobs_list - First observed
jobs_sync - First observed
pacing_get - First observed
pacing_set - First observed
queue_cancel - First observed
queue_pause - First observed
queue_resume - First observed
queue_retry_failed - First observed
queue_start - First observed
queue_status - First observed
queue_tasks - First observed
resume_text
TDQS
Scored across 27 tools
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.
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.
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.
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
Related MCP Connectors
Search, label and export your LinkedIn saved posts, then draft, schedule and publish from them.
Search LinkedIn & Sales Navigator. Get structured people, companies, posts, jobs data. 4.9/5 on G2.
LinkedIn jobs, companies and people for AI agents, plus messages and invites via your own account.
Live LinkedIn people, company, post and job data, ICP search and job-change signals for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables 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.3ISC
- AlicenseNot gradedqualityBmaintenanceLinkedIn 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.13MIT
- FlicenseNot gradedqualityAmaintenanceEnables 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.-
- FlicenseNot gradedqualityAmaintenanceEnables 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.-