LinkedIn Message Drafter
Write LinkedIn outreach that actually gets replies — personalized, warm, and ready in seconds. Feed it a name and a bit of context; it hands back a concise, human-sounding draft you can send as-is or tweak.
Draft-first and safe by design. It does not scrape LinkedIn, automate browser actions, or send anything on its own. You stay in control of every message — review, copy, send. Ready to automate? Wire in an approved LinkedIn integration through the adapter below.
Install
pipx install linkedin-message-drafter # or: pip install linkedin-message-drafter
# for the Claude draft path: pipx install "linkedin-message-drafter[ai]"
Quick start
linkedin-draft prospect.json # prints a draft and saves it to drafts/
From source (development)
git clone https://github.com/wuisabel-gif/linkedin-message-drafter.git
cd linkedin-message-drafter
python3 -m venv .venv && source .venv/bin/activate
pip install -e . # add [ai] for the Claude draft path: pip install -e ".[ai]"
linkedin-draft src/linkedin_message_drafter/examples/prospect.json
Web UI
Prefer a form to the CLI? A zero-dependency local UI ships with it:
linkedin-draft-web # then open http://127.0.0.1:8000
Binds to localhost only, uses the same drafting engine (voice matching, slop check, short mode), and still just drafts — nothing is sent.
Input
See src/linkedin_message_drafter/examples/prospect.json. Required fields are name, context, and goal; company, role, and relationship are optional. CSV input uses the same column names, one prospect per row.
Options
# Batch — pass multiple files, a CSV, or a whole folder
linkedin-draft ./prospects/
linkedin-draft prospects.csv
# Connection-request note, guaranteed under LinkedIn's 300-char limit
linkedin-draft --short my-prospect.json
# Best-of-N — generate 3 AI drafts, keep the one cadence-deslop scores cleanest
linkedin-draft --best-of 3 my-prospect.json
# Write in a Cadence voice preset (AI path; needs CADENCE_VOICES)
export CADENCE_VOICES=/path/to/Cadence/voices
linkedin-draft --style punchy my-prospect.json
--short works with both the template and AI paths; --style applies to AI drafts only. Available presets are the .md files in Cadence's voices/ (punchy, column, plain, dispatch, …).
AI drafting (optional)
Set ANTHROPIC_API_KEY to draft with Claude instead of the built-in template — richer, more personalized copy. With no key set, the tool uses the dependency-free template and works fully offline.
export ANTHROPIC_API_KEY=sk-ant-...
linkedin-draft my-prospect.json
Sounds like you (Cadence)
Point VOICE_SAMPLE at your own past writing and the AI draft mimics the way you speak — tone, rhythm, and word choice. It takes a single file or a directory: drop in a folder of all your previous posts and messages and Cadence learns from the whole corpus.
export VOICE_SAMPLE=~/my-writing-sample.txt # one sample
export VOICE_SAMPLE=~/my-linkedin-posts/ # or a folder of everything you've written
Automatic slop check. If the cadence-deslop detector is installed, every AI draft is scored 0–100 for AI-slop tells (uniform rhythm, hollow-confidence words, clichéd openers). If a draft reads as slop, Claude rewrites it once with the named tells fed back, and the cleaner version is kept. It's a no-op when the detector isn't installed.
npm install -g cadence-deslop # or: export CADENCE_DESLOP=/path/to/deslop.mjs
For a deeper voice pass, run the /cadence skill on any saved draft in drafts/.
Use it inside Claude or ChatGPT
The drafter plugs into AI assistants three ways:
-
MCP server (Claude Desktop/Code, ChatGPT MCP connector). Install the extra and register
linkedin-draft-mcp:pip install "linkedin-message-drafter[mcp]"
It exposes
draft_messageandscore_texttools over stdio. Point your MCP client at thelinkedin-draft-mcpcommand. -
Claude Skill —
skills/linkedin-drafter/SKILL.md. Drop it into your Claude skills directory (or plugin) and Claude drafts on request. -
ChatGPT GPT Action —
integrations/chatgpt-action-openapi.yaml. Host the JSON backend (linkedin-draft-webservesPOST /api/draft) behind a public HTTPS URL, set that URL in the spec'sservers, and import it as an Action in a custom GPT.
All three stay draft-only: they return a message (and its Cadence slop score); nothing is scraped or sent.
Adding an approved API integration
src/linkedin_message_drafter/linkedin.py contains a deliberately unimplemented LinkedInClient. Implement only the endpoints and scopes that LinkedIn has approved for your application. Keep the default workflow draft-only and require a user confirmation before any send operation. Do not add scraping, credential collection, or bulk/unattended sending.
Read TUTORIAL.md before configuring credentials or requesting permissions.
Tests
python -m unittest discover -s tests -v
Publishing
Releases publish to PyPI automatically via Trusted Publishing — no API token is stored anywhere. One-time setup:
-
On pypi.org, add a pending publisher: project
linkedin-message-drafter, ownerwuisabel-gif, repolinkedin-message-drafter, workflowpublish.yml, environmentpypi. -
Cut a release: bump
versioninpyproject.toml, thengit tag v0.1.0 && git push origin v0.1.0 gh release create v0.1.0 --generate-notes
The Publish to PyPI workflow builds the sdist + wheel and uploads them. Local dry run:
python -m build && twine check dist/*
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file linkedin_message_drafter-0.1.2.tar.gz.
File metadata
- Download URL: linkedin_message_drafter-0.1.2.tar.gz
- Upload date:
- Size: 17.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fb2e0974008a21606fb08b844e4255bfe133f378465916cf50b754f366088ae
|
|
| MD5 |
76882baa8cd75c9eee4784303a5cf263
|
|
| BLAKE2b-256 |
8c81991a8a0f65e46e1d74ba56bd7cc43453f00bd7c5b7979c775848478520e1
|
Provenance
The following attestation bundles were made for linkedin_message_drafter-0.1.2.tar.gz:
Publisher:
publish.yml on wuisabel-gif/linkedin-message-drafter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linkedin_message_drafter-0.1.2.tar.gz -
Subject digest:
9fb2e0974008a21606fb08b844e4255bfe133f378465916cf50b754f366088ae - Sigstore transparency entry: 2231304747
- Sigstore integration time:
-
Permalink:
wuisabel-gif/linkedin-message-drafter@0d00e4a4e3b519cf221b0daba74eaa1fb6b05500 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/wuisabel-gif
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0d00e4a4e3b519cf221b0daba74eaa1fb6b05500 -
Trigger Event:
release
-
Statement type:
File details
Details for the file linkedin_message_drafter-0.1.2-py3-none-any.whl.
File metadata
- Download URL: linkedin_message_drafter-0.1.2-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
552e2d51e5bf148ddc21bd1fedbbf501776b39da8974054da3667648db897df4
|
|
| MD5 |
18f8e09536fd759f39c987cfedc048ed
|
|
| BLAKE2b-256 |
7d996ce5b158c7b2c36ab08a5b83f88a87692e30f793767d972a18e5f6bbe64c
|
Provenance
The following attestation bundles were made for linkedin_message_drafter-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on wuisabel-gif/linkedin-message-drafter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linkedin_message_drafter-0.1.2-py3-none-any.whl -
Subject digest:
552e2d51e5bf148ddc21bd1fedbbf501776b39da8974054da3667648db897df4 - Sigstore transparency entry: 2231305161
- Sigstore integration time:
-
Permalink:
wuisabel-gif/linkedin-message-drafter@0d00e4a4e3b519cf221b0daba74eaa1fb6b05500 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/wuisabel-gif
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0d00e4a4e3b519cf221b0daba74eaa1fb6b05500 -
Trigger Event:
release
-
Statement type: