pitch_deck_storyline
Generate a complete investor pitch-deck storyline tailored to your target audience. Returns 8-20 slides with key points, speaker notes, visual hints, and a Q&A bank of board questions and traps to avoid.
Instructions
Build a complete investor pitch-deck storyline for a company. Returns an 8-20 slide narrative tailored to the target audience (seed-vc / series-a-vc / growth-vc / strategic / bank / grant) — each slide carrying a title, key points, a speaker note and a visual hint — plus a Q&A bank of 10-15 likely board questions and traps to avoid. Output is deck JSON ready to export to Google Slides, Notion or Pitch.com. When to use this tool: the user is preparing a fundraise, a board meeting, or an investor presentation. Inputs: the company profile and the target audience type. Delivered by Sarah, the AI Fundraising lead of the Gapup portfolio.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| company | Yes | ||
| audience | Yes | Target audience — adapts tone + emphasis + Q&A bank | |
| slideCount | Yes | 12 = standard VC deck, 15 = bank-friendly with annexes, 20 = growth/strategic | |
| keyFacts | Yes | Hard facts to weave into the deck (traction numbers, milestones, awards) |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| executiveSummary | Yes | One-paragraph elevator pitch distilled from the deck | |
| slides | Yes | 8-20 slide objects ready to export to Google Slides / Notion / Pitch.com | |
| qaBanks | Yes | 10-15 anticipated investor questions with recommended answers | |
| kpis | No | 3-5 headline KPI bubbles surfaced from keyFacts | |
| recommendations | No | Fundraising preparation actions |
Implementation Reference
- The handler function that executes the pitch_deck_storyline tool. It delegates to callGapupEndpoint which sends a POST request to the Gapup API with the slug 'pitch-deck-storyline'.
export async function handle(input: unknown, signal?: AbortSignal): Promise<unknown> { return callGapupEndpoint("pitch-deck-storyline", input, signal); } - src/index.ts:36-42 (registration)Tool registration in the TOOLS array inside index.ts. The 'pitchDeckTool' and 'pitchDeckHandle' exports from pitch-deck-storyline.ts are registered and made available to the MCP server for listing and calling.
const TOOLS: Array<{ def: ToolDef; handle: ToolHandle }> = [ { def: competitorIntelTool, handle: competitorIntelHandle }, { def: trendWatcherTool, handle: trendWatcherHandle }, { def: partnershipTool, handle: partnershipHandle }, { def: pitchDeckTool, handle: pitchDeckHandle }, { def: carbonTool, handle: carbonHandle }, ]; - src/index.ts:24-24 (registration)Import of the tool definition and handler from pitch-deck-storyline.ts into index.ts for registration.
import { tool as pitchDeckTool, handle as pitchDeckHandle } from "./tools/pitch-deck-storyline.js"; - Full tool definition including name ('pitch_deck_storyline'), description, inputSchema, outputSchema, and annotations. Input requires company (name, pitch, stage), audience, slideCount (8-20), and keyFacts. Output includes executiveSummary, slides (8-20 with title, keyPoints, speakerNote, visualHint, layout), qaBanks (10-15 questions), kpis, and recommendations.
export const tool = { name: "pitch_deck_storyline", description: "Build a complete investor pitch-deck storyline for a company. Returns an 8-20 slide narrative tailored to the target audience (seed-vc / series-a-vc / growth-vc / strategic / bank / grant) — each slide carrying a title, key points, a speaker note and a visual hint — plus a Q&A bank of 10-15 likely board questions and traps to avoid. Output is deck JSON ready to export to Google Slides, Notion or Pitch.com. When to use this tool: the user is preparing a fundraise, a board meeting, or an investor presentation. Inputs: the company profile and the target audience type. Delivered by Sarah, the AI Fundraising lead of the Gapup portfolio.", inputSchema: { type: "object", properties: { company: { type: "object", properties: { name: { type: "string", minLength: 2, maxLength: 120 }, pitch: { type: "string", minLength: 20, maxLength: 800 }, stage: { type: "string", enum: ["pre-seed", "seed", "series-a", "series-b", "series-c", "growth"], }, }, required: ["name", "pitch", "stage"], }, audience: { type: "string", enum: ["seed-vc", "series-a-vc", "growth-vc", "strategic", "bank", "grant"], description: "Target audience — adapts tone + emphasis + Q&A bank", }, slideCount: { type: "integer", minimum: 8, maximum: 20, description: "12 = standard VC deck, 15 = bank-friendly with annexes, 20 = growth/strategic", }, keyFacts: { type: "array", items: { type: "string", minLength: 8, maxLength: 200 }, minItems: 2, maxItems: 8, description: "Hard facts to weave into the deck (traction numbers, milestones, awards)", }, }, required: ["company", "audience", "slideCount", "keyFacts"], }, outputSchema: { type: "object", properties: { executiveSummary: { type: "string", description: "One-paragraph elevator pitch distilled from the deck", }, slides: { type: "array", description: "8-20 slide objects ready to export to Google Slides / Notion / Pitch.com", items: { type: "object", properties: { slideNumber: { type: "integer" }, title: { type: "string" }, keyPoints: { type: "array", items: { type: "string" }, }, speakerNote: { type: "string" }, visualHint: { type: "string" }, layout: { type: "string", description: "Suggested slide layout (title-only, two-column, chart, etc.)", }, }, required: ["slideNumber", "title", "keyPoints", "speakerNote"], }, }, qaBanks: { type: "array", description: "10-15 anticipated investor questions with recommended answers", items: { type: "object", properties: { question: { type: "string" }, suggestedAnswer: { type: "string" }, trap: { type: "string", description: "Common mistake to avoid when answering" }, }, required: ["question", "suggestedAnswer"], }, }, kpis: { type: "array", description: "3-5 headline KPI bubbles surfaced from keyFacts", items: { type: "object", properties: { label: { type: "string" }, value: { type: "string" }, trend: { type: "string", enum: ["up", "down", "stable"] }, }, required: ["label", "value"], }, }, recommendations: { type: "array", description: "Fundraising preparation actions", items: { type: "object", properties: { action: { type: "string" }, rationale: { type: "string" }, }, required: ["action", "rationale"], }, }, }, required: ["executiveSummary", "slides", "qaBanks"], }, annotations: { readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false, title: "Investor Pitch Deck Storyline", }, } as const; - src/client.ts:26-86 (helper)The callGapupEndpoint helper function used by the handler. It sends a POST request to the Gapup API at /api/agent/{slug} with the input as JSON body, passing the AbortSignal for cancellation. Includes error handling for 401, 402, 404, 429, and other HTTP errors.
export async function callGapupEndpoint( slug: string, input: unknown, signal?: AbortSignal ): Promise<unknown> { const apiKey = process.env.GAPUP_API_KEY; if (!apiKey) { throw new GapupAuthError( "GAPUP_API_KEY environment variable required. Get a free tier key (100 calls/mo) at https://hub.gapup.io/agents-api/onboard" ); } const res = await fetch(`${HUB_BASE_URL}/api/agent/${slug}`, { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Key": apiKey, "Accept": "application/json", "User-Agent": "@gapup/mcp-knowledge/0.1.0", }, body: JSON.stringify(input), signal, }); if (res.status === 401) { throw new GapupAuthError( "Invalid or missing GAPUP_API_KEY. Verify your key at https://hub.gapup.io/agents-api/onboard" ); } if (res.status === 402) { const body = await res.text().catch(() => ""); throw new GapupAuthError( `Free tier quota exhausted or paid auth required. Upgrade at https://hub.gapup.io/agents-api (received 402: ${body.slice(0, 120)})` ); } if (res.status === 404) { throw new GapupApiError( 404, `Endpoint '${slug}' not found — check GAPUP_API_BASE_URL or update the package (npm update @gapup/mcp-knowledge)` ); } if (res.status === 429) { const retryAfter = res.headers.get("Retry-After"); const retryMsg = retryAfter ? ` Retry after ${retryAfter} seconds.` : ""; const body = await res.text().catch(() => ""); throw new GapupApiError( 429, `Rate limit exceeded.${retryMsg} Upgrade your plan at https://hub.gapup.io/agents-api (received: ${body.slice(0, 100)})` ); } if (!res.ok) { const body = await res.text().catch(() => ""); throw new GapupApiError(res.status, body); } return res.json(); }