update_product_rate_plan_charge
Update a product rate plan charge by sending only the fields to change; the tool merges them with the current charge and validates before updating.
Instructions
Update a product rate plan charge. PUT /product-rateplan-charges/{chargeId}. You can send only the fields you want to change (e.g. chargeTier with new price); the tool fetches the current charge and merges your input so the backend receives all required fields. Validates in MCP before calling the API. Optional inputs: name, chargeType, chargeModel, billCycleType, category, chargeTier (currency, price as dollars e.g. 22.87 or cents e.g. 2287), taxable, weight, endDateCondition, billingPeriod, billingTiming, billingPeriodAlignment, specificBillingPeriod, billCycleDay (1-31 when billCycleType specificDayOfMonth), weeklyBillCycleDay (when specificDayOfWeek), monthlyBillCycleYear (1-12 when specificMonthOfYear). When chargeType is recurring, billingPeriod, specificBillingPeriod, billingPeriodAlignment, billingTiming are required.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chargeId | Yes | Product rate plan charge ID (required) | |
| name | No | Charge name | |
| chargeType | No | oneTime, recurring, or usage | |
| chargeModel | No | flatFeePricing, perUnitPricing, tieredPricing, or volumePricing | |
| billCycleType | No | chargeTriggerDay, defaultFromCustomer, specificDayOfMonth, specificDayOfWeek, specificMonthOfYear, subscriptionStartDay, subscriptionFreeTrial | |
| category | No | physical or digital | |
| chargeTier | No | Array of {currency, price (dollars e.g. 22.87 or cents e.g. 2287), optional startingUnit, endingUnit, priceFormat, tier}. To update only price, send this and chargeId; other fields are filled from current charge. | |
| taxable | No | Whether taxable | |
| weight | No | Weight (integer) | |
| description | No | Description | |
| endDateCondition | No | subscriptionEnd or fixedPeriod | |
| billingPeriod | No | day, week, month, or year (required if chargeType recurring) | |
| billingTiming | No | inAdvance or inArrears (required if chargeType recurring) | |
| billingPeriodAlignment | No | alignToCharge, alignToSubscriptionStart, alignToTermStart (required if chargeType recurring) | |
| specificBillingPeriod | No | Required when chargeType recurring | |
| billCycleDay | No | 1-31 when billCycleType is specificDayOfMonth | |
| weeklyBillCycleDay | No | sunday, monday, tuesday, wednesday, thursday, friday, saturday when billCycleType is specificDayOfWeek | |
| monthlyBillCycleYear | No | 1-12 when billCycleType is specificMonthOfYear |
Implementation Reference
- The main handler function for update_product_rate_plan_charge. Parses input with Zod schema, fetches existing charge via chargeService.getRatePlanCharge(), merges user-provided fields with existing data via existingChargeToUpdateBody(), validates the merged body with validateMergedBody(), then calls chargeService.updateRatePlanCharge() to PUT the update via the API.
async function handler(client: Client, args: Record<string, unknown> | undefined) { const parsed = schema.safeParse(args); if (!parsed.success) { return errorResult(parsed.error.errors.map((e) => e.message).join("; ")); } const input = parsed.data; const { chargeId, chargeTier: inputChargeTier, ...restInput } = input; try { // Fetch existing charge so we can merge when user sends partial update (e.g. only price) const existing = await chargeService.getRatePlanCharge(client, chargeId); const existingBody = chargeService.existingChargeToUpdateBody(existing); // User payload: only include fields the user actually provided (defined), so we don't overwrite with undefined const userPayload: Record<string, unknown> = {}; for (const [k, v] of Object.entries(restInput)) { if (v !== undefined) userPayload[k] = v; } if (inputChargeTier !== undefined) { userPayload.chargeTier = inputChargeTier.map((t) => ({ ...t, price: normalizePriceToCents(t.price), priceFormat: t.priceFormat ?? "", })); } const merged: UpdateRatePlanChargeBody = { ...existingBody, ...userPayload, } as UpdateRatePlanChargeBody; // Ensure chargeTier tiers have priceFormat for API if (merged.chargeTier?.length) { merged.chargeTier = merged.chargeTier.map((t) => ({ ...t, priceFormat: t.priceFormat ?? "", })); } validateMergedBody(merged); return handleToolCall(() => chargeService.updateRatePlanCharge(client, chargeId, merged)); } catch (err) { const message = err instanceof Error ? err.message : String(err); return errorResult(message); } } - Zod input schema defining the tool's input validation. Only chargeId is required; all other fields (name, chargeType, chargeModel, billCycleType, category, chargeTier, taxable, weight, description, endDateCondition, billingPeriod, billingTiming, billingPeriodAlignment, specificBillingPeriod, billCycleDay, weeklyBillCycleDay, monthlyBillCycleYear) are optional for partial updates.
const schema = z.object({ chargeId: z.string().min(1, "chargeId is required"), name: z.string().min(1).optional(), chargeType: z.enum(chargeTypeEnum).optional(), chargeModel: z.enum(chargeModelEnum).optional(), billCycleType: z.enum(billCycleTypeEnum).optional(), category: z.enum(["physical", "digital"]).optional(), chargeTier: z.array(chargeTierItemSchema).optional(), taxable: z.boolean().optional(), weight: z.coerce.number().int().min(0).optional(), description: z.string().optional(), endDateCondition: z.enum(["subscriptionEnd", "fixedPeriod"]).optional(), billingPeriod: z.enum(billingPeriodEnum).optional(), billingTiming: z.enum(billingTimingEnum).optional(), billingPeriodAlignment: z.enum(billingPeriodAlignmentEnum).optional(), specificBillingPeriod: z.number().int().optional(), billCycleDay: z.number().int().min(1).max(31).optional(), weeklyBillCycleDay: z.enum(weeklyBillCycleDayEnum).optional(), monthlyBillCycleYear: z.number().int().min(1).max(12).optional(), }); - Exports updateRatePlanChargeTool as a Tool object with the definition and handler.
export const updateRatePlanChargeTool: Tool = { definition, handler, }; - src/tools/index.ts:49-57 (registration)The executeTool function finds the tool by name and calls its handler. The tool is registered via registerProductRatePlanChargeTools() on line 30, which includes updateRatePlanChargeTool.
export async function executeTool( name: string, args: Record<string, unknown> | undefined, client: RebilliaClient ): Promise<ToolResult | undefined> { const tool = tools.find((t) => t.definition.name === name); if (!tool) return undefined; return tool.handler(client, args); } - The updateRatePlanCharge API service function that performs the actual PUT /product-rateplan-charges/{chargeId} HTTP call, filtering out undefined values and normalizing charge tiers before sending.
export async function updateRatePlanCharge( client: Client, chargeId: string, body: UpdateRatePlanChargeBody ): Promise<unknown> { const payload = Object.fromEntries( Object.entries(body).filter(([, v]) => v !== undefined) ) as UpdateRatePlanChargeBody; if (payload.weight != null) payload.weight = Number(payload.weight); if (payload.chargeTier?.length) payload.chargeTier = normalizeChargeTier(payload.chargeTier); return client.put<unknown>( `/product-rateplan-charges/${chargeId}`, Object.keys(payload).length ? payload : undefined ); }