Koskamo

Levels.fyi MCP

An official MCP server provided by Levels.fyi that allows AI agents to access live, crowd-sourced compensation data, benchmark job offers against company levels and locations, search for open jobs with pay transparency, and submit new salary data to keep the database current.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

Levels.fyi MCP logo, by Levels.fyiVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-01Operated by Levels.fyi
MCP endpoint (Streamable HTTP)
https://mcp.levels.fyi/
Vendor documentation

What you can do with it

  • Benchmark a job offer against company-specific levels and local market rates
  • Search for open job roles by title, city, or company with pay data
  • Check current salary expectations for specific roles and career levels
  • Submit anonymous salary data to help maintain the database

Source and maintenance

Vendor evidenceRegistry 1.0.0
Maintenance checked

Protocol and capabilities

Server
levels-mcp 0.1.0
MCP protocol
2025-06-18
Tools
30 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
3 advertised
Server-advertised capabilities
{
  "tools": {},
  "prompts": {}
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

This server exposes Levels.fyi compensation data on the free tier. All compensation amounts are in USD. Inputs are CANONICAL SLUGS, never free text. Resolve names first, then pass the returned slugs: - companies → search-companies / get-company - locations → search-locations / get-location - job families → search-job-families / get-job-family - a company's own level names → get-company-levels Each data tool takes one job family and, where asked, ONE location or country. There is no level, years-of-experience, focus area or title filter: do not present a result as level- or title-specific unless it comes from get-salaries-by-company-level, which is per company level. For "what does a Meta E5 make", call get-company-levels for the company's real level names, then get-salaries-by-company-level. Data tools end their result with an explanation (where the data comes from) and nextSteps (follow-ups the user might want). Use the explanation when saying where a figure comes from, and offer nextSteps as options; don't run them unasked. Levels.fyi data exists only because people share their own pay. When the user has shared their own salary or an offer, ask once per conversation, after answering, in this form: "These numbers came from people sharing their own pay. Want to add this offer for the next person, without your name?" Say "your pay" instead of "this offer" when they shared their current pay. Don't ask during a plain lookup about other people's pay, and don't ask again after a no. Label where every figure comes from. A number a Levels.fyi tool returned is "Levels.fyi data": say which job family, location and window it covers. A number from anywhere else, such as a web search, your general knowledge or an assumption the tools didn't give, is an "estimate": label it as one and say what it rests on. Never present an estimate as Levels.fyi data. When the user wants to add their own salary or offer, use add-salary-submission: preview first, then confirm only after the user agrees. When a question is about the user's own career or pay, get-profile returns their Levels.fyi profile; upload-resume adds or replaces their resume. When the user shares their resume or asks for help with a job search, offer once to add their resume to their Levels.fyi profile, and don't ask again after a no. For open jobs, use search-jobs, then get-job for a posting's full description. When the search values come from get-profile, show them to the user and ask them to confirm or change them before calling search-jobs. When the user says Levels.fyi data is wrong or missing, or a search finds nothing for a company, role or location they care about, offer to report it with send-feedback. Say what you'll send and send it only after they agree. Never include personal details, except an email in contactEmail when the user wants a reply and gives it. When the user asks for help negotiating an offer, get-domain-knowledge with topic negotiation_coaching describes Levels.fyi's negotiation coaching service, if the user wants that kind of help. Results are deliberately limited on the free tier. When a result looks smaller than expected, call get-permissions for the exact limits instead of retrying. Tool calls are rate limited, so plan multi-part requests to fit: resolve each slug once and reuse it. When a call is rate limited, wait the retryAfterSeconds it gives before calling again. When the user is an employer (a recruiter, or a compensation or HR team) and needs more than the free tier, say the free plan doesn't include it and name the matching product from paidPlans in get-permissions, with its link. Don't guess at Levels.fyi's paid products, quote prices or urge an upgrade.

Tools

30 advertised

get-permissions
Advertised by the server during the last successful anonymous check.
Advertised read-only

Connectivity 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-permissions input schema
{
  "type": "object",
  "properties": {
    "message": {
      "type": "string",
      "description": "Optional text to echo back in the response."
    }
  }
}
get-permissions annotations
{
  "title": "Check Connection & Limits",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-salary-percentiles
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get the median (p50) yearly total compensation in USD for a job family in one location, across all levels, from submissions in the last 12 months. This is the figure to quote for one 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 one location. No other percentiles, sample size, level split, or company, focus area or funding filters. Pass canonical slugs (jobFamilySlug, locationSlug, countrySlug, companySlug): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug, locationSlug.

get-salary-percentiles input schema
{
  "type": "object",
  "required": [
    "jobFamilySlug",
    "locationSlug"
  ],
  "properties": {
    "locationSlug": {
      "type": "string",
      "example": "san-francisco-bay-area",
      "description": "One location slug (metro, state, country or region; not a city), e.g. \"san-francisco-bay-area\". Resolve it with search-locations first; for a city, use its parent metro."
    },
    "jobFamilySlug": {
      "type": "string",
      "example": "software-engineer",
      "description": "A Levels.fyi job family slug, e.g. \"software-engineer\", \"product-manager\", \"data-scientist\"."
    }
  }
}
get-salary-percentiles annotations
{
  "title": "Get Median Total Compensation",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-salaries-by-company-level
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get median (p50) yearly total compensation in USD for each of ONE company's internal levels (e.g. Google L3/L4/L5, Meta E3/E4/E5) for a job family in one location, from submissions in the last 12 months. Call get-company-levels first to learn the company's level names. The company must have a known level structure. Free limit: Total compensation per level for one company and job family. No percentiles or counts. Pass canonical slugs (jobFamilySlug, locationSlug, countrySlug, companySlug): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug, locationSlug, companySlug.

get-salaries-by-company-level input schema
{
  "type": "object",
  "required": [
    "jobFamilySlug",
    "locationSlug",
    "companySlug"
  ],
  "properties": {
    "companySlug": {
      "type": "string",
      "example": "google",
      "description": "A company slug, e.g. \"google\". Resolve it with search-companies first."
    },
    "locationSlug": {
      "type": "string",
      "example": "san-francisco-bay-area",
      "description": "One location slug (metro, state, country or region; not a city), e.g. \"san-francisco-bay-area\". Resolve it with search-locations first; for a city, use its parent metro."
    },
    "jobFamilySlug": {
      "type": "string",
      "example": "software-engineer",
      "description": "A Levels.fyi job family slug, e.g. \"software-engineer\", \"product-manager\", \"data-scientist\"."
    }
  }
}
get-salaries-by-company-level annotations
{
  "title": "Get Pay by Company Level",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-raw-data-points
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get 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, countrySlug, companySlug): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug.

search-raw-data-points input schema
{
  "type": "object",
  "required": [
    "jobFamilySlug"
  ],
  "properties": {
    "companySlug": {
      "type": "string",
      "example": "google",
      "description": "A company slug, e.g. \"google\". Resolve it with search-companies first."
    },
    "locationSlug": {
      "type": "string",
      "example": "san-francisco-bay-area",
      "description": "One location slug (city, metro, state, country or region), e.g. \"san-francisco-bay-area\". Resolve it with search-locations first."
    },
    "jobFamilySlug": {
      "type": "string",
      "example": "software-engineer",
      "description": "A Levels.fyi job family slug, e.g. \"software-engineer\", \"product-manager\", \"data-scientist\"."
    }
  }
}
search-raw-data-points annotations
{
  "title": "Search Recent Salary Submissions",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-company
Advertised by the server during the last successful anonymous check.
Advertised read-only

Look 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 input schema
{
  "type": "object",
  "required": [
    "companyNameOrSlug"
  ],
  "properties": {
    "companyNameOrSlug": {
      "type": "string",
      "description": "Name or slug of a company"
    }
  }
}
get-company annotations
{
  "title": "Get Company Profile",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-company-levels
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get a company's INTERNAL level names for a job family, ordered by seniority (e.g. Google L3/L4/L5, Meta E3/E4/E5, Stripe IC1/IC2/IC3), with any alternate names per level. 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. Returns an empty list with an explanation when the company has no known level structure. Free limit: Full level data for Amazon, Google, Microsoft, Meta and Apple. Other companies: level names only. Pass canonical slugs (jobFamilySlug, locationSlug, countrySlug, companySlug): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: companySlug, jobFamilySlug.

get-company-levels input schema
{
  "type": "object",
  "required": [
    "companySlug",
    "jobFamilySlug"
  ],
  "properties": {
    "companySlug": {
      "type": "string",
      "example": "google",
      "description": "The company (slug) whose internal level structure to list, e.g. \"google\", \"meta\", \"stripe\". Resolve a company name to its slug via search-companies first."
    },
    "jobFamilySlug": {
      "type": "string",
      "example": "software-engineer",
      "description": "A Levels.fyi job family slug, e.g. \"software-engineer\", \"product-manager\", \"data-scientist\"."
    }
  }
}
get-company-levels annotations
{
  "title": "Get Company Levels",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-company-group
Advertised by the server during the last successful anonymous check.
Advertised read-only

Resolve 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-group input schema
{
  "type": "object",
  "properties": {
    "group": {
      "type": "string",
      "example": "FAANG",
      "description": "Company group name to resolve (e.g. \"FAANG\", \"AI\", \"Fintech\"). Case/separator-insensitive. Omit to list all available groups."
    }
  }
}
get-company-group annotations
{
  "title": "Resolve Company Group",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-companies
Advertised by the server during the last successful anonymous check.
Advertised read-only

Search 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 input schema
{
  "type": "object",
  "required": [
    "searchText"
  ],
  "properties": {
    "limit": {
      "type": "number",
      "example": 10,
      "description": "Max results to return (1–15)."
    },
    "searchText": {
      "type": "string",
      "example": "Google",
      "description": "Company name to search for, current or former (e.g. \"Google\", \"Pure Storage\")."
    }
  }
}
search-companies annotations
{
  "title": "Search Companies",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-companies-by-vesting
Advertised by the server during the last successful anonymous check.
Advertised read-only

Find 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-companies-by-vesting input schema
{
  "type": "object",
  "properties": {
    "limit": {
      "type": "number",
      "example": 20,
      "description": "Max companies to return (1–50)."
    },
    "hasCliff": {
      "type": "boolean",
      "description": "Filter by whether the schedule has a 1-year cliff."
    },
    "maxYears": {
      "type": "number",
      "example": 4,
      "description": "Maximum total vesting years."
    },
    "minYears": {
      "type": "number",
      "example": 4,
      "description": "Minimum total vesting years."
    },
    "vestingType": {
      "type": "string",
      "example": "RSU",
      "description": "Filter by vesting/equity type substring, e.g. \"RSU\", \"Stock Options\"."
    },
    "vestingPattern": {
      "enum": [
        "frontloaded",
        "backloaded",
        "linear",
        "one_year",
        "two_year",
        "three_year",
        "extended"
      ],
      "type": "string",
      "example": "backloaded",
      "description": "Filter by vesting shape: frontloaded, backloaded (e.g. Amazon), linear (equal each year), one_year / two_year / three_year (total length), or extended (5+ years)."
    }
  }
}
search-companies-by-vesting annotations
{
  "title": "Search Companies by Vesting",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-top-companies
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get 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, countrySlug, companySlug): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug, countrySlug.

search-top-companies input schema
{
  "type": "object",
  "required": [
    "jobFamilySlug",
    "countrySlug"
  ],
  "properties": {
    "countrySlug": {
      "type": "string",
      "example": "united-states",
      "description": "A COUNTRY location slug, e.g. \"united-states\", \"india\", \"united-kingdom\". Resolve it with search-locations first; cities, metros and states are rejected."
    },
    "jobFamilySlug": {
      "type": "string",
      "example": "software-engineer",
      "description": "A Levels.fyi job family slug, e.g. \"software-engineer\", \"product-manager\", \"data-scientist\"."
    }
  }
}
search-top-companies annotations
{
  "title": "Top-Paying Companies",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-location
Advertised by the server during the last successful anonymous check.
Advertised read-only

Look 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 input schema
{
  "type": "object",
  "required": [
    "locationSlugs"
  ],
  "properties": {
    "locationSlugs": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "example": "['san-francisco-bay-area', 'new-york']",
      "description": "Location slug for the locations"
    }
  }
}
get-location annotations
{
  "title": "Get Location Details",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-location-stats
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get 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, countrySlug, companySlug): resolve names first with search-job-families, search-locations and search-companies; never guess a slug. REQUIRED arguments: jobFamilySlug, countrySlug.

get-location-stats input schema
{
  "type": "object",
  "required": [
    "jobFamilySlug",
    "countrySlug"
  ],
  "properties": {
    "countrySlug": {
      "type": "string",
      "example": "united-states",
      "description": "A COUNTRY location slug, e.g. \"united-states\", \"india\", \"united-kingdom\". Resolve it with search-locations first; cities, metros and states are rejected."
    },
    "jobFamilySlug": {
      "type": "string",
      "example": "software-engineer",
      "description": "A Levels.fyi job family slug, e.g. \"software-engineer\", \"product-manager\", \"data-scientist\"."
    }
  }
}
get-location-stats annotations
{
  "title": "Top-Paying Locations",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-locations
Advertised by the server during the last successful anonymous check.
Advertised read-only

Search locations (countries, metro areas / DMAs, and US states) 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. Without a type, a name that matches no country, metro or state is searched as a city: each city comes with its parent location (a metro, or the country where there is none), which is the slug to pass to the pay tools. 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-locations input schema
{
  "type": "object",
  "required": [
    "searchText"
  ],
  "properties": {
    "type": {
      "enum": [
        "country",
        "dma",
        "state",
        "city"
      ],
      "type": "string",
      "example": "dma",
      "description": "Restrict results to one location type. Omit to search countries, metros (DMAs), and states together. Cities are only returned with type \"city\"."
    },
    "limit": {
      "type": "number",
      "example": 10,
      "description": "Max results to return (1–15)."
    },
    "searchText": {
      "type": "string",
      "example": "San Francisco",
      "description": "Location name to search for (country, metro area / DMA, or US state)."
    }
  }
}
search-locations annotations
{
  "title": "Search Locations",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-job-family
Advertised by the server during the last successful anonymous check.
Advertised read-only

Look 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-job-family input schema
{
  "type": "object",
  "required": [
    "jobFamilySlug"
  ],
  "properties": {
    "jobFamilySlug": {
      "type": "string",
      "example": "software-engineer",
      "description": "A Levels.fyi job family slug, e.g. \"software-engineer\", \"product-manager\", \"data-scientist\"."
    }
  }
}
get-job-family annotations
{
  "title": "Get Job Family",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-job-families
Advertised by the server during the last successful anonymous check.
Advertised read-only

Search 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-job-families input schema
{
  "type": "object",
  "required": [
    "searchText"
  ],
  "properties": {
    "limit": {
      "type": "number",
      "example": 10,
      "description": "Max results to return (1–15)."
    },
    "searchText": {
      "type": "string",
      "example": "software engineer",
      "description": "Job family / role name to search for (e.g. \"software engineer\", \"product\")."
    }
  }
}
search-job-families annotations
{
  "title": "Search Job Families",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-job-titles
Advertised by the server during the last successful anonymous check.
Advertised read-only

Search 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-job-titles input schema
{
  "type": "object",
  "required": [
    "searchText"
  ],
  "properties": {
    "limit": {
      "type": "number",
      "example": 10,
      "description": "Max results to return (1–15)."
    },
    "searchText": {
      "type": "string",
      "example": "forward deployed engineer",
      "description": "Job title to search for (e.g. \"forward deployed engineer\", \"iOS engineer\")."
    },
    "excludeFamilyLikeTitles": {
      "type": "boolean",
      "description": "When true, exclude titles that are effectively the same as their job family."
    }
  }
}
search-job-titles annotations
{
  "title": "Search Job Titles",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-focus-areas
Advertised by the server during the last successful anonymous check.
Advertised read-only

Search 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-focus-areas input schema
{
  "type": "object",
  "required": [
    "searchText"
  ],
  "properties": {
    "limit": {
      "type": "number",
      "example": 10,
      "description": "Max results to return (1–15)."
    },
    "searchText": {
      "type": "string",
      "example": "machine learning",
      "description": "Focus tag / specialization name to search for (e.g. \"mobile\", \"ml\", \"devops\")."
    }
  }
}
search-focus-areas annotations
{
  "title": "Search Focus Areas",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-role-terms
Advertised by the server during the last successful anonymous check.
Advertised read-only

Resolve 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-role-terms input schema
{
  "type": "object",
  "required": [
    "searchText"
  ],
  "properties": {
    "limit": {
      "type": "number",
      "example": 10,
      "description": "Max results to return (1–15)."
    },
    "searchText": {
      "type": "string",
      "example": "machine learning engineer",
      "description": "Role term to resolve across job families, titles, and focus tags."
    }
  }
}
search-role-terms annotations
{
  "title": "Search Role Terms",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-profile
Advertised by the server during the last successful anonymous check.
Advertised read-only

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.

get-profile input schema
{
  "type": "object",
  "properties": {}
}
get-profile annotations
{
  "title": "Get My Profile",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
upload-resume
Advertised by the server during the last successful anonymous check.
Advertised destructive

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.

upload-resume input schema
{
  "type": "object",
  "required": [
    "fileType"
  ],
  "properties": {
    "fileType": {
      "enum": [
        "pdf",
        "docx"
      ],
      "type": "string",
      "description": "The resume file's format: \"pdf\" or \"docx\"."
    }
  }
}
upload-resume annotations
{
  "title": "Upload My Resume",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": true
}
add-salary-submission
Advertised by the server during the last successful anonymous check.
Open-world

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; if a call fails, retry with the same submissionId. 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.

add-salary-submission input schema
{
  "type": "object",
  "required": [
    "jobFamilySlug",
    "title",
    "locationSlug",
    "compensationPerspective",
    "currency",
    "baseSalaryValue",
    "baseSalaryTimeUnit",
    "yearsOfExperience",
    "workArrangement",
    "source"
  ],
  "properties": {
    "team": {
      "type": "string",
      "description": "The team or organization within the company, e.g. \"Google Cloud\". In English."
    },
    "email": {
      "type": "string",
      "format": "email",
      "description": "Required when the user isn't signed in. Used to verify the submission; never shown with it."
    },
    "level": {
      "type": "string",
      "example": "L5",
      "description": "The company's own level name from get-company-levels, e.g. \"L5\". Required for companies with a level ladder; the error says so."
    },
    "title": {
      "type": "string",
      "example": "Software Engineer",
      "description": "The job title as the user states it, e.g. \"Software Engineer\". In English."
    },
    "source": {
      "enum": [
        "user_stated",
        "user_document"
      ],
      "type": "string",
      "description": "\"user_stated\" when the user typed the numbers, \"user_document\" when they come from an offer letter or pay stub the user shared."
    },
    "confirm": {
      "type": "boolean",
      "default": false,
      "description": "false (default) returns a preview and writes nothing. true writes the entry: only after the user said yes to the preview."
    },
    "currency": {
      "enum": [
        "USD",
        "EUR",
        "GBP",
        "JPY",
        "AUD",
        "CAD",
        "CHF",
        "CNY",
        "HKD",
        "NZD",
        "SEK",
        "SGD",
        "INR",
        "RUB",
        "AED",
        "ILS",
        "SAR",
        "CZK",
        "MXN",
        "ZAR",
        "THB",
        "BRL",
        "TWD",
        "PKR",
        "PLN",
        "AFN",
        "ALL",
        "AMD",
        "ANG",
        "AOA",
        "ARS",
        "AWG",
        "AZN",
        "BAM",
        "BBD",
        "BDT",
        "BGN",
        "BHD",
        "BIF",
        "BMD",
        "BND",
        "BOB",
        "BSD",
        "BTC",
        "BTN",
        "BWP",
        "BYN",
        "BZD",
        "CDF",
        "CLF",
        "CLP",
        "CNH",
        "COP",
        "CRC",
        "CUP",
        "CVE",
        "DJF",
        "DKK",
        "DOP",
        "DZD",
        "EGP",
        "ERN",
        "ETB",
        "FJD",
        "FKP",
        "GEL",
        "GGP",
        "GHS",
        "GIP",
        "GMD",
        "GNF",
        "GTQ",
        "GYD",
        "HNL",
        "HTG",
        "HUF",
        "IDR",
        "IMP",
        "IQD",
        "IRR",
        "ISK",
        "JEP",
        "JMD",
        "JOD",
        "KES",
        "KGS",
        "KHR",
        "KMF",
        "KPW",
        "KRW",
        "KWD",
        "KYD",
        "KZT",
        "LAK",
        "LBP",
        "LKR",
        "LRD",
        "LSL",
        "LYD",
        "MAD",
        "MDL",
        "MGA",
        "MKD",
        "MMK",
        "MNT",
        "MOP",
        "MRU",
        "MUR",
        "MVR",
        "MWK",
        "MYR",
        "MZN",
        "NAD",
        "NGN",
        "NIO",
        "NOK",
        "NPR",
        "OMR",
        "PAB",
        "PEN",
        "PGK",
        "PHP",
        "PYG",
        "QAR",
        "RON",
        "RSD",
        "RWF",
        "SBD",
        "SCR",
        "SDG",
        "SHP",
        "SLE",
        "SOS",
        "SRD",
        "STD",
        "SVC",
        "SYP",
        "SZL",
        "TJS",
        "TMT",
        "TND",
        "TOP",
        "TRY",
        "TTD",
        "TZS",
        "UAH",
        "UGX",
        "UYU",
        "UZS",
        "VEF",
        "VES",
        "VND",
        "VUV",
        "WST",
        "XAF",
        "XCD",
        "XDR",
        "XOF",
        "XPF",
        "YER",
        "ZMW",
        "ZWL"
      ],
      "type": "string",
      "example": "USD",
      "description": "The currency of every amount in this submission, e.g. \"USD\", \"INR\", \"GBP\"."
    },
    "anonymise": {
      "type": "boolean",
      "default": false,
      "description": "Enhanced anonymity, as on the website: true can hide details such as company, title, level and team on the public entry; the pay still counts in the data. Set it only when the user asks for it. Defaults to false."
    },
    "stockType": {
      "enum": [
        "stock",
        "option"
      ],
      "type": "string",
      "description": "\"stock\" for RSUs, \"option\" for stock options. Optional: it follows from the fields sent. One type per submission."
    },
    "sourceDate": {
      "type": "string",
      "example": "2026-09-15",
      "description": "YYYY-MM-DD. For an offer, the offer date (required). For an employee, the date the pay was current; omit for today."
    },
    "companyName": {
      "type": "string",
      "example": "Acme Robotics",
      "description": "The company name, only for a company that search-companies cannot find."
    },
    "companySlug": {
      "type": "string",
      "example": "google",
      "description": "The company slug from search-companies. Send companyName instead only when search-companies has no match."
    },
    "focusTagSlug": {
      "type": "string",
      "example": "ml-ai",
      "description": "A focus area slug from get-job-family for this job family, e.g. \"ml-ai\". Omit when the user did not name one."
    },
    "locationSlug": {
      "type": "string",
      "example": "seattle-usa",
      "description": "A CITY slug from search-locations with type \"city\". Not a metro, state or country."
    },
    "otherDetails": {
      "type": "string",
      "description": "Anything else the user wants recorded, e.g. a retention bonus, or RSUs when the submission already has options (or the reverse). In English, at most 500 characters."
    },
    "submissionId": {
      "type": "string",
      "description": "A UUID you generate once per submission. Send the same one when you retry, so a retry cannot add the entry twice."
    },
    "jobFamilySlug": {
      "type": "string",
      "example": "software-engineer",
      "description": "A Levels.fyi job family slug, e.g. \"software-engineer\", \"product-manager\", \"data-scientist\"."
    },
    "employmentType": {
      "enum": [
        "full_time",
        "part_time",
        "contract",
        "temporary",
        "internship",
        "volunteer",
        "other"
      ],
      "type": "string",
      "description": "Defaults to full_time."
    },
    "yearsAtCompany": {
      "type": "number",
      "example": 2,
      "description": "Employees only (required): years at this company. Offers are always 0."
    },
    "baseSalaryValue": {
      "type": "number",
      "example": 185000,
      "description": "The base salary per baseSalaryTimeUnit."
    },
    "vestingSchedule": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "percent",
          "occurrences"
        ],
        "properties": {
          "percent": {
            "type": "number"
          },
          "occurrences": {
            "type": "number"
          },
          "monthlyVests": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "maxItems": 12,
            "minItems": 12,
            "description": "Monthly vests in a year"
          }
        }
      },
      "description": "Offers only; required with totalStockGrantValue. The vesting schedule, one entry per year: percent of the grant vesting that year and occurrences (vests that year, 4 = quarterly). 4 equal years vesting quarterly is four entries of {percent:25,occurrences:4}. Ask the user, or use get-vesting-schedules for the company standard; never assume one."
    },
    "workArrangement": {
      "enum": [
        "remote",
        "office",
        "hybrid"
      ],
      "type": "string",
      "example": "hybrid"
    },
    "annualBonusValue": {
      "type": "number",
      "example": 25000,
      "description": "The yearly bonus: for an offer the target bonus amount, for an employee the bonus they actually receive per year."
    },
    "optionStrikePrice": {
      "type": "number",
      "example": 1.5,
      "description": "With options: the exercise (strike) price per option. Send with optionPreferredPrice; together they value the options."
    },
    "signOnBonusValues": {
      "type": "array",
      "items": {
        "type": "number"
      },
      "description": "Offers only: the sign-on bonus, one amount per year it is paid, e.g. [50000] or [30000, 20000]."
    },
    "yearsOfExperience": {
      "type": "number",
      "example": 6,
      "description": "Total years of professional experience."
    },
    "baseSalaryTimeUnit": {
      "enum": [
        "hourly",
        "monthly",
        "yearly",
        "one-time",
        "quarterly",
        "semi-monthly",
        "bi-weekly",
        "weekly",
        "daily"
      ],
      "type": "string",
      "example": "yearly",
      "description": "The period baseSalaryValue is paid per, usually \"yearly\"."
    },
    "optionPriceCurrency": {
      "enum": [
        "USD",
        "EUR",
        "GBP",
        "JPY",
        "AUD",
        "CAD",
        "CHF",
        "CNY",
        "HKD",
        "NZD",
        "SEK",
        "SGD",
        "INR",
        "RUB",
        "AED",
        "ILS",
        "SAR",
        "CZK",
        "MXN",
        "ZAR",
        "THB",
        "BRL",
        "TWD",
        "PKR",
        "PLN",
        "AFN",
        "ALL",
        "AMD",
        "ANG",
        "AOA",
        "ARS",
        "AWG",
        "AZN",
        "BAM",
        "BBD",
        "BDT",
        "BGN",
        "BHD",
        "BIF",
        "BMD",
        "BND",
        "BOB",
        "BSD",
        "BTC",
        "BTN",
        "BWP",
        "BYN",
        "BZD",
        "CDF",
        "CLF",
        "CLP",
        "CNH",
        "COP",
        "CRC",
        "CUP",
        "CVE",
        "DJF",
        "DKK",
        "DOP",
        "DZD",
        "EGP",
        "ERN",
        "ETB",
        "FJD",
        "FKP",
        "GEL",
        "GGP",
        "GHS",
        "GIP",
        "GMD",
        "GNF",
        "GTQ",
        "GYD",
        "HNL",
        "HTG",
        "HUF",
        "IDR",
        "IMP",
        "IQD",
        "IRR",
        "ISK",
        "JEP",
        "JMD",
        "JOD",
        "KES",
        "KGS",
        "KHR",
        "KMF",
        "KPW",
        "KRW",
        "KWD",
        "KYD",
        "KZT",
        "LAK",
        "LBP",
        "LKR",
        "LRD",
        "LSL",
        "LYD",
        "MAD",
        "MDL",
        "MGA",
        "MKD",
        "MMK",
        "MNT",
        "MOP",
        "MRU",
        "MUR",
        "MVR",
        "MWK",
        "MYR",
        "MZN",
        "NAD",
        "NGN",
        "NIO",
        "NOK",
        "NPR",
        "OMR",
        "PAB",
        "PEN",
        "PGK",
        "PHP",
        "PYG",
        "QAR",
        "RON",
        "RSD",
        "RWF",
        "SBD",
        "SCR",
        "SDG",
        "SHP",
        "SLE",
        "SOS",
        "SRD",
        "STD",
        "SVC",
        "SYP",
        "SZL",
        "TJS",
        "TMT",
        "TND",
        "TOP",
        "TRY",
        "TTD",
        "TZS",
        "UAH",
        "UGX",
        "UYU",
        "UZS",
        "VEF",
        "VES",
        "VND",
        "VUV",
        "WST",
        "XAF",
        "XCD",
        "XDR",
        "XOF",
        "XPF",
        "YER",
        "ZMW",
        "ZWL"
      ],
      "type": "string",
      "description": "The currency of optionStrikePrice and optionPreferredPrice, when it differs from currency (e.g. US-dollar option prices on a rupee salary)."
    },
    "totalOptionsGranted": {
      "type": "number",
      "example": 40000,
      "description": "Offers only: the number of stock options in the whole grant. Send vestingSchedule with it."
    },
    "optionPreferredPrice": {
      "type": "number",
      "example": 6,
      "description": "With options: the latest preferred share price, usually from the last funding round. Send with optionStrikePrice."
    },
    "relocationBonusValue": {
      "type": "number",
      "example": 10000,
      "description": "Offers only: the relocation bonus."
    },
    "totalStockGrantValue": {
      "type": "number",
      "example": 400000,
      "description": "Offers only: the total value of the stock (RSU) grant over the whole vesting period. Send vestingSchedule with it."
    },
    "avgAnnualOptionsGranted": {
      "type": "number",
      "example": 10000,
      "description": "Employees only: the number of stock options that vest per year."
    },
    "compensationPerspective": {
      "enum": [
        "offer",
        "employee"
      ],
      "type": "string",
      "description": "\"offer\" for a job offer the user received, \"employee\" for pay they earn now."
    },
    "avgAnnualStockGrantValue": {
      "type": "number",
      "example": 100000,
      "description": "Employees only: the stock (RSU) value they receive per year."
    },
    "annualTargetBonusPercentage": {
      "type": "number",
      "example": 15,
      "description": "Offers only: the target bonus as a percent of base, when stated that way."
    }
  }
}
add-salary-submission annotations
{
  "title": "Add My Salary",
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
get-salary-submission-by-id
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get 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-salary-submission-by-id input schema
{
  "type": "object",
  "required": [
    "uuid"
  ],
  "properties": {
    "uuid": {
      "type": "string",
      "example": "1308caab-5d3e-51f9-b84e-125dd97fbe30",
      "description": "The unique uuid of the salary"
    }
  }
}
get-salary-submission-by-id annotations
{
  "title": "Get Salary Submission",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-vesting-schedules
Advertised by the server during the last successful anonymous check.
Advertised read-only

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.

get-vesting-schedules input schema
{
  "type": "object",
  "required": [
    "companySlugs"
  ],
  "properties": {
    "companySlugs": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "example": [
        "amazon",
        "google"
      ],
      "description": "Company slugs to fetch vesting schedules for."
    }
  }
}
get-vesting-schedules annotations
{
  "title": "Get Vesting Schedules",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
search-jobs
Advertised by the server during the last successful anonymous check.
Advertised read-only

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.

search-jobs input schema
{
  "type": "object",
  "required": [
    "query"
  ],
  "properties": {
    "limit": {
      "type": "number",
      "default": 10,
      "description": "Companies per page (default 10, max 25). Each returns up to 2 jobs; with one companySlug it is jobs per page."
    },
    "query": {
      "type": "string",
      "example": "backend engineer",
      "description": "Role names and terms that appear in job posts, e.g. \"backend engineer\" or \"product designer fintech\". Simplify the user's words; leave out locations and salary."
    },
    "offset": {
      "type": "number",
      "description": "Pass the previous result's nextOffset to get the next page."
    },
    "sortBy": {
      "enum": [
        "relevance",
        "total_compensation",
        "date_published"
      ],
      "type": "string",
      "default": "relevance"
    },
    "companySlugs": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Company slugs from search-companies, at most 5."
    },
    "locationSlug": {
      "type": "string",
      "example": "greater-seattle-area",
      "description": "One location slug from search-locations (city, metro, state or country). Only a location the user named."
    },
    "jobFamilySlugs": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Job family slugs from search-job-families, at most 10."
    },
    "standardLevels": {
      "type": "array",
      "items": {
        "enum": [
          "internship",
          "entry",
          "mid_staff",
          "principal",
          "manager",
          "director",
          "executive"
        ],
        "type": "string"
      }
    },
    "employmentTypes": {
      "type": "array",
      "items": {
        "enum": [
          "full_time",
          "part_time",
          "contract",
          "temporary",
          "internship",
          "volunteer",
          "other"
        ],
        "type": "string"
      }
    },
    "postedWithinDays": {
      "type": "number",
      "example": 14,
      "description": "Only jobs posted in the last N days."
    },
    "workArrangements": {
      "type": "array",
      "items": {
        "enum": [
          "remote",
          "office",
          "hybrid"
        ],
        "type": "string"
      }
    },
    "minBaseCompensation": {
      "type": "number",
      "example": 150000,
      "description": "Minimum base salary, compared with the top of the posting's range as posted (most postings are USD)."
    },
    "minMedianTotalCompensation": {
      "type": "number",
      "example": 200000,
      "description": "Minimum total compensation, compared with the top of the posting's range."
    }
  }
}
search-jobs annotations
{
  "title": "Search Jobs",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-job
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get 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 input schema
{
  "type": "object",
  "required": [
    "jobId"
  ],
  "properties": {
    "jobId": {
      "type": "string",
      "description": "A jobId from search-jobs."
    }
  }
}
get-job annotations
{
  "title": "Get Job",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
send-feedback
Advertised by the server during the last successful anonymous check.

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

send-feedback input schema
{
  "type": "object",
  "required": [
    "kind",
    "message"
  ],
  "properties": {
    "kind": {
      "enum": [
        "wrong_data",
        "missing_data",
        "missing_feature",
        "other"
      ],
      "type": "string",
      "description": "What the report is about."
    },
    "message": {
      "type": "string",
      "maxLength": 1000,
      "description": "What is wrong or missing, in the user’s words. No personal details."
    },
    "toolName": {
      "type": "string",
      "description": "The tool whose result the report is about."
    },
    "companySlug": {
      "type": "string",
      "description": "The company the report is about, as a slug."
    },
    "contactEmail": {
      "type": "string",
      "format": "email",
      "description": "Only when the user wants a reply and gives this address."
    },
    "locationSlug": {
      "type": "string",
      "description": "The location the report is about, as a slug."
    },
    "jobFamilySlug": {
      "type": "string",
      "description": "The job family the report is about, as a slug."
    }
  }
}
send-feedback annotations
{
  "title": "Send Feedback",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}
search-all
Advertised by the server during the last successful anonymous check.
Advertised read-only

Search 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-all input schema
{
  "type": "object",
  "required": [
    "searchText"
  ],
  "properties": {
    "limit": {
      "type": "number",
      "example": 10,
      "description": "Max results to return (1–15)."
    },
    "searchText": {
      "type": "string",
      "example": "stripe",
      "description": "Free-text query to search across all entity types (companies, job families, job titles, focus tags, locations, industries)."
    },
    "excludeFamilyLikeTitles": {
      "type": "boolean",
      "description": "When true, exclude TITLE results that are effectively the same as their job family."
    }
  }
}
search-all annotations
{
  "title": "Search All",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-available-filters
Advertised by the server during the last successful anonymous check.
Advertised read-only

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.

get-available-filters input schema
{
  "type": "object",
  "properties": {}
}
get-available-filters annotations
{
  "title": "Get Available Filters",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-metric-explanation
Advertised by the server during the last successful anonymous check.
Advertised read-only

Explain 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-metric-explanation input schema
{
  "type": "object",
  "required": [
    "metric"
  ],
  "properties": {
    "metric": {
      "type": "string",
      "example": "total_compensation",
      "description": "Compensation metric or concept to explain, e.g. \"TC\", \"total_compensation\", \"base\", \"vesting\", \"sign_on\", \"tc_first_year\". Case/separator-insensitive."
    }
  }
}
get-metric-explanation annotations
{
  "title": "Explain Compensation Metric",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
get-domain-knowledge
Advertised by the server during the last successful anonymous check.
Advertised read-only

Get 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-domain-knowledge input schema
{
  "type": "object",
  "properties": {
    "topic": {
      "enum": [
        "compensation",
        "levels",
        "vesting",
        "company_groups",
        "locations",
        "job_families",
        "enums",
        "negotiation_coaching"
      ],
      "type": "string",
      "example": "compensation",
      "description": "Data-model topic to fetch. Omit to get all topics."
    }
  }
}
get-domain-knowledge annotations
{
  "title": "Get Domain Knowledge",
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}

Prompts

3 advertised

evaluate-offer — Evaluate a job offer

Advertised by the server during the last successful anonymous check.

Check a job offer against Levels.fyi pay for the company, level and location.

No arguments advertised.

contribute-salary — Add my salary

Advertised by the server during the last successful anonymous check.

Add your own pay or a job offer to Levels.fyi, without your name.

No arguments advertised.

find-jobs — Find jobs that fit me

Advertised by the server during the last successful anonymous check.

Find open jobs that fit your Levels.fyi profile, with pay shown. Needs sign-in.

No arguments advertised.

Resources

Not advertised

No resource definitions available.

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
454 ms
95th percentile latency
454 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 454 ms · metadata changed · 30 tool(s), 3 prompt(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http levels-fyi-mcp https://mcp.levels.fyi/

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add levels-fyi-mcp --url https://mcp.levels.fyi/

Cursor

Merge this server into .cursor/mcp.json in your project, then enable levels-fyi-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "levels-fyi-mcp": {
      "url": "https://mcp.levels.fyi/"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "levels-fyi-mcp": {
      "type": "streamableHttp",
      "url": "https://mcp.levels.fyi/",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start levels-fyi-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "levels-fyi-mcp": {
      "type": "http",
      "url": "https://mcp.levels.fyi/"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http levels-fyi-mcp https://mcp.levels.fyi/

Claude Desktop

Merge this into claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\), then restart Claude Desktop.
Claude Desktop configuration
{
  "mcpServers": {
    "levels-fyi-mcp": {
      "url": "https://mcp.levels.fyi/"
    }
  }
}

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "levels-fyi-mcp": {
      "serverUrl": "https://mcp.levels.fyi/"
    }
  }
}

Let your agent find its tools

Find servers like Levels.fyi MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run Levels.fyi MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing