Skip to main content
Glama
people_profile_search.ts5.16 kB
import { z } from 'zod'; import { getClient } from '../common/client.js'; import { ListEntitiesRequest, ListEntitiesRequestEntityType, ListEntitiesRequest$inboundSchema as ListEntitiesRequestSchema, } from '@gleanwork/api-client/models/components'; // Allowed facet names for filtering people searches. export const PEOPLE_FACETS_VALUES = [ 'email', 'first_name', 'last_name', 'manager_email', 'department', 'title', 'location', 'city', 'country', 'state', 'region', 'business_unit', 'team', 'team_id', 'nickname', 'preferred_name', 'roletype', 'reportsto', 'startafter', 'startbefore', 'industry', 'has', 'from', ] as const; /** * Simplified schema for people profile search requests designed for LLM interaction */ export const ToolPeopleProfileSearchSchema = z .object({ query: z .string() .describe('Free-text query to search people by name, title, etc.') .optional(), filters: z .record(z.string(), z.string()) .refine( (val) => Object.keys(val).every((key) => PEOPLE_FACETS_VALUES.includes(key as any), ), { message: 'Invalid filter key. Must be one of: ' + PEOPLE_FACETS_VALUES.join(', '), }, ) .describe( 'Allowed facet fields: email, first_name, last_name, manager_email, department, title, location, city, country, state, region, business_unit, team, team_id, nickname, preferred_name, roletype, reportsto, startafter, startbefore, industry, has, from. Provide as { "facet": "value" }.', ) .optional(), pageSize: z .number() .int() .min(1) .max(100) .describe( 'Hint to the server for how many people to return (1-100, default 10).', ) .optional(), }) .refine( (val) => val.query || (val.filters && Object.keys(val.filters).length > 0), { message: 'At least one of "query" or "filters" must be provided.', path: [], }, ); export type ToolPeopleProfileSearchRequest = z.infer< typeof ToolPeopleProfileSearchSchema >; /** * Converts a simplified request to a Glean API ListEntitiesRequest. * * @param input The simplified request parameters * @returns The Glean API compatible request */ function convertToAPIEntitiesRequest(input: ToolPeopleProfileSearchRequest) { const { query, filters = {}, pageSize } = input; const request: ListEntitiesRequest = { entityType: ListEntitiesRequestEntityType.People, pageSize: pageSize || 10, }; if (query) { request.query = query; } const filterKeys = Object.keys(filters) as Array<keyof typeof filters>; if (filterKeys.length > 0) { request.filter = filterKeys.map((fieldName) => { const value = filters[fieldName]; return { fieldName: String(fieldName), values: [ { relationType: 'EQUALS', value: value as string, }, ], }; }); } return request; } /** * Executes a people profile search using the Glean API. * * @param params The search parameters using the simplified schema * @returns The search results */ export async function peopleProfileSearch( params: ToolPeopleProfileSearchRequest, ) { const mappedParams = convertToAPIEntitiesRequest(params); const parsedParams = ListEntitiesRequestSchema.parse( mappedParams, ) as ListEntitiesRequest; const client = await getClient(); return await client.entities.list(parsedParams); } /** * Formats the search results for human consumption. * * @param searchResults The raw search results from Glean API * @returns Formatted search results as text */ export function formatResponse(searchResults: any): string { if ( !searchResults || !Array.isArray(searchResults.results) || searchResults.results.length === 0 ) { return 'No matching people found.'; } const formatted = searchResults.results .map((person: any, index: number) => { const metadata = person.metadata ?? {}; const displayName = metadata.preferredName || person.name || 'Unnamed'; const title = metadata.title || 'Unknown title'; const department = metadata.department || 'Unknown department'; const location = metadata.location || metadata.structuredLocation?.city || metadata.structuredLocation?.country || 'Unknown location'; const email = metadata.email || metadata.aliasEmails?.[0] || 'Unknown email'; // Show first team affiliation if present for additional context const primaryTeam = Array.isArray(metadata.teams) && metadata.teams.length > 0 ? metadata.teams[0].name : undefined; const teamSuffix = primaryTeam ? ` [${primaryTeam}]` : ''; return `${index + 1}. ${displayName} – ${title}${teamSuffix}, ${department} (${location}) • ${email}`; }) .join('\n'); const total = typeof searchResults.totalCount === 'number' ? searchResults.totalCount : searchResults.results.length; return `Found ${total} people:\n\n${formatted}`; }

Implementation Reference

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/gleanwork/mcp-server'

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