get_content_run_detail
Retrieve full competitor content intelligence data for a past run to analyze content strategy changes over time. Requires projectId and runId. Read-only JSON output.
Instructions
Get full competitor-by-competitor Content Intelligence data for a specific historical run. Returns the same data structure as get_content_dashboard but for a past point in time. Use this to investigate content strategy changes between runs. Requires runId from get_content_history. Read-only. Returns JSON object.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | Yes | Project ID (from list_projects) | |
| runId | Yes | Run ID (from get_content_history) |
Implementation Reference
- src/index.ts:16-25 (registration)The tool 'get_content_run_detail' is registered via the generic loop over all ToolDef objects. Each tool is registered with server.tool() using its name, description, parameters, and a handler that calls apiGet with the path and query params.
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/tools.ts:146-155 (schema)Schema definition for 'get_content_run_detail': defines name, description, Zod schema parameters (projectId and runId as 24-char hex strings), and API path construction.
{ name: "get_content_run_detail", description: "Get full competitor-by-competitor Content Intelligence data for a specific historical run. Returns the same data structure as get_content_dashboard but for a past point in time. Use this to investigate content strategy changes between runs. Requires runId from get_content_history. Read-only. Returns JSON object.", parameters: z.object({ projectId: objectId("Project ID (from list_projects)"), runId: objectId("Run ID (from get_content_history)"), }), path: (a) => `/v1/projects/${a.projectId}/content/history/${a.runId}`, }, - src/index.ts:17-24 (handler)The handler for 'get_content_run_detail' (shared by all tools) constructs the API path from the tool definition, extracts query parameters, and calls apiGet to fetch data from the CompetLab API.
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/tools.ts:3-7 (helper)Helper function that creates a Zod schema for validating 24-character hex ObjectId strings.
const objectId = (desc: string) => z .string() .regex(/^[a-f\d]{24}$/i, "Invalid ID format — must be a 24-character hex string") .describe(desc); - src/api-client.ts:1-58 (helper)Helper function apiGet that makes authenticated GET requests to the CompetLab API, handling API key checks, query params, error responses, and network errors.
const API_BASE = "https://api.competlab.com"; 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, }; } }