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 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
+ [![Smithery](https://smithery.ai/badge/nuvc)](https://smithery.ai/server/nuvc)
9
+ [![npm](https://img.shields.io/npm/v/nuvc-mcp)](https://npmjs.com/package/nuvc-mcp)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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