kassza 0.11.0 → 0.13.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/README.md +376 -111
- package/agents/README.md +20 -5
- package/agents/api.md +165 -2
- package/agents/pitfalls.md +16 -3
- package/agents/recipes.md +215 -45
- package/agents/skills/kassza/SKILL.md +11 -8
- package/dist/cli.cjs +552 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +552 -0
- package/dist/client-6MkeuFqD.d.ts +268 -0
- package/dist/client-B5FryKxJ.d.cts +268 -0
- package/dist/client-C-PdfL_P.cjs +1275 -0
- package/dist/client-CfaS1-Jx.js +1228 -0
- package/dist/context-BxQlIlug.d.ts +425 -0
- package/dist/context-DXNbTPwz.d.cts +425 -0
- package/dist/create-once-Cz621l8r.js +330 -0
- package/dist/create-once-DgMtKKcG.cjs +395 -0
- package/dist/crypto-Dpn4FHQ6.cjs +65 -0
- package/dist/crypto-usDg8L2K.js +36 -0
- package/dist/data-link/index.cjs +232 -0
- package/dist/data-link/index.d.cts +79 -0
- package/dist/data-link/index.d.ts +79 -0
- package/dist/data-link/index.js +224 -0
- package/dist/{dates-DtHzwNU6.d.cts → dates-9zvESP0i.d.cts} +2 -1
- package/dist/{dates-DtHzwNU6.d.ts → dates-9zvESP0i.d.ts} +2 -1
- package/dist/{dates-PPxH0n5H.cjs → dates-CPgBodkP.cjs} +44 -1
- package/dist/{dates-D2XebOIg.js → dates-CXyj7Azy.js} +39 -2
- package/dist/delegation/index.cjs +396 -0
- package/dist/delegation/index.d.cts +100 -0
- package/dist/delegation/index.d.ts +100 -0
- package/dist/delegation/index.js +378 -0
- package/dist/{errors-DapdV5DK.js → errors-DLrb0g64.js} +122 -5
- package/dist/{errors-B0QJhUW1.cjs → errors-DxeuTslP.cjs} +127 -4
- package/dist/get-Byc9T5WO.d.cts +151 -0
- package/dist/get-Byc9T5WO.d.ts +151 -0
- package/dist/get-Csm2n0rZ.cjs +445 -0
- package/dist/get-DT3JEgQ6.js +380 -0
- package/dist/index.cjs +21 -1410
- package/dist/index.d.cts +20 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.js +8 -1409
- package/dist/ipn/index.cjs +1 -1
- package/dist/ipn/index.js +1 -1
- package/dist/issue-AGRohSm7.cjs +914 -0
- package/dist/issue-DtSLpVdJ.js +777 -0
- package/dist/json-4y4E2xxS.cjs +61 -0
- package/dist/json-B70TwoX1.d.cts +4 -0
- package/dist/json-B70TwoX1.d.ts +4 -0
- package/dist/json-DjnqnaJK.js +32 -0
- package/dist/mcp/index.cjs +10 -0
- package/dist/mcp/index.d.cts +71 -0
- package/dist/mcp/index.d.ts +71 -0
- package/dist/mcp/index.js +2 -0
- package/dist/money/index.cjs +12 -10
- package/dist/money/index.js +3 -1
- package/dist/{money-DkiGukAw.cjs → money-BxytJW6f.cjs} +42 -177
- package/dist/{money-C9j7nBNP.js → money-t7uy1n91.js} +29 -110
- package/dist/nav/index.cjs +1046 -0
- package/dist/nav/index.d.cts +247 -0
- package/dist/nav/index.d.ts +247 -0
- package/dist/nav/index.js +1012 -0
- package/dist/nav-BEwcX7-N.cjs +203 -0
- package/dist/nav-CK1WgYo7.js +156 -0
- package/dist/parse-BJj-aqKy.cjs +380 -0
- package/dist/parse-C608ehqM.js +291 -0
- package/dist/payments/barion.cjs +208 -0
- package/dist/payments/barion.d.cts +28 -0
- package/dist/payments/barion.d.ts +28 -0
- package/dist/payments/barion.js +199 -0
- package/dist/payments/index.cjs +16 -0
- package/dist/payments/index.d.cts +31 -0
- package/dist/payments/index.d.ts +31 -0
- package/dist/payments/index.js +3 -0
- package/dist/payments/paypal.cjs +351 -0
- package/dist/payments/paypal.d.cts +59 -0
- package/dist/payments/paypal.d.ts +59 -0
- package/dist/payments/paypal.js +337 -0
- package/dist/payments/revolut.cjs +261 -0
- package/dist/payments/revolut.d.cts +48 -0
- package/dist/payments/revolut.d.ts +48 -0
- package/dist/payments/revolut.js +249 -0
- package/dist/payments/simplepay.cjs +313 -0
- package/dist/payments/simplepay.d.cts +86 -0
- package/dist/payments/simplepay.d.ts +86 -0
- package/dist/payments/simplepay.js +299 -0
- package/dist/payments/stripe.cjs +315 -0
- package/dist/payments/stripe.d.cts +49 -0
- package/dist/payments/stripe.d.ts +49 -0
- package/dist/payments/stripe.js +303 -0
- package/dist/reports/index.cjs +103 -0
- package/dist/reports/index.d.cts +70 -0
- package/dist/reports/index.d.ts +70 -0
- package/dist/reports/index.js +94 -0
- package/dist/request-body-CH6ZGOrq.cjs +34 -0
- package/dist/request-body-uZHMRlQr.js +29 -0
- package/dist/rounding-BLyHjjI5.js +20 -0
- package/dist/rounding-TxoWy2ww.cjs +37 -0
- package/dist/serialize-BXOww1Uq.cjs +286 -0
- package/dist/serialize-DSE_o9RK.js +215 -0
- package/dist/server-C6PoNkdD.cjs +1557 -0
- package/dist/server-Cd8HB661.js +1504 -0
- package/dist/storage/fs.cjs +1 -1
- package/dist/storage/fs.js +1 -1
- package/dist/storage/index.cjs +2 -2
- package/dist/storage/index.d.cts +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js +2 -2
- package/dist/tax-number-BYFkBp4A.cjs +141 -0
- package/dist/tax-number-BdqKsxNR.js +76 -0
- package/dist/testing/index.cjs +1656 -84
- package/dist/testing/index.d.cts +203 -3
- package/dist/testing/index.d.ts +203 -3
- package/dist/testing/index.js +1653 -83
- package/dist/types-BVeR7KyU.d.cts +56 -0
- package/dist/types-BVeR7KyU.d.ts +56 -0
- package/dist/types-DH8QoNZK.d.cts +135 -0
- package/dist/types-k--zHf4l.d.ts +135 -0
- package/dist/validators/index.cjs +14 -88
- package/dist/validators/index.js +1 -75
- package/dist/vat-g35srbNG.cjs +116 -0
- package/dist/vat-v_dcWCto.js +81 -0
- package/dist/webhook-81giRMdQ.cjs +223 -0
- package/dist/webhook-CpCMHhn2.d.ts +6 -0
- package/dist/webhook-CpMJuP6c.d.cts +6 -0
- package/dist/webhook-CrEGohbg.js +152 -0
- package/llms.txt +4 -4
- package/package.json +148 -2
- package/dist/client-B9kbKKBf.d.cts +0 -748
- package/dist/client-zv4enyq7.d.ts +0 -748
- package/dist/create-5YkUcJSe.js +0 -957
- package/dist/create-DZsl3Z4m.cjs +0 -1178
|
@@ -0,0 +1,1557 @@
|
|
|
1
|
+
const require_create_once = require("./create-once-DgMtKKcG.cjs");
|
|
2
|
+
const require_errors = require("./errors-DxeuTslP.cjs");
|
|
3
|
+
const require_client = require("./client-C-PdfL_P.cjs");
|
|
4
|
+
const require_dates = require("./dates-CPgBodkP.cjs");
|
|
5
|
+
const require_vat = require("./vat-g35srbNG.cjs");
|
|
6
|
+
const require_money = require("./money-BxytJW6f.cjs");
|
|
7
|
+
const require_issue = require("./issue-AGRohSm7.cjs");
|
|
8
|
+
const require_crypto = require("./crypto-Dpn4FHQ6.cjs");
|
|
9
|
+
const require_nav = require("./nav-BEwcX7-N.cjs");
|
|
10
|
+
//#region src/mcp/protocol.ts
|
|
11
|
+
const MCP_MODERN_VERSIONS = ["2026-07-28"];
|
|
12
|
+
const MCP_LEGACY_VERSIONS = [
|
|
13
|
+
"2025-11-25",
|
|
14
|
+
"2025-06-18",
|
|
15
|
+
"2025-03-26",
|
|
16
|
+
"2024-11-05"
|
|
17
|
+
];
|
|
18
|
+
const MCP_SUPPORTED_VERSIONS = [...MCP_MODERN_VERSIONS, ...MCP_LEGACY_VERSIONS];
|
|
19
|
+
const META_PROTOCOL_VERSION = "io.modelcontextprotocol/protocolVersion";
|
|
20
|
+
const META_CLIENT_CAPABILITIES = "io.modelcontextprotocol/clientCapabilities";
|
|
21
|
+
const META_SERVER_INFO = "io.modelcontextprotocol/serverInfo";
|
|
22
|
+
const JSON_RPC_PARSE_ERROR = -32700;
|
|
23
|
+
const JSON_RPC_INVALID_REQUEST = -32600;
|
|
24
|
+
const JSON_RPC_METHOD_NOT_FOUND = -32601;
|
|
25
|
+
const JSON_RPC_INVALID_PARAMS = -32602;
|
|
26
|
+
const JSON_RPC_INTERNAL_ERROR = -32603;
|
|
27
|
+
const MCP_UNSUPPORTED_PROTOCOL_VERSION = -32022;
|
|
28
|
+
var McpProtocolError = class extends Error {
|
|
29
|
+
name = "McpProtocolError";
|
|
30
|
+
code;
|
|
31
|
+
id;
|
|
32
|
+
data;
|
|
33
|
+
constructor(code, message, options = {}) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.code = code;
|
|
36
|
+
this.id = options.id ?? null;
|
|
37
|
+
this.data = options.data;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
function isJsonObject(value) {
|
|
41
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
function isValidId(value) {
|
|
44
|
+
return typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
45
|
+
}
|
|
46
|
+
function readableId(value) {
|
|
47
|
+
return isValidId(value) ? value : null;
|
|
48
|
+
}
|
|
49
|
+
function failure(id, code, message, data) {
|
|
50
|
+
return {
|
|
51
|
+
jsonrpc: "2.0",
|
|
52
|
+
id,
|
|
53
|
+
error: data === void 0 ? {
|
|
54
|
+
code,
|
|
55
|
+
message
|
|
56
|
+
} : {
|
|
57
|
+
code,
|
|
58
|
+
message,
|
|
59
|
+
data
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function success(id, result) {
|
|
64
|
+
return {
|
|
65
|
+
jsonrpc: "2.0",
|
|
66
|
+
id,
|
|
67
|
+
result
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function parseMcpMessage(value) {
|
|
71
|
+
if (!isJsonObject(value) || value.jsonrpc !== "2.0") throw new McpProtocolError(JSON_RPC_INVALID_REQUEST, "Érvénytelen JSON-RPC 2.0 üzenet.", { id: isJsonObject(value) ? readableId(value.id) : null });
|
|
72
|
+
if (typeof value.method !== "string") {
|
|
73
|
+
if ("result" in value || "error" in value) return { kind: "ignored" };
|
|
74
|
+
throw new McpProtocolError(JSON_RPC_INVALID_REQUEST, "Hiányzik a method mező.", { id: readableId(value.id) });
|
|
75
|
+
}
|
|
76
|
+
if (!("id" in value)) return {
|
|
77
|
+
kind: "notification",
|
|
78
|
+
method: value.method
|
|
79
|
+
};
|
|
80
|
+
if (!isValidId(value.id)) throw new McpProtocolError(JSON_RPC_INVALID_REQUEST, "A kérés azonosítója (id) csak szöveg vagy szám lehet, null nem.");
|
|
81
|
+
if (value.params !== void 0 && !isJsonObject(value.params)) throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, "A params mezőnek objektumnak kell lennie.", { id: value.id });
|
|
82
|
+
return {
|
|
83
|
+
kind: "request",
|
|
84
|
+
id: value.id,
|
|
85
|
+
method: value.method,
|
|
86
|
+
params: value.params ?? {}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function protocolFailure(error) {
|
|
90
|
+
return failure(error.id, error.code, error.message, error.data);
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/mcp/arguments.ts
|
|
94
|
+
var ToolInputError = class extends Error {
|
|
95
|
+
name = "ToolInputError";
|
|
96
|
+
};
|
|
97
|
+
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
98
|
+
const NUMERIC_TEXT = /^-?\d+(?:[.,]\d+)?$/;
|
|
99
|
+
function field(path, key) {
|
|
100
|
+
return path === "" ? key : `${path}.${key}`;
|
|
101
|
+
}
|
|
102
|
+
function objectArg(value, path) {
|
|
103
|
+
if (!isJsonObject(value)) throw new ToolInputError(`A(z) ${path} mezőnek objektumnak kell lennie.`);
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
function assertKnownKeys(value, allowed, path) {
|
|
107
|
+
const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
108
|
+
if (unknown.length === 0) return;
|
|
109
|
+
throw new ToolInputError(`Ismeretlen mező ${path === "" ? "a bemenetben" : `a(z) ${path} objektumban`}: ${unknown.join(", ")}. Megengedett mezők: ${allowed.join(", ")}.`);
|
|
110
|
+
}
|
|
111
|
+
function optionalString(value, key, path) {
|
|
112
|
+
const raw = value[key];
|
|
113
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
114
|
+
if (typeof raw !== "string") throw new ToolInputError(`A(z) ${field(path, key)} mezőnek szövegnek kell lennie.`);
|
|
115
|
+
const trimmed = raw.trim();
|
|
116
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
117
|
+
}
|
|
118
|
+
function requiredString(value, key, path) {
|
|
119
|
+
const result = optionalString(value, key, path);
|
|
120
|
+
if (result === void 0) throw new ToolInputError(`A(z) ${field(path, key)} mező kötelező, és nem lehet üres.`);
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
function optionalNumber(value, key, path) {
|
|
124
|
+
const raw = value[key];
|
|
125
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
126
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) throw new ToolInputError(`A(z) ${field(path, key)} mezőnek véges számnak kell lennie.`);
|
|
127
|
+
return raw;
|
|
128
|
+
}
|
|
129
|
+
function optionalInteger(value, key, path) {
|
|
130
|
+
const result = optionalNumber(value, key, path);
|
|
131
|
+
if (result !== void 0 && !Number.isInteger(result)) throw new ToolInputError(`A(z) ${field(path, key)} mezőnek egész számnak kell lennie.`);
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
function optionalBoolean(value, key, path) {
|
|
135
|
+
const raw = value[key];
|
|
136
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
137
|
+
if (typeof raw !== "boolean") throw new ToolInputError(`A(z) ${field(path, key)} mezőnek true vagy false értéknek kell lennie.`);
|
|
138
|
+
return raw;
|
|
139
|
+
}
|
|
140
|
+
function optionalDate(value, key, path) {
|
|
141
|
+
const result = optionalString(value, key, path);
|
|
142
|
+
if (result !== void 0 && !DATE_PATTERN.test(result)) throw new ToolInputError(`A(z) ${field(path, key)} mezőt ÉÉÉÉ-HH-NN formában add meg (például 2026-10-02).`);
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
function optionalEnum(value, key, path, allowed) {
|
|
146
|
+
const raw = optionalString(value, key, path);
|
|
147
|
+
if (raw === void 0) return void 0;
|
|
148
|
+
const match = allowed.find((candidate) => candidate === raw);
|
|
149
|
+
if (match === void 0) throw new ToolInputError(`A(z) ${field(path, key)} értéke nem megengedett: ${raw}. Lehetséges értékek: ${allowed.join(", ")}.`);
|
|
150
|
+
return match;
|
|
151
|
+
}
|
|
152
|
+
function optionalArray(value, key, path) {
|
|
153
|
+
const raw = value[key];
|
|
154
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
155
|
+
if (!Array.isArray(raw)) throw new ToolInputError(`A(z) ${field(path, key)} mezőnek tömbnek kell lennie.`);
|
|
156
|
+
return raw;
|
|
157
|
+
}
|
|
158
|
+
function requiredArray(value, key, path, limits) {
|
|
159
|
+
const result = optionalArray(value, key, path) ?? [];
|
|
160
|
+
if (result.length < limits.min || result.length > limits.max) throw new ToolInputError(`A(z) ${field(path, key)} tömbben ${limits.min} és ${limits.max} közötti számú elem lehet, kapott: ${result.length}.`);
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
function numericFromText(value) {
|
|
164
|
+
if (typeof value !== "string" || !NUMERIC_TEXT.test(value.trim())) return value;
|
|
165
|
+
return Number(value.trim().replace(",", "."));
|
|
166
|
+
}
|
|
167
|
+
function vatFailure(value, path) {
|
|
168
|
+
return new ToolInputError(`Ismeretlen áfakulcs a(z) ${path} mezőben: ${JSON.stringify(value)}. Add meg számként (például 27, 18, 5 vagy 0) vagy kódként (például "AAM", "TAM", "EUT", "EUKT", "F.AFA", "K.AFA").`);
|
|
169
|
+
}
|
|
170
|
+
function invoiceVat(value, path) {
|
|
171
|
+
const candidate = numericFromText(value);
|
|
172
|
+
if (require_vat.isVatRate(candidate)) return candidate;
|
|
173
|
+
throw vatFailure(value, path);
|
|
174
|
+
}
|
|
175
|
+
function receiptVat(value, path) {
|
|
176
|
+
const candidate = numericFromText(value);
|
|
177
|
+
if (require_vat.isVatRate(candidate)) return candidate;
|
|
178
|
+
const code = require_issue.RECEIPT_ONLY_VAT_CODES.find((entry) => entry === candidate);
|
|
179
|
+
if (code !== void 0) return code;
|
|
180
|
+
throw vatFailure(value, path);
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/mcp/confirmation.ts
|
|
184
|
+
const DEFAULT_CONFIRMATION_TTL_MS = 9e5;
|
|
185
|
+
const SECRET_BYTES = 32;
|
|
186
|
+
const SIGNATURE_LENGTH = 32;
|
|
187
|
+
const IDEMPOTENCY_KEY_LENGTH = 20;
|
|
188
|
+
const TOKEN_PATTERN = /^([0-9a-z]+)\.([0-9a-f]+)$/;
|
|
189
|
+
function canonicalize(value) {
|
|
190
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
191
|
+
if (typeof value !== "object" || value === null) return value;
|
|
192
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, entry]) => [key, canonicalize(entry)]));
|
|
193
|
+
}
|
|
194
|
+
function canonicalJson(value) {
|
|
195
|
+
return JSON.stringify(canonicalize(value)) ?? "null";
|
|
196
|
+
}
|
|
197
|
+
async function idempotencyKey(action, day, payload) {
|
|
198
|
+
return (await require_crypto.digestHex("SHA-256", `${action}\n${day}\n${canonicalJson(payload)}`)).slice(0, IDEMPOTENCY_KEY_LENGTH).toUpperCase();
|
|
199
|
+
}
|
|
200
|
+
function randomSecret() {
|
|
201
|
+
return crypto.getRandomValues(new Uint8Array(SECRET_BYTES));
|
|
202
|
+
}
|
|
203
|
+
function resolveTtl(ttlMs) {
|
|
204
|
+
if (ttlMs === void 0) return DEFAULT_CONFIRMATION_TTL_MS;
|
|
205
|
+
if (!Number.isInteger(ttlMs) || ttlMs <= 0) throw new TypeError(`A confirmationTtlMs pozitív egész szám legyen, kapott: ${ttlMs}`);
|
|
206
|
+
return ttlMs;
|
|
207
|
+
}
|
|
208
|
+
function createConfirmations(options) {
|
|
209
|
+
const secret = options.secret ?? randomSecret();
|
|
210
|
+
const ttlMs = resolveTtl(options.ttlMs);
|
|
211
|
+
const sign = async (action, expiresAt, payload) => {
|
|
212
|
+
return (await require_crypto.hmacHex("SHA-256", secret, `${action}\n${expiresAt}\n${canonicalJson(payload)}`)).slice(0, SIGNATURE_LENGTH);
|
|
213
|
+
};
|
|
214
|
+
return {
|
|
215
|
+
async issue(action, payload) {
|
|
216
|
+
const expiresAt = options.now().getTime() + ttlMs;
|
|
217
|
+
const signature = await sign(action, expiresAt, payload);
|
|
218
|
+
return {
|
|
219
|
+
token: `${expiresAt.toString(36)}.${signature}`,
|
|
220
|
+
expiresAt: new Date(expiresAt).toISOString()
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
async verify(action, payload, token, previewTool) {
|
|
224
|
+
const again = `Hívd meg újra a ${previewTool} eszközt pontosan ezekkel az adatokkal, mutasd meg az előnézetet a felhasználónak, és a jóváhagyása után az új kóddal próbáld újra.`;
|
|
225
|
+
if (typeof token !== "string" || token.trim() === "") throw new ToolInputError(`Hiányzik a megerősítő kód (confirmation). ${again}`);
|
|
226
|
+
const match = TOKEN_PATTERN.exec(token.trim());
|
|
227
|
+
const expiresAt = match?.[1] === void 0 ? NaN : Number.parseInt(match[1], 36);
|
|
228
|
+
if (!match?.[2] || !Number.isSafeInteger(expiresAt)) throw new ToolInputError(`A megerősítő kód formátuma érvénytelen. ${again}`);
|
|
229
|
+
if (options.now().getTime() > expiresAt) throw new ToolInputError(`A megerősítő kód lejárt. ${again}`);
|
|
230
|
+
const expected = await sign(action, expiresAt, payload);
|
|
231
|
+
if (!require_crypto.timingSafeEqual(expected, match[2])) throw new ToolInputError(`A megerősítő kód nem ehhez a kéréshez tartozik (az adatok eltérnek az előnézettől). ${again}`);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region src/core/version.ts
|
|
237
|
+
const KASSZA_VERSION = "0.12.0";
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/mcp/tool-kit.ts
|
|
240
|
+
const LOCAL_READ = {
|
|
241
|
+
readOnlyHint: true,
|
|
242
|
+
destructiveHint: false,
|
|
243
|
+
idempotentHint: true,
|
|
244
|
+
openWorldHint: false
|
|
245
|
+
};
|
|
246
|
+
const REMOTE_READ = {
|
|
247
|
+
readOnlyHint: true,
|
|
248
|
+
destructiveHint: false,
|
|
249
|
+
idempotentHint: true,
|
|
250
|
+
openWorldHint: true
|
|
251
|
+
};
|
|
252
|
+
const REMOTE_CREATE = {
|
|
253
|
+
readOnlyHint: false,
|
|
254
|
+
destructiveHint: false,
|
|
255
|
+
idempotentHint: true,
|
|
256
|
+
openWorldHint: true
|
|
257
|
+
};
|
|
258
|
+
const REMOTE_REVERSE = {
|
|
259
|
+
readOnlyHint: false,
|
|
260
|
+
destructiveHint: true,
|
|
261
|
+
idempotentHint: true,
|
|
262
|
+
openWorldHint: true
|
|
263
|
+
};
|
|
264
|
+
const amountFormat = new Intl.NumberFormat("hu-HU", { maximumFractionDigits: 2 });
|
|
265
|
+
function money(value, currency) {
|
|
266
|
+
return `${amountFormat.format(value)} ${currency}`;
|
|
267
|
+
}
|
|
268
|
+
function plainJson(value) {
|
|
269
|
+
return JSON.parse(JSON.stringify(value, (_key, entry) => entry instanceof Uint8Array ? void 0 : entry));
|
|
270
|
+
}
|
|
271
|
+
function lines(...parts) {
|
|
272
|
+
return parts.filter((part) => typeof part === "string" && part !== "").join("\n");
|
|
273
|
+
}
|
|
274
|
+
function splitConfirmation(args) {
|
|
275
|
+
const { confirmation, ...payload } = args;
|
|
276
|
+
return {
|
|
277
|
+
payload,
|
|
278
|
+
confirmation
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/mcp/inputs.ts
|
|
283
|
+
const MCP_INVOICE_TYPES = ["invoice", "proforma"];
|
|
284
|
+
const BUYER_KEYS = [
|
|
285
|
+
"name",
|
|
286
|
+
"zip",
|
|
287
|
+
"city",
|
|
288
|
+
"address",
|
|
289
|
+
"country",
|
|
290
|
+
"email",
|
|
291
|
+
"taxNumber",
|
|
292
|
+
"euTaxNumber"
|
|
293
|
+
];
|
|
294
|
+
const ITEM_KEYS = [
|
|
295
|
+
"name",
|
|
296
|
+
"quantity",
|
|
297
|
+
"unit",
|
|
298
|
+
"netUnitPrice",
|
|
299
|
+
"grossUnitPrice",
|
|
300
|
+
"vat",
|
|
301
|
+
"comment"
|
|
302
|
+
];
|
|
303
|
+
const PAYMENT_KEYS = [
|
|
304
|
+
"method",
|
|
305
|
+
"amount",
|
|
306
|
+
"description"
|
|
307
|
+
];
|
|
308
|
+
const INVOICE_INPUT_KEYS = [
|
|
309
|
+
"type",
|
|
310
|
+
"buyer",
|
|
311
|
+
"items",
|
|
312
|
+
"paymentMethod",
|
|
313
|
+
"currency",
|
|
314
|
+
"exchangeRate",
|
|
315
|
+
"exchangeBank",
|
|
316
|
+
"language",
|
|
317
|
+
"issueDate",
|
|
318
|
+
"fulfillmentDate",
|
|
319
|
+
"dueDate",
|
|
320
|
+
"comment",
|
|
321
|
+
"orderNumber",
|
|
322
|
+
"externalId",
|
|
323
|
+
"prefix",
|
|
324
|
+
"paid",
|
|
325
|
+
"eInvoice",
|
|
326
|
+
"sendEmail"
|
|
327
|
+
];
|
|
328
|
+
const RECEIPT_INPUT_KEYS = [
|
|
329
|
+
"items",
|
|
330
|
+
"payments",
|
|
331
|
+
"paymentMethod",
|
|
332
|
+
"currency",
|
|
333
|
+
"exchangeRate",
|
|
334
|
+
"exchangeBank",
|
|
335
|
+
"comment",
|
|
336
|
+
"orderNumber",
|
|
337
|
+
"prefix"
|
|
338
|
+
];
|
|
339
|
+
function unitPriceOf(item, path) {
|
|
340
|
+
const net = optionalNumber(item, "netUnitPrice", path);
|
|
341
|
+
const gross = optionalNumber(item, "grossUnitPrice", path);
|
|
342
|
+
const quantity = optionalNumber(item, "quantity", path);
|
|
343
|
+
if (net === void 0 === (gross === void 0)) throw new ToolInputError(`A(z) ${path} tételnél pontosan az egyiket add meg: netUnitPrice (nettó egységár) vagy grossUnitPrice (bruttó egységár).`);
|
|
344
|
+
return {
|
|
345
|
+
...net === void 0 ? {} : { netUnitPrice: net },
|
|
346
|
+
...gross === void 0 ? {} : { grossUnitPrice: gross },
|
|
347
|
+
...quantity === void 0 ? {} : { quantity }
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function itemObject(value, index) {
|
|
351
|
+
const path = `items[${index}]`;
|
|
352
|
+
const item = objectArg(value, path);
|
|
353
|
+
assertKnownKeys(item, ITEM_KEYS, path);
|
|
354
|
+
return {
|
|
355
|
+
item,
|
|
356
|
+
path
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function invoiceItemOf(value, index) {
|
|
360
|
+
const { item, path } = itemObject(value, index);
|
|
361
|
+
return {
|
|
362
|
+
name: requiredString(item, "name", path),
|
|
363
|
+
unit: optionalString(item, "unit", path),
|
|
364
|
+
comment: optionalString(item, "comment", path),
|
|
365
|
+
vat: invoiceVat(item.vat, `${path}.vat`),
|
|
366
|
+
...unitPriceOf(item, path)
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
function receiptItemOf(value, index) {
|
|
370
|
+
const { item, path } = itemObject(value, index);
|
|
371
|
+
return {
|
|
372
|
+
name: requiredString(item, "name", path),
|
|
373
|
+
unit: optionalString(item, "unit", path),
|
|
374
|
+
comment: optionalString(item, "comment", path),
|
|
375
|
+
vat: receiptVat(item.vat, `${path}.vat`),
|
|
376
|
+
...unitPriceOf(item, path)
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function buyerOf(value, sendEmail) {
|
|
380
|
+
const buyer = objectArg(value, "buyer");
|
|
381
|
+
assertKnownKeys(buyer, BUYER_KEYS, "buyer");
|
|
382
|
+
return {
|
|
383
|
+
name: requiredString(buyer, "name", "buyer"),
|
|
384
|
+
zip: requiredString(buyer, "zip", "buyer"),
|
|
385
|
+
city: requiredString(buyer, "city", "buyer"),
|
|
386
|
+
address: requiredString(buyer, "address", "buyer"),
|
|
387
|
+
country: optionalString(buyer, "country", "buyer"),
|
|
388
|
+
email: optionalString(buyer, "email", "buyer"),
|
|
389
|
+
taxNumber: optionalString(buyer, "taxNumber", "buyer"),
|
|
390
|
+
euTaxNumber: optionalString(buyer, "euTaxNumber", "buyer"),
|
|
391
|
+
sendEmail
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function paymentOf(value, index) {
|
|
395
|
+
const path = `payments[${index}]`;
|
|
396
|
+
const payment = objectArg(value, path);
|
|
397
|
+
assertKnownKeys(payment, PAYMENT_KEYS, path);
|
|
398
|
+
const amount = optionalNumber(payment, "amount", path);
|
|
399
|
+
if (amount === void 0) throw new ToolInputError(`A(z) ${path}.amount mező kötelező.`);
|
|
400
|
+
return {
|
|
401
|
+
method: requiredString(payment, "method", path),
|
|
402
|
+
amount,
|
|
403
|
+
description: optionalString(payment, "description", path)
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function invoiceInputOf(args, extraKeys = []) {
|
|
407
|
+
assertKnownKeys(args, [...INVOICE_INPUT_KEYS, ...extraKeys], "");
|
|
408
|
+
return {
|
|
409
|
+
type: optionalEnum(args, "type", "", MCP_INVOICE_TYPES),
|
|
410
|
+
buyer: buyerOf(args.buyer, optionalBoolean(args, "sendEmail", "") ?? false),
|
|
411
|
+
items: requiredArray(args, "items", "", {
|
|
412
|
+
min: 1,
|
|
413
|
+
max: 100
|
|
414
|
+
}).map(invoiceItemOf),
|
|
415
|
+
paymentMethod: optionalString(args, "paymentMethod", ""),
|
|
416
|
+
currency: optionalString(args, "currency", ""),
|
|
417
|
+
exchangeRate: optionalNumber(args, "exchangeRate", ""),
|
|
418
|
+
exchangeBank: optionalString(args, "exchangeBank", ""),
|
|
419
|
+
language: optionalEnum(args, "language", "", require_create_once.INVOICE_LANGUAGES),
|
|
420
|
+
issueDate: optionalDate(args, "issueDate", ""),
|
|
421
|
+
fulfillmentDate: optionalDate(args, "fulfillmentDate", ""),
|
|
422
|
+
dueDate: optionalDate(args, "dueDate", ""),
|
|
423
|
+
comment: optionalString(args, "comment", ""),
|
|
424
|
+
orderNumber: optionalString(args, "orderNumber", ""),
|
|
425
|
+
externalId: optionalString(args, "externalId", ""),
|
|
426
|
+
prefix: optionalString(args, "prefix", ""),
|
|
427
|
+
paid: optionalBoolean(args, "paid", ""),
|
|
428
|
+
eInvoice: optionalBoolean(args, "eInvoice", ""),
|
|
429
|
+
downloadPdf: false
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
function receiptInputOf(args, extraKeys = []) {
|
|
433
|
+
assertKnownKeys(args, [...RECEIPT_INPUT_KEYS, ...extraKeys], "");
|
|
434
|
+
return {
|
|
435
|
+
items: requiredArray(args, "items", "", {
|
|
436
|
+
min: 1,
|
|
437
|
+
max: 100
|
|
438
|
+
}).map(receiptItemOf),
|
|
439
|
+
payments: optionalArray(args, "payments", "")?.map(paymentOf),
|
|
440
|
+
paymentMethod: optionalString(args, "paymentMethod", ""),
|
|
441
|
+
currency: optionalString(args, "currency", ""),
|
|
442
|
+
exchangeRate: optionalNumber(args, "exchangeRate", ""),
|
|
443
|
+
exchangeBank: optionalString(args, "exchangeBank", ""),
|
|
444
|
+
comment: optionalString(args, "comment", ""),
|
|
445
|
+
orderNumber: optionalString(args, "orderNumber", ""),
|
|
446
|
+
prefix: optionalString(args, "prefix", ""),
|
|
447
|
+
downloadPdf: false
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
//#endregion
|
|
451
|
+
//#region src/mcp/schemas.ts
|
|
452
|
+
const TEXT = {
|
|
453
|
+
type: "string",
|
|
454
|
+
minLength: 1
|
|
455
|
+
};
|
|
456
|
+
const DATE = {
|
|
457
|
+
type: "string",
|
|
458
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
459
|
+
description: "Dátum ÉÉÉÉ-HH-NN formában (Europe/Budapest szerint)."
|
|
460
|
+
};
|
|
461
|
+
const VAT = {
|
|
462
|
+
description: "Áfakulcs: szám (például 27, 18, 5, 0) vagy kód (például \"AAM\" alanyi adómentes, \"TAM\" tárgyi adómentes, \"EUT\", \"EUKT\", \"F.AFA\", \"K.AFA\"). Nyugtán \"ÁKK\", \"MAA\", \"EU\" és \"EUK\" is lehet.",
|
|
463
|
+
anyOf: [{ type: "number" }, {
|
|
464
|
+
type: "string",
|
|
465
|
+
minLength: 1
|
|
466
|
+
}]
|
|
467
|
+
};
|
|
468
|
+
const ITEMS = {
|
|
469
|
+
type: "array",
|
|
470
|
+
items: {
|
|
471
|
+
type: "object",
|
|
472
|
+
properties: {
|
|
473
|
+
name: {
|
|
474
|
+
...TEXT,
|
|
475
|
+
description: "A tétel megnevezése."
|
|
476
|
+
},
|
|
477
|
+
quantity: {
|
|
478
|
+
type: "number",
|
|
479
|
+
description: "Mennyiség, alapértelmezés: 1."
|
|
480
|
+
},
|
|
481
|
+
unit: {
|
|
482
|
+
...TEXT,
|
|
483
|
+
description: "Mennyiségi egység, például db, óra."
|
|
484
|
+
},
|
|
485
|
+
netUnitPrice: {
|
|
486
|
+
type: "number",
|
|
487
|
+
description: "Nettó egységár. A netUnitPrice és a grossUnitPrice közül pontosan egyet adj meg."
|
|
488
|
+
},
|
|
489
|
+
grossUnitPrice: {
|
|
490
|
+
type: "number",
|
|
491
|
+
description: "Bruttó egységár. A netUnitPrice és a grossUnitPrice közül pontosan egyet adj meg."
|
|
492
|
+
},
|
|
493
|
+
vat: VAT,
|
|
494
|
+
comment: {
|
|
495
|
+
...TEXT,
|
|
496
|
+
description: "Megjegyzés a tételhez."
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
required: ["name", "vat"],
|
|
500
|
+
additionalProperties: false
|
|
501
|
+
},
|
|
502
|
+
minItems: 1,
|
|
503
|
+
maxItems: 100
|
|
504
|
+
};
|
|
505
|
+
const BUYER = {
|
|
506
|
+
type: "object",
|
|
507
|
+
properties: {
|
|
508
|
+
name: {
|
|
509
|
+
...TEXT,
|
|
510
|
+
description: "A vevő neve."
|
|
511
|
+
},
|
|
512
|
+
zip: {
|
|
513
|
+
...TEXT,
|
|
514
|
+
description: "Irányítószám."
|
|
515
|
+
},
|
|
516
|
+
city: {
|
|
517
|
+
...TEXT,
|
|
518
|
+
description: "Település."
|
|
519
|
+
},
|
|
520
|
+
address: {
|
|
521
|
+
...TEXT,
|
|
522
|
+
description: "Utca, házszám."
|
|
523
|
+
},
|
|
524
|
+
country: {
|
|
525
|
+
...TEXT,
|
|
526
|
+
description: "Ország, ha nem Magyarország."
|
|
527
|
+
},
|
|
528
|
+
email: {
|
|
529
|
+
...TEXT,
|
|
530
|
+
description: "A vevő e-mail címe."
|
|
531
|
+
},
|
|
532
|
+
taxNumber: {
|
|
533
|
+
...TEXT,
|
|
534
|
+
description: "Magyar adószám, például 12345676-2-42."
|
|
535
|
+
},
|
|
536
|
+
euTaxNumber: {
|
|
537
|
+
...TEXT,
|
|
538
|
+
description: "Közösségi adószám, például HU12345676."
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
required: [
|
|
542
|
+
"name",
|
|
543
|
+
"zip",
|
|
544
|
+
"city",
|
|
545
|
+
"address"
|
|
546
|
+
],
|
|
547
|
+
additionalProperties: false
|
|
548
|
+
};
|
|
549
|
+
const COMMON_DOCUMENT_PROPERTIES = {
|
|
550
|
+
paymentMethod: {
|
|
551
|
+
...TEXT,
|
|
552
|
+
description: "Fizetési mód, például átutalás, készpénz, bankkártya."
|
|
553
|
+
},
|
|
554
|
+
currency: {
|
|
555
|
+
...TEXT,
|
|
556
|
+
description: "Pénznem, alapértelmezés: HUF."
|
|
557
|
+
},
|
|
558
|
+
exchangeRate: {
|
|
559
|
+
type: "number",
|
|
560
|
+
description: "Árfolyam devizás bizonylathoz."
|
|
561
|
+
},
|
|
562
|
+
exchangeBank: {
|
|
563
|
+
...TEXT,
|
|
564
|
+
description: "Az árfolyamot jegyző bank, például MNB."
|
|
565
|
+
},
|
|
566
|
+
comment: {
|
|
567
|
+
...TEXT,
|
|
568
|
+
description: "Megjegyzés a bizonylaton."
|
|
569
|
+
},
|
|
570
|
+
orderNumber: {
|
|
571
|
+
...TEXT,
|
|
572
|
+
description: "Rendelésszám (a bizonylaton megjelenik)."
|
|
573
|
+
},
|
|
574
|
+
prefix: {
|
|
575
|
+
...TEXT,
|
|
576
|
+
description: "A Számlázz.hu fiókban rögzített előtag."
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
function objectSchema(properties, required = []) {
|
|
580
|
+
return {
|
|
581
|
+
type: "object",
|
|
582
|
+
properties,
|
|
583
|
+
required: [...required],
|
|
584
|
+
additionalProperties: false
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
const INVOICE_PROPERTIES = {
|
|
588
|
+
type: {
|
|
589
|
+
type: "string",
|
|
590
|
+
enum: [...MCP_INVOICE_TYPES],
|
|
591
|
+
description: "invoice (számla, alapértelmezés) vagy proforma (díjbekérő)."
|
|
592
|
+
},
|
|
593
|
+
buyer: BUYER,
|
|
594
|
+
items: ITEMS,
|
|
595
|
+
...COMMON_DOCUMENT_PROPERTIES,
|
|
596
|
+
language: {
|
|
597
|
+
type: "string",
|
|
598
|
+
enum: [...require_create_once.INVOICE_LANGUAGES],
|
|
599
|
+
description: "A számla nyelve."
|
|
600
|
+
},
|
|
601
|
+
issueDate: {
|
|
602
|
+
...DATE,
|
|
603
|
+
description: "Kelt, alapértelmezés: ma."
|
|
604
|
+
},
|
|
605
|
+
fulfillmentDate: {
|
|
606
|
+
...DATE,
|
|
607
|
+
description: "Teljesítés dátuma, alapértelmezés: ma."
|
|
608
|
+
},
|
|
609
|
+
dueDate: {
|
|
610
|
+
...DATE,
|
|
611
|
+
description: "Fizetési határidő."
|
|
612
|
+
},
|
|
613
|
+
externalId: {
|
|
614
|
+
...TEXT,
|
|
615
|
+
description: "Saját külső azonosító. Ha megadod, ugyanezzel csak egy számla készül; ha nem, a kassza képez egyet."
|
|
616
|
+
},
|
|
617
|
+
paid: {
|
|
618
|
+
type: "boolean",
|
|
619
|
+
description: "A számla már ki van fizetve."
|
|
620
|
+
},
|
|
621
|
+
eInvoice: {
|
|
622
|
+
type: "boolean",
|
|
623
|
+
description: "Elektronikus számla."
|
|
624
|
+
},
|
|
625
|
+
sendEmail: {
|
|
626
|
+
type: "boolean",
|
|
627
|
+
description: "A Számlázz.hu e-mailben elküldi-e a vevőnek. Alapértelmezés: false."
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
const RECEIPT_PROPERTIES = {
|
|
631
|
+
items: ITEMS,
|
|
632
|
+
payments: {
|
|
633
|
+
type: "array",
|
|
634
|
+
description: "Kifizetések fizetőeszközönként; az összegük a nyugta bruttó végösszege.",
|
|
635
|
+
items: objectSchema({
|
|
636
|
+
method: {
|
|
637
|
+
...TEXT,
|
|
638
|
+
description: "Fizetőeszköz, például készpénz, bankkártya."
|
|
639
|
+
},
|
|
640
|
+
amount: { type: "number" },
|
|
641
|
+
description: TEXT
|
|
642
|
+
}, ["method", "amount"])
|
|
643
|
+
},
|
|
644
|
+
...COMMON_DOCUMENT_PROPERTIES
|
|
645
|
+
};
|
|
646
|
+
const CONFIRMATION_PROPERTY = {
|
|
647
|
+
...TEXT,
|
|
648
|
+
description: "Az előnézeti eszköz által adott megerősítő kód. Csak azután add meg, hogy a felhasználó jóváhagyta az előnézetet."
|
|
649
|
+
};
|
|
650
|
+
const TEXT_PROPERTY = TEXT;
|
|
651
|
+
const INVOICE_SCHEMA = objectSchema(INVOICE_PROPERTIES, ["buyer", "items"]);
|
|
652
|
+
const CREATE_INVOICE_SCHEMA = objectSchema({
|
|
653
|
+
...INVOICE_PROPERTIES,
|
|
654
|
+
confirmation: CONFIRMATION_PROPERTY
|
|
655
|
+
}, [
|
|
656
|
+
"buyer",
|
|
657
|
+
"items",
|
|
658
|
+
"confirmation"
|
|
659
|
+
]);
|
|
660
|
+
const RECEIPT_SCHEMA = objectSchema(RECEIPT_PROPERTIES, ["items"]);
|
|
661
|
+
const CREATE_RECEIPT_SCHEMA = objectSchema({
|
|
662
|
+
...RECEIPT_PROPERTIES,
|
|
663
|
+
confirmation: CONFIRMATION_PROPERTY
|
|
664
|
+
}, ["items", "confirmation"]);
|
|
665
|
+
//#endregion
|
|
666
|
+
//#region src/mcp/tools-invoice.ts
|
|
667
|
+
const PREVIEW_INVOICE = "preview_invoice";
|
|
668
|
+
const CREATE_INVOICE = "create_invoice";
|
|
669
|
+
function documentLabel(resolved) {
|
|
670
|
+
return resolved.type === "proforma" ? "Díjbekérő" : "Számla";
|
|
671
|
+
}
|
|
672
|
+
function resolve(input, context) {
|
|
673
|
+
return require_create_once.resolveInvoice(context.defaults.invoice ?? {}, input, context.now());
|
|
674
|
+
}
|
|
675
|
+
function previewData(input, resolved) {
|
|
676
|
+
const totals = require_money.summarizeItems(resolved.items.map((item) => item.amounts));
|
|
677
|
+
return {
|
|
678
|
+
document: resolved.type,
|
|
679
|
+
buyer: input.buyer,
|
|
680
|
+
issueDate: resolved.issueDate,
|
|
681
|
+
fulfillmentDate: resolved.fulfillmentDate,
|
|
682
|
+
dueDate: resolved.dueDate,
|
|
683
|
+
paymentMethod: resolved.paymentMethod,
|
|
684
|
+
currency: resolved.currency,
|
|
685
|
+
exchangeRate: resolved.exchangeRate,
|
|
686
|
+
exchangeBank: resolved.exchangeBank,
|
|
687
|
+
language: resolved.language,
|
|
688
|
+
prefix: resolved.prefix,
|
|
689
|
+
eInvoice: resolved.eInvoice,
|
|
690
|
+
sendEmail: resolved.sendEmail,
|
|
691
|
+
orderNumber: input.orderNumber,
|
|
692
|
+
comment: input.comment,
|
|
693
|
+
items: resolved.items.map((item) => ({
|
|
694
|
+
name: item.input.name,
|
|
695
|
+
quantity: item.amounts.quantity,
|
|
696
|
+
unit: item.unit,
|
|
697
|
+
netUnitPrice: item.amounts.netUnitPrice,
|
|
698
|
+
vat: item.amounts.vat,
|
|
699
|
+
netAmount: item.amounts.netAmount,
|
|
700
|
+
vatAmount: item.amounts.vatAmount,
|
|
701
|
+
grossAmount: item.amounts.grossAmount
|
|
702
|
+
})),
|
|
703
|
+
totals: {
|
|
704
|
+
netAmount: totals.netAmount,
|
|
705
|
+
vatAmount: totals.vatAmount,
|
|
706
|
+
grossAmount: totals.grossAmount
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
async function previewInvoice(args, context) {
|
|
711
|
+
const input = invoiceInputOf(args);
|
|
712
|
+
const resolved = resolve(input, context);
|
|
713
|
+
const data = previewData(input, resolved);
|
|
714
|
+
const totals = require_money.summarizeItems(resolved.items.map((item) => item.amounts));
|
|
715
|
+
const confirmation = await context.confirmations.issue(CREATE_INVOICE, args);
|
|
716
|
+
return {
|
|
717
|
+
summary: lines(`${documentLabel(resolved)} előnézete (még NEM készült el): ${input.buyer.name}, nettó ${money(totals.netAmount, resolved.currency)}, bruttó ${money(totals.grossAmount, resolved.currency)}.`, `Kelt: ${resolved.issueDate}, teljesítés: ${resolved.fulfillmentDate}, fizetési határidő: ${resolved.dueDate}, fizetési mód: ${resolved.paymentMethod}.`, resolved.sendEmail ? "A Számlázz.hu e-mailben elküldi a vevőnek." : "A vevő nem kap e-mailt a Számlázz.hu-tól.", `Mutasd meg az előnézetet a felhasználónak. Ha jóváhagyja, hívd meg a ${CREATE_INVOICE} eszközt pontosan ugyanezekkel az adatokkal, a confirmation mezőben ezzel a kóddal: ${confirmation.token} (lejár: ${confirmation.expiresAt}).`),
|
|
718
|
+
data: {
|
|
719
|
+
...data,
|
|
720
|
+
confirmation: confirmation.token,
|
|
721
|
+
confirmationExpiresAt: confirmation.expiresAt
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function existingOutput(details, externalId, created) {
|
|
726
|
+
const currency = details.header.currency ?? "HUF";
|
|
727
|
+
return {
|
|
728
|
+
summary: created ? `Kiállítva: ${details.header.number} (bruttó ${money(details.totals.grossAmount, currency)}). A válasz elveszett, de a kassza visszakereste.` : `Ezzel a külső azonosítóval (${externalId}) már létezik bizonylat: ${details.header.number} (bruttó ${money(details.totals.grossAmount, currency)}). Nem állítottam ki újra. Ha valóban még egy ugyanilyen kell, adj meg eltérő externalId értéket.`,
|
|
729
|
+
data: {
|
|
730
|
+
created,
|
|
731
|
+
number: details.header.number,
|
|
732
|
+
externalId,
|
|
733
|
+
netTotal: details.totals.netAmount,
|
|
734
|
+
grossTotal: details.totals.grossAmount,
|
|
735
|
+
currency
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
async function createInvoice(args, context) {
|
|
740
|
+
const { payload, confirmation } = splitConfirmation(args);
|
|
741
|
+
await context.confirmations.verify(CREATE_INVOICE, payload, confirmation, PREVIEW_INVOICE);
|
|
742
|
+
const input = invoiceInputOf(payload);
|
|
743
|
+
const resolved = resolve(input, context);
|
|
744
|
+
const externalId = input.externalId ?? `MCP-${await idempotencyKey("create_invoice", resolved.issueDate, payload)}`;
|
|
745
|
+
const kassza = context.client();
|
|
746
|
+
const lookup = () => kassza.invoices.find({ externalId }, { includePdf: false });
|
|
747
|
+
const existing = await lookup();
|
|
748
|
+
if (existing) return existingOutput(existing, externalId, false);
|
|
749
|
+
try {
|
|
750
|
+
const created = await kassza.invoices.create({
|
|
751
|
+
...input,
|
|
752
|
+
externalId,
|
|
753
|
+
issueDate: resolved.issueDate,
|
|
754
|
+
fulfillmentDate: resolved.fulfillmentDate,
|
|
755
|
+
dueDate: resolved.dueDate
|
|
756
|
+
});
|
|
757
|
+
return {
|
|
758
|
+
summary: `${documentLabel(resolved)} kiállítva: ${created.number} (bruttó ${money(created.grossTotal, resolved.currency)}).`,
|
|
759
|
+
data: {
|
|
760
|
+
created: true,
|
|
761
|
+
number: created.number,
|
|
762
|
+
externalId,
|
|
763
|
+
netTotal: created.netTotal,
|
|
764
|
+
grossTotal: created.grossTotal,
|
|
765
|
+
outstanding: created.outstanding,
|
|
766
|
+
currency: resolved.currency
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
} catch (error) {
|
|
770
|
+
if (!require_issue.isUncertainOutcome(error)) throw error;
|
|
771
|
+
const recovered = await require_issue.recoverAfterFailure(lookup, context.recoveryDelayMs, void 0);
|
|
772
|
+
if (!recovered) throw error;
|
|
773
|
+
return existingOutput(recovered, externalId, error.category !== "duplicate");
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
const INVOICE_TOOLS = [{
|
|
777
|
+
name: PREVIEW_INVOICE,
|
|
778
|
+
title: "Számla előnézete",
|
|
779
|
+
description: "Kiszámolja egy számla vagy díjbekérő tételeit és végösszegét a Számlázz.hu hívása nélkül, és ad egy megerősítő kódot a create_invoice eszközhöz. Semmit nem állít ki.",
|
|
780
|
+
inputSchema: INVOICE_SCHEMA,
|
|
781
|
+
annotations: LOCAL_READ,
|
|
782
|
+
write: false,
|
|
783
|
+
run: previewInvoice
|
|
784
|
+
}, {
|
|
785
|
+
name: CREATE_INVOICE,
|
|
786
|
+
title: "Számla kiállítása",
|
|
787
|
+
description: "Kiállítja a számlát vagy díjbekérőt a Számlázz.hu-n. Csak a preview_invoice által adott megerősítő kóddal működik, és csak akkor hívd, ha a felhasználó jóváhagyta az előnézetet. Ugyanazzal a kéréssel naponta csak egy bizonylat készül.",
|
|
788
|
+
inputSchema: CREATE_INVOICE_SCHEMA,
|
|
789
|
+
annotations: REMOTE_CREATE,
|
|
790
|
+
write: true,
|
|
791
|
+
run: createInvoice
|
|
792
|
+
}];
|
|
793
|
+
const INVOICE_TYPE_LABELS = {
|
|
794
|
+
invoice: "Számla",
|
|
795
|
+
proforma: "Díjbekérő",
|
|
796
|
+
advance: "Előlegszámla",
|
|
797
|
+
final: "Végszámla",
|
|
798
|
+
corrective: "Helyesbítő számla",
|
|
799
|
+
reversal: "Sztornó számla",
|
|
800
|
+
deliveryNote: "Szállítólevél"
|
|
801
|
+
};
|
|
802
|
+
function exactlyOne(values) {
|
|
803
|
+
const present = Object.entries(values).filter(([, value]) => value !== void 0);
|
|
804
|
+
if (present.length !== 1) throw new ToolInputError(`Pontosan egyet adj meg ezek közül: ${Object.keys(values).join(", ")}.`);
|
|
805
|
+
return present[0]?.[0] ?? "";
|
|
806
|
+
}
|
|
807
|
+
async function getInvoice(args, context) {
|
|
808
|
+
assertKnownKeys(args, [
|
|
809
|
+
"invoiceNumber",
|
|
810
|
+
"orderNumber",
|
|
811
|
+
"externalId"
|
|
812
|
+
], "");
|
|
813
|
+
const invoiceNumber = optionalString(args, "invoiceNumber", "");
|
|
814
|
+
const orderNumber = optionalString(args, "orderNumber", "");
|
|
815
|
+
const externalId = optionalString(args, "externalId", "");
|
|
816
|
+
const kind = exactlyOne({
|
|
817
|
+
invoiceNumber,
|
|
818
|
+
orderNumber,
|
|
819
|
+
externalId
|
|
820
|
+
});
|
|
821
|
+
const reference = kind === "invoiceNumber" ? invoiceNumber ?? "" : kind === "orderNumber" ? { orderNumber: orderNumber ?? "" } : { externalId: externalId ?? "" };
|
|
822
|
+
const details = await context.client().invoices.find(reference, { includePdf: false });
|
|
823
|
+
if (!details) return {
|
|
824
|
+
summary: "Nincs ilyen számla a Számlázz.hu fiókban.",
|
|
825
|
+
data: { found: false }
|
|
826
|
+
};
|
|
827
|
+
const { header } = details;
|
|
828
|
+
const currency = header.currency ?? "HUF";
|
|
829
|
+
return {
|
|
830
|
+
summary: lines(`${INVOICE_TYPE_LABELS[header.type] ?? "Bizonylat"} ${header.number}: ${details.buyer.name}, bruttó ${money(details.totals.grossAmount, currency)}, kelt: ${header.issueDate}.`, header.reversed === true && "A számlát sztornózták.", header.test === true && "Tesztfiókban készült."),
|
|
831
|
+
data: {
|
|
832
|
+
found: true,
|
|
833
|
+
invoice: plainJson(details)
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
async function getReceipt(args, context) {
|
|
838
|
+
assertKnownKeys(args, ["receiptNumber", "orderNumber"], "");
|
|
839
|
+
const receiptNumber = optionalString(args, "receiptNumber", "");
|
|
840
|
+
const orderNumber = optionalString(args, "orderNumber", "");
|
|
841
|
+
const kind = exactlyOne({
|
|
842
|
+
receiptNumber,
|
|
843
|
+
orderNumber
|
|
844
|
+
});
|
|
845
|
+
const receipt = await context.client().receipts.find(kind === "receiptNumber" ? {
|
|
846
|
+
receiptNumber: receiptNumber ?? "",
|
|
847
|
+
downloadPdf: false
|
|
848
|
+
} : {
|
|
849
|
+
orderNumber: orderNumber ?? "",
|
|
850
|
+
downloadPdf: false
|
|
851
|
+
});
|
|
852
|
+
if (!receipt) return {
|
|
853
|
+
summary: "Nincs ilyen nyugta a Számlázz.hu fiókban.",
|
|
854
|
+
data: { found: false }
|
|
855
|
+
};
|
|
856
|
+
return {
|
|
857
|
+
summary: lines(`${receipt.type === "reversal" ? "Sztornónyugta" : "Nyugta"} ${receipt.number}: bruttó ${money(receipt.totals.grossAmount, receipt.currency)}, kelt: ${receipt.issueDate}.`, receipt.isReversed && "A nyugtát sztornózták.", receipt.isTest && "Tesztfiókban készült."),
|
|
858
|
+
data: {
|
|
859
|
+
found: true,
|
|
860
|
+
receipt: plainJson(receipt)
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
async function queryTaxpayer(args, context) {
|
|
865
|
+
assertKnownKeys(args, ["taxNumber"], "");
|
|
866
|
+
const taxNumber = requiredString(args, "taxNumber", "");
|
|
867
|
+
const info = await context.client().taxpayer.query(taxNumber);
|
|
868
|
+
return {
|
|
869
|
+
summary: info.valid ? lines(`${info.name ?? "Ismeretlen név"}${info.taxNumber?.formatted ? ` (${info.taxNumber.formatted})` : ""}: a NAV szerint érvényes adószám.`, info.address && `Székhely: ${info.address.formatted}.`) : "A NAV szerint ez az adószám nem érvényes, vagy nem tartozik működő adóalanyhoz.",
|
|
870
|
+
data: plainJson(info)
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function receiptNumbersOf(args) {
|
|
874
|
+
const listed = optionalArray(args, "receiptNumbers", "");
|
|
875
|
+
const series = optionalString(args, "series", "");
|
|
876
|
+
const from = optionalInteger(args, "from", "");
|
|
877
|
+
const to = optionalInteger(args, "to", "");
|
|
878
|
+
if (listed !== void 0) {
|
|
879
|
+
if (series !== void 0 || from !== void 0 || to !== void 0) throw new ToolInputError("A receiptNumbers mellett ne adj meg series, from vagy to mezőt.");
|
|
880
|
+
const numbers = listed.map((value, index) => {
|
|
881
|
+
if (typeof value !== "string" || value.trim() === "") throw new ToolInputError(`A(z) receiptNumbers[${index}] nem érvényes nyugtaszám.`);
|
|
882
|
+
return value.trim();
|
|
883
|
+
});
|
|
884
|
+
return [...new Set(numbers)];
|
|
885
|
+
}
|
|
886
|
+
if (series === void 0 || from === void 0 || to === void 0) throw new ToolInputError("Add meg a nyugtaszámokat (receiptNumbers), vagy a sorozatot és a tartományt (series, from, to), például series: \"NYGT-2026\", from: 1, to: 120.");
|
|
887
|
+
if (from < 1 || to < from) throw new ToolInputError("A from legalább 1 legyen, és nem lehet nagyobb a to értékénél.");
|
|
888
|
+
return Array.from({ length: to - from + 1 }, (_, index) => `${series}-${from + index}`);
|
|
889
|
+
}
|
|
890
|
+
async function navDailySummary(args, context) {
|
|
891
|
+
assertKnownKeys(args, [
|
|
892
|
+
"receiptNumbers",
|
|
893
|
+
"series",
|
|
894
|
+
"from",
|
|
895
|
+
"to",
|
|
896
|
+
"includeTest"
|
|
897
|
+
], "");
|
|
898
|
+
const numbers = receiptNumbersOf(args);
|
|
899
|
+
if (numbers.length === 0 || numbers.length > 200) throw new ToolInputError(`Egyszerre 1 és 200 közötti számú nyugta összesíthető, kért: ${numbers.length}.`);
|
|
900
|
+
const includeTest = optionalBoolean(args, "includeTest", "") ?? false;
|
|
901
|
+
const kassza = context.client();
|
|
902
|
+
const receipts = [];
|
|
903
|
+
const missing = [];
|
|
904
|
+
for (const receiptNumber of numbers) {
|
|
905
|
+
const receipt = await kassza.receipts.find({
|
|
906
|
+
receiptNumber,
|
|
907
|
+
downloadPdf: false
|
|
908
|
+
});
|
|
909
|
+
if (receipt) receipts.push(receipt);
|
|
910
|
+
else missing.push(receiptNumber);
|
|
911
|
+
}
|
|
912
|
+
const reports = require_nav.navDailyReports(receipts, { includeTest });
|
|
913
|
+
const skippedTest = includeTest ? 0 : receipts.filter((receipt) => receipt.isTest).length;
|
|
914
|
+
return {
|
|
915
|
+
summary: lines(reports.length === 0 ? "Nincs összesíthető nyugta." : reports.map((report) => require_nav.describeNavReport(report)).join("\n\n"), missing.length > 0 && `Nem található nyugták: ${missing.join(", ")}.`, skippedTest > 0 && `${skippedTest} tesztfiókos nyugtát kihagytam (az includeTest: true beveszi őket).`),
|
|
916
|
+
data: {
|
|
917
|
+
reports: plainJson({ reports }).reports,
|
|
918
|
+
missing,
|
|
919
|
+
receiptCount: receipts.length
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
const LOOKUP_TOOLS = [
|
|
924
|
+
{
|
|
925
|
+
name: "get_invoice",
|
|
926
|
+
title: "Számla lekérdezése",
|
|
927
|
+
description: "Lekér egy számlát (díjbekérőt, sztornót) a Számlázz.hu-ról számlaszám, rendelésszám vagy külső azonosító alapján.",
|
|
928
|
+
inputSchema: objectSchema({
|
|
929
|
+
invoiceNumber: TEXT_PROPERTY,
|
|
930
|
+
orderNumber: TEXT_PROPERTY,
|
|
931
|
+
externalId: TEXT_PROPERTY
|
|
932
|
+
}),
|
|
933
|
+
annotations: REMOTE_READ,
|
|
934
|
+
write: false,
|
|
935
|
+
run: getInvoice
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
name: "get_receipt",
|
|
939
|
+
title: "Nyugta lekérdezése",
|
|
940
|
+
description: "Lekér egy nyugtát a Számlázz.hu-ról nyugtaszám vagy rendelésszám alapján.",
|
|
941
|
+
inputSchema: objectSchema({
|
|
942
|
+
receiptNumber: TEXT_PROPERTY,
|
|
943
|
+
orderNumber: TEXT_PROPERTY
|
|
944
|
+
}),
|
|
945
|
+
annotations: REMOTE_READ,
|
|
946
|
+
write: false,
|
|
947
|
+
run: getReceipt
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
name: "query_taxpayer",
|
|
951
|
+
title: "Adószám ellenőrzése",
|
|
952
|
+
description: "A NAV adatai alapján (a Számlázz.hu-n keresztül) ellenőrzi az adószámot, és visszaadja a cég nevét és székhelyét.",
|
|
953
|
+
inputSchema: objectSchema({ taxNumber: {
|
|
954
|
+
...TEXT_PROPERTY,
|
|
955
|
+
description: "8 jegyű törzsszám vagy 11 jegyű adószám, például 12345676-2-42."
|
|
956
|
+
} }, ["taxNumber"]),
|
|
957
|
+
annotations: REMOTE_READ,
|
|
958
|
+
write: false,
|
|
959
|
+
run: queryTaxpayer
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
name: "nav_daily_summary",
|
|
963
|
+
title: "Napi NAV nyugta-összesítő",
|
|
964
|
+
description: `Lekéri a megadott nyugtákat, és elkészíti belőlük a NAV felé teendő napi, áfakategóriánkénti összesítőt (kezdő sorszám, értékesítés, módosító bizonylatok). Egyszerre legfeljebb 200 nyugta.`,
|
|
965
|
+
inputSchema: objectSchema({
|
|
966
|
+
receiptNumbers: {
|
|
967
|
+
type: "array",
|
|
968
|
+
items: TEXT_PROPERTY,
|
|
969
|
+
minItems: 1,
|
|
970
|
+
maxItems: 200
|
|
971
|
+
},
|
|
972
|
+
series: {
|
|
973
|
+
...TEXT_PROPERTY,
|
|
974
|
+
description: "Sorozat, például NYGT-2026."
|
|
975
|
+
},
|
|
976
|
+
from: {
|
|
977
|
+
type: "integer",
|
|
978
|
+
minimum: 1
|
|
979
|
+
},
|
|
980
|
+
to: {
|
|
981
|
+
type: "integer",
|
|
982
|
+
minimum: 1
|
|
983
|
+
},
|
|
984
|
+
includeTest: {
|
|
985
|
+
type: "boolean",
|
|
986
|
+
description: "A tesztfiókos nyugtákat is összesíti."
|
|
987
|
+
}
|
|
988
|
+
}),
|
|
989
|
+
annotations: REMOTE_READ,
|
|
990
|
+
write: false,
|
|
991
|
+
run: navDailySummary
|
|
992
|
+
}
|
|
993
|
+
];
|
|
994
|
+
//#endregion
|
|
995
|
+
//#region src/mcp/tools-receipt.ts
|
|
996
|
+
const PREVIEW_RECEIPT = "preview_receipt";
|
|
997
|
+
const CREATE_RECEIPT = "create_receipt";
|
|
998
|
+
const DUPLICATE_CALL_ID = 338;
|
|
999
|
+
function previewOf(input, context) {
|
|
1000
|
+
const defaults = context.defaults.receipt ?? {};
|
|
1001
|
+
require_issue.buildCreateReceiptXml([], defaults, input);
|
|
1002
|
+
const currency = input.currency ?? defaults.currency ?? "HUF";
|
|
1003
|
+
const items = require_issue.calculateReceiptItems(input.items, currency);
|
|
1004
|
+
const totals = require_money.summarizeItems(items.map((item) => item.amounts));
|
|
1005
|
+
return {
|
|
1006
|
+
currency,
|
|
1007
|
+
grossAmount: totals.grossAmount,
|
|
1008
|
+
data: {
|
|
1009
|
+
document: "receipt",
|
|
1010
|
+
prefix: input.prefix ?? defaults.prefix,
|
|
1011
|
+
paymentMethod: input.paymentMethod ?? defaults.paymentMethod,
|
|
1012
|
+
currency,
|
|
1013
|
+
exchangeRate: input.exchangeRate ?? defaults.exchangeRate,
|
|
1014
|
+
orderNumber: input.orderNumber,
|
|
1015
|
+
comment: input.comment,
|
|
1016
|
+
items: items.map((item) => ({
|
|
1017
|
+
name: item.input.name,
|
|
1018
|
+
quantity: item.amounts.quantity,
|
|
1019
|
+
unit: item.input.unit ?? defaults.unit ?? "db",
|
|
1020
|
+
netUnitPrice: item.amounts.netUnitPrice,
|
|
1021
|
+
vat: item.vat,
|
|
1022
|
+
netAmount: item.amounts.netAmount,
|
|
1023
|
+
vatAmount: item.amounts.vatAmount,
|
|
1024
|
+
grossAmount: item.amounts.grossAmount
|
|
1025
|
+
})),
|
|
1026
|
+
payments: input.payments ?? [],
|
|
1027
|
+
totals: {
|
|
1028
|
+
netAmount: totals.netAmount,
|
|
1029
|
+
vatAmount: totals.vatAmount,
|
|
1030
|
+
grossAmount: totals.grossAmount
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
async function previewReceipt(args, context) {
|
|
1036
|
+
const preview = previewOf(receiptInputOf(args), context);
|
|
1037
|
+
const confirmation = await context.confirmations.issue(CREATE_RECEIPT, args);
|
|
1038
|
+
return {
|
|
1039
|
+
summary: lines(`Nyugta előnézete (még NEM készült el): bruttó ${money(preview.grossAmount, preview.currency)}.`, `Mutasd meg az előnézetet a felhasználónak. Ha jóváhagyja, hívd meg a ${CREATE_RECEIPT} eszközt pontosan ugyanezekkel az adatokkal, a confirmation mezőben ezzel a kóddal: ${confirmation.token} (lejár: ${confirmation.expiresAt}).`),
|
|
1040
|
+
data: {
|
|
1041
|
+
...preview.data,
|
|
1042
|
+
confirmation: confirmation.token,
|
|
1043
|
+
confirmationExpiresAt: confirmation.expiresAt
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
function receiptOutput(receipt, created) {
|
|
1048
|
+
const total = money(receipt.totals.grossAmount, receipt.currency);
|
|
1049
|
+
return {
|
|
1050
|
+
summary: created ? `Nyugta kiállítva: ${receipt.number} (bruttó ${total}).` : `Ehhez a rendelésszámhoz már létezik nyugta: ${receipt.number} (bruttó ${total}). Nem állítottam ki újra.`,
|
|
1051
|
+
data: {
|
|
1052
|
+
created,
|
|
1053
|
+
number: receipt.number,
|
|
1054
|
+
type: receipt.type,
|
|
1055
|
+
orderNumber: receipt.orderNumber,
|
|
1056
|
+
callId: receipt.callId,
|
|
1057
|
+
issueDate: receipt.issueDate,
|
|
1058
|
+
grossTotal: receipt.totals.grossAmount,
|
|
1059
|
+
currency: receipt.currency
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
function duplicateCallIdError(error, callId) {
|
|
1064
|
+
return new require_errors.SzamlazzError(`Ezekkel az adatokkal ma már készült nyugta ezen az eszközön keresztül (hívásazonosító: ${callId}), ezért nem készült új.`, {
|
|
1065
|
+
category: "duplicate",
|
|
1066
|
+
code: error.code,
|
|
1067
|
+
action: error.action,
|
|
1068
|
+
hint: "A Számla Agent hívásazonosító alapján nem tud nyugtát lekérdezni: a nyugtát a Számlázz.hu felületén találod. Ha valóban még egy ugyanilyen nyugta kell, adj meg rendelésszámot (orderNumber), vagy változtass az adatokon.",
|
|
1069
|
+
cause: error
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
async function createReceipt(args, context) {
|
|
1073
|
+
const { payload, confirmation } = splitConfirmation(args);
|
|
1074
|
+
await context.confirmations.verify(CREATE_RECEIPT, payload, confirmation, PREVIEW_RECEIPT);
|
|
1075
|
+
const input = receiptInputOf(payload);
|
|
1076
|
+
previewOf(input, context);
|
|
1077
|
+
const day = require_dates.toBudapestDate(context.now());
|
|
1078
|
+
const callId = `MCP-${await idempotencyKey(CREATE_RECEIPT, day, payload)}`;
|
|
1079
|
+
const kassza = context.client();
|
|
1080
|
+
if (input.orderNumber !== void 0) {
|
|
1081
|
+
const result = await kassza.receipts.createOnce({
|
|
1082
|
+
...input,
|
|
1083
|
+
callId
|
|
1084
|
+
}, { recoveryDelayMs: context.recoveryDelayMs });
|
|
1085
|
+
return receiptOutput(result.receipt, result.created);
|
|
1086
|
+
}
|
|
1087
|
+
try {
|
|
1088
|
+
return receiptOutput(await kassza.receipts.create({
|
|
1089
|
+
...input,
|
|
1090
|
+
callId
|
|
1091
|
+
}), true);
|
|
1092
|
+
} catch (error) {
|
|
1093
|
+
if (require_errors.isSzamlazzError(error) && error.code === DUPLICATE_CALL_ID) throw duplicateCallIdError(error, callId);
|
|
1094
|
+
throw error;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
const RECEIPT_TOOLS = [{
|
|
1098
|
+
name: PREVIEW_RECEIPT,
|
|
1099
|
+
title: "Nyugta előnézete",
|
|
1100
|
+
description: "Kiszámolja egy nyugta tételeit és végösszegét a Számlázz.hu hívása nélkül, ellenőrzi a forintos kerekítési szabályokat, és ad egy megerősítő kódot a create_receipt eszközhöz. Semmit nem állít ki.",
|
|
1101
|
+
inputSchema: RECEIPT_SCHEMA,
|
|
1102
|
+
annotations: LOCAL_READ,
|
|
1103
|
+
write: false,
|
|
1104
|
+
run: previewReceipt
|
|
1105
|
+
}, {
|
|
1106
|
+
name: CREATE_RECEIPT,
|
|
1107
|
+
title: "Nyugta kiállítása",
|
|
1108
|
+
description: "Kiállítja a nyugtát a Számlázz.hu-n. Csak a preview_receipt által adott megerősítő kóddal működik, és csak akkor hívd, ha a felhasználó jóváhagyta az előnézetet. Rendelésszámmal egy rendeléshez csak egy nyugta készül.",
|
|
1109
|
+
inputSchema: CREATE_RECEIPT_SCHEMA,
|
|
1110
|
+
annotations: REMOTE_CREATE,
|
|
1111
|
+
write: true,
|
|
1112
|
+
run: createReceipt
|
|
1113
|
+
}];
|
|
1114
|
+
//#endregion
|
|
1115
|
+
//#region src/mcp/tools-reversal.ts
|
|
1116
|
+
const PREVIEW_REVERSAL = "preview_reversal";
|
|
1117
|
+
const REVERSE_INVOICE = "reverse_invoice";
|
|
1118
|
+
const REVERSE_RECEIPT = "reverse_receipt";
|
|
1119
|
+
const DOCUMENT_KINDS = ["invoice", "receipt"];
|
|
1120
|
+
const ALREADY_REVERSED_MESSAGE = "már sztornózták";
|
|
1121
|
+
function assertReversibleInvoice(details) {
|
|
1122
|
+
const { number, type } = details.header;
|
|
1123
|
+
if (type === "reversal") throw new ToolInputError(`A(z) ${number} sztornó számla, nem sztornózható.`);
|
|
1124
|
+
if (type === "proforma") throw new ToolInputError(`A(z) ${number} díjbekérő, nem sztornózható. Díjbekérőt a Számlázz.hu felületén vagy a kassza deleteProforma függvényével lehet törölni.`);
|
|
1125
|
+
if (type === "deliveryNote") throw new ToolInputError(`A(z) ${number} szállítólevél, nem sztornózható.`);
|
|
1126
|
+
}
|
|
1127
|
+
function assertReversibleReceipt(receipt) {
|
|
1128
|
+
if (receipt.type === "reversal") throw new ToolInputError(`A(z) ${receipt.number} sztornónyugta, nem sztornózható.`);
|
|
1129
|
+
}
|
|
1130
|
+
async function previewInvoiceReversal(number, context) {
|
|
1131
|
+
const details = await context.client().invoices.get(number, { includePdf: false });
|
|
1132
|
+
assertReversibleInvoice(details);
|
|
1133
|
+
if (details.header.reversed === true) throw new ToolInputError(`A(z) ${number} számlát már sztornózták.`);
|
|
1134
|
+
const confirmation = await context.confirmations.issue(REVERSE_INVOICE, { invoiceNumber: number });
|
|
1135
|
+
const currency = details.header.currency ?? "HUF";
|
|
1136
|
+
return {
|
|
1137
|
+
summary: lines(`Sztornó előnézete (még NEM készült el): a(z) ${number} számla sztornózása. Vevő: ${details.buyer.name}, bruttó ${money(details.totals.grossAmount, currency)}, kelt: ${details.header.issueDate}.`, `Mutasd meg a felhasználónak. Ha jóváhagyja, hívd meg a ${REVERSE_INVOICE} eszközt az invoiceNumber mezőben a számlaszámmal, a confirmation mezőben ezzel a kóddal: ${confirmation.token} (lejár: ${confirmation.expiresAt}).`),
|
|
1138
|
+
data: {
|
|
1139
|
+
document: "invoice",
|
|
1140
|
+
number,
|
|
1141
|
+
buyer: details.buyer.name,
|
|
1142
|
+
issueDate: details.header.issueDate,
|
|
1143
|
+
grossTotal: details.totals.grossAmount,
|
|
1144
|
+
currency,
|
|
1145
|
+
confirmation: confirmation.token,
|
|
1146
|
+
confirmationExpiresAt: confirmation.expiresAt
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
async function previewReceiptReversal(number, context) {
|
|
1151
|
+
const receipt = await context.client().receipts.get({
|
|
1152
|
+
receiptNumber: number,
|
|
1153
|
+
downloadPdf: false
|
|
1154
|
+
});
|
|
1155
|
+
assertReversibleReceipt(receipt);
|
|
1156
|
+
if (receipt.isReversed) throw new ToolInputError(`A(z) ${number} nyugtát már sztornózták.`);
|
|
1157
|
+
const confirmation = await context.confirmations.issue(REVERSE_RECEIPT, { receiptNumber: number });
|
|
1158
|
+
return {
|
|
1159
|
+
summary: lines(`Sztornó előnézete (még NEM készült el): a(z) ${number} nyugta sztornózása, bruttó ${money(receipt.totals.grossAmount, receipt.currency)}, kelt: ${receipt.issueDate}.`, `Mutasd meg a felhasználónak. Ha jóváhagyja, hívd meg a ${REVERSE_RECEIPT} eszközt a receiptNumber mezőben a nyugtaszámmal, a confirmation mezőben ezzel a kóddal: ${confirmation.token} (lejár: ${confirmation.expiresAt}).`),
|
|
1160
|
+
data: {
|
|
1161
|
+
document: "receipt",
|
|
1162
|
+
number,
|
|
1163
|
+
issueDate: receipt.issueDate,
|
|
1164
|
+
grossTotal: receipt.totals.grossAmount,
|
|
1165
|
+
currency: receipt.currency,
|
|
1166
|
+
confirmation: confirmation.token,
|
|
1167
|
+
confirmationExpiresAt: confirmation.expiresAt
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
async function previewReversal(args, context) {
|
|
1172
|
+
assertKnownKeys(args, ["document", "number"], "");
|
|
1173
|
+
const document = optionalEnum(args, "document", "", DOCUMENT_KINDS);
|
|
1174
|
+
if (document === void 0) throw new ToolInputError("A document mező kötelező: invoice (számla) vagy receipt (nyugta).");
|
|
1175
|
+
const number = requiredString(args, "number", "");
|
|
1176
|
+
return document === "invoice" ? previewInvoiceReversal(number, context) : previewReceiptReversal(number, context);
|
|
1177
|
+
}
|
|
1178
|
+
function reversalOutput(document, reversedNumber, number, created) {
|
|
1179
|
+
const label = document === "invoice" ? "számla" : "nyugta";
|
|
1180
|
+
return {
|
|
1181
|
+
summary: created ? `A(z) ${reversedNumber} ${label} sztornózva${number === void 0 ? "" : `, sztornó: ${number}`}.` : `A(z) ${reversedNumber} ${label} már sztornózva volt${number === void 0 ? "" : ` (sztornó: ${number})`}, nem sztornóztam újra.`,
|
|
1182
|
+
data: {
|
|
1183
|
+
created,
|
|
1184
|
+
document,
|
|
1185
|
+
reversedNumber,
|
|
1186
|
+
number
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
async function reverseInvoice(args, context) {
|
|
1191
|
+
assertKnownKeys(args, ["invoiceNumber", "confirmation"], "");
|
|
1192
|
+
const invoiceNumber = requiredString(args, "invoiceNumber", "");
|
|
1193
|
+
await context.confirmations.verify(REVERSE_INVOICE, { invoiceNumber }, args.confirmation, PREVIEW_REVERSAL);
|
|
1194
|
+
const kassza = context.client();
|
|
1195
|
+
const externalId = `${invoiceNumber}/SS`;
|
|
1196
|
+
const lookup = () => kassza.invoices.find({ externalId }, { includePdf: false });
|
|
1197
|
+
const existing = await lookup();
|
|
1198
|
+
if (existing) return reversalOutput("invoice", invoiceNumber, existing.header.number, false);
|
|
1199
|
+
const original = await kassza.invoices.get(invoiceNumber, { includePdf: false });
|
|
1200
|
+
assertReversibleInvoice(original);
|
|
1201
|
+
if (original.header.reversed === true) return reversalOutput("invoice", invoiceNumber, void 0, false);
|
|
1202
|
+
try {
|
|
1203
|
+
return reversalOutput("invoice", invoiceNumber, (await kassza.invoices.reverse({
|
|
1204
|
+
invoiceNumber,
|
|
1205
|
+
externalId,
|
|
1206
|
+
downloadPdf: false
|
|
1207
|
+
})).number, true);
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
if (!require_issue.isUncertainOutcome(error)) throw error;
|
|
1210
|
+
const recovered = await require_issue.recoverAfterFailure(lookup, context.recoveryDelayMs, void 0);
|
|
1211
|
+
if (!recovered) throw error;
|
|
1212
|
+
return reversalOutput("invoice", invoiceNumber, recovered.header.number, error.category !== "duplicate");
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
async function reverseReceipt(args, context) {
|
|
1216
|
+
assertKnownKeys(args, ["receiptNumber", "confirmation"], "");
|
|
1217
|
+
const receiptNumber = requiredString(args, "receiptNumber", "");
|
|
1218
|
+
await context.confirmations.verify(REVERSE_RECEIPT, { receiptNumber }, args.confirmation, PREVIEW_REVERSAL);
|
|
1219
|
+
const kassza = context.client();
|
|
1220
|
+
const original = await kassza.receipts.get({
|
|
1221
|
+
receiptNumber,
|
|
1222
|
+
downloadPdf: false
|
|
1223
|
+
});
|
|
1224
|
+
assertReversibleReceipt(original);
|
|
1225
|
+
if (original.isReversed) return reversalOutput("receipt", receiptNumber, void 0, false);
|
|
1226
|
+
try {
|
|
1227
|
+
return reversalOutput("receipt", receiptNumber, (await kassza.receipts.reverse({
|
|
1228
|
+
receiptNumber,
|
|
1229
|
+
callId: `${receiptNumber}/SN`,
|
|
1230
|
+
downloadPdf: false
|
|
1231
|
+
})).number, true);
|
|
1232
|
+
} catch (error) {
|
|
1233
|
+
if (!require_issue.isUncertainOutcome(error)) throw error;
|
|
1234
|
+
if (!(await kassza.receipts.get({
|
|
1235
|
+
receiptNumber,
|
|
1236
|
+
downloadPdf: false
|
|
1237
|
+
})).isReversed) throw error;
|
|
1238
|
+
return reversalOutput("receipt", receiptNumber, void 0, !(error.category === "duplicate" || error.message.includes(ALREADY_REVERSED_MESSAGE)));
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
const REVERSAL_TOOLS = [
|
|
1242
|
+
{
|
|
1243
|
+
name: PREVIEW_REVERSAL,
|
|
1244
|
+
title: "Sztornó előnézete",
|
|
1245
|
+
description: "Lekéri a sztornózandó számlát vagy nyugtát, ellenőrzi, hogy sztornózható-e, és ad egy megerősítő kódot a reverse_invoice vagy reverse_receipt eszközhöz. Semmit nem sztornóz.",
|
|
1246
|
+
inputSchema: objectSchema({
|
|
1247
|
+
document: {
|
|
1248
|
+
type: "string",
|
|
1249
|
+
enum: [...DOCUMENT_KINDS],
|
|
1250
|
+
description: "invoice (számla) vagy receipt (nyugta)."
|
|
1251
|
+
},
|
|
1252
|
+
number: {
|
|
1253
|
+
...TEXT_PROPERTY,
|
|
1254
|
+
description: "A számla- vagy nyugtaszám."
|
|
1255
|
+
}
|
|
1256
|
+
}, ["document", "number"]),
|
|
1257
|
+
annotations: REMOTE_READ,
|
|
1258
|
+
write: false,
|
|
1259
|
+
run: previewReversal
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
name: REVERSE_INVOICE,
|
|
1263
|
+
title: "Számla sztornózása",
|
|
1264
|
+
description: "Sztornó számlát állít ki a megadott számlához. Csak a preview_reversal által adott megerősítő kóddal működik, és csak a felhasználó jóváhagyása után hívd. Egy számlát csak egyszer sztornóz.",
|
|
1265
|
+
inputSchema: objectSchema({
|
|
1266
|
+
invoiceNumber: TEXT_PROPERTY,
|
|
1267
|
+
confirmation: CONFIRMATION_PROPERTY
|
|
1268
|
+
}, ["invoiceNumber", "confirmation"]),
|
|
1269
|
+
annotations: REMOTE_REVERSE,
|
|
1270
|
+
write: true,
|
|
1271
|
+
run: reverseInvoice
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
name: REVERSE_RECEIPT,
|
|
1275
|
+
title: "Nyugta sztornózása",
|
|
1276
|
+
description: "Sztornónyugtát állít ki a megadott nyugtához. Csak a preview_reversal által adott megerősítő kóddal működik, és csak a felhasználó jóváhagyása után hívd. Egy nyugtát csak egyszer sztornóz.",
|
|
1277
|
+
inputSchema: objectSchema({
|
|
1278
|
+
receiptNumber: TEXT_PROPERTY,
|
|
1279
|
+
confirmation: CONFIRMATION_PROPERTY
|
|
1280
|
+
}, ["receiptNumber", "confirmation"]),
|
|
1281
|
+
annotations: REMOTE_REVERSE,
|
|
1282
|
+
write: true,
|
|
1283
|
+
run: reverseReceipt
|
|
1284
|
+
}
|
|
1285
|
+
];
|
|
1286
|
+
//#endregion
|
|
1287
|
+
//#region src/mcp/tools.ts
|
|
1288
|
+
const KASSZA_MCP_TOOLS = [
|
|
1289
|
+
...INVOICE_TOOLS,
|
|
1290
|
+
...RECEIPT_TOOLS,
|
|
1291
|
+
...REVERSAL_TOOLS,
|
|
1292
|
+
...LOOKUP_TOOLS
|
|
1293
|
+
];
|
|
1294
|
+
//#endregion
|
|
1295
|
+
//#region src/mcp/server.ts
|
|
1296
|
+
const SERVER_INFO = {
|
|
1297
|
+
name: "kassza",
|
|
1298
|
+
title: "kassza: Számlázz.hu számlák és nyugták",
|
|
1299
|
+
version: KASSZA_VERSION
|
|
1300
|
+
};
|
|
1301
|
+
const LIST_TTL_MS = 36e5;
|
|
1302
|
+
const INSTRUCTIONS = [
|
|
1303
|
+
"A kassza a Számlázz.hu Számla Agenten keresztül állít ki és kérdez le számlákat és nyugtákat.",
|
|
1304
|
+
"Kiállítás vagy sztornó előtt mindig hívd meg a megfelelő előnézeti eszközt (preview_invoice, preview_receipt, preview_reversal), mutasd meg az eredményt a felhasználónak, és csak a kifejezett jóváhagyása után hívd az író eszközt a kapott confirmation kóddal.",
|
|
1305
|
+
"Az író eszközök csak akkor érhetők el, ha a szervert KASSZA_MCP_ALLOW_WRITE=1 beállítással indították. Próbához Számlázz.hu tesztfiókot ajánlunk."
|
|
1306
|
+
].join(" ");
|
|
1307
|
+
const WRITE_DISABLED_MESSAGE = "Az írási eszközök ki vannak kapcsolva. A kiállításhoz és a sztornóhoz indítsd a szervert a KASSZA_MCP_ALLOW_WRITE=1 környezeti változóval (vagy a kassza mcp --allow-write kapcsolóval).";
|
|
1308
|
+
function describeError(error) {
|
|
1309
|
+
if (error instanceof ToolInputError) return error.message;
|
|
1310
|
+
if (require_errors.isSzamlazzError(error)) return error.hint ? `${error.message}\nTipp: ${error.hint}` : error.message;
|
|
1311
|
+
if (error instanceof Error) return `Váratlan hiba: ${error.message}`;
|
|
1312
|
+
return "Váratlan hiba történt.";
|
|
1313
|
+
}
|
|
1314
|
+
function modernVersionOf(params) {
|
|
1315
|
+
const meta = params._meta;
|
|
1316
|
+
if (!isJsonObject(meta) || !("io.modelcontextprotocol/protocolVersion" in meta)) return void 0;
|
|
1317
|
+
const version = meta[META_PROTOCOL_VERSION];
|
|
1318
|
+
if (typeof version !== "string" || version.trim() === "") throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, `A _meta["${META_PROTOCOL_VERSION}"] mezőnek nem üres szövegnek kell lennie.`);
|
|
1319
|
+
if (!MCP_MODERN_VERSIONS.includes(version)) throw new McpProtocolError(MCP_UNSUPPORTED_PROTOCOL_VERSION, "Unsupported protocol version", { data: {
|
|
1320
|
+
supported: [...MCP_SUPPORTED_VERSIONS],
|
|
1321
|
+
requested: version
|
|
1322
|
+
} });
|
|
1323
|
+
if (!isJsonObject(meta["io.modelcontextprotocol/clientCapabilities"])) throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, `Hiányzik a kötelező _meta["${META_CLIENT_CAPABILITIES}"] mező.`);
|
|
1324
|
+
return version;
|
|
1325
|
+
}
|
|
1326
|
+
function toolDefinition(tool) {
|
|
1327
|
+
return {
|
|
1328
|
+
name: tool.name,
|
|
1329
|
+
title: tool.title,
|
|
1330
|
+
description: tool.description,
|
|
1331
|
+
inputSchema: tool.inputSchema,
|
|
1332
|
+
annotations: {
|
|
1333
|
+
title: tool.title,
|
|
1334
|
+
...tool.annotations
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
function createKasszaMcpServer(options = {}) {
|
|
1339
|
+
const allowWrite = options.allowWrite === true;
|
|
1340
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
1341
|
+
const recoveryDelayMs = require_issue.resolveRecoveryDelay(options.recoveryDelayMs ?? 1e3);
|
|
1342
|
+
const confirmations = createConfirmations({
|
|
1343
|
+
secret: options.confirmationSecret,
|
|
1344
|
+
ttlMs: options.confirmationTtlMs,
|
|
1345
|
+
now
|
|
1346
|
+
});
|
|
1347
|
+
let client;
|
|
1348
|
+
const context = {
|
|
1349
|
+
client: () => {
|
|
1350
|
+
client ??= require_client.createKassza(options);
|
|
1351
|
+
return client;
|
|
1352
|
+
},
|
|
1353
|
+
defaults: options.defaults ?? {},
|
|
1354
|
+
confirmations,
|
|
1355
|
+
now,
|
|
1356
|
+
recoveryDelayMs
|
|
1357
|
+
};
|
|
1358
|
+
const visibleTools = KASSZA_MCP_TOOLS.filter((tool) => allowWrite || !tool.write);
|
|
1359
|
+
const listTools = (params) => {
|
|
1360
|
+
if (params.cursor !== void 0) throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, "Érvénytelen cursor: az eszközlista egyetlen oldalból áll.");
|
|
1361
|
+
return { tools: visibleTools.map(toolDefinition) };
|
|
1362
|
+
};
|
|
1363
|
+
const callTool = async (params) => {
|
|
1364
|
+
const name = params.name;
|
|
1365
|
+
if (typeof name !== "string") throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, "Hiányzik az eszköz neve (name).");
|
|
1366
|
+
const tool = KASSZA_MCP_TOOLS.find((candidate) => candidate.name === name);
|
|
1367
|
+
if (!tool) throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, `Ismeretlen eszköz: ${name}`);
|
|
1368
|
+
const args = params.arguments ?? {};
|
|
1369
|
+
if (!isJsonObject(args)) throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, "Az arguments mezőnek objektumnak kell lennie.");
|
|
1370
|
+
if (tool.write && !allowWrite) return {
|
|
1371
|
+
content: [{
|
|
1372
|
+
type: "text",
|
|
1373
|
+
text: WRITE_DISABLED_MESSAGE
|
|
1374
|
+
}],
|
|
1375
|
+
isError: true
|
|
1376
|
+
};
|
|
1377
|
+
try {
|
|
1378
|
+
const output = await tool.run(args, context);
|
|
1379
|
+
const data = plainJson(output.data);
|
|
1380
|
+
return {
|
|
1381
|
+
content: [{
|
|
1382
|
+
type: "text",
|
|
1383
|
+
text: output.summary
|
|
1384
|
+
}, {
|
|
1385
|
+
type: "text",
|
|
1386
|
+
text: JSON.stringify(data, null, 2)
|
|
1387
|
+
}],
|
|
1388
|
+
structuredContent: data,
|
|
1389
|
+
isError: false
|
|
1390
|
+
};
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
return {
|
|
1393
|
+
content: [{
|
|
1394
|
+
type: "text",
|
|
1395
|
+
text: describeError(error)
|
|
1396
|
+
}],
|
|
1397
|
+
isError: true
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
const modern = async (request) => {
|
|
1402
|
+
const meta = { [META_SERVER_INFO]: SERVER_INFO };
|
|
1403
|
+
switch (request.method) {
|
|
1404
|
+
case "server/discover": return {
|
|
1405
|
+
resultType: "complete",
|
|
1406
|
+
supportedVersions: [...MCP_SUPPORTED_VERSIONS],
|
|
1407
|
+
capabilities: { tools: {} },
|
|
1408
|
+
instructions: INSTRUCTIONS,
|
|
1409
|
+
ttlMs: LIST_TTL_MS,
|
|
1410
|
+
cacheScope: "private",
|
|
1411
|
+
_meta: meta
|
|
1412
|
+
};
|
|
1413
|
+
case "tools/list": return {
|
|
1414
|
+
resultType: "complete",
|
|
1415
|
+
...listTools(request.params),
|
|
1416
|
+
ttlMs: LIST_TTL_MS,
|
|
1417
|
+
cacheScope: "private",
|
|
1418
|
+
_meta: meta
|
|
1419
|
+
};
|
|
1420
|
+
case "tools/call": return {
|
|
1421
|
+
resultType: "complete",
|
|
1422
|
+
...await callTool(request.params),
|
|
1423
|
+
_meta: meta
|
|
1424
|
+
};
|
|
1425
|
+
default: throw new McpProtocolError(JSON_RPC_METHOD_NOT_FOUND, `Ismeretlen metódus: ${request.method}`);
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
const initialize = (request, session) => {
|
|
1429
|
+
const requested = request.params.protocolVersion;
|
|
1430
|
+
if (typeof requested !== "string") throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, "Hiányzik a protocolVersion mező.");
|
|
1431
|
+
const version = MCP_LEGACY_VERSIONS.includes(requested) ? requested : MCP_LEGACY_VERSIONS[0] ?? requested;
|
|
1432
|
+
session.legacyVersion = version;
|
|
1433
|
+
return {
|
|
1434
|
+
protocolVersion: version,
|
|
1435
|
+
capabilities: { tools: { listChanged: false } },
|
|
1436
|
+
serverInfo: SERVER_INFO,
|
|
1437
|
+
instructions: INSTRUCTIONS
|
|
1438
|
+
};
|
|
1439
|
+
};
|
|
1440
|
+
const legacy = async (request, session) => {
|
|
1441
|
+
if (request.method === "ping") return {};
|
|
1442
|
+
if (session.legacyVersion === void 0) throw new McpProtocolError(JSON_RPC_INVALID_PARAMS, `Hiányzik a kötelező _meta["${META_PROTOCOL_VERSION}"] mező. 2026-07-28 előtti protokollnál előbb initialize kérést kell küldeni.`);
|
|
1443
|
+
if (request.method === "tools/list") return listTools(request.params);
|
|
1444
|
+
if (request.method === "tools/call") return callTool(request.params);
|
|
1445
|
+
throw new McpProtocolError(JSON_RPC_METHOD_NOT_FOUND, `Ismeretlen metódus: ${request.method}`);
|
|
1446
|
+
};
|
|
1447
|
+
const respond = async (request, session) => {
|
|
1448
|
+
try {
|
|
1449
|
+
if (request.method === "initialize") return success(request.id, initialize(request, session));
|
|
1450
|
+
const result = modernVersionOf(request.params) === void 0 ? await legacy(request, session) : await modern(request);
|
|
1451
|
+
return success(request.id, result);
|
|
1452
|
+
} catch (error) {
|
|
1453
|
+
if (error instanceof McpProtocolError) return failure(request.id, error.code, error.message, error.data);
|
|
1454
|
+
return failure(request.id, JSON_RPC_INTERNAL_ERROR, "Belső hiba a kassza MCP szerverben.");
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
const handleSingle = async (message, session) => {
|
|
1458
|
+
try {
|
|
1459
|
+
const parsed = parseMcpMessage(message);
|
|
1460
|
+
if (parsed.kind !== "request") return void 0;
|
|
1461
|
+
return await respond(parsed, session);
|
|
1462
|
+
} catch (error) {
|
|
1463
|
+
if (error instanceof McpProtocolError) return protocolFailure(error);
|
|
1464
|
+
return failure(null, JSON_RPC_INTERNAL_ERROR, "Belső hiba a kassza MCP szerverben.");
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
return {
|
|
1468
|
+
tools: KASSZA_MCP_TOOLS.map((tool) => ({
|
|
1469
|
+
name: tool.name,
|
|
1470
|
+
title: tool.title,
|
|
1471
|
+
write: tool.write
|
|
1472
|
+
})),
|
|
1473
|
+
connect() {
|
|
1474
|
+
const session = { legacyVersion: void 0 };
|
|
1475
|
+
const handle = async (message) => {
|
|
1476
|
+
if (!Array.isArray(message)) return handleSingle(message, session);
|
|
1477
|
+
if (message.length === 0) return failure(null, JSON_RPC_INVALID_REQUEST, "Az üres batch érvénytelen.");
|
|
1478
|
+
const responses = [];
|
|
1479
|
+
for (const entry of message) {
|
|
1480
|
+
const response = await handleSingle(entry, session);
|
|
1481
|
+
if (response) responses.push(response);
|
|
1482
|
+
}
|
|
1483
|
+
return responses.length === 0 ? void 0 : responses;
|
|
1484
|
+
};
|
|
1485
|
+
return {
|
|
1486
|
+
handle,
|
|
1487
|
+
async handleLine(line) {
|
|
1488
|
+
const trimmed = line.trim();
|
|
1489
|
+
if (trimmed === "") return void 0;
|
|
1490
|
+
let message;
|
|
1491
|
+
try {
|
|
1492
|
+
message = JSON.parse(trimmed);
|
|
1493
|
+
} catch {
|
|
1494
|
+
return JSON.stringify(failure(null, JSON_RPC_PARSE_ERROR, "Érvénytelen JSON."));
|
|
1495
|
+
}
|
|
1496
|
+
const response = await handle(message);
|
|
1497
|
+
return response === void 0 ? void 0 : JSON.stringify(response);
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
//#endregion
|
|
1504
|
+
Object.defineProperty(exports, "DEFAULT_CONFIRMATION_TTL_MS", {
|
|
1505
|
+
enumerable: true,
|
|
1506
|
+
get: function() {
|
|
1507
|
+
return DEFAULT_CONFIRMATION_TTL_MS;
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
Object.defineProperty(exports, "KASSZA_VERSION", {
|
|
1511
|
+
enumerable: true,
|
|
1512
|
+
get: function() {
|
|
1513
|
+
return KASSZA_VERSION;
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
Object.defineProperty(exports, "MCP_LEGACY_VERSIONS", {
|
|
1517
|
+
enumerable: true,
|
|
1518
|
+
get: function() {
|
|
1519
|
+
return MCP_LEGACY_VERSIONS;
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
Object.defineProperty(exports, "MCP_MODERN_VERSIONS", {
|
|
1523
|
+
enumerable: true,
|
|
1524
|
+
get: function() {
|
|
1525
|
+
return MCP_MODERN_VERSIONS;
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
Object.defineProperty(exports, "MCP_SUPPORTED_VERSIONS", {
|
|
1529
|
+
enumerable: true,
|
|
1530
|
+
get: function() {
|
|
1531
|
+
return MCP_SUPPORTED_VERSIONS;
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1534
|
+
Object.defineProperty(exports, "ToolInputError", {
|
|
1535
|
+
enumerable: true,
|
|
1536
|
+
get: function() {
|
|
1537
|
+
return ToolInputError;
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1540
|
+
Object.defineProperty(exports, "canonicalJson", {
|
|
1541
|
+
enumerable: true,
|
|
1542
|
+
get: function() {
|
|
1543
|
+
return canonicalJson;
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
Object.defineProperty(exports, "createConfirmations", {
|
|
1547
|
+
enumerable: true,
|
|
1548
|
+
get: function() {
|
|
1549
|
+
return createConfirmations;
|
|
1550
|
+
}
|
|
1551
|
+
});
|
|
1552
|
+
Object.defineProperty(exports, "createKasszaMcpServer", {
|
|
1553
|
+
enumerable: true,
|
|
1554
|
+
get: function() {
|
|
1555
|
+
return createKasszaMcpServer;
|
|
1556
|
+
}
|
|
1557
|
+
});
|