track_changes
Monitor pricing changes, expirations, and new deals for developer tools. Get weekly digests or filter by vendor, change type, or date range to track cost-impacting updates.
Instructions
Track developer tool pricing changes, upcoming expirations, and new deals. With no params, returns a weekly digest. Filter by vendor, change type, or date range.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| since | No | ISO date (YYYY-MM-DD). Default: 7 days ago. | |
| change_type | No | Filter by type of change | |
| vendor | No | Filter to one vendor (case-insensitive) | |
| vendors | No | Comma-separated vendor names to filter (e.g. 'Vercel,Supabase') | |
| include_expiring | No | Include upcoming expirations (default: true) | |
| lookahead_days | No | Days to look ahead for expirations (default: 30) |
Implementation Reference
- src/server.ts:257-270 (registration)Registration of the 'track_changes' tool in src/server.ts, including its description and input schema.
server.registerTool( "track_changes", { description: "Track developer tool pricing changes, upcoming expirations, and new deals. With no params, returns a weekly digest. Filter by vendor, change type, or date range.", inputSchema: { since: z.string().optional().describe("ISO date (YYYY-MM-DD). Default: 7 days ago."), change_type: z.enum(["free_tier_removed", "limits_reduced", "limits_increased", "new_free_tier", "pricing_restructured", "open_source_killed", "pricing_model_change", "startup_program_expanded", "pricing_postponed", "product_deprecated"]).optional().describe("Filter by type of change"), vendor: z.string().optional().describe("Filter to one vendor (case-insensitive)"), vendors: z.string().optional().describe("Comma-separated vendor names to filter (e.g. 'Vercel,Supabase')"), include_expiring: z.boolean().optional().describe("Include upcoming expirations (default: true)"), lookahead_days: z.number().optional().describe("Days to look ahead for expirations (default: 30)"), }, }, - src/server.ts:271-306 (handler)Handler implementation for 'track_changes' in src/server.ts, which processes requests for pricing changes or weekly digests.
async ({ since, change_type, vendor, vendors, include_expiring, lookahead_days }) => { try { recordToolCall("track_changes"); // No params = weekly digest if (!since && !change_type && !vendor && !vendors && include_expiring === undefined) { const digest = getWeeklyDigest(); logRequest({ ts: new Date().toISOString(), type: "mcp", endpoint: "track_changes", params: {}, result_count: digest.deal_changes.length, session_id: getSessionId?.() }); return { content: [{ type: "text" as const, text: JSON.stringify(digest, null, 2) }], }; } // Filtered changes const changes = getDealChanges(since, change_type, vendor, vendors); const doExpiring = include_expiring !== false; const days = Math.min(Math.max(lookahead_days ?? 30, 1), 365); let result: any = changes; if (doExpiring) { const expiring = getExpiringDeals(days); result = { ...changes, expiring_deals: expiring }; } logRequest({ ts: new Date().toISOString(), type: "mcp", endpoint: "track_changes", params: { since, change_type, vendor, vendors, include_expiring: doExpiring, lookahead_days: days }, result_count: changes.changes.length, session_id: getSessionId?.() }); return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], }; } catch (err) { console.error("track_changes error:", err); return { isError: true, content: [{ type: "text" as const, text: `Error: ${err instanceof Error ? err.message : String(err)}` }], }; } }