# MCP Outreach

> Creates outreach sequences (email, LinkedIn, or multi-channel) for Leadspicker projects through the Leadspicker MCP server. Triggers on: "sequence", "outreach", "campaign", "email sequence", "LinkedIn sequence", "multi-channel", "follow-up", "sekvence", "kampaň", "oslovení", "cold email", "connection request", "inmail", "message sequence". Builds the whole sequence graph in a single MCP `create_sequence` call (never chains step-by-step).

- Skill: `tomasblatak/mcp-outreach` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tomasblatak/mcp-outreach`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomasblatak/mcp-outreach/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: tomasblatak (https://skillmd.com/u/tomasblatak)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tomasblatak/mcp-outreach

---


# Leadspicker Outreach (MCP)

Creates outreach sequences in Leadspicker projects through the Leadspicker MCP server. The
entire sequence is built in **one** `mcp__leadspicker__create_sequence` call — never chain
multiple `create_sequence_step` calls when constructing a new sequence.

**Three sequence types:**

1. **Email** — cold email + follow-ups in the same thread.
2. **LinkedIn** — connection check → messages, or connection request → messages / InMail.
3. **Multi-channel** — emails + LinkedIn together.

---

## MCP tools used

| MCP tool                                  | Purpose                                                                            |
| ----------------------------------------- | ---------------------------------------------------------------------------------- |
| `mcp__leadspicker__list_projects`         | Look up project by name (`search_query`) or list recent (`order_by="last_active"`) |
| `mcp__leadspicker__get_project`           | Fetch project + `headers_data` to find boolean column IDs (`info_type="detail"`)   |
| `mcp__leadspicker__list_contacts`         | Page contacts (`page=1, page_size=5`) to see what variables exist                  |
| `mcp__leadspicker__list_sequence`         | List steps in an existing sequence (`project_id`)                                  |
| `mcp__leadspicker__delete_sequence`       | Delete the entire sequence for a project (pass `project_id`, no `sequence_id`)     |
| `mcp__leadspicker__create_sequence`       | **Create the whole sequence in one call** (`project_id`, `sequence_messages`)      |
| `mcp__leadspicker__update_sequence_step`  | Patch a single existing step (legacy; only for edits to an already-saved sequence) |
| `mcp__leadspicker__get_sequence_settings` | Read / update sending hours, timezone, daily limits                                |

---

## Step types — use EXACTLY these values

| Step                        | `outreach_step_type`        | Notes                                                                              |
| --------------------------- | --------------------------- | ---------------------------------------------------------------------------------- |
| Boolean condition gate      | `"magic_column_condition"`  | Filters by a boolean magic column. Uses `condition: [header_id]`.                  |
| Email                       | `""` (empty string)         | NOT `"email"` — empty string                                                       |
| Delay / wait                | `"delay"`                   | NOT `"wait"`. Uses `delay_days` (and optionally `delay_hours`).                    |
| Check 1st-degree connection | `"first_degree_connection"` | Branching: `yes` (connected) / `no` (not connected).                               |
| Send connection request     | `"connect"`                 | NOT `"connection_request"`.                                                        |
| Wait for acceptance         | `"after_connection"`        | Branching: `yes` (accepted) / `no` (not accepted). Uses `delay_days` for max wait. |
| LinkedIn message (DM)       | `"message"`                 | Only works with connected contacts.                                                |
| InMail                      | `"inmail_message"`          | NOT `"inmail"`. Premium LinkedIn only — ask the user.                              |

### Boolean condition gate (`magic_column_condition`)

Add condition gates as the first steps of the sequence when the project has boolean magic
columns. Each condition is a separate step with `condition: [header_id]` (one ID per step).
Chain them with `relation_type: "yes"`.

**Find boolean column header IDs** by calling `mcp__leadspicker__get_project(project_id=X)`
and looking in `headers_data` for entries with `is_boolean: true` — use the `id` field.

### Parent references

Inside `sequence_messages`, steps reference parents by named string keys (or numeric IDs of
already-saved steps):

```json
"parents": ["delay-step"]                                      // simple chain
"parents": [{"parent": "wait-accept", "relation_type": "yes"}] // YES branch
"parents": [{"parent": 4567, "relation_type": ""}]             // existing step ID
```

- `relation_type`: `""` (sequential), `"yes"` (positive branch), `"no"` (negative branch)
- Root steps have no `parents` field.
- Position is auto-calculated.

---

## Sequence type 1 — Email

```
EMAIL 1 (root, is_reply=false) — new thread
  → DELAY (3 days)
    → EMAIL 2 (is_reply=true) — same thread
      → DELAY (3 days)
        → EMAIL 3 (is_reply=true) — same thread
```

```json
{
  "sequence_messages": {
    "email-1": {
      "subject": "{subject_line}",
      "message": "<div>{email_body_html}</div>",
      "outreach_step_type": "",
      "is_reply": false
    },
    "delay-1": { "outreach_step_type": "delay", "delay_days": 3, "parents": ["email-1"] },
    "email-2": {
      "subject": "Re: {subject_line}",
      "message": "<div>{follow_up_body_html}</div>",
      "outreach_step_type": "",
      "is_reply": true,
      "parents": ["delay-1"]
    },
    "delay-2": { "outreach_step_type": "delay", "delay_days": 3, "parents": ["email-2"] },
    "email-3": {
      "subject": "Re: {subject_line}",
      "message": "<div>{final_follow_up_body_html}</div>",
      "outreach_step_type": "",
      "is_reply": true,
      "parents": ["delay-2"]
    }
  }
}
```

| Field                | Value                                                | Notes                           |
| -------------------- | ---------------------------------------------------- | ------------------------------- |
| `outreach_step_type` | `""`                                                 | Empty string for email          |
| `subject`            | `"Subject"` (first email) / `"Re: ..."` (follow-ups) | Only first email gets a subject |
| `message`            | `"<div>HTML</div>"`                                  | HTML format                     |
| `is_reply`           | `false` (new thread) / `true` (same thread)          |                                 |

---

## Sequence type 2 — LinkedIn

```
CONNECT (root) — send connection request
  → AFTER_CONNECTION (wait up to 7 days)
    ├── YES (accepted): MESSAGE 1 → DELAY → MESSAGE 2
    └── NO (not accepted): INMAIL (premium) → DELAY → INMAIL 2
```

```json
{
  "sequence_messages": {
    "connect": {
      "message": "{connection_request_text}",
      "outreach_step_type": "connect"
    },
    "wait-accept": {
      "outreach_step_type": "after_connection",
      "delay_days": 7,
      "parents": ["connect"]
    },
    "accepted-msg-1": {
      "message": "{message_body_1}",
      "outreach_step_type": "message",
      "parents": [{ "parent": "wait-accept", "relation_type": "yes" }]
    },
    "accepted-delay": { "outreach_step_type": "delay", "delay_days": 5, "parents": ["accepted-msg-1"] },
    "accepted-msg-2": {
      "message": "{follow_up_body}",
      "outreach_step_type": "message",
      "parents": ["accepted-delay"]
    },
    "not-accepted-inmail-1": {
      "subject": "{inmail_subject}",
      "message": "{inmail_body}",
      "outreach_step_type": "inmail_message",
      "parents": [{ "parent": "wait-accept", "relation_type": "no" }]
    },
    "not-accepted-delay": { "outreach_step_type": "delay", "delay_days": 5, "parents": ["not-accepted-inmail-1"] },
    "not-accepted-inmail-2": {
      "subject": "{inmail_subject_2}",
      "message": "{inmail_follow_up_body}",
      "outreach_step_type": "inmail_message",
      "parents": ["not-accepted-delay"]
    }
  }
}
```

| Field     | Value    | Notes                                        |
| --------- | -------- | -------------------------------------------- |
| `message` | `"text"` | LinkedIn message body — plain text, NOT HTML |
| `subject` | `"text"` | Required only for InMail                     |

**InMail — premium only.** Always ask the user about LinkedIn Premium before adding
`inmail_message` steps. Non-premium → skip InMail entirely.

---

## Sequence type 3 — Multi-channel (email + LinkedIn)

### ⚠️ MANDATORY: always check `first_degree_connection`

NEVER skip the `first_degree_connection` step in multi-channel sequences. It MUST come
before any LinkedIn `connect` / `message` step.

```
CONDITIONS (optional) — filter by boolean columns
  → EMAIL 1 → DELAY → FOLLOW-UP EMAIL → DELAY
    → FIRST_DEGREE_CONNECTION
      ├── YES (already connected): MESSAGE 1 → DELAY → MESSAGE 2
      └── NO (not connected): CONNECT → AFTER_CONNECTION (7 days)
            ├── YES (accepted): MESSAGE 1 → DELAY → MESSAGE 2
            └── NO (not accepted): INMAIL (premium) → DELAY → FINAL EMAIL
```

### Premium vs. non-premium

- **Premium:** include InMail in NO-not-accepted branch.
- **Non-premium:** skip InMail, jump straight to the final email.

### Full payload (with conditions + InMail — 18 steps)

```json
{
  "sequence_messages": {
    "check-icp": {
      "outreach_step_type": "magic_column_condition",
      "condition": [387407]
    },
    "check-language": {
      "outreach_step_type": "magic_column_condition",
      "condition": [387411],
      "parents": [{ "parent": "check-icp", "relation_type": "yes" }]
    },
    "first-email": {
      "subject": "{subject_line}",
      "message": "<div>{email_body_html}</div>",
      "outreach_step_type": "",
      "is_reply": false,
      "parents": [{ "parent": "check-language", "relation_type": "yes" }]
    },
    "delay-after-email-1": { "outreach_step_type": "delay", "delay_days": 3, "parents": ["first-email"] },
    "followup-email": {
      "subject": "Re: {subject_line}",
      "message": "<div>{follow_up_html}</div>",
      "outreach_step_type": "",
      "is_reply": true,
      "parents": ["delay-after-email-1"]
    },
    "delay-after-email-2": { "outreach_step_type": "delay", "delay_days": 3, "parents": ["followup-email"] },
    "check-connected": {
      "outreach_step_type": "first_degree_connection",
      "parents": ["delay-after-email-2"]
    },
    "already-connected-msg": {
      "message": "{li_message_for_connected}",
      "outreach_step_type": "message",
      "parents": [{ "parent": "check-connected", "relation_type": "yes" }]
    },
    "already-connected-delay": { "outreach_step_type": "delay", "delay_days": 3, "parents": ["already-connected-msg"] },
    "already-connected-msg-2": {
      "message": "{li_followup_for_connected}",
      "outreach_step_type": "message",
      "parents": ["already-connected-delay"]
    },
    "not-connected-request": {
      "message": "{connection_request_text}",
      "outreach_step_type": "connect",
      "parents": [{ "parent": "check-connected", "relation_type": "no" }]
    },
    "wait-accept": {
      "outreach_step_type": "after_connection",
      "delay_days": 7,
      "parents": ["not-connected-request"]
    },
    "accepted-msg": {
      "message": "{li_message_after_accept}",
      "outreach_step_type": "message",
      "parents": [{ "parent": "wait-accept", "relation_type": "yes" }]
    },
    "accepted-delay": { "outreach_step_type": "delay", "delay_days": 3, "parents": ["accepted-msg"] },
    "accepted-msg-2": {
      "message": "{li_followup_after_accept}",
      "outreach_step_type": "message",
      "parents": ["accepted-delay"]
    },
    "not-accepted-inmail": {
      "subject": "{inmail_subject}",
      "message": "{inmail_body}",
      "outreach_step_type": "inmail_message",
      "parents": [{ "parent": "wait-accept", "relation_type": "no" }]
    },
    "not-accepted-delay": { "outreach_step_type": "delay", "delay_days": 3, "parents": ["not-accepted-inmail"] },
    "final-email": {
      "subject": "{final_subject}",
      "message": "<div>{final_email_html}</div>",
      "outreach_step_type": "",
      "is_reply": false,
      "parents": ["not-accepted-delay"]
    }
  }
}
```

---

## Message generation — BASHO framework

### Philosophy

Short, relevant, trigger-led messages that read like a real person wrote them. Tied to a
specific reason for reaching out. Never generic templates.

**Core principles:**

- **Relevance beats personalization.** Tie the message to _why now_, not "Congrats on…".
- **One idea per email/message.** Don't stack problems, features, and asks.
- **Mobile-first.** Short lines, white space, fast to understand.
- **Write like you talk.** Simple words, short sentences.
- **No cheesy openers.** NEVER use "I hope you're doing well", "I hope this finds you well", or any throat-clearing phrases.

### Pre-generation checklist

Before generating any messages, complete these in order:

1. **What is the user selling / offering?** Product, service, value prop, ICP, problem solved.
2. **Preferred language?** Default English. If non-English, all messages in that language.
3. **LinkedIn Premium?** Determines connection request text (blank vs ≤200 chars) and InMail availability.
4. **(LinkedIn / multi-channel only) Sender name?** LinkedIn has no auto-signature — ask the user for the exact name to sign with (e.g. `"Tomáš Blaťák"`). Do NOT use recipient variables.
5. **Fetch available variables.** Call `mcp__leadspicker__list_contacts(project_id=X, page=1, page_size=5)` to see all columns. Look for:
   - Standard: `{{first_name}}`, `{{last_name}}`, `{{full_name}}`, `{{position}}`, `{{company_name}}`
   - Enriched: `{{linkedin_company_description}}`, `{{website_text_summary}}`, `{{linkedin_about_me}}`, `{{present_experiences}}`
   - **Personalization** (from mcp-personalizer): `{{website_hook}}`, `{{company_hook}}`, `{{experience_hook}}`, `{{icebreaker}}`, `{{salutation}}`, etc.
   - Any custom AI columns from mcp-classification / mcp-personalizer

### BASHO message structure

Every message follows this 4-part structure:

**1) Opener — "Reason I'm reaching out"** (1–2 lines)

- Sales trigger (funding, hiring, new product, leadership change, expansion)
- Observation (something specific on their site, pricing, messaging, reviews, job posts)
- Peer signal (competitor / adjacent brand doing X)
- Or use a personalization variable: `{{website_hook}}`, `{{icebreaker}}`
- Goal: establish "this isn't a template" immediately.

**2) Reframe — make them think** (1–2 lines)

- Pattern interrupt question: "Curious if you're seeing X…"
- Simple outcome statement: "Teams use us to reduce X / improve Y without Z."
- Goal: create curiosity without pitching features.

**3) Proof — "Others like you succeeded"** (1 line)

- "Teams like [type] are seeing…"
- "We helped a similar [industry] team…"
- "Commonly we see X within Y weeks…"
- 1 line max — no case-study essays.

**4) CTA — low-friction next step** (1 line)

- NOT "book a demo". Instead:
  - "Worth a 10-min chat to compare notes?"
  - "Open to me sending over a quick teardown?"
  - "Should I speak with you or someone else on this?"
- One clear CTA only.

### Word counts

| Step                                       | Word count  | Notes                                               |
| ------------------------------------------ | ----------- | --------------------------------------------------- |
| Initial email (`is_reply=false`)           | 150–200     | Full BASHO                                          |
| Second email follow-up (`is_reply=true`)   | 100–150     | Adds one new insight / proof                        |
| Final email follow-up (`is_reply=true`)    | 50–100      | Breakup, close-the-loop                             |
| LinkedIn DM (`message`)                    | 150–200     | Full BASHO                                          |
| LinkedIn follow-up DM                      | 100–150     | Adds new value, shorter                             |
| **Connection request — non-premium**       | **BLANK**   | Non-premium gets only 5 messages/month; leave empty |
| Connection request — premium               | ≤ 200 chars | Trigger + relevance only                            |
| InMail (`inmail_message`)                  | 150–200     | Full BASHO, subject required                        |
| Email fallback (multi-channel last branch) | 50–100      | Breakup, references previous outreach               |

### Follow-up rules

- **Second follow-up:** NOT "bumping this". Add something NEW — a sharper insight, a tiny teardown, a relevant example. Reference the prior message briefly, then pivot to new value.
- **Final follow-up / breakup:** Polite close-the-loop. "If the timing isn't right, no worries — just wanted to close the loop." Give them an easy out, leave the door open. Can include one final micro-value or observation.

### Spintax

Use spintax `{option1 | option2 | option3}` for text rotation. Required spintax locations:

1. **Greeting** — always: `{Hi | Hey | Hello}`, or in target language: `{Dobrý den | Zdravím}`.
2. **Closing** — always: `{Best | Cheers | Kind regards | Thanks}`, or in target language: `{S pozdravem | Díky | Přeji hezký den}`.

Plus 2–4 additional locations per message:

- Opener: `{I noticed | I saw | Caught my eye that}`
- Reframe: `{Curious if | Wondering whether | Quick question —}`
- Proof intro: `{Teams like yours | Similar companies | Others in your space}`
- CTA: `{Worth a quick chat? | Open to connecting? | Would 10 minutes make sense?}`
- Transitions: `{That said | With that in mind | On that note}`

**Total: at least 4–6 spintax locations per message** (2 required + 2–4 additional).

### Subject lines

- Spintax for rotation, short (3–8 words), curiosity-driven, no clickbait.
- Only the **first email** (`is_reply=false`) has a subject. Follow-ups (`is_reply=true`) have empty subject (same thread).

Examples:

```
{Quick question | Thought about this | Noticed something} about {{company_name}}
{{{first_name}} | Quick thought} — {{company_name}}
{Idea for | Question about | Observation on} {{company_name}}
```

### LinkedIn premium rules

- **Non-premium:** connection request `message` = **blank** (empty string). No InMail step. Total: LinkedIn-only sequence has 9 steps; multi-channel has 16 steps.
- **Premium:** connection request `message` ≤ 200 chars (e.g. `"Hi {{first_name}}, {{website_hook}} — would love to connect and share thoughts."`). InMail available with subject + 150–200-word body. Total: LinkedIn-only 10 steps; multi-channel 18 steps.

### Variable priority for openers

1. `{{icebreaker}}` — LinkedIn posts icebreaker (most personal)
2. `{{website_hook}}` — website-specific
3. `{{company_hook}}` — company LinkedIn-specific
4. `{{experience_hook}}` — person-specific
5. Manual trigger from `{{position}}` + `{{company_name}}` (fallback)

### Hard rules

1. ≈150–200 words for initial messages, shorter for follow-ups.
2. **No "hope you're well" / no throat-clearing / no cheesy openers.**
3. No feature dump — talk outcomes and the problem you solve.
4. Specificity wins — numbers, timeframes, scope (when you can).
5. **One clear CTA** (not multiple questions).
6. Avoid spam signals — too many links, heavy formatting, buzzwords, exclamation marks.
7. Write like you talk — simple words, short sentences.
8. Skimmable — 1–2 lines per paragraph, 3–6 short paragraphs.
9. Mobile-first — short lines that look good on phone screens.
10. Each follow-up adds something new — never just "bumping this".
11. Final follow-up = breakup style, close the loop politely.
12. **Emails: NO sender name / signature.** Leadspicker auto-appends the email signature; adding one in the body would duplicate.
13. **LinkedIn: ALWAYS include the sender's real name** at the end of every LinkedIn message. LinkedIn has no auto-signature. Use the exact name the user provided in the pre-generation checklist — never `{{first_name}}` (that's the recipient).

### Multi-language

- Default English; ask before generating.
- Spintax in the target language (greetings, closings, transitions).
- If a `{{salutation}}` column exists (from mcp-personalizer), use it instead of generic greeting spintax.
- Messages should read naturally, not like translations.

### Email body format

HTML in the `message` field. Use `<div>` and `<br>`.

```html
<div>
  {Hi | Hey | Hello} {{first_name}},<br /><br />
  {{website_hook}}<br /><br />
  {Curious if | Wondering whether} you're seeing similar challenges with [problem].<br /><br />
  {Teams like yours | Similar companies in {{industry}}} are seeing [outcome] within [timeframe].<br /><br />
  {Worth a quick 10-min chat? | Open to comparing notes?}
</div>
```

⚠️ Do NOT include sender name or signature — Leadspicker auto-appends it.

### LinkedIn message format

Plain text in the `message` field (NOT HTML). Always sign with the user's actual name.

```
{Hi | Hey | Hello} {{first_name}},

{{website_hook}}

{Curious if | Wondering whether} you're exploring [topic] — {teams like yours | others in your space} are seeing [outcome].

{Worth connecting to share notes? | Open to a quick chat?}

{Best | Cheers},
{user's actual name}
```

---

## Workflow

1. **Resolve the project** — display as **`{name} (#{id})`**.
   - User named it → `mcp__leadspicker__list_projects(search_query="{name}", limit=5)`.
   - Otherwise → `mcp__leadspicker__list_projects(order_by="last_active", limit=20)` and ask the user to pick.
     Then ask the user which sequence type to build (email / LinkedIn / multi-channel).
2. **Check existing sequence** — `mcp__leadspicker__list_sequence(project_id=X)`. Warn if a sequence already exists.
3. **Pre-generation checklist** — what they're selling, preferred language, LinkedIn Premium, sender name (LI/multi-channel).
4. **Fetch variables** — `mcp__leadspicker__list_contacts(project_id=X, page=1, page_size=5)` to see all columns including hooks / icebreakers / salutations.
5. **Find boolean columns** — `mcp__leadspicker__get_project(project_id=X, info_type="detail")` → look in `headers_data` for entries with `is_boolean: true` and pick their `id`. **If any boolean columns exist, ALWAYS add them as `magic_column_condition` steps at the root** (chained via `relation_type: "yes"`). Skip only if no boolean columns exist.
6. **Generate messages** — apply BASHO, word limits, spintax, and use the available variables. Emails: no sender name. LinkedIn: sign with the user's actual name.
7. **Show all messages to the user for approval** before building.
8. **Delete existing sequence if any** — `mcp__leadspicker__delete_sequence(project_id=X)`.
9. **Build the sequence in ONE call** — `mcp__leadspicker__create_sequence(project_id=X, sequence_messages=...)`. Never chain multiple `create_sequence_step` calls when constructing a new sequence.
10. **Verify** — `mcp__leadspicker__list_sequence(project_id=X)` to show the resulting tree.

### Deleting a sequence

`mcp__leadspicker__delete_sequence(project_id=X)` removes ALL steps. There is no API to
delete individual steps — only the entire sequence.

---

## Common errors

| Error                               | Cause                                                                             | Fix                                                        |
| ----------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `sequence_graph_invalid`            | Payload references an unknown parent key, has a cycle, or a branch without a root | Re-check `parents` references and ensure exactly one root. |
| "Field required" for `parent`       | Used `parent_id` instead of `parent`                                              | Use the `parent` field name inside `parents` objects.      |
| "Input should be '', 'yes' or 'no'" | Invalid `relation_type`                                                           | Use `""`, `"yes"`, or `"no"` only.                         |
| Step created but wrong type in UI   | Wrong `outreach_step_type` value                                                  | See step types table — e.g. `""` for email, NOT `"email"`. |

---

## Natural language matching

| User says (EN)                  | User says (CZ)                  | Action                                            |
| ------------------------------- | ------------------------------- | ------------------------------------------------- |
| "create email sequence"         | "vytvoř emailovou sekvenci"     | Email sequence (ask offer / ICP first)            |
| "create LinkedIn sequence"      | "vytvoř LinkedIn sekvenci"      | LinkedIn sequence (ask offer / ICP + Premium)     |
| "create multi-channel sequence" | "vytvoř multi-channel sekvenci" | Multi-channel (ask offer / ICP + Premium)         |
| "3 emails with 3 day delays"    | "3 emaily s 3denními pauzami"   | Email: 3 emails + 2 delays of 3 days              |
| "LinkedIn outreach with InMail" | "LinkedIn oslovení s InMailem"  | LinkedIn with InMail (ask Premium)                |
| "write outreach messages"       | "napiš oslovení"                | Generate BASHO messages for the picked sequence   |
| "create campaign for [product]" | "vytvoř kampaň pro [produkt]"   | Full sequence + messages (ask language + Premium) |
| "delete sequence"               | "smaž sekvenci"                 | `mcp__leadspicker__delete_sequence(project_id=X)` |
| "show sequence"                 | "ukaž sekvenci"                 | `mcp__leadspicker__list_sequence(project_id=X)`   |

