Prosp MCP Server
by JackJProsp
README.md
# Prosp MCP Server
An MCP server that gives Claude and any MCP-compatible agent control of a Prosp
workspace: LinkedIn lists, campaigns, leads and replies.
> **Disclaimer:** This is a Prosp project. It is not affiliated with, authorised
> by, endorsed by, or sponsored by LinkedIn Corporation or Microsoft. "LinkedIn"
> is a registered trademark of LinkedIn Corporation and is used here only
> descriptively to identify the service Prosp interoperates with.
---
## Why this is not a scraper
Most LinkedIn MCP servers drive a browser session on your own account. They work,
and their own documentation is honest that accounts using automated tools can be
restricted.
This one wraps the Prosp API instead. Prosp already holds the session, runs a
dedicated residential proxy per account, and paces activity under the platform's
thresholds. The MCP is a control surface over infrastructure built for this,
rather than a browser being driven faster than a human drives one.
Two consequences worth stating plainly:
- **You need a Prosp account.** There is a 14 day trial with no card.
- **Nothing here bypasses a rate limit.** The server refuses volumes above the
configured budget rather than finding a way around them.
---
## Install
**Claude Code:**
```bash
claude mcp add prosp -- uvx prosp-mcp-server@latest
```
Then set your key:
```bash
export PROSP_API_KEY=your_key_here
```
**Claude Desktop, or any MCP client:**
```json
{
"mcpServers": {
"prosp": {
"command": "uvx",
"args": ["prosp-mcp-server@latest"],
"env": {
"PROSP_API_KEY": "your_key_here"
}
}
}
}
```
Get a key in Prosp under **Settings → API**.
**Check it works before wiring anything up:**
```bash
uvx prosp-mcp-server@latest --check
```
That verifies the key and prints your connected accounts. It sends nothing.
---
## The tools
### Accounts
| Tool | What it does |
|---|---|
| `list_accounts` | Every connected LinkedIn account, with status |
| `get_account_budget` | Daily send budget left, across all campaigns |
| `get_account_health` | The seven checks, including combined campaign volume |
### Lists
| Tool | What it does |
|---|---|
| `list_lead_lists` | Lists in the workspace |
| `get_lead_list` | One list, with the breakdown by lead state |
| `create_lead_list` | Create an empty list |
| `import_from_post` | Commenters and likers from a post URL |
| `import_from_search` | Leads from a search or Sales Navigator URL |
| `import_leads` | Structured import, with custom variables |
| `set_lead_state` | Blacklist, duplicate, or reset to not contacted |
### Leads
| Tool | What it does |
|---|---|
| `get_lead` | One lead, with custom variables and campaign membership |
| `search_leads` | Filter a list by state or tag |
| `add_tag` | Tag by signal type and campaign |
### Campaigns
| Tool | What it does |
|---|---|
| `list_campaigns` | Campaigns, filterable by account or status |
| `get_campaign` | One campaign, with its node sequence |
| `get_campaign_stats` | Sent, accepted, messaged, replied |
| `create_campaign` | Build from a node list. Starts paused. |
| `set_campaign_status` | Start, pause, archive |
| `set_daily_limits` | Change the per-campaign caps |
### Replies
| Tool | What it does |
|---|---|
| `get_inbox` | Unified inbox across every account |
| `get_conversation` | One full thread with context |
| `send_reply` | Send. Requires `confirm=true`. |
| `mark_read` | Clear something from the queue without replying |
### Reporting
| Tool | What it does |
|---|---|
| `get_workspace_stats` | Figures across every account, for a date range |
| `diagnose_campaign` | The chain, stopped at the first failure |
| `signal_attribution` | Which signal type produced the booked meetings |
---
## The safety rails
These live in `guards.py` and are checked in code. A model cannot talk the
server out of them.
**Writes need `confirm=true`.** Anything that reaches a real person takes an
explicit flag, so the decision is visible in the tool call rather than buried in
reasoning.
**Volumes above the budget are refused.** The default is 20 connection requests
a day. The refusal message explains that the ceiling is account-wide rather than
per campaign, and states the required split.
**Batches are capped.** 200 leads per call by default. Importing is cheap; the
constraint is the send budget.
**Campaigns start paused.** `create_campaign` never starts anything. Starting is
a separate call that needs confirmation.
**Read-only mode.** Set `PROSP_READ_ONLY=true` or pass `--read-only` and every
write refuses while reads keep working. Worth using the first time an agent is
pointed at a live client account.
### Sequence linting
`create_campaign` checks the node list and warns about the three mistakes that
cost most:
- **A wait node after a connection request.** Acceptance auto-detects over two
weeks, checking every 24 hours, so that node only delays the sequence. It is
the most common unnecessary node people add.
- **More than four touches.** After four the answer is no.
- **A voice note sent alone.** They perform well but should never arrive
unaccompanied by a written message.
These are warnings, not refusals. The sequence is the author's call.
### Message linting
`send_reply` checks the draft and returns warnings for length over 300
characters, more than one question, any of the burnt openers, and em dashes.
---
## The diagnostic
`diagnose_campaign` works the chain in order and stops at the first failure,
rather than returning five stages of numbers when the first one is broken.
```
1 Acceptance below 15% the note, or the list. NOT the sequence.
2 Acceptance fine,
replies below 10% the first message.
3 Replies fine, meetings low the ask is mistimed, or the offer is wrong.
4 Meetings fine,
nothing closing not an outreach problem. Price or fit.
5 All fine, volume low the daily cap split across campaigns.
```
If meeting data is missing it says so rather than guessing, because a diagnosis
on partial data points at the wrong stage.
---
## Configuration
Every option has an environment variable. See `.env.example`.
| Variable | Default | What it does |
|---|---|---|
| `PROSP_API_KEY` | required | Your API key |
| `PROSP_API_BASE` | `https://prosp.ai/api/v1` | API base URL |
| `PROSP_READ_ONLY` | `false` | Block every write tool |
| `PROSP_DAILY_CONNECTION_BUDGET` | `20` | Refuse volumes above this |
| `PROSP_DAILY_MESSAGE_BUDGET` | `20` | Refuse volumes above this |
| `PROSP_MAX_LEADS_PER_CALL` | `200` | Batch size cap |
| `PROSP_TIMEOUT` | `30` | Request timeout in seconds |
| `PROSP_LOG_LEVEL` | `WARNING` | DEBUG, INFO, WARNING, ERROR |
**CLI flags:** `--transport`, `--host`, `--port`, `--path`, `--read-only`,
`--check`.
HTTP mode, for web-based clients:
```bash
uvx prosp-mcp-server@latest --transport streamable-http --host 127.0.0.1 --port 8000
```
Binding to a non-loopback address publishes an endpoint with no authentication.
The server warns, but it cannot stop you. Put it behind something that
authenticates.
---
## Develop
```bash
git clone https://github.com/JackJProsp/prosp-mcp-server
cd prosp-mcp-server
uv sync --extra dev
uv run pytest
uv run -m prosp_mcp_server --check
```
Test it with the MCP inspector:
```bash
bunx @modelcontextprotocol/inspector
```
Transport `Streamable HTTP`, URL `http://localhost:8000/mcp`.
---
## FAQ
**Will this get an account restricted?** The server itself sends nothing. It
asks Prosp to, and Prosp paces activity under the platform's thresholds with a
dedicated residential proxy per account. The risk is the volume you configure,
which is why the default budget is 20 a day and why anything above it is
refused rather than warned about.
**What if an agent runs away with it?** Writes need `confirm=true`, campaigns
start paused, and volumes above the budget are refused in code. Set
`PROSP_READ_ONLY=true` for a session where you want none of that to be possible.
**Does it work without a Prosp account?** No. There is a 14 day trial with no
card at [prosp.ai](https://www.prosp.ai).
---
## Acknowledgements
The shape of this server, particularly the CLI surface, the transport handling
and the decision to put the rate-limit budget in the server rather than the
prompt, is indebted to
[stickerdaniel/linkedin-mcp-server](https://github.com/stickerdaniel/linkedin-mcp-server),
which is Apache 2.0 and worth reading. No code is copied from it. The approach
differs in one fundamental way: that server drives a browser session, this one
wraps an API.
Built with [FastMCP](https://gofastmcp.com/).
---
## Licence
Apache 2.0. See `LICENSE` and `NOTICE`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues