lakewave-mcp 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lakewave_mcp-1.0.0/LICENSE +9 -0
- lakewave_mcp-1.0.0/PKG-INFO +50 -0
- lakewave_mcp-1.0.0/README.md +30 -0
- lakewave_mcp-1.0.0/bundle/build-bundle.sh +10 -0
- lakewave_mcp-1.0.0/bundle/manifest.json +43 -0
- lakewave_mcp-1.0.0/bundle/server/main.py +7 -0
- lakewave_mcp-1.0.0/glama.json +4 -0
- lakewave_mcp-1.0.0/publish.sh +22 -0
- lakewave_mcp-1.0.0/pyproject.toml +31 -0
- lakewave_mcp-1.0.0/server.json +20 -0
- lakewave_mcp-1.0.0/smithery.yaml +13 -0
- lakewave_mcp-1.0.0/src/lakewave_mcp/__init__.py +3 -0
- lakewave_mcp-1.0.0/src/lakewave_mcp/server.py +188 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lynbrook Solutions LLC, DBA Lakewave.ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lakewave-mcp
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Lakewave MCP server: verified people at named accounts, email verdicts, public-sector contract signals and drafted campaigns as tools for agents.
|
|
5
|
+
Project-URL: Homepage, https://lakewave.ai/agents/
|
|
6
|
+
Project-URL: Documentation, https://lakewave.ai/agents/
|
|
7
|
+
Project-URL: Source, https://github.com/taylorsec/lakewave-mcp
|
|
8
|
+
Author-email: "Lakewave.ai" <support@lakewave.ai>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,email-verification,lakewave,mcp,model-context-protocol,prospecting,sales
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Office/Business
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: mcp>=1.2
|
|
18
|
+
Requires-Dist: requests>=2.31
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Lakewave MCP server
|
|
22
|
+
|
|
23
|
+
Gives any MCP client (Claude Desktop, Cursor, Claude Code, agent frameworks) Lakewave's outcomes as tools:
|
|
24
|
+
verified people at named accounts, verdicts on a list of addresses, a company's email convention,
|
|
25
|
+
public-sector contracts ending in a territory, and campaigns filled with drafts that a person approves inside Lakewave.
|
|
26
|
+
|
|
27
|
+
mcp-name: ai.lakewave/lakewave-mcp
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
uvx lakewave-mcp # or: pip install lakewave-mcp && lakewave-mcp
|
|
32
|
+
|
|
33
|
+
Set `LAKEWAVE_API_KEY` (Lakewave app → Integrations → API keys). Keys are scoped, rate limited,
|
|
34
|
+
can be capped per day, and can never send email.
|
|
35
|
+
|
|
36
|
+
Claude Desktop / Cursor config:
|
|
37
|
+
|
|
38
|
+
{"mcpServers": {"lakewave": {"command": "uvx", "args": ["lakewave-mcp"],
|
|
39
|
+
"env": {"LAKEWAVE_API_KEY": "lw_live_..."}}}}
|
|
40
|
+
|
|
41
|
+
## Tools
|
|
42
|
+
|
|
43
|
+
`account`, `find_people`, `get_job`, `verify_emails`, `verify_list`, `get_list`, `email_format`,
|
|
44
|
+
`my_contacts`, `contracts_ending`, `create_campaign`, `get_campaign`.
|
|
45
|
+
|
|
46
|
+
Pricing is the same as inside the app and is charged to the key owner's credits: 4 per verified
|
|
47
|
+
person, 1 per definitive verdict; signals, conventions and your own contacts are free. Sending is
|
|
48
|
+
deliberately not exposed: drafts an agent creates wait in Lakewave for a human to approve.
|
|
49
|
+
|
|
50
|
+
Docs and a Clay recipe: https://lakewave.ai/agents/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Lakewave MCP server
|
|
2
|
+
|
|
3
|
+
Gives any MCP client (Claude Desktop, Cursor, Claude Code, agent frameworks) Lakewave's outcomes as tools:
|
|
4
|
+
verified people at named accounts, verdicts on a list of addresses, a company's email convention,
|
|
5
|
+
public-sector contracts ending in a territory, and campaigns filled with drafts that a person approves inside Lakewave.
|
|
6
|
+
|
|
7
|
+
mcp-name: ai.lakewave/lakewave-mcp
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
uvx lakewave-mcp # or: pip install lakewave-mcp && lakewave-mcp
|
|
12
|
+
|
|
13
|
+
Set `LAKEWAVE_API_KEY` (Lakewave app → Integrations → API keys). Keys are scoped, rate limited,
|
|
14
|
+
can be capped per day, and can never send email.
|
|
15
|
+
|
|
16
|
+
Claude Desktop / Cursor config:
|
|
17
|
+
|
|
18
|
+
{"mcpServers": {"lakewave": {"command": "uvx", "args": ["lakewave-mcp"],
|
|
19
|
+
"env": {"LAKEWAVE_API_KEY": "lw_live_..."}}}}
|
|
20
|
+
|
|
21
|
+
## Tools
|
|
22
|
+
|
|
23
|
+
`account`, `find_people`, `get_job`, `verify_emails`, `verify_list`, `get_list`, `email_format`,
|
|
24
|
+
`my_contacts`, `contracts_ending`, `create_campaign`, `get_campaign`.
|
|
25
|
+
|
|
26
|
+
Pricing is the same as inside the app and is charged to the key owner's credits: 4 per verified
|
|
27
|
+
person, 1 per definitive verdict; signals, conventions and your own contacts are free. Sending is
|
|
28
|
+
deliberately not exposed: drafts an agent creates wait in Lakewave for a human to approve.
|
|
29
|
+
|
|
30
|
+
Docs and a Clay recipe: https://lakewave.ai/agents/
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Build lakewave.mcpb (Claude Desktop one-click install). Needs node + python3.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
cd "$(dirname "$0")"
|
|
5
|
+
rm -rf server/vendor server/lakewave_mcp && mkdir -p server/vendor
|
|
6
|
+
cp -r ../src/lakewave_mcp server/lakewave_mcp
|
|
7
|
+
python3 -m pip install -q -t server/vendor "mcp>=1.2" "requests>=2.31"
|
|
8
|
+
npx -y @anthropic-ai/mcpb validate manifest.json
|
|
9
|
+
npx -y @anthropic-ai/mcpb pack . ../dist/lakewave.mcpb
|
|
10
|
+
echo "built ../dist/lakewave.mcpb (serve it at https://lakewave.ai/agents/lakewave.mcpb)"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": "0.4",
|
|
3
|
+
"name": "lakewave",
|
|
4
|
+
"display_name": "Lakewave.ai",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"description": "Verified people at named accounts, email verdicts, contract signals and drafted campaigns for B2B sellers.",
|
|
7
|
+
"long_description": "Lakewave finds and verifies the people a seller needs at named accounts, re-verifies lists, surfaces public-sector contracts ending soon, and fills campaigns with drafts a human approves inside Lakewave. Outcome priced on your Lakewave credits. Never sends email.",
|
|
8
|
+
"author": { "name": "Lakewave.ai", "email": "support@lakewave.ai", "url": "https://lakewave.ai" },
|
|
9
|
+
"homepage": "https://lakewave.ai/agents/",
|
|
10
|
+
"documentation": "https://lakewave.ai/agents/",
|
|
11
|
+
"support": "mailto:support@lakewave.ai",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": ["sales", "prospecting", "email verification", "b2b", "government contracts"],
|
|
14
|
+
"server": {
|
|
15
|
+
"type": "python",
|
|
16
|
+
"entry_point": "server/main.py",
|
|
17
|
+
"mcp_config": {
|
|
18
|
+
"command": "python3",
|
|
19
|
+
"args": ["${__dirname}/server/main.py"],
|
|
20
|
+
"env": { "LAKEWAVE_API_KEY": "${user_config.api_key}" }
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"user_config": {
|
|
24
|
+
"api_key": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"title": "Lakewave API key",
|
|
27
|
+
"description": "From app.lakewave.ai > Integrations > API keys (starts with lw_live_)",
|
|
28
|
+
"sensitive": true,
|
|
29
|
+
"required": true
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"tools": [
|
|
33
|
+
{ "name": "account", "description": "Plan, credits and limits for this key" },
|
|
34
|
+
{ "name": "find_people", "description": "Verified people by title at named companies" },
|
|
35
|
+
{ "name": "verify_emails", "description": "Verdicts for up to 25 addresses" },
|
|
36
|
+
{ "name": "verify_list", "description": "Verdicts for up to 5,000 addresses" },
|
|
37
|
+
{ "name": "email_format", "description": "A company's email convention" },
|
|
38
|
+
{ "name": "my_contacts", "description": "Search your verified contacts" },
|
|
39
|
+
{ "name": "contracts_ending", "description": "Public-sector contracts ending in a territory" },
|
|
40
|
+
{ "name": "create_campaign", "description": "Create a campaign and optionally draft emails for approval" }
|
|
41
|
+
],
|
|
42
|
+
"compatibility": { "claude_desktop": ">=0.10.0", "platforms": ["darwin", "win32", "linux"], "runtimes": { "python": ">=3.10" } }
|
|
43
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Claude Desktop bundle entry: vendored deps in ./vendor, server code in ./lakewave_mcp.
|
|
2
|
+
import os, sys
|
|
3
|
+
_here = os.path.dirname(os.path.abspath(__file__))
|
|
4
|
+
sys.path.insert(0, os.path.join(_here, "vendor"))
|
|
5
|
+
sys.path.insert(0, _here)
|
|
6
|
+
from lakewave_mcp.server import main # noqa: E402
|
|
7
|
+
main()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Publish lakewave-mcp to PyPI, then the MCP Registry. Run from /root/saas/mcp on a machine with Python 3.10+.
|
|
3
|
+
# PYPI_TOKEN=pypi-... ./publish.sh # PyPI only
|
|
4
|
+
# ./publish.sh registry # after DNS verification (see below)
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
cd "$(dirname "$0")"
|
|
7
|
+
if [ "${1:-}" != "registry" ]; then
|
|
8
|
+
: "${PYPI_TOKEN:?set PYPI_TOKEN (an API token from pypi.org, scope: entire account for the first upload)}"
|
|
9
|
+
python3 -m pip install -q --upgrade build twine
|
|
10
|
+
rm -rf dist && python3 -m build
|
|
11
|
+
TWINE_USERNAME=__token__ TWINE_PASSWORD="$PYPI_TOKEN" python3 -m twine upload dist/*
|
|
12
|
+
echo "published: pip install lakewave-mcp | uvx lakewave-mcp"
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
# --- MCP Registry (official): namespace ai.lakewave/* is proven by a DNS TXT record on lakewave.ai ---
|
|
16
|
+
# 1. install: curl -L https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz | tar xz
|
|
17
|
+
# 2. key: openssl genpkey -algorithm ed25519 -out mcp-publisher.key
|
|
18
|
+
# openssl pkey -in mcp-publisher.key -pubout -outform DER | tail -c 32 | base64 -> PUBKEY
|
|
19
|
+
# 3. DNS: add TXT at lakewave.ai: v=MCPv1; k=ed25519; p=<PUBKEY>
|
|
20
|
+
# 4. login: ./mcp-publisher login dns --domain lakewave.ai --private-key "$(openssl pkey -in mcp-publisher.key -noout -text | grep -A3 priv | tail -n +2 | tr -d ' :\n')"
|
|
21
|
+
# 5. publish:
|
|
22
|
+
./mcp-publisher publish
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lakewave-mcp"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Lakewave MCP server: verified people at named accounts, email verdicts, public-sector contract signals and drafted campaigns as tools for agents."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Lakewave.ai", email = "support@lakewave.ai" }]
|
|
13
|
+
keywords = ["mcp", "model-context-protocol", "sales", "prospecting", "email-verification", "lakewave", "agents"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Topic :: Office/Business",
|
|
19
|
+
]
|
|
20
|
+
dependencies = ["mcp>=1.2", "requests>=2.31"]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://lakewave.ai/agents/"
|
|
24
|
+
Documentation = "https://lakewave.ai/agents/"
|
|
25
|
+
Source = "https://github.com/taylorsec/lakewave-mcp"
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
lakewave-mcp = "lakewave_mcp.server:main"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.wheel]
|
|
31
|
+
packages = ["src/lakewave_mcp"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "ai.lakewave/lakewave-mcp",
|
|
4
|
+
"description": "Verified people at named accounts, email verdicts, public-sector contract signals and drafted campaigns for B2B sellers. Outcome priced; never sends email.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"websiteUrl": "https://lakewave.ai/agents/",
|
|
7
|
+
"packages": [
|
|
8
|
+
{
|
|
9
|
+
"registryType": "pypi",
|
|
10
|
+
"registryBaseUrl": "https://pypi.org",
|
|
11
|
+
"identifier": "lakewave-mcp",
|
|
12
|
+
"version": "1.0.0",
|
|
13
|
+
"runtimeHint": "uvx",
|
|
14
|
+
"transport": { "type": "stdio" },
|
|
15
|
+
"environmentVariables": [
|
|
16
|
+
{ "name": "LAKEWAVE_API_KEY", "description": "Lakewave API key (app.lakewave.ai > Integrations > API keys)", "isRequired": true, "isSecret": true }
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Smithery listing (https://smithery.ai). Local stdio server published on PyPI.
|
|
2
|
+
startCommand:
|
|
3
|
+
type: stdio
|
|
4
|
+
configSchema:
|
|
5
|
+
type: object
|
|
6
|
+
required: [lakewaveApiKey]
|
|
7
|
+
properties:
|
|
8
|
+
lakewaveApiKey:
|
|
9
|
+
type: string
|
|
10
|
+
title: Lakewave API key
|
|
11
|
+
description: From app.lakewave.ai > Integrations > API keys (lw_live_...)
|
|
12
|
+
commandFunction: |-
|
|
13
|
+
(config) => ({ command: "uvx", args: ["lakewave-mcp"], env: { LAKEWAVE_API_KEY: config.lakewaveApiKey } })
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Lakewave MCP server. Gives an agent (Claude Desktop, Cursor, Claude Code,
|
|
4
|
+
any MCP client) Lakewave's outcomes as tools: verified people at named
|
|
5
|
+
accounts, verdicts on a list of addresses, a company's email convention,
|
|
6
|
+
public-sector contracts ending in a territory, and campaigns filled with
|
|
7
|
+
drafts that a person approves inside Lakewave.
|
|
8
|
+
|
|
9
|
+
Setup
|
|
10
|
+
pip install 'mcp>=1.2' requests (1.x and 2.x both work)
|
|
11
|
+
export LAKEWAVE_API_KEY=lw_live_... (Lakewave app > Integrations > API keys)
|
|
12
|
+
|
|
13
|
+
Claude Desktop / Cursor (claude_desktop_config.json or mcp.json):
|
|
14
|
+
{"mcpServers": {"lakewave": {"command": "python", "args": ["/path/to/lakewave_mcp.py"],
|
|
15
|
+
"env": {"LAKEWAVE_API_KEY": "lw_live_..."}}}}
|
|
16
|
+
|
|
17
|
+
Pricing is the same as inside the app and is charged to the key owner's
|
|
18
|
+
credits: 4 per verified person, 1 per definitive verdict; signals and
|
|
19
|
+
convention lookups are free. Nothing in this server sends email.
|
|
20
|
+
"""
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
import time
|
|
24
|
+
import json
|
|
25
|
+
|
|
26
|
+
import requests
|
|
27
|
+
try: # mcp 2.x
|
|
28
|
+
from mcp.server.mcpserver import MCPServer as _Server
|
|
29
|
+
except ImportError: # mcp 1.x
|
|
30
|
+
from mcp.server.fastmcp import FastMCP as _Server
|
|
31
|
+
|
|
32
|
+
BASE = os.environ.get("LAKEWAVE_API_BASE", "https://app.lakewave.ai/api/v1").rstrip("/")
|
|
33
|
+
KEY = os.environ.get("LAKEWAVE_API_KEY", "").strip()
|
|
34
|
+
mcp = _Server("lakewave", instructions=(
|
|
35
|
+
"Lakewave finds and verifies the people a seller needs at named accounts, re-verifies lists, "
|
|
36
|
+
"surfaces public-sector contracts ending soon, and fills campaigns with drafts a human approves. "
|
|
37
|
+
"Tools return finished outcomes. Costs: 4 credits per verified person, 1 per definitive email verdict; "
|
|
38
|
+
"everything else is free. Call account() first if you need the balance."))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _call(method, path, **kw):
|
|
42
|
+
if not KEY:
|
|
43
|
+
return {"error": "LAKEWAVE_API_KEY is not set. Create one in the Lakewave app under Integrations."}
|
|
44
|
+
headers = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json", "User-Agent": "lakewave-mcp/1.0"}
|
|
45
|
+
try:
|
|
46
|
+
r = requests.request(method, f"{BASE}{path}", headers=headers, timeout=90, **kw)
|
|
47
|
+
except requests.RequestException as e:
|
|
48
|
+
return {"error": f"Lakewave is unreachable: {e}"}
|
|
49
|
+
try:
|
|
50
|
+
data = r.json()
|
|
51
|
+
except ValueError:
|
|
52
|
+
data = {"error": f"HTTP {r.status_code}: {r.text[:300]}"}
|
|
53
|
+
if r.status_code >= 400 and "error" not in data:
|
|
54
|
+
data["error"] = f"HTTP {r.status_code}"
|
|
55
|
+
return data
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _wait(path, seconds, done_statuses=("done", "failed", "completed", "error")):
|
|
59
|
+
deadline = time.time() + max(0, seconds)
|
|
60
|
+
last = None
|
|
61
|
+
while True:
|
|
62
|
+
last = _call("GET", path)
|
|
63
|
+
if last.get("error") or last.get("status") in done_statuses or time.time() >= deadline:
|
|
64
|
+
return last
|
|
65
|
+
time.sleep(4)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@mcp.tool()
|
|
69
|
+
def account() -> dict:
|
|
70
|
+
"""Plan, remaining credits, prices and request limits for this API key."""
|
|
71
|
+
return _call("GET", "/me")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@mcp.tool()
|
|
75
|
+
def find_people(companies: list[str], titles: list[str], location: str = "", location_mode: str = "",
|
|
76
|
+
wait_seconds: int = 240) -> dict:
|
|
77
|
+
"""Find and verify the people in the given roles at the given companies.
|
|
78
|
+
companies: 1-25 company names. titles: 1-5 role titles (e.g. "Director of IT").
|
|
79
|
+
location: optional territory ("Texas", "Bay Area"); location_mode "hq" keeps
|
|
80
|
+
companies headquartered there, "people" keeps people located there.
|
|
81
|
+
Returns only people at Lakewave's 4-star verified bar: name, title, company,
|
|
82
|
+
email, stars, LinkedIn. Costs 4 credits per verified person; a search that
|
|
83
|
+
finds nobody costs nothing. Waits up to wait_seconds; if still running,
|
|
84
|
+
returns the job_id to resume with get_job."""
|
|
85
|
+
body = {"companies": companies, "titles": titles, "location": location, "location_mode": location_mode}
|
|
86
|
+
started = _call("POST", "/people/find", data=json.dumps(body))
|
|
87
|
+
if started.get("error") or not started.get("job_id"):
|
|
88
|
+
return started
|
|
89
|
+
out = _wait(f"/jobs/{started['job_id']}", wait_seconds)
|
|
90
|
+
if out.get("status") not in ("done", "failed"):
|
|
91
|
+
out["note"] = "Still running. Call get_job(job_id) in a minute."
|
|
92
|
+
return out
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@mcp.tool()
|
|
96
|
+
def get_job(job_id: str) -> dict:
|
|
97
|
+
"""Status and results of a find_people job. People are included once the job is done."""
|
|
98
|
+
return _call("GET", f"/jobs/{job_id}")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@mcp.tool()
|
|
102
|
+
def verify_emails(emails: list[str]) -> dict:
|
|
103
|
+
"""Verdict per address, synchronous for up to 25 addresses: valid, invalid,
|
|
104
|
+
catch_all (accept-all domain), unknown, dead_domain or bad_syntax, each with a
|
|
105
|
+
reason and a 0-5 star rating. 1 credit per definitive verdict (valid or
|
|
106
|
+
invalid); the rest are free. For more than 25 use verify_list."""
|
|
107
|
+
return _call("POST", "/emails/verify", data=json.dumps({"emails": emails}))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@mcp.tool()
|
|
111
|
+
def verify_list(emails: list[str], name: str = "agent list", wait_seconds: int = 240) -> dict:
|
|
112
|
+
"""Re-verify a larger list (up to 5,000 addresses) as a background job.
|
|
113
|
+
Same verdicts and pricing as verify_emails. Waits up to wait_seconds, then
|
|
114
|
+
returns results or the job_id to resume with get_list."""
|
|
115
|
+
started = _call("POST", "/emails/verify-list", data=json.dumps({"emails": emails, "name": name}))
|
|
116
|
+
if started.get("error") or not started.get("job_id"):
|
|
117
|
+
return started
|
|
118
|
+
out = _wait(f"/lists/{started['job_id']}", wait_seconds)
|
|
119
|
+
if out.get("status") != "done":
|
|
120
|
+
out["note"] = "Still running. Call get_list(job_id) shortly."
|
|
121
|
+
return out
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@mcp.tool()
|
|
125
|
+
def get_list(job_id: str) -> dict:
|
|
126
|
+
"""Status and results of a verify_list job."""
|
|
127
|
+
return _call("GET", f"/lists/{job_id}")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@mcp.tool()
|
|
131
|
+
def email_format(domain: str, first: str = "", last: str = "") -> dict:
|
|
132
|
+
"""A company's email convention. 'confirmed' means Lakewave has verified real
|
|
133
|
+
addresses at that domain in that shape; 'evidence' means seen publicly;
|
|
134
|
+
'guess' means nothing on record yet. With first and last, returns rendered
|
|
135
|
+
candidate addresses labelled by basis. Free. Candidates are not verified
|
|
136
|
+
until you run verify_emails on them."""
|
|
137
|
+
return _call("GET", "/emails/format", params={"domain": domain, "first": first, "last": last})
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@mcp.tool()
|
|
141
|
+
def my_contacts(q: str = "", company: str = "", domain: str = "", min_stars: float = 4, limit: int = 100) -> dict:
|
|
142
|
+
"""Search the key owner's own verified contacts (already paid for, never charged
|
|
143
|
+
again). q matches name, title or company. Free."""
|
|
144
|
+
return _call("GET", "/contacts", params={"q": q, "company": company, "domain": domain, "min_stars": min_stars, "limit": limit})
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@mcp.tool()
|
|
148
|
+
def contracts_ending(states: str = "", months: int = 6, terms: str = "", tech_only: bool = True,
|
|
149
|
+
statewide_only: bool = False, page: int = 1) -> dict:
|
|
150
|
+
"""Public-sector (state, county, city, K-12, higher-ed) contracts ending inside
|
|
151
|
+
the window: agency, incumbent vendor, title, end date, amount and source link.
|
|
152
|
+
states: comma-separated two-letter codes ("CA,TX"); terms: free text matched
|
|
153
|
+
against title, agency, vendor and vehicle. These are purchases an agency has
|
|
154
|
+
to re-buy, i.e. rebid and competitive-takeout targets. Free."""
|
|
155
|
+
params = {"states": states, "months": months, "terms": terms, "page": page}
|
|
156
|
+
if tech_only:
|
|
157
|
+
params["cat"] = "tech"
|
|
158
|
+
if statewide_only:
|
|
159
|
+
params["scope"] = "statewide"
|
|
160
|
+
return _call("GET", "/signals/contracts", params=params)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@mcp.tool()
|
|
164
|
+
def create_campaign(name: str, companies: list[str], titles: list[str] = None, location: str = "",
|
|
165
|
+
draft: bool = False) -> dict:
|
|
166
|
+
"""Create a Lakewave campaign with a target set of companies. With draft=true
|
|
167
|
+
and titles given, Lakewave finds the best person at each company and writes a
|
|
168
|
+
personalized draft for each (4 credits per verified person plus 1 per draft).
|
|
169
|
+
Drafts wait in the Lakewave app for a human to approve and schedule; this tool
|
|
170
|
+
never sends email. Returns the campaign id and the app link."""
|
|
171
|
+
body = {"name": name, "companies": companies, "titles": titles or [], "location": location, "draft": draft}
|
|
172
|
+
return _call("POST", "/campaigns", data=json.dumps(body))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@mcp.tool()
|
|
176
|
+
def get_campaign(campaign_id: str) -> dict:
|
|
177
|
+
"""Campaign rollup: company statuses, draft / approved / sent counts, drafting progress."""
|
|
178
|
+
return _call("GET", f"/campaigns/{campaign_id}")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def main():
|
|
182
|
+
if not KEY:
|
|
183
|
+
print("warning: LAKEWAVE_API_KEY is not set; every tool will return an error until it is.", file=sys.stderr)
|
|
184
|
+
mcp.run()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
main()
|