@codemill-solutions/yuki-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/accounting.d.ts +13 -0
- package/dist/tools/accounting.d.ts.map +1 -0
- package/dist/tools/accounting.js +117 -0
- package/dist/tools/accounting.js.map +1 -0
- package/dist/tools/administrations.d.ts +14 -0
- package/dist/tools/administrations.d.ts.map +1 -0
- package/dist/tools/administrations.js +80 -0
- package/dist/tools/administrations.js.map +1 -0
- package/dist/tools/documents.d.ts +18 -0
- package/dist/tools/documents.d.ts.map +1 -0
- package/dist/tools/documents.js +269 -0
- package/dist/tools/documents.js.map +1 -0
- package/dist/tools/invoices.d.ts +22 -0
- package/dist/tools/invoices.d.ts.map +1 -0
- package/dist/tools/invoices.js +549 -0
- package/dist/tools/invoices.js.map +1 -0
- package/dist/tools/relations.d.ts +20 -0
- package/dist/tools/relations.d.ts.map +1 -0
- package/dist/tools/relations.js +271 -0
- package/dist/tools/relations.js.map +1 -0
- package/dist/tools/transactions.d.ts +19 -0
- package/dist/tools/transactions.d.ts.map +1 -0
- package/dist/tools/transactions.js +294 -0
- package/dist/tools/transactions.js.map +1 -0
- package/dist/yuki-client.d.ts +72 -0
- package/dist/yuki-client.d.ts.map +1 -0
- package/dist/yuki-client.js +227 -0
- package/dist/yuki-client.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { XmlValue, escapeXml } from "../yuki-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register tools for working with Yuki relations (contacts).
|
|
5
|
+
*
|
|
6
|
+
* Relations are debitors (customers) and creditors (suppliers) in Yuki.
|
|
7
|
+
* The Contact service uses domainID (the same GUID as your administrationID).
|
|
8
|
+
*
|
|
9
|
+
* Yuki service: Contact.asmx
|
|
10
|
+
* Method: SearchContacts(sessionID, domainID, searchOption, searchValue,
|
|
11
|
+
* sortOrder, modifiedAfter, active, pageNumber)
|
|
12
|
+
* Max results: 100 per page — use pageNumber for pagination.
|
|
13
|
+
*/
|
|
14
|
+
export function registerRelationTools(server, client) {
|
|
15
|
+
/**
|
|
16
|
+
* search_relations
|
|
17
|
+
*
|
|
18
|
+
* Search for contacts / relations (debitors and creditors) in Yuki.
|
|
19
|
+
* Returns up to 100 matches per page with ID, name, code, type, and address.
|
|
20
|
+
*
|
|
21
|
+
* Use this to look up a relation ID before linking invoices, or to find
|
|
22
|
+
* contact details for a customer or supplier.
|
|
23
|
+
*
|
|
24
|
+
* Rate cost: 1 request per page.
|
|
25
|
+
*/
|
|
26
|
+
server.registerTool("search_relations", {
|
|
27
|
+
description: "Search for Yuki relations (customers/suppliers) by name, code, VAT number, or other fields. " +
|
|
28
|
+
"Returns up to 100 contacts per page with IDs, codes, names, and contact details. " +
|
|
29
|
+
"Omit searchValue (or pass an empty string) to retrieve all contacts.",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
searchValue: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.default("")
|
|
35
|
+
.describe("The value to search for (e.g. company name, relation code). " +
|
|
36
|
+
"Omit or pass an empty string to retrieve all contacts (subject to Yuki API support)."),
|
|
37
|
+
searchOption: z
|
|
38
|
+
.enum([
|
|
39
|
+
"All",
|
|
40
|
+
"Name",
|
|
41
|
+
"City",
|
|
42
|
+
"Postcode",
|
|
43
|
+
"Tag",
|
|
44
|
+
"Email",
|
|
45
|
+
"Website",
|
|
46
|
+
"Phone",
|
|
47
|
+
"Code",
|
|
48
|
+
"CoCNumber",
|
|
49
|
+
"VATNumber",
|
|
50
|
+
"BankAccount",
|
|
51
|
+
"ID",
|
|
52
|
+
"ContactType",
|
|
53
|
+
"HID",
|
|
54
|
+
])
|
|
55
|
+
.optional()
|
|
56
|
+
.default("All")
|
|
57
|
+
.describe("Which field to search in. Defaults to 'All' (full-text search)."),
|
|
58
|
+
active: z
|
|
59
|
+
.enum(["Both", "Active", "Inactive"])
|
|
60
|
+
.optional()
|
|
61
|
+
.default("Active")
|
|
62
|
+
.describe("Filter by active status. Defaults to 'Active'."),
|
|
63
|
+
pageNumber: z
|
|
64
|
+
.number()
|
|
65
|
+
.int()
|
|
66
|
+
.positive()
|
|
67
|
+
.optional()
|
|
68
|
+
.default(1)
|
|
69
|
+
.describe("Page number for pagination (100 results per page)."),
|
|
70
|
+
domainId: z
|
|
71
|
+
.string()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe("Domain ID (GUID). Defaults to YUKI_DOMAIN_ID env var."),
|
|
74
|
+
},
|
|
75
|
+
}, async ({ searchValue, searchOption, active, pageNumber, domainId }) => {
|
|
76
|
+
try {
|
|
77
|
+
const domain = domainId ?? client.defaultDomainId;
|
|
78
|
+
if (!domain) {
|
|
79
|
+
throw new Error("domainId is required (or set YUKI_DOMAIN_ID env var)");
|
|
80
|
+
}
|
|
81
|
+
const sessionID = await client.getSessionID();
|
|
82
|
+
// Note: Contact.asmx uses 'domainID', not 'administrationID'
|
|
83
|
+
const result = await client.callSoap({
|
|
84
|
+
service: "Contact.asmx",
|
|
85
|
+
method: "SearchContacts",
|
|
86
|
+
params: {
|
|
87
|
+
sessionID,
|
|
88
|
+
domainID: domain,
|
|
89
|
+
searchOption: searchOption ?? "All",
|
|
90
|
+
searchValue,
|
|
91
|
+
sortOrder: "Name",
|
|
92
|
+
// modifiedAfter is optional — omit to get all
|
|
93
|
+
active: active ?? "Active",
|
|
94
|
+
pageNumber: pageNumber ?? 1,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
const relations = normalizeContacts(result);
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: "text",
|
|
102
|
+
text: JSON.stringify({
|
|
103
|
+
success: true,
|
|
104
|
+
count: relations.length,
|
|
105
|
+
page: pageNumber ?? 1,
|
|
106
|
+
relations,
|
|
107
|
+
}, null, 2),
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
114
|
+
return {
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: "text",
|
|
118
|
+
text: JSON.stringify({ success: false, error: message }, null, 2),
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
isError: true,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Register the upsert_contact write tool.
|
|
128
|
+
* Call this from registerRelationTools — same server/client instance.
|
|
129
|
+
*/
|
|
130
|
+
export function registerContactWriteTools(server, client) {
|
|
131
|
+
/**
|
|
132
|
+
* upsert_contact
|
|
133
|
+
*
|
|
134
|
+
* Create a new contact or update an existing one in Yuki.
|
|
135
|
+
* When a contactCode is provided and matches an existing record, Yuki
|
|
136
|
+
* will update that contact. Otherwise a new contact is created.
|
|
137
|
+
*
|
|
138
|
+
* Use this before process_sales_invoice or process_purchase_invoice when:
|
|
139
|
+
* - A new customer or supplier needs to be registered
|
|
140
|
+
* - Contact details (IBAN, address, VAT number) need to be updated
|
|
141
|
+
* - You want to ensure a contact exists before linking invoices to it
|
|
142
|
+
*
|
|
143
|
+
* Note: Contact.asmx uses sessionID / domainID (uppercase D).
|
|
144
|
+
* Rate cost: 1 request.
|
|
145
|
+
*/
|
|
146
|
+
server.registerTool("upsert_contact", {
|
|
147
|
+
description: "Create or update a contact (customer or supplier) in Yuki. " +
|
|
148
|
+
"When contactCode matches an existing record it will be updated; otherwise a new contact is created. " +
|
|
149
|
+
"Returns the Yuki response with the contact ID.",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
contactCode: z.string().optional()
|
|
152
|
+
.describe("Unique contact code. Used to identify and update existing contacts."),
|
|
153
|
+
fullName: z.string().describe("Full company or person name"),
|
|
154
|
+
firstName: z.string().optional(),
|
|
155
|
+
middleName: z.string().optional(),
|
|
156
|
+
lastName: z.string().optional(),
|
|
157
|
+
contactType: z.enum(["Debtor", "Creditor", "Both", "Person"]).optional().default("Both")
|
|
158
|
+
.describe("Role: Debtor = customer, Creditor = supplier, Both = customer & supplier"),
|
|
159
|
+
emailAddress: z.string().optional(),
|
|
160
|
+
phone: z.string().optional().describe("Main phone number"),
|
|
161
|
+
mobile: z.string().optional(),
|
|
162
|
+
countryCode: z.string().optional().default("NL").describe("ISO 3166-1 alpha-2 country code"),
|
|
163
|
+
city: z.string().optional(),
|
|
164
|
+
zipcode: z.string().optional(),
|
|
165
|
+
addressLine1: z.string().optional(),
|
|
166
|
+
addressLine2: z.string().optional(),
|
|
167
|
+
vatNumber: z.string().optional().describe("VAT / BTW number (e.g. 'NL123456789B01')"),
|
|
168
|
+
cocNumber: z.string().optional().describe("KvK / Chamber of Commerce number"),
|
|
169
|
+
bankAccount: z.string().optional().describe("IBAN bank account number"),
|
|
170
|
+
bic: z.string().optional().describe("BIC / SWIFT code"),
|
|
171
|
+
website: z.string().optional(),
|
|
172
|
+
domainId: z.string().optional()
|
|
173
|
+
.describe("Domain ID (GUID). Defaults to YUKI_DOMAIN_ID env var."),
|
|
174
|
+
},
|
|
175
|
+
}, async ({ contactCode, fullName, firstName, middleName, lastName, contactType, emailAddress, phone, mobile, countryCode, city, zipcode, addressLine1, addressLine2, vatNumber, cocNumber, bankAccount, bic, website, domainId }) => {
|
|
176
|
+
try {
|
|
177
|
+
const domain = domainId ?? client.defaultDomainId;
|
|
178
|
+
if (!domain)
|
|
179
|
+
throw new Error("domainId is required (or set YUKI_DOMAIN_ID env var)");
|
|
180
|
+
const sessionID = await client.getSessionID();
|
|
181
|
+
const xmlDoc = buildContactXml({ contactCode, fullName, firstName, middleName,
|
|
182
|
+
lastName, contactType, emailAddress, phone, mobile, countryCode, city,
|
|
183
|
+
zipcode, addressLine1, addressLine2, vatNumber, cocNumber,
|
|
184
|
+
bankAccount, bic, website });
|
|
185
|
+
// Contact.asmx uses sessionID / domainID (uppercase D)
|
|
186
|
+
const result = await client.callSoap({
|
|
187
|
+
service: "Contact.asmx",
|
|
188
|
+
method: "UpdateContact",
|
|
189
|
+
params: {
|
|
190
|
+
sessionID,
|
|
191
|
+
domainID: domain,
|
|
192
|
+
xmlDoc: new XmlValue(xmlDoc),
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text",
|
|
197
|
+
text: JSON.stringify({ success: true, fullName, contactCode, result }, null, 2) }],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
202
|
+
return {
|
|
203
|
+
content: [{ type: "text",
|
|
204
|
+
text: JSON.stringify({ success: false, error: message }, null, 2) }],
|
|
205
|
+
isError: true,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
// ── XML document builder ──────────────────────────────────────────────────────
|
|
211
|
+
/** Build the xmlDoc string for UpdateContact. */
|
|
212
|
+
function buildContactXml(args) {
|
|
213
|
+
const x = escapeXml;
|
|
214
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
215
|
+
<Contacts xmlns="urn:xmlns:http://www.theyukicompany.com:contacts"
|
|
216
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
217
|
+
<Contact>
|
|
218
|
+
${args.contactCode ? `<ContactCode>${x(args.contactCode)}</ContactCode>` : ""}
|
|
219
|
+
<FullName>${x(args.fullName)}</FullName>
|
|
220
|
+
${args.firstName ? `<FirstName>${x(args.firstName)}</FirstName>` : ""}
|
|
221
|
+
${args.middleName ? `<MiddleName>${x(args.middleName)}</MiddleName>` : ""}
|
|
222
|
+
${args.lastName ? `<LastName>${x(args.lastName)}</LastName>` : ""}
|
|
223
|
+
${args.contactType ? `<ContactType>${x(args.contactType)}</ContactType>` : ""}
|
|
224
|
+
${args.emailAddress ? `<EmailAddress>${x(args.emailAddress)}</EmailAddress>` : ""}
|
|
225
|
+
${args.phone ? `<PhoneHome>${x(args.phone)}</PhoneHome>` : ""}
|
|
226
|
+
${args.mobile ? `<MobileHome>${x(args.mobile)}</MobileHome>` : ""}
|
|
227
|
+
${args.website ? `<Website>${x(args.website)}</Website>` : ""}
|
|
228
|
+
${args.countryCode ? `<CountryCode>${x(args.countryCode)}</CountryCode>` : ""}
|
|
229
|
+
${args.city ? `<City>${x(args.city)}</City>` : ""}
|
|
230
|
+
${args.zipcode ? `<Zipcode>${x(args.zipcode)}</Zipcode>` : ""}
|
|
231
|
+
${args.addressLine1 ? `<AddressLine_1>${x(args.addressLine1)}</AddressLine_1>` : ""}
|
|
232
|
+
${args.addressLine2 ? `<AddressLine_2>${x(args.addressLine2)}</AddressLine_2>` : ""}
|
|
233
|
+
${args.vatNumber ? `<VATNumber>${x(args.vatNumber)}</VATNumber>` : ""}
|
|
234
|
+
${args.cocNumber ? `<CoCNumber>${x(args.cocNumber)}</CoCNumber>` : ""}
|
|
235
|
+
${args.bankAccount ? `<BankAccount>${x(args.bankAccount)}</BankAccount>` : ""}
|
|
236
|
+
${args.bic ? `<BIC>${x(args.bic)}</BIC>` : ""}
|
|
237
|
+
</Contact>
|
|
238
|
+
</Contacts>`;
|
|
239
|
+
}
|
|
240
|
+
/** Unwrap the parsed SOAP result for contacts into a flat array. */
|
|
241
|
+
function normalizeContacts(result) {
|
|
242
|
+
if (!result)
|
|
243
|
+
return [];
|
|
244
|
+
if (Array.isArray(result))
|
|
245
|
+
return result;
|
|
246
|
+
const rec = result;
|
|
247
|
+
const wrappers = ["Contacts", "contacts", "Relations", "relations"];
|
|
248
|
+
const itemTags = ["Contact", "contact", "Relation", "relation"];
|
|
249
|
+
for (const wrapper of wrappers) {
|
|
250
|
+
const c = rec[wrapper];
|
|
251
|
+
if (!c)
|
|
252
|
+
continue;
|
|
253
|
+
if (Array.isArray(c))
|
|
254
|
+
return c;
|
|
255
|
+
const inner = c;
|
|
256
|
+
for (const tag of itemTags) {
|
|
257
|
+
if (Array.isArray(inner[tag]))
|
|
258
|
+
return inner[tag];
|
|
259
|
+
if (inner[tag])
|
|
260
|
+
return [inner[tag]];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
for (const tag of itemTags) {
|
|
264
|
+
if (Array.isArray(rec[tag]))
|
|
265
|
+
return rec[tag];
|
|
266
|
+
if (rec[tag])
|
|
267
|
+
return [rec[tag]];
|
|
268
|
+
}
|
|
269
|
+
return [result];
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=relations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.js","sourceRoot":"","sources":["../../src/tools/relations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { YukiClient } from "../yuki-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register tools for retrieving GL account transactions from Yuki.
|
|
5
|
+
*
|
|
6
|
+
* Yuki exposes transactions per GL account code (e.g. "1200" for a bank account).
|
|
7
|
+
* Use get_gl_accounts first to find the right GL account code for a bank account.
|
|
8
|
+
*
|
|
9
|
+
* Yuki service: Accounting.asmx
|
|
10
|
+
* Method: GLAccountTransactions(sessionID, administrationID, GLAccountCode,
|
|
11
|
+
* StartDate, EndDate)
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerTransactionTools(server: McpServer, client: YukiClient): void;
|
|
14
|
+
/**
|
|
15
|
+
* Register the process_journal write tool.
|
|
16
|
+
* Call this from registerTransactionTools — same server/client instance.
|
|
17
|
+
*/
|
|
18
|
+
export declare function registerJournalWriteTools(server: McpServer, client: YukiClient): void;
|
|
19
|
+
//# sourceMappingURL=transactions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../src/tools/transactions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAuB,MAAM,mBAAmB,CAAC;AAEpE;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,GACjB,IAAI,CA0KN;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,GACjB,IAAI,CAwFN"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { XmlValue, escapeXml } from "../yuki-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register tools for retrieving GL account transactions from Yuki.
|
|
5
|
+
*
|
|
6
|
+
* Yuki exposes transactions per GL account code (e.g. "1200" for a bank account).
|
|
7
|
+
* Use get_gl_accounts first to find the right GL account code for a bank account.
|
|
8
|
+
*
|
|
9
|
+
* Yuki service: Accounting.asmx
|
|
10
|
+
* Method: GLAccountTransactions(sessionID, administrationID, GLAccountCode,
|
|
11
|
+
* StartDate, EndDate)
|
|
12
|
+
*/
|
|
13
|
+
export function registerTransactionTools(server, client) {
|
|
14
|
+
/**
|
|
15
|
+
* get_transactions
|
|
16
|
+
*
|
|
17
|
+
* Retrieve all journal entries / transactions for a specific GL account
|
|
18
|
+
* (e.g. a bank account) within a date range.
|
|
19
|
+
*
|
|
20
|
+
* Returns transaction date, description, debit/credit amounts, and
|
|
21
|
+
* the counterpart GL account. Use this to inspect bank movements or
|
|
22
|
+
* verify bookings for a specific account.
|
|
23
|
+
*
|
|
24
|
+
* To get bank transactions: first call get_gl_accounts to find the GL
|
|
25
|
+
* account code for your bank account (typically in the 1100–1299 range),
|
|
26
|
+
* then pass that code here.
|
|
27
|
+
*
|
|
28
|
+
* Rate cost: 1 request.
|
|
29
|
+
*/
|
|
30
|
+
server.registerTool("get_transactions", {
|
|
31
|
+
description: "Retrieve all journal entries for a specific GL account code within a date range. " +
|
|
32
|
+
"Use this for bank transactions (find the bank GL code with get_gl_accounts first). " +
|
|
33
|
+
"Returns date, description, debit/credit amounts, and counterpart GL account.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
glAccountCode: z
|
|
36
|
+
.string()
|
|
37
|
+
.describe("GL account code to retrieve transactions for (e.g. '1200' for a bank account). " +
|
|
38
|
+
"Use get_gl_accounts to find the right code."),
|
|
39
|
+
startDate: z
|
|
40
|
+
.string()
|
|
41
|
+
.describe("Start date in YYYY-MM-DD format (inclusive)"),
|
|
42
|
+
endDate: z
|
|
43
|
+
.string()
|
|
44
|
+
.describe("End date in YYYY-MM-DD format (inclusive)"),
|
|
45
|
+
administrationId: z
|
|
46
|
+
.string()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe("Administration ID (GUID). Defaults to YUKI_DOMAIN_ID env var."),
|
|
49
|
+
},
|
|
50
|
+
}, async ({ glAccountCode, startDate, endDate, administrationId }) => {
|
|
51
|
+
try {
|
|
52
|
+
const adminId = administrationId ?? client.defaultDomainId;
|
|
53
|
+
if (!adminId) {
|
|
54
|
+
throw new Error("administrationId is required (or set YUKI_DOMAIN_ID env var)");
|
|
55
|
+
}
|
|
56
|
+
const sessionID = await client.getSessionID();
|
|
57
|
+
const result = await client.callSoap({
|
|
58
|
+
service: "Accounting.asmx",
|
|
59
|
+
method: "GLAccountTransactions",
|
|
60
|
+
params: {
|
|
61
|
+
sessionID,
|
|
62
|
+
administrationID: adminId,
|
|
63
|
+
GLAccountCode: glAccountCode,
|
|
64
|
+
StartDate: startDate,
|
|
65
|
+
EndDate: endDate,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const transactions = normalizeTransactions(result);
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: JSON.stringify({
|
|
74
|
+
success: true,
|
|
75
|
+
count: transactions.length,
|
|
76
|
+
glAccountCode,
|
|
77
|
+
period: { startDate, endDate },
|
|
78
|
+
transactions,
|
|
79
|
+
}, null, 2),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: "text",
|
|
90
|
+
text: JSON.stringify({ success: false, error: message }, null, 2),
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
/**
|
|
98
|
+
* get_transaction_details
|
|
99
|
+
*
|
|
100
|
+
* Check whether an outstanding item with a given reference still exists,
|
|
101
|
+
* and retrieve its current status and amount.
|
|
102
|
+
*
|
|
103
|
+
* Use this after get_sales_invoices or get_purchase_invoices to verify
|
|
104
|
+
* the current state of a specific invoice reference.
|
|
105
|
+
*
|
|
106
|
+
* Rate cost: 1 request.
|
|
107
|
+
*/
|
|
108
|
+
server.registerTool("get_transaction_details", {
|
|
109
|
+
description: "Check whether an outstanding item (invoice) with a given reference still exists in Yuki " +
|
|
110
|
+
"and retrieve its current open amount and status.",
|
|
111
|
+
inputSchema: {
|
|
112
|
+
reference: z
|
|
113
|
+
.string()
|
|
114
|
+
.describe("Invoice reference number as shown in Yuki (e.g. '2024-0042')"),
|
|
115
|
+
administrationId: z
|
|
116
|
+
.string()
|
|
117
|
+
.optional()
|
|
118
|
+
.describe("Administration ID (GUID). Defaults to YUKI_DOMAIN_ID env var."),
|
|
119
|
+
},
|
|
120
|
+
}, async ({ reference, administrationId }) => {
|
|
121
|
+
try {
|
|
122
|
+
const adminId = administrationId ?? client.defaultDomainId;
|
|
123
|
+
const sessionID = await client.getSessionID();
|
|
124
|
+
// CheckOutstandingItemAdmin checks a specific reference within an administration
|
|
125
|
+
const result = await client.callSoap({
|
|
126
|
+
service: "Accounting.asmx",
|
|
127
|
+
method: "CheckOutstandingItemAdmin",
|
|
128
|
+
params: {
|
|
129
|
+
sessionID,
|
|
130
|
+
adminID: adminId,
|
|
131
|
+
Reference: reference,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
content: [
|
|
136
|
+
{
|
|
137
|
+
type: "text",
|
|
138
|
+
text: JSON.stringify({ success: true, reference, result }, null, 2),
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: JSON.stringify({ success: false, error: message }, null, 2),
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Register the process_journal write tool.
|
|
159
|
+
* Call this from registerTransactionTools — same server/client instance.
|
|
160
|
+
*/
|
|
161
|
+
export function registerJournalWriteTools(server, client) {
|
|
162
|
+
/**
|
|
163
|
+
* process_journal
|
|
164
|
+
*
|
|
165
|
+
* Post a general journal entry (memoriaal) in Yuki.
|
|
166
|
+
* This is the primary method for recording bank reconciliation,
|
|
167
|
+
* corrections, and any double-entry booking not covered by
|
|
168
|
+
* ProcessSalesInvoices or ProcessPurchaseInvoices.
|
|
169
|
+
*
|
|
170
|
+
* CRITICAL RULE — Yuki will reject the entry if the amounts don't balance:
|
|
171
|
+
* - The sum of all entry amounts must equal exactly 0.00
|
|
172
|
+
* - Positive amounts = debit, negative amounts = credit
|
|
173
|
+
* - Example bank receipt: bank account +100.00, debtor account -100.00
|
|
174
|
+
*
|
|
175
|
+
* Use this for:
|
|
176
|
+
* - Bank reconciliation: match a bank mutation to an outstanding invoice
|
|
177
|
+
* - Opening balances
|
|
178
|
+
* - Corrections and reclassifications
|
|
179
|
+
* - Any custom double-entry booking
|
|
180
|
+
*
|
|
181
|
+
* Rate cost: 1 request.
|
|
182
|
+
*/
|
|
183
|
+
server.registerTool("process_journal", {
|
|
184
|
+
description: "Post a general journal entry (memoriaal) in Yuki for bank reconciliation, corrections, or custom bookings. " +
|
|
185
|
+
"IMPORTANT: all entry amounts must sum to exactly 0 (positive = debit, negative = credit). " +
|
|
186
|
+
"Yuki will reject the entry if this rule is violated.",
|
|
187
|
+
inputSchema: {
|
|
188
|
+
subject: z.string().describe("Document subject / description shown in Yuki (e.g. 'Bank reconciliation April 2024')"),
|
|
189
|
+
journalType: z.enum(["GeneralJournal", "EndOfYearCorrection", "FiscalCorrection"])
|
|
190
|
+
.optional().default("GeneralJournal")
|
|
191
|
+
.describe("Journal type. Year-end and fiscal corrections must have entry dates on the last day of the financial year."),
|
|
192
|
+
entries: z.array(z.object({
|
|
193
|
+
entryDate: z.string().describe("Entry date in YYYY-MM-DD format"),
|
|
194
|
+
glAccount: z.string().describe("GL account code (e.g. '1200' for bank, '1300' for debtors)"),
|
|
195
|
+
amount: z.number().describe("Amount: positive = debit, negative = credit. All entries must sum to 0."),
|
|
196
|
+
description: z.string().optional().describe("Line description (max 256 characters)"),
|
|
197
|
+
contactCode: z.string().optional().describe("Yuki contact code to link this entry to a relation"),
|
|
198
|
+
contactName: z.string().optional().describe("Contact name (used if contactCode is not provided)"),
|
|
199
|
+
})).min(2).describe("Journal entries — must contain at least 2 lines and sum to zero"),
|
|
200
|
+
administrationId: z.string().optional().describe("Administration ID (GUID). Defaults to YUKI_DOMAIN_ID env var."),
|
|
201
|
+
},
|
|
202
|
+
}, async ({ subject, journalType, entries, administrationId }) => {
|
|
203
|
+
try {
|
|
204
|
+
const adminId = administrationId ?? client.defaultDomainId;
|
|
205
|
+
if (!adminId)
|
|
206
|
+
throw new Error("administrationId is required (or set YUKI_DOMAIN_ID env var)");
|
|
207
|
+
// Validate balance before sending — save a request on obvious errors
|
|
208
|
+
const total = entries.reduce((sum, e) => sum + e.amount, 0);
|
|
209
|
+
if (Math.abs(total) > 0.001) {
|
|
210
|
+
throw new Error(`Journal entries do not balance: sum is ${total.toFixed(2)} (must be 0.00). ` +
|
|
211
|
+
`Check your debit/credit amounts.`);
|
|
212
|
+
}
|
|
213
|
+
const sessionID = await client.getSessionID();
|
|
214
|
+
const xmlDoc = buildJournalXml({ administrationId: adminId, subject,
|
|
215
|
+
journalType, entries });
|
|
216
|
+
// Accounting.asmx uses sessionID / administrationID (uppercase D)
|
|
217
|
+
const result = await client.callSoap({
|
|
218
|
+
service: "Accounting.asmx",
|
|
219
|
+
method: "ProcessJournal",
|
|
220
|
+
params: {
|
|
221
|
+
sessionID,
|
|
222
|
+
administrationID: adminId,
|
|
223
|
+
xmlDoc: new XmlValue(xmlDoc),
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
return {
|
|
227
|
+
content: [{ type: "text",
|
|
228
|
+
text: JSON.stringify({ success: true, subject, entryCount: entries.length, result }, null, 2) }],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
233
|
+
return {
|
|
234
|
+
content: [{ type: "text",
|
|
235
|
+
text: JSON.stringify({ success: false, error: message }, null, 2) }],
|
|
236
|
+
isError: true,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
// ── XML document builder ──────────────────────────────────────────────────────
|
|
242
|
+
/** Build the xmlDoc string for ProcessJournal. */
|
|
243
|
+
function buildJournalXml(args) {
|
|
244
|
+
const x = escapeXml;
|
|
245
|
+
const entriesXml = args.entries.map(e => `
|
|
246
|
+
<JournalEntry>
|
|
247
|
+
<EntryDate>${x(e.entryDate)}</EntryDate>
|
|
248
|
+
<GLAccount>${x(e.glAccount)}</GLAccount>
|
|
249
|
+
<Amount>${e.amount}</Amount>
|
|
250
|
+
${e.description ? `<Description>${x(e.description.slice(0, 256))}</Description>` : ""}
|
|
251
|
+
${e.contactCode ? `<ContactCode>${x(e.contactCode)}</ContactCode>` : ""}
|
|
252
|
+
${!e.contactCode && e.contactName ? `<ContactName>${x(e.contactName)}</ContactName>` : ""}
|
|
253
|
+
</JournalEntry>`).join("");
|
|
254
|
+
return `<?xml version="1.0" encoding="utf-8"?>
|
|
255
|
+
<Journal xmlns="urn:xmlns:http://www.theyukicompany.com:journal"
|
|
256
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
257
|
+
<AdministrationID>${x(args.administrationId)}</AdministrationID>
|
|
258
|
+
<DocumentSubject>${x(args.subject)}</DocumentSubject>
|
|
259
|
+
<JournalType>${x(args.journalType ?? "GeneralJournal")}</JournalType>
|
|
260
|
+
${entriesXml}
|
|
261
|
+
</Journal>`;
|
|
262
|
+
}
|
|
263
|
+
/** Unwrap the parsed SOAP GL transaction result into a flat array. */
|
|
264
|
+
function normalizeTransactions(result) {
|
|
265
|
+
if (!result)
|
|
266
|
+
return [];
|
|
267
|
+
if (Array.isArray(result))
|
|
268
|
+
return result;
|
|
269
|
+
const rec = result;
|
|
270
|
+
const wrappers = ["Transactions", "GLTransactions", "Rows"];
|
|
271
|
+
const itemTags = ["Transaction", "GLTransaction", "Row"];
|
|
272
|
+
for (const wrapper of wrappers) {
|
|
273
|
+
const c = rec[wrapper];
|
|
274
|
+
if (!c)
|
|
275
|
+
continue;
|
|
276
|
+
if (Array.isArray(c))
|
|
277
|
+
return c;
|
|
278
|
+
const inner = c;
|
|
279
|
+
for (const tag of itemTags) {
|
|
280
|
+
if (Array.isArray(inner[tag]))
|
|
281
|
+
return inner[tag];
|
|
282
|
+
if (inner[tag])
|
|
283
|
+
return [inner[tag]];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
for (const tag of itemTags) {
|
|
287
|
+
if (Array.isArray(rec[tag]))
|
|
288
|
+
return rec[tag];
|
|
289
|
+
if (rec[tag])
|
|
290
|
+
return [rec[tag]];
|
|
291
|
+
}
|
|
292
|
+
return [result];
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=transactions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactions.js","sourceRoot":"","sources":["../../src/tools/transactions.ts"],"names":[],"mappings":"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"}
|