@mrfentmen/iban-mcp 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -2
- package/dist/api.d.ts +13 -0
- package/dist/api.js +100 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +33 -0
- package/package.json +41 -4
package/README.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
-
#
|
|
1
|
+
# IBAN Checker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Validate IBANs offline with the official country length table and mod-97 checksum, and browse every country's format, keyless.
|
|
4
|
+
|
|
5
|
+
Keyless — no signup, no API key, no environment variables.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx -y @mrfentmen/iban-mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or add it to your MCP client:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"iban-mcp": { "command": "npx", "args": ["-y", "@mrfentmen/iban-mcp"] }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Tools
|
|
24
|
+
|
|
25
|
+
| Tool | What it does |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `validate_iban` | Validate an IBAN: structure, country length table and mod-97 checksum, with a plain reason when it fails. |
|
|
28
|
+
| `list_iban_countries` | List every IBAN country code with its required length, optionally filtered by code or name. |
|
|
29
|
+
|
|
30
|
+
## Data source
|
|
31
|
+
|
|
32
|
+
Runs fully offline — all work happens inside the server with no API calls and no keys.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IBAN Checker — keyless public API client.
|
|
3
|
+
*/
|
|
4
|
+
export declare class SourceError extends Error {
|
|
5
|
+
}
|
|
6
|
+
export declare function clean(s: unknown): string;
|
|
7
|
+
export declare function bullet(label: string, value: unknown): string;
|
|
8
|
+
export declare function validateIban(a: {
|
|
9
|
+
iban: string;
|
|
10
|
+
}): Promise<string>;
|
|
11
|
+
export declare function listIbanCountries(a: {
|
|
12
|
+
search?: string;
|
|
13
|
+
}): Promise<string>;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IBAN Checker — keyless public API client.
|
|
3
|
+
*/
|
|
4
|
+
export class SourceError extends Error {
|
|
5
|
+
}
|
|
6
|
+
export function clean(s) {
|
|
7
|
+
return String(s ?? "").replace(/\s+/g, " ").trim();
|
|
8
|
+
}
|
|
9
|
+
export function bullet(label, value) {
|
|
10
|
+
const v = value === undefined || value === null || value === "" ? "" : String(value);
|
|
11
|
+
return v ? `${label}: ${v}` : "";
|
|
12
|
+
}
|
|
13
|
+
const LENGTHS = {
|
|
14
|
+
AD: 24, AE: 23, AL: 28, AT: 20, AZ: 28, BA: 20, BE: 16, BG: 22, BH: 22, BR: 29, BY: 28, CH: 21,
|
|
15
|
+
CR: 22, CY: 28, CZ: 24, DE: 22, DK: 18, DO: 28, EE: 20, EG: 29, ES: 24, FI: 18, FO: 18, FR: 27,
|
|
16
|
+
GB: 22, GE: 22, GI: 23, GL: 18, GR: 27, GT: 28, HR: 21, HU: 28, IE: 22, IL: 23, IQ: 23, IS: 26,
|
|
17
|
+
IT: 27, JO: 30, KW: 30, KZ: 20, LB: 28, LC: 32, LI: 21, LT: 20, LU: 20, LV: 21, LY: 25, MC: 27,
|
|
18
|
+
MD: 24, ME: 22, MK: 19, MR: 27, MT: 31, MU: 30, NL: 18, NO: 15, PK: 24, PL: 28, PS: 29, PT: 25,
|
|
19
|
+
QA: 29, RO: 24, RS: 22, SA: 24, SC: 31, SE: 24, SI: 19, SK: 24, SM: 27, ST: 25, SV: 28, TL: 23,
|
|
20
|
+
TN: 24, TR: 26, UA: 29, VA: 22, VG: 24, XK: 20,
|
|
21
|
+
};
|
|
22
|
+
const NAMES = {
|
|
23
|
+
AD: "Andorra", AE: "United Arab Emirates", AL: "Albania", AT: "Austria", AZ: "Azerbaijan",
|
|
24
|
+
BA: "Bosnia and Herzegovina", BE: "Belgium", BG: "Bulgaria", BH: "Bahrain", BR: "Brazil",
|
|
25
|
+
BY: "Belarus", CH: "Switzerland", CR: "Costa Rica", CY: "Cyprus", CZ: "Czechia", DE: "Germany",
|
|
26
|
+
DK: "Denmark", DO: "Dominican Republic", EE: "Estonia", EG: "Egypt", ES: "Spain", FI: "Finland",
|
|
27
|
+
FO: "Faroe Islands", FR: "France", GB: "United Kingdom", GE: "Georgia", GI: "Gibraltar",
|
|
28
|
+
GL: "Greenland", GR: "Greece", GT: "Guatemala", HR: "Croatia", HU: "Hungary", IE: "Ireland",
|
|
29
|
+
IL: "Israel", IQ: "Iraq", IS: "Iceland", IT: "Italy", JO: "Jordan", KW: "Kuwait",
|
|
30
|
+
KZ: "Kazakhstan", LB: "Lebanon", LC: "Saint Lucia", LI: "Liechtenstein", LT: "Lithuania",
|
|
31
|
+
LU: "Luxembourg", LV: "Latvia", LY: "Libya", MC: "Monaco", MD: "Moldova", ME: "Montenegro",
|
|
32
|
+
MK: "North Macedonia", MR: "Mauritania", MT: "Malta", MU: "Mauritius", NL: "Netherlands",
|
|
33
|
+
NO: "Norway", PK: "Pakistan", PL: "Poland", PS: "Palestine", PT: "Portugal", QA: "Qatar",
|
|
34
|
+
RO: "Romania", RS: "Serbia", SA: "Saudi Arabia", SC: "Seychelles", SE: "Sweden", SI: "Slovenia",
|
|
35
|
+
SK: "Slovakia", SM: "San Marino", ST: "Sao Tome and Principe", SV: "El Salvador",
|
|
36
|
+
TL: "Timor-Leste", TN: "Tunisia", TR: "Turkey", UA: "Ukraine", VA: "Vatican City",
|
|
37
|
+
VG: "Virgin Islands (British)", XK: "Kosovo",
|
|
38
|
+
};
|
|
39
|
+
function mod97(value) {
|
|
40
|
+
let remainder = 0;
|
|
41
|
+
for (const ch of value) {
|
|
42
|
+
const digit = ch >= "0" && ch <= "9" ? Number(ch) : ch.charCodeAt(0) - 55;
|
|
43
|
+
remainder = (remainder * (digit > 9 ? 100 : 10) + digit) % 97;
|
|
44
|
+
}
|
|
45
|
+
return remainder;
|
|
46
|
+
}
|
|
47
|
+
function formatted(iban) {
|
|
48
|
+
return iban.replace(/(.{4})/g, "$1 ").trim();
|
|
49
|
+
}
|
|
50
|
+
export async function validateIban(a) {
|
|
51
|
+
const raw = clean(a.iban).replace(/[\s-]/g, "").toUpperCase();
|
|
52
|
+
if (!raw)
|
|
53
|
+
throw new SourceError("iban is required");
|
|
54
|
+
if (raw.length < 5 || raw.length > 34)
|
|
55
|
+
return `Invalid: an IBAN must be 5 to 34 characters, got ${raw.length}.`;
|
|
56
|
+
if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(raw)) {
|
|
57
|
+
return "Invalid: IBANs start with two letters, two check digits, then letters or digits only.";
|
|
58
|
+
}
|
|
59
|
+
const country = raw.slice(0, 2);
|
|
60
|
+
const expected = LENGTHS[country];
|
|
61
|
+
if (!expected)
|
|
62
|
+
return `Invalid: "${country}" is not an IBAN country code.`;
|
|
63
|
+
if (raw.length !== expected) {
|
|
64
|
+
return `Invalid: ${NAMES[country] ?? country} IBANs are exactly ${expected} characters, got ${raw.length}.`;
|
|
65
|
+
}
|
|
66
|
+
const rearranged = raw.slice(4) + raw.slice(0, 4);
|
|
67
|
+
const check = mod97(rearranged);
|
|
68
|
+
if (check !== 1) {
|
|
69
|
+
return [
|
|
70
|
+
`Invalid checksum: ${raw.slice(2, 4)} are not the right check digits for this IBAN (mod-97 gave ${check}, expected 1).`,
|
|
71
|
+
`Country: ${NAMES[country] ?? country} (${expected} characters)`,
|
|
72
|
+
`Formatted: ${formatted(raw)}`,
|
|
73
|
+
].join("\n");
|
|
74
|
+
}
|
|
75
|
+
return [
|
|
76
|
+
`Valid IBAN.`,
|
|
77
|
+
"",
|
|
78
|
+
bullet("country", `${NAMES[country] ?? country} (${country})`),
|
|
79
|
+
bullet("check digits", raw.slice(2, 4)),
|
|
80
|
+
bullet("BBAN", raw.slice(4)),
|
|
81
|
+
bullet("formatted", formatted(raw)),
|
|
82
|
+
bullet("length", `${raw.length} characters (${country} requires exactly ${expected})`),
|
|
83
|
+
bullet("checksum", "mod-97 = 1, as required"),
|
|
84
|
+
]
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.join("\n");
|
|
87
|
+
}
|
|
88
|
+
export async function listIbanCountries(a) {
|
|
89
|
+
const search = clean(a.search).toLowerCase();
|
|
90
|
+
const codes = Object.keys(LENGTHS)
|
|
91
|
+
.filter((code) => !search || code.toLowerCase().includes(search) || (NAMES[code] ?? "").toLowerCase().includes(search))
|
|
92
|
+
.sort();
|
|
93
|
+
if (!codes.length)
|
|
94
|
+
throw new SourceError(`no IBAN country matches "${clean(a.search)}"`);
|
|
95
|
+
return [
|
|
96
|
+
`${codes.length} IBAN country code(s)${search ? ` matching "${clean(a.search)}"` : ""}:`,
|
|
97
|
+
"",
|
|
98
|
+
...codes.map((code) => `${code} ${String(LENGTHS[code]).padStart(2)} ${NAMES[code] ?? ""}`),
|
|
99
|
+
].join("\n");
|
|
100
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
import { createServer } from "./server.js";
|
|
3
|
+
async function main() {
|
|
4
|
+
const server = createServer();
|
|
5
|
+
const transport = new StdioServerTransport();
|
|
6
|
+
await server.connect(transport);
|
|
7
|
+
console.error("iban-mcp running on stdio");
|
|
8
|
+
}
|
|
9
|
+
main().catch((err) => {
|
|
10
|
+
console.error("Fatal error:", err);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { listIbanCountries, validateIban, SourceError } from "./api.js";
|
|
4
|
+
const text = (t) => ({ content: [{ type: "text", text: t }] });
|
|
5
|
+
function errorMessage(e) {
|
|
6
|
+
if (e instanceof SourceError || e instanceof Error)
|
|
7
|
+
return `Error: ${e.message}`;
|
|
8
|
+
return `Error: ${String(e)}`;
|
|
9
|
+
}
|
|
10
|
+
export function createServer() {
|
|
11
|
+
const server = new McpServer({ name: "iban-mcp", version: "1.0.0" });
|
|
12
|
+
server.tool("validate_iban", "Validate an IBAN: structure, country length table and mod-97 checksum, with a plain reason when it fails.", {
|
|
13
|
+
iban: z.string().describe('IBAN, spaces and dashes are fine, e.g. GB82 WEST 1234 5698 7654 32')
|
|
14
|
+
}, async (a) => {
|
|
15
|
+
try {
|
|
16
|
+
return text(await validateIban(a));
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
return { content: [{ type: "text", text: errorMessage(e) }], isError: true };
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
server.tool("list_iban_countries", "List every IBAN country code with its required length, optionally filtered by code or name.", {
|
|
23
|
+
search: z.string().optional().describe('Filter by code or country name, e.g. "germany"')
|
|
24
|
+
}, async (a) => {
|
|
25
|
+
try {
|
|
26
|
+
return text(await listIbanCountries(a));
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
return { content: [{ type: "text", text: errorMessage(e) }], isError: true };
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return server;
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrfentmen/iban-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Validate IBANs offline with the official country length table and mod-97 checksum, and browse every country's format, keyless.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"mcpName": "io.github.mrfentmen/iban-mcp",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/mrfentmen/awesome-servers.git"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"iban-mcp": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"start": "node dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"iban",
|
|
25
|
+
"banking",
|
|
26
|
+
"validation",
|
|
27
|
+
"checksum",
|
|
28
|
+
"offline",
|
|
29
|
+
"keyless"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
34
|
+
"zod": "^3.23.8"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.0.0",
|
|
38
|
+
"typescript": "^5.6.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20"
|
|
42
|
+
}
|
|
43
|
+
}
|