CRM hook bundle, auto-lookup customers, log interactions from email drafts
Two mcp_tool hooks for the StudioMeyer CRM that turn customer-mention into instant context and email-draft into automatic interaction log. UserPromptSubmit, PostToolUse on Edit/Write.
CRM hook bundle, auto-lookup customers, log interactions from email drafts
The CRM MCP (studiomeyer-crm, hosted at crm.studiomeyer.io) has 33 tools. Without hooks, every "where do we stand with customer X" question means manually calling crm_search. Every email draft you write means manually calling crm_log_interaction afterwards (and you forget half the time).
Two hooks fix both.
Step 1: UserPromptSubmit hook → auto-lookup on customer mention
When you say "draft a reply to Acme Corp" the assistant should already know who Acme is, pipeline stage, last interaction, open deals, before it writes a single word. Wire it:
{
"UserPromptSubmit": [
{
"hooks": [
{
"type": "mcp_tool",
"server": "studiomeyer-crm",
"tool": "crm_search",
"input": { "query": "${user_prompt}", "limit": 3 },
"timeout": 10,
"statusMessage": "CRM: customer lookup..."
}
]
}
]
}
crm_search does fuzzy matching over company names, contact names, and notes. It returns the top 3 matches. If your prompt mentions "Acme" and there's an Acme Corp in your CRM, the tool result lands in the assistant's context before it composes a response. If there's no match, it returns empty, non-blocking.
This hook fires on EVERY user prompt, which means every prompt costs one CRM lookup. If you don't want that, use bash filtering: only fire when prompt contains capitalized multi-word patterns like "Acme Corp". For now we'll keep it simple, crm_search is fast (under 200ms typical) and returns no-op for non-matching queries.
Step 2: PostToolUse hook on Edit/Write → log email drafts
When you draft an email, usually via Edit/Write on a .md or .txt file with "email" in the path, log that as an interaction:
{
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "mcp_tool",
"server": "studiomeyer-crm",
"tool": "crm_log_interaction",
"input": {
"type": "email-draft",
"summary": "Drafted email at ${tool_input.file_path}",
"tool_use_id": "${tool_use_id}"
},
"timeout": 10,
"statusMessage": "CRM: logging email draft..."
}
],
"if": "Edit(*email*)|Write(*email*)|Edit(*draft*)|Write(*draft*)"
}
]
}
The if-filter narrows the matcher to file paths containing "email" or "draft". This is permission-rule syntax, pipe-separated patterns of Tool(glob). Without the filter, EVERY Edit/Write would log a CRM interaction, which is way too noisy.
Step 3: GDPR + idempotency notes
- Idempotent.
crm_searchis read-only.crm_log_interactionis NOT strictly idempotent, calling it twice creates two interaction records. Thetool_use_idparameter is what fixes this: the CRM dedupes ontool_use_idso re-firing the hook on the same Edit (e.g. via re-run) doesn't duplicate the log. - Fast. Both tools under 300ms typical against a warm Postgres connection.
- Deterministic.
crm_searchranks results by relevance + recency. Same query, same ranking.crm_log_interactionis a write, output is just{success: true, id: N}. - Side-effect-free without user trigger.
crm_searchis read-only.crm_log_interactionwrites, but the user implicitly triggered it by drafting an email. - GDPR. Customer data flows from your Claude Code session to your own CRM tenant on
crm.studiomeyer.io. No third-party sharing. Same tenant-isolation as Memory.
Step 4: Optional, narrow UserPromptSubmit further
If crm_search on every prompt feels too aggressive, swap to a bash hook that only fires on customer-name patterns:
# ~/.claude/hooks/crm-customer-pre-check.sh
#!/usr/bin/env bash
input=$(cat)
prompt=$(echo "$input" | jq -r '.prompt // .user_prompt // ""')
if echo "$prompt" | grep -qE '\b[A-Z][a-z]+ (Corp|GmbH|AG|Inc|Ltd|Solutions|Consulting)\b'; then
echo '{"hookSpecificOutput":{"additionalContext":"Customer-name pattern detected. Suggest calling crm_search before composing response."}}'
fi
Then wire as command type in UserPromptSubmit. Fires only when the prompt contains a recognizable company-name pattern. Trades some recall for less noise.
Step 5: Verify the bundle works
claude
# Type: "draft a quick reply to Acme Corp about pricing"
Check the statusline, "CRM: customer lookup..." should flash. Then check the dashboard at https://crm.studiomeyer.io/dashboard, Acme Corp's last-interaction-timestamp should NOT have changed (no Edit/Write fired yet).
Then drop the draft into a file:
# In Claude Code:
# "save the draft to /tmp/email-acme-pricing.md"
Check the dashboard again, Acme should now have a new "email-draft" interaction logged.
Done
CRM is integrated into your Claude Code workflow without manual crm_search or crm_log_interaction calls. Every customer-mention pre-fetches context, every email-draft logs an interaction.
Source
- Claude Code Hooks Reference
- StudioMeyer CRM MCP Documentation
- Permission-rule syntax for
if-filter: Claude Code Permissions Reference