Skip to main content
Glama

List Monica contacts

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
NameRequiredDescriptionDefault
detailLevelNominimal
filtersNo
limitNo
pageNo

Implementation Reference

  • 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)
      });
    }
  • 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();
  • Invocation of registerContactListTools(context) within the top-level registerTools function, which orchestrates registration of all MCP tools.
    registerContactListTools(context);
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. It mentions pagination and filtering options, but fails to describe critical behaviors like authentication requirements, rate limits, error handling, or what the response format looks like. This leaves significant gaps for a tool with multiple parameters and no output schema.

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

Conciseness5/5

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

The description is perfectly concise with two well-structured sentences that front-load the core purpose and efficiently detail parameter semantics. Every word earns its place with zero wasted text.

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

Completeness3/5

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

Given the tool's complexity (4 parameters, nested objects, no annotations, no output schema), the description provides adequate basic context about purpose and parameters but lacks sufficient behavioral information. It doesn't explain what the tool returns, error conditions, or operational constraints, making it incomplete for safe and effective use.

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

Parameters4/5

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

With 0% schema description coverage, the description compensates well by explaining the meaning of key parameters: it clarifies 'detailLevel' options (minimal/basic/expanded/full) and describes 'filters' content (gender, tags, communication details). However, it doesn't cover 'limit' and 'page' parameters, leaving some gaps in parameter understanding.

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 specific verb ('Retrieve') and resource ('paginated list of contacts'), distinguishing it from sibling tools like 'monica_search_contacts' by explicitly noting it works 'without requiring a search query'. This provides precise differentiation and purpose.

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

Usage Guidelines4/5

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

The description provides clear context by stating this tool is for listing contacts 'without requiring a search query', which implicitly suggests using 'monica_search_contacts' when a search query is needed. However, it lacks explicit when-not-to-use guidance or alternative naming for all scenarios.

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

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/Jacob-Stokes/monica-mcp'

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