nuvc-mcp 1.4.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 +128 -0
- package/index.js +35 -0
- package/lib/tools.js +833 -0
- package/manifest.json +57 -0
- package/package.json +35 -0
- package/smithery.yaml +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# NUVC MCP Server
|
|
2
|
+
|
|
3
|
+
> Two layers of VC-grade intelligence for Claude, Cursor, and any MCP-compatible agent:
|
|
4
|
+
> a **Judgement Layer** that scores any startup or fund with IC-ready conviction (not a generic
|
|
5
|
+
> number), and a **Matching Layer** that finds the right investors from 9,000+ verified profiles.
|
|
6
|
+
> Built for **founders** validating a raise, **VCs** screening deals, and **LPs** screening funds.
|
|
7
|
+
|
|
8
|
+
[](https://smithery.ai/server/nuvc)
|
|
9
|
+
[](https://npmjs.com/package/nuvc-mcp)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
## Tools
|
|
13
|
+
|
|
14
|
+
### For Founders
|
|
15
|
+
|
|
16
|
+
| Tool | What it does |
|
|
17
|
+
|------|-------------|
|
|
18
|
+
| `nuvc_score` | VCGrade 0-10 score across 5 dimensions — like having a VC partner review your idea |
|
|
19
|
+
| `nuvc_match_investors` | Find best-fit investors from 9,000+ database using AI semantic matching |
|
|
20
|
+
| `nuvc_analyze` | Market sizing, competitive analysis, financial review, or full pitch evaluation |
|
|
21
|
+
| `nuvc_roast` | Brutally honest but constructive VC-perspective startup roast |
|
|
22
|
+
| `nuvc_extract` | Extract structured fields (revenue, stage, team, metrics) from any pitch text |
|
|
23
|
+
| `nuvc_venture_math` | Compute burn multiple, LTV/CAC, Rule of 40, runway, dilution — with stage benchmarks |
|
|
24
|
+
|
|
25
|
+
### For VCs & LPs
|
|
26
|
+
|
|
27
|
+
| Tool | What it does |
|
|
28
|
+
|------|-------------|
|
|
29
|
+
| `nuvc_score` | Screen inbound deals — score any pitch 0-10. With a mandate set, also returns a **two-way fit** (does it fit your thesis? would the founder want you?) + 0-100 ConvictionScore + proceed/watch/pass |
|
|
30
|
+
| `nuvc_set_mandate` | Set your fund mandate once (sector, stage, cheque size, thesis) — every `nuvc_score` then returns a mandate↔deck fit automatically |
|
|
31
|
+
| `nuvc_screen_fund` | Screen a VC/PE fund the LP way — 6-dimension LP screen + conviction verdict + decision, optionally against your mandate |
|
|
32
|
+
| `nuvc_match_investors` | Find co-investors or syndicate partners for a deal from 9,000+ investors |
|
|
33
|
+
| `nuvc_venture_math` | Validate startup financials against stage-appropriate VC benchmarks |
|
|
34
|
+
| `nuvc_fund_economics` | Fund economics from an LP lens — fee drag, GP alignment, J-curve, return decomposition |
|
|
35
|
+
| `nuvc_fund_economics` | Portfolio construction — investable capital, implied portfolio count, deployment capacity |
|
|
36
|
+
| `nuvc_analyze` | Competitive landscape or market analysis for diligence |
|
|
37
|
+
|
|
38
|
+
### Utility
|
|
39
|
+
|
|
40
|
+
| Tool | What it does |
|
|
41
|
+
|------|-------------|
|
|
42
|
+
| `nuvc_extract` | Extract structured data from any pitch, CIM, or deal memo |
|
|
43
|
+
| `nuvc_models` | List available AI models and provider health |
|
|
44
|
+
|
|
45
|
+
## Setup
|
|
46
|
+
|
|
47
|
+
### 1. Get an API key
|
|
48
|
+
|
|
49
|
+
Free: **2 score credits** (one-time, never expire) at **[nuvc.ai/api-platform/keys](https://nuvc.ai/api-platform/keys)** — no credit card.
|
|
50
|
+
|
|
51
|
+
### 2. Install via Smithery (Claude Desktop / Cursor)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx @smithery/cli install nuvc --client claude
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 3. Or run directly
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
export NUVC_API_KEY=nuvc_your_key_here
|
|
61
|
+
npx nuvc-mcp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 4. Claude Desktop config
|
|
65
|
+
|
|
66
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"nuvc": {
|
|
72
|
+
"command": "npx",
|
|
73
|
+
"args": ["nuvc-mcp"],
|
|
74
|
+
"env": {
|
|
75
|
+
"NUVC_API_KEY": "nuvc_your_key_here"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 5. Claude Code
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
claude mcp add nuvc -- npx nuvc-mcp
|
|
86
|
+
# Then set NUVC_API_KEY in your environment
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Usage Examples
|
|
90
|
+
|
|
91
|
+
Once connected, just ask your AI agent:
|
|
92
|
+
|
|
93
|
+
**Founders:**
|
|
94
|
+
- *"Score my startup idea — an AI tool that helps solo founders validate ideas"*
|
|
95
|
+
- *"Find investors who back AI/ML startups at seed stage in Australia"*
|
|
96
|
+
- *"What's the market like for AI-powered HR tools?"*
|
|
97
|
+
- *"Roast this idea: Uber for dog walking but with blockchain"*
|
|
98
|
+
- *"I have $1.2M ARR, $80K burn, 18 months runway. How do my metrics stack up for Series A?"*
|
|
99
|
+
|
|
100
|
+
**VCs:**
|
|
101
|
+
- *"Score this pitch: [paste founder's pitch text]"*
|
|
102
|
+
- *"Find co-investors who do seed-stage FinTech deals in Southeast Asia"*
|
|
103
|
+
- *"This founder says $500K ARR, 15% MoM growth, $200K burn at Seed. Run the math."*
|
|
104
|
+
- *"We're raising a $30M Fund II with 2% fee, 20% carry, $500K avg check. What does portfolio construction look like?"*
|
|
105
|
+
- *"Evaluate this fund: $50M AUM, 2.5x TVPI, 1.2x DPI, 18% net IRR, 2020 vintage"*
|
|
106
|
+
|
|
107
|
+
## Pricing
|
|
108
|
+
|
|
109
|
+
Pure pay-as-you-go — no subscription. Credits never expire and are shared across MCP, REST API, and the web app.
|
|
110
|
+
|
|
111
|
+
| Plan | Price | Score Credits |
|
|
112
|
+
|------|-------|---------------|
|
|
113
|
+
| Free | $0 (no card) | 2 (one-time) |
|
|
114
|
+
| Starter Pack | $99 AUD one-time | 50 (~$1.98/score) |
|
|
115
|
+
| Volume | Sales-led | [Talk to us](mailto:hello@nuvc.ai) |
|
|
116
|
+
|
|
117
|
+
One credit = one judgement-or-match action: `nuvc_score`, `nuvc_screen_fund`, or `nuvc_match_investors`.
|
|
118
|
+
`nuvc_venture_math`, `nuvc_fund_economics`, library reads, and lookups are **free** — they never consume a credit.
|
|
119
|
+
|
|
120
|
+
[Get your API key →](https://nuvc.ai/api-platform/keys)
|
|
121
|
+
|
|
122
|
+
## About NUVC
|
|
123
|
+
|
|
124
|
+
NUVC is a VC-grade intelligence platform used by accelerators, angel networks, emerging fund managers, and family offices. Our scoring engine is calibrated on 1,400+ real VC investment decisions and matches against 9,000+ verified investor profiles.
|
|
125
|
+
|
|
126
|
+
- **Website:** [nuvc.ai](https://nuvc.ai)
|
|
127
|
+
- **API Docs:** [nuvc.ai/api-platform/docs](https://nuvc.ai/api-platform/docs)
|
|
128
|
+
- **Support:** hello@nuvc.ai
|
package/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NUVC MCP Server — stdio transport (OpenClaw, Claude Desktop local, npx)
|
|
5
|
+
* Get a free API key at https://nuvc.ai/api-platform/keys
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
9
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { TOOLS, HANDLERS } from "./lib/tools.js";
|
|
12
|
+
|
|
13
|
+
const server = new Server(
|
|
14
|
+
{ name: "nuvc", version: "1.4.0" },
|
|
15
|
+
{ capabilities: { tools: {} } }
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
19
|
+
|
|
20
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
21
|
+
const { name, arguments: args } = request.params;
|
|
22
|
+
const handler = HANDLERS[name];
|
|
23
|
+
if (!handler) {
|
|
24
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const result = await handler(args || {});
|
|
28
|
+
return { content: [{ type: "text", text: result }] };
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const transport = new StdioServerTransport();
|
|
35
|
+
await server.connect(transport);
|
package/lib/tools.js
ADDED
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NUVC tool definitions and handlers — shared by stdio and HTTP transports.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const API_BASE = "https://api.nuvc.ai/api/v3";
|
|
6
|
+
const TIMEOUT_MS = 30_000;
|
|
7
|
+
|
|
8
|
+
// In-process mandate for the current MCP session. Set once via nuvc_set_mandate;
|
|
9
|
+
// every nuvc_score call then returns a two-way (mutual) mandate<->deck fit verdict.
|
|
10
|
+
// Persists for the life of the server process (one Claude Desktop / Cursor session).
|
|
11
|
+
let activeMandate = null;
|
|
12
|
+
|
|
13
|
+
// Build a clean mandate object from loosely-typed args (drop empty fields).
|
|
14
|
+
function buildMandate(args = {}) {
|
|
15
|
+
const m = {};
|
|
16
|
+
if (Array.isArray(args.sectors) && args.sectors.length) m.sectors = args.sectors;
|
|
17
|
+
if (Array.isArray(args.stages) && args.stages.length) m.stages = args.stages;
|
|
18
|
+
if (args.geography) m.geography = args.geography;
|
|
19
|
+
if (args.check_size_min != null) m.check_size_min = args.check_size_min;
|
|
20
|
+
if (args.check_size_max != null) m.check_size_max = args.check_size_max;
|
|
21
|
+
if (args.thesis) m.thesis = args.thesis;
|
|
22
|
+
if (args.anti_thesis) m.anti_thesis = args.anti_thesis;
|
|
23
|
+
return Object.keys(m).length ? m : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// API helper
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
function getApiKey() {
|
|
31
|
+
const key = process.env.NUVC_API_KEY;
|
|
32
|
+
if (!key || !key.startsWith("nuvc_")) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"NUVC_API_KEY not set or invalid. Get a free key at https://nuvc.ai/api-platform/keys"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return key;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function apiCall(method, path, body) {
|
|
41
|
+
const controller = new AbortController();
|
|
42
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch(`${API_BASE}${path}`, {
|
|
45
|
+
method,
|
|
46
|
+
headers: {
|
|
47
|
+
Authorization: `Bearer ${getApiKey()}`,
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
"User-Agent": "nuvc-mcp/1.4",
|
|
50
|
+
},
|
|
51
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
52
|
+
signal: controller.signal,
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const err = await res.json().catch(() => ({ error: { message: res.statusText } }));
|
|
56
|
+
const msg = err?.error?.message || err?.detail?.message || err?.detail || res.statusText;
|
|
57
|
+
if (res.status === 401) throw new Error(`Auth error: ${msg}. Check NUVC_API_KEY at https://nuvc.ai/api-platform/keys`);
|
|
58
|
+
if (res.status === 402) throw new Error(`Out of score credits. Top up at https://nuvc.ai/api-platform/billing — $99 AUD = 50 more scores. Library reads and venture-math tools are always free.`);
|
|
59
|
+
if (res.status === 429) throw new Error(`Rate limit reached. Wait a moment and retry, or buy a Starter Pack for a higher limit at https://nuvc.ai/api-platform/billing`);
|
|
60
|
+
throw new Error(`API error (${res.status}): ${msg}`);
|
|
61
|
+
}
|
|
62
|
+
return res.json();
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (err.name === "AbortError") throw new Error("Request timed out after 30s");
|
|
65
|
+
throw err;
|
|
66
|
+
} finally {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Tool definitions
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
export const TOOLS = [
|
|
76
|
+
{
|
|
77
|
+
name: "nuvc_score",
|
|
78
|
+
description:
|
|
79
|
+
"Get instant, honest VC-grade feedback on a pitch deck or startup: a 0-10 score across 5 dimensions " +
|
|
80
|
+
"(Problem & Market, Solution & Product, Business Model, Traction & Metrics, and Team & Execution) with " +
|
|
81
|
+
"the strengths, gaps, and questions an investor will ask — so a founder sees what a VC sees and gets " +
|
|
82
|
+
"fully prepared before burning a warm intro. Calibrated on 1,400+ real VC investment decisions. " +
|
|
83
|
+
"If you have set a mandate (nuvc_set_mandate) — or pass one here — the result also includes a " +
|
|
84
|
+
"TWO-WAY mandate<->deck fit: Direction A (does this deck fit YOUR thesis?), Direction B (would the " +
|
|
85
|
+
"founder want YOU?), a mutual-fit score, a 0-100 ConvictionScore, and a proceed/watch/pass decision.",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {
|
|
89
|
+
text: { type: "string", description: "Business idea, startup description, or pitch content to score" },
|
|
90
|
+
mandate: {
|
|
91
|
+
type: "object",
|
|
92
|
+
description:
|
|
93
|
+
"Optional investor mandate to score this deck against. If omitted, the mandate set via " +
|
|
94
|
+
"nuvc_set_mandate (if any) is used. Supplying it triggers the two-way fit verdict.",
|
|
95
|
+
properties: {
|
|
96
|
+
sectors: { type: "array", items: { type: "string" }, description: "Sectors you back (e.g. ['FinTech','AI/ML'])" },
|
|
97
|
+
stages: { type: "array", items: { type: "string" }, description: "Stages you invest at (pre_seed..growth)" },
|
|
98
|
+
geography: { type: "string", description: "Your geographic focus (e.g. 'ANZ', 'global')" },
|
|
99
|
+
check_size_min: { type: "number", description: "Minimum cheque (USD)" },
|
|
100
|
+
check_size_max: { type: "number", description: "Maximum cheque (USD)" },
|
|
101
|
+
thesis: { type: "string", description: "Your investment thesis" },
|
|
102
|
+
anti_thesis: { type: "string", description: "What you never back" },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
stage: { type: "string", description: "This deal's stage (sharpens fit), e.g. 'seed'" },
|
|
106
|
+
industry: { type: "string", description: "This deal's primary sector, e.g. 'FinTech'" },
|
|
107
|
+
geography: { type: "string", description: "This company's location/geo" },
|
|
108
|
+
raise_amount: { type: "number", description: "This deal's target raise (USD) — drives the cheque-fit check" },
|
|
109
|
+
},
|
|
110
|
+
required: ["text"],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "nuvc_set_mandate",
|
|
115
|
+
description:
|
|
116
|
+
"Set YOUR investor mandate for this session (sector focus, stages, cheque size, thesis, anti-thesis). " +
|
|
117
|
+
"Once set, every nuvc_score call returns a two-way mandate<->deck fit automatically — you don't have to " +
|
|
118
|
+
"repeat the mandate each time. The mandate persists for the current session. Use nuvc_get_mandate to review it.",
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
sectors: { type: "array", items: { type: "string" }, description: "Sectors you back (e.g. ['FinTech','AI/ML'])" },
|
|
123
|
+
stages: { type: "array", items: { type: "string" }, description: "Stages you invest at (pre_seed, seed, series_a, series_b, series_c, growth)" },
|
|
124
|
+
geography: { type: "string", description: "Your geographic focus (e.g. 'ANZ', 'US', 'global')" },
|
|
125
|
+
check_size_min: { type: "number", description: "Minimum cheque you write (USD)" },
|
|
126
|
+
check_size_max: { type: "number", description: "Maximum cheque you write (USD)" },
|
|
127
|
+
thesis: { type: "string", description: "Your investment thesis in plain language" },
|
|
128
|
+
anti_thesis: { type: "string", description: "What you never back (deal-breakers)" },
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "nuvc_get_mandate",
|
|
134
|
+
description: "Show the investor mandate currently set for this session (used by nuvc_score for two-way fit).",
|
|
135
|
+
inputSchema: { type: "object", properties: {} },
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "nuvc_analyze",
|
|
139
|
+
description:
|
|
140
|
+
"Analyze a market, competitive landscape, financial data, or full pitch deck. " +
|
|
141
|
+
"Returns structured analysis with insights, opportunities, and risks.",
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: {
|
|
145
|
+
text: { type: "string", description: "Content to analyze" },
|
|
146
|
+
analysis_type: {
|
|
147
|
+
type: "string",
|
|
148
|
+
enum: ["market", "competitive", "financial", "pitch_deck", "general"],
|
|
149
|
+
description: "Type of analysis. Defaults to 'market'.",
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
required: ["text"],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "nuvc_roast",
|
|
157
|
+
description:
|
|
158
|
+
"Brutally honest but constructive VC-perspective feedback on any startup idea — the hard truths a " +
|
|
159
|
+
"polite friend or advisor won't tell you, delivered before a real investor does (and before you burn " +
|
|
160
|
+
"the intro). Returns sharp, actionable feedback with a verdict.",
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: {
|
|
164
|
+
text: { type: "string", description: "Startup idea or pitch to roast" },
|
|
165
|
+
},
|
|
166
|
+
required: ["text"],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "nuvc_extract",
|
|
171
|
+
description:
|
|
172
|
+
"Extract structured data from pitch text. Returns fields like revenue, growth rate, team size, funding stage, and key metrics.",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
text: { type: "string", description: "Pitch or business description to extract data from" },
|
|
177
|
+
extraction_type: {
|
|
178
|
+
type: "string",
|
|
179
|
+
enum: ["general", "company", "financial", "contact"],
|
|
180
|
+
description: "Type of extraction. Defaults to 'company'.",
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
required: ["text"],
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "nuvc_models",
|
|
188
|
+
description: "List available AI models, provider health, and embedding models.",
|
|
189
|
+
inputSchema: { type: "object", properties: {} },
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "nuvc_match_investors",
|
|
193
|
+
description:
|
|
194
|
+
"Meet the investors most likely to fund a startup — a ranked, thesis-fit shortlist from NUVC's 9,000+ " +
|
|
195
|
+
"investor database (a well-matched few beats a mass blast to hundreds). " +
|
|
196
|
+
"Uses AI semantic matching (embeddings) to compare the startup description against each investor's " +
|
|
197
|
+
"thesis, focus areas, stage preference, and track record. Returns ranked matches, each with a reason " +
|
|
198
|
+
"and brief — thesis-alignment reasoning, check size ranges, and investor quality signals — so the " +
|
|
199
|
+
"founder can out-research the room before the meeting. " +
|
|
200
|
+
"Contact details are available on the NUVC platform. This tool cannot be replicated locally — " +
|
|
201
|
+
"it searches a proprietary, continuously enriched investor database.",
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: {
|
|
205
|
+
text: {
|
|
206
|
+
type: "string",
|
|
207
|
+
description:
|
|
208
|
+
"Startup description, pitch content, or company summary to match against investors. " +
|
|
209
|
+
"The more detail you provide, the better the matches.",
|
|
210
|
+
},
|
|
211
|
+
industry: {
|
|
212
|
+
type: "string",
|
|
213
|
+
description: "Primary industry/sector (e.g. 'FinTech', 'HealthTech', 'SaaS', 'AI/ML'). Default: 'Technology'.",
|
|
214
|
+
},
|
|
215
|
+
stage: {
|
|
216
|
+
type: "string",
|
|
217
|
+
enum: ["pre_seed", "seed", "series_a", "series_b", "series_c", "growth"],
|
|
218
|
+
description: "Current funding stage. Default: 'seed'.",
|
|
219
|
+
},
|
|
220
|
+
raise_amount: {
|
|
221
|
+
type: "number",
|
|
222
|
+
description: "Target raise amount in USD (helps filter by check size).",
|
|
223
|
+
},
|
|
224
|
+
location: {
|
|
225
|
+
type: "string",
|
|
226
|
+
description: "Company location (e.g. 'Sydney, Australia'). Helps prioritize geographically relevant investors.",
|
|
227
|
+
},
|
|
228
|
+
limit: {
|
|
229
|
+
type: "integer",
|
|
230
|
+
description: "Number of matches to return (1-10). Default: 5.",
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
required: ["text"],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: "nuvc_screen_fund",
|
|
238
|
+
description:
|
|
239
|
+
"Screen a VC or PE fund the way a family office / LP allocator does — through NUVC's " +
|
|
240
|
+
"LP-grade fund engine. Returns a 6-dimension screen (GP team quality, deal flow access, " +
|
|
241
|
+
"track record, fund terms, fund status & timing, LP network), a one-sentence conviction " +
|
|
242
|
+
"verdict, a Pass/Watch/proceed decision, confidence, strengths, and red flags. " +
|
|
243
|
+
"Optionally pass your LP mandate (sectors, stages, geography, thesis) to shape thesis-fit. " +
|
|
244
|
+
"This is the same engine behind the NUVC Family Office product — it cannot be replicated " +
|
|
245
|
+
"locally. Use it to triage a fund before committing diligence time.",
|
|
246
|
+
inputSchema: {
|
|
247
|
+
type: "object",
|
|
248
|
+
properties: {
|
|
249
|
+
fund_name: { type: "string", description: "Fund name (e.g. 'Blackbird Ventures Fund IV')" },
|
|
250
|
+
gp_name: { type: "string", description: "GP / firm name" },
|
|
251
|
+
strategy: { type: "string", description: "Fund strategy (e.g. 'Early-stage venture', 'Growth buyout')" },
|
|
252
|
+
vintage_year: { type: "integer", description: "Vintage year" },
|
|
253
|
+
sector_focus: { type: "string", description: "Sector focus (e.g. 'B2B SaaS, deep tech')" },
|
|
254
|
+
geography: { type: "string", description: "Geographic focus (e.g. 'ANZ', 'US')" },
|
|
255
|
+
fund_size: { type: "number", description: "Fund size in USD" },
|
|
256
|
+
track_record: { type: "string", description: "Prior fund performance / GP track record narrative" },
|
|
257
|
+
description: { type: "string", description: "Free-text description of the fund and GP" },
|
|
258
|
+
net_irr: { type: "number", description: "Net IRR (%) of prior fund(s)" },
|
|
259
|
+
tvpi: { type: "number", description: "TVPI multiple" },
|
|
260
|
+
dpi: { type: "number", description: "DPI multiple" },
|
|
261
|
+
mandate: {
|
|
262
|
+
type: "object",
|
|
263
|
+
description: "Optional LP mandate to assess thesis fit against.",
|
|
264
|
+
properties: {
|
|
265
|
+
sectors: { type: "array", items: { type: "string" }, description: "Target sectors" },
|
|
266
|
+
stages: { type: "array", items: { type: "string" }, description: "Target stages" },
|
|
267
|
+
geography: { type: "string", description: "Target geography" },
|
|
268
|
+
thesis: { type: "string", description: "Your LP investment thesis" },
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
required: ["fund_name"],
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "nuvc_venture_math",
|
|
277
|
+
description:
|
|
278
|
+
"Compute venture finance metrics from raw startup financials — burn multiple, LTV/CAC ratio, " +
|
|
279
|
+
"Rule of 40, default alive status, dilution, ARR multiples, and financial health score. " +
|
|
280
|
+
"Optionally benchmark against stage-appropriate VC thresholds (elite/strong/adequate/concerning). " +
|
|
281
|
+
"Pure math, no AI calls, instant response. Works for founders validating metrics or VCs screening deals.",
|
|
282
|
+
inputSchema: {
|
|
283
|
+
type: "object",
|
|
284
|
+
properties: {
|
|
285
|
+
arr: { type: "number", description: "Annual recurring revenue (USD)" },
|
|
286
|
+
mrr: { type: "number", description: "Monthly recurring revenue (USD)" },
|
|
287
|
+
burn_rate: { type: "number", description: "Monthly burn rate (USD)" },
|
|
288
|
+
runway_months: { type: "number", description: "Months of runway remaining" },
|
|
289
|
+
cac: { type: "number", description: "Customer acquisition cost (USD)" },
|
|
290
|
+
ltv: { type: "number", description: "Customer lifetime value (USD)" },
|
|
291
|
+
gross_margin: { type: "number", description: "Gross margin percentage (0-100)" },
|
|
292
|
+
total_raised: { type: "number", description: "Total capital raised to date (USD)" },
|
|
293
|
+
raise_amount: { type: "number", description: "Current round size (USD)" },
|
|
294
|
+
pre_money_valuation: { type: "number", description: "Pre-money valuation (USD)" },
|
|
295
|
+
revenue_growth_rate: { type: "number", description: "Revenue growth rate (%)" },
|
|
296
|
+
stage: {
|
|
297
|
+
type: "string",
|
|
298
|
+
enum: ["pre_seed", "seed", "series_a", "series_b", "series_c", "growth"],
|
|
299
|
+
description: "Funding stage (default: seed). Used for benchmark comparison.",
|
|
300
|
+
},
|
|
301
|
+
benchmark: {
|
|
302
|
+
type: "boolean",
|
|
303
|
+
description: "If true, also compare metrics against stage-appropriate VC benchmarks. Default: true.",
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: "nuvc_fund_economics",
|
|
310
|
+
description:
|
|
311
|
+
"Compute fund economics from an LP perspective — fee drag, GP alignment score, J-curve position, " +
|
|
312
|
+
"return decomposition (alpha vs beta), concentration risk, and overall fund health score. " +
|
|
313
|
+
"Also computes portfolio construction math: investable capital, implied portfolio count, " +
|
|
314
|
+
"follow-on reserves, and deployment capacity. Pure math, instant response. " +
|
|
315
|
+
"For VCs sizing their fund, or LPs/family offices evaluating VC/PE fund commitments.",
|
|
316
|
+
inputSchema: {
|
|
317
|
+
type: "object",
|
|
318
|
+
properties: {
|
|
319
|
+
fund_size: { type: "number", description: "Total fund size (USD)" },
|
|
320
|
+
management_fee: { type: "number", description: "Annual management fee rate (%, e.g. 2.0)" },
|
|
321
|
+
carry: { type: "number", description: "Carried interest rate (%, e.g. 20)" },
|
|
322
|
+
preferred_return: { type: "number", description: "Preferred return / hurdle rate (%)" },
|
|
323
|
+
tvpi: { type: "number", description: "Total Value to Paid-In multiple" },
|
|
324
|
+
dpi: { type: "number", description: "Distributions to Paid-In multiple" },
|
|
325
|
+
net_irr: { type: "number", description: "Net IRR (%)" },
|
|
326
|
+
vintage_year: { type: "integer", description: "Fund vintage year" },
|
|
327
|
+
portfolio_count: { type: "integer", description: "Number of portfolio companies" },
|
|
328
|
+
fund_life_years: { type: "integer", description: "Fund life in years" },
|
|
329
|
+
avg_check_size: { type: "number", description: "Average initial check size (USD)" },
|
|
330
|
+
reserve_ratio: { type: "number", description: "Follow-on reserve ratio (0-1, default 0.50)" },
|
|
331
|
+
include_portfolio: {
|
|
332
|
+
type: "boolean",
|
|
333
|
+
description: "If true, also compute portfolio construction math. Default: true.",
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
];
|
|
339
|
+
|
|
340
|
+
// ---------------------------------------------------------------------------
|
|
341
|
+
// Handlers
|
|
342
|
+
// ---------------------------------------------------------------------------
|
|
343
|
+
|
|
344
|
+
async function handleScore(args = {}) {
|
|
345
|
+
const { text } = args;
|
|
346
|
+
const body = { text };
|
|
347
|
+
const mandate = buildMandate(args.mandate || {}) || activeMandate;
|
|
348
|
+
if (mandate) body.mandate = mandate;
|
|
349
|
+
for (const k of ["stage", "industry", "geography", "raise_amount"]) {
|
|
350
|
+
if (args[k] != null) body[k] = args[k];
|
|
351
|
+
}
|
|
352
|
+
const res = await apiCall("POST", "/ai/score", body);
|
|
353
|
+
const data = res.data || res;
|
|
354
|
+
const scores = data.scores || {};
|
|
355
|
+
const overall = scores.overall_score;
|
|
356
|
+
const dimensions = scores.scores || {};
|
|
357
|
+
const summary = scores.summary || "";
|
|
358
|
+
|
|
359
|
+
const lines = ["## NUVC VCGrade Score\n"];
|
|
360
|
+
if (overall !== undefined) {
|
|
361
|
+
const n = Number(overall);
|
|
362
|
+
const emoji = n >= 7 ? "🟢" : n >= 5 ? "🟡" : "🔴";
|
|
363
|
+
const verdict =
|
|
364
|
+
n >= 8 ? "Exceptional — investors will lean in"
|
|
365
|
+
: n >= 7 ? "Strong — worth pursuing seriously"
|
|
366
|
+
: n >= 5 ? "Promising but needs work"
|
|
367
|
+
: n >= 3 ? "Significant gaps to address"
|
|
368
|
+
: "Back to the drawing board";
|
|
369
|
+
lines.push(`${emoji} **Overall: ${n} / 10** — ${verdict}\n`);
|
|
370
|
+
}
|
|
371
|
+
const entries = Object.entries(dimensions).filter(
|
|
372
|
+
([k]) => k !== "overall_score" && k !== "summary" && k !== "raw"
|
|
373
|
+
);
|
|
374
|
+
if (entries.length > 0) {
|
|
375
|
+
lines.push("| Dimension | Score | Rationale |");
|
|
376
|
+
lines.push("|-----------|-------|-----------|");
|
|
377
|
+
for (const [key, val] of entries) {
|
|
378
|
+
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
379
|
+
if (typeof val === "object" && val !== null) {
|
|
380
|
+
lines.push(`| ${label} | ${val.score ?? "—"}/10 | ${val.rationale ?? ""} |`);
|
|
381
|
+
} else {
|
|
382
|
+
lines.push(`| ${label} | ${val}/10 | |`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
lines.push("");
|
|
386
|
+
}
|
|
387
|
+
if (summary) lines.push(`**Summary:** ${summary}`);
|
|
388
|
+
|
|
389
|
+
// Two-way mandate fit (present when a mandate was set/passed)
|
|
390
|
+
const fit = data.fit;
|
|
391
|
+
if (fit) {
|
|
392
|
+
lines.push("");
|
|
393
|
+
lines.push("---");
|
|
394
|
+
lines.push("## Mandate Fit — two-way\n");
|
|
395
|
+
const cs = fit.conviction_score;
|
|
396
|
+
const dec = (fit.decision || "").toUpperCase();
|
|
397
|
+
const decEmoji = dec === "PROCEED" ? "🟢" : dec === "WATCH" ? "🟡" : "🔴";
|
|
398
|
+
if (cs !== null && cs !== undefined) {
|
|
399
|
+
lines.push(`${decEmoji} **ConvictionScore: ${cs} / 100** · Decision: **${dec}**`);
|
|
400
|
+
}
|
|
401
|
+
const mf = typeof fit.mutual_fit === "number" ? Math.round(fit.mutual_fit * 100) : null;
|
|
402
|
+
if (mf !== null) lines.push(`**Mutual fit: ${mf}%** — ${fit.mutual_fit_label || ""}\n`);
|
|
403
|
+
|
|
404
|
+
const a = fit.fit_for_investor || {};
|
|
405
|
+
const aScore = typeof a.score === "number" ? Math.round(a.score * 100) : null;
|
|
406
|
+
lines.push(`**→ Does it fit YOUR thesis?** ${aScore !== null ? aScore + "%" : "—"}`);
|
|
407
|
+
for (const p of a.passes || []) lines.push(`- ✅ ${p}`);
|
|
408
|
+
for (const f of a.fails || []) lines.push(`- ⚠️ ${f}`);
|
|
409
|
+
|
|
410
|
+
const b = fit.fit_for_founder || {};
|
|
411
|
+
const bScore = typeof b.score === "number" ? Math.round(b.score * 100) : null;
|
|
412
|
+
lines.push(`\n**← Would the founder want YOU?** ${bScore !== null ? bScore + "%" : "—"}`);
|
|
413
|
+
for (const r of b.reasons || []) lines.push(`- ${r}`);
|
|
414
|
+
|
|
415
|
+
if (!body.stage && !body.industry && !body.geography && !body.raise_amount) {
|
|
416
|
+
lines.push(`\n_Tip: pass stage, industry, geography, and raise_amount for a sharper fit._`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return lines.join("\n");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
async function handleAnalyze({ text, analysis_type = "market" }) {
|
|
424
|
+
const res = await apiCall("POST", "/ai/analyze", { text, analysis_type });
|
|
425
|
+
const data = res.data || res;
|
|
426
|
+
const label = analysis_type.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
427
|
+
return `## NUVC ${label} Analysis\n\n${data.analysis || JSON.stringify(data, null, 2)}`;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function handleRoast({ text }) {
|
|
431
|
+
const res = await apiCall("POST", "/ai/analyze", { text, analysis_type: "pitch_deck" });
|
|
432
|
+
const data = res.data || res;
|
|
433
|
+
return `## 🔥 NUVC Startup Roast\n\n${data.analysis || JSON.stringify(data, null, 2)}`;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function handleExtract({ text, extraction_type = "company" }) {
|
|
437
|
+
const res = await apiCall("POST", "/ai/extract", { text, extraction_type });
|
|
438
|
+
const data = res.data || res;
|
|
439
|
+
const extracted = data.extracted || data;
|
|
440
|
+
if (typeof extracted !== "object" || extracted === null) return String(extracted);
|
|
441
|
+
const lines = ["## NUVC Structured Extraction\n"];
|
|
442
|
+
for (const [key, val] of Object.entries(extracted)) {
|
|
443
|
+
if (val === null || val === undefined) continue;
|
|
444
|
+
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
445
|
+
lines.push(`**${label}:** ${Array.isArray(val) ? val.join(", ") : String(val)}`);
|
|
446
|
+
}
|
|
447
|
+
return lines.join("\n");
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
async function handleModels() {
|
|
451
|
+
const res = await apiCall("GET", "/ai/models");
|
|
452
|
+
const data = res.data || res;
|
|
453
|
+
const lines = ["## NUVC Available Models\n"];
|
|
454
|
+
if (Array.isArray(data.providers) && data.providers.length > 0) {
|
|
455
|
+
lines.push("| Provider | Available | Healthy |");
|
|
456
|
+
lines.push("|----------|-----------|---------|");
|
|
457
|
+
for (const p of data.providers) {
|
|
458
|
+
lines.push(`| ${p.name} | ${p.available ? "✓" : "✗"} | ${p.healthy ? "✓" : "✗"} |`);
|
|
459
|
+
}
|
|
460
|
+
lines.push("");
|
|
461
|
+
}
|
|
462
|
+
if (Array.isArray(data.embedding_models)) {
|
|
463
|
+
lines.push(`**Embedding models:** ${data.embedding_models.join(", ")}`);
|
|
464
|
+
}
|
|
465
|
+
if (data.preference) {
|
|
466
|
+
lines.push(`**Preference:** ${Array.isArray(data.preference) ? data.preference.join(" → ") : data.preference}`);
|
|
467
|
+
}
|
|
468
|
+
return lines.join("\n");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async function handleMatchInvestors(args) {
|
|
472
|
+
const { text, industry = "Technology", stage = "seed", raise_amount, location, limit = 5 } = args;
|
|
473
|
+
const body = { text, industry, stage, limit };
|
|
474
|
+
if (raise_amount) body.raise_amount = raise_amount;
|
|
475
|
+
if (location) body.location = location;
|
|
476
|
+
|
|
477
|
+
const res = await apiCall("POST", "/ai/match", body);
|
|
478
|
+
const data = res.data || res;
|
|
479
|
+
const matches = data.matches || [];
|
|
480
|
+
|
|
481
|
+
if (matches.length === 0) {
|
|
482
|
+
return "## NUVC Investor Matching\n\nNo matching investors found for this profile. Try providing more detail about your business, or adjusting the industry/stage.";
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const lines = [
|
|
486
|
+
`## NUVC Investor Matching\n`,
|
|
487
|
+
`**${matches.length} investors** matched from ${data.pool_size || "5,800+"} database | ` +
|
|
488
|
+
`${data.matching_method === "semantic_embedding" ? "AI semantic matching" : "keyword matching"} | ` +
|
|
489
|
+
`${data.latency_ms || "—"}ms\n`,
|
|
490
|
+
];
|
|
491
|
+
|
|
492
|
+
for (let i = 0; i < matches.length; i++) {
|
|
493
|
+
const m = matches[i];
|
|
494
|
+
const score = (m.match_score * 100).toFixed(0);
|
|
495
|
+
const quality =
|
|
496
|
+
m.match_quality === "excellent" ? "🟣"
|
|
497
|
+
: m.match_quality === "strong" ? "🟢"
|
|
498
|
+
: m.match_quality === "moderate" ? "🟡"
|
|
499
|
+
: "⚪";
|
|
500
|
+
|
|
501
|
+
lines.push(`### ${i + 1}. ${m.investor_name}${m.firm_name ? ` — ${m.firm_name}` : ""}`);
|
|
502
|
+
lines.push(`${quality} **${score}% match** (${m.match_quality})`);
|
|
503
|
+
|
|
504
|
+
const details = [];
|
|
505
|
+
if (m.investor_tier_label) details.push(`**Tier:** ${m.investor_tier_label}`);
|
|
506
|
+
if (m.lead_investor_badge) details.push(`**Role:** ${m.lead_investor_badge}`);
|
|
507
|
+
if (m.check_size_display) details.push(`**Check:** ${m.check_size_display}`);
|
|
508
|
+
if (m.location) details.push(`**Location:** ${m.location}`);
|
|
509
|
+
if (m.geographic_focus) details.push(`**Geo focus:** ${m.geographic_focus}`);
|
|
510
|
+
if (details.length > 0) lines.push(details.join(" | "));
|
|
511
|
+
|
|
512
|
+
if (m.industries && m.industries.length > 0) {
|
|
513
|
+
lines.push(`**Focus:** ${m.industries.join(", ")}`);
|
|
514
|
+
}
|
|
515
|
+
if (m.funding_stages && m.funding_stages.length > 0) {
|
|
516
|
+
lines.push(`**Stages:** ${m.funding_stages.join(", ")}`);
|
|
517
|
+
}
|
|
518
|
+
if (m.thesis_snippet) {
|
|
519
|
+
lines.push(`**Thesis:** ${m.thesis_snippet}`);
|
|
520
|
+
}
|
|
521
|
+
if (m.value_add_tags && m.value_add_tags.length > 0) {
|
|
522
|
+
lines.push(`**Value-add:** ${m.value_add_tags.join(", ")}`);
|
|
523
|
+
}
|
|
524
|
+
if (m.reasoning) {
|
|
525
|
+
lines.push(`*${m.reasoning}*`);
|
|
526
|
+
}
|
|
527
|
+
if (m.website) {
|
|
528
|
+
lines.push(`**Website:** ${m.website}`);
|
|
529
|
+
}
|
|
530
|
+
lines.push("");
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
lines.push("---");
|
|
534
|
+
lines.push("**Get full contact details and introductions at [nuvc.ai](https://nuvc.ai)**");
|
|
535
|
+
|
|
536
|
+
return lines.join("\n");
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function handleScreenFund(args) {
|
|
540
|
+
const res = await apiCall("POST", "/ai/screen-fund", args);
|
|
541
|
+
const data = res.data || res;
|
|
542
|
+
|
|
543
|
+
const lines = [`## NUVC Fund Screen — ${data.fund_name || args.fund_name}\n`];
|
|
544
|
+
|
|
545
|
+
const score = data.screening_score;
|
|
546
|
+
if (score !== null && score !== undefined) {
|
|
547
|
+
const n = Number(score);
|
|
548
|
+
const emoji = n >= 7 ? "🟢" : n >= 5 ? "🟡" : "🔴";
|
|
549
|
+
lines.push(`${emoji} **Screen: ${n.toFixed(1)} / 10**` + (data.confidence ? ` · ${data.confidence} confidence` : "") + "\n");
|
|
550
|
+
}
|
|
551
|
+
if (data.decision) {
|
|
552
|
+
lines.push(`**Decision:** ${data.decision}` + (data.decision_reason ? ` — ${data.decision_reason}` : "") + "\n");
|
|
553
|
+
}
|
|
554
|
+
if (data.verdict) lines.push(`> ${data.verdict}\n`);
|
|
555
|
+
|
|
556
|
+
const dims = data.dimensions || {};
|
|
557
|
+
const dimEntries = Object.entries(dims).filter(([, v]) => v !== null && v !== undefined);
|
|
558
|
+
if (dimEntries.length > 0) {
|
|
559
|
+
lines.push("| LP Dimension | Score |");
|
|
560
|
+
lines.push("|--------------|-------|");
|
|
561
|
+
for (const [key, val] of dimEntries) {
|
|
562
|
+
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
563
|
+
const v = typeof val === "object" && val !== null ? (val.score ?? "—") : val;
|
|
564
|
+
lines.push(`| ${label} | ${v}/10 |`);
|
|
565
|
+
}
|
|
566
|
+
lines.push("");
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (Array.isArray(data.strengths) && data.strengths.length > 0) {
|
|
570
|
+
lines.push("**Strengths:**");
|
|
571
|
+
for (const s of data.strengths) lines.push(`- ✅ ${s}`);
|
|
572
|
+
lines.push("");
|
|
573
|
+
}
|
|
574
|
+
if (Array.isArray(data.red_flags) && data.red_flags.length > 0) {
|
|
575
|
+
lines.push("**Red flags:**");
|
|
576
|
+
for (const r of data.red_flags) lines.push(`- ⚠️ ${r}`);
|
|
577
|
+
lines.push("");
|
|
578
|
+
}
|
|
579
|
+
if (data.insufficient_data) {
|
|
580
|
+
lines.push("_Note: limited data provided — confidence is capped. Add track record, vintage, and metrics for a sharper screen._");
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
lines.push("---");
|
|
584
|
+
lines.push("**Manage your fund pipeline and diligence at [nuvc.ai](https://nuvc.ai)**");
|
|
585
|
+
|
|
586
|
+
return lines.join("\n");
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
async function handleVentureMath(args) {
|
|
590
|
+
const { benchmark = true, ...financials } = args;
|
|
591
|
+
const res = await apiCall("POST", "/ai/venture-math/compute", financials);
|
|
592
|
+
const metrics = res.data || res;
|
|
593
|
+
|
|
594
|
+
const lines = ["## NUVC Venture Math\n"];
|
|
595
|
+
|
|
596
|
+
// Format computed metrics
|
|
597
|
+
const fmt = (v) => (typeof v === "number" ? (Number.isInteger(v) ? v : v.toFixed(2)) : v);
|
|
598
|
+
const metricLabels = {
|
|
599
|
+
burn_multiple: "Burn Multiple",
|
|
600
|
+
ltv_cac_ratio: "LTV/CAC Ratio",
|
|
601
|
+
rule_of_40: "Rule of 40",
|
|
602
|
+
default_alive: "Default Alive",
|
|
603
|
+
arr_multiple: "ARR Multiple",
|
|
604
|
+
dilution_pct: "Dilution %",
|
|
605
|
+
runway_months: "Runway (months)",
|
|
606
|
+
gross_margin: "Gross Margin %",
|
|
607
|
+
revenue_growth_rate: "Revenue Growth %",
|
|
608
|
+
financial_health_score: "Financial Health Score",
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
const entries = Object.entries(metrics).filter(([k]) => k in metricLabels);
|
|
612
|
+
if (entries.length > 0) {
|
|
613
|
+
lines.push("| Metric | Value |");
|
|
614
|
+
lines.push("|--------|-------|");
|
|
615
|
+
for (const [key, val] of entries) {
|
|
616
|
+
const label = metricLabels[key] || key;
|
|
617
|
+
lines.push(`| ${label} | ${fmt(val)} |`);
|
|
618
|
+
}
|
|
619
|
+
lines.push("");
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Benchmark comparison
|
|
623
|
+
if (benchmark) {
|
|
624
|
+
try {
|
|
625
|
+
const stage = financials.stage || "seed";
|
|
626
|
+
const benchRes = await apiCall("POST", "/ai/venture-math/benchmark", { metrics, stage });
|
|
627
|
+
const benchmarks = benchRes.data || benchRes;
|
|
628
|
+
if (benchmarks && typeof benchmarks === "object") {
|
|
629
|
+
const benchEntries = Object.entries(benchmarks).filter(
|
|
630
|
+
([, v]) => typeof v === "object" && v !== null && v.signal
|
|
631
|
+
);
|
|
632
|
+
if (benchEntries.length > 0) {
|
|
633
|
+
lines.push(`### Benchmark vs ${stage.replace(/_/g, " ").toUpperCase()} stage\n`);
|
|
634
|
+
lines.push("| Metric | Value | Signal | Benchmark |");
|
|
635
|
+
lines.push("|--------|-------|--------|-----------|");
|
|
636
|
+
for (const [key, b] of benchEntries) {
|
|
637
|
+
const label = metricLabels[key] || key.replace(/_/g, " ");
|
|
638
|
+
const signal =
|
|
639
|
+
b.signal === "elite" ? "🟣 Elite"
|
|
640
|
+
: b.signal === "strong" ? "🟢 Strong"
|
|
641
|
+
: b.signal === "adequate" ? "🟡 Adequate"
|
|
642
|
+
: "🔴 Concerning";
|
|
643
|
+
lines.push(`| ${label} | ${fmt(b.value)} | ${signal} | ${b.threshold || ""} |`);
|
|
644
|
+
}
|
|
645
|
+
lines.push("");
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
} catch {
|
|
649
|
+
// Benchmark is optional — don't fail the whole tool
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return lines.join("\n");
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async function handleFundEconomics(args) {
|
|
657
|
+
const { include_portfolio = true, avg_check_size, reserve_ratio, ...fundParams } = args;
|
|
658
|
+
|
|
659
|
+
const res = await apiCall("POST", "/ai/venture-math/fund-economics", fundParams);
|
|
660
|
+
const data = res.data || res;
|
|
661
|
+
|
|
662
|
+
const lines = ["## NUVC Fund Economics\n"];
|
|
663
|
+
const fmt = (v) => (typeof v === "number" ? (Number.isInteger(v) ? v : v.toFixed(2)) : v);
|
|
664
|
+
|
|
665
|
+
// Key economics
|
|
666
|
+
const economicFields = [
|
|
667
|
+
["fee_drag", "Fee Drag"],
|
|
668
|
+
["gp_alignment_score", "GP Alignment Score"],
|
|
669
|
+
["j_curve_position", "J-Curve Position"],
|
|
670
|
+
["fund_health_score", "Fund Health Score"],
|
|
671
|
+
["total_fees", "Total Fees (USD)"],
|
|
672
|
+
["net_to_lp", "Net to LP"],
|
|
673
|
+
["concentration_risk", "Concentration Risk"],
|
|
674
|
+
];
|
|
675
|
+
|
|
676
|
+
const hasFields = economicFields.filter(([k]) => data[k] !== undefined);
|
|
677
|
+
if (hasFields.length > 0) {
|
|
678
|
+
lines.push("| Metric | Value |");
|
|
679
|
+
lines.push("|--------|-------|");
|
|
680
|
+
for (const [key, label] of hasFields) {
|
|
681
|
+
lines.push(`| ${label} | ${fmt(data[key])} |`);
|
|
682
|
+
}
|
|
683
|
+
lines.push("");
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Return decomposition
|
|
687
|
+
if (data.return_decomposition) {
|
|
688
|
+
const rd = data.return_decomposition;
|
|
689
|
+
lines.push("### Return Decomposition\n");
|
|
690
|
+
for (const [key, val] of Object.entries(rd)) {
|
|
691
|
+
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
692
|
+
lines.push(`**${label}:** ${fmt(val)}`);
|
|
693
|
+
}
|
|
694
|
+
lines.push("");
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Full data fallback for any extra fields
|
|
698
|
+
const shown = new Set([...economicFields.map(([k]) => k), "return_decomposition"]);
|
|
699
|
+
const extra = Object.entries(data).filter(
|
|
700
|
+
([k, v]) => !shown.has(k) && v !== null && v !== undefined && typeof v !== "object"
|
|
701
|
+
);
|
|
702
|
+
if (extra.length > 0) {
|
|
703
|
+
for (const [key, val] of extra) {
|
|
704
|
+
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
705
|
+
lines.push(`**${label}:** ${fmt(val)}`);
|
|
706
|
+
}
|
|
707
|
+
lines.push("");
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Portfolio construction (optional second call)
|
|
711
|
+
if (include_portfolio && (args.fund_size || fundParams.fund_size)) {
|
|
712
|
+
try {
|
|
713
|
+
const portfolioReq = {
|
|
714
|
+
fund_size: args.fund_size || fundParams.fund_size,
|
|
715
|
+
avg_check_size: avg_check_size || undefined,
|
|
716
|
+
reserve_ratio: reserve_ratio ?? 0.50,
|
|
717
|
+
management_fee_rate: fundParams.management_fee ? fundParams.management_fee / 100 : 0.02,
|
|
718
|
+
fund_life_years: fundParams.fund_life_years || 10,
|
|
719
|
+
investment_period_years: Math.min(fundParams.fund_life_years || 10, 5),
|
|
720
|
+
};
|
|
721
|
+
const pRes = await apiCall("POST", "/ai/venture-math/portfolio", portfolioReq);
|
|
722
|
+
const pData = pRes.data || pRes;
|
|
723
|
+
|
|
724
|
+
lines.push("### Portfolio Construction\n");
|
|
725
|
+
const portfolioFields = [
|
|
726
|
+
["investable_capital", "Investable Capital"],
|
|
727
|
+
["implied_portfolio_count", "Implied Portfolio Count"],
|
|
728
|
+
["follow_on_reserves", "Follow-On Reserves"],
|
|
729
|
+
["fee_drag_pct", "Fee Drag %"],
|
|
730
|
+
["deployment_per_year", "Deployment Per Year"],
|
|
731
|
+
];
|
|
732
|
+
const pEntries = portfolioFields.filter(([k]) => pData[k] !== undefined);
|
|
733
|
+
if (pEntries.length > 0) {
|
|
734
|
+
lines.push("| Metric | Value |");
|
|
735
|
+
lines.push("|--------|-------|");
|
|
736
|
+
for (const [key, label] of pEntries) {
|
|
737
|
+
const val = pData[key];
|
|
738
|
+
lines.push(`| ${label} | ${typeof val === "number" && val > 1000 ? `$${val.toLocaleString()}` : fmt(val)} |`);
|
|
739
|
+
}
|
|
740
|
+
lines.push("");
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Fallback for extra portfolio fields
|
|
744
|
+
const pShown = new Set(portfolioFields.map(([k]) => k));
|
|
745
|
+
const pExtra = Object.entries(pData).filter(
|
|
746
|
+
([k, v]) => !pShown.has(k) && v !== null && v !== undefined && typeof v !== "object"
|
|
747
|
+
);
|
|
748
|
+
for (const [key, val] of pExtra) {
|
|
749
|
+
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
750
|
+
lines.push(`**${label}:** ${typeof val === "number" && val > 1000 ? `$${val.toLocaleString()}` : fmt(val)}`);
|
|
751
|
+
}
|
|
752
|
+
} catch {
|
|
753
|
+
// Portfolio construction is optional
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
return lines.join("\n");
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function renderMandate(m) {
|
|
761
|
+
const lines = [];
|
|
762
|
+
if (m.sectors) lines.push(`**Sectors:** ${m.sectors.join(", ")}`);
|
|
763
|
+
if (m.stages) lines.push(`**Stages:** ${m.stages.join(", ")}`);
|
|
764
|
+
if (m.geography) lines.push(`**Geography:** ${m.geography}`);
|
|
765
|
+
if (m.check_size_min != null || m.check_size_max != null) {
|
|
766
|
+
const lo = m.check_size_min != null ? `$${Number(m.check_size_min).toLocaleString()}` : "—";
|
|
767
|
+
const hi = m.check_size_max != null ? `$${Number(m.check_size_max).toLocaleString()}` : "—";
|
|
768
|
+
lines.push(`**Cheque size:** ${lo} – ${hi}`);
|
|
769
|
+
}
|
|
770
|
+
if (m.thesis) lines.push(`**Thesis:** ${m.thesis}`);
|
|
771
|
+
if (m.anti_thesis) lines.push(`**Anti-thesis:** ${m.anti_thesis}`);
|
|
772
|
+
return lines.join("\n");
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
async function handleSetMandate(args) {
|
|
776
|
+
const m = buildMandate(args || {});
|
|
777
|
+
if (!m) {
|
|
778
|
+
return "No mandate fields provided. Pass at least one of: sectors, stages, geography, check_size_min/max, thesis, anti_thesis.";
|
|
779
|
+
}
|
|
780
|
+
activeMandate = m;
|
|
781
|
+
// Persist server-side (best-effort) so the mandate survives across sessions
|
|
782
|
+
// and applies to REST + web scoring too, not just this session.
|
|
783
|
+
let persisted = false;
|
|
784
|
+
try {
|
|
785
|
+
const res = await apiCall("POST", "/ai/mandate", m);
|
|
786
|
+
const data = res.data || res;
|
|
787
|
+
persisted = data?.persisted === true;
|
|
788
|
+
} catch {
|
|
789
|
+
// best-effort — the in-process mandate still applies for this session
|
|
790
|
+
}
|
|
791
|
+
return [
|
|
792
|
+
"## Mandate set ✓\n",
|
|
793
|
+
persisted
|
|
794
|
+
? "Saved to your account — every `nuvc_score` (here, REST, or web) now returns a two-way mandate↔deck fit.\n"
|
|
795
|
+
: "Active for this session — every `nuvc_score` now returns a two-way mandate↔deck fit.\n",
|
|
796
|
+
renderMandate(m),
|
|
797
|
+
].join("\n");
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
async function handleGetMandate() {
|
|
801
|
+
let m = activeMandate;
|
|
802
|
+
if (!m) {
|
|
803
|
+
// Fall back to the mandate stored on the account (set in a prior session).
|
|
804
|
+
try {
|
|
805
|
+
const res = await apiCall("GET", "/ai/mandate");
|
|
806
|
+
const data = res.data || res;
|
|
807
|
+
if (data?.mandate) {
|
|
808
|
+
m = data.mandate;
|
|
809
|
+
activeMandate = m;
|
|
810
|
+
}
|
|
811
|
+
} catch {
|
|
812
|
+
// ignore — treat as no mandate
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (!m) {
|
|
816
|
+
return "No mandate set. Use `nuvc_set_mandate` to set one, then score decks to get a two-way fit verdict.";
|
|
817
|
+
}
|
|
818
|
+
return ["## Active mandate\n", renderMandate(m)].join("\n");
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
export const HANDLERS = {
|
|
822
|
+
nuvc_score: handleScore,
|
|
823
|
+
nuvc_set_mandate: handleSetMandate,
|
|
824
|
+
nuvc_get_mandate: handleGetMandate,
|
|
825
|
+
nuvc_analyze: handleAnalyze,
|
|
826
|
+
nuvc_roast: handleRoast,
|
|
827
|
+
nuvc_extract: handleExtract,
|
|
828
|
+
nuvc_models: handleModels,
|
|
829
|
+
nuvc_match_investors: handleMatchInvestors,
|
|
830
|
+
nuvc_screen_fund: handleScreenFund,
|
|
831
|
+
nuvc_venture_math: handleVentureMath,
|
|
832
|
+
nuvc_fund_economics: handleFundEconomics,
|
|
833
|
+
};
|
package/manifest.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": "0.3",
|
|
3
|
+
"name": "nuvc",
|
|
4
|
+
"display_name": "NUVC — VC-grade deal & fund intelligence",
|
|
5
|
+
"version": "1.4.0",
|
|
6
|
+
"description": "VC-grade feedback in Claude: get honest, instant feedback on any pitch deck, then meet the investors most likely to fund you — a ranked shortlist with a brief for each. Also screens funds for LPs.",
|
|
7
|
+
"long_description": "NUVC is the conviction layer for capital allocation. For founders: get instant, honest VC-grade feedback on your pitch deck (a 0–10 score with the strengths, gaps, and questions a VC will ask), then meet the investors most likely to fund you — a ranked, thesis-fit shortlist from 9,000+ verified profiles, each with a reason and brief so you out-research the room and walk in prepared. For VCs and LPs: score any startup or fund with an IC-ready verdict, screen against your mandate, and run venture math — all from Claude. Get a free API key (2 free credits, no card) at https://nuvc.ai/api-platform/keys.",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "NUAI Pty Ltd",
|
|
10
|
+
"url": "https://nuvc.ai"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://nuvc.ai/developers",
|
|
13
|
+
"documentation": "https://nuvc.ai/api-platform/docs",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"vc",
|
|
17
|
+
"venture capital",
|
|
18
|
+
"startup scoring",
|
|
19
|
+
"investor matching",
|
|
20
|
+
"due diligence",
|
|
21
|
+
"fund screening",
|
|
22
|
+
"pitch deck"
|
|
23
|
+
],
|
|
24
|
+
"server": {
|
|
25
|
+
"type": "node",
|
|
26
|
+
"entry_point": "index.js",
|
|
27
|
+
"mcp_config": {
|
|
28
|
+
"command": "node",
|
|
29
|
+
"args": ["${__dirname}/index.js"],
|
|
30
|
+
"env": {
|
|
31
|
+
"NUVC_API_KEY": "${user_config.api_key}"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"tools": [
|
|
36
|
+
{ "name": "nuvc_score", "description": "Instant, honest VC-grade feedback on a startup or pitch deck — a 0–10 score with strengths, gaps, and the questions a VC will ask, so you see what a VC sees before burning a warm intro" },
|
|
37
|
+
{ "name": "nuvc_match_investors", "description": "Meet the investors most likely to fund you — a ranked, thesis-fit shortlist from 9,000+ verified investors (a well-matched few beats a mass blast), each with a reason and brief to out-research the room" },
|
|
38
|
+
{ "name": "nuvc_screen_fund", "description": "Screen a VC fund across 6 LP-grade dimensions against your mandate" },
|
|
39
|
+
{ "name": "nuvc_analyze", "description": "Market, competitive, financial, or pitch-deck analysis" },
|
|
40
|
+
{ "name": "nuvc_extract", "description": "Extract structured fields from any pitch or business description" },
|
|
41
|
+
{ "name": "nuvc_set_mandate", "description": "Set your investment mandate for mandate-aware scoring and matching" },
|
|
42
|
+
{ "name": "nuvc_get_mandate", "description": "Retrieve your current investment mandate" },
|
|
43
|
+
{ "name": "nuvc_venture_math", "description": "Compute dilution, ownership, burn multiple, LTV/CAC, Rule of 40, IRR/MOIC vs VC thresholds" },
|
|
44
|
+
{ "name": "nuvc_fund_economics", "description": "Fund economics for LPs — fee drag, GP alignment, J-curve, portfolio construction" },
|
|
45
|
+
{ "name": "nuvc_roast", "description": "The hard truths a polite friend won't tell you — blunt, honest VC-perspective feedback on a startup or pitch, before a real investor delivers it" },
|
|
46
|
+
{ "name": "nuvc_models", "description": "List available scoring models and provider health" }
|
|
47
|
+
],
|
|
48
|
+
"user_config": {
|
|
49
|
+
"api_key": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"title": "NUVC API Key",
|
|
52
|
+
"description": "Your NUVC API key (starts with nuvc_). Get a free key — 2 free credits, no card — at https://nuvc.ai/api-platform/keys",
|
|
53
|
+
"sensitive": true,
|
|
54
|
+
"required": true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nuvc-mcp",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "MCP server for NUVC — VC-grade startup intelligence for founders and investors. Score ideas, analyze markets, compute venture math, evaluate fund economics.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"nuvc-mcp": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node server.js",
|
|
12
|
+
"start:stdio": "node index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"ai",
|
|
17
|
+
"startup",
|
|
18
|
+
"vc",
|
|
19
|
+
"business-intelligence",
|
|
20
|
+
"nuvc",
|
|
21
|
+
"smithery"
|
|
22
|
+
],
|
|
23
|
+
"homepage": "https://nuvc.ai/api-platform/keys",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/nuvcai/nuvc-mcp.git"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/smithery.yaml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: nuvc
|
|
2
|
+
description: Instant, honest VC-grade feedback on any pitch deck, then meet the investors most likely to fund you — a ranked, thesis-fit shortlist from 9,000+ verified profiles, each with a brief so you out-research the room before the meeting. Also screens funds and deals for VCs and LPs.
|
|
3
|
+
version: 1.4.0
|
|
4
|
+
homepage: https://nuvc.ai/api-platform/keys
|
|
5
|
+
license: MIT
|
|
6
|
+
|
|
7
|
+
startCommand:
|
|
8
|
+
type: stdio
|
|
9
|
+
command: npx
|
|
10
|
+
args:
|
|
11
|
+
- -y
|
|
12
|
+
- nuvc-mcp
|
|
13
|
+
env:
|
|
14
|
+
NUVC_API_KEY:
|
|
15
|
+
description: Your NUVC API key (starts with nuvc_). Get a free key at https://nuvc.ai/api-platform/keys
|
|
16
|
+
required: true
|
|
17
|
+
|
|
18
|
+
tools:
|
|
19
|
+
- name: nuvc_score
|
|
20
|
+
description: Instant, honest VC-grade feedback on a pitch deck — a 0-10 score with the strengths, gaps, and questions a VC will ask, so you see what a VC sees before burning a warm intro
|
|
21
|
+
- name: nuvc_analyze
|
|
22
|
+
description: Market, competitive, financial, or pitch deck analysis
|
|
23
|
+
- name: nuvc_roast
|
|
24
|
+
description: The hard truths a polite friend won't tell you — brutally honest VC-perspective feedback, before a real investor delivers it
|
|
25
|
+
- name: nuvc_extract
|
|
26
|
+
description: Extract structured fields from any pitch or business description
|
|
27
|
+
- name: nuvc_models
|
|
28
|
+
description: List available AI models and provider health
|
|
29
|
+
- name: nuvc_match_investors
|
|
30
|
+
description: Meet the investors most likely to fund you — a ranked, thesis-fit shortlist from 9,000+ verified investors (a well-matched few beats a mass blast), each with a reason and brief to out-research the room
|
|
31
|
+
- name: nuvc_screen_fund
|
|
32
|
+
description: Screen a VC fund across 6 LP-grade dimensions against your mandate
|
|
33
|
+
- name: nuvc_set_mandate
|
|
34
|
+
description: Set your investment mandate for mandate-aware scoring and matching
|
|
35
|
+
- name: nuvc_get_mandate
|
|
36
|
+
description: Retrieve your current investment mandate
|
|
37
|
+
- name: nuvc_venture_math
|
|
38
|
+
description: Compute burn multiple, LTV/CAC, Rule of 40, dilution, and benchmark against VC thresholds
|
|
39
|
+
- name: nuvc_fund_economics
|
|
40
|
+
description: Fund economics for LPs — fee drag, GP alignment, J-curve, portfolio construction
|