cleanup
Delete expired receipts using TTL metadata to reclaim storage and enforce data retention policies. Preview deletions with dry_run mode before permanent removal.
Instructions
Delete receipts that have passed their expiration time based on the expires_at field in metadata. Expired receipts are receipts where metadata.expires_at is set and is earlier than the current time. Supports dry_run mode to preview deletions without committing. Returns count of deleted receipts and remaining total. Use periodically to manage storage and enforce TTL policies set during receipt creation.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| dry_run | No | If true, returns what would be deleted without actually deleting. Defaults to false. |
Implementation Reference
- The MCP tool handler for 'cleanup'. Implements the tool logic including dry_run mode (preview deletions without committing) and actual cleanup execution. Validates input with Zod schema, filters expired receipts by checking metadata.expires_at, and returns JSON results with counts and details.
import { z } from 'zod' import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import type { ReceiptEngine } from '../engine/receipt-engine.js' export function registerCleanup(server: McpServer, engine: ReceiptEngine): void { server.tool( 'cleanup', 'Delete receipts that have passed their expiration time based on the expires_at field in metadata. Expired receipts are receipts where metadata.expires_at is set and is earlier than the current time. Supports dry_run mode to preview deletions without committing. Returns count of deleted receipts and remaining total. Use periodically to manage storage and enforce TTL policies set during receipt creation.', { dry_run: z.boolean().default(false).describe('If true, returns what would be deleted without actually deleting. Defaults to false.'), }, async (params) => { if (params.dry_run) { const now = new Date().toISOString() const all = await engine.list(undefined, 1, 100000) const expired = all.data.filter(r => { const expiresAt = (r.metadata as Record<string, unknown>)?.expires_at as string | undefined return expiresAt && expiresAt < now }) return { content: [{ type: 'text' as const, text: JSON.stringify({ dry_run: true, would_delete: expired.length, total: all.data.length, expired_receipts: expired.map(r => ({ receipt_id: r.receipt_id, action: r.action, expires_at: (r.metadata as Record<string, unknown>)?.expires_at, })), }, null, 2), }], } } const result = await engine.cleanup() return { content: [{ type: 'text' as const, text: JSON.stringify({ deleted: result.deleted, remaining: result.remaining, }, null, 2), }], } }, ) } - Zod schema definition for the cleanup tool's input parameters. Defines 'dry_run' as an optional boolean parameter that defaults to false, used to preview deletions without actually deleting receipts.
{ dry_run: z.boolean().default(false).describe('If true, returns what would be deleted without actually deleting. Defaults to false.'), }, - packages/mcp-server/src/tools/index.ts:30-30 (registration)Registration point where the cleanup tool is registered to the MCP server. Called within registerAllTools() alongside other tool registrations.
registerCleanup(server, engine) - Engine helper method that delegates cleanup operations to the underlying storage store. Wraps the store's cleanup result to return deleted count and remaining count.
async cleanup(): Promise<{ deleted: number; remaining: number }> { const result = await this.store.cleanup() return { deleted: result.deleted, remaining: result.total - result.deleted } } - Core storage layer cleanup implementation. Iterates through all receipts, identifies expired ones by checking metadata.expires_at against current time, deletes them, and returns deletion statistics.
async cleanup(): Promise<{ deleted: number; total: number }> { const now = new Date().toISOString() const allReceipts = await this.list(undefined, 1, 100000) let deleted = 0 for (const receipt of allReceipts.data) { const expiresAt = (receipt.metadata as Record<string, unknown>)?.expires_at as string | undefined if (expiresAt && expiresAt < now) { await this.delete(receipt.receipt_id) deleted++ } } return { deleted, total: allReceipts.data.length } }