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.
@@ -0,0 +1,678 @@
1
+ import { z } from "zod";
2
+ export function registerTextTools(server) {
3
+ // Text case conversion tools
4
+ server.tool("text-uppercase", "Convert text to uppercase", {
5
+ text: z.string().describe("Text to convert to uppercase"),
6
+ }, async ({ text }) => {
7
+ return {
8
+ content: [
9
+ {
10
+ type: "text",
11
+ text: `Uppercase: ${text.toUpperCase()}`,
12
+ },
13
+ ],
14
+ };
15
+ });
16
+ server.tool("text-lowercase", "Convert text to lowercase", {
17
+ text: z.string().describe("Text to convert to lowercase"),
18
+ }, async ({ text }) => {
19
+ return {
20
+ content: [
21
+ {
22
+ type: "text",
23
+ text: `Lowercase: ${text.toLowerCase()}`,
24
+ },
25
+ ],
26
+ };
27
+ });
28
+ server.tool("text-capitalize", "Capitalize first letter of each word", {
29
+ text: z.string().describe("Text to capitalize"),
30
+ }, async ({ text }) => {
31
+ const capitalized = text.replace(/\b\w/g, l => l.toUpperCase());
32
+ return {
33
+ content: [
34
+ {
35
+ type: "text",
36
+ text: `Capitalized: ${capitalized}`,
37
+ },
38
+ ],
39
+ };
40
+ });
41
+ server.tool("text-camelcase", "Convert text to camelCase", {
42
+ text: z.string().describe("Text to convert to camelCase"),
43
+ }, async ({ text }) => {
44
+ const camelCase = text
45
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
46
+ return index === 0 ? word.toLowerCase() : word.toUpperCase();
47
+ })
48
+ .replace(/\s+/g, '');
49
+ return {
50
+ content: [
51
+ {
52
+ type: "text",
53
+ text: `camelCase: ${camelCase}`,
54
+ },
55
+ ],
56
+ };
57
+ });
58
+ server.tool("text-pascalcase", "Convert text to PascalCase", {
59
+ text: z.string().describe("Text to convert to PascalCase"),
60
+ }, async ({ text }) => {
61
+ const pascalCase = text
62
+ .replace(/(?:^\w|[A-Z]|\b\w)/g, word => word.toUpperCase())
63
+ .replace(/\s+/g, '');
64
+ return {
65
+ content: [
66
+ {
67
+ type: "text",
68
+ text: `PascalCase: ${pascalCase}`,
69
+ },
70
+ ],
71
+ };
72
+ });
73
+ server.tool("text-kebabcase", "Convert text to kebab-case", {
74
+ text: z.string().describe("Text to convert to kebab-case"),
75
+ }, async ({ text }) => {
76
+ const kebabCase = text
77
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
78
+ .replace(/[^a-zA-Z0-9]+/g, '-')
79
+ .toLowerCase()
80
+ .replace(/^-+|-+$/g, '');
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: `kebab-case: ${kebabCase}`,
86
+ },
87
+ ],
88
+ };
89
+ });
90
+ server.tool("text-snakecase", "Convert text to snake_case", {
91
+ text: z.string().describe("Text to convert to snake_case"),
92
+ }, async ({ text }) => {
93
+ const snakeCase = text
94
+ .replace(/([a-z])([A-Z])/g, '$1_$2')
95
+ .replace(/[^a-zA-Z0-9]+/g, '_')
96
+ .toLowerCase()
97
+ .replace(/^_+|_+$/g, '');
98
+ return {
99
+ content: [
100
+ {
101
+ type: "text",
102
+ text: `snake_case: ${snakeCase}`,
103
+ },
104
+ ],
105
+ };
106
+ });
107
+ // Text statistics tool
108
+ server.tool("text-stats", "Get statistics about text (character count, word count, etc.)", {
109
+ text: z.string().describe("Text to analyze"),
110
+ }, async ({ text }) => {
111
+ const lines = text.split('\n');
112
+ const words = text.trim().split(/\s+/).filter(word => word.length > 0);
113
+ const characters = text.length;
114
+ const charactersNoSpaces = text.replace(/\s/g, '').length;
115
+ const paragraphs = text.split(/\n\s*\n/).filter(para => para.trim().length > 0);
116
+ return {
117
+ content: [
118
+ {
119
+ type: "text",
120
+ text: `Text Statistics:
121
+
122
+ Characters: ${characters}
123
+ Characters (no spaces): ${charactersNoSpaces}
124
+ Words: ${words.length}
125
+ Lines: ${lines.length}
126
+ Paragraphs: ${paragraphs.length}
127
+
128
+ Reading time: ~${Math.ceil(words.length / 200)} minutes (200 WPM)
129
+ Speaking time: ~${Math.ceil(words.length / 150)} minutes (150 WPM)`,
130
+ },
131
+ ],
132
+ };
133
+ });
134
+ // Text comparison tool
135
+ server.tool("text-diff", "Compare two texts and show differences", {
136
+ text1: z.string().describe("First text to compare"),
137
+ text2: z.string().describe("Second text to compare"),
138
+ }, async ({ text1, text2 }) => {
139
+ try {
140
+ // Simple diff implementation
141
+ const lines1 = text1.split('\n');
142
+ const lines2 = text2.split('\n');
143
+ const maxLines = Math.max(lines1.length, lines2.length);
144
+ let differences = [];
145
+ let same = true;
146
+ for (let i = 0; i < maxLines; i++) {
147
+ const line1 = lines1[i] || '';
148
+ const line2 = lines2[i] || '';
149
+ if (line1 !== line2) {
150
+ same = false;
151
+ if (line1 && line2) {
152
+ differences.push(`Line ${i + 1}: Changed`);
153
+ differences.push(` - ${line1}`);
154
+ differences.push(` + ${line2}`);
155
+ }
156
+ else if (line1) {
157
+ differences.push(`Line ${i + 1}: Removed`);
158
+ differences.push(` - ${line1}`);
159
+ }
160
+ else {
161
+ differences.push(`Line ${i + 1}: Added`);
162
+ differences.push(` + ${line2}`);
163
+ }
164
+ }
165
+ }
166
+ if (same) {
167
+ return {
168
+ content: [
169
+ {
170
+ type: "text",
171
+ text: "โœ… Texts are identical - no differences found.",
172
+ },
173
+ ],
174
+ };
175
+ }
176
+ else {
177
+ return {
178
+ content: [
179
+ {
180
+ type: "text",
181
+ text: `โŒ Found differences:
182
+
183
+ ${differences.join('\n')}
184
+
185
+ Summary:
186
+ Lines in text 1: ${lines1.length}
187
+ Lines in text 2: ${lines2.length}`,
188
+ },
189
+ ],
190
+ };
191
+ }
192
+ }
193
+ catch (error) {
194
+ return {
195
+ content: [
196
+ {
197
+ type: "text",
198
+ text: `Error comparing texts: ${error instanceof Error ? error.message : 'Unknown error'}`,
199
+ },
200
+ ],
201
+ };
202
+ }
203
+ });
204
+ // ASCII art generator
205
+ server.tool("ascii-art-text", "Generate ASCII art text", {
206
+ text: z.string().describe("Text to convert to ASCII art, or use 'LIST_FONTS' to get all available font names"),
207
+ font: z.string().describe("ASCII art font style. Supports all 295+ figlet fonts. Use 'standard' if unsure.").optional(),
208
+ }, async ({ text, font = "standard" }) => {
209
+ try {
210
+ // Generate ASCII art using figlet
211
+ const figlet = await import('figlet');
212
+ // Get list of available fonts
213
+ const availableFonts = figlet.default.fontsSync();
214
+ // Check if user wants to list all fonts
215
+ if (text.toUpperCase() === 'LIST_FONTS') {
216
+ const sortedFonts = availableFonts.sort();
217
+ const popularFonts = [
218
+ "Standard", "Big", "Small", "Slant", "3-D", "Banner", "Block", "Shadow",
219
+ "Larry 3D", "Doom", "Star Wars", "Gothic", "Graffiti", "Bubble", "Digital"
220
+ ];
221
+ // Filter popular fonts that are actually available
222
+ const availablePopularFonts = popularFonts.filter(f => sortedFonts.some(availableFont => availableFont === f));
223
+ return {
224
+ content: [
225
+ {
226
+ type: "text",
227
+ text: `Available ASCII Art Fonts (${sortedFonts.length} total):
228
+
229
+ ๐ŸŒŸ POPULAR FONTS:
230
+ ${availablePopularFonts.join(', ')}
231
+
232
+ ๐Ÿ“ ALL AVAILABLE FONTS:
233
+ ${sortedFonts.join(', ')}
234
+
235
+ ๐Ÿ’ก Usage: Use any font name above as the 'font' parameter.
236
+ Examples: 'Standard', '3-D', 'Larry 3D', 'Banner', 'Block', etc.`,
237
+ },
238
+ ],
239
+ };
240
+ }
241
+ // Find the exact font match (case insensitive and flexible matching)
242
+ let targetFont = "Standard"; // Default fallback
243
+ const inputFont = font.toLowerCase();
244
+ // Direct match
245
+ const exactMatch = availableFonts.find(f => f.toLowerCase() === inputFont);
246
+ if (exactMatch) {
247
+ targetFont = exactMatch;
248
+ }
249
+ else {
250
+ // Fuzzy match - look for fonts that contain the input as substring
251
+ const partialMatch = availableFonts.find(f => f.toLowerCase().includes(inputFont) ||
252
+ inputFont.includes(f.toLowerCase()));
253
+ if (partialMatch) {
254
+ targetFont = partialMatch;
255
+ }
256
+ }
257
+ // Generate ASCII art
258
+ const asciiArt = figlet.default.textSync(text, {
259
+ font: targetFont,
260
+ horizontalLayout: 'default',
261
+ verticalLayout: 'default'
262
+ });
263
+ const fontUsed = targetFont === font ? font : `${font} โ†’ ${targetFont}`;
264
+ return {
265
+ content: [
266
+ {
267
+ type: "text",
268
+ text: `ASCII Art (${fontUsed}):\n\n${asciiArt}`,
269
+ },
270
+ ],
271
+ };
272
+ }
273
+ catch (error) {
274
+ // Get available fonts for error message
275
+ try {
276
+ const figlet = await import('figlet');
277
+ const availableFonts = figlet.default.fontsSync();
278
+ const popularFonts = [
279
+ "Standard", "Big", "Small", "Slant", "3-D", "Banner", "Block", "Shadow",
280
+ "Larry 3D", "Doom", "Star Wars", "Gothic", "Graffiti", "Bubble", "Digital"
281
+ ];
282
+ return {
283
+ content: [
284
+ {
285
+ type: "text",
286
+ text: `Error generating ASCII art: ${error instanceof Error ? error.message : 'Unknown error'}
287
+
288
+ Font '${font}' not found or invalid.
289
+
290
+ Popular fonts to try: ${popularFonts.join(', ')}
291
+
292
+ Total available fonts: ${availableFonts.length}
293
+ Some examples: ${availableFonts.slice(0, 10).join(', ')}...
294
+
295
+ Note: ASCII art generation works best with short text (1-10 characters).`,
296
+ },
297
+ ],
298
+ };
299
+ }
300
+ catch {
301
+ return {
302
+ content: [
303
+ {
304
+ type: "text",
305
+ text: `Error generating ASCII art: ${error instanceof Error ? error.message : 'Unknown error'}
306
+
307
+ Note: ASCII art generation works best with short text (1-10 characters).`,
308
+ },
309
+ ],
310
+ };
311
+ }
312
+ }
313
+ });
314
+ // NATO phonetic alphabet converter
315
+ server.tool("text-to-nato-alphabet", "Convert text to NATO phonetic alphabet", {
316
+ text: z.string().describe("Text to convert to NATO alphabet"),
317
+ }, async ({ text }) => {
318
+ const natoAlphabet = {
319
+ 'A': 'Alfa', 'B': 'Bravo', 'C': 'Charlie', 'D': 'Delta',
320
+ 'E': 'Echo', 'F': 'Foxtrot', 'G': 'Golf', 'H': 'Hotel',
321
+ 'I': 'India', 'J': 'Juliett', 'K': 'Kilo', 'L': 'Lima',
322
+ 'M': 'Mike', 'N': 'November', 'O': 'Oscar', 'P': 'Papa',
323
+ 'Q': 'Quebec', 'R': 'Romeo', 'S': 'Sierra', 'T': 'Tango',
324
+ 'U': 'Uniform', 'V': 'Victor', 'W': 'Whiskey', 'X': 'X-ray',
325
+ 'Y': 'Yankee', 'Z': 'Zulu',
326
+ '0': 'Zero', '1': 'One', '2': 'Two', '3': 'Three',
327
+ '4': 'Four', '5': 'Five', '6': 'Six', '7': 'Seven',
328
+ '8': 'Eight', '9': 'Nine', ' ': '[SPACE]'
329
+ };
330
+ const result = text
331
+ .toUpperCase()
332
+ .split('')
333
+ .map(char => natoAlphabet[char] || `[${char}]`)
334
+ .join(' ');
335
+ return {
336
+ content: [
337
+ {
338
+ type: "text",
339
+ text: `NATO Phonetic Alphabet:
340
+
341
+ Original: ${text}
342
+ NATO: ${result}`,
343
+ },
344
+ ],
345
+ };
346
+ });
347
+ // String obfuscator
348
+ server.tool("string-obfuscator", "Obfuscate text by replacing characters with their HTML entities or other representations", {
349
+ text: z.string().describe("Text to obfuscate"),
350
+ method: z.enum(["html-entities", "unicode", "base64"]).describe("Obfuscation method").optional(),
351
+ }, async ({ text, method = "html-entities" }) => {
352
+ try {
353
+ let result = '';
354
+ switch (method) {
355
+ case 'html-entities':
356
+ result = text
357
+ .split('')
358
+ .map(char => `&#${char.charCodeAt(0)};`)
359
+ .join('');
360
+ break;
361
+ case 'unicode':
362
+ result = text
363
+ .split('')
364
+ .map(char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`)
365
+ .join('');
366
+ break;
367
+ case 'base64':
368
+ result = Buffer.from(text, 'utf-8').toString('base64');
369
+ break;
370
+ }
371
+ return {
372
+ content: [
373
+ {
374
+ type: "text",
375
+ text: `Obfuscated Text (${method}):
376
+
377
+ Original: ${text}
378
+ Obfuscated: ${result}`,
379
+ },
380
+ ],
381
+ };
382
+ }
383
+ catch (error) {
384
+ return {
385
+ content: [
386
+ {
387
+ type: "text",
388
+ text: `Error obfuscating text: ${error instanceof Error ? error.message : 'Unknown error'}`,
389
+ },
390
+ ],
391
+ };
392
+ }
393
+ });
394
+ // Slugify string
395
+ server.tool("slugify-string", "Convert text to URL-friendly slug format", {
396
+ text: z.string().describe("Text to convert to slug"),
397
+ separator: z.string().describe("Character to use as separator").optional(),
398
+ lowercase: z.boolean().describe("Convert to lowercase").optional(),
399
+ }, async ({ text, separator = "-", lowercase = true }) => {
400
+ try {
401
+ let slug = text
402
+ .normalize('NFD')
403
+ .replace(/[\u0300-\u036f]/g, '') // Remove diacritics
404
+ .replace(/[^\w\s-]/g, '') // Remove special characters
405
+ .trim()
406
+ .replace(/\s+/g, separator) // Replace spaces with separator
407
+ .replace(new RegExp(`${separator}+`, 'g'), separator); // Remove duplicate separators
408
+ if (lowercase) {
409
+ slug = slug.toLowerCase();
410
+ }
411
+ return {
412
+ content: [
413
+ {
414
+ type: "text",
415
+ text: `Original: ${text}
416
+ Slug: ${slug}
417
+
418
+ Settings:
419
+ - Separator: "${separator}"
420
+ - Lowercase: ${lowercase}`,
421
+ },
422
+ ],
423
+ };
424
+ }
425
+ catch (error) {
426
+ return {
427
+ content: [
428
+ {
429
+ type: "text",
430
+ text: `Error creating slug: ${error instanceof Error ? error.message : 'Unknown error'}`,
431
+ },
432
+ ],
433
+ };
434
+ }
435
+ });
436
+ // Lorem Ipsum generator
437
+ server.tool("lorem-ipsum-generator", "Generate Lorem Ipsum placeholder text", {
438
+ count: z.number().describe("Number of items to generate").optional(),
439
+ type: z.enum(["words", "sentences", "paragraphs"]).describe("Type of content to generate").optional(),
440
+ }, async ({ count = 5, type = "sentences" }) => {
441
+ try {
442
+ if (count < 1 || count > 100) {
443
+ return {
444
+ content: [
445
+ {
446
+ type: "text",
447
+ text: "Count must be between 1 and 100.",
448
+ },
449
+ ],
450
+ };
451
+ }
452
+ const words = [
453
+ "lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit",
454
+ "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore",
455
+ "magna", "aliqua", "enim", "ad", "minim", "veniam", "quis", "nostrud",
456
+ "exercitation", "ullamco", "laboris", "nisi", "aliquip", "ex", "ea", "commodo",
457
+ "consequat", "duis", "aute", "irure", "in", "reprehenderit", "voluptate",
458
+ "velit", "esse", "cillum", "fugiat", "nulla", "pariatur", "excepteur", "sint",
459
+ "occaecat", "cupidatat", "non", "proident", "sunt", "culpa", "qui", "officia",
460
+ "deserunt", "mollit", "anim", "id", "est", "laborum"
461
+ ];
462
+ let result = "";
463
+ if (type === "words") {
464
+ const selectedWords = [];
465
+ for (let i = 0; i < count; i++) {
466
+ selectedWords.push(words[Math.floor(Math.random() * words.length)]);
467
+ }
468
+ result = selectedWords.join(" ");
469
+ }
470
+ else if (type === "sentences") {
471
+ const sentences = [];
472
+ for (let i = 0; i < count; i++) {
473
+ const sentenceLength = Math.floor(Math.random() * 10) + 5;
474
+ const sentenceWords = [];
475
+ for (let j = 0; j < sentenceLength; j++) {
476
+ sentenceWords.push(words[Math.floor(Math.random() * words.length)]);
477
+ }
478
+ sentenceWords[0] = sentenceWords[0].charAt(0).toUpperCase() + sentenceWords[0].slice(1);
479
+ sentences.push(sentenceWords.join(" ") + ".");
480
+ }
481
+ result = sentences.join(" ");
482
+ }
483
+ else if (type === "paragraphs") {
484
+ const paragraphs = [];
485
+ for (let i = 0; i < count; i++) {
486
+ const sentenceCount = Math.floor(Math.random() * 5) + 3;
487
+ const sentences = [];
488
+ for (let j = 0; j < sentenceCount; j++) {
489
+ const sentenceLength = Math.floor(Math.random() * 10) + 5;
490
+ const sentenceWords = [];
491
+ for (let k = 0; k < sentenceLength; k++) {
492
+ sentenceWords.push(words[Math.floor(Math.random() * words.length)]);
493
+ }
494
+ sentenceWords[0] = sentenceWords[0].charAt(0).toUpperCase() + sentenceWords[0].slice(1);
495
+ sentences.push(sentenceWords.join(" ") + ".");
496
+ }
497
+ paragraphs.push(sentences.join(" "));
498
+ }
499
+ result = paragraphs.join("\n\n");
500
+ }
501
+ return {
502
+ content: [
503
+ {
504
+ type: "text",
505
+ text: `Lorem Ipsum (${count} ${type}):\n\n${result}`,
506
+ },
507
+ ],
508
+ };
509
+ }
510
+ catch (error) {
511
+ return {
512
+ content: [
513
+ {
514
+ type: "text",
515
+ text: `Error generating Lorem Ipsum: ${error instanceof Error ? error.message : 'Unknown error'}`,
516
+ },
517
+ ],
518
+ };
519
+ }
520
+ });
521
+ // Numeronym generator
522
+ server.tool("numeronym-generator", "Generate numeronyms (abbreviations with numbers) from text", {
523
+ text: z.string().describe("Text to convert to numeronym"),
524
+ }, async ({ text }) => {
525
+ try {
526
+ const words = text.trim().split(/\s+/);
527
+ const numeronyms = words.map(word => {
528
+ if (word.length <= 3) {
529
+ return word;
530
+ }
531
+ const firstChar = word[0];
532
+ const lastChar = word[word.length - 1];
533
+ const middleCount = word.length - 2;
534
+ return `${firstChar}${middleCount}${lastChar}`;
535
+ });
536
+ return {
537
+ content: [
538
+ {
539
+ type: "text",
540
+ text: `Original: ${text}\nNumeronym: ${numeronyms.join(' ')}\n\nExamples:\n- internationalization โ†’ i18n\n- localization โ†’ l10n\n- accessibility โ†’ a11y`,
541
+ },
542
+ ],
543
+ };
544
+ }
545
+ catch (error) {
546
+ return {
547
+ content: [
548
+ {
549
+ type: "text",
550
+ text: `Error generating numeronym: ${error instanceof Error ? error.message : 'Unknown error'}`,
551
+ },
552
+ ],
553
+ };
554
+ }
555
+ });
556
+ // Text to Unicode converter
557
+ server.tool("text-to-unicode", "Convert text to Unicode code points and vice versa", {
558
+ input: z.string().describe("Text to convert to Unicode or Unicode to convert to text"),
559
+ operation: z.enum(["encode", "decode"]).describe("Operation: encode text to Unicode or decode Unicode to text"),
560
+ }, async ({ input, operation }) => {
561
+ try {
562
+ if (operation === "encode") {
563
+ const unicode = input
564
+ .split('')
565
+ .map(char => `U+${char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`)
566
+ .join(' ');
567
+ return {
568
+ content: [
569
+ {
570
+ type: "text",
571
+ text: `Text: ${input}\nUnicode: ${unicode}`,
572
+ },
573
+ ],
574
+ };
575
+ }
576
+ else {
577
+ // Decode Unicode to text
578
+ const unicodePattern = /U\+([0-9A-Fa-f]{4,6})/g;
579
+ const text = input.replace(unicodePattern, (match, hex) => {
580
+ const decimal = parseInt(hex, 16);
581
+ return String.fromCharCode(decimal);
582
+ });
583
+ return {
584
+ content: [
585
+ {
586
+ type: "text",
587
+ text: `Unicode: ${input}\nText: ${text}`,
588
+ },
589
+ ],
590
+ };
591
+ }
592
+ }
593
+ catch (error) {
594
+ return {
595
+ content: [
596
+ {
597
+ type: "text",
598
+ text: `Error converting text/Unicode: ${error instanceof Error ? error.message : 'Unknown error'}`,
599
+ },
600
+ ],
601
+ };
602
+ }
603
+ });
604
+ // Emoji search
605
+ server.tool("emoji-search", "Search for emojis by name or category", {
606
+ query: z.string().describe("Search term for emoji (name, category, or keyword)"),
607
+ }, async ({ query }) => {
608
+ try {
609
+ // Basic emoji database (simplified)
610
+ const emojis = {
611
+ // Faces
612
+ "happy": ["๐Ÿ˜€", "๐Ÿ˜ƒ", "๐Ÿ˜„", "๐Ÿ˜", "๐Ÿ˜Š", "๐Ÿ™‚", "๐Ÿ˜‰"],
613
+ "sad": ["๐Ÿ˜ข", "๐Ÿ˜ญ", "๐Ÿ˜”", "โ˜น๏ธ", "๐Ÿ™", "๐Ÿ˜ž", "๐Ÿ˜Ÿ"],
614
+ "love": ["๐Ÿ˜", "๐Ÿฅฐ", "๐Ÿ˜˜", "๐Ÿ’•", "๐Ÿ’–", "๐Ÿ’—", "โค๏ธ"],
615
+ "angry": ["๐Ÿ˜ ", "๐Ÿ˜ก", "๐Ÿคฌ", "๐Ÿ‘ฟ", "๐Ÿ’ข"],
616
+ // Animals
617
+ "cat": ["๐Ÿฑ", "๐Ÿˆ", "๐Ÿ™€", "๐Ÿ˜ธ", "๐Ÿ˜น", "๐Ÿ˜ป", "๐Ÿ˜ผ"],
618
+ "dog": ["๐Ÿถ", "๐Ÿ•", "๐Ÿฆฎ", "๐Ÿ•โ€๐Ÿฆบ"],
619
+ "animal": ["๐Ÿถ", "๐Ÿฑ", "๐Ÿญ", "๐Ÿน", "๐Ÿฐ", "๐ŸฆŠ", "๐Ÿป"],
620
+ // Food
621
+ "food": ["๐Ÿ•", "๐Ÿ”", "๐ŸŸ", "๐ŸŒญ", "๐Ÿฅช", "๐ŸŒฎ", "๐Ÿ", "๐Ÿœ"],
622
+ "fruit": ["๐ŸŽ", "๐ŸŠ", "๐Ÿ‹", "๐ŸŒ", "๐Ÿ‡", "๐Ÿ“", "๐Ÿซ", "๐Ÿˆ"],
623
+ // Objects
624
+ "tech": ["๐Ÿ’ป", "๐Ÿ“ฑ", "โŒš", "๐Ÿ“บ", "๐Ÿ“ท", "๐ŸŽฎ", "๐Ÿ’พ", "๐Ÿ’ฟ"],
625
+ "tools": ["๐Ÿ”ง", "๐Ÿ”จ", "โš’๏ธ", "๐Ÿ› ๏ธ", "โ›๏ธ", "๐Ÿช“", "๐Ÿ”ฉ"],
626
+ // Symbols
627
+ "check": ["โœ…", "โ˜‘๏ธ", "โœ”๏ธ"],
628
+ "cross": ["โŒ", "โŽ", "โœ–๏ธ"],
629
+ "star": ["โญ", "๐ŸŒŸ", "โœจ", "๐Ÿ’ซ", "โญ"],
630
+ "heart": ["โค๏ธ", "๐Ÿ’™", "๐Ÿ’š", "๐Ÿ’›", "๐Ÿงก", "๐Ÿ’œ", "๐Ÿ–ค", "๐Ÿค"]
631
+ };
632
+ const searchTerm = query.toLowerCase();
633
+ let results = [];
634
+ for (const [category, emojiList] of Object.entries(emojis)) {
635
+ if (category.includes(searchTerm)) {
636
+ results.push(...emojiList);
637
+ }
638
+ }
639
+ // Remove duplicates
640
+ results = [...new Set(results)];
641
+ if (results.length === 0) {
642
+ return {
643
+ content: [
644
+ {
645
+ type: "text",
646
+ text: `No emojis found for "${query}".
647
+
648
+ Available categories: ${Object.keys(emojis).join(', ')}`,
649
+ },
650
+ ],
651
+ };
652
+ }
653
+ return {
654
+ content: [
655
+ {
656
+ type: "text",
657
+ text: `Emojis for "${query}":
658
+
659
+ ${results.join(' ')}
660
+
661
+ Found ${results.length} emoji(s)
662
+ Copy any emoji above to use it!`,
663
+ },
664
+ ],
665
+ };
666
+ }
667
+ catch (error) {
668
+ return {
669
+ content: [
670
+ {
671
+ type: "text",
672
+ text: `Error searching emojis: ${error instanceof Error ? error.message : 'Unknown error'}`,
673
+ },
674
+ ],
675
+ };
676
+ }
677
+ });
678
+ }