Recipe-Inhalt ist auf Englisch. Englisches Original lesen →
← Alle Recipes
Phase 16 · Claude Code Hooks·15 min·5 steps

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.

5 steps0%
Du liest ohne Account. Mit Login speichern wir Step-Fortschritt + Notes.

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_search is read-only. crm_log_interaction is NOT strictly idempotent, calling it twice creates two interaction records. The tool_use_id parameter is what fixes this: the CRM dedupes on tool_use_id so 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_search ranks results by relevance + recency. Same query, same ranking. crm_log_interaction is a write, output is just {success: true, id: N}.
  • Side-effect-free without user trigger. crm_search is read-only. crm_log_interaction writes, 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

Memory hook bundle, auto-persiGEO + Crew hook bundles, auto-