protogrid

Search / fyi.levels/levels-fyi

Levels.fyi

R0· no sign-in

Salary, level and job data from 1M+ pay reports. Benchmark pay, evaluate offers, search open jobs.

v1.0.0 · active · repository · website · descriptor JSON · versions

An agent can connect right now, no human step.Remote endpoint, no authentication, reachable on the last probe.
Quality79good
Trust96of 100
Uptime, 30 days100%
Latency p50280 ms

Connect

8 clients · secrets stay placeholders
{
  "mcpServers": {
    "levels-fyi": {
      "type": "http",
      "url": "https://mcp.levels.fyi/"
    }
  }
}
claude mcp add --transport http levels-fyi https://mcp.levels.fyi/
[mcp_servers.levels-fyi]
url = "https://mcp.levels.fyi/"
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "levels-fyi": {
      "type": "remote",
      "url": "https://mcp.levels.fyi/",
      "enabled": true
    }
  }
}
{
  "mcpServers": {
    "levels-fyi": {
      "type": "http",
      "url": "https://mcp.levels.fyi/"
    }
  },
  "deeplink": "cursor://anysphere.cursor-deeplink/mcp/install?name=levels-fyi&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vbWNwLmxldmVscy5meWkvIn0%3D"
}
{
  "servers": {
    "levels-fyi": {
      "type": "http",
      "url": "https://mcp.levels.fyi/"
    }
  }
}
{
  "mcpServers": {
    "levels-fyi": {
      "httpUrl": "https://mcp.levels.fyi/"
    }
  }
}
extensions:
  "levels-fyi":
    enabled: true
    name: "levels-fyi"
    type: streamable_http
    uri: "https://mcp.levels.fyi/"
    timeout: 300

Quality 79 / 100good

protocol75 · weight 25
  • warn
    protocol.modernnewest supported version is 2025-11-25; 2026-07-28 not supported, older versions are deprecated until 2027-07-28
  • n/a
    protocol.statelessonly applies to 2026-07-28 servers
  • pass
    protocol.transportstreamable HTTP
  • n/a
    protocol.list_ttlonly applies to 2026-07-28 servers
authorizationn/a · weight 15
  • n/a
    auth.prm, auth.as_metadata, auth.cimdno remote uses OAuth
  • n/a
    auth.secret_in_urlno templated URL
tool hygiene81 · weight 30
  • pass
    tools.descriptionsevery tool has a description
  • warn
    tools.description_length3 tools have descriptions over 1,024 characters, which crowds the context
  • pass
    tools.schemasevery tool has an object input schema
  • pass
    tools.annotations30 of 30 tools declare readOnlyHint or destructiveHint
  • warn
    tools.directory_hints3 of 30 tools declare all four hints; missing: get-salaries-by-company-level (idempotentHint); search-raw-data-points (idempotentHint); get-company (idempotentHint); …
  • warn
    tools.token_costabout 10,901 tokens to load every tool
  • pass
    tools.api_dumptools are not a one-to-one API dump
  • pass
    tools.hidden_textno hidden text in tool names, descriptions, schemas or annotations
stability83 · weight 15
  • warn
    stability.changes19 tool changes in 30 days
  • pass
    stability.rug_pullno tool changed its meaning under the same name
  • pass
    stability.new_linksno tool change named a new site
dependenciesn/a · weight 15
  • n/a
    deps.known_vulns, deps.mcp_sdk_version, deps.resolvableno npm or PyPI package

30 tools, about 10,901 tokens to load them all · tool set 8e06770ab2e4 · tools last changed 2026-11-13 · checked 2026-11-14 07:03 UTC

Tool changes

  • search-jobschangeddescription
    before

    Search open jobs on Levels.fyi, with the pay range each posting states. Returns { jobs, totalMatchingJobs, nextOffset }; each job has its title, company, locations, work arrangement, posting date, pay range, url (the Levels.fyi job page) and applyUrl. Simplify the user's request to role names and terms that appear in job posts. Pass at most one location per call, and only one the user named: resolve it with search-locations, and never use the user's own position. Resolve companies and job families with search-companies and search-job-families. If nothing matches, widen the search (drop a filter or simplify the query) and try once more; if there's still nothing, say so rather than listing unrelated jobs. When the user asks for jobs without saying which role, offer to search from their Levels.fyi profile. If they agree, call get-profile and draft the query from its title, job family and location (locationSlug). Before searching, show the user the values you will use and ask them to confirm or change them: a profile describes the job they have, not necessarily the one they want next. Do not call get-profile without asking. Results come up to 2 jobs per company; limit counts companies, except with a single companySlug, where it counts jobs. A page can hold fewer than limit; keep paging while nextOffset is not null. Put each job's url on its title and keep URLs complete. When the client shows results as cards, do not list them again in text: summarise the patterns instead. Call get-job for a posting's full description. REQUIRED arguments: query. Array-typed arguments (e.g. companySlugs, jobFamilySlugs) must be passed as arrays, even for a single value.

    after · 95% of words in common

    Search open jobs on Levels.fyi, with the pay range each posting states. Returns { jobs, totalMatchingJobs, nextOffset }; each job has its title, company, locations, work arrangement, posting date, pay range, url (the Levels.fyi job page) and applyUrl. Simplify the user's request to role names and terms that appear in job posts. Pass at most one location per call, and only one the user named: resolve it with search-locations, and never use the user's own position. Resolve companies and job families with search-companies and search-job-families. If nothing matches, widen the search (drop a filter or simplify the query) and try once more; if there's still nothing, say so rather than listing unrelated jobs. When the user asks for jobs without saying which role, offer to search from their Levels.fyi profile. If they agree, call get-profile and draft the query from its title, job family and location (locationSlug). Before searching, show the user the values you will use and ask them to confirm or change them: a profile describes the job they have, not necessarily the one they want next. Do not call get-profile without asking. Results come up to 2 jobs per company; limit counts companies, except with a single companySlug, where it counts jobs. A page can hold fewer than limit; keep paging while nextOffset is not null. Put each job's url on its title and keep URLs complete. When the client shows results as cards, do not list them again in text: summarise the patterns instead. Call get-job for a posting's full description. For a signed-in user the result also says whether a resume is on file (profile.resumeStatus). REQUIRED arguments: query. Array-typed arguments (e.g. companySlugs, jobFamilySlugs) must be passed as arrays, even for a single value.

  • add-salary-submissionchangeddescription, input schema
    before

    Add the user's own compensation to Levels.fyi: pay they earn now or a job offer they received. Call this only when the user is evaluating their compensation, asks to add their pay, or says yes when you offer. Adding compensation is how the data stays useful for everyone, the user included. Write a complete submission. Resolve the company, job family and focus area to slugs with the search tools first, and the location with search-locations and type "city". Every number must come from the user's own words or from a document the user shared; ask for each required field that is missing. Gather the details conversationally, a question or two at a time, starting with the company, role and location; do not list every field up front. Write text fields in English. Call once with confirm=false and show the preview to the user, then call again with the same arguments and confirm=true only after a clear yes. If the user isn't signed in, ask for their email before the confirm=true call and send it in email; signing in isn't needed. Amounts are in the submission's own currency. Stock options go in the option fields (totalOptionsGranted for an offer, avgAnnualOptionsGranted for current pay, with the strike and preferred price), not in otherDetails; option prices may use optionPriceCurrency. If the user wants enhanced anonymity, set anonymise=true; the preview shows it. Do not submit: pay for another person, a number you estimated or took from Levels.fyi data, a range from a job posting, a hypothetical or negotiation target, or a test entry. One submission per distinct job or offer per conversation, and at most 5 confirmed submissions per 60s. Never repeat a confirmed submission. A submission is reviewed before it counts in the data. Nobody replies here, and submitting does not end your turn: keep working on the user's task. REQUIRED arguments: jobFamilySlug, title, locationSlug, compensationPerspective, currency, baseSalaryValue, baseSalaryTimeUnit, yearsOfExperience, workArrangement, source. Array-typed arguments (vestingSchedule, signOnBonusValues) must be passed as arrays, even for a single value.

    after · 91% of words in common

    Add the user's own compensation to Levels.fyi: pay they earn now or a job offer they received. Call this only when the user is evaluating their compensation, asks to add their pay, or says yes when you offer. Adding compensation is how the data stays useful for everyone, the user included. Write a complete submission. Resolve the company, job family and focus area to slugs with the search tools first, and the location with search-locations and type "city". Every number must come from the user's own words or from a document the user shared; ask for each required field that is missing. Gather the details conversationally, starting with the company, role and location; do not list every field up front. Split base, yearly stock and bonus as the user stated them; never put a total into baseSalaryValue. Put nothing identifying in otherDetails: no names, managers or team nicknames. Write text fields in English. Call once with confirm=false and show the preview to the user, then call again with the same arguments and confirm=true only after a clear yes. If the user isn't signed in, ask for their email before the confirm=true call and send it in email; signing in isn't needed. Amounts are in the submission's own currency. Stock options go in the option fields (totalOptionsGranted for an offer, avgAnnualOptionsGranted for current pay, with the strike and preferred price), not in otherDetails; option prices may use optionPriceCurrency. If the user wants enhanced anonymity, set anonymise=true; the preview shows it. Do not submit: pay for another person, a number you estimated or took from Levels.fyi data, a range from a job posting, a hypothetical or negotiation target, or a test entry. One submission per distinct job or offer per conversation, and at most 5 confirmed submissions per 60s. Never repeat a confirmed submission. A submission is reviewed before it counts in the data. Nobody replies here, and submitting does not end your turn: keep working on the user's task. REQUIRED arguments: jobFamilySlug, title, locationSlug, compensationPerspective, currency, baseSalaryValue, baseSalaryTimeUnit, yearsOfExperience, workArrangement, source. Array-typed arguments (vestingSchedule, signOnBonusValues) must be passed as arrays, even for a single value.

  • upload-resumechangeddescription
    before

    Add or replace the signed-in user's resume on Levels.fyi. When the user shares their resume or asks for help with a job search, you can offer this once; call it only after the user asks or agrees. Before you call, tell the user Levels.fyi uses it to fill in their profile and match them with jobs and recruiters. Never upload a resume for another person. Accepts a PDF (at most 5 pages) or a DOCX, at most 4.5 MB. Set fileType from the file's actual extension: the upload must be a file of that format, or it cannot be read. Returns two ways to upload: - upload: a presigned S3 upload that expires in 10 minutes. Use it only if you can read the user's file and make HTTP requests yourself, e.g. a terminal agent with the file on disk: send an HTTP POST with multipart/form-data to upload.url, with every entry of upload.fields as form fields, followed by the file in a field named "file". A 201 response means it worked; a larger file is refused. - webUploadUrl: otherwise, give the user this link to upload the file themselves after signing in. Never paste the resume's text or contents anywhere else. The resume is parsed in the background, usually within a couple of minutes. Call get-profile afterwards to check it; until parsing finishes, it can still show the previous resume. REQUIRED arguments: fileType.

    after · 80% of words in common

    Add or replace the signed-in user's resume on Levels.fyi. Offer it when the user shares a resume or asks for help with a job search; call it only after they agree. Before you call, tell the user Levels.fyi reads it to fill their profile; the result's disclosure has the details. Never upload a resume for another person. Accepts a PDF (at most 5 pages) or a DOCX, at most 4.5 MB. Set fileType from the file's actual extension: the upload must be a file of that format, or it cannot be read. Returns up to two ways to upload (a hosted client such as Claude.ai or ChatGPT gets webUploadUrl only): - upload: a presigned S3 upload that expires in 10 minutes. Use it only if you can read the user's file and make HTTP requests yourself, e.g. a terminal agent with the file on disk: send an HTTP POST with multipart/form-data to upload.url, with every entry of upload.fields as form fields, followed by the file in a field named "file". A 201 response means it worked; a larger file is refused. - webUploadUrl: otherwise, give the user this link; they sign in there with the same Levels.fyi account they connected here, upload the file themselves, and come back and say "uploaded". Never paste the resume's text or contents anywhere else. The resume is parsed in the background, usually within a couple of minutes. Call get-profile afterwards to check it; until parsing finishes, it can still show the previous resume. REQUIRED arguments: fileType.

  • get-profilechangeddescription
    before

    Get the signed-in user's own Levels.fyi career profile: current title, company, career level, job family, focus area, location, years of experience, skills, certifications and work history, plus the status of their resume (none, pending while it is being parsed, parsed, or failed) and when it was uploaded. Never returns contact details or the resume file. Use it to personalize answers about the user's own pay, level or career.

    after · 80% of words in common

    Get the signed-in user's own Levels.fyi career profile: current title, company, career level, job family, focus area, location, years of experience, skills, certifications and work history, plus the status of their resume (none, pending while it is being parsed, parsed, or failed) and when it was uploaded. Never returns contact details or the resume file. Needs a Levels.fyi sign-in: call it only when the user asks about their own profile or asks you to use it. For a pay check, ask the user instead.

  • search-locationschangeddescription
    before

    Search locations (countries, metro areas / DMAs, US states and cities) using searchText input (required) and get their Levels.fyi slugs. Use this to resolve a location name (e.g. "San Francisco", "United States") into the slug the benchmark tools require in their locationSlugs filter. Each result includes its location type. Optionally restrict to one type. Results can include cities; a city's parent location (a metro, or the country where there is none) is the slug to pass to the pay tools. Search by place name alone: "Cambridge", not "Cambridge, MA". Several places can share a name: pick the one the user means from context, and ask if unsure. If this returns no matches, fall back to search-all, which searches every entity type and applies a fuzzy fallback. REQUIRED arguments: searchText.

    after · 91% of words in common

    Search locations (countries, metro areas / DMAs, US states and cities) using searchText input (required) and get their Levels.fyi slugs. Use this to resolve a location name (e.g. "San Francisco", "United States") into the slug the benchmark tools require in their locationSlugs filter. Each result includes its location type. Optionally restrict to one type. Results can include cities; pass a city's own slug to the pay tools. When a tool says a city has too little data, pass its parent location instead (a metro, or the country where there is none). Search by place name alone: "Cambridge", not "Cambridge, MA". Several places can share a name: pick the one the user means from context, and ask if unsure. If this returns no matches, fall back to search-all, which searches every entity type and applies a fuzzy fallback. REQUIRED arguments: searchText.

  • get-salaries-by-company-levelchangedinput schema
  • get-salary-percentileschangedinput schema
  • get-available-filterschangeddescription
    before

    List which inputs and filter values the free tier can use. The free inputs are jobFamilySlug, one locationSlug or countrySlug, and companySlug; every result is total compensation from the last 12 months. Dimensions marked free: false (levels, years of experience, focus areas, titles, company size, funding stage, revenue, valuation, work arrangement, offer vs employee) are not accepted by any tool here. Call this before building a query if unsure what a tool accepts.

    after · 91% of words in common

    List which inputs and filter values the free tier can use. The free inputs are jobFamilySlug, a location or country, and a company; every result is total compensation from the last 12 months. Dimensions marked free: false (levels, years of experience, focus areas, titles, company size, funding stage, revenue, valuation, work arrangement, offer vs employee) are not accepted by any tool here. Call this before building a query if unsure what a tool accepts.

  • search-jobschangedinput schema
  • get-vesting-scheduleschangeddescription
    before

    Get equity vesting schedules for one or more companies, decoded into structured data: per-year vesting percent and frequency (cliff/quarterly/monthly), whether there's a 1-year cliff, whether it's backloaded (e.g. Amazon 5/15/40/40) / frontloaded / linear, plus a plain-English description and vesting type (RSU, options). Pass company slugs (use search-companies to resolve names first). REQUIRED arguments: companySlugs. Array-typed arguments (companySlugs) must be passed as arrays, even for a single value.

    after · 86% of words in common

    Get equity vesting schedules for one or more companies, decoded into structured data: per-year vesting percent and frequency (cliff/quarterly/monthly), whether there's a 1-year cliff, whether it's backloaded (e.g. Amazon 5/15/40/40) / frontloaded / linear, plus a plain-English description and vesting type (RSU, options). A company that uses more than one schedule lists the others in alternatives, decoded the same way. Pass company slugs (use search-companies to resolve names first). REQUIRED arguments: companySlugs. Array-typed arguments (companySlugs) must be passed as arrays, even for a single value.

Showing the latest 10. Full history: list_changes · trend: quality JSON.

Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed. How it is computed.

Trust 96 / 100

provenance85
liveness100
freshness100
hygiene100

+repository +dns-namespace +website +reachable +uptime-100% +updated-15d-ago

Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed. How it is computed.

Endpoints

remoteauthreachableuptime 30dp50protocollast ok
https://mcp.levels.fyi/ streamable-httpnoneyes100%280 ms2025-11-252026-11-14

Tools (30)

add-salary-submissionAdd the user's own compensation to Levels.fyi: pay they earn now or a job offer they received. Call this only when the user is evaluating their compensation, asks to add their pay, or says yes when you offer. Adding compensation is how the data stays useful for everyone, the user included. Write a complete submission. Resolve the company, job family and focus area to slugs with the search tools first, and the location with search-locations and type "city". Every number must come from the user's own words or from a document the user shared; ask for each required field that is missing. Gather the details conversationally, starting with the company, role and location; do not list every field up front. Split base, yearly stock and bonus as the user stated them; never put a total into baseSalaryValue. Put nothing identifying in otherDetails: no names, managers or team nicknames. Write text fields in English. Call once with confirm=false and show the preview to the user, then call again with the same arguments and confirm=true only after a clear yes. If the user isn't signed in, ask for their email before the confirm=true call and send it in email; signing in isn't needed. Amounts are in the submission's own currency. Stock options go in the option fields (totalOptionsGranted for an offer, avgAnnualOptionsGranted for current pay, with the strike and preferred price), not in otherDetails; option prices may use optionPriceCurrency. If the user wants enhanced anonymity, set anonymise=true; the preview shows it. Do not submit: pay for another person, a number you estimated or took from Levels.fyi data, a range from a job posting, a hypothetical or negotiation target, or a test entry. One submission per distinct job or offer per conversation, and at most 5 confirmed submissions per 60s. Never repeat a confirmed submission. A submission is reviewed before it counts in the data. Nobody replies here, and submitting does not end your turn: keep working on the user's task. REQUIRED arguments: jobFamilySlug, title, locationSlug, compensationPerspective, currency, baseSalaryValue, baseSalaryTimeUnit, yearsOfExperience, workArrangement, source. Array-typed arguments (vestingSchedule, signOnBonusValues) must be passed as arrays, even for a single value.
get-available-filtersread-onlyList which inputs and filter values the free tier can use. The free inputs are jobFamilySlug, a location or country, and a company; every result is total compensation from the last 12 months. Dimensions marked free: false (levels, years of experience, focus areas, titles, company size, funding stage, revenue, valuation, work arrangement, offer vs employee) are not accepted by any tool here. Call this before building a query if unsure what a tool accepts.
get-companyread-onlyLook up a single company by its slug (preferred) or name, returning its full profile: name, slug, employee count, headquarters, founding year, ticker, company type, and estimated valuation. Use search-companies first if you only have a name and need the exact slug. REQUIRED arguments: companyNameOrSlug.
get-company-groupread-onlyResolve a named company group (e.g. "FAANG", "AI", "Fintech") into its list of company slugs, for use in the benchmark tools' companySlugs filter. Matching is case- and separator-insensitive. Omit "group" (or pass an unknown one) to get the list of available group names. Available groups: FAANG, AUTOMOTIVE, SPACE, SOCIAL_MEDIA, AI, AI_PLUS, FINTECH, FAANG_PLUS, HEDGE_FUNDS, SEMICONDUCTOR, BIOTECH, WEB3, TRADING_FIRMS, DEFENSE.
get-company-levelsread-onlyGet the INTERNAL level names of up to 5 companies for a job family, each ordered by seniority (e.g. Google L3/L4/L5, Meta E3/E4/E5, Stripe IC1/IC2/IC3), with any alternate names per level. Pass every company you are comparing in one call. Use this FIRST when a question names a specific level at a specific company (e.g. "what does a Meta E5 make"), then call get-salaries-by-company-level. A company with no known level structure comes back with an empty list and an explanation. Free limit: Full level data for Amazon, Google, Microsoft, Meta and Apple. Other companies: level names only. Pass canonical slugs (jobFamilySlug, locationSlug(s), countrySlug, companySlug(s)): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: companySlugs, jobFamilySlug. Array-typed arguments (companySlugs) must be passed as arrays, even for a single value.
get-domain-knowledgeread-onlyGet reference knowledge about the Levels.fyi data model, by topic: compensation, levels, vesting, company_groups, locations, job_families, enums, negotiation_coaching. Covers compensation concepts, the standard level ladder, vesting patterns, company groups, and how to resolve locations / job families / enums. negotiation_coaching describes Levels.fyi's offer negotiation coaching: use it only when the user asks for help negotiating. Omit "topic" to get every data-model topic.
get-jobread-onlyGet one job posting by its jobId from search-jobs: the full description as text, locations, work arrangement, employment type, the pay range the posting states, Levels.fyi's estimated total compensation by level for the role (USD; matchesThisPosting marks the level the posting is for), the company, the url of the Levels.fyi job page, and applyUrl to apply on the employer's site. REQUIRED arguments: jobId.
get-job-familyread-onlyLook up a job family by its slug and get its focus areas (specializations) with their titles, plus untagged titles. Use this to discover the focus tags and titles available within a role (e.g. to find focusTagSlugs for the benchmark tools). Use search-job-families first if you only have a name. REQUIRED arguments: jobFamilySlug.
get-locationread-onlyLook up one or more location slugs and get their details: name, type (country / metro-DMA / state), parent country, currency, and coordinates. Use search-locations first if you only have a name. Errors if any slug is invalid. REQUIRED arguments: locationSlugs. Array-typed arguments (locationSlugs) must be passed as arrays, even for a single value.
get-location-statsread-onlyGet the top-paying locations for a job family within one country, ranked by median (p50) yearly total compensation in USD from submissions in the last 2 years. Returns up to 10 locations with their median. Use it to compare where a role pays most inside a country. Its window differs from get-salary-percentiles (the last 12 months), so the same location can show a different median: quote get-salary-percentiles for one location's pay. Free limit: Median (p50) total compensation for the top locations in one country. No company or level filter. Pass canonical slugs (jobFamilySlug, locationSlug(s), countrySlug, companySlug(s)): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug, countrySlug.
get-metric-explanationread-onlyExplain a Levels.fyi compensation metric or concept (e.g. "TC" / total_compensation, base salary, total_cash, tc_first_year, vesting, sign_on, target_bonus) — its meaning, formula, and caveats. Pass an unknown metric to get the list of known ones. REQUIRED arguments: metric.
get-permissionsread-onlyConnectivity check for the Levels.fyi MCP server. Returns "pong", echoes an optional message, and lists what each tool returns and the rate limits. Call it when a result looks smaller than expected, instead of retrying.
get-profileread-onlyGet the signed-in user's own Levels.fyi career profile: current title, company, career level, job family, focus area, location, years of experience, skills, certifications and work history, plus the status of their resume (none, pending while it is being parsed, parsed, or failed) and when it was uploaded. Never returns contact details or the resume file. Needs a Levels.fyi sign-in: call it only when the user asks about their own profile or asks you to use it. For a pay check, ask the user instead.
get-salaries-by-company-levelread-onlyGet median (p50) yearly total compensation in USD for each internal level (e.g. Google L3/L4/L5, Meta E3/E4/E5) of up to 5 companies, for a job family in one location, from submissions in the last 12 months. Pass every company you are comparing in one call. Call get-company-levels first to learn each company's level names. A company must have a known level structure. Free limit: Total compensation per level for up to 5 companies per call, in one job family and location. No percentiles or counts. Pass canonical slugs (jobFamilySlug, locationSlug(s), countrySlug, companySlug(s)): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug, locationSlug, companySlugs. Array-typed arguments (companySlugs) must be passed as arrays, even for a single value.
get-salary-percentilesread-onlyGet the median (p50) yearly total compensation in USD for a job family in each of up to 5 locations, across all levels, from submissions in the last 12 months. Pass every location you are comparing in one call. This is the figure to quote for a location's pay; get-location-stats ranks locations over a longer window, so its medians can differ. For one company's pay by level, use get-salaries-by-company-level instead. Free limit: Median (p50) total compensation for one job family in up to 5 locations per call. No other percentiles, sample size, level split, or company, focus area or funding filters. Pass canonical slugs (jobFamilySlug, locationSlug(s), countrySlug, companySlug(s)): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug, locationSlugs. Array-typed arguments (locationSlugs) must be passed as arrays, even for a single value.
get-salary-submission-by-idread-onlyGet a single salary submission by its UUID: company, title, level, location, years of experience, offer date, base salary and total compensation. Every amount is in USD; originalCurrency is only the currency the salary was submitted in. Use the uuid from a search-raw-data-points row. Free limit: Base salary and total compensation only, in USD. REQUIRED arguments: uuid.
get-vesting-schedulesread-onlyGet equity vesting schedules for one or more companies, decoded into structured data: per-year vesting percent and frequency (cliff/quarterly/monthly), whether there's a 1-year cliff, whether it's backloaded (e.g. Amazon 5/15/40/40) / frontloaded / linear, plus a plain-English description and vesting type (RSU, options). A company that uses more than one schedule lists the others in alternatives, decoded the same way. Pass company slugs (use search-companies to resolve names first). REQUIRED arguments: companySlugs. Array-typed arguments (companySlugs) must be passed as arrays, even for a single value.
search-allread-onlySearch across ALL entity types at once (companies, job families, job titles, focus tags, locations, and industries) by passing searchText (required) and get a single ranked list, each result tagged with its type and slug/id. Use this when you don't yet know which track an entity belongs to, or to disambiguate an ambiguous name; once the type is known, prefer the type-specific tools (search-companies, search-job-titles, search-locations, search-job-families, search-focus-areas) for tighter results. If a match is found under a DIFFERENT entity type than the user expected, do not silently substitute it — surface that mismatch and confirm with the user before proceeding. REQUIRED arguments: searchText.
search-companiesread-onlySearch companies using searchText input (required) and get their Levels.fyi slugs. Use this FIRST to resolve a company name (e.g. "Google", "Meta") into the slug that the benchmark tools require in their companySlugs filter. Also matches former names and aliases, so a renamed company is found under its old name. Returns an array of { name, slug } matches. If this returns no matches, fall back to search-all, which searches every entity type and applies a fuzzy fallback. REQUIRED arguments: searchText.
search-companies-by-vestingread-onlyFind companies by their equity vesting characteristics: vesting/equity type (e.g. "RSU"), vesting shape (frontloaded, backloaded like Amazon, linear, one/two/three_year, or extended 5+ years), total years, and whether there's a 1-year cliff. Returns matching companies (most data-rich first) with their decoded vesting schedules.
search-focus-areasread-onlySearch focus tags (specializations within a role, e.g. "mobile-ios-android", "ml-ai", "devops", "security") using searchText input (required) and get their slugs. Use this to resolve a specialization into the focusTagSlugs the benchmark tools accept. Returns matching focus tags with their slugs. If this returns no matches, fall back to search-all, which searches every entity type and applies a fuzzy fallback. REQUIRED arguments: searchText.
search-job-familiesread-onlySearch job families / roles using searchText input (required) and get their Levels.fyi slugs. Use this to resolve a role name (e.g. "software engineer", "product manager", "data scientist") into the jobFamilySlug the benchmark tools require. Returns matching job families with their slugs. A specific title (e.g. "UX designer") is not a job family: if this returns no matches, use search-role-terms, which returns the title with its job family slug, then search-all. REQUIRED arguments: searchText.
search-jobsread-onlySearch open jobs on Levels.fyi, with the pay range each posting states. Returns { jobs, totalMatchingJobs, nextOffset }; each job has its title, company, locations, work arrangement, posting date, pay range, url (the Levels.fyi job page) and applyUrl. Simplify the user's request to role names and terms that appear in job posts. Pass at most one location per call, and only one the user named: resolve it with search-locations, and never use the user's own position. Resolve companies and job families with search-companies and search-job-families. If nothing matches, widen the search (drop a filter or simplify the query) and try once more; if there's still nothing, say so rather than listing unrelated jobs. When the user asks for jobs without saying which role, offer to search from their Levels.fyi profile. If they agree, call get-profile and draft the query from its title, job family and location (locationSlug). Before searching, show the user the values you will use and ask them to confirm or change them: a profile describes the job they have, not necessarily the one they want next. Do not call get-profile without asking. Results come up to 2 jobs per company; limit counts companies, except with a single companySlug, where it counts jobs. A page can hold fewer than limit; keep paging while nextOffset is not null. Put each job's url on its title and keep URLs complete. When the client shows results as cards, do not list them again in text: summarise the patterns instead. Call get-job for a posting's full description. For a signed-in user the result also says whether a resume is on file (profile.resumeStatus). REQUIRED arguments: query. Array-typed arguments (e.g. companySlugs, jobFamilySlugs) must be passed as arrays, even for a single value.
search-job-titlesread-onlySearch specific job titles by name. Each result includes its id (the titleId the benchmark tools accept in the jobTitleIds filter) and its parent job family, so use this to narrow a benchmark query to an exact title (e.g. "Forward Deployed Software Engineer") within a job family. Set excludeFamilyLikeTitles to drop titles that are effectively just the job family. If this returns no matches, fall back to search-all, which searches every entity type and applies a fuzzy fallback. REQUIRED arguments: searchText.
search-locationsread-onlySearch locations (countries, metro areas / DMAs, US states and cities) using searchText input (required) and get their Levels.fyi slugs. Use this to resolve a location name (e.g. "San Francisco", "United States") into the slug the benchmark tools require in their locationSlugs filter. Each result includes its location type. Optionally restrict to one type. Results can include cities; pass a city's own slug to the pay tools. When a tool says a city has too little data, pass its parent location instead (a metro, or the country where there is none). Search by place name alone: "Cambridge", not "Cambridge, MA". Several places can share a name: pick the one the user means from context, and ask if unsure. If this returns no matches, fall back to search-all, which searches every entity type and applies a fuzzy fallback. REQUIRED arguments: searchText.
search-raw-data-pointsread-onlyGet the newest individual salary submissions for a job family, optionally narrowed to one location and/or one company. Each row has the company, title, level, location, years of experience, base salary and total compensation. Every amount is in USD; originalCurrency is only the currency the salary was submitted in. Returns { total, rows }; total counts matches up to 250, and only the newest rows are returned. Free limit: The 3 newest submissions, with base salary and total compensation only. No text search, level, years-of-experience, sort or paging. Pass canonical slugs (jobFamilySlug, locationSlug(s), countrySlug, companySlug(s)): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug.
search-role-termsread-onlyResolve a role term by searching job families, job titles, and focus tags TOGETHER in one call. Use this when you're unsure whether a term (e.g. "ML engineer", "mobile", "SRE") is a job family, a specific title, or a specialization — each result is tagged with its type and slug so you can feed the right one to the benchmark tools. REQUIRED arguments: searchText.
search-top-companiesread-onlyGet the top-paying companies for a job family within one country, ranked by median (p50) yearly total compensation in USD. Returns up to 10 companies with their median. Companies with too few submissions are left out. Free limit: Median (p50) total compensation for the top companies in one country. No sample size, level or years-of-experience filter. Pass canonical slugs (jobFamilySlug, locationSlug(s), countrySlug, companySlug(s)): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug, countrySlug.
send-feedbackReport wrong or missing Levels.fyi data, or a missing feature, to the Levels.fyi team. Call it only after telling the user what you will send and getting their agreement. Never include personal details; contactEmail only when the user wants a reply and gives it. Returns { received, id }. REQUIRED arguments: kind, message.
upload-resumedestructiveAdd or replace the signed-in user's resume on Levels.fyi. Offer it when the user shares a resume or asks for help with a job search; call it only after they agree. Before you call, tell the user Levels.fyi reads it to fill their profile; the result's disclosure has the details. Never upload a resume for another person. Accepts a PDF (at most 5 pages) or a DOCX, at most 4.5 MB. Set fileType from the file's actual extension: the upload must be a file of that format, or it cannot be read. Returns up to two ways to upload (a hosted client such as Claude.ai or ChatGPT gets webUploadUrl only): - upload: a presigned S3 upload that expires in 10 minutes. Use it only if you can read the user's file and make HTTP requests yourself, e.g. a terminal agent with the file on disk: send an HTTP POST with multipart/form-data to upload.url, with every entry of upload.fields as form fields, followed by the file in a field named "file". A 201 response means it worked; a larger file is refused. - webUploadUrl: otherwise, give the user this link; they sign in there with the same Levels.fyi account they connected here, upload the file themselves, and come back and say "uploaded". Never paste the resume's text or contents anywhere else. The resume is parsed in the background, usually within a couple of minutes. Call get-profile afterwards to check it; until parsing finishes, it can still show the previous resume. REQUIRED arguments: fileType.

Schemas: list_tools.

Similar MCP servers

Official server.json
{
  "name": "fyi.levels/levels-fyi",
  "icons": [
    {
      "src": "https://raw.githubusercontent.com/Levels-fyi/levels-fyi-plugin/main/plugin/assets/icon.png",
      "sizes": [
        "512x512"
      ],
      "mimeType": "image/png"
    }
  ],
  "title": "Levels.fyi",
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
  "remotes": [
    {
      "url": "https://mcp.levels.fyi/",
      "type": "streamable-http"
    }
  ],
  "version": "1.0.0",
  "repository": {
    "url": "https://github.com/Levels-fyi/levels-fyi-plugin",
    "source": "github"
  },
  "websiteUrl": "https://www.levels.fyi",
  "description": "Salary, level and job data from 1M+ pay reports. Benchmark pay, evaluate offers, search open jobs."
}