update_estimate
Modify existing estimates in Harvest by updating fields like client ID, subject, notes, taxes, discount, currency, issue date, and purchase order number.
Instructions
Update an existing estimate including subject, terms, taxes, and other details. Only provided fields will be updated.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | The ID of the estimate to update (required) | |
| client_id | No | Update the client ID | |
| subject | No | Update estimate subject | |
| notes | No | Update estimate notes | |
| currency | No | Update currency code | |
| issue_date | No | Update issue date | |
| tax | No | Update tax percentage | |
| tax2 | No | Update second tax percentage | |
| discount | No | Update discount percentage | |
| purchase_order | No | Update purchase order number |
Implementation Reference
- src/tools/estimates.ts:76-90 (handler)The UpdateEstimateHandler class handles the execution of the update_estimate tool by validating input arguments against UpdateEstimateSchema and calling the Harvest API.
class UpdateEstimateHandler implements ToolHandler { constructor(private readonly config: BaseToolConfig) {} async execute(args: Record<string, any>): Promise<CallToolResult> { try { const validatedArgs = validateInput(UpdateEstimateSchema, args, 'update estimate'); logger.info('Updating estimate via Harvest API', { estimateId: validatedArgs.id }); const estimate = await this.config.harvestClient.updateEstimate(validatedArgs); return { content: [{ type: 'text', text: JSON.stringify(estimate, null, 2) }], }; } catch (error) { return handleMCPToolError(error, 'update_estimate'); } - src/tools/estimates.ts:174-197 (registration)The update_estimate tool is registered with its description, input schema, and handler instance in src/tools/estimates.ts.
{ tool: { name: 'update_estimate', description: 'Update an existing estimate including subject, terms, taxes, and other details. Only provided fields will be updated.', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'The ID of the estimate to update (required)' }, client_id: { type: 'number', description: 'Update the client ID' }, subject: { type: 'string', description: 'Update estimate subject' }, notes: { type: 'string', description: 'Update estimate notes' }, currency: { type: 'string', minLength: 3, maxLength: 3, description: 'Update currency code' }, issue_date: { type: 'string', format: 'date', description: 'Update issue date' }, tax: { type: 'number', minimum: 0, maximum: 100, description: 'Update tax percentage' }, tax2: { type: 'number', minimum: 0, maximum: 100, description: 'Update second tax percentage' }, discount: { type: 'number', minimum: 0, maximum: 100, description: 'Update discount percentage' }, purchase_order: { type: 'string', description: 'Update purchase order number' }, }, required: ['id'], additionalProperties: false, }, }, handler: new UpdateEstimateHandler(config), }, - src/schemas/estimate.ts:91-162 (schema)The input schema for the update_estimate tool is defined here using Zod.
export const UpdateEstimateSchema = CreateEstimateSchema.partial().extend({ id: z.number().int().positive(), }); // Estimate line item input schemas export const CreateEstimateLineItemSchema = z.object({ kind: z.enum(['Service', 'Product']).optional().default('Service'), description: z.string(), quantity: z.number().min(0).optional().default(1), unit_price: z.number().min(0), taxed: z.boolean().optional().default(false), taxed2: z.boolean().optional().default(false), }); // Estimate item categories for services/products export const EstimateItemCategorySchema = z.object({ id: z.number().int().positive(), name: z.string().min(1), use_as_service: z.boolean(), use_as_expense: z.boolean(), created_at: z.string().datetime({ offset: true }), updated_at: z.string().datetime({ offset: true }), }); export const EstimateItemCategoriesListSchema = z.object({ estimate_item_categories: z.array(EstimateItemCategorySchema), per_page: z.number().int().positive(), total_pages: z.number().int().min(0), total_entries: z.number().int().min(0), next_page: z.number().int().positive().nullable(), previous_page: z.number().int().positive().nullable(), page: z.number().int().positive(), links: z.object({ first: z.string().url(), next: z.string().url().nullable(), previous: z.string().url().nullable(), last: z.string().url(), }), }); // Query parameters for listing estimates export const EstimateQuerySchema = z.object({ client_id: z.number().int().positive().optional(), state: z.enum(['draft', 'sent', 'accepted', 'declined']).optional(), from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(), to: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(), updated_since: z.string().datetime({ offset: true }).optional(), page: z.number().int().positive().optional(), per_page: z.number().int().min(1).max(2000).optional().default(2000), }); // Estimate action schemas (send, accept, decline) export const SendEstimateSchema = z.object({ id: z.number().int().positive(), event_type: z.literal('send'), }); export const AcceptEstimateSchema = z.object({ id: z.number().int().positive(), event_type: z.literal('accept'), }); export const DeclineEstimateSchema = z.object({ id: z.number().int().positive(), event_type: z.literal('decline'), }); // Type exports for use in other files export type Estimate = z.infer<typeof EstimateSchema>; export type EstimatesList = z.infer<typeof EstimatesListSchema>; export type CreateEstimateInput = z.infer<typeof CreateEstimateSchema>; export type UpdateEstimateInput = z.infer<typeof UpdateEstimateSchema>;