update_project
Update an existing project by modifying its settings like name, billing configuration, budget, and timeline. Only specified fields are changed.
Instructions
Update an existing project. Can modify any project settings including name, billing configuration, budget settings, and project timeline. Only provided fields will be updated.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | The ID of the project to update (required) | |
| name | No | Update project name | |
| code | No | Update project code | |
| is_active | No | Update active status | |
| is_billable | No | Update billable status | |
| is_fixed_fee | No | Update fixed fee billing | |
| bill_by | No | Update billing method | |
| hourly_rate | No | Update hourly rate | |
| budget | No | Update budget amount | |
| budget_by | No | Update budget calculation method | |
| budget_is_monthly | No | Update monthly budget reset | |
| notify_when_over_budget | No | Update budget notifications | |
| over_budget_notification_percentage | No | Update notification threshold | |
| show_budget_to_all | No | Update budget visibility | |
| cost_budget | No | Update cost budget | |
| cost_budget_include_expenses | No | Update expense inclusion | |
| fee | No | Update fixed fee | |
| notes | No | Update project notes | |
| starts_on | No | Update start date (YYYY-MM-DD) | |
| ends_on | No | Update end date (YYYY-MM-DD) |
Implementation Reference
- src/tools/projects.ts:81-96 (handler)The UpdateProjectHandler class implements the 'update_project' tool logic. It validates input using UpdateProjectSchema, calls the Harvest API's updateProject method, and returns the result. Errors are caught and handled via handleMCPToolError with the tool name 'update_project'.
class UpdateProjectHandler implements ToolHandler { constructor(private readonly config: BaseToolConfig) {} async execute(args: Record<string, any>): Promise<CallToolResult> { try { const validatedArgs = validateInput(UpdateProjectSchema, args, 'update project'); logger.info('Updating project via Harvest API', { projectId: validatedArgs.id }); const project = await this.config.harvestClient.updateProject(validatedArgs); return { content: [{ type: 'text', text: JSON.stringify(project, null, 2) }], }; } catch (error) { return handleMCPToolError(error, 'update_project'); } } - src/schemas/project.ts:84-86 (schema)The UpdateProjectSchema is defined by taking CreateProjectSchema (all fields optional via .partial()) and extending it with a required 'id' field (positive integer). This defines the full set of validated input fields for updating a project.
export const UpdateProjectSchema = CreateProjectSchema.partial().extend({ id: z.number().int().positive(), }); - src/tools/projects.ts:273-306 (registration)The tool 'update_project' is registered in the registerProjectTools function. It specifies the name 'update_project', a description, an inputSchema (JSON Schema) listing all updatable project fields with 'id' as required, and maps to new UpdateProjectHandler(config) as the handler.
{ tool: { name: 'update_project', description: 'Update an existing project. Can modify any project settings including name, billing configuration, budget settings, and project timeline. Only provided fields will be updated.', inputSchema: { type: 'object', properties: { id: { type: 'number', description: 'The ID of the project to update (required)' }, name: { type: 'string', minLength: 1, description: 'Update project name' }, code: { type: 'string', description: 'Update project code' }, is_active: { type: 'boolean', description: 'Update active status' }, is_billable: { type: 'boolean', description: 'Update billable status' }, is_fixed_fee: { type: 'boolean', description: 'Update fixed fee billing' }, bill_by: { type: 'string', enum: ['Project', 'Tasks', 'People', 'none'], description: 'Update billing method' }, hourly_rate: { type: 'number', minimum: 0, description: 'Update hourly rate' }, budget: { type: 'number', minimum: 0, description: 'Update budget amount' }, budget_by: { type: 'string', enum: ['project', 'project_cost', 'task', 'task_fees', 'person', 'none'], description: 'Update budget calculation method' }, budget_is_monthly: { type: 'boolean', description: 'Update monthly budget reset' }, notify_when_over_budget: { type: 'boolean', description: 'Update budget notifications' }, over_budget_notification_percentage: { type: 'number', minimum: 0, maximum: 100, description: 'Update notification threshold' }, show_budget_to_all: { type: 'boolean', description: 'Update budget visibility' }, cost_budget: { type: 'number', minimum: 0, description: 'Update cost budget' }, cost_budget_include_expenses: { type: 'boolean', description: 'Update expense inclusion' }, fee: { type: 'number', minimum: 0, description: 'Update fixed fee' }, notes: { type: 'string', description: 'Update project notes' }, starts_on: { type: 'string', format: 'date', description: 'Update start date (YYYY-MM-DD)' }, ends_on: { type: 'string', format: 'date', description: 'Update end date (YYYY-MM-DD)' }, }, required: ['id'], additionalProperties: false, }, }, handler: new UpdateProjectHandler(config), }, - src/client/projects-client.ts:69-90 (helper)The ProjectsClient.updateProject method is the actual HTTP API client helper. It destructures 'id' from the input, performs a PATCH request to /projects/{id}, logs the operation, and returns the response data.
async updateProject(input: any): Promise<any> { try { const { id, ...updateData } = input; this.logger.debug('Updating project', { projectId: id, updateFields: Object.keys(updateData) }); const response = await this.client.patch(`/projects/${id}`, updateData); this.logger.info('Successfully updated project', { projectId: response.data.id, projectName: response.data.name }); return response.data; } catch (error) { this.logger.error('Failed to update project:', error); throw error; } } - src/client/harvest-api.ts:147-149 (helper)The HarvestApi.updateProject method is a thin wrapper that delegates to projectsClient.updateProject(input). This is the intermediary between the tool handler and the low-level HTTP client.
async updateProject(input: any): Promise<any> { return this.projectsClient.updateProject(input); }