Skip to main content
Glama
enzoemir1

leadpipe-mcp

Export Leads

lead_export
Idempotent

Export leads to CRM, Google Sheets, or file formats. Filter by lead IDs or minimum score to target only qualified prospects.

Instructions

Push leads to an external destination. target must be one of "hubspot", "pipedrive", "google_sheets", "csv", or "json". For CRM targets (hubspot, pipedrive) the respective API key env var must be set (HUBSPOT_API_KEY, PIPEDRIVE_API_TOKEN) — if missing, the tool returns a dry-run payload instead of erroring. Filter the export via lead_ids (explicit list) or min_score (everything above threshold). Returns {target, count, summary, errors?}.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
lead_idsNoExplicit list of lead UUIDs to export. If omitted, every lead matching min_score (or all leads, when min_score is also omitted) is exported.
targetYesWhere to send the leads. "hubspot" / "pipedrive" require HUBSPOT_API_KEY / PIPEDRIVE_API_TOKEN env vars — without them, the tool returns a dry-run payload instead of erroring. "google_sheets" requires GOOGLE_SHEETS_CREDENTIALS. "csv" / "json" produce inline output you can pipe to disk.
min_scoreNoInclusive minimum score for inclusion. Use 60+ for "qualified-only" exports. Ignored when lead_ids is provided.

Implementation Reference

  • Main export handler: orchestrates fetching leads (by IDs or by min_score filter), routing to the correct target (hubspot, pipedrive, google_sheets, csv, json), and returning an ExportResult with summary, counts, and optional errors/data.
    export async function exportLeads(input: LeadExportInput): Promise<ExportResult> {
      const leads = await getLeadsForExport(input);
    
      if (leads.length === 0) {
        return {
          target: input.target,
          exported_count: 0,
          failed_count: 0,
          summary: 'No leads matched the export criteria.',
        };
      }
    
      switch (input.target) {
        case 'hubspot':
          return exportToHubSpot(leads);
    
        case 'pipedrive':
          return exportToPipedrive(leads);
    
        case 'google_sheets':
          return {
            target: 'google_sheets',
            exported_count: leads.length,
            failed_count: 0,
            summary: `Prepared ${leads.length} leads for Google Sheets. Set GOOGLE_SHEETS_CREDENTIALS to enable direct export.`,
            data: { rows: leads.map(formatForCRM) },
          };
    
        case 'csv': {
          const csv = leadsToCSV(leads);
          const ids = leads.map((l) => l.id);
          await markExported(ids);
          return {
            target: 'csv',
            exported_count: leads.length,
            failed_count: 0,
            summary: `Exported ${leads.length} leads as CSV.`,
            data: csv,
          };
        }
    
        case 'json': {
          const ids = leads.map((l) => l.id);
          await markExported(ids);
          return {
            target: 'json',
            exported_count: leads.length,
            failed_count: 0,
            summary: `Exported ${leads.length} leads as JSON.`,
            data: leads,
          };
        }
    
        default:
          return {
            target: input.target,
            exported_count: 0,
            failed_count: 0,
            summary: `Unknown export target: ${input.target}`,
          };
      }
    }
  • src/index.ts:277-300 (registration)
    Tool registration for 'lead_export': registers with title 'Export Leads', description, LeadExportInputSchema, and a handler that checks pro license then calls exportLeads(input).
    // ━━━ TOOL: lead_export ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    server.registerTool(
      'lead_export',
      {
        title: 'Export Leads',
        description:
          'Push leads to an external destination. target must be one of "hubspot", "pipedrive", "google_sheets", "csv", or "json". For CRM targets (hubspot, pipedrive) the respective API key env var must be set (HUBSPOT_API_KEY, PIPEDRIVE_API_TOKEN) — if missing, the tool returns a dry-run payload instead of erroring. Filter the export via lead_ids (explicit list) or min_score (everything above threshold). Returns {target, count, summary, errors?}.',
        inputSchema: LeadExportInputSchema,
        annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
      },
      async (input) => {
        try {
          const reject = await ensureProOrReject(LICENSE_CONFIG, 'lead_export');
          if (reject) return reject;
          const result = await exportLeads(input);
          return {
            content: [{ type: 'text' as const, text: result.summary }],
            structuredContent: result as unknown as Record<string, unknown>,
          };
        } catch (error) {
          return handleToolError(error);
        }
      }
    );
  • LeadExportInputSchema and LeadExportInput type: defines input with optional lead_ids array, target enum (hubspot/pipedrive/google_sheets/csv/json), and optional min_score number.
    /** Input schema for exporting leads to CRM or file. */
    export const LeadExportInputSchema = z.object({
      lead_ids: z.array(z.string()).optional().describe('Explicit list of lead UUIDs to export. If omitted, every lead matching min_score (or all leads, when min_score is also omitted) is exported.'),
      target: ExportTargetSchema.describe('Where to send the leads. "hubspot" / "pipedrive" require HUBSPOT_API_KEY / PIPEDRIVE_API_TOKEN env vars — without them, the tool returns a dry-run payload instead of erroring. "google_sheets" requires GOOGLE_SHEETS_CREDENTIALS. "csv" / "json" produce inline output you can pipe to disk.'),
      min_score: z.number().optional().describe('Inclusive minimum score for inclusion. Use 60+ for "qualified-only" exports. Ignored when lead_ids is provided.'),
    });
    export type LeadExportInput = z.infer<typeof LeadExportInputSchema>;
  • getLeadsForExport: fetches leads by explicit IDs via storage.getLeadById, or filters all leads by min_score and qualified/scored status.
    async function getLeadsForExport(input: LeadExportInput): Promise<Lead[]> {
      if (input.lead_ids && input.lead_ids.length > 0) {
        const leads: Lead[] = [];
        for (const id of input.lead_ids) {
          const lead = await storage.getLeadById(id);
          if (lead) leads.push(lead);
        }
        return leads;
      }
    
      const all = await storage.getAllLeads();
      return all.filter((lead) => {
        if (input.min_score != null && (lead.score == null || lead.score < input.min_score)) {
          return false;
        }
        return lead.status === 'qualified' || lead.status === 'scored';
      });
    }
  • ensureProOrReject: license gate that returns a rejection message if the user doesn't have a Pro license for 'lead_export'.
    export async function ensureProOrReject(
      config: LicenseConfig,
      toolName: string,
    ): Promise<{ content: Array<{ type: 'text'; text: string }> } | null> {
      const status = await validateLicense(config);
      if (status.isPro) return null;
      return {
        content: [{ type: 'text', text: proGateMessage(toolName, status) }],
      };
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations mark idempotentHint true and destructiveHint false. The description adds key behavioral details: dry-run when API keys missing, return payload shape (target, count, summary, errors?). No contradictions.

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?

Three concise sentences front-loaded with the main action, followed by essential details. No redundant or vague statements. Every sentence 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?

Covers parameters, behavior with missing env vars, filtering options, and return structure. Lacks details on error format or summary structure, but the overall description is adequate for a tool with 3 params and no nested objects.

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?

Schema covers 100% of parameters. Description adds value beyond schema by explaining env var requirements for CRM targets, dry-run behavior, and filtering logic (lead_ids vs min_score). This compensates for any ambiguity in the schema.

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 'Push leads to an external destination' and enumerates specific targets (hubspot, pipedrive, google_sheets, csv, json). This distinguishes it from siblings which focus on scoring, ingestion, enrichment, search, etc. No ambiguity.

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?

Provides clear context: when to export, required env vars for CRM targets, and fallback dry-run behavior. However, it does not explicitly state when NOT to use this tool or mention alternatives among siblings, though the purpose differentiation is implicit.

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/enzoemir1/leadpipe-mcp'

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