Add erpnext-mcp/src/tools/accounting.ts
This commit is contained in:
parent
ea23d7146e
commit
ad1dad03d9
1 changed files with 234 additions and 0 deletions
234
erpnext-mcp/src/tools/accounting.ts
Normal file
234
erpnext-mcp/src/tools/accounting.ts
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
|
import { ERPNextClient, truncateResponse } from "../services/erpnext-client.js";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export function registerAccountingTools(server: McpServer, client: ERPNextClient): void {
|
||||||
|
|
||||||
|
// ─── 16. Get Account Balance ───────────────────────────
|
||||||
|
server.registerTool(
|
||||||
|
"erpnext_get_account_balance",
|
||||||
|
{
|
||||||
|
title: "Get Account Balance",
|
||||||
|
description: `Get the balance of a specific GL account.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- account: Account name
|
||||||
|
- company: Company name
|
||||||
|
- date: Optional date for balance as-of (YYYY-MM-DD)`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
account: z.string().describe("Account name"),
|
||||||
|
company: z.string().describe("Company name"),
|
||||||
|
date: z.string().optional().describe("Date for as-of balance (YYYY-MM-DD)"),
|
||||||
|
}).strict(),
|
||||||
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
||||||
|
},
|
||||||
|
async (params) => {
|
||||||
|
try {
|
||||||
|
const result = await client.callMethod("erpnext.accounts.utils.get_balance_on", {
|
||||||
|
account: params.account,
|
||||||
|
date: params.date,
|
||||||
|
company: params.company,
|
||||||
|
});
|
||||||
|
return { content: [{ type: "text", text: `Balance of ${params.account}: ${result}` }] };
|
||||||
|
} catch (error) {
|
||||||
|
return { isError: true, content: [{ type: "text", text: `Error: ${(error as Error).message}` }] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── 17. Get Exchange Rate ─────────────────────────────
|
||||||
|
server.registerTool(
|
||||||
|
"erpnext_get_exchange_rate",
|
||||||
|
{
|
||||||
|
title: "Get Exchange Rate",
|
||||||
|
description: `Get currency exchange rate between two currencies.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- from_currency: Source currency code (e.g. "USD")
|
||||||
|
- to_currency: Target currency code (e.g. "EUR")
|
||||||
|
- date: Optional transaction date`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
from_currency: z.string().describe("Source currency (e.g. USD)"),
|
||||||
|
to_currency: z.string().describe("Target currency (e.g. EUR)"),
|
||||||
|
date: z.string().optional().describe("Transaction date (YYYY-MM-DD)"),
|
||||||
|
}).strict(),
|
||||||
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
||||||
|
},
|
||||||
|
async (params) => {
|
||||||
|
try {
|
||||||
|
const result = await client.callMethod("erpnext.setup.utils.get_exchange_rate", {
|
||||||
|
from_currency: params.from_currency,
|
||||||
|
to_currency: params.to_currency,
|
||||||
|
transaction_date: params.date,
|
||||||
|
});
|
||||||
|
return { content: [{ type: "text", text: `Exchange rate ${params.from_currency} → ${params.to_currency}: ${result}` }] };
|
||||||
|
} catch (error) {
|
||||||
|
return { isError: true, content: [{ type: "text", text: `Error: ${(error as Error).message}` }] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── 18. Create Journal Entry ──────────────────────────
|
||||||
|
server.registerTool(
|
||||||
|
"erpnext_create_journal_entry",
|
||||||
|
{
|
||||||
|
title: "Create Journal Entry",
|
||||||
|
description: `Create a Journal Entry with debit/credit lines.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- company: Company name
|
||||||
|
- posting_date: Posting date (YYYY-MM-DD)
|
||||||
|
- accounts: Array of account line objects with {account, debit_in_account_currency, credit_in_account_currency, party_type?, party?, cost_center?}
|
||||||
|
- user_remark: Optional remark/narration
|
||||||
|
- voucher_type: Optional (default "Journal Entry"). Options: "Journal Entry", "Bank Entry", "Cash Entry", "Credit Card Entry", "Debit Note", "Credit Note"`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
company: z.string(),
|
||||||
|
posting_date: z.string(),
|
||||||
|
accounts: z.array(z.object({
|
||||||
|
account: z.string(),
|
||||||
|
debit_in_account_currency: z.number().default(0),
|
||||||
|
credit_in_account_currency: z.number().default(0),
|
||||||
|
party_type: z.string().optional(),
|
||||||
|
party: z.string().optional(),
|
||||||
|
cost_center: z.string().optional(),
|
||||||
|
})),
|
||||||
|
user_remark: z.string().optional(),
|
||||||
|
voucher_type: z.string().default("Journal Entry"),
|
||||||
|
}).strict(),
|
||||||
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
||||||
|
},
|
||||||
|
async (params) => {
|
||||||
|
try {
|
||||||
|
const doc = await client.createDocument("Journal Entry", {
|
||||||
|
company: params.company,
|
||||||
|
posting_date: params.posting_date,
|
||||||
|
voucher_type: params.voucher_type,
|
||||||
|
user_remark: params.user_remark,
|
||||||
|
accounts: params.accounts,
|
||||||
|
});
|
||||||
|
return { content: [{ type: "text", text: `Created Journal Entry: ${doc.name}` }] };
|
||||||
|
} catch (error) {
|
||||||
|
return { isError: true, content: [{ type: "text", text: `Error: ${(error as Error).message}` }] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── 19. Make Payment Entry ────────────────────────────
|
||||||
|
server.registerTool(
|
||||||
|
"erpnext_make_payment_entry",
|
||||||
|
{
|
||||||
|
title: "Make Payment Entry from Invoice",
|
||||||
|
description: `Create a Payment Entry from a Sales or Purchase Invoice.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- source_doctype: "Sales Invoice" or "Purchase Invoice"
|
||||||
|
- source_name: Invoice name/ID`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
source_doctype: z.enum(["Sales Invoice", "Purchase Invoice"]),
|
||||||
|
source_name: z.string(),
|
||||||
|
}).strict(),
|
||||||
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
||||||
|
},
|
||||||
|
async (params) => {
|
||||||
|
try {
|
||||||
|
const method = params.source_doctype === "Sales Invoice"
|
||||||
|
? "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"
|
||||||
|
: "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
|
||||||
|
const result = await client.callMethod(method, {
|
||||||
|
dt: params.source_doctype,
|
||||||
|
dn: params.source_name,
|
||||||
|
});
|
||||||
|
return { content: [{ type: "text", text: `Payment Entry draft created:\n${JSON.stringify(result, null, 2)}` }] };
|
||||||
|
} catch (error) {
|
||||||
|
return { isError: true, content: [{ type: "text", text: `Error: ${(error as Error).message}` }] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── 20. Get Outstanding Invoices ──────────────────────
|
||||||
|
server.registerTool(
|
||||||
|
"erpnext_get_outstanding_invoices",
|
||||||
|
{
|
||||||
|
title: "Get Outstanding Invoices",
|
||||||
|
description: `List outstanding (unpaid) invoices for a party.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- party_type: "Customer" or "Supplier"
|
||||||
|
- party: Party name
|
||||||
|
- company: Company name`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
party_type: z.enum(["Customer", "Supplier"]),
|
||||||
|
party: z.string(),
|
||||||
|
company: z.string(),
|
||||||
|
}).strict(),
|
||||||
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
||||||
|
},
|
||||||
|
async (params) => {
|
||||||
|
try {
|
||||||
|
const result = await client.callMethod(
|
||||||
|
"erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents",
|
||||||
|
{
|
||||||
|
party_type: params.party_type,
|
||||||
|
party: params.party,
|
||||||
|
company: params.company,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return { content: [{ type: "text", text: truncateResponse(JSON.stringify(result, null, 2)) }] };
|
||||||
|
} catch (error) {
|
||||||
|
return { isError: true, content: [{ type: "text", text: `Error: ${(error as Error).message}` }] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── 21. Get Fiscal Year ───────────────────────────────
|
||||||
|
server.registerTool(
|
||||||
|
"erpnext_get_fiscal_year",
|
||||||
|
{
|
||||||
|
title: "Get Fiscal Year",
|
||||||
|
description: `Get the fiscal year for a given date and company.`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
date: z.string().describe("Date (YYYY-MM-DD)"),
|
||||||
|
company: z.string().optional(),
|
||||||
|
}).strict(),
|
||||||
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
||||||
|
},
|
||||||
|
async (params) => {
|
||||||
|
try {
|
||||||
|
const result = await client.callMethod("erpnext.accounts.utils.get_fiscal_year", {
|
||||||
|
date: params.date,
|
||||||
|
company: params.company,
|
||||||
|
});
|
||||||
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
||||||
|
} catch (error) {
|
||||||
|
return { isError: true, content: [{ type: "text", text: `Error: ${(error as Error).message}` }] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── 22. Get Party Account Balance ─────────────────────
|
||||||
|
server.registerTool(
|
||||||
|
"erpnext_get_party_balance",
|
||||||
|
{
|
||||||
|
title: "Get Party Account Balance",
|
||||||
|
description: `Get the account balance for a Customer or Supplier.`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
party_type: z.enum(["Customer", "Supplier"]),
|
||||||
|
party: z.string(),
|
||||||
|
company: z.string(),
|
||||||
|
}).strict(),
|
||||||
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
||||||
|
},
|
||||||
|
async (params) => {
|
||||||
|
try {
|
||||||
|
const result = await client.callMethod("erpnext.accounts.utils.get_balance_on", {
|
||||||
|
party_type: params.party_type,
|
||||||
|
party: params.party,
|
||||||
|
company: params.company,
|
||||||
|
});
|
||||||
|
return { content: [{ type: "text", text: `Balance for ${params.party_type} "${params.party}": ${result}` }] };
|
||||||
|
} catch (error) {
|
||||||
|
return { isError: true, content: [{ type: "text", text: `Error: ${(error as Error).message}` }] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue