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