it-tools-mcp 3.0.24 → 3.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/README.dockerhub.md +23 -18
- package/README.md +77 -34
- package/build/index.js +51 -24
- package/build/tools/ansible/ansible-inventory-generator/index.js +212 -0
- package/build/tools/ansible/ansible-playbook-validator/index.js +128 -0
- package/build/tools/ansible/ansible-reference/index.js +393 -0
- package/build/tools/ansible/ansible-vault-decrypt/index.js +137 -0
- package/build/tools/ansible/ansible-vault-encrypt/index.js +79 -0
- package/build/tools/color/color-hex-to-rgb/index.js +29 -0
- package/build/tools/{color.js → color/color-rgb-to-hex/index.js} +1 -27
- package/build/tools/crypto/basic-auth-generator/index.js +45 -0
- package/build/tools/crypto/bcrypt-hash/index.js +67 -0
- package/build/tools/crypto/bip39-generate/index.js +53 -0
- package/build/tools/crypto/hash-md5/index.js +19 -0
- package/build/tools/crypto/hash-sha1/index.js +19 -0
- package/build/tools/crypto/hash-sha256/index.js +19 -0
- package/build/tools/crypto/hash-sha512/index.js +19 -0
- package/build/tools/crypto/hmac-generator/index.js +37 -0
- package/build/tools/crypto/jwt-decode/index.js +41 -0
- package/build/tools/crypto/otp-code-generator/index.js +67 -0
- package/build/tools/crypto/password-generate/index.js +54 -0
- package/build/tools/crypto/token-generator/index.js +75 -0
- package/build/tools/dataFormat/html-to-markdown/index.js +34 -0
- package/build/tools/dataFormat/json-diff/index.js +94 -0
- package/build/tools/dataFormat/json-format/index.js +100 -0
- package/build/tools/dataFormat/json-minify/index.js +29 -0
- package/build/tools/dataFormat/json-to-csv/index.js +34 -0
- package/build/tools/dataFormat/json-to-toml/index.js +30 -0
- package/build/tools/dataFormat/markdown-to-html/index.js +32 -0
- package/build/tools/dataFormat/phone-format/index.js +35 -0
- package/build/tools/dataFormat/sql-format/index.js +37 -0
- package/build/tools/dataFormat/toml-to-json/index.js +29 -0
- package/build/tools/dataFormat/xml-format/index.js +44 -0
- package/build/tools/dataFormat/yaml-format/index.js +58 -0
- package/build/tools/{development.js → development/crontab-generate/index.js} +1 -129
- package/build/tools/development/html-prettifier/index.js +47 -0
- package/build/tools/development/javascript-prettifier/index.js +74 -0
- package/build/tools/development/list-converter/index.js +62 -0
- package/build/tools/development/markdown-toc-generator/index.js +53 -0
- package/build/tools/development/regex-tester/index.js +69 -0
- package/build/tools/docker/docker-compose-to-docker-run/index.js +138 -0
- package/build/tools/docker/docker-compose-validator/index.js +125 -0
- package/build/tools/docker/docker-reference/index.js +188 -0
- package/build/tools/docker/docker-run-to-docker-compose/index.js +117 -0
- package/build/tools/docker/traefik-compose-generator/index.js +98 -0
- package/build/tools/encoding/base64-decode/index.js +28 -0
- package/build/tools/encoding/base64-encode/index.js +16 -0
- package/build/tools/encoding/html-decode/index.js +21 -0
- package/build/tools/encoding/html-encode/index.js +21 -0
- package/build/tools/encoding/html-entities-extended/index.js +72 -0
- package/build/tools/encoding/text-to-binary/index.js +51 -0
- package/build/tools/encoding/url-decode/index.js +28 -0
- package/build/tools/encoding/url-encode/index.js +16 -0
- package/build/tools/forensic/file-type-identifier/index.js +90 -0
- package/build/tools/forensic/safelink-decoder/index.js +54 -0
- package/build/tools/forensic/url-fanger/index.js +52 -0
- package/build/tools/idGenerators/qr-generate/index.js +76 -0
- package/build/tools/idGenerators/svg-placeholder-generator/index.js +59 -0
- package/build/tools/idGenerators/ulid-generate/index.js +34 -0
- package/build/tools/idGenerators/uuid-generate/index.js +14 -0
- package/build/tools/math/math-evaluate/index.js +33 -0
- package/build/tools/math/number-base-converter/index.js +46 -0
- package/build/tools/math/percentage-calculator/index.js +50 -0
- package/build/tools/math/roman-numeral-converter/index.js +76 -0
- package/build/tools/math/temperature-converter/index.js +59 -0
- package/build/tools/math/unix-timestamp-converter/index.js +55 -0
- package/build/tools/network/cat/index.js +15 -0
- package/build/tools/network/cidr-to-ip-range/index.js +108 -0
- package/build/tools/network/curl/index.js +35 -0
- package/build/tools/network/dig/index.js +19 -0
- package/build/tools/network/grep/index.js +18 -0
- package/build/tools/network/head/index.js +17 -0
- package/build/tools/network/iban-validate/index.js +83 -0
- package/build/tools/network/ip-range-to-cidr/index.js +88 -0
- package/build/tools/network/ip-subnet-calculator/index.js +102 -0
- package/build/tools/network/ipv4-subnet-calc/index.js +112 -0
- package/build/tools/network/ipv6-subnet-calculator/index.js +104 -0
- package/build/tools/network/ipv6-ula-generator/index.js +65 -0
- package/build/tools/network/mac-address-generate/index.js +68 -0
- package/build/tools/network/nslookup/index.js +18 -0
- package/build/tools/network/ping/index.js +20 -0
- package/build/tools/network/ps/index.js +22 -0
- package/build/tools/network/random-port/index.js +53 -0
- package/build/tools/network/scp/index.js +134 -0
- package/build/tools/network/ssh/index.js +83 -0
- package/build/tools/network/tail/index.js +16 -0
- package/build/tools/network/telnet/index.js +45 -0
- package/build/tools/network/top/index.js +14 -0
- package/build/tools/network/url-parse/index.js +52 -0
- package/build/tools/physics/angle-converter/index.js +73 -0
- package/build/tools/physics/energy-converter/index.js +72 -0
- package/build/tools/physics/power-converter/index.js +71 -0
- package/build/tools/text/ascii-art-text/index.js +112 -0
- package/build/tools/text/distinct-words/index.js +30 -0
- package/build/tools/text/emoji-search/index.js +76 -0
- package/build/tools/text/lorem-ipsum-generator/index.js +87 -0
- package/build/tools/text/numeronym-generator/index.js +37 -0
- package/build/tools/text/slugify-string/index.js +44 -0
- package/build/tools/text/string-obfuscator/index.js +49 -0
- package/build/tools/text/text-camelcase/index.js +20 -0
- package/build/tools/text/text-capitalize/index.js +16 -0
- package/build/tools/text/text-diff/index.js +72 -0
- package/build/tools/text/text-kebabcase/index.js +20 -0
- package/build/tools/text/text-lowercase/index.js +15 -0
- package/build/tools/text/text-pascalcase/index.js +18 -0
- package/build/tools/text/text-snakecase/index.js +20 -0
- package/build/tools/text/text-stats/index.js +29 -0
- package/build/tools/text/text-to-nato-alphabet/index.js +57 -0
- package/build/tools/text/text-to-unicode/index.js +50 -0
- package/build/tools/text/text-to-unicode-names/index.js +34 -0
- package/build/tools/text/text-uppercase/index.js +15 -0
- package/build/tools/utility/css-prettifier/index.js +70 -0
- package/build/tools/utility/device-info/index.js +44 -0
- package/build/tools/utility/email-normalizer/index.js +73 -0
- package/build/tools/utility/http-status-codes/index.js +173 -0
- package/build/tools/utility/mime-types/index.js +121 -0
- package/build/tools/utility/port-numbers/index.js +106 -0
- package/build/tools/utility/rem-px-converter/index.js +63 -0
- package/package.json +3 -3
- package/build/tools/crypto.js +0 -445
- package/build/tools/dataFormat.js +0 -535
- package/build/tools/encoding.js +0 -240
- package/build/tools/idGenerators.js +0 -180
- package/build/tools/math.js +0 -310
- package/build/tools/network.js +0 -939
- package/build/tools/text.js +0 -678
- package/build/tools/utility.js +0 -407
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Buffer } from 'buffer';
|
|
3
|
+
export function registerFileTypeIdentifier(server) {
|
|
4
|
+
server.tool("file-type-identifier", "Identify file type based on magic numbers/file signatures", {
|
|
5
|
+
data: z.string().describe("Hex data or base64 data of file header"),
|
|
6
|
+
format: z.enum(["hex", "base64"]).describe("Format of the input data")
|
|
7
|
+
}, async ({ data, format }) => {
|
|
8
|
+
try {
|
|
9
|
+
let buffer;
|
|
10
|
+
if (format === "hex") {
|
|
11
|
+
const cleanHex = data.replace(/\s/g, '');
|
|
12
|
+
buffer = Buffer.from(cleanHex, 'hex');
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
buffer = Buffer.from(data, 'base64');
|
|
16
|
+
}
|
|
17
|
+
// Magic number signatures
|
|
18
|
+
const signatures = [
|
|
19
|
+
{ pattern: [0xFF, 0xD8, 0xFF], type: "JPEG Image", extension: ".jpg" },
|
|
20
|
+
{ pattern: [0x89, 0x50, 0x4E, 0x47], type: "PNG Image", extension: ".png" },
|
|
21
|
+
{ pattern: [0x47, 0x49, 0x46, 0x38], type: "GIF Image", extension: ".gif" },
|
|
22
|
+
{ pattern: [0x50, 0x4B, 0x03, 0x04], type: "ZIP Archive", extension: ".zip" },
|
|
23
|
+
{ pattern: [0x50, 0x4B, 0x05, 0x06], type: "ZIP Archive (empty)", extension: ".zip" },
|
|
24
|
+
{ pattern: [0x50, 0x4B, 0x07, 0x08], type: "ZIP Archive (spanned)", extension: ".zip" },
|
|
25
|
+
{ pattern: [0x25, 0x50, 0x44, 0x46], type: "PDF Document", extension: ".pdf" },
|
|
26
|
+
{ pattern: [0x52, 0x61, 0x72, 0x21], type: "RAR Archive", extension: ".rar" },
|
|
27
|
+
{ pattern: [0x7F, 0x45, 0x4C, 0x46], type: "ELF Executable", extension: "" },
|
|
28
|
+
{ pattern: [0x4D, 0x5A], type: "Windows Executable", extension: ".exe" },
|
|
29
|
+
{ pattern: [0xCA, 0xFE, 0xBA, 0xBE], type: "Java Class File", extension: ".class" },
|
|
30
|
+
{ pattern: [0x1F, 0x8B], type: "GZIP Archive", extension: ".gz" },
|
|
31
|
+
{ pattern: [0x42, 0x5A, 0x68], type: "BZIP2 Archive", extension: ".bz2" },
|
|
32
|
+
{ pattern: [0x37, 0x7A, 0xBC, 0xAF], type: "7-Zip Archive", extension: ".7z" },
|
|
33
|
+
{ pattern: [0x52, 0x49, 0x46, 0x46], type: "RIFF Container (WAV/AVI)", extension: ".wav/.avi" },
|
|
34
|
+
{ pattern: [0x49, 0x44, 0x33], type: "MP3 Audio", extension: ".mp3" },
|
|
35
|
+
{ pattern: [0x66, 0x74, 0x79, 0x70], type: "MP4 Video", extension: ".mp4", offset: 4 },
|
|
36
|
+
];
|
|
37
|
+
let detectedType = "Unknown";
|
|
38
|
+
let extension = "";
|
|
39
|
+
let matchDetails = "";
|
|
40
|
+
for (const sig of signatures) {
|
|
41
|
+
const offset = sig.offset || 0;
|
|
42
|
+
if (buffer.length >= offset + sig.pattern.length) {
|
|
43
|
+
let matches = true;
|
|
44
|
+
for (let i = 0; i < sig.pattern.length; i++) {
|
|
45
|
+
if (buffer[offset + i] !== sig.pattern[i]) {
|
|
46
|
+
matches = false;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (matches) {
|
|
51
|
+
detectedType = sig.type;
|
|
52
|
+
extension = sig.extension;
|
|
53
|
+
matchDetails = `Matched at offset ${offset}: ${sig.pattern.map(b => b.toString(16).padStart(2, '0')).join(' ')}`;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const hexDump = buffer.slice(0, Math.min(32, buffer.length))
|
|
59
|
+
.toString('hex')
|
|
60
|
+
.toUpperCase()
|
|
61
|
+
.replace(/.{2}/g, '$& ')
|
|
62
|
+
.trim();
|
|
63
|
+
return {
|
|
64
|
+
content: [{
|
|
65
|
+
type: "text",
|
|
66
|
+
text: `File Type Identification Results:
|
|
67
|
+
|
|
68
|
+
Detected Type: ${detectedType}
|
|
69
|
+
Extension: ${extension || "N/A"}
|
|
70
|
+
Data Size: ${buffer.length} bytes
|
|
71
|
+
|
|
72
|
+
Hex Dump (first 32 bytes):
|
|
73
|
+
${hexDump}
|
|
74
|
+
|
|
75
|
+
${matchDetails ? `Match Details: ${matchDetails}` : "No signature match found"}
|
|
76
|
+
|
|
77
|
+
Note: This is based on magic number detection. Some files may have multiple valid interpretations.`
|
|
78
|
+
}]
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return {
|
|
83
|
+
content: [{
|
|
84
|
+
type: "text",
|
|
85
|
+
text: `Error identifying file type: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
86
|
+
}]
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerSafelinkDecoder(server) {
|
|
3
|
+
server.tool("safelink-decoder", "Decode Microsoft Outlook SafeLink URLs", {
|
|
4
|
+
safelink: z.string().describe("SafeLink URL to decode")
|
|
5
|
+
}, async ({ safelink }) => {
|
|
6
|
+
try {
|
|
7
|
+
const url = new URL(safelink);
|
|
8
|
+
// Check if it's a SafeLink URL
|
|
9
|
+
if (!url.hostname.includes('safelinks.protection.outlook.com')) {
|
|
10
|
+
return {
|
|
11
|
+
content: [{
|
|
12
|
+
type: "text",
|
|
13
|
+
text: "This doesn't appear to be a SafeLink URL."
|
|
14
|
+
}]
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
// Extract the actual URL from the 'url' parameter
|
|
18
|
+
const actualUrl = url.searchParams.get('url');
|
|
19
|
+
if (!actualUrl) {
|
|
20
|
+
return {
|
|
21
|
+
content: [{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: "Could not find the original URL in the SafeLink."
|
|
24
|
+
}]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// Decode the URL
|
|
28
|
+
const decodedUrl = decodeURIComponent(actualUrl);
|
|
29
|
+
return {
|
|
30
|
+
content: [{
|
|
31
|
+
type: "text",
|
|
32
|
+
text: `SafeLink Decoder Results:
|
|
33
|
+
|
|
34
|
+
Original SafeLink: ${safelink}
|
|
35
|
+
|
|
36
|
+
Decoded URL: ${decodedUrl}
|
|
37
|
+
|
|
38
|
+
Additional Parameters:
|
|
39
|
+
- Data: ${url.searchParams.get('data') || 'N/A'}
|
|
40
|
+
- Reserved: ${url.searchParams.get('reserved') || 'N/A'}
|
|
41
|
+
- Source: ${url.searchParams.get('source') || 'N/A'}`
|
|
42
|
+
}]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return {
|
|
47
|
+
content: [{
|
|
48
|
+
type: "text",
|
|
49
|
+
text: `Error decoding SafeLink: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
50
|
+
}]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerUrlFanger(server) {
|
|
3
|
+
server.tool("url-fanger", "Defang or refang URLs for safe sharing (security analysis)", {
|
|
4
|
+
text: z.string().describe("Text containing URLs to fang/defang"),
|
|
5
|
+
operation: z.enum(["defang", "refang"]).describe("Whether to defang (make safe) or refang (restore) URLs")
|
|
6
|
+
}, async ({ text, operation }) => {
|
|
7
|
+
try {
|
|
8
|
+
let result = text;
|
|
9
|
+
if (operation === "defang") {
|
|
10
|
+
// Defang URLs to make them safe
|
|
11
|
+
result = result
|
|
12
|
+
.replace(/https?:\/\//g, 'hxxp://') // Replace http/https with hxxp
|
|
13
|
+
.replace(/\./g, '[.]') // Replace dots with [.]
|
|
14
|
+
.replace(/@/g, '[@]') // Replace @ with [@]
|
|
15
|
+
.replace(/:/g, '[:]'); // Replace colons with [:]
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
// Refang URLs to restore them
|
|
19
|
+
result = result
|
|
20
|
+
.replace(/hxxp:\/\//g, 'http://') // Restore http
|
|
21
|
+
.replace(/hxxps:\/\//g, 'https://') // Restore https
|
|
22
|
+
.replace(/\[\.\]/g, '.') // Restore dots
|
|
23
|
+
.replace(/\[@\]/g, '@') // Restore @
|
|
24
|
+
.replace(/\[:\]/g, ':'); // Restore colons
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
content: [{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: `URL ${operation === "defang" ? "Defanging" : "Refanging"} Results:
|
|
30
|
+
|
|
31
|
+
Original:
|
|
32
|
+
${text}
|
|
33
|
+
|
|
34
|
+
${operation === "defang" ? "Defanged" : "Refanged"}:
|
|
35
|
+
${result}
|
|
36
|
+
|
|
37
|
+
Note: ${operation === "defang"
|
|
38
|
+
? "Defanged URLs are safe to share and won't be clickable."
|
|
39
|
+
: "Refanged URLs have been restored to their original form."}`
|
|
40
|
+
}]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return {
|
|
45
|
+
content: [{
|
|
46
|
+
type: "text",
|
|
47
|
+
text: `Error processing URLs: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
48
|
+
}]
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerQrGenerate(server) {
|
|
3
|
+
server.tool("qr-generate", "Generate QR code for any text including URLs, WiFi networks, contact info, etc.", {
|
|
4
|
+
text: z.string().describe("Text to encode in QR code (URLs, WiFi: WIFI:T:WPA;S:network;P:password;;, contact info, etc.)"),
|
|
5
|
+
size: z.number().describe("Size multiplier (1-3)").optional(),
|
|
6
|
+
}, async ({ text, size = 1 }) => {
|
|
7
|
+
try {
|
|
8
|
+
const QRCode = (await import("qrcode")).default;
|
|
9
|
+
if (size < 1 || size > 3) {
|
|
10
|
+
return {
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
type: "text",
|
|
14
|
+
text: "Size must be between 1 and 3.",
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// Generate QR code as base64 data URL
|
|
20
|
+
console.log(`[DEBUG] Generating QR code for: "${text}" with size: ${size}`);
|
|
21
|
+
const dataUrl = await QRCode.toDataURL(text, {
|
|
22
|
+
type: 'image/png',
|
|
23
|
+
errorCorrectionLevel: 'M',
|
|
24
|
+
width: Math.max(256, size * 128), // Minimum 256px, scales with size parameter
|
|
25
|
+
margin: 2,
|
|
26
|
+
color: {
|
|
27
|
+
dark: '#000000', // Black
|
|
28
|
+
light: '#FFFFFF' // White
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
console.log(`[DEBUG] QR code generated successfully`);
|
|
32
|
+
// Extract just the base64 data (remove the data:image/png;base64, prefix)
|
|
33
|
+
const base64Data = dataUrl.split(',')[1];
|
|
34
|
+
const markdown = ``;
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: `📱 QR Code for: "${text}"
|
|
40
|
+
\n📊 Data encoded: "${text}" (${text.length} characters)
|
|
41
|
+
🎯 Error correction: Medium (M)
|
|
42
|
+
📐 Image size: ${Math.max(256, size * 128)}x${Math.max(256, size * 128)} pixels
|
|
43
|
+
\n✅ This QR code can be scanned with any QR code reader app
|
|
44
|
+
💡 Generated using the 'qrcode' npm library!
|
|
45
|
+
\n---\n**Markdown for inline display:**\n${markdown}`,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "image",
|
|
49
|
+
data: base64Data,
|
|
50
|
+
mimeType: "image/png"
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error(`[DEBUG] QR code generation failed:`, error);
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: "text",
|
|
61
|
+
text: `Error generating QR code: ${error instanceof Error ? error.message : 'Unknown error'}\n\nDebug info:\n- Text: \"${text}\"\n- Size: ${size}`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Fallback return in case of unexpected behavior
|
|
67
|
+
return {
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: "Unknown error: No response generated by qr-generate.",
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerSvgPlaceholderGenerator(server) {
|
|
3
|
+
server.tool("svg-placeholder-generator", "Generate SVG placeholder images", {
|
|
4
|
+
width: z.number().describe("Width in pixels").optional(),
|
|
5
|
+
height: z.number().describe("Height in pixels").optional(),
|
|
6
|
+
backgroundColor: z.string().describe("Background color (hex)").optional(),
|
|
7
|
+
textColor: z.string().describe("Text color (hex)").optional(),
|
|
8
|
+
text: z.string().optional().describe("Custom text (default: dimensions)"),
|
|
9
|
+
}, async ({ width = 300, height = 200, backgroundColor = "#cccccc", textColor = "#666666", text }) => {
|
|
10
|
+
try {
|
|
11
|
+
if (width < 1 || width > 2000 || height < 1 || height > 2000) {
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: "Width and height must be between 1 and 2000 pixels.",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const displayText = text || `${width}×${height}`;
|
|
22
|
+
const fontSize = Math.min(width, height) / 8;
|
|
23
|
+
const svg = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
|
|
24
|
+
<rect width="100%" height="100%" fill="${backgroundColor}"/>
|
|
25
|
+
<text x="50%" y="50%" font-family="Arial, sans-serif" font-size="${fontSize}" fill="${textColor}" text-anchor="middle" dy=".3em">${displayText}</text>
|
|
26
|
+
</svg>`;
|
|
27
|
+
const dataUrl = `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`;
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: "text",
|
|
32
|
+
text: `SVG Placeholder Generated:
|
|
33
|
+
|
|
34
|
+
Dimensions: ${width}×${height}
|
|
35
|
+
Background: ${backgroundColor}
|
|
36
|
+
Text Color: ${textColor}
|
|
37
|
+
Text: ${displayText}
|
|
38
|
+
|
|
39
|
+
SVG Code:
|
|
40
|
+
${svg}
|
|
41
|
+
|
|
42
|
+
Data URL:
|
|
43
|
+
${dataUrl}`,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: "text",
|
|
53
|
+
text: `Error generating SVG placeholder: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function registerUlidGenerate(server) {
|
|
2
|
+
server.tool("ulid-generate", "Generate Universally Unique Lexicographically Sortable Identifier", {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
// Simplified ULID implementation
|
|
5
|
+
const timestamp = Date.now();
|
|
6
|
+
const randomPart = Math.random().toString(36).substring(2, 18);
|
|
7
|
+
const ulid = timestamp.toString(36).toUpperCase() + randomPart.toUpperCase();
|
|
8
|
+
return {
|
|
9
|
+
content: [
|
|
10
|
+
{
|
|
11
|
+
type: "text",
|
|
12
|
+
text: `Generated ULID: ${ulid}
|
|
13
|
+
|
|
14
|
+
Timestamp: ${timestamp}
|
|
15
|
+
Generated at: ${new Date(timestamp).toISOString()}
|
|
16
|
+
|
|
17
|
+
Note: This is a simplified ULID implementation.
|
|
18
|
+
For production use, please use a proper ULID library.`,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: `Error generating ULID: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
export function registerUuidGenerate(server) {
|
|
3
|
+
server.tool("uuid-generate", "Generate a random UUID v4", {}, async () => {
|
|
4
|
+
const uuid = randomUUID();
|
|
5
|
+
return {
|
|
6
|
+
content: [
|
|
7
|
+
{
|
|
8
|
+
type: "text",
|
|
9
|
+
text: `Generated UUID: ${uuid}`,
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerMathEvaluate(server) {
|
|
3
|
+
server.tool("math-evaluate", "Safely evaluate mathematical expressions", {
|
|
4
|
+
expression: z.string().describe("Mathematical expression to evaluate (e.g., '2 + 3 * 4')")
|
|
5
|
+
}, async ({ expression }) => {
|
|
6
|
+
try {
|
|
7
|
+
// @ts-ignore: Ignore missing type declarations for mathjs
|
|
8
|
+
const { compile } = await import("mathjs");
|
|
9
|
+
const start = Date.now();
|
|
10
|
+
const code = compile(expression);
|
|
11
|
+
const result = code.evaluate();
|
|
12
|
+
const elapsed = Date.now() - start;
|
|
13
|
+
return {
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: "text",
|
|
17
|
+
text: `Expression: ${expression}\nResult: ${result}\n(evaluated in ${elapsed} ms)`
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: `Error evaluating expression: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerNumberBaseConverter(server) {
|
|
3
|
+
server.tool("number-base-converter", "Convert numbers between different bases (binary, octal, decimal, hexadecimal)", {
|
|
4
|
+
number: z.string().describe("Number to convert"),
|
|
5
|
+
fromBase: z.number().describe("Source base (2-36)"),
|
|
6
|
+
toBase: z.number().describe("Target base (2-36)")
|
|
7
|
+
}, async ({ number, fromBase, toBase }) => {
|
|
8
|
+
try {
|
|
9
|
+
if (fromBase < 2 || fromBase > 36 || toBase < 2 || toBase > 36) {
|
|
10
|
+
return {
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
type: "text",
|
|
14
|
+
text: "Base must be between 2 and 36.",
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// Parse number from source base to decimal
|
|
20
|
+
const decimal = parseInt(number, fromBase);
|
|
21
|
+
if (isNaN(decimal)) {
|
|
22
|
+
throw new Error("Invalid number for the specified base");
|
|
23
|
+
}
|
|
24
|
+
// Convert decimal to target base
|
|
25
|
+
const result = decimal.toString(toBase);
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{
|
|
29
|
+
type: "text",
|
|
30
|
+
text: `${number} (base ${fromBase}) = ${result} (base ${toBase})\nDecimal equivalent: ${decimal}`
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: `Error converting number: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerPercentageCalculator(server) {
|
|
3
|
+
server.tool("percentage-calculator", "Calculate percentages, percentage of a number, or percentage change", {
|
|
4
|
+
operation: z.enum(["percentage-of", "what-percentage", "percentage-change"]).describe("Type of percentage calculation"),
|
|
5
|
+
value1: z.number().describe("First value"),
|
|
6
|
+
value2: z.number().describe("Second value")
|
|
7
|
+
}, async ({ operation, value1, value2 }) => {
|
|
8
|
+
try {
|
|
9
|
+
let result;
|
|
10
|
+
let explanation;
|
|
11
|
+
switch (operation) {
|
|
12
|
+
case "percentage-of":
|
|
13
|
+
// value1% of value2
|
|
14
|
+
result = (value1 / 100) * value2;
|
|
15
|
+
explanation = `${value1}% of ${value2} = ${result}`;
|
|
16
|
+
break;
|
|
17
|
+
case "what-percentage":
|
|
18
|
+
// value1 is what percentage of value2
|
|
19
|
+
result = (value1 / value2) * 100;
|
|
20
|
+
explanation = `${value1} is ${result.toFixed(2)}% of ${value2}`;
|
|
21
|
+
break;
|
|
22
|
+
case "percentage-change":
|
|
23
|
+
// percentage change from value1 to value2
|
|
24
|
+
result = ((value2 - value1) / value1) * 100;
|
|
25
|
+
explanation = `Percentage change from ${value1} to ${value2} = ${result.toFixed(2)}%`;
|
|
26
|
+
break;
|
|
27
|
+
default:
|
|
28
|
+
throw new Error("Invalid operation");
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: explanation
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: `Error calculating percentage: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerRomanNumeralConverter(server) {
|
|
3
|
+
server.tool("roman-numeral-converter", "Convert between Arabic numbers and Roman numerals", {
|
|
4
|
+
input: z.string().describe("Number to convert (Arabic number 1-3999 or Roman numeral)")
|
|
5
|
+
}, async ({ input }) => {
|
|
6
|
+
try {
|
|
7
|
+
// Auto-detect if input is a number or Roman numeral
|
|
8
|
+
const isNumber = /^\d+$/.test(input.trim());
|
|
9
|
+
if (isNumber) {
|
|
10
|
+
// Convert number to Roman numeral
|
|
11
|
+
const num = parseInt(input);
|
|
12
|
+
if (isNaN(num) || num < 1 || num > 3999) {
|
|
13
|
+
throw new Error("Number must be between 1 and 3999");
|
|
14
|
+
}
|
|
15
|
+
const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
|
|
16
|
+
const symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
|
|
17
|
+
let result = "";
|
|
18
|
+
let remaining = num;
|
|
19
|
+
for (let i = 0; i < values.length; i++) {
|
|
20
|
+
while (remaining >= values[i]) {
|
|
21
|
+
result += symbols[i];
|
|
22
|
+
remaining -= values[i];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: `${num} = ${result} in Roman numerals`
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Convert Roman numeral to number
|
|
36
|
+
const roman = input.toUpperCase();
|
|
37
|
+
const romanMap = {
|
|
38
|
+
I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000
|
|
39
|
+
};
|
|
40
|
+
let result = 0;
|
|
41
|
+
let prev = 0;
|
|
42
|
+
for (let i = roman.length - 1; i >= 0; i--) {
|
|
43
|
+
const current = romanMap[roman[i]];
|
|
44
|
+
if (!current) {
|
|
45
|
+
throw new Error(`Invalid Roman numeral character: ${roman[i]}`);
|
|
46
|
+
}
|
|
47
|
+
if (current < prev) {
|
|
48
|
+
result -= current;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
result += current;
|
|
52
|
+
}
|
|
53
|
+
prev = current;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
content: [
|
|
57
|
+
{
|
|
58
|
+
type: "text",
|
|
59
|
+
text: `${roman} = ${result} in decimal`
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: `Error converting Roman numeral: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function registerTemperatureConverter(server) {
|
|
3
|
+
server.tool("temperature-converter", "Convert temperatures between Celsius, Fahrenheit, and Kelvin", {
|
|
4
|
+
temperature: z.number().describe("Temperature value to convert"),
|
|
5
|
+
from: z.enum(["celsius", "fahrenheit", "kelvin"]).describe("Source temperature unit"),
|
|
6
|
+
to: z.enum(["celsius", "fahrenheit", "kelvin"]).describe("Target temperature unit")
|
|
7
|
+
}, async ({ temperature, from, to }) => {
|
|
8
|
+
try {
|
|
9
|
+
// Convert to Celsius first
|
|
10
|
+
let celsius;
|
|
11
|
+
switch (from) {
|
|
12
|
+
case "celsius":
|
|
13
|
+
celsius = temperature;
|
|
14
|
+
break;
|
|
15
|
+
case "fahrenheit":
|
|
16
|
+
celsius = (temperature - 32) * 5 / 9;
|
|
17
|
+
break;
|
|
18
|
+
case "kelvin":
|
|
19
|
+
celsius = temperature - 273.15;
|
|
20
|
+
break;
|
|
21
|
+
default:
|
|
22
|
+
throw new Error("Invalid source unit");
|
|
23
|
+
}
|
|
24
|
+
// Convert from Celsius to target unit
|
|
25
|
+
let result;
|
|
26
|
+
switch (to) {
|
|
27
|
+
case "celsius":
|
|
28
|
+
result = celsius;
|
|
29
|
+
break;
|
|
30
|
+
case "fahrenheit":
|
|
31
|
+
result = celsius * 9 / 5 + 32;
|
|
32
|
+
break;
|
|
33
|
+
case "kelvin":
|
|
34
|
+
result = celsius + 273.15;
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
throw new Error("Invalid target unit");
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: "text",
|
|
43
|
+
text: `${temperature}° ${from} = ${result.toFixed(2)}° ${to}`
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: "text",
|
|
53
|
+
text: `Error converting temperature: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|