Skip to main content
Glama
bcharleson

Instantly MCP Server

list_campaigns

Retrieve and filter email campaigns from Instantly API with automatic pagination support to access all campaigns efficiently.

Instructions

List campaigns with optional filters and complete pagination support. COMPLETE PAGINATION: To get ALL campaigns, use one of these approaches:

  1. Set limit=100 or higher (automatically triggers complete pagination)

  2. Set get_all=true (forces complete pagination)

  3. Use limit="all" (string triggers complete pagination)

PAGINATION ALGORITHM: When requesting all campaigns, the tool will automatically:

  • Start with limit=100 per page

  • Continue fetching until next_starting_after is null or empty results

  • Report progress: "Retrieved 100... 200... 304 total campaigns"

  • Return summarized data to prevent size limits

  • Use get_campaign for full details of specific campaigns

FILTERS: search and status filters work with both single-page and complete pagination modes.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
get_allNoSet to true to force complete pagination and retrieve ALL campaigns regardless of limit setting.
limitNoNumber of campaigns to return (1-100, default: 20). Use limit=100+ or limit="all" to trigger complete pagination that retrieves ALL campaigns automatically.
searchNoSearch term to filter campaigns by name (works with complete pagination)
starting_afterNoID of the last item from previous page for manual pagination. Not needed when using complete pagination (limit=100+).
statusNoFilter by campaign status (works with complete pagination)

Implementation Reference

  • Main handler execution logic for list_campaigns tool. Makes API call to /campaigns, handles pagination, maps numeric status codes to human-readable labels, adds metadata.
    case 'list_campaigns': {
      console.error('[Instantly MCP] 📋 Executing list_campaigns (sequential pagination)...');
    
      try {
        const startTime = Date.now();
    
        // Status mapping: API returns numbers, we convert to human-readable labels
        const STATUS_MAP: Record<number, string> = {
          0: 'Draft',
          1: 'Active',
          2: 'Paused',
          3: 'Completed',
          4: 'Running Subsequences',
          '-99': 'Account Suspended',
          '-1': 'Accounts Unhealthy',
          '-2': 'Bounce Protect'
        };
    
        // Build query parameters for single page request
        const queryParams: any = {
          limit: args?.limit || 100, // Default to 100 items per page (max pagination)
        };
    
        // Add cursor if provided (for subsequent pages)
        if (args?.starting_after) {
          queryParams.starting_after = args.starting_after;
          console.error(`[Instantly MCP] 📄 Fetching page with cursor: ${args.starting_after}`);
        } else {
          console.error('[Instantly MCP] 📄 Fetching first page');
        }
    
        // Add API filter parameters
        if (args?.search) queryParams.search = args.search;
        if (args?.tag_ids) queryParams.tag_ids = args.tag_ids;
    
        // Make single API call to /campaigns endpoint
        const response = await makeInstantlyRequest('/campaigns', {
          method: 'GET',
          params: queryParams
        }, apiKey);
    
        const elapsed = Date.now() - startTime;
    
        // Extract data and pagination info from response
        const data = Array.isArray(response) ? response : (response.items || response.data || []);
        const nextCursor = response.next_starting_after || null;
        const hasMore = !!nextCursor;
    
        console.error(`[Instantly MCP] ✅ Retrieved ${data.length} campaigns in ${elapsed}ms (has_more: ${hasMore})`);
    
        // Apply status mapping to all campaigns (convert numeric status to human-readable labels)
        const campaignsWithReadableStatus = data.map((campaign: any) => ({
          ...campaign,
          status_label: STATUS_MAP[campaign.status] || `Unknown (${campaign.status})`,
          status_code: campaign.status // Keep original numeric code for reference
        }));
    
        // Track applied filters
        const filtersApplied: any = {};
        if (args?.search) filtersApplied.search = args.search;
        if (args?.tag_ids) filtersApplied.tag_ids = args.tag_ids;
    
        // Return single page with clear pagination metadata
        return createMCPResponse({
          data: campaignsWithReadableStatus,
          pagination: {
            returned_count: campaignsWithReadableStatus.length,
            has_more: hasMore,
            next_starting_after: nextCursor,
            limit: queryParams.limit,
            current_page_note: hasMore
              ? `Retrieved ${campaignsWithReadableStatus.length} campaigns. More results available. To get next page, call list_campaigns again with starting_after='${nextCursor}'`
              : `Retrieved all available campaigns (${campaignsWithReadableStatus.length} items).`
          },
          filters_applied: Object.keys(filtersApplied).length > 0 ? filtersApplied : undefined,
          metadata: {
            request_time_ms: elapsed,
            success: true,
            status_mapping_note: 'All campaigns include status_label (human-readable) and status_code (numeric) fields'
          },
          success: true
        });
      } catch (error: any) {
        console.error('[Instantly MCP] ❌ Error in list_campaigns:', error.message);
        throw error;
      }
    }
  • MCP tool definition and input schema for list_campaigns, including properties for pagination and filtering.
    {
      name: 'list_campaigns',
      title: 'List Campaigns',
      description: 'List campaigns with pagination. Filter by name search or tags.',
      annotations: { readOnlyHint: true },
      inputSchema: {
        type: 'object',
        properties: {
          limit: { type: 'number', description: '1-100, default: 100' },
          starting_after: { type: 'string', description: 'Cursor from pagination.next_starting_after' },
          search: { type: 'string', description: 'Search by campaign NAME only (not status)' },
          tag_ids: { type: 'string', description: 'Comma-separated tag IDs' }
        }
      }
    },
  • Registration of campaignTools (containing list_campaigns) into the main TOOLS_DEFINITION array used by MCP server.
      return [
        ...accountTools,
        ...campaignTools,
        ...leadTools,
        ...emailTools,
        ...analyticsTools,
      ];
    }
  • Zod validation schema ListCampaignsSchema for input parameters and associated validator function.
     * List campaigns validation schema
     */
    export const ListCampaignsSchema = z.object({
      limit: PaginationLimitSchema, // Already optional in the schema definition
      starting_after: z.string().optional(),
      get_all: z.boolean().optional(),
      search: z.string().optional(),
      status: z.string().optional()
    });
Behavior5/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 and does so comprehensively. It details the pagination algorithm (starting limit, continuation logic, progress reporting), explains that summarized data is returned to prevent size limits, clarifies that filters work with both pagination modes, and specifies that full details require 'get_campaign'. This provides rich behavioral context beyond basic functionality.

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 well-structured with clear sections (COMPLETE PAGINATION, PAGINATION ALGORITHM, FILTERS) and uses bullet points effectively. It's appropriately sized for the complexity, though some sentences could be more concise (e.g., the pagination algorithm explanation is somewhat verbose). Overall, it's front-loaded with key information and most content earns its place.

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

Completeness4/5

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

Given the tool's complexity (pagination algorithms, multiple modes) and lack of annotations/output schema, the description does an excellent job explaining behavior and usage. It covers the key aspects an agent needs: purpose, pagination approaches, algorithm details, filter compatibility, and relationship to 'get_campaign'. The main gap is lack of explicit sibling tool differentiation, but otherwise it's quite complete for a list operation with advanced features.

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 five parameters thoroughly. The description adds some context about how parameters interact (e.g., 'limit=100+ or limit="all" triggers complete pagination', 'starting_after not needed when using complete pagination'), but doesn't provide significant additional semantic meaning beyond what's in the schema descriptions. This meets the baseline for high schema coverage.

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

Purpose4/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: 'List campaigns with optional filters and complete pagination support.' It specifies the verb ('list') and resource ('campaigns'), but doesn't explicitly differentiate from sibling tools like 'get_campaign' or 'get_campaign_analytics' beyond mentioning that 'get_campaign' provides full details. The purpose is clear but sibling differentiation is only implied.

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 on when to use complete pagination (with three specific approaches) and mentions that 'get_campaign' should be used for full details of specific campaigns. However, it doesn't explicitly state when to use this tool versus alternatives like 'get_campaign_analytics' or 'list_lead_lists', nor does it provide exclusion criteria for when not to use it.

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/bcharleson/Instantly-MCP'

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