Ghost Writer MCP
An MCP server that scans your Git repositories, identifies interesting engineering work, and generates LinkedIn post drafts — with built-in confidentiality sanitisation.
What it does
Your repos → scan activity → classify by content potential → generate draft → sanitise → review
- Scan — reads
git logfrom local repo clones (zero API calls, instant) - Aggregate — groups commits by conventional-commit prefix and clusters related work
- Classify — LLM ranks groups by content potential, assigns pillars and angles
- Generate — LLM writes a LinkedIn draft in your chosen format (war story, hot take, tactical howto, TIL, deep dive)
- Sanitise — three-gate safety: regex blocklist → LLM review → human review
Quick start
Prerequisites
- Python 3.12+
- uv (recommended) or pip
- An LLM provider: Ollama (free, local) or an Anthropic API key
- Local clones of the repos you want to scan
Install
Option A — clone and run (recommended for customisation):
git clone https://github.com/fabdendev/ghost-writer-mcp.git
cd ghost-writer-mcp
uv sync
Option B — run directly with uvx (no clone needed):
uvx ghost-writer-mcp
Configure
cp config.example.yaml config.yaml
Edit config.yaml with your repos, blocklist, and LLM settings. See config.example.yaml for a fully documented template.
For Ollama (free, local):
llm:
provider: ollama
base_url: "http://localhost:11434/v1"
classifier_model: qwen3:8b
generator_model: qwen3:8b
api_key: "ollama"
ollama pull qwen3:8b
ollama serve
For Anthropic (cloud):
llm:
provider: anthropic
classifier_model: claude-haiku-4-5-20251001
generator_model: claude-haiku-4-5-20251001
api_key: "${ANTHROPIC_API_KEY}"
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
Add to Claude Code
Add to your Claude Code MCP settings (~/.claude/settings.json):
If installed from clone:
{
"mcpServers": {
"ghost-writer": {
"command": "uv",
"args": ["run", "--directory", "/path/to/ghost-writer-mcp", "fastmcp", "run", "src/server.py"]
}
}
}
If using uvx:
{
"mcpServers": {
"ghost-writer": {
"command": "uvx",
"args": ["ghost-writer-mcp"]
}
}
}
Use via MCP
From Claude Code (or any MCP client):
scan_activity # scan all configured repos (last 7 days)
scan_activity(repo="my-project") # scan a single repo
scan_activity(days=30) # look back 30 days
generate_draft(activity_index=1) # generate from top candidate
generate_draft(3, format="hot_take") # override format
edit_draft(1, "make it shorter") # refine with natural language
list_drafts(status="pending") # see saved drafts
Use via CLI
You can also test without an MCP client:
uv run python -m src scan --days 14 # scan all repos
uv run python -m src scan --repo my-project # scan one repo
uv run python -m src generate 1 # draft from top result
uv run python -m src generate 3 --format hot_take # override format
uv run python -m src list # list saved drafts
Tools
| Tool | Description |
|---|---|
scan_activity |
Scan repos, aggregate commits, classify and rank by content potential |
generate_draft |
Generate a LinkedIn draft from a classified activity |
edit_draft |
Refine a draft with natural language instructions |
list_drafts |
List saved drafts, optionally filtered by status |
Scanning modes
Ghost Writer supports two scanning backends:
- Local git (default) — reads
git logfrom local clones. Zero API calls, instant results. Setlocal_pathon each repo in your config. - GitHub API (fallback) — used automatically for repos without
local_path. Requires a GitHub token (github.tokenin config). Fetches up to 30 commits and 20 merged PRs per repo.
You can mix both: some repos with local clones, others via API.
Confidentiality
Ghost Writer uses three safety gates to prevent leaking sensitive information:
- Gate 1 — Blocklist: regex-based detection and replacement of company names, client names, product names, infrastructure details, and people names
- Gate 2 — LLM Review: the LLM scans generated text for anything that looks confidential and flags it
- Gate 3 — Human Review: drafts are saved as
pending— you always get the final say
Configure your blocklist and abstractions in config.yaml:
sanitisation:
blocklist:
company_names: ["Acme Corp"]
client_names: ["Big Client"]
product_names: ["internal-tool"]
infrastructure: ["prod-db-01.internal"]
people: ["John Doe"]
abstractions:
"Acme Corp": "a mid-size tech company"
"internal-tool": "an internal platform"
Content pillars
Define what topics you want to post about. The classifier maps activities to pillars:
content:
pillars:
- name: ai_engineering
description: "Building AI agents, LLM integration, prompt engineering"
repo_signals: ["agent", "llm", "prompt"]
weight: 1.0
- name: data_architecture
description: "Data pipelines, ETL, event-driven systems"
repo_signals: ["pipeline", "etl", "kafka"]
weight: 0.8
Post formats
| Format | Description |
|---|---|
tactical_howto |
Problem → 3-5 concrete steps → takeaway |
hot_take |
Contrarian claim backed by one specific thing you built |
war_story |
What broke, what you tried, what worked, the lesson |
til |
One surprising thing you learned, under 500 chars |
deep_dive |
3-4 sections with trade-offs and alternatives |
Architecture
src/
├── server.py # FastMCP server (4 tools)
├── cli.py # Standalone CLI for testing without MCP
├── config.py # Pydantic config with env/shell resolution
├── llm_client.py # Unified Anthropic + OpenAI-compatible client
├── scanner/
│ ├── local_git.py # Git CLI scanner (primary)
│ ├── github_client.py # GitHub API scanner (alternative)
│ ├── aggregator.py # Commit grouping and clustering
│ └── activity.py # ActivityItem dataclass
├── content/
│ ├── classifier.py # LLM-based content scoring
│ ├── generator.py # Draft generation with sanitisation
│ ├── abstractor.py # Two-gate confidentiality layer
│ └── prompts/ # System prompts (classifier, generator, reviewer)
└── store/
├── database.py # SQLite persistence
└── blocklist.py # Regex-based blocklist
Development
uv sync --extra dev
uv run pytest # run tests
uv run ruff check src/ tests/ # lint
License
MIT
Metadata
Release files for ghost-writer-mcp 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ghost_writer_mcp-0.2.0.tar.gz | 107.6 kB | Without provenance | Details for ghost_writer_mcp-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ghost_writer_mcp-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for ghost_writer_mcp-0.2.0-py3-none-any.whl |
Total release size: 140.7 kB
Release files / ghost_writer_mcp-0.2.0.tar.gz
| Download URL | ghost_writer_mcp-0.2.0.tar.gz |
|---|---|
| Size | 107.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
259aac224d67dfc2bc359f0a862346a491872c51703da3c92facfd20db0b3d5d
|
|
BLAKE2b-256 checksum How to use checksums |
e959be14c13f5c2c58fa4bd3c1edea03c8db34d18e0e697043cae0440309f22c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / ghost_writer_mcp-0.2.0-py3-none-any.whl
| Download URL | ghost_writer_mcp-0.2.0-py3-none-any.whl |
|---|---|
| Size | 33.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ca3e493f02f3400f5734523140956a2d9a634b05ad7a8b877dc5b55e3b972dce
|
|
BLAKE2b-256 checksum How to use checksums |
d29c1e1c70cfbddcb2485e4cc4df9e023311ec1873720d14914262dc19433c8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for ghost-writer-mcp 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.