UNPKG

@clicktime/mcp-server

Version:

ClickTime MCP Tech Demo for AI agents to interact with ClickTime API

381 lines (373 loc) 16.5 kB
// src/expense/expense-tools.ts import { SUPPORTED_RECEIPT_MIME_TYPES } from './expense-types.js'; export function createExpenseTools(client) { return [ { name: 'create_expense_sheet', description: 'Create a new expense sheet (expense report) for a given period', inputSchema: { type: 'object', properties: { title: { type: 'string', maxLength: 100, description: 'Human-readable title, e.g. "June 2025 – Trade-show expenses"', }, expenseSheetDate: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$', description: 'Report date in YYYY-MM-DD format (usually today)', }, description: { type: 'string', maxLength: 2000, description: 'Optional longer description / purpose', }, enableForeignCurrency: { type: 'boolean', default: false, description: 'Enable foreign-currency items on this sheet', }, trackingId: { type: 'string', maxLength: 50, description: 'Optional internal reference / tracking code', }, }, required: ['title', 'expenseSheetDate'], }, }, { name: 'add_expense_item', description: 'Add an expense item (creates expense sheet if needed)', inputSchema: { type: 'object', properties: { date: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$', description: 'Expense date in YYYY-MM-DD format', }, amount: { type: 'number', minimum: 0, description: 'Expense amount', }, description: { type: 'string', maxLength: 50, description: 'Brief description of the expense', }, comment: { type: 'string', maxLength: 2000, description: 'Detailed comment about the expense', }, expenseTypeId: { type: 'string', description: 'Expense type ID (optional - uses default if not provided)', }, paymentTypeId: { type: 'string', description: 'Payment type ID (optional - uses default if not provided)', }, jobId: { type: 'string', description: 'Job ID for billable expenses', }, taskId: { type: 'string', description: 'Task ID (requires jobId)', }, billable: { type: 'boolean', default: false, description: 'Whether this expense is billable to a client', }, }, required: ['date', 'amount', 'expenseTypeId', 'paymentTypeId'], }, }, { name: 'add_expense_from_receipt', description: `Add expense from receipt image with intelligent analysis. COMPLETE WORKFLOW (execute in this exact order): 1. FIRST: Call list_my_expense_types and list_my_payment_types to get available options 2. READ and ANALYZE the receipt image to extract amount, merchant, date, items, payment method 3. GENERATE professional business description based on receipt content (max 50 chars) 4. INTELLIGENTLY select appropriate expense type (e.g., "Meals" for restaurants, "Travel" for gas) 5. INTELLIGENTLY select appropriate payment type based on receipt payment method 6. CALL this tool with all extracted and processed data The tool expects you to have completed the full analysis workflow before calling. RECEIPT REQUIREMENTS: - Maximum file size: 2MB (ClickTime API limit) - Supported formats: JPG, PNG, GIF, BMP, PDF only`, inputSchema: { type: 'object', properties: { receiptPath: { type: 'string', description: 'Full path to receipt image file (e.g., /Users/john/Downloads/receipt.jpg) - REQUIRED for receipt attachment. Must be under 2MB and in JPG/PNG/GIF/BMP/PDF format.', }, receiptResourceUri: { type: 'string', pattern: '^file://', description: 'Resource URI for receipt file (e.g., file:///Users/john/Downloads/receipt.jpg) - Alternative to receiptPath. Must be under 2MB.', }, receiptBase64: { type: 'string', description: 'Base64 encoded receipt image (alternative to file methods, but rarely available from chat uploads). Must result in under 2MB file size.', }, receiptMimeType: { type: 'string', enum: [...SUPPORTED_RECEIPT_MIME_TYPES], description: 'MIME type of base64 receipt (required with receiptBase64). Must be ClickTime-supported format.', }, amount: { type: 'number', minimum: 0, description: 'Expense amount (EXTRACT from receipt image analysis)', }, description: { type: 'string', maxLength: 50, description: 'Professional business description (GENERATE from receipt analysis - be specific and business-appropriate, not just merchant name)', }, date: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$', description: 'Expense date in YYYY-MM-DD format (EXTRACT from receipt, fallback to today)', }, expenseTypeId: { type: 'string', description: 'Expense type ID (SELECT from list_my_expense_types based on receipt content and business context)', }, paymentTypeId: { type: 'string', description: 'Payment type ID (SELECT from list_my_payment_types based on payment method shown on receipt)', }, comment: { type: 'string', maxLength: 2000, description: 'Detailed comment about the expense (OPTIONAL - can include extracted line items or additional context)', }, jobId: { type: 'string', description: 'Job ID for billable expenses', }, taskId: { type: 'string', description: 'Task ID (requires jobId)', }, billable: { type: 'boolean', default: false, description: 'Whether this expense is billable to a client', }, }, anyOf: [ { required: [ 'receiptPath', 'amount', 'description', 'expenseTypeId', 'paymentTypeId', ], }, { required: [ 'receiptResourceUri', 'amount', 'description', 'expenseTypeId', 'paymentTypeId', ], }, { required: [ 'receiptBase64', 'receiptMimeType', 'amount', 'description', 'expenseTypeId', 'paymentTypeId', ], }, ], }, }, { name: 'analyze_receipt_image', description: `Analyze an uploaded receipt image to extract expense information and map to user's account types. USE THIS TOOL WHEN: - User uploads/shares a receipt image in chat - User says "analyze this receipt" or similar - User wants to create an expense but hasn't provided a file path WORKFLOW: 1. FIRST: Call list_my_expense_types and list_my_payment_types to get user's available options 2. Analyze the uploaded receipt image to extract all information 3. INTELLIGENTLY select the best matching expense type ID from the user's available types 4. INTELLIGENTLY select the best matching payment type ID from the user's available types 5. Call this tool with extracted data + selected IDs 6. Tool will ask user for file path and then create the expense You handle all the intelligent mapping based on receipt content and available account types. NOTE: Final expense creation requires file path (2MB max, JPG/PNG/GIF/BMP/PDF only).`, inputSchema: { type: 'object', properties: { amount: { type: 'number', minimum: 0, description: 'Total amount from receipt (EXTRACT from image analysis)', }, merchantName: { type: 'string', description: 'Business/merchant name (EXTRACT from receipt header)', }, receiptDate: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$', description: 'Date from receipt in YYYY-MM-DD format (EXTRACT from receipt, convert if needed)', }, description: { type: 'string', maxLength: 50, description: 'Professional business description (GENERATE based on merchant and context)', }, expenseTypeId: { type: 'string', description: 'Selected expense type ID (SELECT best match from list_my_expense_types based on receipt content)', }, paymentTypeId: { type: 'string', description: 'Selected payment type ID (SELECT best match from list_my_payment_types based on payment method on receipt)', }, lineItems: { type: 'array', items: { type: 'object', properties: { description: { type: 'string' }, amount: { type: 'number' }, }, }, description: 'Individual line items from receipt (OPTIONAL - extract if clearly visible)', }, extractedText: { type: 'string', description: 'Key text extracted from receipt for reference (OPTIONAL)', }, }, required: [ 'amount', 'merchantName', 'receiptDate', 'description', 'expenseTypeId', 'paymentTypeId', ], }, }, { name: 'get_receipt_access_help', description: 'Get instructions on how to properly access receipt images for expense creation, including ClickTime file size and format requirements', inputSchema: { type: 'object', properties: {}, }, }, { name: 'list_my_expense_types', description: 'List all available expense types with IDs for intelligent expense categorization', inputSchema: { type: 'object', properties: { activeOnly: { type: 'boolean', default: true, description: 'Only show active expense types', }, }, }, }, { name: 'list_my_payment_types', description: 'List all available payment types with IDs for intelligent payment method mapping', inputSchema: { type: 'object', properties: { activeOnly: { type: 'boolean', default: true, description: 'Only show active payment types', }, }, }, }, { name: 'get_my_expense_sheets', description: 'Get my expense sheets with optional filtering', inputSchema: { type: 'object', properties: { status: { type: 'array', items: { type: 'string', enum: ['open', 'approved', 'rejected', 'waiting', 'paid'], }, description: 'Filter by expense sheet status', }, fromDate: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$', description: 'Filter from this date (YYYY-MM-DD)', }, toDate: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$', description: 'Filter to this date (YYYY-MM-DD)', }, limit: { type: 'number', minimum: 1, maximum: 500, default: 50, description: 'Maximum number of sheets to return', }, }, }, }, { name: 'get_my_expense_items', description: 'Get my expense items with optional filtering', inputSchema: { type: 'object', properties: { expenseSheetId: { type: 'string', description: 'Filter by expense sheet ID', }, fromDate: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$', description: 'Filter from this date (YYYY-MM-DD)', }, toDate: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$', description: 'Filter to this date (YYYY-MM-DD)', }, limit: { type: 'number', minimum: 1, maximum: 500, default: 50, description: 'Maximum number of items to return', }, }, }, }, ]; }