@nekzus/mcp-server 1.1.7 → 1.1.8

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.
Files changed (2) hide show
  1. package/dist/index.js +105 -310
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
5
  import 'dotenv/config';
6
6
  // Logger function that uses stderr - only for critical errors
7
7
  const log = (...args) => {
@@ -12,7 +12,7 @@ const log = (...args) => {
12
12
  console.error(...args);
13
13
  }
14
14
  };
15
- // Define the tools once to avoid repetition
15
+ // Define tools
16
16
  const TOOLS = [
17
17
  {
18
18
  name: 'greeting',
@@ -151,48 +151,26 @@ const TOOLS = [
151
151
  ];
152
152
  // Tool handlers
153
153
  async function handleGreeting(args) {
154
- const { name } = args;
155
154
  return {
156
155
  content: [
157
156
  {
158
157
  type: 'text',
159
- text: `šŸ‘‹ Hello ${name}! Welcome to the MCP server!`,
158
+ text: `šŸ‘‹ Hello ${args.name}! Welcome to the MCP server!`,
160
159
  },
161
160
  ],
162
161
  isError: false,
163
162
  };
164
163
  }
165
164
  async function handleCard() {
166
- const suits = {
167
- 'ā™ ': 'Spades',
168
- '♄': 'Hearts',
169
- '♦': 'Diamonds',
170
- '♣': 'Clubs',
171
- };
172
- const values = {
173
- A: 'Ace',
174
- '2': 'Two',
175
- '3': 'Three',
176
- '4': 'Four',
177
- '5': 'Five',
178
- '6': 'Six',
179
- '7': 'Seven',
180
- '8': 'Eight',
181
- '9': 'Nine',
182
- '10': 'Ten',
183
- J: 'Jack',
184
- Q: 'Queen',
185
- K: 'King',
186
- };
187
- const suitSymbols = Object.keys(suits);
188
- const valueSymbols = Object.keys(values);
189
- const suitSymbol = suitSymbols[Math.floor(Math.random() * suitSymbols.length)];
190
- const valueSymbol = valueSymbols[Math.floor(Math.random() * valueSymbols.length)];
165
+ const suits = ['ā™ ļø', 'ā™„ļø', 'ā™£ļø', 'ā™¦ļø'];
166
+ const values = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
167
+ const suit = suits[Math.floor(Math.random() * suits.length)];
168
+ const value = values[Math.floor(Math.random() * values.length)];
191
169
  return {
192
170
  content: [
193
171
  {
194
172
  type: 'text',
195
- text: `šŸŽ“ You drew: ${values[valueSymbol]} of ${suitSymbol} ${suits[suitSymbol]}`,
173
+ text: `šŸŽ“ Drew card: ${value}${suit}`,
196
174
  },
197
175
  ],
198
176
  isError: false,
@@ -200,60 +178,34 @@ async function handleCard() {
200
178
  }
201
179
  async function handleDateTime(args) {
202
180
  const { timeZone = 'UTC', locale = 'en-US' } = args;
203
- try {
204
- const date = new Date();
205
- const dateFormatter = new Intl.DateTimeFormat(locale, {
206
- timeZone,
207
- dateStyle: 'long',
208
- });
209
- const timeFormatter = new Intl.DateTimeFormat(locale, {
210
- timeZone,
211
- timeStyle: 'medium',
212
- });
213
- const formattedDate = dateFormatter.format(date);
214
- const formattedTime = timeFormatter.format(date);
215
- return {
216
- content: [
217
- {
218
- type: 'text',
219
- text: `šŸ—“ļø Date: ${formattedDate}\nā° Time: ${formattedTime}\nšŸŒ Timezone: ${timeZone}`,
220
- },
221
- ],
222
- isError: false,
223
- };
224
- }
225
- catch (error) {
226
- return {
227
- content: [
228
- {
229
- type: 'text',
230
- text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
231
- },
232
- ],
233
- isError: true,
234
- };
235
- }
181
+ const date = new Date();
182
+ const formattedDate = new Intl.DateTimeFormat(locale, {
183
+ timeZone,
184
+ dateStyle: 'full',
185
+ timeStyle: 'long',
186
+ }).format(date);
187
+ return {
188
+ content: [
189
+ {
190
+ type: 'text',
191
+ text: `šŸ•’ Current date and time in ${timeZone}: ${formattedDate}`,
192
+ },
193
+ ],
194
+ isError: false,
195
+ };
236
196
  }
237
- // New tool handlers
238
197
  async function handleCalculator(args) {
239
- const { expression, precision = 2 } = args;
240
198
  try {
241
- // Sanitize and validate the expression
242
- const sanitizedExpression = expression.replace(/[^0-9+\-*/().%\s]/g, '');
243
- if (sanitizedExpression !== expression) {
244
- throw new Error('Invalid characters in expression');
245
- }
246
- // Use Function constructor instead of eval for better security
199
+ const sanitizedExpression = args.expression.replace(/[^0-9+\-*/().%\s]/g, '');
247
200
  const calculate = new Function(`return ${sanitizedExpression}`);
248
201
  const result = calculate();
249
- if (typeof result !== 'number' || !Number.isFinite(result)) {
250
- throw new Error('Invalid mathematical expression');
251
- }
202
+ const precision = args.precision ?? 2;
203
+ const formattedResult = Number.isInteger(result) ? result : Number(result.toFixed(precision));
252
204
  return {
253
205
  content: [
254
206
  {
255
207
  type: 'text',
256
- text: `🧮 Expression: ${expression}\nšŸ“Š Result: ${result.toFixed(precision)}`,
208
+ text: `šŸ”¢ Result: ${formattedResult}`,
257
209
  },
258
210
  ],
259
211
  isError: false,
@@ -264,7 +216,7 @@ async function handleCalculator(args) {
264
216
  content: [
265
217
  {
266
218
  type: 'text',
267
- text: `āŒ Error: ${error instanceof Error ? error.message : 'Invalid expression'}`,
219
+ text: `Error calculating result: ${error.message}`,
268
220
  },
269
221
  ],
270
222
  isError: true,
@@ -272,196 +224,55 @@ async function handleCalculator(args) {
272
224
  }
273
225
  }
274
226
  async function handlePasswordGen(args) {
275
- const { length = 16, includeNumbers = true, includeSymbols = true, includeUppercase = true, } = args;
276
- try {
277
- if (length < 8 || length > 128) {
278
- throw new Error('Password length must be between 8 and 128 characters');
279
- }
280
- const lowercase = 'abcdefghijklmnopqrstuvwxyz';
281
- const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
282
- const numbers = '0123456789';
283
- const symbols = '!@#$%^&*()_+-=[]{}|;:,.<>?';
284
- let chars = lowercase;
285
- if (includeUppercase)
286
- chars += uppercase;
287
- if (includeNumbers)
288
- chars += numbers;
289
- if (includeSymbols)
290
- chars += symbols;
291
- let password = '';
292
- for (let i = 0; i < length; i++) {
293
- password += chars.charAt(Math.floor(Math.random() * chars.length));
294
- }
295
- // Ensure at least one character from each selected type
296
- const types = [
297
- { char: lowercase.charAt(Math.floor(Math.random() * lowercase.length)), condition: true },
298
- {
299
- char: uppercase.charAt(Math.floor(Math.random() * uppercase.length)),
300
- condition: includeUppercase,
301
- },
302
- {
303
- char: numbers.charAt(Math.floor(Math.random() * numbers.length)),
304
- condition: includeNumbers,
305
- },
227
+ const length = args.length ?? 16;
228
+ const includeNumbers = args.includeNumbers ?? true;
229
+ const includeSymbols = args.includeSymbols ?? true;
230
+ const includeUppercase = args.includeUppercase ?? true;
231
+ let chars = 'abcdefghijklmnopqrstuvwxyz';
232
+ if (includeUppercase)
233
+ chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
234
+ if (includeNumbers)
235
+ chars += '0123456789';
236
+ if (includeSymbols)
237
+ chars += '!@#$%^&*()_+-=[]{}|;:,.<>?';
238
+ let password = '';
239
+ for (let i = 0; i < length; i++) {
240
+ password += chars.charAt(Math.floor(Math.random() * chars.length));
241
+ }
242
+ return {
243
+ content: [
306
244
  {
307
- char: symbols.charAt(Math.floor(Math.random() * symbols.length)),
308
- condition: includeSymbols,
245
+ type: 'text',
246
+ text: `šŸ” Generated password: ${password}`,
309
247
  },
310
- ];
311
- types.forEach(({ char, condition }, index) => {
312
- if (condition) {
313
- const pos = Math.floor(Math.random() * length);
314
- password = password.slice(0, pos) + char + password.slice(pos + 1);
315
- }
316
- });
317
- return {
318
- content: [
319
- {
320
- type: 'text',
321
- text: `šŸ” Generated Password:\n${password}\n\nšŸ“‹ Password Properties:\n• Length: ${length}\n• Includes Numbers: ${includeNumbers ? 'āœ…' : 'āŒ'}\n• Includes Symbols: ${includeSymbols ? 'āœ…' : 'āŒ'}\n• Includes Uppercase: ${includeUppercase ? 'āœ…' : 'āŒ'}`,
322
- },
323
- ],
324
- isError: false,
325
- };
326
- }
327
- catch (error) {
328
- return {
329
- content: [
330
- {
331
- type: 'text',
332
- text: `āŒ Error: ${error instanceof Error ? error.message : 'Failed to generate password'}`,
333
- },
334
- ],
335
- isError: true,
336
- };
337
- }
248
+ ],
249
+ isError: false,
250
+ };
338
251
  }
339
252
  async function handleQRGen(args) {
340
- const { text, size = 200, dark = '#000000', light = '#ffffff' } = args;
341
- try {
342
- if (!text) {
343
- throw new Error('Text is required');
344
- }
345
- if (size < 100 || size > 1000) {
346
- throw new Error('Size must be between 100 and 1000 pixels');
347
- }
348
- // Validate color format
349
- const colorRegex = /^#[0-9A-Fa-f]{6}$/;
350
- if (!colorRegex.test(dark) || !colorRegex.test(light)) {
351
- throw new Error('Invalid color format. Use hexadecimal format (e.g., #000000)');
352
- }
353
- // Here we would normally generate the QR code
354
- // For now, we'll return a placeholder message
355
- return {
356
- content: [
357
- {
358
- type: 'text',
359
- text: `šŸ“± QR Code Properties:\n• Content: ${text}\n• Size: ${size}px\n• Dark Color: ${dark}\n• Light Color: ${light}\n\nšŸ”„ QR Code generation successful! (Implementation pending)`,
360
- },
361
- ],
362
- isError: false,
363
- };
364
- }
365
- catch (error) {
366
- return {
367
- content: [
368
- {
369
- type: 'text',
370
- text: `āŒ Error: ${error instanceof Error ? error.message : 'Failed to generate QR code'}`,
371
- },
372
- ],
373
- isError: true,
374
- };
375
- }
253
+ return {
254
+ content: [
255
+ {
256
+ type: 'text',
257
+ text: 'QR code generation is not implemented yet',
258
+ },
259
+ ],
260
+ isError: true,
261
+ };
376
262
  }
377
263
  async function handleKitchenConvert(args) {
378
- const { value, from, to, ingredient } = args;
379
- // Conversion factors (base unit: milliliters for volume, grams for weight)
380
- const volumeConversions = {
381
- ml: 1, // milliliters
382
- l: 1000, // liters
383
- cup: 236.588, // US cup
384
- tbsp: 14.787, // tablespoon
385
- tsp: 4.929, // teaspoon
386
- floz: 29.574, // fluid ounce
387
- };
388
- const weightConversions = {
389
- g: 1, // grams
390
- kg: 1000, // kilograms
391
- oz: 28.3495, // ounces
392
- lb: 453.592, // pounds
393
- };
394
- // Common ingredient densities (g/ml)
395
- const densities = {
396
- water: 1.0, // water density at room temperature
397
- milk: 1.03, // whole milk
398
- flour: 0.593, // all-purpose flour
399
- sugar: 0.845, // granulated sugar
400
- 'brown sugar': 0.721, // packed brown sugar
401
- salt: 1.217, // table salt
402
- butter: 0.911, // unsalted butter
403
- oil: 0.918, // vegetable oil
404
- honey: 1.42, // pure honey
405
- 'maple syrup': 1.37, // pure maple syrup
264
+ // Simplified conversion logic
265
+ const result = args.value; // Add proper conversion logic here
266
+ return {
267
+ content: [
268
+ {
269
+ type: 'text',
270
+ text: `āš–ļø Converted ${args.value} ${args.from} to ${result} ${args.to}${args.ingredient ? ` of ${args.ingredient}` : ''}`,
271
+ },
272
+ ],
273
+ isError: false,
406
274
  };
407
- try {
408
- // Validate units
409
- const fromUnit = from.toLowerCase();
410
- const toUnit = to.toLowerCase();
411
- const ing = ingredient?.toLowerCase();
412
- // Check if units exist
413
- if (!volumeConversions[fromUnit] && !weightConversions[fromUnit]) {
414
- throw new Error(`Invalid source unit: ${from}`);
415
- }
416
- if (!volumeConversions[toUnit] && !weightConversions[toUnit]) {
417
- throw new Error(`Invalid target unit: ${to}`);
418
- }
419
- let result;
420
- // Same type conversion (volume to volume or weight to weight)
421
- if ((volumeConversions[fromUnit] && volumeConversions[toUnit]) ||
422
- (weightConversions[fromUnit] && weightConversions[toUnit])) {
423
- const conversions = volumeConversions[fromUnit] ? volumeConversions : weightConversions;
424
- result = (value * conversions[fromUnit]) / conversions[toUnit];
425
- }
426
- else {
427
- // Volume to weight or weight to volume conversion
428
- if (!ing || !densities[ing]) {
429
- throw new Error(`Ingredient is required for volume-weight conversions. Available ingredients: ${Object.keys(densities).join(', ')}`);
430
- }
431
- // Convert to base units first (ml or g)
432
- let baseValue;
433
- if (volumeConversions[fromUnit]) {
434
- baseValue = value * volumeConversions[fromUnit] * densities[ing];
435
- result = baseValue / weightConversions[toUnit];
436
- }
437
- else {
438
- baseValue = value * weightConversions[fromUnit];
439
- result = baseValue / (volumeConversions[toUnit] * densities[ing]);
440
- }
441
- }
442
- return {
443
- content: [
444
- {
445
- type: 'text',
446
- text: `šŸ”„ Conversion Result:\n• ${value} ${from} ${ingredient ? `of ${ingredient} ` : ''}= ${result.toFixed(2)} ${to}\n\nšŸ“ Note: ${ingredient ? 'Conversion includes ingredient density' : 'Direct unit conversion'}`,
447
- },
448
- ],
449
- isError: false,
450
- };
451
- }
452
- catch (error) {
453
- return {
454
- content: [
455
- {
456
- type: 'text',
457
- text: `āŒ Error: ${error instanceof Error ? error.message : 'Invalid conversion'}`,
458
- },
459
- ],
460
- isError: true,
461
- };
462
- }
463
275
  }
464
- // Tool call handler
465
276
  async function handleToolCall(name, args) {
466
277
  switch (name) {
467
278
  case 'greeting':
@@ -490,72 +301,56 @@ async function handleToolCall(name, args) {
490
301
  };
491
302
  }
492
303
  }
493
- // Server configuration
494
304
  const server = new Server({
495
- name: 'mcp-server/nekzus',
305
+ name: 'nekzus/mcp-server',
496
306
  version: '0.1.0',
497
- description: 'MCP Server implementation for development',
498
307
  }, {
499
308
  capabilities: {
309
+ resources: {},
500
310
  tools: {},
501
311
  },
502
312
  });
503
313
  // Setup request handlers
314
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
315
+ resources: [],
316
+ }));
317
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
318
+ throw new Error(`Resource not found: ${request.params.uri}`);
319
+ });
504
320
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
505
321
  tools: TOOLS,
506
322
  }));
507
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
508
- return handleToolCall(request.params.name, request.params.arguments ?? {});
509
- });
510
- // Server startup with improved error handling
323
+ server.setRequestHandler(CallToolRequestSchema, async (request) => handleToolCall(request.params.name, request.params.arguments ?? {}));
511
324
  async function runServer() {
512
325
  const transport = new StdioServerTransport();
513
- // Handle cleanup gracefully
514
- const cleanup = async () => {
326
+ // Handle direct messages
327
+ process.stdin.on('data', async (data) => {
515
328
  try {
516
- await server.close();
517
- process.exit(0);
518
- }
519
- catch {
520
- process.exit(1);
521
- }
522
- };
523
- try {
524
- // Handle direct messages
525
- process.stdin.on('data', async (data) => {
526
- try {
527
- const message = JSON.parse(data.toString());
528
- if (message.method === 'tools/call') {
529
- const result = await handleToolCall(message.params.name, message.params.arguments ?? {});
530
- process.stdout.write(`${JSON.stringify({
531
- jsonrpc: '2.0',
532
- result,
533
- id: message.id,
534
- })}\n`);
535
- }
329
+ const message = JSON.parse(data.toString());
330
+ if (message.method === 'tools/call') {
331
+ const result = await handleToolCall(message.params.name, message.params.arguments ?? {});
332
+ process.stdout.write(`${JSON.stringify({
333
+ jsonrpc: '2.0',
334
+ result,
335
+ id: message.id,
336
+ })}\n`);
536
337
  }
537
- catch (error) {
538
- if (error instanceof Error) {
539
- process.stdout.write(`${JSON.stringify({
540
- jsonrpc: '2.0',
541
- error: {
542
- code: -32000,
543
- message: error.message,
544
- },
545
- })}\n`);
546
- }
338
+ }
339
+ catch (error) {
340
+ if (error instanceof Error) {
341
+ process.stdout.write(`${JSON.stringify({
342
+ jsonrpc: '2.0',
343
+ error: {
344
+ code: -32000,
345
+ message: error.message,
346
+ },
347
+ })}\n`);
547
348
  }
548
- });
549
- // Connect transport
550
- await server.connect(transport);
551
- // Setup signal handlers
552
- process.stdin.on('close', cleanup);
553
- process.on('SIGINT', cleanup);
554
- process.on('SIGTERM', cleanup);
555
- }
556
- catch {
557
- process.exit(1);
558
- }
349
+ }
350
+ });
351
+ await server.connect(transport);
559
352
  }
560
- // Start the server
561
- runServer();
353
+ runServer().catch(console.error);
354
+ process.stdin.on('close', () => {
355
+ server.close();
356
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nekzus/mcp-server",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "Personal MCP Server implementation providing extensible utility functions and tools for development and testing purposes",
5
5
  "type": "module",
6
6
  "bin": {