get_content_history
Retrieve paginated history of content monitoring runs with completion timestamps to compare competitor sitemap snapshots over time. Use runId to access detailed run data for specific analyses.
Instructions
Get paginated history of Content Intelligence monitoring runs with completion timestamps. Use this to compare content snapshots over time — each run captures sitemap analysis for all competitors. Retrieve specific run data with get_content_run_detail using the runId from this response. Read-only. Returns paginated JSON array with pagination.hasMore flag.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | Yes | Project ID (from list_projects) | |
| page | No | Page number (1-indexed, default: 1) | |
| limit | No | Items per page (default: 20, max: 100) |
Implementation Reference
- src/tools.ts:136-145 (schema)Tool definition (schema) for 'get_content_history': defines name, description, parameters (projectId + pagination), API path (/v1/projects/{projectId}/content/history), and query params.
name: "get_content_history", description: "Get paginated history of Content Intelligence monitoring runs with completion timestamps. Use this to compare content snapshots over time — each run captures sitemap analysis for all competitors. Retrieve specific run data with get_content_run_detail using the runId from this response. Read-only. Returns paginated JSON array with pagination.hasMore flag.", parameters: z.object({ projectId: objectId("Project ID (from list_projects)"), ...pagination, }), path: (a) => `/v1/projects/${a.projectId}/content/history`, queryParams: ["page", "limit"], }, - src/index.ts:16-25 (registration)Generic tool registration loop in index.ts — iterates all tools from tools.ts (including get_content_history) and registers them with the MCP server using server.tool(). The handler is a generic async function that calls apiGet() with the tool's path and queryParams.
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/index.ts:17-24 (handler)Handler logic for get_content_history (and all tools): calls tool.path(args) to build the API URL, extracts queryParams from args, and invokes apiGet() to make the HTTP request.
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)apiGet helper function: constructs a URL to api.competlab.com, reads COMPETLAB_API_KEY from env, makes a fetch call with the CL-API-Key header, and returns the response as MCP 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, }; } }