@growth-loop/mcp-server 0.1.1 → 0.1.3
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 +12 -7
- package/dist/index.js +14 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,8 +48,9 @@ From inside your project:
|
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
50
|
claude mcp add growth-loop \
|
|
51
|
-
|
|
52
|
-
-e
|
|
51
|
+
-e GROWTH_API_KEY=pk_live_… \
|
|
52
|
+
-e GROWTH_HOST=https://api.growth-loop.dev \
|
|
53
|
+
-- npx -y @growth-loop/mcp-server
|
|
53
54
|
```
|
|
54
55
|
|
|
55
56
|
---
|
|
@@ -97,11 +98,15 @@ Two modes — pick one (or both):
|
|
|
97
98
|
|
|
98
99
|
### Prompts (slash commands)
|
|
99
100
|
|
|
100
|
-
- `/
|
|
101
|
-
- `/
|
|
102
|
-
- `/
|
|
103
|
-
- `/
|
|
104
|
-
- `/
|
|
101
|
+
- `/growth-init` — auto-instrument this repo with the SDK and open a PR.
|
|
102
|
+
- `/growth-weekly` — what changed, biggest mover, 3 actions for the week.
|
|
103
|
+
- `/growth-pmf` — score 0–10 + what blocks the next click up.
|
|
104
|
+
- `/growth-pivot` — cohorts outperforming by ≥2× on retention or LTV.
|
|
105
|
+
- `/growth-icp` — who pays the most, retains the longest.
|
|
106
|
+
- `/growth-strategy` — 90-day plan, one focus area.
|
|
107
|
+
- `/growth-diagnose <metric>` — root-cause analysis for a dropped metric.
|
|
108
|
+
|
|
109
|
+
(Prompts are prefixed with `growth-` so they don't collide with Claude Code's built-in `/init` or other editors' built-ins.)
|
|
105
110
|
- `/diagnose <metric>` — root-cause cross-referencing events + commits.
|
|
106
111
|
|
|
107
112
|
---
|
package/dist/index.js
CHANGED
|
@@ -247,27 +247,27 @@ function jsonResource(uri, data) {
|
|
|
247
247
|
// src/prompts.ts
|
|
248
248
|
var prompts = [
|
|
249
249
|
{
|
|
250
|
-
name: "weekly",
|
|
250
|
+
name: "growth-weekly",
|
|
251
251
|
description: "Weekly review \u2014 what changed in the last 7 days, biggest mover, 3 actions to take this week."
|
|
252
252
|
},
|
|
253
253
|
{
|
|
254
|
-
name: "pmf",
|
|
254
|
+
name: "growth-pmf",
|
|
255
255
|
description: "PMF check \u2014 score 0-10 from retention flatness, organic share, revenue concentration. What blocks the next click up?"
|
|
256
256
|
},
|
|
257
257
|
{
|
|
258
|
-
name: "pivot",
|
|
258
|
+
name: "growth-pivot",
|
|
259
259
|
description: "Pivot analysis \u2014 which behavioral cohorts are outperforming by \u22652\xD7 on retention or LTV, ranked."
|
|
260
260
|
},
|
|
261
261
|
{
|
|
262
|
-
name: "icp",
|
|
262
|
+
name: "growth-icp",
|
|
263
263
|
description: "Ideal customer profile \u2014 cross behavioral \xD7 revenue cohorts. Who pays the most, retains the longest?"
|
|
264
264
|
},
|
|
265
265
|
{
|
|
266
|
-
name: "strategy",
|
|
266
|
+
name: "growth-strategy",
|
|
267
267
|
description: "90-day plan \u2014 pick ONE highest-leverage focus area, justify with current metrics + backlog hypotheses."
|
|
268
268
|
},
|
|
269
269
|
{
|
|
270
|
-
name: "diagnose",
|
|
270
|
+
name: "growth-diagnose",
|
|
271
271
|
description: "Why a metric dropped \u2014 root-cause analysis cross-referencing events, funnel position, and recent commits.",
|
|
272
272
|
arguments: [
|
|
273
273
|
{
|
|
@@ -278,18 +278,18 @@ var prompts = [
|
|
|
278
278
|
]
|
|
279
279
|
},
|
|
280
280
|
{
|
|
281
|
-
name: "init",
|
|
281
|
+
name: "growth-init",
|
|
282
282
|
description: "Auto-instrument this repo with growth-loop.dev SDK \u2014 detect stack, install SDK, wrap signup/checkout/key call sites, open a single PR."
|
|
283
283
|
}
|
|
284
284
|
];
|
|
285
285
|
var QUESTIONS = {
|
|
286
|
-
weekly: () => "Give me a weekly review. What changed in the last 7 days vs the prior 7? What's the single biggest mover, and what 3 actions should I take this week?",
|
|
287
|
-
pmf: () => "Where am I on PMF? Use retention curve flatness, organic acquisition share, and revenue concentration. Score 0-10 and tell me what's blocking the next click up.",
|
|
288
|
-
pivot: () => "Should I pivot? Look at cohort patterns. Are there segments outperforming by \u22652\xD7 on retention or LTV? Propose 3-5 plausible pivots ranked by data evidence.",
|
|
289
|
-
icp: () => "Who is my ideal customer? Cross behavioral cohorts \xD7 revenue cohorts: who pays the most, retains the longest, refers the most. Output ICP attributes with numbers.",
|
|
290
|
-
strategy: () => "What's my highest-leverage focus for the next 90 days? Pick ONE area and justify it with my current metrics + my backlog hypotheses + memory of prior decisions.",
|
|
291
|
-
diagnose: (args) => `Why has ${args.metric ?? "this metric"} dropped recently? Cross-reference funnel data, raw events, and recent commits. Return ranked hypotheses with confidence scores.`,
|
|
292
|
-
init: () => `Instrument this repo with growth-loop.dev. Read the codebase, install the SDK, wrap the highest-signal call sites with growth helpers, and open a single PR with all changes.
|
|
286
|
+
"growth-weekly": () => "Give me a weekly review. What changed in the last 7 days vs the prior 7? What's the single biggest mover, and what 3 actions should I take this week?",
|
|
287
|
+
"growth-pmf": () => "Where am I on PMF? Use retention curve flatness, organic acquisition share, and revenue concentration. Score 0-10 and tell me what's blocking the next click up.",
|
|
288
|
+
"growth-pivot": () => "Should I pivot? Look at cohort patterns. Are there segments outperforming by \u22652\xD7 on retention or LTV? Propose 3-5 plausible pivots ranked by data evidence.",
|
|
289
|
+
"growth-icp": () => "Who is my ideal customer? Cross behavioral cohorts \xD7 revenue cohorts: who pays the most, retains the longest, refers the most. Output ICP attributes with numbers.",
|
|
290
|
+
"growth-strategy": () => "What's my highest-leverage focus for the next 90 days? Pick ONE area and justify it with my current metrics + my backlog hypotheses + memory of prior decisions.",
|
|
291
|
+
"growth-diagnose": (args) => `Why has ${args.metric ?? "this metric"} dropped recently? Cross-reference funnel data, raw events, and recent commits. Return ranked hypotheses with confidence scores.`,
|
|
292
|
+
"growth-init": () => `Instrument this repo with growth-loop.dev. Read the codebase, install the SDK, wrap the highest-signal call sites with growth helpers, and open a single PR with all changes.
|
|
293
293
|
|
|
294
294
|
## Step 1 \u2014 detect the stack
|
|
295
295
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/tools.ts","../src/resources.ts","../src/prompts.ts","../src/api.ts"],"sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n ReadResourceRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { tools, callTool } from './tools.js';\nimport { resources, readResource } from './resources.js';\nimport { prompts, getPrompt } from './prompts.js';\nimport { GrowthApi } from './api.js';\n\nconst apiKey = process.env.GROWTH_API_KEY;\nconst sessionToken = process.env.GROWTH_SESSION_TOKEN;\nconst host = process.env.GROWTH_HOST ?? 'https://api.growth-loop.dev';\nconst projectId = process.env.GROWTH_PROJECT_ID;\n\nif (!apiKey && !sessionToken) {\n process.stderr.write(\n '[growth-mcp] either GROWTH_API_KEY or GROWTH_SESSION_TOKEN is required\\n' +\n ' api-key works for ingest + read endpoints; session-token unlocks growth_engineer_ask.\\n',\n );\n process.exit(1);\n}\n\nconst api = new GrowthApi({ apiKey, sessionToken, host, projectId });\n\nconst server = new Server(\n { name: 'growth-loop', version: '0.1.0' },\n { capabilities: { tools: {}, resources: {}, prompts: {} } },\n);\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));\nserver.setRequestHandler(CallToolRequestSchema, async (req) => callTool(api, req));\n\nserver.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources }));\nserver.setRequestHandler(ReadResourceRequestSchema, async (req) => readResource(api, req));\n\nserver.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts }));\nserver.setRequestHandler(GetPromptRequestSchema, async (req) => getPrompt(req));\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import type {\n CallToolRequest,\n CallToolResult,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport type { GrowthApi } from './api.js';\n\nconst RangeEnum = z.enum(['1h', '24h', '7d', '30d', '90d']);\n\nconst inputs = {\n overview: z.object({ range: RangeEnum.default('30d') }),\n metric_series: z.object({\n metric: z.string(),\n range: RangeEnum.default('30d'),\n }),\n funnel_get: z.object({\n name: z.string(),\n range: RangeEnum.default('30d'),\n }),\n funnels_list: z.object({}),\n retention: z.object({ range: RangeEnum.default('90d') }),\n diagnose: z.object({\n metric: z.string(),\n since: z.string().default('24h'),\n }),\n why_event: z.object({ event: z.string() }),\n anomalies_list: z.object({\n severity: z.enum(['info', 'warn', 'critical']).optional(),\n }),\n commits_list: z.object({ since: z.string().default('48h') }),\n peer_benchmark: z.object({}),\n hypotheses_list: z.object({}),\n growth_engineer_ask: z.object({\n question: z\n .string()\n .min(2)\n .max(8000)\n .describe('Free-form question to the AI growth engineer'),\n }),\n};\n\nexport const tools: Tool[] = [\n {\n name: 'overview',\n description:\n 'Top-line snapshot for a date range: MRR, active users, signup→paid, deltas vs prior period.',\n inputSchema: zodToJson(inputs.overview),\n },\n {\n name: 'metric_series',\n description: 'Return a time series for a metric. Supports system + custom event metrics.',\n inputSchema: zodToJson(inputs.metric_series),\n },\n {\n name: 'funnels_list',\n description: 'List configured funnels with name + step count.',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'funnel_get',\n description: 'Get a funnel by name with conversion + drop-off per step.',\n inputSchema: zodToJson(inputs.funnel_get),\n },\n {\n name: 'retention',\n description: 'Cohort retention curve (D1, D7, D30) over the requested range.',\n inputSchema: zodToJson(inputs.retention),\n },\n {\n name: 'diagnose',\n description:\n 'Closed-loop investigation on a metric. Returns ranked hypotheses with citations to events + commits.',\n inputSchema: zodToJson(inputs.diagnose),\n },\n {\n name: 'why_event',\n description:\n 'Explain why users drop on or interact with a specific event. Cross-references replays + funnel position.',\n inputSchema: zodToJson(inputs.why_event),\n },\n {\n name: 'anomalies_list',\n description: 'List recent statistical anomalies with attached candidate hypotheses.',\n inputSchema: zodToJson(inputs.anomalies_list),\n },\n {\n name: 'commits_list',\n description: 'Recent commits annotated with their measured metric impact (git-aware analytics).',\n inputSchema: zodToJson(inputs.commits_list),\n },\n {\n name: 'peer_benchmark',\n description:\n 'Compare your project to anonymized peer SaaS at the same category + stage (signup→paid, retention, MRR).',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'hypotheses_list',\n description: 'RICE-ranked hypothesis backlog (open + testing + completed).',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'growth_engineer_ask',\n description:\n 'Talk to the AI growth engineer for vibe-coded SaaS. The agent is grounded in your funnel, retention, peer benchmarks, hypotheses, deploys, and the last 30 raw events. Every answer cites real rows from your project. Requires a session token (set GROWTH_SESSION_TOKEN env).',\n inputSchema: zodToJson(inputs.growth_engineer_ask),\n },\n];\n\nexport async function callTool(api: GrowthApi, req: CallToolRequest): Promise<CallToolResult> {\n const name = req.params.name;\n const args = req.params.arguments ?? {};\n\n switch (name) {\n case 'overview': {\n const input = inputs.overview.parse(args);\n return text(await api.getOverview(input.range));\n }\n case 'metric_series': {\n const input = inputs.metric_series.parse(args);\n return text(await api.getMetricSeries(input.metric, input.range));\n }\n case 'funnels_list': {\n return text(await api.listFunnels());\n }\n case 'funnel_get': {\n const input = inputs.funnel_get.parse(args);\n return text(await api.getFunnel(input.name, input.range));\n }\n case 'retention': {\n const input = inputs.retention.parse(args);\n return text(await api.getRetention(input.range));\n }\n case 'diagnose': {\n const input = inputs.diagnose.parse(args);\n return text(await api.diagnose(input.metric, input.since));\n }\n case 'why_event': {\n const input = inputs.why_event.parse(args);\n return text(await api.whyEvent(input.event));\n }\n case 'anomalies_list': {\n const input = inputs.anomalies_list.parse(args);\n return text(await api.listAnomalies(input.severity));\n }\n case 'commits_list': {\n const input = inputs.commits_list.parse(args);\n return text(await api.listCommits(input.since));\n }\n case 'peer_benchmark': {\n return text(await api.getPeerBenchmarks());\n }\n case 'hypotheses_list': {\n return text(await api.listHypotheses());\n }\n case 'growth_engineer_ask': {\n const input = inputs.growth_engineer_ask.parse(args);\n return text(await api.growthEngineerAsk(input.question));\n }\n default:\n throw new Error(`unknown tool: ${name}`);\n }\n}\n\nfunction text(payload: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction zodToJson(schema: z.ZodTypeAny): Tool['inputSchema'] {\n const shape = (schema as z.ZodObject<Record<string, z.ZodTypeAny>>).shape;\n const properties: Record<string, object> = {};\n const required: string[] = [];\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = describe(value);\n if (!value.isOptional()) required.push(key);\n }\n return {\n type: 'object',\n properties,\n required,\n additionalProperties: false,\n };\n}\n\nfunction describe(schema: z.ZodTypeAny): Record<string, unknown> {\n if (schema instanceof z.ZodString) {\n const desc = schema.description;\n return desc ? { type: 'string', description: desc } : { type: 'string' };\n }\n if (schema instanceof z.ZodNumber) return { type: 'number' };\n if (schema instanceof z.ZodBoolean) return { type: 'boolean' };\n if (schema instanceof z.ZodEnum) return { type: 'string', enum: schema.options };\n if (schema instanceof z.ZodOptional) return describe(schema.unwrap());\n if (schema instanceof z.ZodDefault) return describe(schema.removeDefault());\n return { type: 'string' };\n}\n","import type {\n ReadResourceRequest,\n ReadResourceResult,\n Resource,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { GrowthApi } from './api.js';\n\nexport const resources: Resource[] = [\n {\n uri: 'growth://overview',\n name: 'Overview snapshot',\n description: 'Top-line numbers (last 30d): MRR, active users, signup→paid, deltas vs prior period.',\n mimeType: 'application/json',\n },\n {\n uri: 'growth://anomalies/recent',\n name: 'Recent anomalies',\n description: 'Statistical anomalies detected in the last 24h with attached candidate hypotheses.',\n mimeType: 'application/json',\n },\n {\n uri: 'growth://commits/recent',\n name: 'Recent commits with metric deltas',\n description: 'Last 48h of commits annotated with their measured metric impact.',\n mimeType: 'application/json',\n },\n {\n uri: 'growth://benchmarks/peers',\n name: 'Peer benchmarks',\n description: 'How your project compares to anonymized peer SaaS at the same category + stage.',\n mimeType: 'application/json',\n },\n {\n uri: 'growth://hypotheses',\n name: 'Hypothesis backlog',\n description: 'Open + testing + completed hypotheses, RICE-ranked.',\n mimeType: 'application/json',\n },\n];\n\nexport async function readResource(\n api: GrowthApi,\n req: ReadResourceRequest,\n): Promise<ReadResourceResult> {\n const { uri } = req.params;\n\n if (uri === 'growth://overview') return jsonResource(uri, await api.getOverview('30d'));\n if (uri === 'growth://anomalies/recent') return jsonResource(uri, await api.listAnomalies());\n if (uri === 'growth://commits/recent') return jsonResource(uri, await api.listCommits('48h'));\n if (uri === 'growth://benchmarks/peers') return jsonResource(uri, await api.getPeerBenchmarks());\n if (uri === 'growth://hypotheses') return jsonResource(uri, await api.listHypotheses());\n\n throw new Error(`unknown resource: ${uri}`);\n}\n\nfunction jsonResource(uri: string, data: unknown): ReadResourceResult {\n return {\n contents: [\n {\n uri,\n mimeType: 'application/json',\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n}\n","import type {\n GetPromptRequest,\n GetPromptResult,\n Prompt,\n} from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * MCP prompts surface as slash-commands in Claude Desktop / Claude Code.\n * Each prompt is a pre-baked question for the AI growth engineer that the\n * model will run via the `growth_engineer_ask` tool — saving the user from\n * remembering the framework.\n */\nexport const prompts: Prompt[] = [\n {\n name: 'weekly',\n description:\n 'Weekly review — what changed in the last 7 days, biggest mover, 3 actions to take this week.',\n },\n {\n name: 'pmf',\n description:\n 'PMF check — score 0-10 from retention flatness, organic share, revenue concentration. What blocks the next click up?',\n },\n {\n name: 'pivot',\n description:\n 'Pivot analysis — which behavioral cohorts are outperforming by ≥2× on retention or LTV, ranked.',\n },\n {\n name: 'icp',\n description:\n 'Ideal customer profile — cross behavioral × revenue cohorts. Who pays the most, retains the longest?',\n },\n {\n name: 'strategy',\n description:\n \"90-day plan — pick ONE highest-leverage focus area, justify with current metrics + backlog hypotheses.\",\n },\n {\n name: 'diagnose',\n description:\n 'Why a metric dropped — root-cause analysis cross-referencing events, funnel position, and recent commits.',\n arguments: [\n {\n name: 'metric',\n description: 'The metric that dropped (e.g. signup_to_paid, d7_retention, mrr).',\n required: true,\n },\n ],\n },\n {\n name: 'init',\n description:\n 'Auto-instrument this repo with growth-loop.dev SDK — detect stack, install SDK, wrap signup/checkout/key call sites, open a single PR.',\n },\n];\n\nconst QUESTIONS: Record<string, (args: Record<string, string>) => string> = {\n weekly: () =>\n \"Give me a weekly review. What changed in the last 7 days vs the prior 7? What's the single biggest mover, and what 3 actions should I take this week?\",\n pmf: () =>\n \"Where am I on PMF? Use retention curve flatness, organic acquisition share, and revenue concentration. Score 0-10 and tell me what's blocking the next click up.\",\n pivot: () =>\n 'Should I pivot? Look at cohort patterns. Are there segments outperforming by ≥2× on retention or LTV? Propose 3-5 plausible pivots ranked by data evidence.',\n icp: () =>\n 'Who is my ideal customer? Cross behavioral cohorts × revenue cohorts: who pays the most, retains the longest, refers the most. Output ICP attributes with numbers.',\n strategy: () =>\n \"What's my highest-leverage focus for the next 90 days? Pick ONE area and justify it with my current metrics + my backlog hypotheses + memory of prior decisions.\",\n diagnose: (args) =>\n `Why has ${args.metric ?? 'this metric'} dropped recently? Cross-reference funnel data, raw events, and recent commits. Return ranked hypotheses with confidence scores.`,\n init: () => `Instrument this repo with growth-loop.dev. Read the codebase, install the SDK, wrap the highest-signal call sites with growth helpers, and open a single PR with all changes.\n\n## Step 1 — detect the stack\n\nRead package.json, pyproject.toml, framework configs. Pick the SDK:\n- JS/TS (Next, Vite, Express, etc.) → @growth-loop/sdk\n- Python (FastAPI, Flask) → growth-loop-sdk\n\nIf you see @supabase/supabase-js in package.json, the canonical signup/login signal is the onAuthStateChange callback — wrap that, do NOT chase a custom /api/auth/signup route.\n\n## Step 2 — install one client module\n\nCreate exactly ONE shared client. Pull GROWTH_KEY from env.\n\nTypeScript browser (src/lib/growth.ts):\n\\`\\`\\`ts\n'use client';\nimport { instrument } from '@growth-loop/sdk';\nimport { createBrowserClient } from '@growth-loop/sdk/browser';\nexport const growth = instrument(createBrowserClient({\n apiKey: process.env.NEXT_PUBLIC_GROWTH_KEY!,\n host: process.env.NEXT_PUBLIC_GROWTH_HOST ?? 'https://api.growth-loop.dev',\n}));\n\\`\\`\\`\n\nTypeScript server (src/lib/growth.server.ts):\n\\`\\`\\`ts\nimport { instrument } from '@growth-loop/sdk';\nimport { createServerClient } from '@growth-loop/sdk/node';\nexport const growth = instrument(createServerClient({\n apiKey: process.env.GROWTH_KEY!,\n host: process.env.GROWTH_HOST ?? 'https://api.growth-loop.dev',\n environment: process.env.NODE_ENV as 'production' | 'development' | 'preview',\n release: process.env.GIT_COMMIT_SHA,\n}));\n\\`\\`\\`\n\nPython (growth_setup.py):\n\\`\\`\\`python\nimport os\nfrom growth import Growth, GrowthOptions\ngrowth = Growth(GrowthOptions(\n api_key=os.environ[\"GROWTH_KEY\"],\n host=os.environ.get(\"GROWTH_HOST\", \"https://api.growth-loop.dev\"),\n))\n\\`\\`\\`\n\n## Step 3 — wrap the highest-signal call sites\n\nAim for 15–30 events total. Priority order:\n\n1. **Auth** — wrap signup and login callbacks. After auth: \\`growth.identify({ distinctId: user.id })\\` then \\`growth.step('signup_completed' | 'login_completed')\\`.\n2. **Activation funnel** — one event per step the user must clear to \"get value\": \\`onboarding.started\\`, \\`first_<thing>_created\\`. Use \\`growth.step('activation.<step>')\\`.\n3. **Revenue** — at Stripe Checkout entry: \\`checkout_started\\`. At webhook handler: \\`subscription_created\\`. At /pricing visit: \\`pricing_viewed\\`.\n4. **Top 5–10 buttons that matter** — wrap with \\`growth.button('<event>', onClick, { location })\\`. SKIP nav links, modal close, tooltips.\n5. **Critical async flows (>500ms)** — wrap with \\`growth.span('<name>', fn)\\`. Use on payment processing, AI calls, file uploads.\n\nPython decorator pattern:\n\\`\\`\\`python\nfrom growth import growth_span\nfrom growth_setup import growth\n\n@growth_span(growth, \"checkout\", distinct_id=lambda body: body.user_id)\nasync def checkout(body: CheckoutBody): ...\n\\`\\`\\`\n\n## Step 4 — env vars\n\nAppend to .env.example, prompt user to copy to .env.local:\n\\`\\`\\`\nGROWTH_KEY=pk_live_…\nGROWTH_HOST=https://api.growth-loop.dev\nNEXT_PUBLIC_GROWTH_KEY=pk_live_… # only for browser SDK\n\\`\\`\\`\n\n## Step 5 — open a SINGLE PR\n\nOne commit, one branch (\\`feature/growth-init-instrumentation\\`), one PR. Description should list each file modified and each event added inline (e.g. \\`signup_completed (auth/sign-up.tsx:42)\\`).\n\n## Guardrails — do not violate\n\n- Never invent events. Only wrap call sites that already exist.\n- 15–30 events max in the first PR. More can be added later.\n- NEVER log PII as event properties (email, password, payment details). Use \\`distinctId\\` for user identity; never put email in \\`properties\\`.\n- Never log inside tight loops.\n- Don't mix instrumentation with feature work — separate PR.\n- If the SDK is already installed, don't reinstall — just add the missing wraps.\n\nBegin by reading package.json or pyproject.toml.`,\n};\n\nexport function getPrompt(req: GetPromptRequest): GetPromptResult {\n const name = req.params.name;\n const builder = QUESTIONS[name];\n if (!builder) throw new Error(`unknown prompt: ${name}`);\n const question = builder((req.params.arguments ?? {}) as Record<string, string>);\n return {\n description: prompts.find((p) => p.name === name)?.description ?? '',\n messages: [\n {\n role: 'user',\n content: { type: 'text', text: question },\n },\n ],\n };\n}\n","interface GrowthApiOptions {\n apiKey?: string | undefined;\n sessionToken?: string | undefined;\n host: string;\n projectId?: string | undefined;\n}\n\nexport class GrowthApi {\n constructor(private readonly options: GrowthApiOptions) {}\n\n // ─── read endpoints ───────────────────────────────────────────────────\n\n async getOverview(range: string): Promise<unknown> {\n return this.get(`/v1/overview?range=${encodeURIComponent(range)}`);\n }\n\n async getMetricSeries(metric: string, range: string): Promise<unknown> {\n return this.get(\n `/v1/metrics/series?metric=${encodeURIComponent(metric)}&range=${encodeURIComponent(range)}`,\n );\n }\n\n async getFunnel(name: string, range: string): Promise<unknown> {\n return this.get(`/v1/funnels/${encodeURIComponent(name)}?range=${encodeURIComponent(range)}`);\n }\n\n async listFunnels(): Promise<unknown> {\n return this.get('/v1/funnels');\n }\n\n async getRetention(range: string): Promise<unknown> {\n return this.get(`/v1/retention?range=${encodeURIComponent(range)}`);\n }\n\n async listAnomalies(severity?: string): Promise<unknown> {\n const qs = severity ? `?severity=${encodeURIComponent(severity)}` : '';\n return this.get(`/v1/anomalies${qs}`);\n }\n\n async listCommits(since: string): Promise<unknown> {\n return this.get(`/v1/commits?since=${encodeURIComponent(since)}`);\n }\n\n async getPeerBenchmarks(): Promise<unknown> {\n return this.get('/v1/benchmarks/peers');\n }\n\n async listHypotheses(): Promise<unknown> {\n return this.get('/v1/hypotheses');\n }\n\n // ─── agent endpoints ──────────────────────────────────────────────────\n\n async diagnose(metric: string, since: string): Promise<unknown> {\n return this.post('/v1/agent/diagnose', { metric, since });\n }\n\n async whyEvent(event: string): Promise<unknown> {\n return this.post('/v1/agent/why_event', { event });\n }\n\n /**\n * Talk to the AI growth engineer. Creates an ephemeral thread, posts the\n * question, returns the assistant reply with citations. Requires a\n * session token (cookie auth) — api-keys don't reach this endpoint.\n */\n async growthEngineerAsk(content: string): Promise<unknown> {\n const created = (await this.post('/v1/growth-engineer/threads', {})) as { id: string };\n return this.post(`/v1/growth-engineer/threads/${created.id}/messages`, { content });\n }\n\n // ─── transport ────────────────────────────────────────────────────────\n\n private async get(path: string): Promise<unknown> {\n return this.request('GET', path);\n }\n\n private async post(path: string, body: unknown): Promise<unknown> {\n return this.request('POST', path, body);\n }\n\n private async request(method: string, path: string, body?: unknown): Promise<unknown> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n };\n if (this.options.apiKey) headers['x-growth-api-key'] = this.options.apiKey;\n if (this.options.sessionToken) headers['cookie'] = `growth_session=${this.options.sessionToken}`;\n if (this.options.projectId) headers['x-growth-project'] = this.options.projectId;\n\n const res = await fetch(`${this.options.host}${path}`, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`growth-api ${res.status} ${path}: ${text}`);\n }\n return res.json();\n }\n}\n"],"mappings":";;;AAAA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACJP,SAAS,SAAS;AAGlB,IAAM,YAAY,EAAE,KAAK,CAAC,MAAM,OAAO,MAAM,OAAO,KAAK,CAAC;AAE1D,IAAM,SAAS;AAAA,EACb,UAAU,EAAE,OAAO,EAAE,OAAO,UAAU,QAAQ,KAAK,EAAE,CAAC;AAAA,EACtD,eAAe,EAAE,OAAO;AAAA,IACtB,QAAQ,EAAE,OAAO;AAAA,IACjB,OAAO,UAAU,QAAQ,KAAK;AAAA,EAChC,CAAC;AAAA,EACD,YAAY,EAAE,OAAO;AAAA,IACnB,MAAM,EAAE,OAAO;AAAA,IACf,OAAO,UAAU,QAAQ,KAAK;AAAA,EAChC,CAAC;AAAA,EACD,cAAc,EAAE,OAAO,CAAC,CAAC;AAAA,EACzB,WAAW,EAAE,OAAO,EAAE,OAAO,UAAU,QAAQ,KAAK,EAAE,CAAC;AAAA,EACvD,UAAU,EAAE,OAAO;AAAA,IACjB,QAAQ,EAAE,OAAO;AAAA,IACjB,OAAO,EAAE,OAAO,EAAE,QAAQ,KAAK;AAAA,EACjC,CAAC;AAAA,EACD,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAAA,EACzC,gBAAgB,EAAE,OAAO;AAAA,IACvB,UAAU,EAAE,KAAK,CAAC,QAAQ,QAAQ,UAAU,CAAC,EAAE,SAAS;AAAA,EAC1D,CAAC;AAAA,EACD,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,KAAK,EAAE,CAAC;AAAA,EAC3D,gBAAgB,EAAE,OAAO,CAAC,CAAC;AAAA,EAC3B,iBAAiB,EAAE,OAAO,CAAC,CAAC;AAAA,EAC5B,qBAAqB,EAAE,OAAO;AAAA,IAC5B,UAAU,EACP,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAI,EACR,SAAS,8CAA8C;AAAA,EAC5D,CAAC;AACH;AAEO,IAAM,QAAgB;AAAA,EAC3B;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,aAAa,UAAU,OAAO,QAAQ;AAAA,EACxC;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa,UAAU,OAAO,aAAa;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa,EAAE,MAAM,UAAU,YAAY,CAAC,GAAG,sBAAsB,MAAM;AAAA,EAC7E;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa,UAAU,OAAO,UAAU;AAAA,EAC1C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa,UAAU,OAAO,SAAS;AAAA,EACzC;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,aAAa,UAAU,OAAO,QAAQ;AAAA,EACxC;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,aAAa,UAAU,OAAO,SAAS;AAAA,EACzC;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa,UAAU,OAAO,cAAc;AAAA,EAC9C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa,UAAU,OAAO,YAAY;AAAA,EAC5C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,aAAa,EAAE,MAAM,UAAU,YAAY,CAAC,GAAG,sBAAsB,MAAM;AAAA,EAC7E;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa;AAAA,IACb,aAAa,EAAE,MAAM,UAAU,YAAY,CAAC,GAAG,sBAAsB,MAAM;AAAA,EAC7E;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,aAAa,UAAU,OAAO,mBAAmB;AAAA,EACnD;AACF;AAEA,eAAsB,SAASA,MAAgB,KAA+C;AAC5F,QAAM,OAAO,IAAI,OAAO;AACxB,QAAM,OAAO,IAAI,OAAO,aAAa,CAAC;AAEtC,UAAQ,MAAM;AAAA,IACZ,KAAK,YAAY;AACf,YAAM,QAAQ,OAAO,SAAS,MAAM,IAAI;AACxC,aAAO,KAAK,MAAMA,KAAI,YAAY,MAAM,KAAK,CAAC;AAAA,IAChD;AAAA,IACA,KAAK,iBAAiB;AACpB,YAAM,QAAQ,OAAO,cAAc,MAAM,IAAI;AAC7C,aAAO,KAAK,MAAMA,KAAI,gBAAgB,MAAM,QAAQ,MAAM,KAAK,CAAC;AAAA,IAClE;AAAA,IACA,KAAK,gBAAgB;AACnB,aAAO,KAAK,MAAMA,KAAI,YAAY,CAAC;AAAA,IACrC;AAAA,IACA,KAAK,cAAc;AACjB,YAAM,QAAQ,OAAO,WAAW,MAAM,IAAI;AAC1C,aAAO,KAAK,MAAMA,KAAI,UAAU,MAAM,MAAM,MAAM,KAAK,CAAC;AAAA,IAC1D;AAAA,IACA,KAAK,aAAa;AAChB,YAAM,QAAQ,OAAO,UAAU,MAAM,IAAI;AACzC,aAAO,KAAK,MAAMA,KAAI,aAAa,MAAM,KAAK,CAAC;AAAA,IACjD;AAAA,IACA,KAAK,YAAY;AACf,YAAM,QAAQ,OAAO,SAAS,MAAM,IAAI;AACxC,aAAO,KAAK,MAAMA,KAAI,SAAS,MAAM,QAAQ,MAAM,KAAK,CAAC;AAAA,IAC3D;AAAA,IACA,KAAK,aAAa;AAChB,YAAM,QAAQ,OAAO,UAAU,MAAM,IAAI;AACzC,aAAO,KAAK,MAAMA,KAAI,SAAS,MAAM,KAAK,CAAC;AAAA,IAC7C;AAAA,IACA,KAAK,kBAAkB;AACrB,YAAM,QAAQ,OAAO,eAAe,MAAM,IAAI;AAC9C,aAAO,KAAK,MAAMA,KAAI,cAAc,MAAM,QAAQ,CAAC;AAAA,IACrD;AAAA,IACA,KAAK,gBAAgB;AACnB,YAAM,QAAQ,OAAO,aAAa,MAAM,IAAI;AAC5C,aAAO,KAAK,MAAMA,KAAI,YAAY,MAAM,KAAK,CAAC;AAAA,IAChD;AAAA,IACA,KAAK,kBAAkB;AACrB,aAAO,KAAK,MAAMA,KAAI,kBAAkB,CAAC;AAAA,IAC3C;AAAA,IACA,KAAK,mBAAmB;AACtB,aAAO,KAAK,MAAMA,KAAI,eAAe,CAAC;AAAA,IACxC;AAAA,IACA,KAAK,uBAAuB;AAC1B,YAAM,QAAQ,OAAO,oBAAoB,MAAM,IAAI;AACnD,aAAO,KAAK,MAAMA,KAAI,kBAAkB,MAAM,QAAQ,CAAC;AAAA,IACzD;AAAA,IACA;AACE,YAAM,IAAI,MAAM,iBAAiB,IAAI,EAAE;AAAA,EAC3C;AACF;AAEA,SAAS,KAAK,SAAkC;AAC9C,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,CAAC;AAAA,EACpE;AACF;AAEA,SAAS,UAAU,QAA2C;AAC5D,QAAM,QAAS,OAAqD;AACpE,QAAM,aAAqC,CAAC;AAC5C,QAAM,WAAqB,CAAC;AAC5B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,eAAW,GAAG,IAAI,SAAS,KAAK;AAChC,QAAI,CAAC,MAAM,WAAW,EAAG,UAAS,KAAK,GAAG;AAAA,EAC5C;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,EACxB;AACF;AAEA,SAAS,SAAS,QAA+C;AAC/D,MAAI,kBAAkB,EAAE,WAAW;AACjC,UAAM,OAAO,OAAO;AACpB,WAAO,OAAO,EAAE,MAAM,UAAU,aAAa,KAAK,IAAI,EAAE,MAAM,SAAS;AAAA,EACzE;AACA,MAAI,kBAAkB,EAAE,UAAW,QAAO,EAAE,MAAM,SAAS;AAC3D,MAAI,kBAAkB,EAAE,WAAY,QAAO,EAAE,MAAM,UAAU;AAC7D,MAAI,kBAAkB,EAAE,QAAS,QAAO,EAAE,MAAM,UAAU,MAAM,OAAO,QAAQ;AAC/E,MAAI,kBAAkB,EAAE,YAAa,QAAO,SAAS,OAAO,OAAO,CAAC;AACpE,MAAI,kBAAkB,EAAE,WAAY,QAAO,SAAS,OAAO,cAAc,CAAC;AAC1E,SAAO,EAAE,MAAM,SAAS;AAC1B;;;AC/LO,IAAM,YAAwB;AAAA,EACnC;AAAA,IACE,KAAK;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AACF;AAEA,eAAsB,aACpBC,MACA,KAC6B;AAC7B,QAAM,EAAE,IAAI,IAAI,IAAI;AAEpB,MAAI,QAAQ,oBAAqB,QAAO,aAAa,KAAK,MAAMA,KAAI,YAAY,KAAK,CAAC;AACtF,MAAI,QAAQ,4BAA6B,QAAO,aAAa,KAAK,MAAMA,KAAI,cAAc,CAAC;AAC3F,MAAI,QAAQ,0BAA2B,QAAO,aAAa,KAAK,MAAMA,KAAI,YAAY,KAAK,CAAC;AAC5F,MAAI,QAAQ,4BAA6B,QAAO,aAAa,KAAK,MAAMA,KAAI,kBAAkB,CAAC;AAC/F,MAAI,QAAQ,sBAAuB,QAAO,aAAa,KAAK,MAAMA,KAAI,eAAe,CAAC;AAEtF,QAAM,IAAI,MAAM,qBAAqB,GAAG,EAAE;AAC5C;AAEA,SAAS,aAAa,KAAa,MAAmC;AACpE,SAAO;AAAA,IACL,UAAU;AAAA,MACR;AAAA,QACE;AAAA,QACA,UAAU;AAAA,QACV,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AACF;;;ACrDO,IAAM,UAAoB;AAAA,EAC/B;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,WAAW;AAAA,MACT;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AACF;AAEA,IAAM,YAAsE;AAAA,EAC1E,QAAQ,MACN;AAAA,EACF,KAAK,MACH;AAAA,EACF,OAAO,MACL;AAAA,EACF,KAAK,MACH;AAAA,EACF,UAAU,MACR;AAAA,EACF,UAAU,CAAC,SACT,WAAW,KAAK,UAAU,aAAa;AAAA,EACzC,MAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyFd;AAEO,SAAS,UAAU,KAAwC;AAChE,QAAM,OAAO,IAAI,OAAO;AACxB,QAAM,UAAU,UAAU,IAAI;AAC9B,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,mBAAmB,IAAI,EAAE;AACvD,QAAM,WAAW,QAAS,IAAI,OAAO,aAAa,CAAC,CAA4B;AAC/E,SAAO;AAAA,IACL,aAAa,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG,eAAe;AAAA,IAClE,UAAU;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,SAAS,EAAE,MAAM,QAAQ,MAAM,SAAS;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AACF;;;ACxKO,IAAM,YAAN,MAAgB;AAAA,EACrB,YAA6B,SAA2B;AAA3B;AAAA,EAA4B;AAAA,EAA5B;AAAA;AAAA,EAI7B,MAAM,YAAY,OAAiC;AACjD,WAAO,KAAK,IAAI,sBAAsB,mBAAmB,KAAK,CAAC,EAAE;AAAA,EACnE;AAAA,EAEA,MAAM,gBAAgB,QAAgB,OAAiC;AACrE,WAAO,KAAK;AAAA,MACV,6BAA6B,mBAAmB,MAAM,CAAC,UAAU,mBAAmB,KAAK,CAAC;AAAA,IAC5F;AAAA,EACF;AAAA,EAEA,MAAM,UAAU,MAAc,OAAiC;AAC7D,WAAO,KAAK,IAAI,eAAe,mBAAmB,IAAI,CAAC,UAAU,mBAAmB,KAAK,CAAC,EAAE;AAAA,EAC9F;AAAA,EAEA,MAAM,cAAgC;AACpC,WAAO,KAAK,IAAI,aAAa;AAAA,EAC/B;AAAA,EAEA,MAAM,aAAa,OAAiC;AAClD,WAAO,KAAK,IAAI,uBAAuB,mBAAmB,KAAK,CAAC,EAAE;AAAA,EACpE;AAAA,EAEA,MAAM,cAAc,UAAqC;AACvD,UAAM,KAAK,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK;AACpE,WAAO,KAAK,IAAI,gBAAgB,EAAE,EAAE;AAAA,EACtC;AAAA,EAEA,MAAM,YAAY,OAAiC;AACjD,WAAO,KAAK,IAAI,qBAAqB,mBAAmB,KAAK,CAAC,EAAE;AAAA,EAClE;AAAA,EAEA,MAAM,oBAAsC;AAC1C,WAAO,KAAK,IAAI,sBAAsB;AAAA,EACxC;AAAA,EAEA,MAAM,iBAAmC;AACvC,WAAO,KAAK,IAAI,gBAAgB;AAAA,EAClC;AAAA;AAAA,EAIA,MAAM,SAAS,QAAgB,OAAiC;AAC9D,WAAO,KAAK,KAAK,sBAAsB,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC1D;AAAA,EAEA,MAAM,SAAS,OAAiC;AAC9C,WAAO,KAAK,KAAK,uBAAuB,EAAE,MAAM,CAAC;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAkB,SAAmC;AACzD,UAAM,UAAW,MAAM,KAAK,KAAK,+BAA+B,CAAC,CAAC;AAClE,WAAO,KAAK,KAAK,+BAA+B,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC;AAAA,EACpF;AAAA;AAAA,EAIA,MAAc,IAAI,MAAgC;AAChD,WAAO,KAAK,QAAQ,OAAO,IAAI;AAAA,EACjC;AAAA,EAEA,MAAc,KAAK,MAAc,MAAiC;AAChE,WAAO,KAAK,QAAQ,QAAQ,MAAM,IAAI;AAAA,EACxC;AAAA,EAEA,MAAc,QAAQ,QAAgB,MAAc,MAAkC;AACpF,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,IAClB;AACA,QAAI,KAAK,QAAQ,OAAQ,SAAQ,kBAAkB,IAAI,KAAK,QAAQ;AACpE,QAAI,KAAK,QAAQ,aAAc,SAAQ,QAAQ,IAAI,kBAAkB,KAAK,QAAQ,YAAY;AAC9F,QAAI,KAAK,QAAQ,UAAW,SAAQ,kBAAkB,IAAI,KAAK,QAAQ;AAEvE,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,QAAQ,IAAI,GAAG,IAAI,IAAI;AAAA,MACrD;AAAA,MACA;AAAA,MACA,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,IACpD,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAMC,QAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,YAAM,IAAI,MAAM,cAAc,IAAI,MAAM,IAAI,IAAI,KAAKA,KAAI,EAAE;AAAA,IAC7D;AACA,WAAO,IAAI,KAAK;AAAA,EAClB;AACF;;;AJtFA,IAAM,SAAS,QAAQ,IAAI;AAC3B,IAAM,eAAe,QAAQ,IAAI;AACjC,IAAM,OAAO,QAAQ,IAAI,eAAe;AACxC,IAAM,YAAY,QAAQ,IAAI;AAE9B,IAAI,CAAC,UAAU,CAAC,cAAc;AAC5B,UAAQ,OAAO;AAAA,IACb;AAAA,EAEF;AACA,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,MAAM,IAAI,UAAU,EAAE,QAAQ,cAAc,MAAM,UAAU,CAAC;AAEnE,IAAM,SAAS,IAAI;AAAA,EACjB,EAAE,MAAM,eAAe,SAAS,QAAQ;AAAA,EACxC,EAAE,cAAc,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,GAAG,SAAS,CAAC,EAAE,EAAE;AAC5D;AAEA,OAAO,kBAAkB,wBAAwB,aAAa,EAAE,MAAM,EAAE;AACxE,OAAO,kBAAkB,uBAAuB,OAAO,QAAQ,SAAS,KAAK,GAAG,CAAC;AAEjF,OAAO,kBAAkB,4BAA4B,aAAa,EAAE,UAAU,EAAE;AAChF,OAAO,kBAAkB,2BAA2B,OAAO,QAAQ,aAAa,KAAK,GAAG,CAAC;AAEzF,OAAO,kBAAkB,0BAA0B,aAAa,EAAE,QAAQ,EAAE;AAC5E,OAAO,kBAAkB,wBAAwB,OAAO,QAAQ,UAAU,GAAG,CAAC;AAE9E,IAAM,YAAY,IAAI,qBAAqB;AAC3C,MAAM,OAAO,QAAQ,SAAS;","names":["api","api","text"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tools.ts","../src/resources.ts","../src/prompts.ts","../src/api.ts"],"sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n ReadResourceRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { tools, callTool } from './tools.js';\nimport { resources, readResource } from './resources.js';\nimport { prompts, getPrompt } from './prompts.js';\nimport { GrowthApi } from './api.js';\n\nconst apiKey = process.env.GROWTH_API_KEY;\nconst sessionToken = process.env.GROWTH_SESSION_TOKEN;\nconst host = process.env.GROWTH_HOST ?? 'https://api.growth-loop.dev';\nconst projectId = process.env.GROWTH_PROJECT_ID;\n\nif (!apiKey && !sessionToken) {\n process.stderr.write(\n '[growth-mcp] either GROWTH_API_KEY or GROWTH_SESSION_TOKEN is required\\n' +\n ' api-key works for ingest + read endpoints; session-token unlocks growth_engineer_ask.\\n',\n );\n process.exit(1);\n}\n\nconst api = new GrowthApi({ apiKey, sessionToken, host, projectId });\n\nconst server = new Server(\n { name: 'growth-loop', version: '0.1.0' },\n { capabilities: { tools: {}, resources: {}, prompts: {} } },\n);\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));\nserver.setRequestHandler(CallToolRequestSchema, async (req) => callTool(api, req));\n\nserver.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources }));\nserver.setRequestHandler(ReadResourceRequestSchema, async (req) => readResource(api, req));\n\nserver.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts }));\nserver.setRequestHandler(GetPromptRequestSchema, async (req) => getPrompt(req));\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import type {\n CallToolRequest,\n CallToolResult,\n Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { z } from 'zod';\nimport type { GrowthApi } from './api.js';\n\nconst RangeEnum = z.enum(['1h', '24h', '7d', '30d', '90d']);\n\nconst inputs = {\n overview: z.object({ range: RangeEnum.default('30d') }),\n metric_series: z.object({\n metric: z.string(),\n range: RangeEnum.default('30d'),\n }),\n funnel_get: z.object({\n name: z.string(),\n range: RangeEnum.default('30d'),\n }),\n funnels_list: z.object({}),\n retention: z.object({ range: RangeEnum.default('90d') }),\n diagnose: z.object({\n metric: z.string(),\n since: z.string().default('24h'),\n }),\n why_event: z.object({ event: z.string() }),\n anomalies_list: z.object({\n severity: z.enum(['info', 'warn', 'critical']).optional(),\n }),\n commits_list: z.object({ since: z.string().default('48h') }),\n peer_benchmark: z.object({}),\n hypotheses_list: z.object({}),\n growth_engineer_ask: z.object({\n question: z\n .string()\n .min(2)\n .max(8000)\n .describe('Free-form question to the AI growth engineer'),\n }),\n};\n\nexport const tools: Tool[] = [\n {\n name: 'overview',\n description:\n 'Top-line snapshot for a date range: MRR, active users, signup→paid, deltas vs prior period.',\n inputSchema: zodToJson(inputs.overview),\n },\n {\n name: 'metric_series',\n description: 'Return a time series for a metric. Supports system + custom event metrics.',\n inputSchema: zodToJson(inputs.metric_series),\n },\n {\n name: 'funnels_list',\n description: 'List configured funnels with name + step count.',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'funnel_get',\n description: 'Get a funnel by name with conversion + drop-off per step.',\n inputSchema: zodToJson(inputs.funnel_get),\n },\n {\n name: 'retention',\n description: 'Cohort retention curve (D1, D7, D30) over the requested range.',\n inputSchema: zodToJson(inputs.retention),\n },\n {\n name: 'diagnose',\n description:\n 'Closed-loop investigation on a metric. Returns ranked hypotheses with citations to events + commits.',\n inputSchema: zodToJson(inputs.diagnose),\n },\n {\n name: 'why_event',\n description:\n 'Explain why users drop on or interact with a specific event. Cross-references replays + funnel position.',\n inputSchema: zodToJson(inputs.why_event),\n },\n {\n name: 'anomalies_list',\n description: 'List recent statistical anomalies with attached candidate hypotheses.',\n inputSchema: zodToJson(inputs.anomalies_list),\n },\n {\n name: 'commits_list',\n description: 'Recent commits annotated with their measured metric impact (git-aware analytics).',\n inputSchema: zodToJson(inputs.commits_list),\n },\n {\n name: 'peer_benchmark',\n description:\n 'Compare your project to anonymized peer SaaS at the same category + stage (signup→paid, retention, MRR).',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'hypotheses_list',\n description: 'RICE-ranked hypothesis backlog (open + testing + completed).',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'growth_engineer_ask',\n description:\n 'Talk to the AI growth engineer for vibe-coded SaaS. The agent is grounded in your funnel, retention, peer benchmarks, hypotheses, deploys, and the last 30 raw events. Every answer cites real rows from your project. Requires a session token (set GROWTH_SESSION_TOKEN env).',\n inputSchema: zodToJson(inputs.growth_engineer_ask),\n },\n];\n\nexport async function callTool(api: GrowthApi, req: CallToolRequest): Promise<CallToolResult> {\n const name = req.params.name;\n const args = req.params.arguments ?? {};\n\n switch (name) {\n case 'overview': {\n const input = inputs.overview.parse(args);\n return text(await api.getOverview(input.range));\n }\n case 'metric_series': {\n const input = inputs.metric_series.parse(args);\n return text(await api.getMetricSeries(input.metric, input.range));\n }\n case 'funnels_list': {\n return text(await api.listFunnels());\n }\n case 'funnel_get': {\n const input = inputs.funnel_get.parse(args);\n return text(await api.getFunnel(input.name, input.range));\n }\n case 'retention': {\n const input = inputs.retention.parse(args);\n return text(await api.getRetention(input.range));\n }\n case 'diagnose': {\n const input = inputs.diagnose.parse(args);\n return text(await api.diagnose(input.metric, input.since));\n }\n case 'why_event': {\n const input = inputs.why_event.parse(args);\n return text(await api.whyEvent(input.event));\n }\n case 'anomalies_list': {\n const input = inputs.anomalies_list.parse(args);\n return text(await api.listAnomalies(input.severity));\n }\n case 'commits_list': {\n const input = inputs.commits_list.parse(args);\n return text(await api.listCommits(input.since));\n }\n case 'peer_benchmark': {\n return text(await api.getPeerBenchmarks());\n }\n case 'hypotheses_list': {\n return text(await api.listHypotheses());\n }\n case 'growth_engineer_ask': {\n const input = inputs.growth_engineer_ask.parse(args);\n return text(await api.growthEngineerAsk(input.question));\n }\n default:\n throw new Error(`unknown tool: ${name}`);\n }\n}\n\nfunction text(payload: unknown): CallToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nfunction zodToJson(schema: z.ZodTypeAny): Tool['inputSchema'] {\n const shape = (schema as z.ZodObject<Record<string, z.ZodTypeAny>>).shape;\n const properties: Record<string, object> = {};\n const required: string[] = [];\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = describe(value);\n if (!value.isOptional()) required.push(key);\n }\n return {\n type: 'object',\n properties,\n required,\n additionalProperties: false,\n };\n}\n\nfunction describe(schema: z.ZodTypeAny): Record<string, unknown> {\n if (schema instanceof z.ZodString) {\n const desc = schema.description;\n return desc ? { type: 'string', description: desc } : { type: 'string' };\n }\n if (schema instanceof z.ZodNumber) return { type: 'number' };\n if (schema instanceof z.ZodBoolean) return { type: 'boolean' };\n if (schema instanceof z.ZodEnum) return { type: 'string', enum: schema.options };\n if (schema instanceof z.ZodOptional) return describe(schema.unwrap());\n if (schema instanceof z.ZodDefault) return describe(schema.removeDefault());\n return { type: 'string' };\n}\n","import type {\n ReadResourceRequest,\n ReadResourceResult,\n Resource,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { GrowthApi } from './api.js';\n\nexport const resources: Resource[] = [\n {\n uri: 'growth://overview',\n name: 'Overview snapshot',\n description: 'Top-line numbers (last 30d): MRR, active users, signup→paid, deltas vs prior period.',\n mimeType: 'application/json',\n },\n {\n uri: 'growth://anomalies/recent',\n name: 'Recent anomalies',\n description: 'Statistical anomalies detected in the last 24h with attached candidate hypotheses.',\n mimeType: 'application/json',\n },\n {\n uri: 'growth://commits/recent',\n name: 'Recent commits with metric deltas',\n description: 'Last 48h of commits annotated with their measured metric impact.',\n mimeType: 'application/json',\n },\n {\n uri: 'growth://benchmarks/peers',\n name: 'Peer benchmarks',\n description: 'How your project compares to anonymized peer SaaS at the same category + stage.',\n mimeType: 'application/json',\n },\n {\n uri: 'growth://hypotheses',\n name: 'Hypothesis backlog',\n description: 'Open + testing + completed hypotheses, RICE-ranked.',\n mimeType: 'application/json',\n },\n];\n\nexport async function readResource(\n api: GrowthApi,\n req: ReadResourceRequest,\n): Promise<ReadResourceResult> {\n const { uri } = req.params;\n\n if (uri === 'growth://overview') return jsonResource(uri, await api.getOverview('30d'));\n if (uri === 'growth://anomalies/recent') return jsonResource(uri, await api.listAnomalies());\n if (uri === 'growth://commits/recent') return jsonResource(uri, await api.listCommits('48h'));\n if (uri === 'growth://benchmarks/peers') return jsonResource(uri, await api.getPeerBenchmarks());\n if (uri === 'growth://hypotheses') return jsonResource(uri, await api.listHypotheses());\n\n throw new Error(`unknown resource: ${uri}`);\n}\n\nfunction jsonResource(uri: string, data: unknown): ReadResourceResult {\n return {\n contents: [\n {\n uri,\n mimeType: 'application/json',\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n}\n","import type {\n GetPromptRequest,\n GetPromptResult,\n Prompt,\n} from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * MCP prompts surface as slash-commands in Claude Desktop / Claude Code.\n * Each prompt is a pre-baked question for the AI growth engineer that the\n * model will run via the `growth_engineer_ask` tool — saving the user from\n * remembering the framework.\n */\nexport const prompts: Prompt[] = [\n {\n name: 'growth-weekly',\n description:\n 'Weekly review — what changed in the last 7 days, biggest mover, 3 actions to take this week.',\n },\n {\n name: 'growth-pmf',\n description:\n 'PMF check — score 0-10 from retention flatness, organic share, revenue concentration. What blocks the next click up?',\n },\n {\n name: 'growth-pivot',\n description:\n 'Pivot analysis — which behavioral cohorts are outperforming by ≥2× on retention or LTV, ranked.',\n },\n {\n name: 'growth-icp',\n description:\n 'Ideal customer profile — cross behavioral × revenue cohorts. Who pays the most, retains the longest?',\n },\n {\n name: 'growth-strategy',\n description:\n \"90-day plan — pick ONE highest-leverage focus area, justify with current metrics + backlog hypotheses.\",\n },\n {\n name: 'growth-diagnose',\n description:\n 'Why a metric dropped — root-cause analysis cross-referencing events, funnel position, and recent commits.',\n arguments: [\n {\n name: 'metric',\n description: 'The metric that dropped (e.g. signup_to_paid, d7_retention, mrr).',\n required: true,\n },\n ],\n },\n {\n name: 'growth-init',\n description:\n 'Auto-instrument this repo with growth-loop.dev SDK — detect stack, install SDK, wrap signup/checkout/key call sites, open a single PR.',\n },\n];\n\nconst QUESTIONS: Record<string, (args: Record<string, string>) => string> = {\n 'growth-weekly': () =>\n \"Give me a weekly review. What changed in the last 7 days vs the prior 7? What's the single biggest mover, and what 3 actions should I take this week?\",\n 'growth-pmf': () =>\n \"Where am I on PMF? Use retention curve flatness, organic acquisition share, and revenue concentration. Score 0-10 and tell me what's blocking the next click up.\",\n 'growth-pivot': () =>\n 'Should I pivot? Look at cohort patterns. Are there segments outperforming by ≥2× on retention or LTV? Propose 3-5 plausible pivots ranked by data evidence.',\n 'growth-icp': () =>\n 'Who is my ideal customer? Cross behavioral cohorts × revenue cohorts: who pays the most, retains the longest, refers the most. Output ICP attributes with numbers.',\n 'growth-strategy': () =>\n \"What's my highest-leverage focus for the next 90 days? Pick ONE area and justify it with my current metrics + my backlog hypotheses + memory of prior decisions.\",\n 'growth-diagnose': (args) =>\n `Why has ${args.metric ?? 'this metric'} dropped recently? Cross-reference funnel data, raw events, and recent commits. Return ranked hypotheses with confidence scores.`,\n 'growth-init': () => `Instrument this repo with growth-loop.dev. Read the codebase, install the SDK, wrap the highest-signal call sites with growth helpers, and open a single PR with all changes.\n\n## Step 1 — detect the stack\n\nRead package.json, pyproject.toml, framework configs. Pick the SDK:\n- JS/TS (Next, Vite, Express, etc.) → @growth-loop/sdk\n- Python (FastAPI, Flask) → growth-loop-sdk\n\nIf you see @supabase/supabase-js in package.json, the canonical signup/login signal is the onAuthStateChange callback — wrap that, do NOT chase a custom /api/auth/signup route.\n\n## Step 2 — install one client module\n\nCreate exactly ONE shared client. Pull GROWTH_KEY from env.\n\nTypeScript browser (src/lib/growth.ts):\n\\`\\`\\`ts\n'use client';\nimport { instrument } from '@growth-loop/sdk';\nimport { createBrowserClient } from '@growth-loop/sdk/browser';\nexport const growth = instrument(createBrowserClient({\n apiKey: process.env.NEXT_PUBLIC_GROWTH_KEY!,\n host: process.env.NEXT_PUBLIC_GROWTH_HOST ?? 'https://api.growth-loop.dev',\n}));\n\\`\\`\\`\n\nTypeScript server (src/lib/growth.server.ts):\n\\`\\`\\`ts\nimport { instrument } from '@growth-loop/sdk';\nimport { createServerClient } from '@growth-loop/sdk/node';\nexport const growth = instrument(createServerClient({\n apiKey: process.env.GROWTH_KEY!,\n host: process.env.GROWTH_HOST ?? 'https://api.growth-loop.dev',\n environment: process.env.NODE_ENV as 'production' | 'development' | 'preview',\n release: process.env.GIT_COMMIT_SHA,\n}));\n\\`\\`\\`\n\nPython (growth_setup.py):\n\\`\\`\\`python\nimport os\nfrom growth import Growth, GrowthOptions\ngrowth = Growth(GrowthOptions(\n api_key=os.environ[\"GROWTH_KEY\"],\n host=os.environ.get(\"GROWTH_HOST\", \"https://api.growth-loop.dev\"),\n))\n\\`\\`\\`\n\n## Step 3 — wrap the highest-signal call sites\n\nAim for 15–30 events total. Priority order:\n\n1. **Auth** — wrap signup and login callbacks. After auth: \\`growth.identify({ distinctId: user.id })\\` then \\`growth.step('signup_completed' | 'login_completed')\\`.\n2. **Activation funnel** — one event per step the user must clear to \"get value\": \\`onboarding.started\\`, \\`first_<thing>_created\\`. Use \\`growth.step('activation.<step>')\\`.\n3. **Revenue** — at Stripe Checkout entry: \\`checkout_started\\`. At webhook handler: \\`subscription_created\\`. At /pricing visit: \\`pricing_viewed\\`.\n4. **Top 5–10 buttons that matter** — wrap with \\`growth.button('<event>', onClick, { location })\\`. SKIP nav links, modal close, tooltips.\n5. **Critical async flows (>500ms)** — wrap with \\`growth.span('<name>', fn)\\`. Use on payment processing, AI calls, file uploads.\n\nPython decorator pattern:\n\\`\\`\\`python\nfrom growth import growth_span\nfrom growth_setup import growth\n\n@growth_span(growth, \"checkout\", distinct_id=lambda body: body.user_id)\nasync def checkout(body: CheckoutBody): ...\n\\`\\`\\`\n\n## Step 4 — env vars\n\nAppend to .env.example, prompt user to copy to .env.local:\n\\`\\`\\`\nGROWTH_KEY=pk_live_…\nGROWTH_HOST=https://api.growth-loop.dev\nNEXT_PUBLIC_GROWTH_KEY=pk_live_… # only for browser SDK\n\\`\\`\\`\n\n## Step 5 — open a SINGLE PR\n\nOne commit, one branch (\\`feature/growth-init-instrumentation\\`), one PR. Description should list each file modified and each event added inline (e.g. \\`signup_completed (auth/sign-up.tsx:42)\\`).\n\n## Guardrails — do not violate\n\n- Never invent events. Only wrap call sites that already exist.\n- 15–30 events max in the first PR. More can be added later.\n- NEVER log PII as event properties (email, password, payment details). Use \\`distinctId\\` for user identity; never put email in \\`properties\\`.\n- Never log inside tight loops.\n- Don't mix instrumentation with feature work — separate PR.\n- If the SDK is already installed, don't reinstall — just add the missing wraps.\n\nBegin by reading package.json or pyproject.toml.`,\n};\n\nexport function getPrompt(req: GetPromptRequest): GetPromptResult {\n const name = req.params.name;\n const builder = QUESTIONS[name];\n if (!builder) throw new Error(`unknown prompt: ${name}`);\n const question = builder((req.params.arguments ?? {}) as Record<string, string>);\n return {\n description: prompts.find((p) => p.name === name)?.description ?? '',\n messages: [\n {\n role: 'user',\n content: { type: 'text', text: question },\n },\n ],\n };\n}\n","interface GrowthApiOptions {\n apiKey?: string | undefined;\n sessionToken?: string | undefined;\n host: string;\n projectId?: string | undefined;\n}\n\nexport class GrowthApi {\n constructor(private readonly options: GrowthApiOptions) {}\n\n // ─── read endpoints ───────────────────────────────────────────────────\n\n async getOverview(range: string): Promise<unknown> {\n return this.get(`/v1/overview?range=${encodeURIComponent(range)}`);\n }\n\n async getMetricSeries(metric: string, range: string): Promise<unknown> {\n return this.get(\n `/v1/metrics/series?metric=${encodeURIComponent(metric)}&range=${encodeURIComponent(range)}`,\n );\n }\n\n async getFunnel(name: string, range: string): Promise<unknown> {\n return this.get(`/v1/funnels/${encodeURIComponent(name)}?range=${encodeURIComponent(range)}`);\n }\n\n async listFunnels(): Promise<unknown> {\n return this.get('/v1/funnels');\n }\n\n async getRetention(range: string): Promise<unknown> {\n return this.get(`/v1/retention?range=${encodeURIComponent(range)}`);\n }\n\n async listAnomalies(severity?: string): Promise<unknown> {\n const qs = severity ? `?severity=${encodeURIComponent(severity)}` : '';\n return this.get(`/v1/anomalies${qs}`);\n }\n\n async listCommits(since: string): Promise<unknown> {\n return this.get(`/v1/commits?since=${encodeURIComponent(since)}`);\n }\n\n async getPeerBenchmarks(): Promise<unknown> {\n return this.get('/v1/benchmarks/peers');\n }\n\n async listHypotheses(): Promise<unknown> {\n return this.get('/v1/hypotheses');\n }\n\n // ─── agent endpoints ──────────────────────────────────────────────────\n\n async diagnose(metric: string, since: string): Promise<unknown> {\n return this.post('/v1/agent/diagnose', { metric, since });\n }\n\n async whyEvent(event: string): Promise<unknown> {\n return this.post('/v1/agent/why_event', { event });\n }\n\n /**\n * Talk to the AI growth engineer. Creates an ephemeral thread, posts the\n * question, returns the assistant reply with citations. Requires a\n * session token (cookie auth) — api-keys don't reach this endpoint.\n */\n async growthEngineerAsk(content: string): Promise<unknown> {\n const created = (await this.post('/v1/growth-engineer/threads', {})) as { id: string };\n return this.post(`/v1/growth-engineer/threads/${created.id}/messages`, { content });\n }\n\n // ─── transport ────────────────────────────────────────────────────────\n\n private async get(path: string): Promise<unknown> {\n return this.request('GET', path);\n }\n\n private async post(path: string, body: unknown): Promise<unknown> {\n return this.request('POST', path, body);\n }\n\n private async request(method: string, path: string, body?: unknown): Promise<unknown> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n };\n if (this.options.apiKey) headers['x-growth-api-key'] = this.options.apiKey;\n if (this.options.sessionToken) headers['cookie'] = `growth_session=${this.options.sessionToken}`;\n if (this.options.projectId) headers['x-growth-project'] = this.options.projectId;\n\n const res = await fetch(`${this.options.host}${path}`, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`growth-api ${res.status} ${path}: ${text}`);\n }\n return res.json();\n }\n}\n"],"mappings":"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","names":["api","api","text"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growth-loop/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "MCP server for growth-loop.dev — an AI growth engineer for vibe-coded SaaS. Exposes funnels, retention, peer benchmarks, and the growth engineer to Claude Desktop, Claude Code, and any MCP-compatible client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://growth-loop.dev",
|