it-tools-mcp 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.dockerhub.md +98 -0
- package/README.md +263 -0
- package/build/index.js +75 -0
- package/build/security.js +201 -0
- package/build/tools/color.js +67 -0
- package/build/tools/crypto.js +445 -0
- package/build/tools/dataFormat.js +517 -0
- package/build/tools/development.js +267 -0
- package/build/tools/encoding.js +240 -0
- package/build/tools/idGenerators.js +176 -0
- package/build/tools/math.js +306 -0
- package/build/tools/network.js +578 -0
- package/build/tools/text.js +678 -0
- package/build/tools/utility.js +407 -0
- package/package.json +94 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import { isValidPhoneNumber, parsePhoneNumber } from "libphonenumber-js";
|
|
2
|
+
import IBAN from "iban";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export function registerNetworkTools(server) {
|
|
5
|
+
// IP address tools
|
|
6
|
+
server.tool("ip-subnet-calculator", "Calculate subnet information for IPv4", {
|
|
7
|
+
ip: z.string().describe("IPv4 address (e.g., 192.168.1.1)"),
|
|
8
|
+
cidr: z.number().describe("CIDR notation (e.g., 24)"),
|
|
9
|
+
}, async ({ ip, cidr }) => {
|
|
10
|
+
try {
|
|
11
|
+
if (cidr < 1 || cidr > 32) {
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: "CIDR must be between 1 and 32.",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const ipParts = ip.split('.').map(part => {
|
|
22
|
+
const num = parseInt(part);
|
|
23
|
+
if (isNaN(num) || num < 0 || num > 255) {
|
|
24
|
+
throw new Error(`Invalid IP address part: ${part}`);
|
|
25
|
+
}
|
|
26
|
+
return num;
|
|
27
|
+
});
|
|
28
|
+
if (ipParts.length !== 4) {
|
|
29
|
+
throw new Error("Invalid IP address format");
|
|
30
|
+
}
|
|
31
|
+
// Calculate subnet mask
|
|
32
|
+
const mask = (0xFFFFFFFF << (32 - cidr)) >>> 0;
|
|
33
|
+
const maskParts = [
|
|
34
|
+
(mask >>> 24) & 0xFF,
|
|
35
|
+
(mask >>> 16) & 0xFF,
|
|
36
|
+
(mask >>> 8) & 0xFF,
|
|
37
|
+
mask & 0xFF
|
|
38
|
+
];
|
|
39
|
+
// Calculate network address
|
|
40
|
+
const ipNum = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3];
|
|
41
|
+
const networkNum = (ipNum & mask) >>> 0;
|
|
42
|
+
const networkParts = [
|
|
43
|
+
(networkNum >>> 24) & 0xFF,
|
|
44
|
+
(networkNum >>> 16) & 0xFF,
|
|
45
|
+
(networkNum >>> 8) & 0xFF,
|
|
46
|
+
networkNum & 0xFF
|
|
47
|
+
];
|
|
48
|
+
// Calculate broadcast address
|
|
49
|
+
const broadcastNum = (networkNum | (0xFFFFFFFF >>> cidr)) >>> 0;
|
|
50
|
+
const broadcastParts = [
|
|
51
|
+
(broadcastNum >>> 24) & 0xFF,
|
|
52
|
+
(broadcastNum >>> 16) & 0xFF,
|
|
53
|
+
(broadcastNum >>> 8) & 0xFF,
|
|
54
|
+
broadcastNum & 0xFF
|
|
55
|
+
];
|
|
56
|
+
// Calculate first and last usable addresses
|
|
57
|
+
const firstUsableNum = networkNum + 1;
|
|
58
|
+
const lastUsableNum = broadcastNum - 1;
|
|
59
|
+
const firstUsableParts = [
|
|
60
|
+
(firstUsableNum >>> 24) & 0xFF,
|
|
61
|
+
(firstUsableNum >>> 16) & 0xFF,
|
|
62
|
+
(firstUsableNum >>> 8) & 0xFF,
|
|
63
|
+
firstUsableNum & 0xFF
|
|
64
|
+
];
|
|
65
|
+
const lastUsableParts = [
|
|
66
|
+
(lastUsableNum >>> 24) & 0xFF,
|
|
67
|
+
(lastUsableNum >>> 16) & 0xFF,
|
|
68
|
+
(lastUsableNum >>> 8) & 0xFF,
|
|
69
|
+
lastUsableNum & 0xFF
|
|
70
|
+
];
|
|
71
|
+
const totalHosts = Math.pow(2, 32 - cidr);
|
|
72
|
+
const usableHosts = totalHosts - 2;
|
|
73
|
+
return {
|
|
74
|
+
content: [
|
|
75
|
+
{
|
|
76
|
+
type: "text",
|
|
77
|
+
text: `IPv4 Subnet Information:
|
|
78
|
+
|
|
79
|
+
Input: ${ip}/${cidr}
|
|
80
|
+
|
|
81
|
+
Network Address: ${networkParts.join('.')}
|
|
82
|
+
Subnet Mask: ${maskParts.join('.')}
|
|
83
|
+
Broadcast Address: ${broadcastParts.join('.')}
|
|
84
|
+
First Usable: ${firstUsableParts.join('.')}
|
|
85
|
+
Last Usable: ${lastUsableParts.join('.')}
|
|
86
|
+
|
|
87
|
+
Total Addresses: ${totalHosts}
|
|
88
|
+
Usable Addresses: ${usableHosts}
|
|
89
|
+
CIDR: /${cidr}`,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
return {
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: `Error calculating subnet: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
// Enhanced IPv4 subnet calculator
|
|
106
|
+
server.tool("ipv4-subnet-calc", "Calculate IPv4 subnet information", {
|
|
107
|
+
cidr: z.string().describe("IPv4 CIDR notation (e.g., 192.168.1.0/24)"),
|
|
108
|
+
}, async ({ cidr }) => {
|
|
109
|
+
try {
|
|
110
|
+
const [ip, prefixLength] = cidr.split('/');
|
|
111
|
+
const prefix = parseInt(prefixLength);
|
|
112
|
+
if (isNaN(prefix) || prefix < 0 || prefix > 32) {
|
|
113
|
+
throw new Error("Invalid CIDR prefix length");
|
|
114
|
+
}
|
|
115
|
+
const ipParts = ip.split('.').map(part => {
|
|
116
|
+
const num = parseInt(part);
|
|
117
|
+
if (isNaN(num) || num < 0 || num > 255) {
|
|
118
|
+
throw new Error(`Invalid IP address part: ${part}`);
|
|
119
|
+
}
|
|
120
|
+
return num;
|
|
121
|
+
});
|
|
122
|
+
if (ipParts.length !== 4) {
|
|
123
|
+
throw new Error("Invalid IP address format");
|
|
124
|
+
}
|
|
125
|
+
// Calculate all subnet information
|
|
126
|
+
const mask = (0xFFFFFFFF << (32 - prefix)) >>> 0;
|
|
127
|
+
const inverseMask = (0xFFFFFFFF >>> prefix);
|
|
128
|
+
const maskOctets = [
|
|
129
|
+
(mask >>> 24) & 0xFF,
|
|
130
|
+
(mask >>> 16) & 0xFF,
|
|
131
|
+
(mask >>> 8) & 0xFF,
|
|
132
|
+
mask & 0xFF
|
|
133
|
+
];
|
|
134
|
+
const wildcardOctets = [
|
|
135
|
+
inverseMask >>> 24,
|
|
136
|
+
(inverseMask >>> 16) & 0xFF,
|
|
137
|
+
(inverseMask >>> 8) & 0xFF,
|
|
138
|
+
inverseMask & 0xFF
|
|
139
|
+
];
|
|
140
|
+
const ipNum = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3];
|
|
141
|
+
const networkNum = (ipNum & mask) >>> 0;
|
|
142
|
+
const broadcastNum = (networkNum | inverseMask) >>> 0;
|
|
143
|
+
const networkOctets = [
|
|
144
|
+
(networkNum >>> 24) & 0xFF,
|
|
145
|
+
(networkNum >>> 16) & 0xFF,
|
|
146
|
+
(networkNum >>> 8) & 0xFF,
|
|
147
|
+
networkNum & 0xFF
|
|
148
|
+
];
|
|
149
|
+
const broadcastOctets = [
|
|
150
|
+
(broadcastNum >>> 24) & 0xFF,
|
|
151
|
+
(broadcastNum >>> 16) & 0xFF,
|
|
152
|
+
(broadcastNum >>> 8) & 0xFF,
|
|
153
|
+
broadcastNum & 0xFF
|
|
154
|
+
];
|
|
155
|
+
const totalAddresses = Math.pow(2, 32 - prefix);
|
|
156
|
+
const usableAddresses = Math.max(0, totalAddresses - 2);
|
|
157
|
+
// Determine network class
|
|
158
|
+
let networkClass = 'Unknown';
|
|
159
|
+
const firstOctet = networkOctets[0];
|
|
160
|
+
if (firstOctet >= 1 && firstOctet <= 126)
|
|
161
|
+
networkClass = 'A';
|
|
162
|
+
else if (firstOctet >= 128 && firstOctet <= 191)
|
|
163
|
+
networkClass = 'B';
|
|
164
|
+
else if (firstOctet >= 192 && firstOctet <= 223)
|
|
165
|
+
networkClass = 'C';
|
|
166
|
+
else if (firstOctet >= 224 && firstOctet <= 239)
|
|
167
|
+
networkClass = 'D (Multicast)';
|
|
168
|
+
else if (firstOctet >= 240 && firstOctet <= 255)
|
|
169
|
+
networkClass = 'E (Reserved)';
|
|
170
|
+
// Check if private
|
|
171
|
+
const isPrivate = (firstOctet === 10) ||
|
|
172
|
+
(firstOctet === 172 && networkOctets[1] >= 16 && networkOctets[1] <= 31) ||
|
|
173
|
+
(firstOctet === 192 && networkOctets[1] === 168);
|
|
174
|
+
return {
|
|
175
|
+
content: [
|
|
176
|
+
{
|
|
177
|
+
type: "text",
|
|
178
|
+
text: `Enhanced IPv4 Subnet Calculation:
|
|
179
|
+
|
|
180
|
+
Input CIDR: ${cidr}
|
|
181
|
+
|
|
182
|
+
Network Information:
|
|
183
|
+
Network Address: ${networkOctets.join('.')}
|
|
184
|
+
Broadcast Address: ${broadcastOctets.join('.')}
|
|
185
|
+
Subnet Mask: ${maskOctets.join('.')}
|
|
186
|
+
Wildcard Mask: ${wildcardOctets.join('.')}
|
|
187
|
+
|
|
188
|
+
Address Range:
|
|
189
|
+
First Host: ${networkOctets[0]}.${networkOctets[1]}.${networkOctets[2]}.${networkOctets[3] + 1}
|
|
190
|
+
Last Host: ${broadcastOctets[0]}.${broadcastOctets[1]}.${broadcastOctets[2]}.${broadcastOctets[3] - 1}
|
|
191
|
+
|
|
192
|
+
Capacity:
|
|
193
|
+
Total Addresses: ${totalAddresses.toLocaleString()}
|
|
194
|
+
Usable Host Addresses: ${usableAddresses.toLocaleString()}
|
|
195
|
+
|
|
196
|
+
Network Properties:
|
|
197
|
+
Class: ${networkClass}
|
|
198
|
+
Type: ${isPrivate ? 'Private' : 'Public'}
|
|
199
|
+
CIDR Prefix: /${prefix}`,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
return {
|
|
206
|
+
content: [
|
|
207
|
+
{
|
|
208
|
+
type: "text",
|
|
209
|
+
text: `Error calculating IPv4 subnet: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
// IPv6 ULA generator
|
|
216
|
+
server.tool("ipv6-ula-generator", "Generate IPv6 Unique Local Address (ULA) prefix", {
|
|
217
|
+
globalId: z.string().optional().describe("Global ID (40 bits in hex, auto-generated if not provided)"),
|
|
218
|
+
}, async ({ globalId }) => {
|
|
219
|
+
try {
|
|
220
|
+
// Generate random 40-bit Global ID if not provided
|
|
221
|
+
let gid = globalId;
|
|
222
|
+
if (!gid) {
|
|
223
|
+
const randomBytes = [];
|
|
224
|
+
for (let i = 0; i < 5; i++) {
|
|
225
|
+
randomBytes.push(Math.floor(Math.random() * 256).toString(16).padStart(2, '0'));
|
|
226
|
+
}
|
|
227
|
+
gid = randomBytes.join('');
|
|
228
|
+
}
|
|
229
|
+
// Validate Global ID
|
|
230
|
+
if (!/^[0-9a-fA-F]{10}$/.test(gid)) {
|
|
231
|
+
throw new Error("Global ID must be exactly 10 hexadecimal characters (40 bits)");
|
|
232
|
+
}
|
|
233
|
+
// Format the ULA prefix
|
|
234
|
+
const prefix = `fd${gid.substring(0, 2)}:${gid.substring(2, 6)}:${gid.substring(6, 10)}`;
|
|
235
|
+
const fullPrefix = `${prefix}::/48`;
|
|
236
|
+
// Generate some example subnets
|
|
237
|
+
const subnets = [];
|
|
238
|
+
for (let i = 0; i < 5; i++) {
|
|
239
|
+
const subnetId = Math.floor(Math.random() * 65536).toString(16).padStart(4, '0');
|
|
240
|
+
subnets.push(`${prefix}:${subnetId}::/64`);
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
content: [
|
|
244
|
+
{
|
|
245
|
+
type: "text",
|
|
246
|
+
text: `IPv6 ULA (Unique Local Address) Generated:
|
|
247
|
+
|
|
248
|
+
ULA Prefix: ${fullPrefix}
|
|
249
|
+
Global ID: ${gid}
|
|
250
|
+
|
|
251
|
+
Example Subnets:
|
|
252
|
+
${subnets.map((subnet, i) => `${i + 1}. ${subnet}`).join('\n')}
|
|
253
|
+
|
|
254
|
+
Properties:
|
|
255
|
+
- Scope: Local (not routed on the internet)
|
|
256
|
+
- Prefix: fd00::/8 (ULA)
|
|
257
|
+
- Global ID: ${gid} (40 bits)
|
|
258
|
+
- Subnet ID: 16 bits available
|
|
259
|
+
- Interface ID: 64 bits available
|
|
260
|
+
|
|
261
|
+
Note: ULAs are designed for local communications within a site.
|
|
262
|
+
They are not expected to be routable on the global Internet.`,
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
return {
|
|
269
|
+
content: [
|
|
270
|
+
{
|
|
271
|
+
type: "text",
|
|
272
|
+
text: `Error generating IPv6 ULA: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
// URL parser
|
|
279
|
+
server.tool("url-parse", "Parse URL into components", {
|
|
280
|
+
url: z.string().describe("URL to parse"),
|
|
281
|
+
}, async ({ url }) => {
|
|
282
|
+
try {
|
|
283
|
+
const urlObj = new URL(url);
|
|
284
|
+
// Parse query parameters
|
|
285
|
+
const params = {};
|
|
286
|
+
urlObj.searchParams.forEach((value, key) => {
|
|
287
|
+
params[key] = value;
|
|
288
|
+
});
|
|
289
|
+
return {
|
|
290
|
+
content: [
|
|
291
|
+
{
|
|
292
|
+
type: "text",
|
|
293
|
+
text: `URL Components:
|
|
294
|
+
|
|
295
|
+
Original URL: ${url}
|
|
296
|
+
|
|
297
|
+
Protocol: ${urlObj.protocol}
|
|
298
|
+
Host: ${urlObj.host}
|
|
299
|
+
Hostname: ${urlObj.hostname}
|
|
300
|
+
Port: ${urlObj.port || 'default'}
|
|
301
|
+
Pathname: ${urlObj.pathname}
|
|
302
|
+
Search: ${urlObj.search}
|
|
303
|
+
Hash: ${urlObj.hash}
|
|
304
|
+
Origin: ${urlObj.origin}
|
|
305
|
+
|
|
306
|
+
Query Parameters:
|
|
307
|
+
${Object.keys(params).length > 0
|
|
308
|
+
? Object.entries(params).map(([key, value]) => ` ${key}: ${value}`).join('\n')
|
|
309
|
+
: ' (none)'}
|
|
310
|
+
|
|
311
|
+
Path Segments:
|
|
312
|
+
${urlObj.pathname.split('/').filter(segment => segment).map((segment, i) => ` ${i + 1}. ${segment}`).join('\n') || ' (none)'}`,
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
return {
|
|
319
|
+
content: [
|
|
320
|
+
{
|
|
321
|
+
type: "text",
|
|
322
|
+
text: `Error parsing URL: ${error instanceof Error ? error.message : 'Invalid URL format'}`,
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
// Random port generator
|
|
329
|
+
server.tool("random-port", "Generate random port numbers", {
|
|
330
|
+
count: z.number().describe("Number of ports to generate").optional(),
|
|
331
|
+
min: z.number().describe("Minimum port number").optional(),
|
|
332
|
+
max: z.number().describe("Maximum port number").optional(),
|
|
333
|
+
exclude: z.array(z.number()).optional().describe("Ports to exclude"),
|
|
334
|
+
}, async ({ count = 1, min = 1024, max = 65535, exclude = [] }) => {
|
|
335
|
+
try {
|
|
336
|
+
const ports = [];
|
|
337
|
+
const excludeSet = new Set(exclude);
|
|
338
|
+
// Well-known ports to avoid by default
|
|
339
|
+
const wellKnownPorts = [22, 23, 25, 53, 80, 110, 143, 443, 993, 995];
|
|
340
|
+
wellKnownPorts.forEach(port => excludeSet.add(port));
|
|
341
|
+
for (let i = 0; i < count; i++) {
|
|
342
|
+
let port;
|
|
343
|
+
let attempts = 0;
|
|
344
|
+
do {
|
|
345
|
+
port = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
346
|
+
attempts++;
|
|
347
|
+
if (attempts > 1000) {
|
|
348
|
+
throw new Error("Could not generate unique port after 1000 attempts");
|
|
349
|
+
}
|
|
350
|
+
} while (excludeSet.has(port) || ports.includes(port));
|
|
351
|
+
ports.push(port);
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
content: [
|
|
355
|
+
{
|
|
356
|
+
type: "text",
|
|
357
|
+
text: `Random Ports Generated:
|
|
358
|
+
|
|
359
|
+
${ports.map((port, i) => `${i + 1}. ${port}`).join('\n')}
|
|
360
|
+
|
|
361
|
+
Range: ${min} - ${max}
|
|
362
|
+
Excluded well-known ports: ${wellKnownPorts.join(', ')}
|
|
363
|
+
${exclude.length > 0 ? `Custom excluded: ${exclude.join(', ')}` : ''}`,
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
return {
|
|
370
|
+
content: [
|
|
371
|
+
{
|
|
372
|
+
type: "text",
|
|
373
|
+
text: `Error generating random ports: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
// MAC address generator
|
|
380
|
+
server.tool("mac-address-generate", "Generate random MAC address", {
|
|
381
|
+
prefix: z.string().optional().describe("MAC address prefix (e.g., '00:1B:44')"),
|
|
382
|
+
separator: z.enum([":", "-"]).describe("Separator character").optional(),
|
|
383
|
+
}, async ({ prefix, separator = ":" }) => {
|
|
384
|
+
try {
|
|
385
|
+
let macParts = [];
|
|
386
|
+
if (prefix) {
|
|
387
|
+
// Validate and use provided prefix
|
|
388
|
+
const prefixParts = prefix.split(/[:-]/);
|
|
389
|
+
if (prefixParts.length > 6) {
|
|
390
|
+
throw new Error("Prefix cannot have more than 6 parts");
|
|
391
|
+
}
|
|
392
|
+
for (const part of prefixParts) {
|
|
393
|
+
if (!/^[0-9A-Fa-f]{2}$/.test(part)) {
|
|
394
|
+
throw new Error(`Invalid MAC address part: ${part}`);
|
|
395
|
+
}
|
|
396
|
+
macParts.push(part.toUpperCase());
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// Generate remaining parts
|
|
400
|
+
while (macParts.length < 6) {
|
|
401
|
+
const randomByte = Math.floor(Math.random() * 256).toString(16).padStart(2, '0').toUpperCase();
|
|
402
|
+
macParts.push(randomByte);
|
|
403
|
+
}
|
|
404
|
+
// Ensure first octet indicates locally administered unicast
|
|
405
|
+
if (!prefix) {
|
|
406
|
+
const firstOctet = parseInt(macParts[0], 16);
|
|
407
|
+
// Set locally administered bit (bit 1) and clear multicast bit (bit 0)
|
|
408
|
+
macParts[0] = ((firstOctet | 0x02) & 0xFE).toString(16).padStart(2, '0').toUpperCase();
|
|
409
|
+
}
|
|
410
|
+
const macAddress = macParts.join(separator);
|
|
411
|
+
// Analyze the MAC address
|
|
412
|
+
const firstOctet = parseInt(macParts[0], 16);
|
|
413
|
+
const isMulticast = (firstOctet & 0x01) !== 0;
|
|
414
|
+
const isLocallyAdministered = (firstOctet & 0x02) !== 0;
|
|
415
|
+
return {
|
|
416
|
+
content: [
|
|
417
|
+
{
|
|
418
|
+
type: "text",
|
|
419
|
+
text: `Generated MAC Address: ${macAddress}
|
|
420
|
+
|
|
421
|
+
Properties:
|
|
422
|
+
Type: ${isMulticast ? 'Multicast' : 'Unicast'}
|
|
423
|
+
Administration: ${isLocallyAdministered ? 'Locally Administered' : 'Universally Administered'}
|
|
424
|
+
Format: ${separator === ':' ? 'Colon notation' : 'Hyphen notation'}
|
|
425
|
+
|
|
426
|
+
Binary representation:
|
|
427
|
+
${macParts.map(part => parseInt(part, 16).toString(2).padStart(8, '0')).join(' ')}
|
|
428
|
+
|
|
429
|
+
${prefix ? `Used prefix: ${prefix}` : 'Randomly generated'}`,
|
|
430
|
+
},
|
|
431
|
+
],
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
return {
|
|
436
|
+
content: [
|
|
437
|
+
{
|
|
438
|
+
type: "text",
|
|
439
|
+
text: `Error generating MAC address: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
// Phone number formatter
|
|
446
|
+
server.tool("phone-format", "Parse and format phone numbers", {
|
|
447
|
+
phoneNumber: z.string().describe("Phone number to parse and format"),
|
|
448
|
+
countryCode: z.string().optional().describe("Country code (e.g., 'US', 'GB', 'FR')"),
|
|
449
|
+
}, async ({ phoneNumber, countryCode }) => {
|
|
450
|
+
try {
|
|
451
|
+
// First check if it's a valid phone number
|
|
452
|
+
if (!isValidPhoneNumber(phoneNumber, countryCode)) {
|
|
453
|
+
throw new Error("Invalid phone number format");
|
|
454
|
+
}
|
|
455
|
+
// Parse the phone number
|
|
456
|
+
const parsedNumber = parsePhoneNumber(phoneNumber, countryCode);
|
|
457
|
+
return {
|
|
458
|
+
content: [
|
|
459
|
+
{
|
|
460
|
+
type: "text",
|
|
461
|
+
text: `Phone Number Formatting:
|
|
462
|
+
|
|
463
|
+
Original: ${phoneNumber}
|
|
464
|
+
Country: ${parsedNumber.country || 'Unknown'}
|
|
465
|
+
National: ${parsedNumber.formatNational()}
|
|
466
|
+
International: ${parsedNumber.formatInternational()}
|
|
467
|
+
E.164: ${parsedNumber.format('E.164')}
|
|
468
|
+
URI: ${parsedNumber.getURI()}
|
|
469
|
+
|
|
470
|
+
Details:
|
|
471
|
+
Type: ${parsedNumber.getType() || 'Unknown'}
|
|
472
|
+
Country Code: +${parsedNumber.countryCallingCode}
|
|
473
|
+
National Number: ${parsedNumber.nationalNumber}
|
|
474
|
+
Valid: ${parsedNumber.isValid()}
|
|
475
|
+
|
|
476
|
+
✅ Formatted using libphonenumber-js library for accuracy.`,
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
catch (error) {
|
|
482
|
+
return {
|
|
483
|
+
content: [
|
|
484
|
+
{
|
|
485
|
+
type: "text",
|
|
486
|
+
text: `Error formatting phone number: ${error instanceof Error ? error.message : 'Unknown error'}
|
|
487
|
+
|
|
488
|
+
💡 Tips:
|
|
489
|
+
• Include country code (e.g., +1 555-123-4567)
|
|
490
|
+
• Use standard formats (e.g., (555) 123-4567)
|
|
491
|
+
• Specify country code parameter if needed
|
|
492
|
+
• Examples: "+1-555-123-4567", "555-123-4567" with countryCode="US"`,
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
// IBAN validator (using iban library)
|
|
499
|
+
server.tool("iban-validate", "Validate and parse IBAN (International Bank Account Number)", {
|
|
500
|
+
iban: z.string().describe("IBAN to validate and parse"),
|
|
501
|
+
}, async ({ iban }) => {
|
|
502
|
+
try {
|
|
503
|
+
// Clean the input
|
|
504
|
+
const cleanIban = iban.replace(/\s/g, '').toUpperCase();
|
|
505
|
+
// Validate using the IBAN library
|
|
506
|
+
const isValid = IBAN.isValid(cleanIban);
|
|
507
|
+
if (isValid) {
|
|
508
|
+
// Extract components
|
|
509
|
+
const countryCode = cleanIban.slice(0, 2);
|
|
510
|
+
const checkDigits = cleanIban.slice(2, 4);
|
|
511
|
+
const bban = cleanIban.slice(4); // Basic Bank Account Number
|
|
512
|
+
// Get country information if available (simplified approach)
|
|
513
|
+
const countryInfo = IBAN.countries[countryCode];
|
|
514
|
+
const countryName = countryInfo ? 'Available' : 'Unknown';
|
|
515
|
+
return {
|
|
516
|
+
content: [
|
|
517
|
+
{
|
|
518
|
+
type: "text",
|
|
519
|
+
text: `IBAN Validation Result: ✅ VALID
|
|
520
|
+
|
|
521
|
+
IBAN: ${iban}
|
|
522
|
+
Formatted: ${IBAN.printFormat(cleanIban)}
|
|
523
|
+
|
|
524
|
+
Components:
|
|
525
|
+
Country Code: ${countryCode} (${countryName})
|
|
526
|
+
Check Digits: ${checkDigits}
|
|
527
|
+
BBAN: ${bban}
|
|
528
|
+
Length: ${cleanIban.length} characters
|
|
529
|
+
|
|
530
|
+
Validation:
|
|
531
|
+
Format: ✅ Valid
|
|
532
|
+
Length: ✅ Correct for ${countryCode}
|
|
533
|
+
Checksum: ✅ Valid (MOD-97)
|
|
534
|
+
|
|
535
|
+
Electronic Format: ${cleanIban}
|
|
536
|
+
Print Format: ${IBAN.printFormat(cleanIban)}`,
|
|
537
|
+
},
|
|
538
|
+
],
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
return {
|
|
543
|
+
content: [
|
|
544
|
+
{
|
|
545
|
+
type: "text",
|
|
546
|
+
text: `IBAN Validation Result: ❌ INVALID
|
|
547
|
+
|
|
548
|
+
IBAN: ${iban}
|
|
549
|
+
Cleaned: ${cleanIban}
|
|
550
|
+
|
|
551
|
+
The provided IBAN is not valid. Please check:
|
|
552
|
+
- Country code (first 2 characters)
|
|
553
|
+
- Check digits (characters 3-4)
|
|
554
|
+
- Bank account number format
|
|
555
|
+
- Overall length for the country
|
|
556
|
+
|
|
557
|
+
Common issues:
|
|
558
|
+
- Incorrect country code
|
|
559
|
+
- Invalid check digits
|
|
560
|
+
- Wrong length for the country
|
|
561
|
+
- Invalid characters in BBAN`,
|
|
562
|
+
},
|
|
563
|
+
],
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
return {
|
|
569
|
+
content: [
|
|
570
|
+
{
|
|
571
|
+
type: "text",
|
|
572
|
+
text: `Error validating IBAN: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
573
|
+
},
|
|
574
|
+
],
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
}
|