Skip to main content
Glama

update_customer_address

Update a customer's address book entry by modifying only the provided fields, leaving other details unchanged.

Instructions

Update an address book entry (partial update). PUT /customers/{customerId}/addressbooks/{addressId}. Only explicitly provided fields are modified; omitted fields are kept from the existing address record.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
customerIdYesCustomer ID
addressIdYesAddress book entry ID
street1NoStreet line 1
cityNoCity
stateNoState
zipNoPostal code
countryCodeNoISO 3166-1 alpha-2 country code, e.g. ES, AR, MX
nameNoAddress name
contactNameNoContact name
street2NoStreet line 2
companyNoCompany name
contactEmailNoContact email
contactPhoneNoContact phone
typeNoAddress type: residential or commercial

Implementation Reference

  • Exports updateCustomerAddressTool as a Tool object, linking the definition and handler together
    export const updateCustomerAddressTool: Tool = {
      definition,
      handler,
    };
  • Registration of updateCustomerAddressTool in the registerCustomerTools() array that returns all customer tools
    export function registerCustomerTools(): Tool[] {
      return [
        listCustomersTool,
        getCustomerTool,
        createCustomerTool,
        updateCustomerTool,
        deleteCustomerTool,
        getCustomerInvoicesTool,
        getCustomerUnpaidInvoicesTool,
        getCustomerSubscriptionsTool,
        getCustomerLogsTool,
        listCustomerAddressesTool,
        getCustomerAddressTool,
        createCustomerAddressTool,
        updateCustomerAddressTool,
  • Main handler function: parses args, fetches existing address, merges fields using pickRequired/pickOptional, resolves country code, and calls the service layer
    async function handler(client: Client, args: Record<string, unknown> | undefined) {
      const rawArgs = args ?? {};
      const parsed = schema.safeParse(rawArgs);
      if (!parsed.success) {
        return errorResult(parsed.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; "));
      }
    
      const { customerId, addressId, ...incoming } = parsed.data;
      const hasField = (key: string): boolean => Object.prototype.hasOwnProperty.call(rawArgs, key);
    
      const existing = await customerService.getCustomerAddress(client, customerId, addressId);
    
      const pickOptional = <T extends string | null>(
        key: keyof typeof incoming,
        existingVal: T,
        incomingVal: T | undefined | null
      ): string | null | undefined => {
        if (hasField(String(key))) return incomingVal === undefined ? undefined : incomingVal;
        return existingVal === null ? undefined : existingVal;
      };
    
      const pickRequired = (
        key: "street1" | "city" | "state" | "zip",
        existingVal: string | null,
        incomingVal: string | null | undefined
      ): string | null | undefined => {
        if (hasField(key)) {
          // If explicitly provided as `null`, we allow passing it through.
          return incomingVal === undefined ? existingVal ?? undefined : incomingVal;
        }
        if (existingVal == null) {
          // Upstream expects these fields to be present for a meaningful update.
          throw new Error(`Existing address record is missing "${key}". Provide "${key}" in the update payload.`);
        }
        return existingVal;
      };
    
      const requiredStreet1 = pickRequired("street1", existing.street1, incoming.street1);
      const requiredCity = pickRequired("city", existing.city, incoming.city);
      const requiredState = pickRequired("state", existing.state, incoming.state);
      const requiredZip = pickRequired("zip", existing.zip, incoming.zip);
    
      const chosenCountryCode = (() => {
        if (hasField("countryCode")) {
          return incoming.countryCode === undefined ? existing.country.alpha2Code : incoming.countryCode;
        }
        return existing.country.alpha2Code;
      })();
    
      const countryId: number | null =
        chosenCountryCode === null
          ? null
          : // Re-resolve using the authoritative alpha2Code (keeps us aligned with upstream's numeric id).
            await resolveCountryId(client, chosenCountryCode);
    
      const chosenType = (() => {
        if (hasField("type")) {
          return incoming.type === undefined ? undefined : incoming.type;
        }
        return existing.type === "residential" || existing.type === "commercial" ? existing.type : undefined;
      })();
    
      const body: customerService.UpdateCustomerAddressBody = {
        street1: requiredStreet1,
        city: requiredCity,
        state: requiredState,
        zip: requiredZip,
        countryId,
      };
    
      const name = pickOptional("name", existing.name, incoming.name);
      if (name !== undefined) body.name = name;
    
      const contactName = pickOptional("contactName", existing.contactName, incoming.contactName);
      if (contactName !== undefined) body.contactName = contactName;
    
      const street2 = pickOptional("street2", existing.street2, incoming.street2);
      if (street2 !== undefined) body.street2 = street2;
    
      const company = pickOptional("company", existing.company, incoming.company);
      if (company !== undefined) body.company = company;
    
      const contactEmail = pickOptional("contactEmail", existing.contactEmail, incoming.contactEmail);
      if (contactEmail !== undefined) body.contactEmail = contactEmail;
    
      const contactPhone = pickOptional("contactPhone", existing.contactPhone, incoming.contactPhone);
      if (contactPhone !== undefined) body.contactPhone = contactPhone;
    
      if (chosenType !== undefined) body.type = chosenType;
    
      return handleToolCall(() =>
        customerService.updateCustomerAddress(client, customerId, addressId, body)
      );
    }
  • Zod validation schema for input: customerId, addressId (required), and optional nullable fields for partial update
    const schema = z.object({
      customerId: z.string().min(1, "customerId is required"),
      addressId: z.string().min(1, "addressId is required"),
      street1: nullableMin1String.optional().nullable(),
      city: nullableMin1String.optional().nullable(),
      state: nullableMin1String.optional().nullable(),
      zip: nullableMin1String.optional().nullable(),
      countryCode: countryCodeSchema,
      name: nullableMin1String.optional().nullable(),
      contactName: nullableMin1String.optional().nullable(),
      street2: nullableMin1String.optional().nullable(),
      company: nullableMin1String.optional().nullable(),
      contactEmail: nullableMin1String.optional().nullable(),
      contactPhone: nullableMin1String.optional().nullable(),
      type: z.enum(["residential", "commercial"]).optional().nullable(),
    });
  • Service function that sends PUT /customers/{customerId}/addressbooks/{addressId} with the filtered payload
    export async function updateCustomerAddress(
      client: Client,
      customerId: string,
      addressId: string,
      body: UpdateCustomerAddressBody
    ): Promise<CustomerAddressBook> {
      const payload = Object.fromEntries(
        Object.entries(body).filter(([, v]) => v !== undefined)
      ) as UpdateCustomerAddressBody;
      return client.put<CustomerAddressBook>(
        `/customers/${customerId}/addressbooks/${addressId}`,
        Object.keys(payload).length ? payload : undefined
      );
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It correctly identifies the tool as a partial update (non-destructive to omitted fields) and specifies the HTTP method. However, it lacks details on side effects (e.g., does it return the updated record?), error conditions, idempotency, or authorization requirements. For a mutation tool with many parameters, this is a moderate gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, consisting of two clear sentences that convey purpose and behavioral nuance. It is front-loaded with the core action, followed by the endpoint and partial update detail. No unnecessary words, making it easy to parse quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (14 parameters, no annotations, no output schema), the description covers the primary behavioral aspect (partial update) but leaves gaps. It does not describe the return value, prerequisites (e.g., customer must exist), or error handling. For completeness, more context on expected outcomes or failure modes would be beneficial.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% coverage with descriptions for all 14 parameters. The description adds value by clarifying the partial update semantics, which explains why parameters are optional. However, it does not provide additional meaning beyond what the schema already offers for individual parameters. Baseline score of 3 is appropriate given high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: updating an address book entry partially. It specifies the HTTP method (PUT) and endpoint, and the verb 'update' combined with 'partial update' leaves no ambiguity. The tool name itself is descriptive, and the description effectively distinguishes it from sibling tools like create_customer_address and delete_customer_address.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly notes that this is a partial update: only explicitly provided fields are modified, omitted fields remain unchanged. This is a key usage guideline that helps the agent understand it is not a full replacement. While it does not provide explicit when-to-use vs alternatives (e.g., create vs update), the partial update nature implicitly guides the agent for modifying existing records. No exclusions are stated, but the context is clear enough.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/rhinosaas/rebillia-mcp-server'

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