get_conversions
Retrieve conversion statistics for a QR code, including total conversions, total value, event breakdowns, daily trends, and recent events, to measure ROI and identify which codes drive the most value.
Instructions
Get conversion statistics for a QR code. Returns total conversions, total value, breakdowns by event name, daily trends, and recent events. Use this to measure QR code ROI and understand which codes drive the most value.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| short_id | Yes | The short_id of the QR code to get conversion stats for. | |
| period | No | Time period for aggregations. Default: 30d. | 30d |
| event | No | Filter by event name (e.g., 'purchase'). |
Implementation Reference
- packages/mcp/src/tools.ts:883-888 (handler)MCP handler for get_conversions tool - calls the HTTP API endpoint /api/conversions/:short_id with query params.
handler: async (input: { short_id: string; period: string; event?: string }) => { const query: Record<string, string> = { period: input.period }; if (input.event) query.event = input.event; return apiRequest(`/api/conversions/${input.short_id}`, { query }); }, }, - packages/mcp/src/tools.ts:872-882 (schema)Zod input schema for get_conversions tool: requires short_id, optional period (7d/30d/90d/all, default 30d), optional event filter.
inputSchema: z.object({ short_id: z.string().describe("The short_id of the QR code to get conversion stats for."), period: z .enum(["7d", "30d", "90d", "all"]) .default("30d") .describe("Time period for aggregations. Default: 30d."), event: z .string() .optional() .describe("Filter by event name (e.g., 'purchase')."), }), - packages/mcp/src/server.ts:21-54 (registration)Registration loop that iterates over all tools (including get_conversions) and registers them with the McpServer via server.tool().
for (const [name, tool] of Object.entries(tools)) { server.tool( name, tool.description, tool.inputSchema.shape, async (input: Record<string, unknown>) => { try { const result = await tool.handler(input as any); return { content: [ { type: "text" as const, text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const message = error instanceof Error ? error.message : String(error); return { content: [ { type: "text" as const, text: JSON.stringify({ error: message, hint: "Check the input parameters and try again. Use list_qr_codes to verify available QR codes.", }), }, ], isError: true, }; } } ); } - Backend service function getConversions() that queries the database for conversion stats: total count, total value, breakdown by event, daily trends, and recent events.
export function getConversions( qrCodeId: number, shortId: string, period: string = "30d", eventFilter?: string ) { const periodStart = getPeriodStart(period); // Build conditions const conditions = [eq(conversionEvents.qrCodeId, qrCodeId)]; if (periodStart) conditions.push(gt(conversionEvents.createdAt, periodStart)); if (eventFilter) conditions.push(eq(conversionEvents.eventName, eventFilter)); const whereClause = conditions.length === 1 ? conditions[0] : and(...conditions); // Total conversions const [{ total }] = db .select({ total: count() }) .from(conversionEvents) .where(whereClause) .all(); // Total value const [{ totalValue }] = db .select({ totalValue: sql<string | null>`SUM(CAST(${conversionEvents.value} AS REAL))` }) .from(conversionEvents) .where(whereClause) .all(); // By event const byEvent = db.all<{ event_name: string; count: number; total_value: string | null }>( sql`SELECT ${conversionEvents.eventName} as event_name, COUNT(*) as count, SUM(CAST(${conversionEvents.value} AS REAL)) as total_value FROM conversion_events WHERE ${whereClause} GROUP BY ${conversionEvents.eventName} ORDER BY count DESC` ); // By day const byDay = db.all<{ date: string; count: number; total_value: string | null }>( sql`SELECT date(${conversionEvents.createdAt}) as date, COUNT(*) as count, SUM(CAST(${conversionEvents.value} AS REAL)) as total_value FROM conversion_events WHERE ${whereClause} GROUP BY date(${conversionEvents.createdAt}) ORDER BY date ASC` ); // Recent events (last 20) const recent = db .select() .from(conversionEvents) .where(whereClause) .orderBy(sql`${conversionEvents.createdAt} DESC`) .limit(20) .all(); return { short_id: shortId, total_conversions: total, total_value: totalValue ? parseFloat(totalValue) : 0, period, by_event: byEvent.map((e) => ({ event_name: e.event_name, count: e.count, total_value: e.total_value ? parseFloat(e.total_value) : 0, })), by_day: byDay.map((d) => ({ date: d.date, count: d.count, total_value: d.total_value ? parseFloat(d.total_value) : 0, })), recent_events: recent.map((r) => ({ event_name: r.eventName, value: r.value ? parseFloat(r.value) : null, metadata: r.metadata ? JSON.parse(r.metadata) : null, created_at: r.createdAt, })), }; } - Fastify JSON schema for the conversion stats API endpoint (GET /:shortId) with shortId param, period enum, and optional event filter.
export const conversionStatsSchema = { params: { type: "object" as const, required: ["shortId"], properties: { shortId: { type: "string", description: "The short_id of the QR code to get conversion stats for.", }, }, }, querystring: { type: "object" as const, properties: { period: { type: "string", enum: ["7d", "30d", "90d", "all"], default: "30d", description: "Time period for aggregations. Default: 30d.", }, event: { type: "string", description: "Filter by event name (e.g., 'purchase').", }, }, }, };