Skip to main content
Glama
XeroAPI

Xero MCP Server

Official

update-quote

Modify draft quotes in Xero by updating line items, reference details, and terms. All line items must be provided in the update request.

Instructions

Update a quote in Xero. Only works on draft quotes. All line items must be provided. Any line items not provided will be removed. Including existing line items. Do not modify line items that have not been specified by the user. When a quote is updated, a deep link to the quote in Xero is returned. This deep link can be used to view the quote in Xero directly. This link should be displayed to the user.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
quoteIdYes
lineItemsNoAll line items must be provided. Any line items not provided will be removed. Including existing line items. Do not modify line items that have not been specified by the user
referenceNo
termsNo
titleNo
summaryNo
quoteNumberNo
contactIdNo
dateNo
expiryDateNo

Implementation Reference

  • Defines the 'update-quote' MCP tool using CreateXeroTool, including input schema, description, and the async handler function that invokes updateXeroQuote, handles response, and provides a deep link.
    const UpdateQuoteTool = CreateXeroTool(
      "update-quote",
      "Update a quote in Xero. Only works on draft quotes.\
      All line items must be provided. Any line items not provided will be removed. Including existing line items.\
      Do not modify line items that have not been specified by the user. \
     When a quote is updated, a deep link to the quote in Xero is returned. \
     This deep link can be used to view the quote in Xero directly. \
     This link should be displayed to the user.",
      {
        quoteId: z.string(),
        lineItems: z.array(lineItemSchema).optional().describe(
          "All line items must be provided. Any line items not provided will be removed. Including existing line items. \
          Do not modify line items that have not been specified by the user",
        ),
        reference: z.string().optional(),
        terms: z.string().optional(),
        title: z.string().optional(),
        summary: z.string().optional(),
        quoteNumber: z.string().optional(),
        contactId: z.string().optional(),
        date: z.string().optional(),
        expiryDate: z.string().optional(),
      },
      async (
        {
          quoteId,
          lineItems,
          reference,
          terms,
          title,
          summary,
          quoteNumber,
          contactId,
          date,
          expiryDate,
        }
      ) => {
        const result = await updateXeroQuote(
          quoteId,
          lineItems,
          reference,
          terms,
          title,
          summary,
          quoteNumber,
          contactId,
          date,
          expiryDate,
        );
        if (result.isError) {
          return {
            content: [
              {
                type: "text" as const,
                text: `Error updating quote: ${result.error}`,
              },
            ],
          };
        }
    
        const quote = result.result;
    
        const deepLink = quote.quoteID
          ? await getDeepLink(DeepLinkType.QUOTE, quote.quoteID)
          : null;
    
        return {
          content: [
            {
              type: "text" as const,
              text: [
                "Quote updated successfully:",
                `ID: ${quote?.quoteID}`,
                `Contact: ${quote?.contact?.name}`,
                `Total: ${quote?.total}`,
                `Status: ${quote?.status}`,
                deepLink ? `Link to view: ${deepLink}` : null,
              ].join("\n"),
            },
          ],
        };
      },
    );
  • The updateXeroQuote function implements the core logic for updating a Xero quote: authenticates, checks if draft status, updates via Xero API, handles errors, returns structured response.
    export async function updateXeroQuote(
      quoteId: string,
      lineItems?: QuoteLineItem[],
      reference?: string,
      terms?: string,
      title?: string,
      summary?: string,
      quoteNumber?: string,
      contactId?: string,
      date?: string,
      expiryDate?: string,
    ): Promise<XeroClientResponse<Quote>> {
      try {
        const existingQuote = await getQuote(quoteId);
    
        const quoteStatus = existingQuote?.status;
    
        // Only allow updates to DRAFT quotes
        if (quoteStatus !== QuoteStatusCodes.DRAFT) {
          return {
            result: null,
            isError: true,
            error: `Cannot update quote because it is not a draft. Current status: ${quoteStatus}`,
          };
        }
    
        const updatedQuote = await updateQuote(
          quoteId,
          lineItems,
          reference,
          terms,
          title,
          summary,
          quoteNumber,
          contactId,
          date,
          expiryDate,
          existingQuote
        );
    
        if (!updatedQuote) {
          throw new Error("Quote update failed.");
        }
    
        return {
          result: updatedQuote,
          isError: false,
          error: null,
        };
      } catch (error) {
        return {
          result: null,
          isError: true,
          error: formatError(error),
        };
      }
    } 
  • Imports UpdateQuoteTool and includes it in the UpdateTools array (line 21) for subsequent registration.
    import UpdateQuoteTool from "./update-quote.tool.js";
    import UpdateTrackingCategoryTool from "./update-tracking-category.tool.js";
    import UpdateTrackingOptionsTool from "./update-tracking-options.tool.js";
    
    export const UpdateTools = [
      UpdateContactTool,
      UpdateCreditNoteTool,
      UpdateInvoiceTool,
      UpdateManualJournalTool,
      UpdateQuoteTool,
      UpdateItemTool,
      UpdateBankTransactionTool,
      ApprovePayrollTimesheetTool,
      AddTimesheetLineTool,
      UpdatePayrollTimesheetLineTool,
      RevertPayrollTimesheetTool,
      UpdateTrackingCategoryTool,
      UpdateTrackingOptionsTool
    ];
  • Batch registers all tools from UpdateTools (including 'update-quote') on the MCP server.
    UpdateTools.map((tool) => tool()).forEach((tool) =>
      server.tool(tool.name, tool.description, tool.schema, tool.handler),
    );
Behavior4/5

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

With no annotations provided, the description carries the full burden and does well: it discloses that the tool mutates data (implied by 'Update'), specifies a precondition ('draft quotes'), warns about destructive behavior ('Any line items not provided will be removed'), and describes the return value ('a deep link to the quote in Xero is returned'). It lacks details on permissions or rate limits, but covers key behavioral traits effectively.

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

Conciseness4/5

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

The description is appropriately sized and front-loaded with the most important information (purpose and key constraint). Each sentence adds value, such as behavioral warnings and output details. Minor redundancy with the schema's lineItems description slightly reduces efficiency, but overall it's well-structured.

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

Completeness4/5

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

Given the complexity (10 parameters, no annotations, no output schema), the description is quite complete: it covers purpose, constraints, critical parameter behavior, and output format. It lacks details on other parameters and error cases, but for a mutation tool with sparse structured data, it provides sufficient context for effective use.

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

Parameters4/5

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

Schema description coverage is low (10%), but the description compensates by explaining the critical semantics of the 'lineItems' parameter: 'All line items must be provided. Any line items not provided will be removed.' This adds essential meaning beyond the schema's minimal descriptions. It does not cover other parameters, but the high value of this guidance justifies a strong score.

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 verb ('Update') and resource ('a quote in Xero'), and distinguishes it from siblings by specifying 'Only works on draft quotes'—a crucial constraint not implied by the name alone. This makes the purpose specific and differentiated.

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 provides explicit context for when to use the tool ('Only works on draft quotes') and implies an alternative (e.g., not using it for non-draft quotes). However, it does not name specific sibling tools as alternatives (e.g., 'create-quote' for new quotes), which prevents a perfect score.

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/XeroAPI/xero-mcp-server'

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