Skip to main content
Glama
lists.tsβ€’22 kB
/** * Lists-related tool configurations */ import { AttioList, AttioListEntry } from '../../types/attio.js'; import { isValidUUID } from '../../utils/validation/uuid-validation.js'; import { getLists, getListDetails, getListEntries, filterListEntries, advancedFilterListEntries, addRecordToList, removeRecordFromList, updateListEntry, getRecordListMemberships, filterListEntriesByParent, filterListEntriesByParentId, ListMembership, } from '../../objects/lists.js'; import { GetListsToolConfig, ToolConfig, GetListEntriesToolConfig, ListActionToolConfig, } from '../tool-types.js'; import { formatToolDescription } from '@/handlers/tools/standards/index.js'; // Lists tool configurations export const listsToolConfigs = { getLists: { name: 'get-lists', handler: getLists, formatResult: (results: AttioList[]) => { // Return JSON string - dispatcher will convert to JSON content return JSON.stringify(Array.isArray(results) ? results : []); }, } as GetListsToolConfig, getRecordListMemberships: { name: 'get-record-list-memberships', handler: getRecordListMemberships, formatResult: (results: ListMembership[] | null | undefined) => { // Return JSON string - dispatcher will convert to JSON content return JSON.stringify(Array.isArray(results) ? results : []); }, } as ToolConfig, getListDetails: { name: 'get-list-details', handler: async (listId: string) => { // Let Attio API decide if list ID is valid (supports UUIDs and slugs) return await getListDetails(listId); }, formatResult: (result: AttioList) => { // Return JSON string return JSON.stringify(result); }, } as ToolConfig, getListEntries: { name: 'get-list-entries', handler: async (listId: string, limit?: number, offset?: number) => { // UUID validation - hard fail for invalid list IDs if (!isValidUUID(listId)) { return { isError: true, content: [ { type: 'text', text: `Invalid list_id: must be a UUID. Got: ${listId}`, }, ], }; } return await getListEntries(listId, limit, offset); }, formatResult: ( results: | AttioListEntry[] | { isError: boolean; content: Array<Record<string, unknown>> } ) => { // Handle validation error response if (results && typeof results === 'object' && 'isError' in results) { return 'Error: Invalid list ID'; } // Return JSON string return JSON.stringify(Array.isArray(results) ? results : []); }, } as GetListEntriesToolConfig, filterListEntries: { name: 'filter-list-entries', handler: filterListEntries, formatResult: (results: AttioListEntry[]) => { // Return JSON string return JSON.stringify(Array.isArray(results) ? results : []); }, } as ToolConfig, advancedFilterListEntries: { name: 'advanced-filter-list-entries', handler: advancedFilterListEntries, formatResult: (results: AttioListEntry[]) => { // Return JSON string return JSON.stringify(Array.isArray(results) ? results : []); }, } as ToolConfig, addRecordToList: { name: 'add-record-to-list', handler: async ( listId: string, recordId: string, objectType: string, values?: Record<string, unknown> ) => { // UUID validation - hard fail for invalid list IDs if (!isValidUUID(listId)) { return { isError: true, content: [ { type: 'text', text: `Invalid list_id: must be a UUID. Got: ${listId}`, }, ], }; } return await addRecordToList(listId, recordId, objectType, values); }, idParams: ['listId', 'recordId'], formatResult: ( result: | AttioListEntry | { isError: boolean; content: Array<Record<string, unknown>> } ) => { // Handle validation error response if (result && typeof result === 'object' && 'isError' in result) { return 'Error: Invalid list ID'; } // Return JSON string return JSON.stringify(result); }, } as ToolConfig, removeRecordFromList: { name: 'remove-record-from-list', handler: async (listId: string, entryId: string) => { // UUID validation - hard fail for invalid list IDs if (!isValidUUID(listId)) { return { isError: true, content: [ { type: 'text', text: `Invalid list_id: must be a UUID. Got: ${listId}`, }, ], }; } return await removeRecordFromList(listId, entryId); }, idParams: ['listId', 'entryId'], } as ListActionToolConfig, updateListEntry: { name: 'update-list-entry', handler: updateListEntry, formatResult: (result: AttioListEntry) => { // Return JSON string return JSON.stringify(result); }, } as ToolConfig, filterListEntriesByParent: { name: 'filter-list-entries-by-parent', handler: filterListEntriesByParent, formatResult: (results: AttioListEntry[]) => { // Return JSON string return JSON.stringify(Array.isArray(results) ? results : []); }, } as ToolConfig, filterListEntriesByParentId: { name: 'filter-list-entries-by-parent-id', handler: filterListEntriesByParentId, formatResult: (results: AttioListEntry[]) => { // Return JSON string return JSON.stringify(Array.isArray(results) ? results : []); }, } as ToolConfig, }; // Lists tool definitions export const listsToolDefinitions = [ { name: 'get-lists', description: formatToolDescription({ capability: 'Retrieve all CRM lists (sales pipelines, lead stages, customer segments).', boundaries: 'create or modify lists, only reads existing lists.', constraints: 'Returns all lists visible to the authenticated workspace.', recoveryHint: 'Use get-list-details to inspect individual list schemas.', }), inputSchema: { type: 'object', properties: {}, additionalProperties: false, }, }, { name: 'get-record-list-memberships', description: formatToolDescription({ capability: 'Find all lists containing a specific company or person record.', boundaries: 'modify list memberships or retrieve list entries.', constraints: 'Requires recordId; processes 5 lists in parallel by default (max 20).', recoveryHint: 'If record not found, verify recordId with records_search first.', }), inputSchema: { type: 'object', properties: { recordId: { type: 'string', description: 'ID of the record to find in lists', example: '550e8400-e29b-41d4-a716-446655440000', }, objectType: { type: 'string', description: 'Type of record (e.g., "companies", "people")', enum: ['companies', 'people'], }, includeEntryValues: { type: 'boolean', description: 'Whether to include entry values in the response (e.g., stage, status)', default: false, }, batchSize: { type: 'number', description: 'Number of lists to process in parallel (1-20, default: 5)', minimum: 1, maximum: 20, default: 5, }, }, required: ['recordId'], additionalProperties: false, }, }, { name: 'get-list-details', description: formatToolDescription({ capability: 'Retrieve schema and configuration for a specific list (stages, fields, attributes).', boundaries: 'modify list structure or retrieve list entries.', constraints: 'Requires valid list UUID or slug; accepts both formats.', recoveryHint: 'Use get-lists to discover available list IDs and slugs first.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'ID or slug of the list to get details for', example: 'sales-pipeline', }, }, required: ['listId'], additionalProperties: false, }, }, { name: 'get-list-entries', description: formatToolDescription({ capability: 'Retrieve all records in a list with pagination (companies, people in pipelines).', boundaries: 'filter entries or modify list memberships.', constraints: 'Requires list UUID (not slug); default limit 20, max per page varies by API.', recoveryHint: 'Use filter-list-entries for attribute-based filtering instead.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'UUID of the list to get entries for', example: '550e8400-e29b-41d4-a716-446655440000', }, limit: { type: 'number', description: 'Maximum number of entries to fetch (default: 20)', example: 50, }, offset: { type: 'number', description: 'Number of entries to skip for pagination (default: 0)', example: 0, }, }, required: ['listId'], additionalProperties: false, }, }, { name: 'filter-list-entries', description: formatToolDescription({ capability: 'Filter list entries by single attribute condition.', boundaries: 'combine conditions; use advanced-filter for multi-condition.', constraints: 'Requires listId, attributeSlug, condition, value.', recoveryHint: 'Use get-list-details for valid attribute slugs.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'ID of the list to filter entries from', example: '550e8400-e29b-41d4-a716-446655440000', }, attributeSlug: { type: 'string', description: "Slug of the attribute to filter by (e.g., 'stage', 'status')", example: 'stage', }, condition: { type: 'string', description: "Filter condition (e.g., 'equals', 'contains', 'greater_than')", enum: [ 'equals', 'not_equals', 'contains', 'not_contains', 'starts_with', 'ends_with', 'greater_than', 'less_than', 'greater_than_or_equals', 'less_than_or_equals', 'is_empty', 'is_not_empty', 'is_set', 'is_not_set', ], example: 'equals', }, value: { description: 'Value to filter by (type depends on the attribute)', example: 'Qualified', }, limit: { type: 'number', description: 'Maximum number of entries to fetch (default: 20)', example: 50, }, offset: { type: 'number', description: 'Number of entries to skip for pagination (default: 0)', example: 0, }, }, required: ['listId', 'attributeSlug', 'condition', 'value'], additionalProperties: false, }, }, { name: 'advanced-filter-list-entries', description: formatToolDescription({ capability: 'Filter entries with multi-condition queries (AND/OR logic).', boundaries: 'modify entries; read-only.', constraints: 'Requires listId, filters array; matchAny for OR logic.', recoveryHint: 'Use filter-list-entries for single conditions.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'ID of the list to filter entries from', example: '550e8400-e29b-41d4-a716-446655440000', }, filters: { type: 'object', description: 'Advanced filter configuration', properties: { filters: { type: 'array', description: 'Array of filter conditions', items: { type: 'object', properties: { attribute: { type: 'object', properties: { slug: { type: 'string', description: "Slug of the attribute to filter by (e.g., 'stage', 'status')", }, }, required: ['slug'], }, condition: { type: 'string', description: "Filter condition (e.g., 'equals', 'contains', 'greater_than')", enum: [ 'equals', 'not_equals', 'contains', 'not_contains', 'starts_with', 'ends_with', 'greater_than', 'less_than', 'greater_than_or_equals', 'less_than_or_equals', 'is_empty', 'is_not_empty', 'is_set', 'is_not_set', ], }, value: { description: 'Value to filter by (type depends on the attribute)', }, logicalOperator: { type: 'string', description: "Logical operator to use with the next filter (default: 'and')", enum: ['and', 'or'], }, }, required: ['attribute', 'condition', 'value'], }, }, matchAny: { type: 'boolean', description: 'When true, at least one filter must match (OR logic). When false, all filters must match (AND logic). Default: false', }, }, required: ['filters'], }, limit: { type: 'number', description: 'Maximum number of entries to fetch (default: 20)', example: 50, }, offset: { type: 'number', description: 'Number of entries to skip for pagination (default: 0)', example: 0, }, }, required: ['listId', 'filters'], additionalProperties: false, }, }, { name: 'add-record-to-list', description: formatToolDescription({ capability: 'Add company or person to list with optional initial values.', boundaries: 'create records; record must exist first.', requiresApproval: true, constraints: 'Requires list UUID, record UUID, object type.', recoveryHint: 'If not found, create record first with create-record.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'UUID of the list to add the record to', example: '550e8400-e29b-41d4-a716-446655440000', }, recordId: { type: 'string', description: 'UUID of the record to add to the list', example: '660e8400-e29b-41d4-a716-446655440001', }, objectType: { type: 'string', description: 'Type of record (e.g., "companies", "people")', enum: ['companies', 'people'], example: 'companies', }, initialValues: { type: 'object', description: 'Initial values for the list entry (e.g., {"stage": "Prospect"})', example: { stage: 'Qualified' }, }, }, required: ['listId', 'recordId', 'objectType'], additionalProperties: false, }, }, { name: 'remove-record-from-list', description: formatToolDescription({ capability: 'Remove company or person from list (membership only).', boundaries: 'delete underlying record; membership only.', requiresApproval: true, constraints: 'Requires list UUID, entry UUID (not record UUID).', recoveryHint: 'Use get-list-entries to find entry UUID.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'UUID of the list to remove the entry from', example: '550e8400-e29b-41d4-a716-446655440000', }, entryId: { type: 'string', description: 'UUID of the list entry to remove (not the record ID)', example: '770e8400-e29b-41d4-a716-446655440002', }, }, required: ['listId', 'entryId'], additionalProperties: false, }, }, { name: 'update-list-entry', description: formatToolDescription({ capability: 'Update list entry attributes (stage, status, custom fields).', boundaries: 'update record attributes; use update-record for that.', requiresApproval: true, constraints: 'Requires list UUID, entry UUID, attributes object.', recoveryHint: 'Use get-list-details for valid attributes and values.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'UUID of the list containing the entry', example: '550e8400-e29b-41d4-a716-446655440000', }, entryId: { type: 'string', description: 'UUID of the list entry to update', example: '770e8400-e29b-41d4-a716-446655440002', }, attributes: { type: 'object', description: 'Attributes to update on the list entry', properties: { stage: { type: 'string', description: "New stage value (e.g., 'Demo Scheduling', 'Interested', 'Won')", example: 'Demo Scheduling', }, }, additionalProperties: true, }, }, required: ['listId', 'entryId', 'attributes'], additionalProperties: false, }, }, { name: 'filter-list-entries-by-parent', description: formatToolDescription({ capability: 'Filter entries by parent record attributes (industry, role).', boundaries: 'search multiple lists or modify records.', constraints: 'Requires listId, parentObjectType, parentAttributeSlug, condition, value.', recoveryHint: 'Use records_discover_attributes for valid slugs.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'UUID of the list to filter entries from', example: '550e8400-e29b-41d4-a716-446655440000', }, parentObjectType: { type: 'string', description: 'Type of the parent record (e.g., "companies", "people")', enum: ['companies', 'people'], example: 'companies', }, parentAttributeSlug: { type: 'string', description: 'Attribute of the parent record to filter by (e.g., "name", "email_addresses", "categories")', example: 'categories', }, condition: { type: 'string', description: 'Filter condition (e.g., "equals", "contains", "starts_with")', enum: [ 'equals', 'not_equals', 'contains', 'not_contains', 'starts_with', 'ends_with', 'greater_than', 'less_than', 'greater_than_or_equals', 'less_than_or_equals', 'is_empty', 'is_not_empty', 'is_set', 'is_not_set', ], example: 'contains', }, value: { description: 'Value to filter by (type depends on the attribute)', example: 'Technology', }, limit: { type: 'number', description: 'Maximum number of entries to fetch (default: 20)', example: 50, }, offset: { type: 'number', description: 'Number of entries to skip for pagination (default: 0)', example: 0, }, }, required: [ 'listId', 'parentObjectType', 'parentAttributeSlug', 'condition', 'value', ], additionalProperties: false, }, }, { name: 'filter-list-entries-by-parent-id', description: formatToolDescription({ capability: 'Filter entries by exact parent record UUID.', boundaries: 'search multiple lists.', constraints: 'Requires list UUID, record UUID; faster than attribute filtering.', recoveryHint: 'Use get-record-list-memberships for workspace-wide search.', }), inputSchema: { type: 'object', properties: { listId: { type: 'string', description: 'UUID of the list to filter entries from', example: '550e8400-e29b-41d4-a716-446655440000', }, recordId: { type: 'string', description: 'UUID of the parent record to filter by', example: '660e8400-e29b-41d4-a716-446655440001', }, limit: { type: 'number', description: 'Maximum number of entries to fetch (default: 20)', example: 50, }, offset: { type: 'number', description: 'Number of entries to skip for pagination (default: 0)', example: 0, }, }, required: ['listId', 'recordId'], additionalProperties: false, }, }, ];

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

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