idea.expand
Generate keyword scaffolding for any product idea, including core keywords, adjacent niches, pain points, competitor names, and exclusion terms. No search execution required. Use to plan manual outreach copy.
Instructions
Generate the keyword scaffolding (core keywords, adjacent niches, pain points, competitor names, exclusion terms) for a product idea, without running searches. Behavior: hits the same theme-expansion endpoint leads.find calls internally as its first step. Consumes one credit. Stateless; nothing persists. Usage: call this when the user wants to see the search scaffolding before committing to a full run, or when planning manual outreach copy and you want the buyer-language vocabulary. Do NOT use this as a precursor to leads.find in the same session, leads.find runs theme expansion itself; calling both is double-billing. Returns: { core_keywords, adjacent_niches, pain_points, competitor_names, exclusion_terms } as string arrays.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| idea | Yes | The app idea to expand into search themes |
Implementation Reference
- src/index.ts:467-507 (handler)The handler function for the 'idea.expand' tool. Makes POST to 'theme-expansion' endpoint, formats the returned ThemeExpansion fields into a text response.
server.tool( "idea.expand", "Generate the keyword scaffolding (core keywords, adjacent niches, pain points, competitor names, exclusion terms) for a product idea, without running searches. Behavior: hits the same theme-expansion endpoint leads.find calls internally as its first step. Consumes one credit. Stateless; nothing persists. Usage: call this when the user wants to see the search scaffolding before committing to a full run, or when planning manual outreach copy and you want the buyer-language vocabulary. Do NOT use this as a precursor to leads.find in the same session, leads.find runs theme expansion itself; calling both is double-billing. Returns: { core_keywords, adjacent_niches, pain_points, competitor_names, exclusion_terms } as string arrays.", { idea: z.string().describe("The app idea to expand into search themes"), }, { title: "Expand themes", readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true, }, async ({ idea }) => { const err = requireKey(); if (err) return err; const { expansion } = await call<{ expansion: ThemeExpansion }>( "POST", "theme-expansion", { idea } ); const sections = [ `Keywords: ${expansion.core_keywords.join(", ")}`, `Niches: ${expansion.adjacent_niches.join(", ")}`, `Pain points: ${expansion.pain_points.join(", ")}`, `Competitors: ${expansion.competitor_names.join(", ")}`, `Exclusions: ${expansion.exclusion_terms.join(", ")}`, ].join("\n"); return { content: [ { type: "text" as const, text: `Theme expansion for "${idea}":\n\n${sections}\n\nUse these to build queries for leads.search.`, }, ], }; } ); - src/index.ts:150-156 (schema)The ThemeExpansion interface defining the shape of the response: core_keywords, adjacent_niches, pain_points, competitor_names, exclusion_terms (all string arrays).
interface ThemeExpansion { core_keywords: string[]; adjacent_niches: string[]; pain_points: string[]; competitor_names: string[]; exclusion_terms: string[]; } - src/index.ts:467-507 (registration)Registration of the tool with name 'idea.expand' via server.tool(), including its description and input schema.
server.tool( "idea.expand", "Generate the keyword scaffolding (core keywords, adjacent niches, pain points, competitor names, exclusion terms) for a product idea, without running searches. Behavior: hits the same theme-expansion endpoint leads.find calls internally as its first step. Consumes one credit. Stateless; nothing persists. Usage: call this when the user wants to see the search scaffolding before committing to a full run, or when planning manual outreach copy and you want the buyer-language vocabulary. Do NOT use this as a precursor to leads.find in the same session, leads.find runs theme expansion itself; calling both is double-billing. Returns: { core_keywords, adjacent_niches, pain_points, competitor_names, exclusion_terms } as string arrays.", { idea: z.string().describe("The app idea to expand into search themes"), }, { title: "Expand themes", readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true, }, async ({ idea }) => { const err = requireKey(); if (err) return err; const { expansion } = await call<{ expansion: ThemeExpansion }>( "POST", "theme-expansion", { idea } ); const sections = [ `Keywords: ${expansion.core_keywords.join(", ")}`, `Niches: ${expansion.adjacent_niches.join(", ")}`, `Pain points: ${expansion.pain_points.join(", ")}`, `Competitors: ${expansion.competitor_names.join(", ")}`, `Exclusions: ${expansion.exclusion_terms.join(", ")}`, ].join("\n"); return { content: [ { type: "text" as const, text: `Theme expansion for "${idea}":\n\n${sections}\n\nUse these to build queries for leads.search.`, }, ], }; } ); - src/index.ts:240-252 (helper)The requireKey helper used by the handler to check for a valid GORILLA_API_KEY before proceeding.
function requireKey() { if (!GORILLA_API_KEY) { return { content: [ { type: "text" as const, text: "GORILLA_API_KEY is not set. Sign up at https://usegorilla.app, then create a key at gorilla.opusforge.com.br > Menu > API Keys and set GORILLA_API_KEY in your environment.", }, ], }; } return null; } - src/index.ts:91-113 (helper)The call<T> HTTP helper used by the handler to POST to the 'theme-expansion' API endpoint.
async function call<T>( method: "GET" | "POST" | "DELETE", endpoint: string, body?: unknown ): Promise<T> { const cfg = await getConfig(); const res = await fetch(`${cfg.api_base}/${endpoint}`, { method, headers: { "Content-Type": "application/json", "x-api-key": GORILLA_API_KEY, apikey: cfg.gateway_key, }, ...(body !== undefined ? { body: JSON.stringify(body) } : {}), }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`${method} /${endpoint} failed (${res.status}): ${text}`); } return res.json() as Promise<T>; }