delete_category
Delete a category or category group. Force deletion to remove all associated budgets and transactions (irreversible).
Instructions
Delete a single category or category group. By default fails (HTTP 422) if dependencies exist, returning a structured dependents payload. Set force=true to delete and disassociate from all related budgets, transactions, recurring items, etc. Force delete is irreversible.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| category_id | Yes | Id of the category or category group to delete. | |
| force | No | If true, force deletion even if dependencies exist (irreversible). |
Implementation Reference
- src/tools/categories.ts:290-338 (registration)Registration of the 'delete_category' tool with the MCP server, including its input schema (category_id required, force optional) and the async handler that calls DELETE /categories/{id} API endpoint.
server.registerTool( "delete_category", { description: "Delete a single category or category group. By default fails (HTTP 422) if dependencies exist, returning a structured `dependents` payload. Set force=true to delete and disassociate from all related budgets, transactions, recurring items, etc. Force delete is irreversible.", inputSchema: { category_id: z.coerce .number() .describe( "Id of the category or category group to delete.", ), force: z .boolean() .optional() .describe( "If true, force deletion even if dependencies exist (irreversible).", ), }, annotations: { destructiveHint: true, }, }, async ({ category_id, force }) => { try { const path = force ? `/categories/${category_id}?force=true` : `/categories/${category_id}`; const response = await api.delete(path); if (response.status === 204) { return successResponse("Category deleted."); } if (!response.ok) { if (response.status === 422) { return dataResponse(await response.json()); } return handleApiError( response, "Failed to delete category", ); } return dataResponse(await response.json()); } catch (error) { return catchError(error, "Failed to delete category"); } }, ); - src/tools/categories.ts:295-311 (schema)Input schema for the delete_category tool: requires category_id (number) and optionally force (boolean). Annotated as destructive.
inputSchema: { category_id: z.coerce .number() .describe( "Id of the category or category group to delete.", ), force: z .boolean() .optional() .describe( "If true, force deletion even if dependencies exist (irreversible).", ), }, annotations: { destructiveHint: true, }, }, - src/tools/categories.ts:312-337 (handler)Handler function for delete_category: sends DELETE request to /categories/{id} (with ?force=true if force flag set). Handles 204 (success with no content), 422 (dependencies exist, returns dependents payload), and other errors.
async ({ category_id, force }) => { try { const path = force ? `/categories/${category_id}?force=true` : `/categories/${category_id}`; const response = await api.delete(path); if (response.status === 204) { return successResponse("Category deleted."); } if (!response.ok) { if (response.status === 422) { return dataResponse(await response.json()); } return handleApiError( response, "Failed to delete category", ); } return dataResponse(await response.json()); } catch (error) { return catchError(error, "Failed to delete category"); } }, - src/types.ts:46-56 (helper)Type definition for the response payload when deletion is blocked due to existing dependencies (HTTP 422). Contains the category name and counts of dependents across budgets, rules, transactions, children, recurring items, and Plaid categories.
export interface DeleteCategoryWithDependencies { category_name: string; dependents: { budget: number; category_rules: number; transactions: number; children: number; recurring: number; plaid_cats: number; }; }