Skip to main content
Glama

search_people

Find LinkedIn profiles by name, job title, or keywords, with optional location filtering to identify professionals matching specific criteria.

Instructions

Search for people on LinkedIn.

Args: keywords: Search keywords (e.g., 'product manager', 'ML engineer at Meta') location: Optional location filter (e.g., 'London', 'Berlin')

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
keywordsYes
locationNo

Implementation Reference

  • MCP tool registration for 'search_people' - defines the tool interface with name, description, parameters (keywords, location), and error handling. Calls SearchPeopleUseCase.execute().
    @mcp.tool(
        name="search_people",
        description=(
            "Search for people on LinkedIn.\n\n"
            "Args:\n"
            "    keywords: Search keywords (e.g., 'product manager', 'ML engineer at Meta')\n"
            "    location: Optional location filter (e.g., 'London', 'Berlin')"
        ),
    )
    async def search_people(
        keywords: str,
        ctx: Context,
        location: str | None = None,
    ) -> dict[str, Any]:
        try:
            result = await search_people_uc.execute(keywords, location)
            return {
                "url": result.url,
                "sections": serialize_sections(result.sections),
            }
        except Exception as e:
            map_domain_error(e, "search_people")
  • Main use case implementation - SearchPeopleUseCase class with execute() method that builds LinkedIn search URL, fetches HTML via browser, parses search results, and returns ScrapeResponse.
    class SearchPeopleUseCase:
        """Search for people on LinkedIn."""
    
        def __init__(self, browser: BrowserPort, auth: AuthPort, *, debug: bool = False):
            self._browser = browser
            self._auth = auth
            self._debug = debug
    
        async def execute(
            self,
            keywords: str,
            location: str | None = None,
        ) -> ScrapeResponse:
            await self._auth.ensure_authenticated()
    
            params = f"keywords={quote_plus(keywords)}"
            if location:
                params += f"&location={quote_plus(location)}"
    
            url = f"https://www.linkedin.com/search/results/people/?{params}"
            content = await self._browser.extract_page_html(url)
    
            sections: dict[str, Any] = {}
            if content.html:
                sections["search_results"] = parse_section(
                    "search_results",
                    content.html,
                    entity_type="search_people",
                    include_raw=self._debug,
                )
    
            return ScrapeResponse(url=url, sections=sections)
  • Domain models for search results - PersonSearchResult and PeopleSearchResults dataclasses defining the structure of parsed people search data including name, connection_degree, headline, location, profile_url, etc.
    @dataclass
    class PersonSearchResult:
        """A single person result from people search."""
    
        name: str
        connection_degree: str
        headline: str | None = None
        location: str | None = None
        current: str | None = None
        mutual_connections: str | None = None
        followers: str | None = None
        profile_url: str | None = None
        linkedin_username: str | None = None
        profile_image_url: str | None = None
    
    
    @dataclass
    class PeopleSearchResults:
        """People search results page."""
    
        people: list[PersonSearchResult] = field(default_factory=list)
        raw: str | None = None
  • HTML parser implementation - parse_search_results_people() function that extracts person search results from LinkedIn's SDUI layout, parsing name, profile URL, connection degree, headline, location, mutual connections, and profile image.
    def parse_search_results_people(
        html: str, *, include_raw: bool = False
    ) -> PeopleSearchResults:
        """Parse people search results page HTML.
    
        Extracts list of PersonSearchResult from SDUI search result cards.
        Each card is identified by data-view-name="people-search-result".
        """
        soup = _soup(html)
        results: list[PersonSearchResult] = []
    
        cards = soup.find_all(
            attrs={"data-view-name": "people-search-result"}
        )
    
        for card in cards:
            # Profile URL and username from the main <a> link
            profile_link = card.find(
                "a",
                attrs={"data-view-name": "search-result-lockup-title"},
            )
            profile_url: str | None = None
            linkedin_username: str | None = None
            name: str = ""
    
            if profile_link:
                name = _text(profile_link) or ""
                href = profile_link.get("href", "")
                if href:
                    profile_url = href
                    m = _PROFILE_URL_RE.search(href)
                    if m:
                        linkedin_username = m.group(1)
    
            if not name:
                continue
    
            # Connection degree from <span class="_45102191">
            connection_degree = ""
            degree_container = card.find(
                "span", class_=lambda c: c and "_45102191" in c
            )
            if degree_container:
                degree_text = _text(degree_container)
                if degree_text:
                    # Extract "1st", "2nd", "3rd" etc.
                    m = re.search(r"(\d+(?:st|nd|rd|th))", degree_text)
                    if m:
                        connection_degree = m.group(1)
    
            # Profile image from <figure> with aria-label
            profile_image_url: str | None = None
            figure = card.find("figure", attrs={"data-view-name": "image"})
            if figure:
                img = figure.find("img")
                if img:
                    src = img.get("src", "")
                    if src and "profile-displayphoto" in src:
                        profile_image_url = src
    
            # Headline — first <p> with _37677861 class in name's parent
            headline: str | None = None
            location: str | None = None
    
            # The listitem div contains the name + headline + location in order
            listitem = card.find("div", attrs={"role": "listitem"})
            if listitem:
                # Find all <p> with _37677861 class that are direct text content
                info_divs = listitem.find_all(
                    "div",
                    class_=lambda c: c
                    and "_04bda81b" in c
                    and "_9dfef8a0" in c
                    and "_837488b5" in c,
                )
                for i, div in enumerate(info_divs):
                    p = div.find("p", class_=lambda c: c and "_37677861" in c)
                    if p:
                        text = _text(p)
                        if text:
                            if i == 0:
                                headline = text
                            elif i == 1:
                                location = text
    
            # Mutual connections from social proof insight
            mutual_connections: str | None = None
            social_proof_links = card.find_all(
                "a",
                attrs={"data-view-name": "search-result-social-proof-insight"},
            )
            for sp_link in social_proof_links:
                sp_text = _text(sp_link)
                if sp_text and "mutual connection" in sp_text.lower():
                    mutual_connections = sp_text
    
            # Followers from social proof
            followers: str | None = None
            for sp_link in social_proof_links:
                sp_text = _text(sp_link)
                if sp_text and "follower" in sp_text.lower():
                    followers = sp_text
    
            results.append(
                PersonSearchResult(
                    name=name,
                    connection_degree=connection_degree,
                    headline=headline,
                    location=location,
                    mutual_connections=mutual_connections,
                    followers=followers,
                    profile_url=profile_url,
                    linkedin_username=linkedin_username,
                    profile_image_url=profile_image_url,
                )
            )
    
        return PeopleSearchResults(
            people=results,
            raw=html if include_raw else None,
        )
  • Server registration call - register_person_tools() invoked with container.search_people dependency, wiring the use case into the MCP server.
    register_person_tools(mcp, container.scrape_person, container.search_people)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eliasbiondo/linkedin-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server