@casys/mcp-einvoice 0.1.0 → 0.1.1
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/mcp-einvoice.mjs +120 -33
- package/package.json +2 -2
package/mcp-einvoice.mjs
CHANGED
|
@@ -35907,6 +35907,37 @@ async function loadYamlAuth(path) {
|
|
|
35907
35907
|
var MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
|
|
35908
35908
|
var MCP_APP_URI_SCHEME = "ui:";
|
|
35909
35909
|
|
|
35910
|
+
// node_modules/@casys/mcp-server/src/ui/viewer-utils.ts
|
|
35911
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["shared", "dist", "node_modules", ".cache", ".vite"]);
|
|
35912
|
+
function fileUrlToPath(url2) {
|
|
35913
|
+
const decoded = decodeURIComponent(url2.pathname);
|
|
35914
|
+
if (/^\/[A-Za-z]:\//.test(decoded)) return decoded.slice(1);
|
|
35915
|
+
if (url2.host.length > 0) return `//${url2.host}${decoded}`;
|
|
35916
|
+
return decoded;
|
|
35917
|
+
}
|
|
35918
|
+
function resolveViewerDistPath(moduleUrl, viewerName, exists) {
|
|
35919
|
+
const candidates = [
|
|
35920
|
+
fileUrlToPath(new URL(`./src/ui/dist/${viewerName}/index.html`, moduleUrl)),
|
|
35921
|
+
fileUrlToPath(new URL(`./ui-dist/${viewerName}/index.html`, moduleUrl))
|
|
35922
|
+
];
|
|
35923
|
+
for (const candidate of candidates) {
|
|
35924
|
+
if (exists(candidate)) return candidate;
|
|
35925
|
+
}
|
|
35926
|
+
return null;
|
|
35927
|
+
}
|
|
35928
|
+
function discoverViewers(uiDir, fs) {
|
|
35929
|
+
const entries = fs.readDir(uiDir);
|
|
35930
|
+
const viewers = [];
|
|
35931
|
+
for (const entry of entries) {
|
|
35932
|
+
if (!entry.isDirectory) continue;
|
|
35933
|
+
if (entry.name.startsWith(".")) continue;
|
|
35934
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
35935
|
+
if (!fs.hasIndexHtml(uiDir, entry.name)) continue;
|
|
35936
|
+
viewers.push(entry.name);
|
|
35937
|
+
}
|
|
35938
|
+
return viewers.sort();
|
|
35939
|
+
}
|
|
35940
|
+
|
|
35910
35941
|
// node_modules/@casys/mcp-server/src/security/csp.ts
|
|
35911
35942
|
function buildCspHeader(options = {}) {
|
|
35912
35943
|
const allowInline = options.allowInline !== false;
|
|
@@ -36748,6 +36779,63 @@ var ConcurrentMCPServer = class _ConcurrentMCPServer {
|
|
|
36748
36779
|
}
|
|
36749
36780
|
this.log(`Registered ${resources.length} resources`);
|
|
36750
36781
|
}
|
|
36782
|
+
/**
|
|
36783
|
+
* Register MCP Apps viewers with automatic dist path resolution.
|
|
36784
|
+
*
|
|
36785
|
+
* Replaces the manual pattern of: enumerate viewers → resolve paths → register resources.
|
|
36786
|
+
* Each viewer gets a `ui://{prefix}/{viewerName}` resource URI.
|
|
36787
|
+
*
|
|
36788
|
+
* Viewers whose dist is not found are skipped with a warning (not an error),
|
|
36789
|
+
* so that the server can start in dev without building UIs first.
|
|
36790
|
+
*
|
|
36791
|
+
* @returns Summary of registered and skipped viewers
|
|
36792
|
+
*/
|
|
36793
|
+
registerViewers(config2) {
|
|
36794
|
+
if (!config2.prefix) {
|
|
36795
|
+
throw new Error("[ConcurrentMCPServer] registerViewers: prefix is required");
|
|
36796
|
+
}
|
|
36797
|
+
let viewerNames;
|
|
36798
|
+
if (config2.viewers) {
|
|
36799
|
+
viewerNames = config2.viewers;
|
|
36800
|
+
} else if (config2.discover) {
|
|
36801
|
+
viewerNames = discoverViewers(config2.discover.uiDir, config2.discover);
|
|
36802
|
+
} else {
|
|
36803
|
+
viewerNames = [];
|
|
36804
|
+
}
|
|
36805
|
+
const humanNameFn = config2.humanName ?? defaultHumanName;
|
|
36806
|
+
const registered = [];
|
|
36807
|
+
const skipped = [];
|
|
36808
|
+
for (const viewerName of viewerNames) {
|
|
36809
|
+
const distPath = resolveViewerDistPath(config2.moduleUrl, viewerName, config2.exists);
|
|
36810
|
+
if (!distPath) {
|
|
36811
|
+
this.log(
|
|
36812
|
+
`Warning: UI not built for ui://${config2.prefix}/${viewerName}. Run the UI build step first.`
|
|
36813
|
+
);
|
|
36814
|
+
skipped.push(viewerName);
|
|
36815
|
+
continue;
|
|
36816
|
+
}
|
|
36817
|
+
const resourceUri = `ui://${config2.prefix}/${viewerName}`;
|
|
36818
|
+
const readFile3 = config2.readFile;
|
|
36819
|
+
const currentDistPath = distPath;
|
|
36820
|
+
this.registerResource(
|
|
36821
|
+
{
|
|
36822
|
+
uri: resourceUri,
|
|
36823
|
+
name: humanNameFn(viewerName),
|
|
36824
|
+
description: `MCP App: ${viewerName}`,
|
|
36825
|
+
mimeType: MCP_APP_MIME_TYPE
|
|
36826
|
+
},
|
|
36827
|
+
async () => {
|
|
36828
|
+
const html = await Promise.resolve(readFile3(currentDistPath));
|
|
36829
|
+
return { uri: resourceUri, mimeType: MCP_APP_MIME_TYPE, text: html };
|
|
36830
|
+
}
|
|
36831
|
+
);
|
|
36832
|
+
registered.push(viewerName);
|
|
36833
|
+
}
|
|
36834
|
+
if (registered.length > 0) {
|
|
36835
|
+
this.log(`Registered ${registered.length} viewer(s): ${registered.join(", ")}`);
|
|
36836
|
+
}
|
|
36837
|
+
return { registered, skipped };
|
|
36838
|
+
}
|
|
36751
36839
|
/**
|
|
36752
36840
|
* Start the MCP server with stdio transport
|
|
36753
36841
|
*/
|
|
@@ -37609,6 +37697,9 @@ data: ${JSON.stringify(message2)}
|
|
|
37609
37697
|
}
|
|
37610
37698
|
}
|
|
37611
37699
|
};
|
|
37700
|
+
function defaultHumanName(name) {
|
|
37701
|
+
return name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
37702
|
+
}
|
|
37612
37703
|
|
|
37613
37704
|
// src/generated-store.ts
|
|
37614
37705
|
var EXPIRY_MS = 10 * 60 * 1e3;
|
|
@@ -37634,6 +37725,13 @@ function getGenerated(id) {
|
|
|
37634
37725
|
}
|
|
37635
37726
|
|
|
37636
37727
|
// src/tools/invoice.ts
|
|
37728
|
+
function uint8ToBase64(data) {
|
|
37729
|
+
let binary = "";
|
|
37730
|
+
for (let i = 0; i < data.length; i += 8192) {
|
|
37731
|
+
binary += String.fromCharCode(...data.subarray(i, i + 8192));
|
|
37732
|
+
}
|
|
37733
|
+
return btoa(binary);
|
|
37734
|
+
}
|
|
37637
37735
|
function normalizeInvoiceForGenerate(inv) {
|
|
37638
37736
|
const normalized = { ...inv };
|
|
37639
37737
|
for (const party of ["seller", "buyer"]) {
|
|
@@ -37694,6 +37792,7 @@ function mapToViewerPreview(inv) {
|
|
|
37694
37792
|
direction: "sent"
|
|
37695
37793
|
};
|
|
37696
37794
|
}
|
|
37795
|
+
var INVOICE_SCHEMA_DESCRIPTION = 'Invoice data in Iopole format. Required fields: invoiceId (string, max 20 chars): invoice number e.g. "CASYS-001"; invoiceDate (string, YYYY-MM-DD): issue date; type (number): invoice type code, usually 380 for commercial invoice; processType (string): e.g. "B1" for goods; invoiceDueDate (string, YYYY-MM-DD): due date; seller (object): { name, siren, siret, country, vatNumber, electronicAddress (format "0225:siren_siret"), identifiers: [{ type: "ELECTRONIC_ADDRESS", value: "siren_siret", scheme: "0225" }] }; buyer (object): same structure as seller; monetary (object): { invoiceCurrency: "EUR", invoiceAmount: { amount }, payableAmount: { amount }, taxTotalAmount: { amount, currency: "EUR" }, lineTotalAmount: { amount }, taxBasisTotalAmount: { amount } }; taxDetails (array): [{ percent, taxType: "VAT", categoryCode: "S", taxableAmount: { amount }, taxAmount: { amount } }]; lines (array): [{ id: "1", item: { name }, billedQuantity: { quantity, unitCode: "DAY"|"C62" }, price: { netAmount: { amount }, baseQuantity: { quantity: 1, unitCode } }, totalAmount: { amount }, taxDetail: { percent, taxType: "VAT", categoryCode: "S" } }]; paymentTerms (string, optional): payment conditions text; notes (array, optional): [{ type: { code: "PMT" }, content: "..." }]';
|
|
37697
37796
|
var invoiceTools = [
|
|
37698
37797
|
// ── Emit ────────────────────────────────────────────────
|
|
37699
37798
|
{
|
|
@@ -37737,7 +37836,12 @@ var invoiceTools = [
|
|
|
37737
37836
|
if (!lower.endsWith(".pdf") && !lower.endsWith(".xml")) {
|
|
37738
37837
|
throw new Error("[einvoice_invoice_emit] filename must end in .pdf or .xml");
|
|
37739
37838
|
}
|
|
37740
|
-
|
|
37839
|
+
let binaryString;
|
|
37840
|
+
try {
|
|
37841
|
+
binaryString = atob(input.file_base64);
|
|
37842
|
+
} catch {
|
|
37843
|
+
throw new Error("[einvoice_invoice_emit] 'file_base64' is not valid base64");
|
|
37844
|
+
}
|
|
37741
37845
|
const bytes = new Uint8Array(binaryString.length);
|
|
37742
37846
|
for (let i = 0; i < binaryString.length; i++) {
|
|
37743
37847
|
bytes[i] = binaryString.charCodeAt(i);
|
|
@@ -37905,12 +38009,7 @@ var invoiceTools = [
|
|
|
37905
38009
|
throw new Error("[einvoice_invoice_download] 'id' is required");
|
|
37906
38010
|
}
|
|
37907
38011
|
const { data, contentType } = await ctx.adapter.downloadInvoice(input.id);
|
|
37908
|
-
|
|
37909
|
-
for (let i = 0; i < data.length; i += 8192) {
|
|
37910
|
-
binary += String.fromCharCode(...data.subarray(i, i + 8192));
|
|
37911
|
-
}
|
|
37912
|
-
const base643 = btoa(binary);
|
|
37913
|
-
return { content_type: contentType, data_base64: base643, size_bytes: data.length };
|
|
38012
|
+
return { content_type: contentType, data_base64: uint8ToBase64(data), size_bytes: data.length };
|
|
37914
38013
|
}
|
|
37915
38014
|
},
|
|
37916
38015
|
// ── Download readable ───────────────────────────────────
|
|
@@ -37930,12 +38029,7 @@ var invoiceTools = [
|
|
|
37930
38029
|
throw new Error("[einvoice_invoice_download_readable] 'id' is required");
|
|
37931
38030
|
}
|
|
37932
38031
|
const { data, contentType } = await ctx.adapter.downloadReadable(input.id);
|
|
37933
|
-
|
|
37934
|
-
for (let i = 0; i < data.length; i += 8192) {
|
|
37935
|
-
binary += String.fromCharCode(...data.subarray(i, i + 8192));
|
|
37936
|
-
}
|
|
37937
|
-
const base643 = btoa(binary);
|
|
37938
|
-
return { content_type: contentType, data_base64: base643, size_bytes: data.length };
|
|
38032
|
+
return { content_type: contentType, data_base64: uint8ToBase64(data), size_bytes: data.length };
|
|
37939
38033
|
}
|
|
37940
38034
|
},
|
|
37941
38035
|
// ── Invoice Files ─────────────────────────────────────────
|
|
@@ -37993,12 +38087,7 @@ var invoiceTools = [
|
|
|
37993
38087
|
throw new Error("[einvoice_invoice_download_file] 'file_id' is required");
|
|
37994
38088
|
}
|
|
37995
38089
|
const { data, contentType } = await ctx.adapter.downloadFile(input.file_id);
|
|
37996
|
-
|
|
37997
|
-
for (let i = 0; i < data.length; i += 8192) {
|
|
37998
|
-
binary += String.fromCharCode(...data.subarray(i, i + 8192));
|
|
37999
|
-
}
|
|
38000
|
-
const base643 = btoa(binary);
|
|
38001
|
-
return { content_type: contentType, data_base64: base643, size_bytes: data.length };
|
|
38090
|
+
return { content_type: contentType, data_base64: uint8ToBase64(data), size_bytes: data.length };
|
|
38002
38091
|
}
|
|
38003
38092
|
},
|
|
38004
38093
|
// ── Mark as seen ────────────────────────────────────────
|
|
@@ -38072,7 +38161,7 @@ var invoiceTools = [
|
|
|
38072
38161
|
properties: {
|
|
38073
38162
|
invoice: {
|
|
38074
38163
|
type: "object",
|
|
38075
|
-
description:
|
|
38164
|
+
description: INVOICE_SCHEMA_DESCRIPTION
|
|
38076
38165
|
},
|
|
38077
38166
|
flavor: {
|
|
38078
38167
|
type: "string",
|
|
@@ -38112,7 +38201,7 @@ var invoiceTools = [
|
|
|
38112
38201
|
properties: {
|
|
38113
38202
|
invoice: {
|
|
38114
38203
|
type: "object",
|
|
38115
|
-
description:
|
|
38204
|
+
description: INVOICE_SCHEMA_DESCRIPTION
|
|
38116
38205
|
},
|
|
38117
38206
|
flavor: {
|
|
38118
38207
|
type: "string",
|
|
@@ -38152,7 +38241,7 @@ var invoiceTools = [
|
|
|
38152
38241
|
properties: {
|
|
38153
38242
|
invoice: {
|
|
38154
38243
|
type: "object",
|
|
38155
|
-
description:
|
|
38244
|
+
description: INVOICE_SCHEMA_DESCRIPTION
|
|
38156
38245
|
},
|
|
38157
38246
|
flavor: {
|
|
38158
38247
|
type: "string",
|
|
@@ -38774,7 +38863,10 @@ var IopoleClient = class {
|
|
|
38774
38863
|
}
|
|
38775
38864
|
// ─── Generic Request ────────────────────────────────────
|
|
38776
38865
|
async request(method, path, options) {
|
|
38777
|
-
|
|
38866
|
+
return this.requestWithBase(this.config.baseUrl, method, path, options);
|
|
38867
|
+
}
|
|
38868
|
+
async requestWithBase(baseUrl, method, path, options) {
|
|
38869
|
+
const url2 = new URL(`${baseUrl}${path}`);
|
|
38778
38870
|
if (options?.query) {
|
|
38779
38871
|
for (const [key, value] of Object.entries(options.query)) {
|
|
38780
38872
|
if (value !== void 0) {
|
|
@@ -38827,17 +38919,12 @@ var IopoleClient = class {
|
|
|
38827
38919
|
}
|
|
38828
38920
|
/**
|
|
38829
38921
|
* GET request against the v1.1 API endpoint.
|
|
38830
|
-
*
|
|
38831
|
-
* (e.g.
|
|
38922
|
+
* Builds a v1.1 URL without mutating this.config.baseUrl,
|
|
38923
|
+
* so concurrent calls (e.g. getV11 + get) cannot interfere.
|
|
38832
38924
|
*/
|
|
38833
38925
|
async getV11(path, query) {
|
|
38834
|
-
const
|
|
38835
|
-
|
|
38836
|
-
this.config.baseUrl = originalBase.replace(/\/v1\b/, "/v1.1");
|
|
38837
|
-
return await this.request("GET", path, { query });
|
|
38838
|
-
} finally {
|
|
38839
|
-
this.config.baseUrl = originalBase;
|
|
38840
|
-
}
|
|
38926
|
+
const baseV11 = this.config.baseUrl.replace(/\/v1\b/, "/v1.1");
|
|
38927
|
+
return this.requestWithBase(baseV11, "GET", path, { query });
|
|
38841
38928
|
}
|
|
38842
38929
|
async post(path, body) {
|
|
38843
38930
|
return this.request("POST", path, { body });
|
|
@@ -39152,7 +39239,7 @@ async function main() {
|
|
|
39152
39239
|
);
|
|
39153
39240
|
const server = new ConcurrentMCPServer({
|
|
39154
39241
|
name: "mcp-einvoice",
|
|
39155
|
-
version: "0.1.
|
|
39242
|
+
version: "0.1.1",
|
|
39156
39243
|
maxConcurrent: 10,
|
|
39157
39244
|
backpressureStrategy: "queue",
|
|
39158
39245
|
validateSchema: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@casys/mcp-einvoice",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "PA-agnostic MCP server for French e-invoicing (Iopole, Chorus Pro...)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/Casys-AI/
|
|
31
|
+
"url": "https://github.com/Casys-AI/mcp-einvoice"
|
|
32
32
|
},
|
|
33
33
|
"license": "MIT"
|
|
34
34
|
}
|