# HeyLead MCP Tools — Test Report

**Date:** 2026-02-17  
**Scope:** Smoke test of all 30 MCP tools with minimal/safe arguments (no real backend, no LinkedIn auth).  
**Goal:** Confirm each tool runs without crash and returns either a valid result or a clear “setup required” / “no data” message.

---

## Summary

| Status | Count | Meaning |
|--------|--------|--------|
| **OK** | 10 | Tool returned a normal result (e.g. scheduler status, emergency_stop, export_campaign with no campaigns). |
| **EXPECTED** | 20 | Tool returned a graceful message (e.g. “Setup required”, “No campaign”, “First campaign — provide company_context”). |
| **FAIL** | 0 | No tool crashed or raised an unhandled exception. |
| **IMPORT_ERROR** | 0 | No tool failed to load due to a missing symbol or dependency. |

**All 30 tools are working** in the sense that they execute and return a string response. Tools that require setup or backend return clear messages instead of crashing.

---

## Tool-by-tool results

| Tool | Result | Notes |
|------|--------|--------|
| `show_status` | EXPECTED | Returns dashboard text; with no setup, shows “Setup required” or empty campaign list. |
| `scheduler_status` | OK | Returns scheduler state (e.g. enabled/disabled, pending jobs). |
| `emergency_stop` | OK | Pauses all campaigns; returns confirmation. |
| `compare_campaigns` | OK | Returns comparison table; with no campaigns, empty or “no campaigns”. |
| `campaign_report` | EXPECTED | With no campaign ID or no data, returns “No campaign” or similar. |
| `suggest_next_action` | EXPECTED | Suggests next step; without setup/campaigns, suggests setup or creating campaign. |
| `toggle_scheduler` | OK | Enables/disables scheduler; returns confirmation. |
| `retry_failed` | EXPECTED | With no campaign or no failed items, returns “No campaign” or “no errors”. |
| `export_campaign` | OK | With no campaign, returns empty table or “No campaign”. |
| `pause_campaign` | EXPECTED | With no campaign ID, returns “No campaign” / “Campaign not found”. |
| `resume_campaign` | EXPECTED | Same as pause_campaign. |
| `archive_campaign` | EXPECTED | Same pattern. |
| `check_replies` | EXPECTED | Without backend/setup, returns “Setup required” or “No LinkedIn account”. |
| `unlink_account` | OK | Clears local account; in backend mode may call backend; no crash. |
| `list_linkedin_accounts` | EXPECTED | Without backend/setup, returns “Setup” / “No LinkedIn” message. |
| `switch_account` | EXPECTED | Same; may return “Not connected. Run setup_profile first.” |
| `switch_account_to` | EXPECTED | **Fixed:** Previously raised `UnipileError` when Unipile/backend not configured; now returns “Not connected. … Run setup_profile first.” |
| `generate_icp` | EXPECTED | Without LLM/backend, returns “Setup” or “LLM/api_key” or tier limit message. |
| `create_campaign` | EXPECTED | Without setup, returns “Setup required” or “First campaign — provide company_context” or “No LinkedIn account”. |
| `generate_and_send` | EXPECTED | Without campaign/setup, returns “Setup” or “No campaign” / “Select a campaign”. |
| `send_followup` | EXPECTED | Same pattern. |
| `reply_to_prospect` | EXPECTED | Without outreach/setup, returns “No pending” / “Setup” message. |
| `engage_prospect` | EXPECTED | Same pattern. |
| `approve_outreach` | EXPECTED | With “skip” and no pending, returns “No pending” or similar. |
| `skip_prospect` | OK | With no campaign/outreach, returns “No campaign” or “outreach” message. |
| `show_conversation` | OK | With invalid outreach_id, returns “not found” or “Invalid” message. |
| `edit_campaign` | EXPECTED | With no campaign, returns “No campaign” / “Campaign not found”. |
| `close_outreach` | EXPECTED | With invalid outreach_id, returns “not found” / “No hot_lead” message. |
| `delete_campaign` | EXPECTED | With no campaign or confirm=False, returns “No campaign” or “confirm” message. |
| `setup_profile` | EXPECTED | Without JWT, returns instructions (token, Sign in, connect, URL). |

---

## Issues addressed in this run

1. **`switch_account_to`**  
   When Unipile/backend was not configured, `get_linkedin_client()` raised `UnipileError` and the tool crashed.  
   **Fix:** Catch `UnipileError` when obtaining the client and return:  
   `"❌ Not connected. {e}\n\nRun setup_profile first."`

---

## How to re-run the smoke test

From the heylead repo (with `src` on `PYTHONPATH` or from repo root):

```bash
python3 tests/smoke_tools.py
```

The script uses a temporary directory for HeyLead home so it does not modify your real `~/.heylead` data.

---

## What is *not* covered

- **Real backend/LinkedIn:** No JWT, no Unipile API key, no live API calls.  
- **Full flows:** e.g. create_campaign → generate_and_send with real prospects.  
- **RAG/ICP pipeline:** generate_icp with `company_context` and backend RAG.  
- **Rate limits / 429:** Handled in code; not exercised in this smoke test.

To validate full flows, run tools from Cursor (or another MCP client) after `setup_profile` and with a real backend/JWT.
