get_action_plan
Retrieve a consolidated competitive action plan with insights and recommended actions across all 5 monitoring dimensions. Evidence-backed intelligence with rationale for strategic overview before drilling into specifics.
Instructions
Get the AI-generated competitive action plan aggregated across all 5 monitoring dimensions. Returns insights (with evidence and related competitors) and recommended actions (with rationale), plus per-dimension analysis freshness timestamps. This is the highest-level intelligence output — start here for a strategic overview before drilling into specific dimensions. Read-only. Returns JSON object.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | Yes | Project ID (from list_projects) |
Implementation Reference
- src/tools.ts:319-326 (handler)Definition of the 'get_action_plan' tool in the tools array. It specifies the tool name, description, Zod schema (requiring a projectId), and the API path (/v1/projects/{projectId}/analysis/action-plan). The actual handler is a generic loop in src/index.ts that calls apiGet with this path.
name: "get_action_plan", description: "Get the AI-generated competitive action plan aggregated across all 5 monitoring dimensions. Returns insights (with evidence and related competitors) and recommended actions (with rationale), plus per-dimension analysis freshness timestamps. This is the highest-level intelligence output — start here for a strategic overview before drilling into specific dimensions. Read-only. Returns JSON object.", parameters: z.object({ projectId: objectId("Project ID (from list_projects)"), }), path: (a) => `/v1/projects/${a.projectId}/analysis/action-plan`, }, - src/tools.ts:322-324 (schema)Zod input schema for get_action_plan: requires a 'projectId' parameter that must be a 24-character hex string (validated by the objectId helper).
parameters: z.object({ projectId: objectId("Project ID (from list_projects)"), }), - src/index.ts:16-25 (registration)Generic registration loop: iterates over the tools array and registers each tool with the MCP server using server.tool(). For get_action_plan, the handler constructs the path, extracts query params, and calls apiGet() to make the HTTP GET request.
for (const tool of tools) { server.tool(tool.name, tool.description, tool.parameters.shape, async (args: Record<string, any>) => { const path = tool.path(args); const query: Record<string, any> = {}; for (const key of tool.queryParams ?? []) { if (args[key] !== undefined) query[key] = args[key]; } return apiGet(path, Object.keys(query).length ? query : undefined); }); } - src/api-client.ts:3-58 (helper)The apiGet helper used by the tool handler. It reads the COMPETLAB_API_KEY environment variable, makes a GET request to the CompetLab API with the constructed path and optional query params, and returns the response content.
export async function apiGet( path: string, query?: Record<string, string | number>, ): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: true }> { const apiKey = process.env.COMPETLAB_API_KEY; if (!apiKey) { return { content: [ { type: "text", text: JSON.stringify({ error: "api_key_missing", message: "COMPETLAB_API_KEY environment variable is not set", }), }, ], isError: true, }; } const url = new URL(`${API_BASE}${path}`); if (query) { for (const [k, v] of Object.entries(query)) { if (v !== undefined) url.searchParams.set(k, String(v)); } } try { const res = await fetch(url, { headers: { "CL-API-Key": apiKey }, }); const body = await res.text(); if (!res.ok) { return { content: [{ type: "text", text: body }], isError: true }; } return { content: [{ type: "text", text: body }] }; } catch (err) { return { content: [ { type: "text", text: JSON.stringify({ error: "api_unreachable", message: err instanceof Error ? err.message : "Failed to reach CompetLab API", status: 503, }), }, ], isError: true, }; } } - src/index.ts:29-55 (registration)A prompt template 'competitive_overview' that references 'get_action_plan' as step 1 of a workflow for getting a comprehensive competitive briefing.
server.prompt( "competitive_overview", "Get a full competitive briefing for a project — action plan, alerts, and all 5 dimension dashboards in one go.", { projectId: z.string().describe("Project ID (from list_projects)") }, async ({ projectId }) => ({ messages: [ { role: "user" as const, content: { type: "text" as const, text: [ `Give me a comprehensive competitive briefing for project ${projectId}.`, "", "Follow this workflow:", "1. Call get_action_plan to get the strategic overview with insights and recommended actions.", "2. Call list_alerts (limit 10, severity critical or high) to surface the most important recent changes.", "3. Call each dashboard tool for the full picture:", " - get_tech_trust_dashboard (security, trust signals, tech stack)", " - get_content_dashboard (sitemap, content gaps)", " - get_positioning_dashboard (homepage messaging, CTAs)", " - get_pricing_dashboard (plans, market stats)", " - get_ai_visibility_dashboard (LLM brand rankings)", "", "Synthesize everything into a concise executive briefing with:", "- Top 3 competitive threats", "- Top 3 opportunities", "- Recommended immediate actions",