Skip to main content
Glama

searchContacts

Find specific contacts in Clay by searching job titles, companies, locations, or keywords. Returns detailed contact records to answer questions like "Who works at X?" or "Who specializes in Y?".

Instructions

Search for contacts and return matching people. Use for questions about specific contacts or "who" questions (e.g. "Who did I meet most?" or "who works as an engineer?"). Returns actual contact records for queries needing specific people.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
company_nameNoIf the query refers to a company or acronym of companies, list company names as they would on a LinkedIn profile.
exclude_contact_idsNoUsed to exclude previously returned contact IDs when the user asks for more results (e.g. "who else" or "show me more"). You should pass all contact IDs from previous searchContacts responses to ensure new results are shown.
job_titleNoIf the query refers to a job title, position, or industry, list relevant job titles as they would be on a LinkedIn profile. Examples: Developer should return positions such as 'Software Engineer', 'Full Stack Developer', 'Data Scientist', etc. Banker should return positions such as 'Financial Analyst', 'Investment Banker', 'Credit Analyst', etc. Healthcare industry should return positions such as 'Registered Nurse', 'Physician', 'Medical Director', etc. Legal industry should return positions such as 'Attorney', 'Legal Counsel', 'Paralegal', etc.
keywordsNoExtract and list specific keywords related to professional expertise, skills, interests, or hobbies that the user is searching for. For example, if someone asks for 'people who know about machine learning or play tennis', the keywords would be ['machine learning', 'tennis']. Do not include job titles or company names here as those have dedicated fields. Focus on capturing domain expertise, technical skills, personal interests, and hobby-related terms that help identify relevant contacts.
limitNoThe number of contacts to return if the user asks for an amount.
locationNoIf the query refers to a location (city, state, country, region) where people are located or based, list the locations as they would appear on a LinkedIn profile. For example, if someone asks about "people in New York", return "New York City Metropolitan Area" or if they ask about "contacts in California", return "San Francisco Bay Area", "Greater Los Angeles Area", etc.
queryYesThe raw search query from the user. Must preserve exact intent and details to enable accurate searching, including: relationship qualifiers, interaction metrics, relationship strength, names, companies, locations, dates (specific dates, date ranges, or relative dates like "last week" are required if mentioned by user), job titles, skills, and logical conditions (OR/AND).
sort_instructionsNoHow would you like the results sorted? For example: "most recent contacts" will sort by last interaction date, "closest connections" will sort by interaction count, and "alphabetical" will sort by name. If no sort preference is given, this can be left empty.

Implementation Reference

  • index.js:96-96 (handler)
    The execute handler for searchContacts tool, which proxies the call to the external Clay API endpoint /search using the shared callTool helper.
    execute: async (params, { session }) => callTool("/search", params, session),
  • Zod schema defining input parameters for the searchContacts tool, including fields like job_title, company_name, location, query, keywords, limit, exclude_contact_ids, and sort_instructions.
    parameters: z.object({
      job_title: z
        .array(z.string())
        .describe(
          "If the query refers to a job title, position, or industry, list relevant job titles as they would be on a LinkedIn profile. Examples: Developer should return positions such as 'Software Engineer', 'Full Stack Developer', 'Data Scientist', etc. Banker should return positions such as 'Financial Analyst', 'Investment Banker', 'Credit Analyst', etc. Healthcare industry should return positions such as 'Registered Nurse', 'Physician', 'Medical Director', etc. Legal industry should return positions such as 'Attorney', 'Legal Counsel', 'Paralegal', etc."
        )
        .default([]),
      company_name: z
        .array(z.string())
        .describe(
          "If the query refers to a company or acronym of companies, list company names as they would on a LinkedIn profile."
        )
        .default([]),
      location: z
        .array(z.string())
        .describe(
          'If the query refers to a location (city, state, country, region) where people are located or based, list the locations as they would appear on a LinkedIn profile. For example, if someone asks about "people in New York", return "New York City Metropolitan Area" or if they ask about "contacts in California", return "San Francisco Bay Area", "Greater Los Angeles Area", etc.'
        )
        .default([]),
      query: z
        .string()
        .describe(
          'The raw search query from the user. Must preserve exact intent and details to enable accurate searching, including: relationship qualifiers, interaction metrics, relationship strength, names, companies, locations, dates (specific dates, date ranges, or relative dates like "last week" are required if mentioned by user), job titles, skills, and logical conditions (OR/AND).'
        ),
      keywords: z
        .array(z.string())
        .describe(
          "Extract and list specific keywords related to professional expertise, skills, interests, or hobbies that the user is searching for. For example, if someone asks for 'people who know about machine learning or play tennis', the keywords would be ['machine learning', 'tennis']. Do not include job titles or company names here as those have dedicated fields. Focus on capturing domain expertise, technical skills, personal interests, and hobby-related terms that help identify relevant contacts."
        )
        .default([]),
      limit: z
        .number()
        .describe(
          "The number of contacts to return if the user asks for an amount."
        )
        .default(10),
      exclude_contact_ids: z
        .array(z.number())
        .describe(
          'Used to exclude previously returned contact IDs when the user asks for more results (e.g. "who else" or "show me more"). You should pass all contact IDs from previous searchContacts responses to ensure new results are shown.'
        )
        .optional(),
      sort_instructions: z
        .string()
        .describe(
          'How would you like the results sorted? For example: "most recent contacts" will sort by last interaction date, "closest connections" will sort by interaction count, and "alphabetical" will sort by name. If no sort preference is given, this can be left empty.'
        )
        .optional(),
    }),
  • index.js:43-97 (registration)
    Registration of the searchContacts tool via server.addTool, specifying name, description, input schema, and execute handler.
    server.addTool({
      name: "searchContacts",
      description:
        'Search for contacts and return matching people. Use for questions about specific contacts or "who" questions (e.g. "Who did I meet most?" or "who works as an engineer?"). Returns actual contact records for queries needing specific people.',
      parameters: z.object({
        job_title: z
          .array(z.string())
          .describe(
            "If the query refers to a job title, position, or industry, list relevant job titles as they would be on a LinkedIn profile. Examples: Developer should return positions such as 'Software Engineer', 'Full Stack Developer', 'Data Scientist', etc. Banker should return positions such as 'Financial Analyst', 'Investment Banker', 'Credit Analyst', etc. Healthcare industry should return positions such as 'Registered Nurse', 'Physician', 'Medical Director', etc. Legal industry should return positions such as 'Attorney', 'Legal Counsel', 'Paralegal', etc."
          )
          .default([]),
        company_name: z
          .array(z.string())
          .describe(
            "If the query refers to a company or acronym of companies, list company names as they would on a LinkedIn profile."
          )
          .default([]),
        location: z
          .array(z.string())
          .describe(
            'If the query refers to a location (city, state, country, region) where people are located or based, list the locations as they would appear on a LinkedIn profile. For example, if someone asks about "people in New York", return "New York City Metropolitan Area" or if they ask about "contacts in California", return "San Francisco Bay Area", "Greater Los Angeles Area", etc.'
          )
          .default([]),
        query: z
          .string()
          .describe(
            'The raw search query from the user. Must preserve exact intent and details to enable accurate searching, including: relationship qualifiers, interaction metrics, relationship strength, names, companies, locations, dates (specific dates, date ranges, or relative dates like "last week" are required if mentioned by user), job titles, skills, and logical conditions (OR/AND).'
          ),
        keywords: z
          .array(z.string())
          .describe(
            "Extract and list specific keywords related to professional expertise, skills, interests, or hobbies that the user is searching for. For example, if someone asks for 'people who know about machine learning or play tennis', the keywords would be ['machine learning', 'tennis']. Do not include job titles or company names here as those have dedicated fields. Focus on capturing domain expertise, technical skills, personal interests, and hobby-related terms that help identify relevant contacts."
          )
          .default([]),
        limit: z
          .number()
          .describe(
            "The number of contacts to return if the user asks for an amount."
          )
          .default(10),
        exclude_contact_ids: z
          .array(z.number())
          .describe(
            'Used to exclude previously returned contact IDs when the user asks for more results (e.g. "who else" or "show me more"). You should pass all contact IDs from previous searchContacts responses to ensure new results are shown.'
          )
          .optional(),
        sort_instructions: z
          .string()
          .describe(
            'How would you like the results sorted? For example: "most recent contacts" will sort by last interaction date, "closest connections" will sort by interaction count, and "alphabetical" will sort by name. If no sort preference is given, this can be left empty.'
          )
          .optional(),
      }),
      execute: async (params, { session }) => callTool("/search", params, session),
    });
  • Shared helper function callTool used by searchContacts (and other tools) to proxy requests to the external Clay API server with authentication.
    async function callTool(path, params, session) {
      console.log('Calling tool', path, session)
      return fetch(`https://nexum.clay.earth/tools${path}`, {
        body: JSON.stringify(params),
        headers: {
          Authorization: `ApiKey ${session.apiKey}`,
          "Content-Type": "application/json",
        },
        method: "POST",
      }).then((res) => res.text());
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. While it mentions what the tool returns ('actual contact records'), it lacks critical behavioral details: whether this is a read-only operation, how results are paginated (though limit parameter exists), error conditions, authentication requirements, or rate limits. For a search tool with 8 parameters, this is insufficient.

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

Conciseness4/5

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

The description is appropriately sized with three sentences that each serve distinct purposes: stating the core function, providing usage examples, and specifying return type. It's front-loaded with the main purpose and avoids unnecessary elaboration. Minor improvement could be made by combining the second and third sentences.

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

Completeness2/5

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

For a search tool with 8 parameters, no annotations, and no output schema, the description is incomplete. While it covers purpose and usage well, it lacks critical behavioral context about how the search works, what 'actual contact records' contain, error handling, and performance characteristics. The absence of output schema means the description should ideally explain return format, which it doesn't.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all 8 parameters thoroughly. The description doesn't add any parameter-specific information beyond what's in the schema. However, it does provide context about the overall search intent ('who' questions), which gives semantic meaning to the parameter usage collectively.

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

Purpose5/5

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

The description clearly states the tool's purpose with specific verbs ('search for contacts and return matching people') and distinguishes it from siblings by specifying it's for 'who' questions and returning 'actual contact records'. It differentiates from tools like getContact (single contact) and searchInteractions (interactions rather than people).

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

Usage Guidelines5/5

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

The description provides explicit guidance on when to use this tool: 'for questions about specific contacts or "who" questions' with concrete examples ('Who did I meet most?' or 'who works as an engineer?'). It also specifies the return type ('actual contact records for queries needing specific people'), helping differentiate from other contact-related tools.

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

Install Server

Other Tools

Related Tools

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/clay-inc/clay-mcp'

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