Model Context Protocol

The CRM MCP connector for Claude & ChatGPT

Bring your CRM into the AI you already use. agentlyleads ships a Model Context Protocol connector, so Claude and ChatGPT can search your contacts, draft personalized outreach, log conversations from a pasted thread, and run a pipeline report — all scoped to your workspace, all with writes saved as drafts for your approval.

Connector URL
https://agentlyleads.com/api/mcp

Authorization happens in your browser on connect (OAuth 2.1). No API keys to copy around.

Connect Claude

  • claude.ai: Settings → Connectors → Add custom connector → paste the URL → Connect.
  • Claude Code: claude mcp add --transport http agentlyleads https://agentlyleads.com/api/mcp.

Connect ChatGPT

  • Settings → Apps & Connectors → Developer mode.
  • Add a connector with the URL above and approve the OAuth sign-in.

57 tools, one connector

Everything the assistant can do, grouped by area. Read tools answer questions; write tools save drafts and records for a human to approve.

Search & read · 10

  • Fetch record by typed idfetch

    Retrieve the full detail of a single record given a typed id in the form `type:rawId` (e.g. `contact:abc123`, `lead:...`, `company:...`, `deal:...`), exactly as returned by the `search` tool. Returns the complete record including its activity timeline as markdown. An unknown or malformed id returns a readable error.

  • Find a contact by exact emailfind_contact_by_email

    Look up a contact by exact email address (case-insensitive equality — NOT fuzzy search). Use this before log_communication or send_email when you have an email address and must be certain you attach activity to the right person. Returns the single matching contact's id and summary, or a clear 'no match'. If duplicates share the address, all are listed so you can pick.

  • Find likely duplicate contactsfind_duplicate_contacts

    List likely duplicate contact PAIRS in the workspace — same normalized name or same phone number (emails can't collide; duplicates always differ there). Heuristic suspects only: review each pair with the human before merging. Use `merge_contacts` to merge a confirmed pair.

  • Get one record's full detailget_record

    Fetch the complete detail (including activity timeline and related records) for one record identified by its type and raw id. Equivalent to `fetch` but with type and id supplied separately rather than as a combined `type:rawId` string.

  • List calendarlist_calendar

    The CRM calendar for a date range, grouped by day: tasks and appointments, deal close dates, scheduled campaign sends, follow-ups due, contact birthdays, and the user's own connected Google/Outlook events as read-only busy time. Defaults to today through seven days out. Times are shown on the WORKSPACE's clock. Entries that are tasks carry a `task:<id>` handle — use it with `manage_task` to reschedule or complete one. Use this before `schedule_appointment` to find a free slot. Read-only.

  • List taskslist_tasks

    List workspace tasks as a markdown checklist, with due dates and any linked contact or deal. By default returns all tasks (open first, then by soonest due date). Pass `done: false` for only open tasks or `done: true` for only completed ones. Results are capped (default 25, max 100).

  • Schedule appointmentschedule_appointment

    Book something on the CRM calendar: a one-off meeting, an all-day entry, or a repeating series. Times are WALL CLOCK in the workspace's own timezone — write '14:00' for 2pm there, never a UTC instant or an offset. Give `repeat` to make it recur (daily/weekly/monthly, with intervals, and open-ended if you name neither `until` nor `count`); omit it for a one-off. Link it to a contact, lead or deal so it shows on their timeline. Calling this twice books it twice — check `list_calendar` first if you are unsure whether it is already there. Use `manage_task` instead for an all-day to-do with no clock time.

  • Search everythingsearch

    Free-text search across all CRM record types at once — contacts, leads, companies, and deals. Returns a flat markdown list; each result carries a typed id of the form `type:rawId` (e.g. `contact:abc123`), a title, and a one-line subtitle. Pass an id from these results to the `fetch` tool to retrieve the full record. Use this when you do not yet know which record type the user means.

  • Search records of one typesearch_records

    List records of a single chosen type (contact, lead, company, or deal) as a markdown table, optionally narrowed by free-text query and/or status. Status values are type-specific: contacts use NEW/ACTIVE/CUSTOMER/DORMANT; leads use NEW/WORKING/QUALIFIED/UNQUALIFIED/CONVERTED; deals use NEW/QUALIFIED/PROPOSAL/NEGOTIATION/WON/LOST; companies have no status. Contacts and deals can also be filtered by `owner` (a workspace user's email, or "unassigned"). Results are capped (default 25, max 100).

  • Who am Iwhoami

    Return the identity of the currently authenticated CRM user: their name, email, role, and the workspace they belong to. Takes no arguments. Use this to confirm the connection is working and to learn which workspace all other tools operate within.

Contacts & leads · 13

  • Add a noteadd_note

    Add a free-text note to the workspace, optionally attached to a contact. `body` is required. If `contactId` is given it is verified to belong to your workspace. Returns the new note's id.

  • Convert a lead to a contactconvert_lead

    Convert a QUALIFIED lead into a contact. The lead must already be in status QUALIFIED or this fails. On success the lead is marked CONVERTED, a new active contact is created, and the lead's communications move onto the contact. Returns the new contact's typed id.

  • Create a recordcreate_record

    Create a new contact, lead, company, or deal. The `name` field is always required. Other fields are type-specific and optional: contacts/leads accept title/company/email/phone/status (+ leads accept source/estimatedInterest/notes/interestedOfferingId); companies accept domain/industry/size/website/notes; deals accept value/stage/contactId/companyId/offeringId/expectedCloseDate/notes. Contacts and deals also accept `ownerEmail` to assign an owner. Relation ids (contactId, companyId, offeringId) are verified to belong to your workspace. Returns the new record's typed id and a summary.

  • Delete a notedelete_note

    Permanently delete a single note. Prefer update_note to revise; delete only a note that is wrong or no longer applies. Confirm with the human before deleting a note you didn't create in this conversation.

  • Delete a recorddelete_record

    Permanently delete a contact, lead, company, or deal — including its notes and activity timeline. This cannot be undone. Prefer a status update (contact DORMANT, lead UNQUALIFIED, deal LOST) when the history is worth keeping; delete only records that are wrong, duplicated, or dead ends (e.g. a hard-bounced address). Confirm with the human before deleting a record you didn't create in this conversation. The delete is scoped to your workspace; an id from another workspace returns 'not found'.

  • Generate an AI brief for a contactgenerate_brief

    Generate an AI relationship brief for a contact, summarizing their history and recommending a next action plus a draft outreach message. Provide the contact id. This calls the configured AI provider and persists the brief; if no AI API key is configured in this environment, an error message is returned instead.

  • Log a communicationlog_communication

    Record a communication (call, email, note, or meeting) against a contact. `contactId` and `body` are required. `direction` (INBOUND/OUTBOUND) is optional and typically used for calls/emails. `occurredAt` defaults to now if omitted; supply an ISO datetime to backdate. The contact is verified to belong to your workspace.

  • Log a pasted conversation (LinkedIn / meeting / call)log_conversation

    Log a conversation that happened off-platform — a LinkedIn thread, a Zoom/other meeting, or a phone call — onto a person's CRM timeline. The human pastes the raw text (or gives their notes); YOU clean it up. Steps: (1) pick `channel`: "linkedin", "meeting", or "call". (2) Identify the other person — `personName`, and for LinkedIn their profile URL (`linkedinUrl`), `title`, `company`, `email` if visible. (3) Rewrite the messy copy into a clean chronological `transcript` — "Name: message" lines, dropping platform chrome (LinkedIn's "· 3rd", reactions, "sent", duplicated name lines; meeting speaker-timestamp noise). (4) Write a 1-2 sentence `summary` of where it stands / the next step. (5) If the human already told you which contact or lead this is, pass `contactId` or `leadId`; otherwise omit them and a lead is created if the person isn't found. Never invent content that wasn't in the paste. Re-logging the same transcript updates the existing entry (deduped), never duplicates.

  • Create or modify a taskmanage_task

    Create a task or change an existing one. `action` selects the operation: 'create' (requires `title`), 'complete' / 'reopen' (require `id`; toggle the done flag), 'update' (requires `id`; change title/dueDate/contactId/dealId/done), or 'end_series' (requires `id` of ANY occurrence of a repeating task; stops the repeat — upcoming occurrences are removed, completed and past ones are kept). `dueDate` is an ISO 'YYYY-MM-DD' string. To create a REPEATING task here, pass `repeatWeekdays` (day names, e.g. ['mon','wed','fri']) together with `repeatUntil` ('YYYY-MM-DD', the last day the repeat runs); one task is created per matching weekday from today until that date, and `dueDate` is ignored. A repeat in this CRM is not limited to that shape — in the app a repeat can be daily, weekly or monthly, can skip weeks or months (every 2 weeks, every 3 months), and can run open-ended with no last day — so an existing repeating task may be any of those, and 'update' handles it whatever shape it has. Pass `repeat` to create any of those shapes here: daily, weekly or monthly, with an `interval` for every-N, weekdays by name, a monthly day-of-month or nth-weekday position, ending on `until`, after `count` occurrences, or open-ended if you name neither. `repeat` and the older `repeatWeekdays`/`repeatUntil` pair are two ways to say the same thing, so pass one or the other, never both. Everything created here is an ALL-DAY to-do — if it has a clock time, a duration or is a meeting, use `schedule_appointment` instead, which takes the same `repeat`. Every occurrence of a repeat is its own task with its own id, so an 'update' must say WHICH ones it means: `scope` is 'this' (the default — just the one occurrence you named), 'following' (that occurrence and every later one; earlier ones keep the old values) or 'all' (the whole repeat, past occurrences included). `scope` only makes sense on a repeating task — 'following'/'all' on a one-off task is an error — and 'following'/'all' change the repeat itself, so `dueDate` and `done` (which belong to a single occurrence) cannot be combined with them. Any `contactId`/`dealId` is verified to belong to your workspace. Returns the resulting task summary.

  • Merge two duplicate contactsmerge_contacts

    Merge a duplicate contact into a surviving one. ALL history moves to the survivor (communications, emails, deals, tasks, notes, briefs, attachments, tags); blanks on the survivor are backfilled from the duplicate; the duplicate is then DELETED. Irreversible — you MUST confirm the pair and the merge direction with the human first, then pass `confirm: true`. Pick the survivor as the contact with the better email/most history.

  • Today's brieftoday_brief

    What needs attention today, exactly as the CRM's own home screen computes it: a headline count, the pipeline snapshot, and the top-ranked items. Takes no arguments. Each item carries its TYPE and its ID, so act on one directly rather than searching for it by name: a `deal` or `lead` id goes to `get_record` or `draft_outreach`, a `reply` id to `get_email` then `classify_reply`, a `followup` id to `get_email` then `reply_to_email`. The ranking is already computed — read it in the order given. Read-only: this proposes nothing and writes nothing.

  • Update a noteupdate_note

    Replace the text of an existing note (and optionally re-attach it to a different contact). Use this to keep a status/ownership note current instead of adding a correction note next to a stale one. The note id comes from add_note's response or a record's detail view.

  • Update a recordupdate_record

    Partially update an existing contact, lead, company, or deal. Supply the record `type` and `id`, plus only the fields you want to change — omitted fields are left unchanged. Field set mirrors `create_record`. Relation ids are workspace-verified. The update is scoped to your workspace; an id from another workspace returns 'not found'. Returns a confirmation summary.

Deals & catalog · 6

  • Create an offeringcreate_offering

    Add a new offering (catalog item). `name` is required; `type` defaults to PRODUCT (others: SERVICE, SUBSCRIPTION, CONTRACT). `category` is a name and is created automatically if new. `price`/`cost` are USD numbers. For SUBSCRIPTION set `billingPeriod`; for CONTRACT set `termMonths`. Returns the new offering's typed id.

  • Get one offering's detailget_offering

    Fetch the full detail of a single offering (catalog item) by its raw id: type, price, cost, currency, sku, unit, stock, billing, category, description, and more, as markdown. An unknown id or one from another workspace returns 'Offering not found.'.

  • List offering categorieslist_offering_categories

    List the workspace's offering categories with the number of offerings in each, as a markdown table. Use this to see how the catalog is organized before filtering `list_offerings` by category.

  • List offerings (catalog)list_offerings

    List the workspace's offerings — its product/service catalog — as a markdown table. An offering's `type` is one of PRODUCT, SERVICE, SUBSCRIPTION, or CONTRACT (a 'product' is an offering of type PRODUCT). Optionally filter by free-text `query` (name/sku/description/category), `type`, `category` name, and `activeOnly`. Capped (default 25, max 100). Use this to answer 'what products/offerings do I have?'.

  • Sales pipeline reportpipeline_report

    Summarize the deal pipeline for the workspace: open deal count and value, won count and value, win rate, and a per-stage breakdown. Takes no arguments. Returns markdown.

  • Update an offeringupdate_offering

    Partially update an offering. Supply `id` plus only the fields to change (same fields as create_offering). `category` (a name) is resolved/created. Omitted fields are left unchanged. A foreign or unknown id returns 'Offering not found.'.

Email & outreach · 15

  • Classify a received replyclassify_reply

    Record your classification of a received (INBOUND) email. INTERESTED replies feed the campaign positive-reply rate — the metric that says which pitch works. Re-classifying overwrites.

  • Delete a drafted email (drafts only)delete_email_draft

    Permanently delete a single DRAFT email (and its attachments). Only DRAFTs can be deleted — sent and failed emails are immutable history. Use this to clean up a bad draft without deleting the whole campaign. Confirm with the human before deleting a draft you didn't create in this conversation.

  • Generate personalized drafts for a matchdraft_outreach

    Stage 2 of context-aware outreach. For every INCLUDED candidate of a match, the AI writes a customized email grounded in that contact's timeline and the offering, and saves it as a DRAFT grouped under an 'Outreach: <offering>' campaign. Nothing is sent — drafts land in the draft-first review queue where a human edits/approves them. Idempotent: candidates already drafted are skipped. Provide the raw match id.

  • Read one email (draft or sent) by idget_email

    Fetch a single email by its raw id — subject, From/To, status, direction, campaign, contact, any send error, attachments, and the full HTML body. Works for DRAFTs too, so you can verify exactly what a draft contains before a human approves it (read-back QA). Ids come from send_email/reply_to_email responses, get_campaign's email list, or the app's Emails list.

  • List emails (drafts and sent)list_emails

    List the workspace's emails as a markdown table — id, To, subject, From, status, campaign, created/sent time. Filter by `contactId`, `campaignId`, and/or `status` (DRAFT / QUEUED / SENT / FAILED). Use it to find pending drafts awaiting approval, recover a draft id, or review what a contact has been sent. Read one email's full body with get_email. Results are capped (default 25, max 100).

  • List threads that are due a follow-uplist_followups_due

    Find conversations that went quiet: threads whose LAST message is an outbound send with no reply for `quietDays` (default 3), no follow-up draft queued yet, and fewer than 3 unanswered touches (after 3 we stop nagging). Each row carries the email id to reply to, the contact (with their saved context), days quiet, nudges already sent, and a snippet of the last message. To nudge a thread: call `reply_to_email` with that email id and a SHORT personalised follow-up — use the contact's context, reference the original offer, and NEVER invent new offers, quantities, or prices. Drafts are approval-gated: the human reviews them (Emails list) and then you send with `send_draft`.

  • Get contacts + timeline context for an offering (for you to match & draft)list_offering_prospects

    Read-only batch context for model-driven outreach. Given an offering id, returns the offering details plus every emailable, non-suppressed contact with a compact digest of their CRM timeline (recent notes, emails, meetings) — in ONE call. Use this when YOU (the assistant) want to judge which contacts fit the offering and write customized outreach yourself, rather than the server's AI: read each prospect's digest, decide fit, then call `send_email` per good-fit contact (it saves a DRAFT for human approval). This needs no server-side AI key, so it works from claude.ai and the desktop app. `limit` caps how many prospects are returned (default 50).

  • List the workspace's From addresseslist_sending_identities

    List the addresses this workspace can send email from — its verified sending domains and connected mailboxes (Gmail, Microsoft, or SMTP/Amazon WorkMail). Pass one of these as `from` on `send_email`, or as `fromEmail` on `create_campaign` / `create_sequence`, to send from that mailbox instead of the default. Takes no arguments.

  • List replies awaiting classificationlist_unclassified_replies

    Received (INBOUND) emails that have not been classified yet, newest first, with a snippet of each. Read each one and call `classify_reply` with your judgement — the classifications feed the positive-reply rate on campaign analytics and follow-up prioritization. Classify in the same pass as checking replies.

  • Find contacts suitable for an offeringmatch_offering_to_contacts

    Stage 1 of context-aware outreach. Given an offering (product/deal) id, the AI reads each emailable contact's CRM timeline (notes, prior emails, meetings) and scores how well the offering fits what they actually buy. Returns a ranked list of candidates with a fit score, tier (STRONG/POSSIBLE/WEAK), a one-line reason, and a suggested personalization angle. Contacts scoring above the threshold are marked INCLUDED by default. Nothing is sent or drafted here — use `set_candidate_decision` to adjust who's included, then `draft_outreach` to generate the emails. Provide the raw offering id.

  • Draft a reply to an existing email (requires human approval to send)reply_to_email

    Reply to a specific email in the workspace. The human copies an email's id from the Emails list (the 'Ref' chip) and gives it to you. Writes a DRAFT reply to that email's contact, in the SAME conversation thread, with a 'Re:' subject — nothing is sent until a human approves it (draft-first). Use this to continue an existing thread; use send_email to start a new conversation. Pass the id of the email being replied to and the reply body.

  • Send a drafted email (transmits real email)send_draft

    Send a specific email DRAFT (created earlier by send_email) — this transmits a REAL email to the recipient. Draft-first is the default: only call this after the human has reviewed and explicitly approved sending this exact draft (e.g. they said "send it"). Pass the single draft id from send_email's response — never send in bulk, and never send a draft the human hasn't approved. If you're unsure, share the review link from send_email and ask first. Returns the send status (SENT / FAILED).

  • Draft an email to a contact (requires human approval to send)send_email

    Compose an email to a contact. It is ALWAYS saved as a DRAFT — nothing is transmitted until a human reviews and approves it in the app (draft-first policy). The recipient address is the contact's own email on file — never a client-supplied address. Every email is grouped under a campaign for tracking: pass `campaign` with a short title you choose from the email's purpose (e.g. "Leaf Blower outreach") to group related emails together; omit it and the email lands in the workspace's standing "Direct Outreach" campaign. To attach files (e.g. a PDF sell-sheet or one-pager), pass `attachments` — each either as base64 bytes (`contentBase64`) or as a public https `url` the server downloads for you (preferred for anything over ~100 KB). The email also appears on the contact's timeline once sent. The HTML body is stored verbatim — write email-client-safe HTML: inline styles only (no <style> blocks or CSS classes), and for tabular data use a simple <table> with border/cellpadding attributes or aligned text lines.

  • Include or exclude an outreach candidateset_candidate_decision

    Set whether a matched contact is INCLUDED or EXCLUDED before drafting. Provide the raw candidate id (from `match_offering_to_contacts`) and the decision. Only INCLUDED candidates get a draft in `draft_outreach`.

  • Update a drafted email (subject / body / From / campaign)update_email_draft

    Edit a DRAFT email in place — fix its subject, body, From address, or re-file it under a different campaign — instead of deleting and recreating it. Only DRAFTs are editable; sent emails are immutable history. Use get_email afterward to verify what was stored. The From must be one of list_sending_identities.

Campaigns & sequences · 11

  • Attach a file to a campaignadd_campaign_attachment

    Attach a file (up to 10 MB) to a DRAFT or SCHEDULED campaign — either as base64 bytes (`contentBase64`) or as a public https `url` the server downloads (preferred for anything over ~100 KB; base64 wastes the context window). The file is sent with every recipient email when the campaign sends. Provide the raw campaign id.

  • Create a draft campaigncreate_campaign

    Create a DRAFT email campaign. `segmentType` is 'tag' (send to all contacts with a given tag) or 'static' (send to a fixed list of contact ids). For 'tag', supply `tagName` (case-insensitive exact match against workspace tags). For 'static', supply `contactIds`. Use `update_campaign` to revise a draft's copy or audience, and `launch_campaign` to actually send.

  • Create an email/task sequencecreate_sequence

    Create a new outreach sequence with one or more ordered steps. Each step is EMAIL (requires `subject` + `bodyHtml`) or TASK (requires `taskTitle`). `delayDays` is the number of days after the previous step (or enrollment for step 0) before this step runs. Returns the new sequence id and step count. Use `enroll_in_sequence` to start contacts on it.

  • Delete a draft campaigndelete_campaign

    Permanently delete a DRAFT campaign that was never sent (e.g. an abandoned or duplicate draft). Only DRAFT campaigns can be deleted — to stop a scheduled or sent campaign, that carries history, cancel it instead. This cannot be undone.

  • Enroll contacts in a sequenceenroll_in_sequence

    Enroll one or more contacts into a sequence. Enrolled contacts begin receiving the sequence's scheduled steps. Contacts without an email, suppressed contacts, or already-enrolled contacts are automatically skipped. Returns enrolled and skipped counts.

  • Get one campaign's detail and analyticsget_campaign

    Fetch the full detail and delivery analytics for a single campaign by its raw id. Returns campaign metadata (name, subject, status, segment) plus recipient/delivery stats. An unknown or foreign id returns 'Campaign not found.'.

  • Get one sequence's detail and statsget_sequence

    Fetch the full detail of a single sequence (including its ordered steps) plus enrollment stats. An unknown or foreign id returns 'Sequence not found.'.

  • Launch a campaign (materializes drafts for approval)launch_campaign

    Launch a DRAFT campaign now or schedule it (`scheduledAt`, ISO 8601). Launching does NOT transmit email: it materializes one DRAFT email per recipient, which a human reviews and approves in the app (Emails page or campaign view) before anything is sent. Returns the queued recipient count and new status.

  • List campaignslist_campaigns

    List email campaigns in the workspace as a markdown table, showing id, name, status, and scheduled/sent time. Results are capped (default 25, max 100). Statuses: DRAFT, SCHEDULED, SENDING, SENT, CANCELED.

  • List sequenceslist_sequences

    List email/task sequences in the workspace as a markdown table: id, name, status, step count, and total enrollments. Results are capped (default 25, max 100).

  • Edit a draft campaignupdate_campaign

    Revise a DRAFT or SCHEDULED campaign in place — its name, subject, body, or audience — instead of creating a new one. Only works before the campaign has started sending; once SENDING/SENT it is locked (create a new campaign instead). For the audience, pass `segmentType` with `tagName` (for 'tag') or `contactIds` (for 'static'). Only the fields you pass are changed. Returns the updated campaign.

Admin & sync · 2

  • HubSpot sync status, and optionally trigger a synchubspot_sync

    Report this workspace's HubSpot integration status (connected, enabled, last synced, last error, last summary). With trigger=true, start an incremental two-way sync now (workspace OWNER only).

  • Zoho sync status, and optionally trigger a synczoho_sync

    Report this workspace's Zoho CRM integration status (connected, last synced, last error, last summary). With trigger=true, start an incremental two-way sync now (workspace OWNER only).

Questions

What is a CRM MCP connector?
It is a Model Context Protocol server that exposes your CRM as tools an AI assistant can call. Connect it once in Claude or ChatGPT and the assistant can search records, draft emails, log conversations, and report on your pipeline on your behalf.
Does it work with both Claude and ChatGPT?
Yes. The same connector is a Claude custom connector and a ChatGPT connector. It uses OAuth 2.1, so you sign in with your existing CRM account and approve access in your browser.
Can the AI send emails without my approval?
No. Every write is confirmation-gated and outbound emails are always saved as drafts. A human reviews and approves before anything is sent.

Already have an account? Connect it from Settings → AI connector.