Skip to main content
Glama
XeroAPI

Xero MCP Server

Official

create-manual-journal

Create manual journal entries in Xero by specifying narration and balanced debit/credit lines with account codes, ensuring proper accounting records.

Instructions

Create a manual journal in Xero. Retrieve a list of account codes in Xero to use for the journal lines. Journal lines must contain at least two individual journal lines with account codes, use basic accounting account types pairing when not specified, and make sure journal line pairs have credit and debit balanced.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
narrationYesDescription of manual journal being posted
manualJournalLinesYesThe manualJournalLines element must contain at least two individual manualJournalLine sub-elements
dateNoOptional date in YYYY-MM-DD format
lineAmountTypesNoOptional line amount types (EXCLUSIVE, INCLUSIVE, NO_TAX), NO_TAX by default
statusNoOptional status of the manual journal (DRAFT, POSTED, DELETED, VOID, ARCHIVED), DRAFT by default
urlNoOptional URL link to a source document
showOnCashBasisReportsNoOptional boolean to show on cash basis reports, default is true

Implementation Reference

  • MCP tool handler: processes arguments, invokes Xero handler, formats success/error responses with deep link.
    async (args) => { try { const response = await createXeroManualJournal( args.narration, args.manualJournalLines, args.date, args.lineAmountTypes as LineAmountTypes | undefined, args.status as ManualJournal.StatusEnum | undefined, args.url, args.showOnCashBasisReports, ); if (response.isError) { return { content: [ { type: "text" as const, text: `Error creating manual journal: ${response.error}`, }, ], }; } const manualJournal = response.result; const deepLink = manualJournal.manualJournalID ? await getDeepLink( DeepLinkType.MANUAL_JOURNAL, manualJournal.manualJournalID, ) : null; return { content: [ { type: "text" as const, text: [ `Manual journal created: ${manualJournal.narration} (ID: ${manualJournal.manualJournalID})`, manualJournal.date ? `Date: ${manualJournal.date}` : null, manualJournal.status ? `Status: ${manualJournal.status}` : "No status", manualJournal.journalLines ? manualJournal.journalLines.map((line) => ({ type: "text" as const, text: [ `Line Amount: ${line.lineAmount}`, line.accountCode ? `Account Code: ${line.accountCode}` : "No account code", line.description ? `Description: ${line.description}` : "No description", line.taxType ? `Tax Type: ${line.taxType}` : "No tax type", `Tax Amount: ${line.taxAmount}`, ] .filter(Boolean) .join("\n"), })) : [{ type: "text" as const, text: "No journal lines" }], `Show on Cash Basis Reports: ${manualJournal.showOnCashBasisReports}`, deepLink ? `Link to view: ${deepLink}` : null, ] .filter(Boolean) .join("\n"), }, ], }; } catch (error) { const err = ensureError(error); return { content: [ { type: "text" as const, text: `Error creating manual journal: ${err.message}`, }, ], }; } },
  • Zod input schema defining parameters for the create-manual-journal tool.
    { narration: z .string() .describe("Description of manual journal being posted"), manualJournalLines: z .array( z.object({ lineAmount: z .number() .describe( "Total for manual journal line. Debits are positive, credits are negative value", ), accountCode: z.string().describe("Account code for the journal line"), description: z .string() .optional() .describe("Optional description for manual journal line"), taxType: z .string() .optional() .describe("Optional tax type for the manual journal line"), // TODO: TODO: tracking can be added here }), ) .describe( "The manualJournalLines element must contain at least two individual manualJournalLine sub-elements", ), date: z.string().optional().describe("Optional date in YYYY-MM-DD format"), lineAmountTypes: z .enum(["EXCLUSIVE", "INCLUSIVE", "NO_TAX"]) .optional() .describe( "Optional line amount types (EXCLUSIVE, INCLUSIVE, NO_TAX), NO_TAX by default", ), status: z .enum(["DRAFT", "POSTED", "DELETED", "VOID", "ARCHIVED"]) .optional() .describe( "Optional status of the manual journal (DRAFT, POSTED, DELETED, VOID, ARCHIVED), DRAFT by default", ), url: z .string() .optional() .describe("Optional URL link to a source document"), showOnCashBasisReports: z .boolean() .optional() .describe( "Optional boolean to show on cash basis reports, default is true", ), },
  • Registers all create tools (including create-manual-journal) on the MCP server via ToolFactory.
    CreateTools.map((tool) => tool()).forEach((tool) => server.tool(tool.name, tool.description, tool.schema, tool.handler), );
  • Groups and exports CreateManualJournalTool in CreateTools array for registration.
    export const CreateTools = [ CreateContactTool, CreateCreditNoteTool, CreateManualJournalTool, CreateInvoiceTool, CreateQuoteTool, CreatePaymentTool, CreateItemTool, CreateBankTransactionTool, CreatePayrollTimesheetTool, CreateTrackingCategoryTool, CreateTrackingOptionsTool ];
  • Helper handler that wraps Xero API call for creating manual journal with error handling.
    export async function createXeroManualJournal( narration: string, manualJournalLines: ManualJournalLine[], date?: string, lineAmountTypes?: LineAmountTypes, status?: ManualJournal.StatusEnum, url?: string, showOnCashBasisReports?: boolean, ): Promise<XeroClientResponse<ManualJournal>> { try { const createdManualJournal = await createManualJournal( narration, manualJournalLines, date, lineAmountTypes, status, url, showOnCashBasisReports, ); if (!createdManualJournal) { throw new Error("Manual journal creation failed."); } return { result: createdManualJournal, isError: false, error: null, }; } catch (error) { return { result: null, isError: true, error: formatError(error), }; } }

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