Nose for Leads

MCP Server for Local Business Leads

Nose for Leads ships an MCP server alongside the web app and REST API. Same pipeline, same receipts, no browser required. If you're running outreach through an agent, connect it and run a full campaign end to end: discover, check fit, verify, export.

Why this exists

Most local-lead sourcing assumes a person in a browser describing a target, waiting for results, and downloading a CSV. That's a bad fit for a pipeline an agent is supposed to run without a human clicking through steps. MCP support means an agent can call the same operations a person would through the UI, get typed responses back, and chain the output directly into whatever comes next: a sender, a CRM import, a dedupe pass.

What the server exposes

Nine tools total. Built on the standard MCP Python SDK, four of them drive a typical campaign:

  • translate_icp(text) turns a plain-English target description into a structured query (vertical, geo, category, location) before you start a campaign, and lists anything it couldn't parse.
  • start_campaign(query, vertical, geo, idempotency_key) kicks off discovery and verification for a target, returns a job_id to poll. Pass an idempotency_key and a retried call replays the original job instead of starting a duplicate one.
  • get_campaign_status(job_id) returns queued | running | done | done_partial | failed, plus a summary: totals discovered, sourced, needs-review, and a per-reason breakdown of what got cut.
  • fetch_results(job_id, offset, limit, kept_only) returns the leads themselves, paginated. Set kept_only=false and the same call returns the full receipt: every row, passed or cut, with the reason.

Five more tools handle secondary operations: get_icp_pack (saved vertical/geo templates), add_suppression (block an email or domain from future campaigns), review_lead (mark a specific result kept or discarded after the fact), get_credits (balance and spend ledger), and send_feedback (report a bug or a gap). None of those are required to run a campaign start to finish.

Here's fetch_results called against a finished job, with kept_only=false so both a passed and a cut row come back:

// call
{ "name": "fetch_results",
  "arguments": { "job_id": "job_8f2a1c", "kept_only": false, "limit": 2 } }

// response (2 of 96 rows)
{
  "summary": {
    "total_discovered": 142, "sourced": 96, "needs_review": 11,
    "removed_reasons": { "no_verified_email": 31, "out_of_credits": 4 }
  },
  "total": 96, "credits_spent": 96,
  "rows": [
    { "id": 1042, "domain": "riverside-plumbing.example",
      "email": "info@riverside-plumbing.example",
      "match_status": "validated", "rejection_reason": null,
      "verified_by": "zerobounce", "verifier_verdict": "valid" },
    { "id": 1043, "domain": "oakvalley-plumbing.example",
      "email": null, "match_status": "needs_review",
      "rejection_reason": "no_verified_email",
      "verified_by": null, "verifier_verdict": null }
  ]
}

verified_by names the vendor that actually cleared the address, verifier_verdict is its raw verdict, and rejection_reason says why a row didn't make it, not just that it didn't. Typed JSON. Not a file to parse.

A lead only reaches validated after two passes. First a free, deterministic pre-screen: syntax, role-based prefixes (info@, sales@), disposable or proxy domains, MX-record presence, dedupe. Whatever survives goes to ZeroBounce, a third-party mailbox verifier, which returns one of: valid, invalid, catch-all, disposable, role-based, spamtrap, or abuse. Catch-all gets its own status instead of folding into valid. A catch-all server accepts mail for any address at the domain, so it confirms the domain exists but not that a specific inbox does. That's a known limit of address-level verification generally. Every email source runs through the same ZeroBounce check regardless of where it came from, so nothing skips the pass because of how it was found.

Rate limits and polling

A tenant can run up to five campaigns at once; a sixth start_campaign call queues behind the others rather than failing. It just waits. Bulk verification batches through ZeroBounce's async file API and polls for completion, so get_campaign_status can legitimately sit at running for a few minutes on a large campaign. Poll every several seconds, not in a tight loop. If a call would put you over your key's budget or a shared quota, the error comes back with a retry_after value in the JSON response body rather than an HTTP header, since MCP calls don't carry one the way REST does.

Same billing model, same receipts

One credit per validated lead, nothing charged for a lead that gets cut, whether the campaign started through the web app or an MCP client. Same billing either way. A lead earns validated the same way regardless of who asked, so downstream logic (checking reasons, filtering by confidence, re-running with adjusted filters) can trust that "passed" means the same thing whether a person or an agent requested it.

The receipt distinguishes why a lead didn't ship. no_verified_email means nothing survived the mailbox check. out_of_credits means a lead cleared verification but the account ran out of credits mid-campaign. key_budget_exhausted means the specific API key making the call hit its own spend cap, even though the account overall still had credits. Worth knowing if you're running separate keys with separate budgets across a team or client roster. Those counts roll up into removed_reasons and flagged_reasons on the campaign summary, so attrition shows up by cause instead of one pass/fail number.

What this is not

This isn't a general-purpose scraping API, and it doesn't send anything either. It sources and verifies local-business lead data, then hands off to whatever sender or CRM you're already using for the outreach itself, same as the web app does. Nothing more. It's also a newer part of the product than the core web app, and coverage for MCP-specific workflows is still early. A gap in what the server exposes is useful signal to report, not something to assume is intentional.

A typical run

An agent running outreach for a plumbing client across three metros calls translate_icp once per metro, then start_campaign for each. It polls get_campaign_status until each job resolves, then calls fetch_results twice per job: once for the passed leads, once with kept_only=false for the receipt. A downstream step merges the three exports, dedupes across metros, and hands the list to whatever sender is already in use. No custom scraping or parsing logic required. Same response shape whether one campaign runs or ten run in parallel.

Getting started

Sign up first: every account starts with 25 free validated leads, so the first campaign through the MCP server costs nothing to test. No anonymous tier, no read-only tier beyond that. Every call needs an API key or an OAuth token. Two ways to connect from Claude Code:

API key. Mint one from the dashboard, then:

claude mcp add --transport http nose-for-leads https://api.noseforleads.com/mcp --header "X-API-Key: YOUR-KEY"

OAuth. Nothing to copy or store yourself:

claude mcp add --transport http nose-for-leads https://api.noseforleads.com/mcp
claude mcp login nose-for-leads

The first command registers the server with no credentials attached. The second opens a browser tab to approve access and set a budget cap; the token it issues persists across sessions. Under the hood it's OAuth 2.1 with PKCE and dynamic client registration, so there's no client ID or secret to request in advance. The token persists across sessions and is revocable any time from the Keys page.

Codex uses codex mcp add nose-for-leads --url https://api.noseforleads.com/mcp, which probes for OAuth automatically. Cursor and other clients that take a headers config use the same X-API-Key header shown above.

Once connected, describe a campaign the way you would through the web app and the agent runs discovery, verification, and export as one flow. The web app landing page covers what "verified" means if you want the non-technical version of the same pipeline, and the pay-per-validated-lead breakdown covers how billing works regardless of which interface triggers it.

For agencies wiring this into an outreach stack an agent already runs, the agency use-case page covers how per-campaign billing maps onto running the same playbook across verticals and clients.

FAQ

What can an MCP client actually do with this server? Nine tools total. The four that matter for a campaign: turn a target description into a structured query, start a campaign, check its status, and fetch results (either the passed leads or, with one flag, the full receipt) including cut leads and reasons.

Does authentication require manual API key handling? Not necessarily. You can connect with an API key header, or with OAuth (PKCE, dynamic client registration) via claude mcp add followed by claude mcp login, two commands with no key to copy. The resulting token is long-lived and revocable from the dashboard rather than a short refreshing session.

Is data pulled through the MCP server verified the same way as the web app? Yes. Every email runs through the same free pre-screen and the same ZeroBounce check regardless of whether the campaign was started through the web app, the REST API, or an MCP client. Billing (pay-for-passes, no charge for cut leads) is identical too.

Is this a general lead-scraping API for any use case? No. It's scoped to local-business lead discovery and verification, same as the web app. It doesn't send emails and it isn't a general-purpose web scraper; it hands off the verified list to whatever sender or CRM you're already using.

You describe. The hound tracks it down.

25 free leads to start. Your first list in minutes.