@nekzus/mcp-server 1.0.33 ā 1.0.35
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.md +123 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +388 -26
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -79,6 +79,129 @@ Gets the current date and time for a specific timezone.
|
|
|
79
79
|
}
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
### 4. calculator
|
|
83
|
+
|
|
84
|
+
Performs mathematical calculations with support for basic and advanced operations.
|
|
85
|
+
|
|
86
|
+
**Parameters:**
|
|
87
|
+
|
|
88
|
+
- `expression` (string): Mathematical expression (e.g., "2 + 2 * 3")
|
|
89
|
+
- `precision` (number, optional): Decimal places in the result (default: 2)
|
|
90
|
+
|
|
91
|
+
**Example:**
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// Result: 8
|
|
95
|
+
{
|
|
96
|
+
expression: "2 + 2 * 3"
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 5. passwordGen
|
|
101
|
+
|
|
102
|
+
Generates secure passwords with customizable options.
|
|
103
|
+
|
|
104
|
+
**Parameters:**
|
|
105
|
+
|
|
106
|
+
- `length` (number, optional): Password length (default: 16)
|
|
107
|
+
- `includeNumbers` (boolean, optional): Include numbers (default: true)
|
|
108
|
+
- `includeSymbols` (boolean, optional): Include special characters (default: true)
|
|
109
|
+
- `includeUppercase` (boolean, optional): Include uppercase letters (default: true)
|
|
110
|
+
|
|
111
|
+
**Example:**
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Result: 4v7&9G8$
|
|
115
|
+
{
|
|
116
|
+
length: 16,
|
|
117
|
+
includeNumbers: true,
|
|
118
|
+
includeSymbols: true,
|
|
119
|
+
includeUppercase: true
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 6. qrGen
|
|
124
|
+
|
|
125
|
+
Generates QR codes for text or URLs.
|
|
126
|
+
|
|
127
|
+
**Parameters:**
|
|
128
|
+
|
|
129
|
+
- `text` (string): Text or URL to encode
|
|
130
|
+
- `size` (number, optional): Size in pixels (default: 200)
|
|
131
|
+
- `dark` (string, optional): Dark module color (default: "#000000")
|
|
132
|
+
- `light` (string, optional): Light module color (default: "#ffffff")
|
|
133
|
+
|
|
134
|
+
**Example:**
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Result: QR code for "https://example.com"
|
|
138
|
+
{
|
|
139
|
+
text: "https://example.com"
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 7. kitchenConvert
|
|
144
|
+
|
|
145
|
+
Converts between common kitchen measurements and weights, including volume-to-weight conversions based on specific ingredients.
|
|
146
|
+
|
|
147
|
+
**Parameters:**
|
|
148
|
+
|
|
149
|
+
- `value` (number): Value to convert
|
|
150
|
+
- `from` (string): Source unit (e.g., "cup", "tbsp", "g", "oz", "ml")
|
|
151
|
+
- `to` (string): Target unit (e.g., "cup", "tbsp", "g", "oz", "ml")
|
|
152
|
+
- `ingredient` (string, optional): Ingredient for accurate volume-to-weight conversions
|
|
153
|
+
|
|
154
|
+
**Supported Units:**
|
|
155
|
+
|
|
156
|
+
*Volume:*
|
|
157
|
+
- ml (milliliters)
|
|
158
|
+
- l (liters)
|
|
159
|
+
- cup (US cup)
|
|
160
|
+
- tbsp (tablespoon)
|
|
161
|
+
- tsp (teaspoon)
|
|
162
|
+
- floz (fluid ounce)
|
|
163
|
+
|
|
164
|
+
*Weight:*
|
|
165
|
+
- g (grams)
|
|
166
|
+
- kg (kilograms)
|
|
167
|
+
- oz (ounces)
|
|
168
|
+
- lb (pounds)
|
|
169
|
+
|
|
170
|
+
**Supported Ingredients:**
|
|
171
|
+
- water
|
|
172
|
+
- milk
|
|
173
|
+
- flour
|
|
174
|
+
- sugar
|
|
175
|
+
- brown sugar
|
|
176
|
+
- salt
|
|
177
|
+
- butter
|
|
178
|
+
- oil
|
|
179
|
+
- honey
|
|
180
|
+
- maple syrup
|
|
181
|
+
|
|
182
|
+
**Examples:**
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Simple volume conversion
|
|
186
|
+
// Result: š Conversion Result:
|
|
187
|
+
// ⢠1 cup = 236.59 ml
|
|
188
|
+
{
|
|
189
|
+
value: 1,
|
|
190
|
+
from: "cup",
|
|
191
|
+
to: "ml"
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Volume to weight conversion with ingredient
|
|
195
|
+
// Result: š Conversion Result:
|
|
196
|
+
// ⢠1 cup of flour = 140.25 g
|
|
197
|
+
{
|
|
198
|
+
value: 1,
|
|
199
|
+
from: "cup",
|
|
200
|
+
to: "g",
|
|
201
|
+
ingredient: "flour"
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
82
205
|
## š Usage
|
|
83
206
|
|
|
84
207
|
### As MCP Server
|
package/dist/index.d.ts
ADDED
package/dist/index.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
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
|
|
5
9
|
const TOOLS = [
|
|
6
10
|
{
|
|
7
11
|
name: 'greeting',
|
|
@@ -22,13 +26,7 @@ const TOOLS = [
|
|
|
22
26
|
description: 'Draw a random card from a standard 52-card poker deck',
|
|
23
27
|
inputSchema: {
|
|
24
28
|
type: 'object',
|
|
25
|
-
properties: {
|
|
26
|
-
random_string: {
|
|
27
|
-
type: 'string',
|
|
28
|
-
description: 'Dummy parameter for no-parameter tools',
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
required: ['random_string'],
|
|
29
|
+
properties: {},
|
|
32
30
|
},
|
|
33
31
|
},
|
|
34
32
|
{
|
|
@@ -48,29 +46,146 @@ const TOOLS = [
|
|
|
48
46
|
},
|
|
49
47
|
},
|
|
50
48
|
},
|
|
49
|
+
{
|
|
50
|
+
name: 'calculator',
|
|
51
|
+
description: 'Perform mathematical calculations with support for basic and advanced operations',
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
expression: {
|
|
56
|
+
type: 'string',
|
|
57
|
+
description: 'Mathematical expression to evaluate (e.g., "2 + 2 * 3")',
|
|
58
|
+
},
|
|
59
|
+
precision: {
|
|
60
|
+
type: 'number',
|
|
61
|
+
description: 'Number of decimal places for the result (default: 2)',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
required: ['expression'],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'passwordGen',
|
|
69
|
+
description: 'Generate a secure password with customizable options',
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
length: {
|
|
74
|
+
type: 'number',
|
|
75
|
+
description: 'Length of the password (default: 16)',
|
|
76
|
+
},
|
|
77
|
+
includeNumbers: {
|
|
78
|
+
type: 'boolean',
|
|
79
|
+
description: 'Include numbers in the password (default: true)',
|
|
80
|
+
},
|
|
81
|
+
includeSymbols: {
|
|
82
|
+
type: 'boolean',
|
|
83
|
+
description: 'Include special symbols in the password (default: true)',
|
|
84
|
+
},
|
|
85
|
+
includeUppercase: {
|
|
86
|
+
type: 'boolean',
|
|
87
|
+
description: 'Include uppercase letters in the password (default: true)',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'qrGen',
|
|
94
|
+
description: 'Generate a QR code for the given text or URL',
|
|
95
|
+
inputSchema: {
|
|
96
|
+
type: 'object',
|
|
97
|
+
properties: {
|
|
98
|
+
text: {
|
|
99
|
+
type: 'string',
|
|
100
|
+
description: 'Text or URL to encode in the QR code',
|
|
101
|
+
},
|
|
102
|
+
size: {
|
|
103
|
+
type: 'number',
|
|
104
|
+
description: 'Size of the QR code in pixels (default: 200)',
|
|
105
|
+
},
|
|
106
|
+
dark: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
description: 'Color for dark modules (default: "#000000")',
|
|
109
|
+
},
|
|
110
|
+
light: {
|
|
111
|
+
type: 'string',
|
|
112
|
+
description: 'Color for light modules (default: "#ffffff")',
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
required: ['text'],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'kitchenConvert',
|
|
120
|
+
description: 'Convert between common kitchen measurements and weights',
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: {
|
|
124
|
+
value: {
|
|
125
|
+
type: 'number',
|
|
126
|
+
description: 'Value to convert',
|
|
127
|
+
},
|
|
128
|
+
from: {
|
|
129
|
+
type: 'string',
|
|
130
|
+
description: 'Source unit (e.g., "cup", "tbsp", "g", "oz", "ml")',
|
|
131
|
+
},
|
|
132
|
+
to: {
|
|
133
|
+
type: 'string',
|
|
134
|
+
description: 'Target unit (e.g., "cup", "tbsp", "g", "oz", "ml")',
|
|
135
|
+
},
|
|
136
|
+
ingredient: {
|
|
137
|
+
type: 'string',
|
|
138
|
+
description: 'Optional ingredient for accurate volume-to-weight conversions',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
required: ['value', 'from', 'to'],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
51
144
|
];
|
|
145
|
+
// Tool handlers
|
|
52
146
|
async function handleGreeting(args) {
|
|
53
147
|
const { name } = args;
|
|
54
148
|
return {
|
|
55
149
|
content: [
|
|
56
150
|
{
|
|
57
151
|
type: 'text',
|
|
58
|
-
text:
|
|
152
|
+
text: `š Hello ${name}! Welcome to the MCP server!`,
|
|
59
153
|
},
|
|
60
154
|
],
|
|
61
155
|
isError: false,
|
|
62
156
|
};
|
|
63
157
|
}
|
|
64
158
|
async function handleCard() {
|
|
65
|
-
const suits =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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)];
|
|
69
184
|
return {
|
|
70
185
|
content: [
|
|
71
186
|
{
|
|
72
187
|
type: 'text',
|
|
73
|
-
text:
|
|
188
|
+
text: `š“ You drew: ${values[valueSymbol]} of ${suitSymbol} ${suits[suitSymbol]}`,
|
|
74
189
|
},
|
|
75
190
|
],
|
|
76
191
|
isError: false,
|
|
@@ -80,16 +195,21 @@ async function handleDateTime(args) {
|
|
|
80
195
|
const { timeZone = 'UTC', locale = 'en-US' } = args;
|
|
81
196
|
try {
|
|
82
197
|
const date = new Date();
|
|
83
|
-
const
|
|
198
|
+
const dateFormatter = new Intl.DateTimeFormat(locale, {
|
|
199
|
+
timeZone,
|
|
200
|
+
dateStyle: 'long',
|
|
201
|
+
});
|
|
202
|
+
const timeFormatter = new Intl.DateTimeFormat(locale, {
|
|
84
203
|
timeZone,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
204
|
+
timeStyle: 'medium',
|
|
205
|
+
});
|
|
206
|
+
const formattedDate = dateFormatter.format(date);
|
|
207
|
+
const formattedTime = timeFormatter.format(date);
|
|
88
208
|
return {
|
|
89
209
|
content: [
|
|
90
210
|
{
|
|
91
211
|
type: 'text',
|
|
92
|
-
text: formattedDate
|
|
212
|
+
text: `šļø Date: ${formattedDate}\nā° Time: ${formattedTime}\nš Timezone: ${timeZone}`,
|
|
93
213
|
},
|
|
94
214
|
],
|
|
95
215
|
isError: false,
|
|
@@ -107,6 +227,234 @@ async function handleDateTime(args) {
|
|
|
107
227
|
};
|
|
108
228
|
}
|
|
109
229
|
}
|
|
230
|
+
// New tool handlers
|
|
231
|
+
async function handleCalculator(args) {
|
|
232
|
+
const { expression, precision = 2 } = args;
|
|
233
|
+
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
|
|
240
|
+
const calculate = new Function(`return ${sanitizedExpression}`);
|
|
241
|
+
const result = calculate();
|
|
242
|
+
if (typeof result !== 'number' || !Number.isFinite(result)) {
|
|
243
|
+
throw new Error('Invalid mathematical expression');
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
content: [
|
|
247
|
+
{
|
|
248
|
+
type: 'text',
|
|
249
|
+
text: `š§® Expression: ${expression}\nš Result: ${result.toFixed(precision)}`,
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
isError: false,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
return {
|
|
257
|
+
content: [
|
|
258
|
+
{
|
|
259
|
+
type: 'text',
|
|
260
|
+
text: `ā Error: ${error instanceof Error ? error.message : 'Invalid expression'}`,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
isError: true,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
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
|
+
},
|
|
299
|
+
{
|
|
300
|
+
char: symbols.charAt(Math.floor(Math.random() * symbols.length)),
|
|
301
|
+
condition: includeSymbols,
|
|
302
|
+
},
|
|
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
|
+
}
|
|
331
|
+
}
|
|
332
|
+
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
|
+
}
|
|
369
|
+
}
|
|
370
|
+
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
|
|
399
|
+
};
|
|
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
|
+
}
|
|
457
|
+
// Tool call handler
|
|
110
458
|
async function handleToolCall(name, args) {
|
|
111
459
|
switch (name) {
|
|
112
460
|
case 'greeting':
|
|
@@ -115,6 +463,14 @@ async function handleToolCall(name, args) {
|
|
|
115
463
|
return handleCard();
|
|
116
464
|
case 'datetime':
|
|
117
465
|
return handleDateTime(args);
|
|
466
|
+
case 'calculator':
|
|
467
|
+
return handleCalculator(args);
|
|
468
|
+
case 'passwordGen':
|
|
469
|
+
return handlePasswordGen(args);
|
|
470
|
+
case 'qrGen':
|
|
471
|
+
return handleQRGen(args);
|
|
472
|
+
case 'kitchenConvert':
|
|
473
|
+
return handleKitchenConvert(args);
|
|
118
474
|
default:
|
|
119
475
|
return {
|
|
120
476
|
content: [
|
|
@@ -127,8 +483,9 @@ async function handleToolCall(name, args) {
|
|
|
127
483
|
};
|
|
128
484
|
}
|
|
129
485
|
}
|
|
486
|
+
// Server configuration
|
|
130
487
|
const server = new Server({
|
|
131
|
-
name: '@nekzus/server
|
|
488
|
+
name: '@nekzus/mcp-server',
|
|
132
489
|
version: '0.1.0',
|
|
133
490
|
description: 'MCP Server implementation for development',
|
|
134
491
|
}, {
|
|
@@ -136,35 +493,40 @@ const server = new Server({
|
|
|
136
493
|
tools: {},
|
|
137
494
|
},
|
|
138
495
|
});
|
|
496
|
+
// Setup request handlers
|
|
139
497
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
140
498
|
tools: TOOLS,
|
|
141
499
|
}));
|
|
142
500
|
server.setRequestHandler(CallToolRequestSchema, async (request) => handleToolCall(request.params.name, request.params.arguments ?? {}));
|
|
501
|
+
// Server startup
|
|
143
502
|
async function runServer() {
|
|
144
503
|
try {
|
|
145
504
|
const transport = new StdioServerTransport();
|
|
146
505
|
await server.connect(transport);
|
|
147
|
-
|
|
148
|
-
|
|
506
|
+
log('[Server] MCP Server is running');
|
|
507
|
+
log('[Server] Available tools:', TOOLS.map((t) => t.name).join(', '));
|
|
508
|
+
// Handle stdin close
|
|
149
509
|
process.stdin.on('close', () => {
|
|
150
|
-
|
|
510
|
+
log('[Server] Input stream closed');
|
|
151
511
|
cleanup();
|
|
152
512
|
});
|
|
153
513
|
}
|
|
154
514
|
catch (error) {
|
|
155
|
-
|
|
515
|
+
log('[Server] Failed to start MCP Server:', error);
|
|
156
516
|
process.exit(1);
|
|
157
517
|
}
|
|
158
518
|
}
|
|
519
|
+
// Cleanup function
|
|
159
520
|
async function cleanup() {
|
|
160
521
|
try {
|
|
161
522
|
await server.close();
|
|
162
|
-
|
|
523
|
+
log('[Server] MCP Server stopped gracefully');
|
|
163
524
|
process.exit(0);
|
|
164
525
|
}
|
|
165
526
|
catch (error) {
|
|
166
|
-
|
|
527
|
+
log('[Server] Error during cleanup:', error);
|
|
167
528
|
process.exit(1);
|
|
168
529
|
}
|
|
169
530
|
}
|
|
170
|
-
|
|
531
|
+
// Start the server
|
|
532
|
+
runServer().catch((error) => log('[Server] Unhandled error:', error));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nekzus/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.35",
|
|
4
4
|
"description": "Personal MCP Server implementation providing extensible utility functions and tools for development and testing purposes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
|
+
"dist/index.js",
|
|
12
13
|
"README.md",
|
|
13
14
|
"LICENSE"
|
|
14
15
|
],
|