@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.
- package/dist/index.js +105 -310
- 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
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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: `š“
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
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
|
-
|
|
250
|
-
|
|
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:
|
|
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:
|
|
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
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
308
|
-
|
|
245
|
+
type: 'text',
|
|
246
|
+
text: `š Generated password: ${password}`,
|
|
309
247
|
},
|
|
310
|
-
]
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
|
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
|
|
514
|
-
|
|
326
|
+
// Handle direct messages
|
|
327
|
+
process.stdin.on('data', async (data) => {
|
|
515
328
|
try {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
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
|
-
|
|
550
|
-
|
|
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
|
-
|
|
561
|
-
|
|
353
|
+
runServer().catch(console.error);
|
|
354
|
+
process.stdin.on('close', () => {
|
|
355
|
+
server.close();
|
|
356
|
+
});
|
package/package.json
CHANGED