@agifyai/leadify-mcp
v8.7.4
Published
MCP server for Leadify lead management API
Readme
Leadify MCP Server
Serveur MCP (Model Context Protocol) pour l'API Leadify. Expose les endpoints REST de Leadify sous forme de tools utilisables depuis Claude Desktop, Claude Code, Cursor ou tout client compatible MCP.
Package npm : @agifyai/leadify-mcp
get_mcp_runtime_info est le diagnostic de livraison en lecture seule. Il expose la version exacte du package et du serveur, puis vérifie qu'un organization_id explicitement choisi est accessible à la clé configurée. Il ne lit aucun prospect et n'effectue aucune écriture, aucun envoi, aucune publication ni activation.
📦 Pour les utilisateurs
Aucun clone, aucun build. npx télécharge la dernière version à chaque démarrage de session MCP.
Pré-requis
- Node.js ≥ 18 (
node --versionpour vérifier) - Une clé API Leadify (demander à l'équipe ou la générer dans l'app)
Claude Code
claude mcp add leadify -e LEADIFY_API_KEY=votre-clé-api -- npx -y @agifyai/leadify-mcp@latestLe
--est nécessaire pour queclaude mcp addne tente pas d'interpréter le-ydenpxcomme une de ses propres options.
Vérifier que c'est bien branché :
claude mcp listTu dois voir leadify dans la liste. Dans une session Claude Code, demande "appelle test_api_key" pour valider.
Claude Desktop
Ouvrir le fichier de configuration :
- macOS :
~/Library/Application Support/Claude/claude_desktop_config.json - Windows :
%APPDATA%\Claude\claude_desktop_config.json
Ajouter une entrée leadify dans mcpServers :
{
"mcpServers": {
"leadify": {
"command": "npx",
"args": ["-y", "@agifyai/leadify-mcp@latest"],
"env": {
"LEADIFY_API_KEY": "votre-clé-api"
}
}
}
}Redémarrer Claude Desktop. Les tools Leadify apparaissent (icône marteau dans la zone de saisie).
Mise à jour automatique
Le tag @latest force npx à vérifier la dernière version publiée à chaque lancement de session. Quand un nouveau tool est mergé sur main et publié, il est dispo dès la session suivante — sans git pull, sans rebuild, sans rien.
Pour forcer un rafraîchissement immédiat sans attendre le cache npm :
npm cache clean --forceDépannage
| Symptôme | Cause / solution |
|---|---|
| LEADIFY_API_KEY environment variable is required | La clé n'est pas passée. Vérifier le -e (Claude Code) ou le bloc env (Claude Desktop). |
| 401 Unauthorized sur tous les tools | Clé API invalide ou révoquée. Tester avec test_api_key. |
| Le serveur ne démarre pas | Vérifier que npx -y @agifyai/leadify-mcp@latest tourne en standalone. Si erreur réseau, vérifier l'accès à registry.npmjs.org. |
| Tool ajouté côté équipe mais pas visible chez moi | Quitter complètement le client (Claude Desktop : ⌘Q ; Claude Code : ferme la session) et relancer. |
🛠️ Pour les contributeurs
Setup local
git clone [email protected]:AgifyAI/mcp_leadify.git
cd mcp_leadify
npm install
npm run buildBrancher Claude Code sur ta build locale (en plus de la version npm si tu veux comparer) :
claude mcp add leadify-dev -e LEADIFY_API_KEY=votre-clé-api -- node /chemin/absolu/vers/mcp_leadify/dist/index.jsMode watch :
npm run devCanari de vérité commerciale en lecture seule :
LEADIFY_CANARY_ORGANIZATION_ID=org-id npm run canary:commercialLe canari sélectionne une organisation et un groupe accessibles, relit la même source deux fois, ne publie aucun message et n'ajoute aucune activité. Les variables LEADIFY_CANARY_LEAD_GROUP_ID, LEADIFY_CANARY_EMAIL, LEADIFY_CANARY_LINKEDIN_URL et LEADIFY_CANARY_LEAD_ID permettent de fixer une cible déjà autorisée ; la clé et le contenu des conversations ne sont jamais affichés.
Architecture
src/
├── index.ts # entrée stdio (shebang + transport)
├── server.ts # création du McpServer + register* de chaque module
├── client.ts # LeadifyClient (singleton, lit LEADIFY_API_KEY)
├── types.ts # toolResult, handleToolError, LeadifyApiError
└── tools/
├── auth.ts
├── leads.ts
├── events.ts
├── campaigns.ts
├── context_workspace.ts
├── context_entities.ts
├── personas.ts
└── ... # un fichier = un domaine fonctionnelUn tool = un appel à server.tool(name, description, zodSchema, async handler). Voir src/tools/auth.ts pour le plus simple.
Ajouter un tool
- Coder le tool dans le fichier de domaine pertinent (
src/tools/<domaine>.ts), ou créer un nouveau fichier. - Si nouveau fichier : exporter
registerXxxTools(server)et l'appeler depuissrc/server.ts. - Vérifier que ça compile :
npm run build - Tester en local (cf. setup ci-dessus).
- Bumper la version et publier (cf. section suivante).
Publier une nouvelle version
Chaque push sur main déclenche automatiquement une publication npm distincte. Le workflow sérialise les publications, exécute les tests, puis :
- publie la version de
package.jsonsi elle est supérieure à la version npm courante (pour un changement minor ou major intentionnel) ; - sinon, incrémente automatiquement le patch de la dernière version npm ;
- publie avec Trusted Publishing OIDC, puis ajoute un tag Git
vX.Y.Zsur le SHA exact publié.
Il n'est donc pas nécessaire de bumper le patch à la main. Pour annoncer volontairement une nouvelle minor ou major, modifier la version source avant le merge :
npm version minor --no-git-tag-version # 8.3.x → 8.4.0
npm version major --no-git-tag-version # 8.x → 9.0.0npm version crée un commit + un tag git automatiquement.
Vérifier la publication :
npm view @agifyai/leadify-mcp versionEt le run du workflow : https://github.com/AgifyAI/mcp_leadify/actions
Comment marche la CI
.github/workflows/publish.yml se déclenche sur push main :
- Checkout + install Node 20.
npm cipour installer les deps.- Calcule une version inédite à partir de la version npm courante et de l'éventuelle intention minor/major dans
package.json. - Exécute la suite de tests complète.
- Upgrade npm vers ≥ 11.5.1 (requis pour Trusted Publishing), puis publie via OIDC. La provenance Sigstore explicite reste désactivée tant que npm ne la supporte pas pour les dépôts GitHub privés.
- Pose le tag de version sur le commit
mainpublié.
L'authentification npm passe par Trusted Publishing (OIDC) : pas de token stocké, GitHub Actions s'authentifie directement auprès de npm via la permission id-token: write du workflow. La trust relation est configurée sur la page npm du package (Trusted Publisher : AgifyAI/mcp_leadify / publish.yml).
Conséquences pratiques :
- Pas de secret
NPM_TOKENà rotater. - Le workflow ne peut publier que depuis ce repo + ce fichier de workflow exact. Renommer
publish.ymlcasse la trust → mettre à jour côté npm si besoin.
Conventions
- Versionning : suivre semver. Ajout de tool =
patch(rétrocompatible). Renommage / suppression / signature breaking =major. - Description des tools : verbeuse et précise — c'est ce que le modèle lit pour décider d'utiliser le tool. Voir les tools
outreach_*pour des exemples détaillés. - Erreurs : toujours wrapper le handler dans
try / catchet retournerhandleToolError(error)en cas d'échec — ça normalise les erreurs API en réponse MCP propre.
Sémantique des événements
Une participation est rattachée à l’entreprise canonique (Account), même
lorsqu’un lead_id est fourni au tool. get_leads.event_filter et
list_event_participations ajoutent explicitement VERIFIED lorsque le statut
n’est pas fourni. Pour relire des faits UNVERIFIED, DISPUTED ou REFUTED,
l’agent doit demander ces statuts explicitement. Une donnée incertaine ou
l’absence d’un enregistrement ne constitue jamais une preuve de présence ou de
non-participation.
Les corrections ajoutent un fait immuable et mettent à jour le snapshot
courant dans la même opération ; elles exigent un motif. Il n’existe aucun tool
de suppression de participation ou de preuve. Archiver un événement le retire
des sélecteurs courants sans effacer son historique. Les champs legacy
congress_presence, next_congress, next_congress_date et card_congress
restent disponibles pour compatibilité/export, mais ne sont jamais synchronisés
depuis ce modèle.
Il n’existe plus de score de confiance événementiel : le statut et ses preuves
portent seuls la validation utile. Event.description explique l’audience et
l’utilité de l’événement ; activity_summary explique ce que l’entreprise y
fait, présente ou cible. proof_excerpt reste l’extrait exact destiné aux
agents et à l’audit. record_event_participations sert aussi bien à créer une
participation qu’à ajouter une nouvelle preuve ou à corriger le snapshot.
Pour add_leads et update_lead, location utilise l’objet canonique
{ city, region?, postalCode?, countryCode, street? }. city et le code pays
ISO-2 countryCode sont obligatoires. La projection geo est calculée par
Leadify et ne doit jamais être envoyée par un agent MCP.
Tools disponibles
| Tool | Description |
|------|-------------|
| test_api_key | Vérifier que la clé API configurée est valide (health check). |
| list_lead_groups | Lister les groupes accessibles d'une organisation explicitement sélectionnée, en vue compacte. |
| get_lead_group | Consulter les métadonnées compactes d'un groupe accessible par son ID. |
| create_lead_group | Créer un groupe, choisir son type canonique optionnel et rattacher atomiquement persona_id avec l’offer_id ACTIVE du même tenant. |
| update_lead_group | Modifier un groupe avec réconciliation du schéma ; lorsque le serveur exige un contexte complet, transmettre ensemble persona_id et offer_id sans inférer l’offre. |
| add_leads | Ajouter un ou plusieurs leads à un groupe. |
| upsert_person_leads_with_employment | Créer ou réconcilier atomiquement des Person Leads, PERSON, EMPLOYED_BY, projections et historique, sans effet commercial. |
| create_person_employment_evidence / get_person_employment_evidence | Créer puis relire une preuve d’emploi individuelle tenant-scopée ; transmettre l’evidence.id retourné tel quel à l’upsert Person. |
| get_leads | Rechercher et lister des leads avec filtres, recherche et pagination, dont le critère composé event_filter. |
| get_lead | Récupérer les détails complets d'un lead par son ID. |
| update_lead | Mettre à jour un ou plusieurs champs d'un lead existant. |
| clear_lead_field | Effacer irréversiblement une valeur stockée, sans modifier le schéma du groupe. Exige la confirmation explicite clear_lead_field. |
| list_events | Lister les événements tenant-scoped ; les événements archivés sont masqués par défaut. |
| get_event | Lire un événement partagé et son état d’archivage. |
| create_event | Créer un événement partagé avec une clé d’idempotence. |
| update_event | Modifier ou archiver sans suppression un événement partagé, avec version attendue. |
| list_event_participations | Lister les participations d’entreprise ; filtre explicitement VERIFIED par défaut. |
| get_event_participation | Lire un snapshot courant et son historique ; VERIFIED par défaut, statut incertain à demander explicitement. |
| record_event_participations | Valider à blanc ou enregistrer un lot idempotent de 1 à 100 observations/corrections. |
| preview_reset_sequence_messages / execute_reset_sequence_messages | Prévisualiser puis effacer irréversiblement les messages générés d'un prospect, d'une liste explicite ou d'un groupe explicite. L'exécution exige le digest de preview et une clé d'idempotence. |
| preview_reset_ai_fields / execute_reset_ai_fields | Prévisualiser puis effacer irréversiblement les champs IA d'un prospect, d'une liste explicite ou d'un groupe explicite, sans toucher aux contacts, flags manuels, activités ou campagnes. |
| delete_leads | Supprimer définitivement des leads par leurs IDs. |
| update_schema | Ajouter ou modifier les définitions de champs d'un groupe. |
| delete_columns | Supprimer des colonnes du schéma et des données d'un groupe. Avec force: true, supprimer aussi une clé de données orpheline absente du schéma. |
| update_hidden_columns | Afficher ou masquer des colonnes dans la vue tableau (réversible). |
| list_crm_schema_packs | Lister le catalogue canonique des CRM Schema Packs, leurs domaines et contraintes de compatibilité. |
| add_campaign_log | Enregistrer une entrée de log de campagne pour un lead. |
| get_campaign_logs | Récupérer les logs de campagne avec filtres et pagination. |
| delete_campaign_log | Supprimer une entrée de log de campagne. |
| update_campaign_stats | Mettre à jour les statistiques d'email d'une campagne pour un groupe de leads. |
| create_campaign | Créer une campagne DRAFT mono-canal rattachée à un Lead Group qui possède déjà sa paire Persona–Offre. Aucun persona_id ou contexte indépendant n’est accepté au niveau Campagne. channel (LINKEDIN ou EMAIL) est obligatoire ; start_at inclusif, end_at exclusif et timezone IANA configurent la fenêtre métier. À end_at, Leadify met la campagne en pause réversible (WINDOW_END) sans la clôturer. |
| update_campaign / update_campaign_configuration | Modifier le nom, la description et la fenêtre de toute campagne encore OPEN; le canal reste modifiable uniquement en DRAFT. Prolonger end_at dans le futur ou le supprimer relance automatiquement une pause WINDOW_END après validation du fournisseur, mais jamais une pause manuelle. La fenêtre demandée reste enregistrée si cette validation échoue. La Persona est héritée du Lead Group et n’est pas modifiable au niveau Campagne. |
| delete_campaign | Supprimer irréversiblement une campagne DRAFT ou PAUSED encore OPEN, avec la confirmation explicite delete_campaign. Une campagne active ou clôturée est refusée, et aucun envoi n’est déclenché. |
| list_campaigns | Lister compactement les campagnes d'une organisation explicitement sélectionnée, avec état effectif, fenêtre, motif de pause et clôture. |
| get_campaign | Récupérer les détails d'une campagne, son état effectif, sa clôture et son éventuel rapport final figé, ainsi que ses KPIs temps réel. |
| update_campaign_status | Changer le statut d'une campagne. PAUSED est une pause manuelle réversible ; COMPLETED déclenche la clôture définitive terminale et son rapport final immuable. |
| get_campaign_audience | Lire l'audience explicitement enrôlée d'une campagne dans une organisation explicitement sélectionnée, avec les seuls signaux nécessaires à la décision, sans contenu de message ni envoi. |
| preview_unenroll_campaign_audience | Prévisualiser de façon déterministe les membres explicites sans message pour le canal de leur campagne ou sans aucun canal de contact. Retourne les lead_ids exacts, sans mutation. |
| unenroll_campaign_audience | Désenrôler uniquement la liste complète de lead_ids renvoyée par le preview courant. Relit et refuse toute sélection partielle, étendue ou périmée ; aucun envoi. |
| export_campaign | Exporter les statistiques en CSV ; après clôture définitive, l'export provient du rapport final figé. |
| signal_upsert | Créer ou mettre à jour un signal de business intelligence (INFO, CRITICAL, GOLDEN). Remet le state à active. |
| signal_expire | Expirer un signal (événement périmé). Flip de state uniquement. |
| signal_disable | Désactiver un signal (faux positif / écarté manuellement). Flip de state uniquement. |
| signal_delete | Supprimer définitivement un signal (cas rare : donnée erronée, doublon). |
| add_activity | Journaliser une interaction prospect (LinkedIn, email, call) dans le feed du lead. |
| crm_lookup_person | Lire la personne, les deals et le stage CRM d'une cible explicite, sans écrire dans le CRM. |
| email_read_thread | Lire un thread email borné et ses réponses, exclusions de délivrabilité et ambiguïtés. |
| linkedin_read_conversation | Lire la conversation LinkedIn bornée d'un profil explicitement sélectionné. |
| unipile_read_messages | Lire les messages LinkedIn Unipile bornés d'un profil explicitement sélectionné. |
| leadify_read_activity_feed | Lire l'audit borné d'un lead ; le feed ne constitue jamais une preuve de réponse. |
| get_commercial_truth | Réconcilier CRM, email, LinkedIn, Unipile et activité avec un statut, une fraîcheur et une action recommandée ; aucune écriture ni envoi. |
| compile_context_pack | Compiler en lecture seule un Context Pack canonique depuis une cible explicite (organization, lead_group, campaign, account, person) ou un couple lead + campagne pour WRITE_OUTREACH. Retourne publications courantes, sources, freshness, contradictions et provenance, sans choisir de cible implicite. |
| get_claim_contradiction | Lire une contradiction canonique tenant-scopée avec ses deux claims immuables, leurs preuves, sa version et son éventuelle résolution auditée. |
| resolve_claim_contradiction | Arbitrer explicitement une contradiction relue par sélection d’un claim ou coexistence, avec justification, version, état attendu et clé d’idempotence ; aucun effet commercial. |
| resolve_claim_contradictions_batch | Soumettre jusqu’à 100 arbitrages explicites et relire chaque résultat ordonné, sans choix automatique ni résolution implicite. |
| get_context_workspace | Lire le seul Context Workspace encore exposé et versionné : le company_brain d'une organisation explicitement sélectionnée. Retourne le brouillon et la dernière version publiée ; l'historique GTM reste stocké mais n'est plus exposé. |
| list_verticals / get_vertical | Lister ou lire les Verticales JSON simples d’une organisation explicitement sélectionnée, avec leurs Personas et Offres liées. |
| create_vertical | Créer une Verticale tenant-scoped en DRAFT, sans activation ni sélection implicite. Son schéma JSON fermé porte les règles sectorielles et commercialExperience ; les métadonnées de preuve/source/provenance et les champs propres à l’Offre sont refusés. |
| update_vertical | Modifier le nom et/ou des clés JSON d’une Verticale après relecture. expected_updated_at est obligatoire ; une révision périmée est refusée avec 409 et impose une nouvelle lecture. Une Verticale ACTIVE repasse en DRAFT. |
| change_vertical_status | Passer une Verticale entre DRAFT, ACTIVE et ARCHIVED avec contrôle optimiste, readiness et protection des références. |
| list_offers / get_offer | Lister ou lire les Offres JSON simples tenant-scoped, leurs Verticales et leurs Lead Groups. |
| create_offer | Créer une Offre tenant-scoped en DRAFT, sans relation ni activation implicite. Son schéma JSON fermé est l'unique propriétaire des allégations, résultats démontrés et cas clients ; les Verticales référencent les identifiants de ces cas. |
| update_offer | Modifier le nom et/ou des clés JSON d’une Offre avec expected_updated_at obligatoire et refus 409 de toute écriture périmée. Une Offre ACTIVE repasse en DRAFT. |
| change_offer_status | Passer une Offre entre DRAFT, ACTIVE et ARCHIVED avec contrôle optimiste, readiness et protection des références. |
| link_vertical_offer / unlink_vertical_offer | Créer ou retirer la relation SQL tenant-scoped. Les updatedAt lus pour les deux objets sont obligatoires ; aucune relation cross-tenant ou dépendance Lead Group n’est contournée. |
| preview_resolved_context | Résoudre sans effet Company Brain + Verticale + Offre liée + Persona, ainsi que sender/CTA du Lead Group lorsqu'ils existent. Retourne le digest courant avec dryRun:true, persisted:false, noSend:true, runtimeApplied:false et externalActivation:0. |
| list_canonical_relationships | Lire les liens canoniques typés d'une organisation, dans les deux directions. |
| create_canonical_relationship | Créer un lien canonique tenant-scoped et idempotent entre deux identités fortes. |
| update_canonical_relationship | Modifier, restaurer ou tombstoner un lien via le ledger transactionnel partagé. |
| tombstone_canonical_relationship | Retirer réversiblement un lien sans supprimer son historique ni ses preuves. |
| preview_canonical_relationship_migration | Prévisualiser une migration de champs historiques, divergences et quarantaines comprises, sans mutation. |
| apply_canonical_relationship_migration | Appliquer exactement un plan prévisualisé et borné grâce à son digest. |
| rollback_canonical_relationship_migration | Tombstoner les liens créés par un plan de migration précis. |
| configure_lead_group_relations | Configurer, dans un tenant explicitement vérifié, la projection legacy relue par une migration canonique. |
| preview_canonical_identity_backfill | Prévisualiser le backfill d'identité tenant-scoped d'un groupe et de ses parents Company configurés. |
| apply_canonical_identity_backfill | Appliquer exactement un backfill d'identité relu par digest, sans outreach ni activation. |
| update_company_brain_sections | Modifier uniquement les sections globales encore actives (identity, positioning, allowedVocabulary, prohibitedVocabulary, legalConstraints). Les anciennes sections Offre/Verticale sont filtrées et refusées. |
| publish_context_workspace | Publier une révision prête du Company Brain après confirmation explicite (confirm_publish: true). Admin de l'organisation requis ; les raisons de non-readiness sont renvoyées par le serveur. |
| list_persona_contracts / get_persona_contract | Lister ou lire les contrats Persona canoniques v2 d’une organisation explicitement sélectionnée. intelligence ne contient que painPoints, icpStrategy et cardAnalysisSections ; activeTools n’appartient pas au contrat. |
| create_persona_contract | Créer un Persona tenant-scoped et lié à une Verticale depuis un contrat canonique v2 complet et strict. tool_pack_id est requis à la création et reste hors contrat. Toute clé inconnue est refusée. |
| replace_persona_contract / patch_persona_contract | Remplacer ou modifier un contrat canonique v2 avec verrou optimiste. name / description sont acceptés sur replace. tool_pack_id est optionnel : s’il est omis, le pack persisté est réutilisé. Une Persona ACTIVE mutée repasse en DRAFT. |
| change_persona_status | Passer un Persona entre DRAFT, ACTIVE et ARCHIVED avec le même verrou updated_at, readiness et protection des références. Après un replace/patch, rappeler ce tool vers ACTIVE pour que les groupes résolvent à nouveau la Persona. |
| list_data_sources | Lister toutes les sources de données configurées (par pays puis nom). |
| create_data_source | Créer une nouvelle source de données (admin uniquement). |
| update_data_source | Modifier une source de données existante (admin uniquement). |
| delete_data_source | Supprimer définitivement une source de données (admin uniquement). |
| get_outreach_settings | Récupérer la config outreach d'un lead group (positioning, sequence, rules, URLs, case studies). |
| update_outreach_settings | Update full-form (escape hatch) — remplace les blocs JSON entièrement. Préférer les tools granulaires ci-dessous. |
| update_outreach_positioning | Patch partiel du positioning (dream / fear / whyNow individuellement). |
| update_outreach_sequence_slot | Patch d'un seul slot de séquence (connexion, linkedin.one/two/three, email.one/two/three) sans toucher les autres. |
| update_outreach_rules | Patch du bloc rules : forbidden/priority (replace), format.* (deep-merge). |
| update_outreach_case_study | Ajouter / remplacer / supprimer un case study par index, sans re-envoyer la liste. |
| update_outreach_urls | Patch booking_url et/ou website_url uniquement. |
| set_outreach_connection_request | Toggle du flag connectionRequestEnabled (LinkedIn invite vs cold DM). |
| trigger_outreach | Générer un message via le runtime Write Outreach (dry-run par défaut). Le rédacteur libre peut fournir un context_selection complet Verticale–Offre–Persona ; aucun objet ou défaut n’est inféré. Avant une écriture, le runtime relit le digest et refuse toute persistance si le contexte a changé. |
| append_fine_tuning | Appendre du contenu à une section du Fine Tuning (nonNegotiableRules, pitfalls, structure, examples). Toujours en mode append — garantie contractuelle. |
| set_fine_tuning_output_config | Remplacer uniquement la configuration métier de sorties d’un Fine Tuning (linkedinConnection, linkedinMessage, email, activations et quantités). Préserve Markdown, langues et fallback ; ne génère, ne planifie, n’active ni n’envoie aucun outreach. |
| pipeline_next_lead | Sélectionner le prochain lead à traiter (score descendant, sans message). Exclusion des IDs déjà vus, limit 1-5. |
source-icp-prospects : preuve d’emploi
Pour chaque candidat accepté, créer create_person_employment_evidence avec le tenant, la Company Lead, l’identité Person, la provenance et une clé idempotente. Relire si nécessaire avec get_person_employment_evidence, puis transmettre exclusivement l’evidence.id retourné dans evidence_ids de upsert_person_leads_with_employment.
