monica_list_contacts
Retrieve paginated contacts from Monica CRM with customizable detail levels and optional filters for gender, tags, and communication details.
Instructions
Retrieve a paginated list of contacts without requiring a search query. Choose the detail level (minimal/basic/expanded/full) and optional filters (gender, tags, communication details).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| detailLevel | No | minimal | |
| filters | No | ||
| limit | No | ||
| page | No |
Implementation Reference
- src/tools/modules/contactList.ts:48-112 (handler)The main execution handler for the 'monica_list_contacts' tool. Parses input with Zod schema, determines required data inclusions based on detail level and filters, resolves gender name if needed, fetches contacts via client.listContacts API, applies post-fetch filtering (gender, tags, emails, phones, partial status), maps contacts to requested detail level using helper functions, generates summary, and returns structured paginated list response.async (rawInput) => { const input = listContactsInputSchema.parse(rawInput); const filters = input.filters ?? {}; const includePartial = filters.includePartial ?? false; const requiresContactFields = input.detailLevel !== 'minimal' || filters.hasEmail === true || filters.hasPhone === true; const hasTagFilters = Boolean( (filters.tagIds && filters.tagIds.length > 0) || (filters.tagNames && filters.tagNames.length > 0) ); const requiresTags = input.detailLevel !== 'minimal' && (input.detailLevel !== 'basic' || hasTagFilters); const requiresAddresses = input.detailLevel === 'expanded' || input.detailLevel === 'full'; const genderNameFromId = filters.genderId ? await resolveGenderNameById(client, filters.genderId) : undefined; const normalizedGender = (filters.genderName ?? genderNameFromId)?.trim().toLowerCase(); const normalizedTagNames = (filters.tagNames ?? []).map((name) => name.trim().toLowerCase()); const response = await client.listContacts({ limit: input.limit, page: input.page, includePartial, includeContactFields: requiresContactFields || input.detailLevel === 'full', includeTags: requiresTags || input.detailLevel !== 'minimal', includeAddresses: requiresAddresses || input.detailLevel === 'full' }); const contacts = response.data.filter((contact) => matchesFilters({ contact, includePartial, normalizedGender, normalizedTagNames, requiredTagIds: filters.tagIds, requireEmail: filters.hasEmail, requirePhone: filters.hasPhone }) ); const mapped = contacts.map((contact) => buildContactRepresentation(contact, input.detailLevel as DetailLevel) ); const summary = generateListSummary({ count: mapped.length, itemName: 'contact', contextInfo: `detail level ${input.detailLevel}` }); return buildListResponse({ items: mapped, itemName: 'contact', summaryText: summary, structuredData: { action: 'list', detailLevel: input.detailLevel, filters: { ...filters, genderName: normalizedGender ? genderNameFromId ?? filters.genderName : filters.genderName }, contacts: mapped }, pagination: extractPagination(response) }); }
- src/tools/modules/contactList.ts:35-113 (registration)Direct registration of the 'monica_list_contacts' tool via server.registerTool, specifying title, description, and inline inputSchema (Zod-based with detailLevel, filters, pagination). The handler is provided inline.server.registerTool( 'monica_list_contacts', { title: 'List Monica contacts', description: 'Retrieve a paginated list of contacts without requiring a search query. Choose the detail level (minimal/basic/expanded/full) and optional filters (gender, tags, communication details).', inputSchema: { detailLevel: z.enum(detailLevels).default('minimal'), filters: filtersSchema, limit: z.number().int().min(1).max(100).optional(), page: z.number().int().min(1).optional() } }, async (rawInput) => { const input = listContactsInputSchema.parse(rawInput); const filters = input.filters ?? {}; const includePartial = filters.includePartial ?? false; const requiresContactFields = input.detailLevel !== 'minimal' || filters.hasEmail === true || filters.hasPhone === true; const hasTagFilters = Boolean( (filters.tagIds && filters.tagIds.length > 0) || (filters.tagNames && filters.tagNames.length > 0) ); const requiresTags = input.detailLevel !== 'minimal' && (input.detailLevel !== 'basic' || hasTagFilters); const requiresAddresses = input.detailLevel === 'expanded' || input.detailLevel === 'full'; const genderNameFromId = filters.genderId ? await resolveGenderNameById(client, filters.genderId) : undefined; const normalizedGender = (filters.genderName ?? genderNameFromId)?.trim().toLowerCase(); const normalizedTagNames = (filters.tagNames ?? []).map((name) => name.trim().toLowerCase()); const response = await client.listContacts({ limit: input.limit, page: input.page, includePartial, includeContactFields: requiresContactFields || input.detailLevel === 'full', includeTags: requiresTags || input.detailLevel !== 'minimal', includeAddresses: requiresAddresses || input.detailLevel === 'full' }); const contacts = response.data.filter((contact) => matchesFilters({ contact, includePartial, normalizedGender, normalizedTagNames, requiredTagIds: filters.tagIds, requireEmail: filters.hasEmail, requirePhone: filters.hasPhone }) ); const mapped = contacts.map((contact) => buildContactRepresentation(contact, input.detailLevel as DetailLevel) ); const summary = generateListSummary({ count: mapped.length, itemName: 'contact', contextInfo: `detail level ${input.detailLevel}` }); return buildListResponse({ items: mapped, itemName: 'contact', summaryText: summary, structuredData: { action: 'list', detailLevel: input.detailLevel, filters: { ...filters, genderName: normalizedGender ? genderNameFromId ?? filters.genderName : filters.genderName }, contacts: mapped }, pagination: extractPagination(response) }); } );
- listContactsInputSchema: Zod object schema defining validated input structure - detailLevel enum (minimal/basic/expanded/full), optional filters object, limit (1-100), page.const listContactsInputSchema = z.object({ detailLevel: z.enum(detailLevels).default('minimal'), filters: filtersSchema, limit: z.number().int().min(1).max(100).optional(), page: z.number().int().min(1).optional() });
- filtersSchema: Zod schema for optional filters including genderId/name, tagIds/names, hasEmail/hasPhone booleans, includePartial.const filtersSchema = z .object({ genderId: z.number().int().positive().optional(), genderName: z.string().min(1).max(50).optional(), tagIds: z.array(z.number().int().positive()).max(25).optional(), tagNames: z.array(z.string().min(1).max(255)).max(25).optional(), hasEmail: z.boolean().optional(), hasPhone: z.boolean().optional(), includePartial: z.boolean().optional() }) .optional();
- src/tools/registerTools.ts:27-27 (registration)Invocation of registerContactListTools(context) within the top-level registerTools function, which orchestrates registration of all MCP tools.registerContactListTools(context);