beliq-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/config.js +14 -0
- package/dist/deps.js +1 -0
- package/dist/index.js +32 -0
- package/dist/schema.js +47 -0
- package/dist/summary.js +36 -0
- package/dist/tools/checkAccount.js +17 -0
- package/dist/tools/register.js +7 -0
- package/dist/tools/shared.js +105 -0
- package/dist/tools/validate.js +23 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 beliq
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# beliq-mcp
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server for [beliq](https://beliq.eu), the EU e-invoicing compliance API. It lets MCP clients (Claude Desktop, Claude Code, Cursor, and others) **validate** electronic invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS, and other UBL/CII documents) against authority-pinned, drift-checked rules, and explain exactly what fails.
|
|
4
|
+
|
|
5
|
+
beliq validates the compliant document. Transmission (Peppol, PDP, KSeF, SDI), archiving, and tax-authority reporting stay with your access point.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
- **`beliq_validate_einvoice`** - validate a UBL/CII XML invoice (inline or by file path) or a Factur-X/ZUGFeRD PDF (by file path). Returns the verdict, the detected format and profile, the ruleset (Schematron) version it was checked against, and every error and warning with its rule id, severity, location, and message.
|
|
10
|
+
- **`beliq_check_account`** - verify the configured API key and report the plan and remaining quota. Calls `GET /v1/me`, which draws no quota; useful as a connection and credential smoke test.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Requires Node.js >= 20.15. Published to npm, so clients can run it with `npx`:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
npx -y beliq-mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The server is configured entirely through environment variables (see below).
|
|
21
|
+
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
| Variable | Required | Default | Description |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `BELIQ_API_KEY` | yes | - | API key from the beliq dashboard (API Keys). |
|
|
27
|
+
| `BELIQ_AUTH` | no | `header` | How the key is sent: `header` (X-API-Key) or `bearer` (Authorization: Bearer). |
|
|
28
|
+
| `BELIQ_BASE_URL` | no | `https://api.beliq.eu` | Override for self-hosted or staging. |
|
|
29
|
+
|
|
30
|
+
## Client setup
|
|
31
|
+
|
|
32
|
+
### Claude Code
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
claude mcp add beliq -e BELIQ_API_KEY=your-key -- npx -y beliq-mcp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Claude Desktop
|
|
39
|
+
|
|
40
|
+
Add to `claude_desktop_config.json` (Settings > Developer > Edit Config):
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"beliq": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "beliq-mcp"],
|
|
48
|
+
"env": {
|
|
49
|
+
"BELIQ_API_KEY": "your-key"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Cursor
|
|
57
|
+
|
|
58
|
+
Add to `~/.cursor/mcp.json` (or a project `.cursor/mcp.json`) using the same `mcpServers` block shown for Claude Desktop.
|
|
59
|
+
|
|
60
|
+
## Reading a validation result
|
|
61
|
+
|
|
62
|
+
`beliq_validate_einvoice` returns a short text verdict plus a structured result:
|
|
63
|
+
|
|
64
|
+
- `valid` is true only when there are no errors; warnings do not make a document invalid.
|
|
65
|
+
- `format` and `profileDetected` report the detected syntax and business profile.
|
|
66
|
+
- `schematronVersion` is the exact ruleset revision the check ran against.
|
|
67
|
+
- `errors[]` and `warnings[]` each carry `ruleId`, `severity`, `location` (an XPath when available), and `message`.
|
|
68
|
+
|
|
69
|
+
A PDF (Factur-X / ZUGFeRD) must be passed by `documentPath`, not inlined as text.
|
|
70
|
+
|
|
71
|
+
## Agent skill
|
|
72
|
+
|
|
73
|
+
[`skill/SKILL.md`](./skill/SKILL.md) is a portable agent skill that teaches a model when to validate, how to read `errors`/`warnings`, and how to report a verdict, using the tools above. Drop it into a skills directory for an agent that should validate invoices on request.
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
This server depends on the published [`@beliq/sdk`](https://www.npmjs.com/package/@beliq/sdk), which carries the request, transport, and result-shaping logic. No lockfile is committed; `@beliq/sdk` is resolved fresh at install time.
|
|
78
|
+
|
|
79
|
+
- `npm install`
|
|
80
|
+
- `npm run build` - compile to `dist/`
|
|
81
|
+
- `npm run typecheck`
|
|
82
|
+
- `npm run lint`
|
|
83
|
+
- `npm test` - unit tests (result summary) and an in-memory MCP round-trip with a fake SDK client
|
|
84
|
+
- `BELIQ_API_KEY=your-key npm run test:integration` - live smoke tests against the real API
|
|
85
|
+
- `npm run scrub:check` - check for em-dashes in source and docs
|
|
86
|
+
|
|
87
|
+
Run the built server directly for a quick check:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
BELIQ_API_KEY=your-key node dist/index.js
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
[MIT](./LICENSE)
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DEFAULT_BASE_URL } from '@beliq/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Build the typed config from the environment. Throws if BELIQ_API_KEY is
|
|
4
|
+
* missing so the server fails fast before connecting the transport.
|
|
5
|
+
*/
|
|
6
|
+
export function loadConfig(env = process.env) {
|
|
7
|
+
const apiKey = env.BELIQ_API_KEY?.trim();
|
|
8
|
+
if (!apiKey) {
|
|
9
|
+
throw new Error('BELIQ_API_KEY is required. Create a key in the beliq dashboard under API Keys and set it in the MCP server env.');
|
|
10
|
+
}
|
|
11
|
+
const baseUrl = (env.BELIQ_BASE_URL?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
12
|
+
const auth = env.BELIQ_AUTH?.trim().toLowerCase() === 'bearer' ? 'bearer' : 'header';
|
|
13
|
+
return { apiKey, baseUrl, auth };
|
|
14
|
+
}
|
package/dist/deps.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// On stdio, stdout carries the JSON-RPC protocol. All diagnostics MUST go to
|
|
3
|
+
// stderr; a stray console.log anywhere reachable from the running server
|
|
4
|
+
// corrupts the protocol stream.
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
import { Beliq } from '@beliq/sdk';
|
|
9
|
+
import { loadConfig } from './config.js';
|
|
10
|
+
import { registerAllTools } from './tools/register.js';
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const { version } = require('../package.json');
|
|
13
|
+
async function main() {
|
|
14
|
+
let config;
|
|
15
|
+
try {
|
|
16
|
+
config = loadConfig();
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
console.error(`[beliq-mcp] ${err.message}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const client = new Beliq({ apiKey: config.apiKey, baseUrl: config.baseUrl, auth: config.auth });
|
|
23
|
+
const server = new McpServer({ name: 'beliq-mcp', version });
|
|
24
|
+
registerAllTools(server, { client });
|
|
25
|
+
const transport = new StdioServerTransport();
|
|
26
|
+
await server.connect(transport);
|
|
27
|
+
console.error(`[beliq-mcp] v${version} ready on stdio (base ${config.baseUrl})`);
|
|
28
|
+
}
|
|
29
|
+
main().catch((err) => {
|
|
30
|
+
console.error('[beliq-mcp] fatal:', err);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Validate accepts the live, publicly offered source-syntax hints only. The
|
|
3
|
+
// engine can technically detect more, but the public option set stays narrow
|
|
4
|
+
// (LPD-1): callers pass the syntax family, not a country profile.
|
|
5
|
+
export const validateInputShape = {
|
|
6
|
+
document: z
|
|
7
|
+
.string()
|
|
8
|
+
.min(1)
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('The invoice as XML text. Provide this OR documentPath, not both.'),
|
|
11
|
+
documentPath: z
|
|
12
|
+
.string()
|
|
13
|
+
.min(1)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Path to an invoice file on disk: a UBL/CII XML, or a PDF carrying embedded XML (Factur-X / ZUGFeRD). Provide this OR document.'),
|
|
16
|
+
format: z
|
|
17
|
+
.enum(['auto', 'cii', 'ubl'])
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Source syntax hint. 'auto' (default) detects CII vs UBL from the document."),
|
|
20
|
+
franceCtc: z
|
|
21
|
+
.boolean()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Apply the French CTC (Factur-X / Chorus Pro) rule overlay during validation.'),
|
|
24
|
+
};
|
|
25
|
+
const issueShape = z.object({
|
|
26
|
+
ruleId: z.string(),
|
|
27
|
+
severity: z.string(),
|
|
28
|
+
location: z.string().optional(),
|
|
29
|
+
message: z.string(),
|
|
30
|
+
});
|
|
31
|
+
export const validateOutputShape = {
|
|
32
|
+
valid: z.boolean(),
|
|
33
|
+
format: z.string(),
|
|
34
|
+
profileDetected: z.string().optional(),
|
|
35
|
+
schematronVersion: z.string().optional(),
|
|
36
|
+
errorCount: z.number(),
|
|
37
|
+
warningCount: z.number(),
|
|
38
|
+
errors: z.array(issueShape),
|
|
39
|
+
warnings: z.array(issueShape),
|
|
40
|
+
};
|
|
41
|
+
export const checkAccountOutputShape = {
|
|
42
|
+
ok: z.boolean(),
|
|
43
|
+
status: z.number(),
|
|
44
|
+
plan: z.string().optional(),
|
|
45
|
+
message: z.string(),
|
|
46
|
+
};
|
|
47
|
+
const validateInputSchema = z.object(validateInputShape);
|
package/dist/summary.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** How many issues to spell out before collapsing the rest into a count. */
|
|
2
|
+
const MAX_LISTED_ISSUES = 20;
|
|
3
|
+
function issueLine(issue) {
|
|
4
|
+
const where = issue.location ? ` at ${issue.location}` : '';
|
|
5
|
+
return `- ${issue.ruleId} (${issue.severity})${where}: ${issue.message}`;
|
|
6
|
+
}
|
|
7
|
+
function plural(n, word) {
|
|
8
|
+
return `${n} ${word}${n === 1 ? '' : 's'}`;
|
|
9
|
+
}
|
|
10
|
+
function listSection(title, issues) {
|
|
11
|
+
if (!issues.length)
|
|
12
|
+
return [];
|
|
13
|
+
const shown = issues.slice(0, MAX_LISTED_ISSUES);
|
|
14
|
+
const lines = [`${title}:`, ...shown.map(issueLine)];
|
|
15
|
+
if (issues.length > shown.length) {
|
|
16
|
+
lines.push(`... and ${issues.length - shown.length} more.`);
|
|
17
|
+
}
|
|
18
|
+
return lines;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A compact human-readable verdict for the MCP text content: the pass/fail
|
|
22
|
+
* line with format, profile, and ruleset version, then the errors and warnings
|
|
23
|
+
* (capped). The full structured result rides alongside in structuredContent.
|
|
24
|
+
*/
|
|
25
|
+
export function summarizeValidation(result) {
|
|
26
|
+
const errors = (result.errors ?? []);
|
|
27
|
+
const warnings = (result.warnings ?? []);
|
|
28
|
+
const verdict = result.valid ? 'VALID' : 'INVALID';
|
|
29
|
+
const profile = result.profileDetected ? ` (profile ${result.profileDetected})` : '';
|
|
30
|
+
const schematron = result.schematronVersion
|
|
31
|
+
? `, checked against Schematron ${result.schematronVersion}`
|
|
32
|
+
: '';
|
|
33
|
+
const header = `Validation: ${verdict}. Format ${result.format}${profile}${schematron}. ` +
|
|
34
|
+
`${plural(errors.length, 'error')}, ${plural(warnings.length, 'warning')}.`;
|
|
35
|
+
return [header, ...listSection('Errors', errors), ...listSection('Warnings', warnings)].join('\n');
|
|
36
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { checkAccountOutputShape } from '../schema.js';
|
|
2
|
+
import { runCheckAccount } from './shared.js';
|
|
3
|
+
export function registerCheckAccount(server, deps) {
|
|
4
|
+
server.registerTool('beliq_check_account', {
|
|
5
|
+
title: 'beliq: Check Account',
|
|
6
|
+
description: 'Verify the configured beliq API key and report the plan and remaining quota. Calls GET /v1/me, which draws no quota. Use it as a connection and credential smoke test.',
|
|
7
|
+
inputSchema: {},
|
|
8
|
+
outputSchema: checkAccountOutputShape,
|
|
9
|
+
annotations: {
|
|
10
|
+
title: 'beliq: Check Account',
|
|
11
|
+
readOnlyHint: true,
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
idempotentHint: true,
|
|
14
|
+
openWorldHint: true,
|
|
15
|
+
},
|
|
16
|
+
}, async () => runCheckAccount(deps));
|
|
17
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { registerValidate } from './validate.js';
|
|
2
|
+
import { registerCheckAccount } from './checkAccount.js';
|
|
3
|
+
/** Register every beliq tool on the server with the given dependencies. */
|
|
4
|
+
export function registerAllTools(server, deps) {
|
|
5
|
+
registerValidate(server, deps);
|
|
6
|
+
registerCheckAccount(server, deps);
|
|
7
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { BeliqApiError } from '@beliq/sdk';
|
|
3
|
+
import { summarizeValidation } from '../summary.js';
|
|
4
|
+
function errorResult(message) {
|
|
5
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
6
|
+
}
|
|
7
|
+
function nonEmpty(value) {
|
|
8
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
9
|
+
}
|
|
10
|
+
/** A bad tool input that should come back as a normal (non-fatal) tool error. */
|
|
11
|
+
class BeliqInputError extends Error {
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the tool input to the bytes/text the SDK validates. Exactly one of
|
|
15
|
+
* document (inline XML) or documentPath (a file on disk) must be set; a file is
|
|
16
|
+
* read as raw bytes so the SDK can sniff XML vs an embedded-XML PDF.
|
|
17
|
+
*/
|
|
18
|
+
async function resolveDocument(input) {
|
|
19
|
+
const hasDoc = nonEmpty(input.document);
|
|
20
|
+
const hasPath = nonEmpty(input.documentPath);
|
|
21
|
+
if (hasDoc === hasPath) {
|
|
22
|
+
throw new BeliqInputError('Provide exactly one of document (XML text) or documentPath (a file path).');
|
|
23
|
+
}
|
|
24
|
+
if (hasDoc)
|
|
25
|
+
return input.document;
|
|
26
|
+
return readFile(input.documentPath.trim());
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validate one document and shape the MCP result: a human-readable verdict in
|
|
30
|
+
* the text content plus the full structured result. Input and API errors come
|
|
31
|
+
* back as isError so the model can self-correct; only unexpected faults throw.
|
|
32
|
+
*/
|
|
33
|
+
export async function runValidate(input, deps) {
|
|
34
|
+
let document;
|
|
35
|
+
try {
|
|
36
|
+
document = await resolveDocument(input);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
if (err instanceof BeliqInputError)
|
|
40
|
+
return errorResult(err.message);
|
|
41
|
+
return errorResult(`Could not read the document: ${err.message}`);
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const result = await deps.client.validate(document, {
|
|
45
|
+
format: input.format,
|
|
46
|
+
franceCtc: input.franceCtc,
|
|
47
|
+
});
|
|
48
|
+
const errors = result.errors ?? [];
|
|
49
|
+
const warnings = result.warnings ?? [];
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: 'text', text: summarizeValidation(result) }],
|
|
52
|
+
structuredContent: {
|
|
53
|
+
valid: result.valid,
|
|
54
|
+
format: result.format,
|
|
55
|
+
profileDetected: result.profileDetected,
|
|
56
|
+
schematronVersion: result.schematronVersion,
|
|
57
|
+
errorCount: errors.length,
|
|
58
|
+
warningCount: warnings.length,
|
|
59
|
+
errors,
|
|
60
|
+
warnings,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof BeliqApiError) {
|
|
66
|
+
const code = err.code ? ` (${err.code})` : '';
|
|
67
|
+
return errorResult(`beliq API error ${err.status}${code}: ${err.message}`);
|
|
68
|
+
}
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Verify the configured key against GET /v1/me, which draws no quota. Reports
|
|
74
|
+
* validity rather than throwing, so a rejected key is a normal (non-error)
|
|
75
|
+
* result the model can act on.
|
|
76
|
+
*/
|
|
77
|
+
export async function runCheckAccount(deps) {
|
|
78
|
+
const reply = (r) => ({
|
|
79
|
+
content: [{ type: 'text', text: r.message }],
|
|
80
|
+
structuredContent: { ...r },
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
const account = await deps.client.me();
|
|
84
|
+
const plan = account.plan?.name ?? undefined;
|
|
85
|
+
const quota = account.quota;
|
|
86
|
+
const quotaText = quota
|
|
87
|
+
? ` Quota: ${quota.used}/${quota.limit} used, ${quota.remaining} remaining.`
|
|
88
|
+
: '';
|
|
89
|
+
return reply({
|
|
90
|
+
ok: true,
|
|
91
|
+
status: 200,
|
|
92
|
+
plan,
|
|
93
|
+
message: `beliq credentials valid; the API key was accepted.${plan ? ` Plan: ${plan}.` : ''}${quotaText}`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (err instanceof BeliqApiError) {
|
|
98
|
+
const message = err.status === 401
|
|
99
|
+
? 'beliq API key rejected (401). Check the key set in BELIQ_API_KEY.'
|
|
100
|
+
: `Credential check failed (${err.status}): ${err.message}`;
|
|
101
|
+
return reply({ ok: false, status: err.status, message });
|
|
102
|
+
}
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { validateInputShape, validateOutputShape } from '../schema.js';
|
|
2
|
+
import { runValidate } from './shared.js';
|
|
3
|
+
const description = [
|
|
4
|
+
'Validate an EU electronic invoice against authority-pinned, drift-checked rules and report whether it is compliant.',
|
|
5
|
+
'Accepts a UBL or CII XML document (inline via document, or a file via documentPath), or a Factur-X / ZUGFeRD PDF via documentPath.',
|
|
6
|
+
'Returns the verdict (valid or not), the detected format and profile, the ruleset (Schematron) version it was checked against, and every error and warning with its rule id, severity, location, and message.',
|
|
7
|
+
'beliq validates the compliant document; transmission (Peppol, PDP, KSeF, SDI), archiving, and tax-authority reporting stay with your access point.',
|
|
8
|
+
].join(' ');
|
|
9
|
+
export function registerValidate(server, deps) {
|
|
10
|
+
server.registerTool('beliq_validate_einvoice', {
|
|
11
|
+
title: 'beliq: Validate E-Invoice',
|
|
12
|
+
description,
|
|
13
|
+
inputSchema: validateInputShape,
|
|
14
|
+
outputSchema: validateOutputShape,
|
|
15
|
+
annotations: {
|
|
16
|
+
title: 'beliq: Validate E-Invoice',
|
|
17
|
+
readOnlyHint: true,
|
|
18
|
+
destructiveHint: false,
|
|
19
|
+
idempotentHint: true,
|
|
20
|
+
openWorldHint: true,
|
|
21
|
+
},
|
|
22
|
+
}, async (args) => runValidate(args, deps));
|
|
23
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "beliq-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "beliq MCP server: validate EU e-invoices (XRechnung, ZUGFeRD, Factur-X, Peppol BIS) against authority-pinned, drift-checked rules, for MCP clients.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "beliq",
|
|
9
|
+
"email": "hello@beliq.eu"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://beliq.eu",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/beliq-eu/beliq-mcp.git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/beliq-eu/beliq-mcp/issues"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"model-context-protocol",
|
|
22
|
+
"beliq",
|
|
23
|
+
"e-invoice",
|
|
24
|
+
"einvoice",
|
|
25
|
+
"xrechnung",
|
|
26
|
+
"zugferd",
|
|
27
|
+
"factur-x",
|
|
28
|
+
"peppol",
|
|
29
|
+
"en16931",
|
|
30
|
+
"validation",
|
|
31
|
+
"compliance"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20.15"
|
|
35
|
+
},
|
|
36
|
+
"bin": {
|
|
37
|
+
"beliq-mcp": "dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"provenance": true
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"clean": "rm -rf dist",
|
|
47
|
+
"build": "npm run clean && tsc",
|
|
48
|
+
"dev": "tsc --watch",
|
|
49
|
+
"start": "node dist/index.js",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"lint": "eslint src",
|
|
52
|
+
"lint:fix": "eslint src --fix",
|
|
53
|
+
"test": "vitest run --exclude test/integration.test.ts",
|
|
54
|
+
"test:integration": "vitest run test/integration.test.ts",
|
|
55
|
+
"scrub:check": "bash scripts/scrub-check.sh",
|
|
56
|
+
"prepublishOnly": "npm run build"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@beliq/sdk": "^0.1.0",
|
|
60
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
61
|
+
"zod": "^4.4.3"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/node": "^20.14.0",
|
|
65
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
66
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
67
|
+
"eslint": "^8.57.0",
|
|
68
|
+
"typescript": "^5.5.4",
|
|
69
|
+
"vitest": "^2.1.8"
|
|
70
|
+
}
|
|
71
|
+
}
|