L

Knowledge Pack Files

LinkedIn Outreach Platform Skill Pack Files

Browse the source files that power the LinkedIn Outreach Platform MCP server knowledge pack.

Available free v1.1.0 Browser LLM
$ sidebutton install linkedin
Download ZIP
linkedin_outreach_prep.yaml
6.1 KB
schema_version: 1
version: "1.0.0"
last_verified: "2026-08-25"
id: linkedin_outreach_prep
title: "Prepare Tailored Message"
description: "Full per-person outreach pass: reads the existing conversation, briefs the profile, has the LLM write a short tailored first message, and types it into the compose box — draft only, never sends"
overview: |
  One call instead of the eight-step manual loop: conversation history → profile research
  → tailored draft → compose box. Built from the 2026-08-25 manual outreach session,
  where per-person research changed the message every single time (a 2022 inbound pitch,
  a developer contact who is now a business angel, a "CEO" who turned out to run
  conferences).

  The draft follows the campaign style current at authoring time (see `campaign_context`
  to override): ~35 words, English, builder opener, unlimited-context mechanism, exactly
  one personalization clause in the person's own vocabulary, no call CTA, no em dashes.

  **Never sends.** The draft lands in the compose box for human review; sending is a
  human Enter. Requires an existing thread with the person (see
  `linkedin_draft_outreach` for the boundary); for thread-less contacts run
  `linkedin_profile_brief` and draft manually.
category:
  level: task
  domain: sales
  reusable: true
params:
  person_name:
    type: string
    description: Name as shown in the inbox, used for search and thread selection
    required: true
  profile_url:
    type: string
    description: Full LinkedIn profile URL (e.g. https://www.linkedin.com/in/username/)
    required: true
  campaign_context:
    type: string
    description: Optional override for the campaign framing baked into the prompt (product one-liner, style rules, current test variant)
    required: false
policies:
  allowed_domains:
    - "*.linkedin.com"
steps:
  - type: browser.navigate
    url: https://www.linkedin.com/messaging/?searchTerm={{person_name}}
  - type: browser.wait
    selector: main
    timeout: 12000
  - type: browser.wait
    ms: 1500
  - type: browser.extract
    selector: '.msg-conversations-container__conversations-list, [class*="conversation-list"]'
    as: thread_list
  - type: browser.click
    selector: 'li[class*="conversation"]:has-text("{{person_name}}")'
  - type: browser.wait
    selector: .msg-s-message-list-content
    timeout: 8000
  - type: browser.wait
    ms: 1000
  - type: browser.extract
    selector: .msg-s-message-list-content
    as: history
  - type: browser.navigate
    url: "{{profile_url}}"
  - type: browser.wait
    selector: main
    timeout: 12000
  - type: browser.scroll
    direction: down
    amount: 1400
  - type: browser.wait
    ms: 800
  - type: browser.scroll
    direction: down
    amount: 1400
  - type: browser.wait
    ms: 800
  - type: browser.scroll
    direction: down
    amount: 1400
  - type: browser.wait
    ms: 800
  - type: browser.extract
    selector: main
    as: profile_blob
  - type: llm.generate
    as: draft_text
    prompt: >
      Write ONE short LinkedIn message from Max to this first-degree connection.

      OUTPUT THE MESSAGE TEXT ONLY — no quotes, no preamble, no alternatives, no
      commentary. It will be typed verbatim into the compose box.


      ## Campaign framing (override applies if non-empty)

      {{campaign_context}}

      Default framing: Max spent the last months building SideButton and uses it daily —
      teams of AI coding agents with unlimited context on your own systems, so any coding
      or engineering task gets finished. For non-technical readers translate the
      mechanism into plain words (agents that fully know your systems and finish any
      software task), for technical readers "unlimited context" may stand as is.


      ## Hard style rules

      - At most 35 words, greeting and sign-off included. Sign off "Best Max".
      - English unless the existing conversation with this person is in another language.
      - Structure: greeting with first name, one product sentence starting from
        "I built SideButton", one relevance clause tailored to the person, short closer.
      - Exactly ONE personalization clause, using the person's own vocabulary from their
        profile (their company, venture, event, or queue) — never a generic compliment.
      - No meeting ask, no "quick call?", no links, no em dashes.
      - Match the ask to who they are: buyers get the product line; agencies and
        fractional execs get a white-label angle; investors, coaches and community
        owners get a your-take/your-portfolio angle; conference organizers get a
        contributor offer.
      - If the history shows THEY once wrote first (pitch, invite), acknowledge it in
        passing without apologizing for the delay and without quoting it. Ignore stale
        pleasantries older than a year otherwise.


      ## Existing conversation with this person

      ---

      {{history}}

      ---


      ## Their profile (raw extract)

      ---

      {{profile_blob}}

      ---
  - type: browser.navigate
    url: https://www.linkedin.com/messaging/?searchTerm={{person_name}}
  - type: browser.wait
    selector: main
    timeout: 12000
  - type: browser.wait
    ms: 1500
  - type: browser.click
    selector: 'li[class*="conversation"]:has-text("{{person_name}}")'
  - type: browser.wait
    selector: .msg-form__contenteditable
    timeout: 8000
  - type: browser.click
    selector: .msg-form__contenteditable
  - type: browser.injectJS
    id: sb-linkedin-clear-compose
    as: clear_result
    js: |
      (() => {
        const box = document.querySelector('.msg-form__contenteditable');
        if (!box) return 'compose box not found';
        box.focus();
        document.execCommand('selectAll', false, null);
        document.execCommand('delete', false, null);
        return 'cleared';
      })();
  - type: browser.type
    selector: .msg-form__contenteditable
    text: "{{draft_text}}"
  - type: browser.wait
    ms: 300
  - type: control.stop
    message: |
      Draft typed into the thread with {{person_name}} — NOT sent. Review and press Enter to send.

      ## Draft
      {{draft_text}}

      ## Threads found for this name
      {{thread_list}}

      ## History of the opened thread
      {{history}}