Help us improve
Share bugs, ideas, or general feedback.
From monday CRM
Audits a CRM board for missing/stale data, then runs a bounded write loop to normalize phone/email/country fields and fill gaps. The 'act' sibling to board-diagnosis's 'report.'
npx claudepluginhub mondaycom/mcp --plugin monday-crmHow this skill is triggered — by the user, by Claude, or both
Slash command
/monday-crm:bulk-data-hygiene [optional: board name or ID][optional: board name or ID]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Audits a CRM board for data gaps, lets the user pick which gaps to fix, then runs a bounded write loop to fix them. The "do" sibling to `board-diagnosis`'s "report." Designed for the recurring "my CRM is messy" pain — Sidekick's #1 negative-sentiment JTBD.
Audits a monday CRM board for missing data, stalled groups, abandoned columns, automation gaps, and data-quality scores. Publishes a prioritized diagnosis doc for CSM/admin action.
Generate a phased implementation plan from a HubSpot audit report. Creates prioritized, sequenced cleanup processes with effort estimates, dependencies, and automation feasibility. Use after running /hubspot-audit.
Syncs marketing data with CRM platforms (Salesforce, HubSpot, Zoho, Pipedrive) with deduplication, field mapping, and compliance checks. Includes bi-directional sync for contacts, deals, and campaigns.
Share bugs, ideas, or general feedback.
Audits a CRM board for data gaps, lets the user pick which gaps to fix, then runs a bounded write loop to fix them. The "do" sibling to board-diagnosis's "report." Designed for the recurring "my CRM is messy" pain — Sidekick's #1 negative-sentiment JTBD.
Flow: Trigger → Audit → User picks gaps → Plan writes → Execute (bounded, idempotent) → Summary.
Hygiene fix — <Mon DD> doc lists every change with Generated by Claude footer + <!-- claude-skill-id: bulk-data-hygiene -->.get_user_context, list_workspaces, search, get_board_info, get_column_type_info, get_board_items_page, board_insights — Gather.change_item_column_values — the core write. Loop with bounded concurrency.create_doc / create_update — publish summary + before/after snapshot.all_monday_api — escape hatch for column types not exposed by change_item_column_values (e.g., bulk write of country columns)./monday-crm:bulk-data-hygiene to fix" — this skill is the natural handoff target.Goal: Fail fast if the user has no monday MCP connection — no partial writes, no guessing.
mcp__monday__get_user_context.user.id + user.name for artifact metadata.Hard safety rail regardless of mode: no deletes, no amount-column writes, no cross-workspace moves, no normalization on columns the user didn't pick. Stage / owner / source / last-touch edits ARE allowed when the user explicitly selected that gap type at Step 4 — bulk-data-hygiene is the canonical place for board-wide column rewrites that other skills point users at.
Goal: Land on a single boardId before reading any data.
boardId directly; string → mcp__monday__search with searchType: "BOARD". One match → use it; multiple → AskUserQuestion.get_user_context → scan relevantBoards + favorites for names matching deals|opportunities|pipeline|leads|sales|contacts|accounts. One candidate → use it; multiple → AskUserQuestion; zero → list_workspaces → search. Still zero → stop with "I don't see a CRM-shaped board in your workspaces. Tell me the board name or ID, or run /monday-crm:workspace-builder to create one."Goal: Compute a gap report grouped by fix-type, with a concrete count per type.
Pull all items via get_board_items_page (paginated up to 5000, then sample with disclosure). For each item, check:
| Gap type | Audit rule | Fixable here? |
|---|---|---|
| Missing email | email column empty AND contact name + company present | ✅ (heuristic guess from company domain — flagged with Source = guessed) |
| Missing phone | phone column empty | ❌ (no source — flag only) |
| Phone format | Phone present but no country code, or contains letters / >2 separators | ✅ (E.164 normalization) |
| Country code missing | country column empty AND phone has parseable country code OR email domain has CCTLD | ✅ |
| Email case | Mixed-case (John.Doe@Acme.COM) | ✅ (lowercase) |
| Stale last-touch | date last-touch >90d AND stage active | ✅ (set to "today" only if user picks; never overwrites a more-recent date) |
| Owner missing | people owner empty AND stage active | ✅ (bulk-assign within a user-picked group/scope) |
| Stage drift | items in non-canonical groups; user explicitly wants normalization | ✅ (bulk-set within user-picked scope) |
| Amount missing | numbers value empty AND stage past "qualifying" | ❌ (flag only — forecast integrity rail) |
| Source missing | status source column empty | ✅ (bulk-set within user-picked scope) |
Resolve columns by type, not English name (same rule as morning-briefing). Skip gap types where the matching column doesn't exist.
Produce the audit summary in chat:
## Audit — <board name>
**Items scanned:** N
**Gaps found:**
- ✅ Fixable (column normalization): <N> phone formats · <N> mixed-case emails · <N> missing country codes · <N> guessable missing emails
- ✅ Fixable (bulk-set within picked scope): <N> missing owners · <N> stale last-touch · <N> missing sources · <N> stage drift
- ⚠️ Flag-only: <N> missing phones (no source) · <N> missing amounts (forecast integrity rail)
Pick which fixable gaps to run, or "all". Flag-only items list in the summary doc — no writes.
AskUserQuestion, multiSelect:
For each bulk-set option, after Step 4 selection, run a scope picker sub-prompt: "Which group / status filter should this apply to? Pick a target value.". Never bulk-set across the entire board without scoping.
If user picks "Guess missing emails", surface a strong warning: "Email guessing uses <first>.<last>@<companydomain> heuristic. ~60% accuracy in our testing. Recommend running only on a Leads board where invalid sends are tolerable, not a Customers board. Continue?"
Hard rule: no fix runs without an explicit Step 4 selection (and a scope-picker confirm for bulk-set options). Empty selection → stop with summary-only.
Goal: Bounded, idempotent loop. Every write is undoable.
For each picked gap type, build a write plan: list of (itemId, columnId, oldValue, newValue, reason).
+ characters.+ AND country column is populated → prepend dial code per country.+ AND country empty AND phone starts with a known country prefix (e.g., 1 for US, 44 for UK) → prepend +.value.toLowerCase(). Only write if changed..uk / .de / .fr etc. and country empty → infer.<firstname>.<lastname>@<domain> lowercase. Add a Source = guessed status flag on the item so the user can filter and verify later. If a source/status column is missing, prompt the user once to add one — never write the email without a verifiability flag.change_item_column_values. Never extend beyond the picked scope.change_item_column_values, batched by item (one call per item updates multiple columns at once). Concurrency: 5 in flight max to avoid rate limits.Updated 50/500. Continuing....Compute oldValue == newValue per cell — never write a no-op (avoids audit-log noise).
create_doc (idempotent — search same-day docs by title + skill-id comment, update in place if found), title Hygiene fix — <Mon DD>. Body:
# Hygiene fix — <board name> · <Mon DD>
<!-- claude-skill-id: bulk-data-hygiene -->
## Writes executed (<N>)
| Item | Column | Old → New | Scope / reason |
|---|---|---|---|
| ... | ... | ... | ... |
## Skipped (<N>)
| Item | Column | Reason |
|---|---|---|
## Flag-only (no writes)
- N items missing phone (no source — manual entry required)
- N items missing amount (forecast integrity rail — review manually)
## Undo
Every write here is reversible. The "Old" column above shows the prior value. To revert, copy the rows you want undone into a follow-up run with `--undo` (coming in v0.2; for v0.1, restore manually using the Old values).
---
Generated by Claude · <ISO timestamp>
Optional β: create_update on a "snapshot" item with the pre-fix state of every touched item, for 30-day rollback window. Only runs if user opted in at Step 4.
One-line chat summary: Cleaned <N> cells across <M> items on <board>. <K> skipped. Doc: <url>.
If any failures: <N> failed (rate limits / permissions). Re-run to retry — already-fixed cells are idempotent.
Generated by Claude · <ISO timestamp> footer + <!-- claude-skill-id: bulk-data-hygiene --> in summary/snapshot doc bodies.oldValue == newValue.| Failure | Behavior |
|---|---|
| Connector missing | Step 0 stops; print install link. |
| No board found | Step 2 stops; suggest workspace-builder. |
| Empty selection at Step 4 | Skip writes; publish summary doc with audit only. |
| 0 fixable gaps | Print "Board's already clean — N flag-only items in summary doc." |
| 429 rate limit | Backoff 3x; halt on third with partial summary. |
| Per-item permission error | Skip, log, continue. |
| Column type changed mid-run | Skip that column on that item, continue. |
| User halt | Keep writes done; emit partial summary with last-written item ID. |
| Email-guess accuracy concern | Strong warning at Step 4; Source = guessed flag on every guessed write. |
<!-- claude-skill-id: bulk-data-hygiene --> + Generated by Claude footer.Source = guessed flag (or were withheld if no flag column existed).