Skip to main content
Glama

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
NameRequiredDescriptionDefault
idYesThe ID of the estimate to update (required)
client_idNoUpdate the client ID
subjectNoUpdate estimate subject
notesNoUpdate estimate notes
currencyNoUpdate currency code
issue_dateNoUpdate issue date
taxNoUpdate tax percentage
tax2NoUpdate second tax percentage
discountNoUpdate discount percentage
purchase_orderNoUpdate purchase order number

Implementation Reference

  • 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');
        }
  • 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),
    },
  • 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>;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ianaleck/harvest-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server