@fundzwatch/mcp-server 1.0.0
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.
- package/README.md +89 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +293 -0
- package/package.json +42 -0
- package/src/index.ts +342 -0
- package/tsconfig.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# FundzWatch MCP Server
|
|
2
|
+
|
|
3
|
+
Real-time business event intelligence for AI agents via [Model Context Protocol](https://modelcontextprotocol.io).
|
|
4
|
+
|
|
5
|
+
Get AI-scored sales leads, funding rounds, acquisitions, executive hires, and market intelligence directly in Claude, Cursor, Windsurf, or any MCP-compatible client.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
### 1. Get a Free API Key
|
|
10
|
+
|
|
11
|
+
Sign up at [fundzwatch.ai/onboarding](https://fundzwatch.ai/onboarding) (no credit card required).
|
|
12
|
+
|
|
13
|
+
### 2. Add to Claude Desktop
|
|
14
|
+
|
|
15
|
+
Edit your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"mcpServers": {
|
|
20
|
+
"fundzwatch": {
|
|
21
|
+
"command": "npx",
|
|
22
|
+
"args": ["-y", "@fundzwatch/mcp-server"],
|
|
23
|
+
"env": {
|
|
24
|
+
"FUNDZWATCH_API_KEY": "fundz_test_your_key_here"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 3. Start Asking
|
|
32
|
+
|
|
33
|
+
- "Find me companies that just raised Series B in healthtech"
|
|
34
|
+
- "Show me the largest funding rounds this week"
|
|
35
|
+
- "Get my AI-scored leads with a score above 70"
|
|
36
|
+
- "What's happening in the market today?"
|
|
37
|
+
- "Track stripe.com and github.com on my watchlist"
|
|
38
|
+
|
|
39
|
+
## Available Tools
|
|
40
|
+
|
|
41
|
+
| Tool | Description |
|
|
42
|
+
|------|-------------|
|
|
43
|
+
| `get_scored_leads` | AI-scored leads matched to your ICP with buyer intent, buying stage, and outreach recommendations |
|
|
44
|
+
| `get_events` | Real-time business events: funding, acquisitions, hiring, contracts, product launches |
|
|
45
|
+
| `get_market_pulse` | Market activity overview with totals, trends, and largest rounds |
|
|
46
|
+
| `get_market_brief` | AI-generated strategic intelligence brief |
|
|
47
|
+
| `manage_watchlist` | Add, remove, or list tracked companies |
|
|
48
|
+
| `get_watchlist_events` | Events for your tracked companies |
|
|
49
|
+
| `get_usage` | Check API usage and limits |
|
|
50
|
+
|
|
51
|
+
## Use with Cursor / Windsurf
|
|
52
|
+
|
|
53
|
+
Add to your MCP settings:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"fundzwatch": {
|
|
58
|
+
"command": "npx",
|
|
59
|
+
"args": ["-y", "@fundzwatch/mcp-server"],
|
|
60
|
+
"env": {
|
|
61
|
+
"FUNDZWATCH_API_KEY": "fundz_test_your_key_here"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Pricing
|
|
68
|
+
|
|
69
|
+
| Tier | Price | API Calls/mo | AI Score Calls/mo |
|
|
70
|
+
|------|-------|-------------|-------------------|
|
|
71
|
+
| Sandbox | Free | 500 | 50 |
|
|
72
|
+
| Growth | $199/mo | 10,000 | 500 |
|
|
73
|
+
| Scale | $599/mo | 100,000 | 5,000 |
|
|
74
|
+
| Enterprise | Custom | Unlimited | Unlimited |
|
|
75
|
+
|
|
76
|
+
## What is FundzWatch?
|
|
77
|
+
|
|
78
|
+
[FundzWatch.ai](https://fundzwatch.ai) provides real-time business event intelligence for AI agents and sales teams:
|
|
79
|
+
|
|
80
|
+
- **AI-Scored Leads**: Companies scored daily against your ICP using Claude AI, with buyer intent signals, buying stage analysis, and outreach recommendations
|
|
81
|
+
- **Event Feeds**: Real-time funding rounds, acquisitions, executive moves, government contracts, and product launches
|
|
82
|
+
- **Predictive Intelligence**: ML models predicting which companies are likely to make moves (82% accuracy)
|
|
83
|
+
- **Market Briefs**: AI-generated daily strategic intelligence briefings
|
|
84
|
+
|
|
85
|
+
Built by [Fundz](https://fundz.net), tracking 50M+ business events since 2017.
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
5
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
7
|
+
const API_BASE = "https://api.fundz.net/api/v1/watch";
|
|
8
|
+
function getApiKey() {
|
|
9
|
+
const key = process.env.FUNDZWATCH_API_KEY;
|
|
10
|
+
if (!key) {
|
|
11
|
+
throw new Error("FUNDZWATCH_API_KEY environment variable is required. " +
|
|
12
|
+
"Get a free key at https://fundzwatch.ai/onboarding");
|
|
13
|
+
}
|
|
14
|
+
return key;
|
|
15
|
+
}
|
|
16
|
+
async function apiRequest(method, path, params) {
|
|
17
|
+
const apiKey = getApiKey();
|
|
18
|
+
const url = new URL(`${API_BASE}${path}`);
|
|
19
|
+
const options = {
|
|
20
|
+
method,
|
|
21
|
+
headers: {
|
|
22
|
+
Authorization: `Bearer ${apiKey}`,
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
"User-Agent": "fundzwatch-mcp/1.0.0",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
if (method === "GET" && params) {
|
|
28
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
29
|
+
if (value !== undefined && value !== null) {
|
|
30
|
+
url.searchParams.append(key, String(value));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
else if (method !== "GET" && params) {
|
|
35
|
+
options.body = JSON.stringify(params);
|
|
36
|
+
}
|
|
37
|
+
const response = await fetch(url.toString(), options);
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const errBody = await response.json().catch(() => ({ message: response.statusText }));
|
|
40
|
+
throw new Error(`API error ${response.status}: ${errBody.message || errBody.error || response.statusText}`);
|
|
41
|
+
}
|
|
42
|
+
return response.json();
|
|
43
|
+
}
|
|
44
|
+
function textResult(text) {
|
|
45
|
+
return { content: [{ type: "text", text }] };
|
|
46
|
+
}
|
|
47
|
+
// ─── Server Setup ───────────────────────────────────────────────────────
|
|
48
|
+
const server = new index_js_1.Server({ name: "fundzwatch", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
49
|
+
// ─── List Tools ─────────────────────────────────────────────────────────
|
|
50
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
51
|
+
tools: [
|
|
52
|
+
{
|
|
53
|
+
name: "get_scored_leads",
|
|
54
|
+
description: "Get AI-scored sales leads based on your ICP (Ideal Customer Profile). " +
|
|
55
|
+
"Returns companies with recent business events scored by AI for buyer intent, " +
|
|
56
|
+
"buying stage, and recommended outreach strategy.",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
min_score: { type: "number", description: "Minimum buyer intent score (0-100). Default: 0" },
|
|
61
|
+
max_results: { type: "number", description: "Max leads to return (1-50). Default: 25" },
|
|
62
|
+
buying_stages: {
|
|
63
|
+
type: "array",
|
|
64
|
+
items: { type: "string" },
|
|
65
|
+
description: "Filter by buying stage: 'Active Evaluation', 'Decision', 'Research', 'Awareness'",
|
|
66
|
+
},
|
|
67
|
+
industries: {
|
|
68
|
+
type: "array",
|
|
69
|
+
items: { type: "string" },
|
|
70
|
+
description: "Filter by industry (e.g., ['SaaS', 'HealthTech', 'FinTech'])",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "get_events",
|
|
77
|
+
description: "Get real-time business events: funding rounds, acquisitions, executive hires, " +
|
|
78
|
+
"government contracts, and product launches. Filter by type, industry, and location.",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
types: { type: "string", description: "Comma-separated: funding, acquisition, hiring, contract, product_launch. Default: all" },
|
|
83
|
+
days: { type: "number", description: "Look back days (1-90). Default: 7" },
|
|
84
|
+
limit: { type: "number", description: "Max events (1-200). Default: 50" },
|
|
85
|
+
industries: { type: "string", description: "Comma-separated industries" },
|
|
86
|
+
locations: { type: "string", description: "Comma-separated locations" },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "get_market_pulse",
|
|
92
|
+
description: "Get real-time market activity overview: funding totals, acquisition counts, " +
|
|
93
|
+
"executive moves, contracts, and product launches for the past 7 and 30 days.",
|
|
94
|
+
inputSchema: { type: "object", properties: {} },
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "get_market_brief",
|
|
98
|
+
description: "Get today's AI-generated strategic intelligence brief with narrative analysis " +
|
|
99
|
+
"of the most important market movements, patterns, and opportunities.",
|
|
100
|
+
inputSchema: { type: "object", properties: {} },
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "manage_watchlist",
|
|
104
|
+
description: "Add, remove, or list companies on your watchlist. Tracked companies generate " +
|
|
105
|
+
"alerts when they have new events.",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: {
|
|
109
|
+
action: {
|
|
110
|
+
type: "string",
|
|
111
|
+
enum: ["list", "add", "remove"],
|
|
112
|
+
description: "Action: 'list' to view, 'add' to track, 'remove' to untrack",
|
|
113
|
+
},
|
|
114
|
+
domains: {
|
|
115
|
+
type: "array",
|
|
116
|
+
items: { type: "string" },
|
|
117
|
+
description: "Company domains for add/remove (e.g., ['stripe.com', 'github.com'])",
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
required: ["action"],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "get_watchlist_events",
|
|
125
|
+
description: "Get recent events for companies on your watchlist: funding, acquisitions, " +
|
|
126
|
+
"executive hires, contracts.",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
days: { type: "number", description: "Look back days (1-90). Default: 7" },
|
|
131
|
+
types: { type: "string", description: "Comma-separated event types" },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "get_usage",
|
|
137
|
+
description: "Check your FundzWatch API usage: calls made, limits, current tier.",
|
|
138
|
+
inputSchema: { type: "object", properties: {} },
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
}));
|
|
142
|
+
// ─── Handle Tool Calls ──────────────────────────────────────────────────
|
|
143
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
144
|
+
const { name, arguments: args = {} } = request.params;
|
|
145
|
+
try {
|
|
146
|
+
switch (name) {
|
|
147
|
+
case "get_scored_leads": {
|
|
148
|
+
const data = await apiRequest("POST", "/signals", {
|
|
149
|
+
min_score: args.min_score ?? 0,
|
|
150
|
+
max_results: args.max_results ?? 25,
|
|
151
|
+
buying_stages: args.buying_stages,
|
|
152
|
+
industries: args.industries,
|
|
153
|
+
});
|
|
154
|
+
const leads = data.signals || [];
|
|
155
|
+
if (leads.length === 0) {
|
|
156
|
+
return textResult("No scored leads found for your current ICP. Leads are generated daily by the AI scoring engine.");
|
|
157
|
+
}
|
|
158
|
+
const summary = leads
|
|
159
|
+
.map((lead, i) => `${i + 1}. **${lead.company_name}** (Score: ${lead.score}/100)\n` +
|
|
160
|
+
` Stage: ${lead.buying_stage} | Priority: ${lead.priority}\n` +
|
|
161
|
+
` Pain Point: ${lead.pain_point}\n` +
|
|
162
|
+
` Outreach: ${lead.outreach_angle}\n` +
|
|
163
|
+
` Action: ${lead.recommended_action}\n` +
|
|
164
|
+
(lead.domain ? ` Domain: ${lead.domain}\n` : "") +
|
|
165
|
+
(lead.industries?.length ? ` Industries: ${lead.industries.join(", ")}\n` : ""))
|
|
166
|
+
.join("\n");
|
|
167
|
+
return textResult(`Found ${data.signals_found} scored leads (showing ${leads.length}):\n\n${summary}`);
|
|
168
|
+
}
|
|
169
|
+
case "get_events": {
|
|
170
|
+
const data = await apiRequest("GET", "/events", {
|
|
171
|
+
types: args.types,
|
|
172
|
+
days: args.days,
|
|
173
|
+
limit: args.limit,
|
|
174
|
+
industries: args.industries,
|
|
175
|
+
locations: args.locations,
|
|
176
|
+
});
|
|
177
|
+
const events = data.events || [];
|
|
178
|
+
if (events.length === 0) {
|
|
179
|
+
return textResult("No events found matching your filters.");
|
|
180
|
+
}
|
|
181
|
+
const summary = events
|
|
182
|
+
.map((e, i) => {
|
|
183
|
+
let detail = `${i + 1}. [${e.type.toUpperCase()}] ${e.title}`;
|
|
184
|
+
if (e.amount)
|
|
185
|
+
detail += ` ($${(e.amount / 1_000_000).toFixed(1)}M)`;
|
|
186
|
+
if (e.series)
|
|
187
|
+
detail += ` - ${e.series}`;
|
|
188
|
+
if (e.date)
|
|
189
|
+
detail += ` | ${e.date}`;
|
|
190
|
+
return detail;
|
|
191
|
+
})
|
|
192
|
+
.join("\n");
|
|
193
|
+
return textResult(`${data.total} events found (showing ${events.length}):\n\n${summary}`);
|
|
194
|
+
}
|
|
195
|
+
case "get_market_pulse": {
|
|
196
|
+
const data = await apiRequest("GET", "/market/pulse");
|
|
197
|
+
const p = data.pulse;
|
|
198
|
+
const text = `Market Pulse (as of ${p.generated_at}):\n\n` +
|
|
199
|
+
`Funding: ${p.funding.count_7d} rounds this week (${p.funding.count_30d} this month), ` +
|
|
200
|
+
`$${(p.funding.total_raised_7d / 1_000_000).toFixed(0)}M raised this week\n` +
|
|
201
|
+
`Acquisitions: ${p.acquisitions.count_7d} this week (${p.acquisitions.count_30d} this month)\n` +
|
|
202
|
+
`Executive Moves: ${p.executive_moves.count_7d} this week (${p.executive_moves.count_30d} this month)\n` +
|
|
203
|
+
`Contracts: ${p.contracts.count_7d} this week (${p.contracts.count_30d} this month)\n` +
|
|
204
|
+
`Product Launches: ${p.product_launches.count_7d} this week (${p.product_launches.count_30d} this month)\n\n` +
|
|
205
|
+
`Largest Rounds This Week:\n` +
|
|
206
|
+
(p.funding.largest_round || [])
|
|
207
|
+
.map((r, i) => ` ${i + 1}. ${r.title} - $${(r.amount / 1_000_000).toFixed(1)}M`)
|
|
208
|
+
.join("\n");
|
|
209
|
+
return textResult(text);
|
|
210
|
+
}
|
|
211
|
+
case "get_market_brief": {
|
|
212
|
+
const data = await apiRequest("GET", "/market/brief");
|
|
213
|
+
const brief = data.brief;
|
|
214
|
+
return textResult(`Strategic Intelligence Brief (${brief.date}):\n\n${brief.text}\n\n` +
|
|
215
|
+
`Companies mentioned: ${(brief.companies || []).join(", ")}`);
|
|
216
|
+
}
|
|
217
|
+
case "manage_watchlist": {
|
|
218
|
+
const action = args.action;
|
|
219
|
+
const domains = args.domains;
|
|
220
|
+
if (action === "list") {
|
|
221
|
+
const data = await apiRequest("GET", "/watchlist");
|
|
222
|
+
const companies = data.companies || [];
|
|
223
|
+
if (companies.length === 0) {
|
|
224
|
+
return textResult("Your watchlist is empty. Add companies with the 'add' action.");
|
|
225
|
+
}
|
|
226
|
+
const list = companies
|
|
227
|
+
.map((c) => `- ${c.name || c.domain} (${c.domain})${c.matched ? "" : " [pending match]"}`)
|
|
228
|
+
.join("\n");
|
|
229
|
+
return textResult(`Watchlist (${data.total}/${data.limit} slots used):\n\n${list}`);
|
|
230
|
+
}
|
|
231
|
+
if (!domains || domains.length === 0) {
|
|
232
|
+
return textResult("Please provide domains to add or remove.");
|
|
233
|
+
}
|
|
234
|
+
if (action === "add") {
|
|
235
|
+
const data = await apiRequest("POST", "/watchlist", { domains });
|
|
236
|
+
return textResult(`Added ${data.added} companies. Already tracked: ${data.already_tracked}. ` +
|
|
237
|
+
`Not found: ${data.not_found}. Total tracked: ${data.total_tracked}.`);
|
|
238
|
+
}
|
|
239
|
+
if (action === "remove") {
|
|
240
|
+
const data = await apiRequest("DELETE", "/watchlist", { domains });
|
|
241
|
+
return textResult(`Removed ${data.removed} companies. Total tracked: ${data.total_tracked}.`);
|
|
242
|
+
}
|
|
243
|
+
return textResult("Invalid action. Use 'list', 'add', or 'remove'.");
|
|
244
|
+
}
|
|
245
|
+
case "get_watchlist_events": {
|
|
246
|
+
const data = await apiRequest("GET", "/watchlist/events", {
|
|
247
|
+
days: args.days,
|
|
248
|
+
types: args.types,
|
|
249
|
+
});
|
|
250
|
+
const events = data.events || [];
|
|
251
|
+
if (events.length === 0) {
|
|
252
|
+
return textResult(`No events found for your ${data.tracked_companies} tracked companies in the last ${data.period_days} days.`);
|
|
253
|
+
}
|
|
254
|
+
const summary = events
|
|
255
|
+
.map((e, i) => {
|
|
256
|
+
let detail = `${i + 1}. [${e.type.toUpperCase()}] ${e.company_name}: ${e.title}`;
|
|
257
|
+
if (e.amount)
|
|
258
|
+
detail += ` ($${(e.amount / 1_000_000).toFixed(1)}M)`;
|
|
259
|
+
if (e.date)
|
|
260
|
+
detail += ` | ${e.date}`;
|
|
261
|
+
return detail;
|
|
262
|
+
})
|
|
263
|
+
.join("\n");
|
|
264
|
+
return textResult(`${data.total} events for ${data.tracked_companies} tracked companies:\n\n${summary}`);
|
|
265
|
+
}
|
|
266
|
+
case "get_usage": {
|
|
267
|
+
const data = await apiRequest("GET", "/usage");
|
|
268
|
+
const text = `FundzWatch Usage (${data.current_period}):\n\n` +
|
|
269
|
+
`Tier: ${data.tier}\n` +
|
|
270
|
+
`API Calls: ${data.api_calls_used} / ${data.limits.api_calls_monthly}\n` +
|
|
271
|
+
`AI Score Calls: ${data.ai_score_calls_used} / ${data.limits.ai_score_calls_monthly}\n` +
|
|
272
|
+
(data.last_api_call ? `Last API Call: ${data.last_api_call}` : "");
|
|
273
|
+
return textResult(text);
|
|
274
|
+
}
|
|
275
|
+
default:
|
|
276
|
+
return textResult(`Unknown tool: ${name}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
return textResult(`Error: ${err.message}`);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
// ─── Start Server ───────────────────────────────────────────────────────
|
|
284
|
+
async function main() {
|
|
285
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
286
|
+
await server.connect(transport);
|
|
287
|
+
console.error("FundzWatch MCP server running on stdio");
|
|
288
|
+
}
|
|
289
|
+
main().catch((error) => {
|
|
290
|
+
console.error("Fatal error:", error);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
});
|
|
293
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fundzwatch/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for FundzWatch.ai — Real-time business event intelligence for AI agents",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"fundzwatch-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc && node -e \"const fs=require('fs');const f='dist/index.js';const c=fs.readFileSync(f,'utf8');if(!c.startsWith('#!')){fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c);}\" && chmod +x dist/index.js",
|
|
11
|
+
"dev": "ts-node src/index.ts",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"fundzwatch",
|
|
18
|
+
"business-events",
|
|
19
|
+
"ai-agents",
|
|
20
|
+
"lead-generation",
|
|
21
|
+
"funding-events",
|
|
22
|
+
"sales-intelligence"
|
|
23
|
+
],
|
|
24
|
+
"author": "Fundz Inc.",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/fundz/fundzwatch-mcp"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
32
|
+
"zod": "^3.25.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"typescript": "^5.7.0",
|
|
36
|
+
"@types/node": "^22.0.0",
|
|
37
|
+
"ts-node": "^10.9.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
ListToolsRequestSchema,
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
|
|
8
|
+
const API_BASE = "https://api.fundz.net/api/v1/watch";
|
|
9
|
+
|
|
10
|
+
function getApiKey(): string {
|
|
11
|
+
const key = process.env.FUNDZWATCH_API_KEY;
|
|
12
|
+
if (!key) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"FUNDZWATCH_API_KEY environment variable is required. " +
|
|
15
|
+
"Get a free key at https://fundzwatch.ai/onboarding"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return key;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function apiRequest(
|
|
22
|
+
method: string,
|
|
23
|
+
path: string,
|
|
24
|
+
params?: Record<string, any>
|
|
25
|
+
): Promise<any> {
|
|
26
|
+
const apiKey = getApiKey();
|
|
27
|
+
const url = new URL(`${API_BASE}${path}`);
|
|
28
|
+
|
|
29
|
+
const options: RequestInit = {
|
|
30
|
+
method,
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: `Bearer ${apiKey}`,
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
"User-Agent": "fundzwatch-mcp/1.0.0",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (method === "GET" && params) {
|
|
39
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
40
|
+
if (value !== undefined && value !== null) {
|
|
41
|
+
url.searchParams.append(key, String(value));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
} else if (method !== "GET" && params) {
|
|
45
|
+
options.body = JSON.stringify(params);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const response = await fetch(url.toString(), options);
|
|
49
|
+
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const errBody: any = await response.json().catch(() => ({ message: response.statusText }));
|
|
52
|
+
throw new Error(`API error ${response.status}: ${errBody.message || errBody.error || response.statusText}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return response.json();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function textResult(text: string) {
|
|
59
|
+
return { content: [{ type: "text" as const, text }] };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─── Server Setup ───────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
const server = new Server(
|
|
65
|
+
{ name: "fundzwatch", version: "1.0.0" },
|
|
66
|
+
{ capabilities: { tools: {} } }
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// ─── List Tools ─────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
72
|
+
tools: [
|
|
73
|
+
{
|
|
74
|
+
name: "get_scored_leads",
|
|
75
|
+
description:
|
|
76
|
+
"Get AI-scored sales leads based on your ICP (Ideal Customer Profile). " +
|
|
77
|
+
"Returns companies with recent business events scored by AI for buyer intent, " +
|
|
78
|
+
"buying stage, and recommended outreach strategy.",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object" as const,
|
|
81
|
+
properties: {
|
|
82
|
+
min_score: { type: "number", description: "Minimum buyer intent score (0-100). Default: 0" },
|
|
83
|
+
max_results: { type: "number", description: "Max leads to return (1-50). Default: 25" },
|
|
84
|
+
buying_stages: {
|
|
85
|
+
type: "array",
|
|
86
|
+
items: { type: "string" },
|
|
87
|
+
description: "Filter by buying stage: 'Active Evaluation', 'Decision', 'Research', 'Awareness'",
|
|
88
|
+
},
|
|
89
|
+
industries: {
|
|
90
|
+
type: "array",
|
|
91
|
+
items: { type: "string" },
|
|
92
|
+
description: "Filter by industry (e.g., ['SaaS', 'HealthTech', 'FinTech'])",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "get_events",
|
|
99
|
+
description:
|
|
100
|
+
"Get real-time business events: funding rounds, acquisitions, executive hires, " +
|
|
101
|
+
"government contracts, and product launches. Filter by type, industry, and location.",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object" as const,
|
|
104
|
+
properties: {
|
|
105
|
+
types: { type: "string", description: "Comma-separated: funding, acquisition, hiring, contract, product_launch. Default: all" },
|
|
106
|
+
days: { type: "number", description: "Look back days (1-90). Default: 7" },
|
|
107
|
+
limit: { type: "number", description: "Max events (1-200). Default: 50" },
|
|
108
|
+
industries: { type: "string", description: "Comma-separated industries" },
|
|
109
|
+
locations: { type: "string", description: "Comma-separated locations" },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "get_market_pulse",
|
|
115
|
+
description:
|
|
116
|
+
"Get real-time market activity overview: funding totals, acquisition counts, " +
|
|
117
|
+
"executive moves, contracts, and product launches for the past 7 and 30 days.",
|
|
118
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "get_market_brief",
|
|
122
|
+
description:
|
|
123
|
+
"Get today's AI-generated strategic intelligence brief with narrative analysis " +
|
|
124
|
+
"of the most important market movements, patterns, and opportunities.",
|
|
125
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "manage_watchlist",
|
|
129
|
+
description:
|
|
130
|
+
"Add, remove, or list companies on your watchlist. Tracked companies generate " +
|
|
131
|
+
"alerts when they have new events.",
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: "object" as const,
|
|
134
|
+
properties: {
|
|
135
|
+
action: {
|
|
136
|
+
type: "string",
|
|
137
|
+
enum: ["list", "add", "remove"],
|
|
138
|
+
description: "Action: 'list' to view, 'add' to track, 'remove' to untrack",
|
|
139
|
+
},
|
|
140
|
+
domains: {
|
|
141
|
+
type: "array",
|
|
142
|
+
items: { type: "string" },
|
|
143
|
+
description: "Company domains for add/remove (e.g., ['stripe.com', 'github.com'])",
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
required: ["action"],
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "get_watchlist_events",
|
|
151
|
+
description:
|
|
152
|
+
"Get recent events for companies on your watchlist: funding, acquisitions, " +
|
|
153
|
+
"executive hires, contracts.",
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: "object" as const,
|
|
156
|
+
properties: {
|
|
157
|
+
days: { type: "number", description: "Look back days (1-90). Default: 7" },
|
|
158
|
+
types: { type: "string", description: "Comma-separated event types" },
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "get_usage",
|
|
164
|
+
description: "Check your FundzWatch API usage: calls made, limits, current tier.",
|
|
165
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
}));
|
|
169
|
+
|
|
170
|
+
// ─── Handle Tool Calls ──────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
173
|
+
const { name, arguments: args = {} } = request.params;
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
switch (name) {
|
|
177
|
+
case "get_scored_leads": {
|
|
178
|
+
const data = await apiRequest("POST", "/signals", {
|
|
179
|
+
min_score: (args as any).min_score ?? 0,
|
|
180
|
+
max_results: (args as any).max_results ?? 25,
|
|
181
|
+
buying_stages: (args as any).buying_stages,
|
|
182
|
+
industries: (args as any).industries,
|
|
183
|
+
});
|
|
184
|
+
const leads = data.signals || [];
|
|
185
|
+
if (leads.length === 0) {
|
|
186
|
+
return textResult("No scored leads found for your current ICP. Leads are generated daily by the AI scoring engine.");
|
|
187
|
+
}
|
|
188
|
+
const summary = leads
|
|
189
|
+
.map(
|
|
190
|
+
(lead: any, i: number) =>
|
|
191
|
+
`${i + 1}. **${lead.company_name}** (Score: ${lead.score}/100)\n` +
|
|
192
|
+
` Stage: ${lead.buying_stage} | Priority: ${lead.priority}\n` +
|
|
193
|
+
` Pain Point: ${lead.pain_point}\n` +
|
|
194
|
+
` Outreach: ${lead.outreach_angle}\n` +
|
|
195
|
+
` Action: ${lead.recommended_action}\n` +
|
|
196
|
+
(lead.domain ? ` Domain: ${lead.domain}\n` : "") +
|
|
197
|
+
(lead.industries?.length ? ` Industries: ${lead.industries.join(", ")}\n` : "")
|
|
198
|
+
)
|
|
199
|
+
.join("\n");
|
|
200
|
+
return textResult(`Found ${data.signals_found} scored leads (showing ${leads.length}):\n\n${summary}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
case "get_events": {
|
|
204
|
+
const data = await apiRequest("GET", "/events", {
|
|
205
|
+
types: (args as any).types,
|
|
206
|
+
days: (args as any).days,
|
|
207
|
+
limit: (args as any).limit,
|
|
208
|
+
industries: (args as any).industries,
|
|
209
|
+
locations: (args as any).locations,
|
|
210
|
+
});
|
|
211
|
+
const events = data.events || [];
|
|
212
|
+
if (events.length === 0) {
|
|
213
|
+
return textResult("No events found matching your filters.");
|
|
214
|
+
}
|
|
215
|
+
const summary = events
|
|
216
|
+
.map((e: any, i: number) => {
|
|
217
|
+
let detail = `${i + 1}. [${e.type.toUpperCase()}] ${e.title}`;
|
|
218
|
+
if (e.amount) detail += ` ($${(e.amount / 1_000_000).toFixed(1)}M)`;
|
|
219
|
+
if (e.series) detail += ` - ${e.series}`;
|
|
220
|
+
if (e.date) detail += ` | ${e.date}`;
|
|
221
|
+
return detail;
|
|
222
|
+
})
|
|
223
|
+
.join("\n");
|
|
224
|
+
return textResult(`${data.total} events found (showing ${events.length}):\n\n${summary}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
case "get_market_pulse": {
|
|
228
|
+
const data = await apiRequest("GET", "/market/pulse");
|
|
229
|
+
const p = data.pulse;
|
|
230
|
+
const text =
|
|
231
|
+
`Market Pulse (as of ${p.generated_at}):\n\n` +
|
|
232
|
+
`Funding: ${p.funding.count_7d} rounds this week (${p.funding.count_30d} this month), ` +
|
|
233
|
+
`$${(p.funding.total_raised_7d / 1_000_000).toFixed(0)}M raised this week\n` +
|
|
234
|
+
`Acquisitions: ${p.acquisitions.count_7d} this week (${p.acquisitions.count_30d} this month)\n` +
|
|
235
|
+
`Executive Moves: ${p.executive_moves.count_7d} this week (${p.executive_moves.count_30d} this month)\n` +
|
|
236
|
+
`Contracts: ${p.contracts.count_7d} this week (${p.contracts.count_30d} this month)\n` +
|
|
237
|
+
`Product Launches: ${p.product_launches.count_7d} this week (${p.product_launches.count_30d} this month)\n\n` +
|
|
238
|
+
`Largest Rounds This Week:\n` +
|
|
239
|
+
(p.funding.largest_round || [])
|
|
240
|
+
.map((r: any, i: number) => ` ${i + 1}. ${r.title} - $${(r.amount / 1_000_000).toFixed(1)}M`)
|
|
241
|
+
.join("\n");
|
|
242
|
+
return textResult(text);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
case "get_market_brief": {
|
|
246
|
+
const data = await apiRequest("GET", "/market/brief");
|
|
247
|
+
const brief = data.brief;
|
|
248
|
+
return textResult(
|
|
249
|
+
`Strategic Intelligence Brief (${brief.date}):\n\n${brief.text}\n\n` +
|
|
250
|
+
`Companies mentioned: ${(brief.companies || []).join(", ")}`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
case "manage_watchlist": {
|
|
255
|
+
const action = (args as any).action;
|
|
256
|
+
const domains = (args as any).domains;
|
|
257
|
+
|
|
258
|
+
if (action === "list") {
|
|
259
|
+
const data = await apiRequest("GET", "/watchlist");
|
|
260
|
+
const companies = data.companies || [];
|
|
261
|
+
if (companies.length === 0) {
|
|
262
|
+
return textResult("Your watchlist is empty. Add companies with the 'add' action.");
|
|
263
|
+
}
|
|
264
|
+
const list = companies
|
|
265
|
+
.map((c: any) => `- ${c.name || c.domain} (${c.domain})${c.matched ? "" : " [pending match]"}`)
|
|
266
|
+
.join("\n");
|
|
267
|
+
return textResult(`Watchlist (${data.total}/${data.limit} slots used):\n\n${list}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!domains || domains.length === 0) {
|
|
271
|
+
return textResult("Please provide domains to add or remove.");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (action === "add") {
|
|
275
|
+
const data = await apiRequest("POST", "/watchlist", { domains });
|
|
276
|
+
return textResult(
|
|
277
|
+
`Added ${data.added} companies. Already tracked: ${data.already_tracked}. ` +
|
|
278
|
+
`Not found: ${data.not_found}. Total tracked: ${data.total_tracked}.`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (action === "remove") {
|
|
283
|
+
const data = await apiRequest("DELETE", "/watchlist", { domains });
|
|
284
|
+
return textResult(`Removed ${data.removed} companies. Total tracked: ${data.total_tracked}.`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return textResult("Invalid action. Use 'list', 'add', or 'remove'.");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
case "get_watchlist_events": {
|
|
291
|
+
const data = await apiRequest("GET", "/watchlist/events", {
|
|
292
|
+
days: (args as any).days,
|
|
293
|
+
types: (args as any).types,
|
|
294
|
+
});
|
|
295
|
+
const events = data.events || [];
|
|
296
|
+
if (events.length === 0) {
|
|
297
|
+
return textResult(
|
|
298
|
+
`No events found for your ${data.tracked_companies} tracked companies in the last ${data.period_days} days.`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const summary = events
|
|
302
|
+
.map((e: any, i: number) => {
|
|
303
|
+
let detail = `${i + 1}. [${e.type.toUpperCase()}] ${e.company_name}: ${e.title}`;
|
|
304
|
+
if (e.amount) detail += ` ($${(e.amount / 1_000_000).toFixed(1)}M)`;
|
|
305
|
+
if (e.date) detail += ` | ${e.date}`;
|
|
306
|
+
return detail;
|
|
307
|
+
})
|
|
308
|
+
.join("\n");
|
|
309
|
+
return textResult(`${data.total} events for ${data.tracked_companies} tracked companies:\n\n${summary}`);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
case "get_usage": {
|
|
313
|
+
const data = await apiRequest("GET", "/usage");
|
|
314
|
+
const text =
|
|
315
|
+
`FundzWatch Usage (${data.current_period}):\n\n` +
|
|
316
|
+
`Tier: ${data.tier}\n` +
|
|
317
|
+
`API Calls: ${data.api_calls_used} / ${data.limits.api_calls_monthly}\n` +
|
|
318
|
+
`AI Score Calls: ${data.ai_score_calls_used} / ${data.limits.ai_score_calls_monthly}\n` +
|
|
319
|
+
(data.last_api_call ? `Last API Call: ${data.last_api_call}` : "");
|
|
320
|
+
return textResult(text);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
default:
|
|
324
|
+
return textResult(`Unknown tool: ${name}`);
|
|
325
|
+
}
|
|
326
|
+
} catch (err: any) {
|
|
327
|
+
return textResult(`Error: ${err.message}`);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// ─── Start Server ───────────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
async function main() {
|
|
334
|
+
const transport = new StdioServerTransport();
|
|
335
|
+
await server.connect(transport);
|
|
336
|
+
console.error("FundzWatch MCP server running on stdio");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
main().catch((error) => {
|
|
340
|
+
console.error("Fatal error:", error);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|