@nekzus/mcp-server 1.1.8 → 1.3.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.
package/dist/index.js CHANGED
@@ -1,8 +1,96 @@
1
1
  #!/usr/bin/env node
2
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
- import 'dotenv/config';
2
+ import fetch from 'node-fetch';
3
+ import { z } from 'zod';
4
+ // Zod schemas for npm package data
5
+ const NpmPackageVersionSchema = z
6
+ .object({
7
+ name: z.string(),
8
+ version: z.string(),
9
+ description: z.string().optional(),
10
+ author: z.union([z.string(), z.object({}).passthrough()]).optional(),
11
+ license: z.string().optional(),
12
+ repository: z
13
+ .object({
14
+ type: z.string().optional(),
15
+ url: z.string().optional(),
16
+ })
17
+ .passthrough()
18
+ .optional(),
19
+ bugs: z
20
+ .object({
21
+ url: z.string().optional(),
22
+ })
23
+ .passthrough()
24
+ .optional(),
25
+ homepage: z.string().optional(),
26
+ })
27
+ .passthrough();
28
+ const NpmPackageInfoSchema = z
29
+ .object({
30
+ name: z.string(),
31
+ 'dist-tags': z.record(z.string()),
32
+ versions: z.record(NpmPackageVersionSchema),
33
+ time: z.record(z.string()).optional(),
34
+ repository: z
35
+ .object({
36
+ type: z.string().optional(),
37
+ url: z.string().optional(),
38
+ })
39
+ .passthrough()
40
+ .optional(),
41
+ bugs: z
42
+ .object({
43
+ url: z.string().optional(),
44
+ })
45
+ .passthrough()
46
+ .optional(),
47
+ homepage: z.string().optional(),
48
+ })
49
+ .passthrough();
50
+ const NpmPackageDataSchema = z.object({
51
+ name: z.string(),
52
+ version: z.string(),
53
+ description: z.string().optional(),
54
+ license: z.string().optional(),
55
+ dependencies: z.record(z.string()).optional(),
56
+ devDependencies: z.record(z.string()).optional(),
57
+ peerDependencies: z.record(z.string()).optional(),
58
+ types: z.string().optional(),
59
+ typings: z.string().optional(),
60
+ });
61
+ const BundlephobiaDataSchema = z.object({
62
+ size: z.number(),
63
+ gzip: z.number(),
64
+ dependencyCount: z.number(),
65
+ });
66
+ const NpmDownloadsDataSchema = z.object({
67
+ downloads: z.number(),
68
+ start: z.string(),
69
+ end: z.string(),
70
+ package: z.string(),
71
+ });
72
+ // Schemas for NPM quality, maintenance and popularity metrics
73
+ const NpmQualitySchema = z.object({
74
+ score: z.number(),
75
+ tests: z.number(),
76
+ coverage: z.number(),
77
+ linting: z.number(),
78
+ types: z.number(),
79
+ });
80
+ const NpmMaintenanceSchema = z.object({
81
+ score: z.number(),
82
+ issuesResolutionTime: z.number(),
83
+ commitsFrequency: z.number(),
84
+ releaseFrequency: z.number(),
85
+ lastUpdate: z.string(),
86
+ });
87
+ const NpmPopularitySchema = z.object({
88
+ score: z.number(),
89
+ stars: z.number(),
90
+ downloads: z.number(),
91
+ dependents: z.number(),
92
+ communityInterest: z.number(),
93
+ });
6
94
  // Logger function that uses stderr - only for critical errors
7
95
  const log = (...args) => {
8
96
  // Filter out server status messages
@@ -14,200 +102,292 @@ const log = (...args) => {
14
102
  };
15
103
  // Define tools
16
104
  const TOOLS = [
105
+ // NPM Package Analysis Tools
17
106
  {
18
- name: 'greeting',
19
- description: 'Generate a personalized greeting message for the specified person',
107
+ name: 'npmVersions',
108
+ description: 'Get all available versions of an NPM package',
109
+ parameters: z.object({
110
+ packageName: z.string().describe('The name of the package'),
111
+ }),
20
112
  inputSchema: {
21
113
  type: 'object',
22
114
  properties: {
23
- name: {
24
- type: 'string',
25
- description: 'Name of the recipient for the greeting',
26
- },
115
+ packageName: { type: 'string' },
27
116
  },
28
- required: ['name'],
117
+ required: ['packageName'],
29
118
  },
30
119
  },
31
120
  {
32
- name: 'card',
33
- description: 'Draw a random card from a standard 52-card poker deck',
121
+ name: 'npmLatest',
122
+ description: 'Get the latest version and changelog of an NPM package',
123
+ parameters: z.object({
124
+ packageName: z.string().describe('The name of the package'),
125
+ }),
34
126
  inputSchema: {
35
127
  type: 'object',
36
- properties: {},
128
+ properties: {
129
+ packageName: { type: 'string' },
130
+ },
131
+ required: ['packageName'],
37
132
  },
38
133
  },
39
134
  {
40
- name: 'datetime',
41
- description: 'Get the current date and time for a specific timezone',
135
+ name: 'npmDeps',
136
+ description: 'Analyze dependencies and devDependencies of an NPM package',
137
+ parameters: z.object({
138
+ packageName: z.string().describe('The name of the package'),
139
+ }),
42
140
  inputSchema: {
43
141
  type: 'object',
44
142
  properties: {
45
- timeZone: {
46
- type: 'string',
47
- description: 'Timezone identifier (e.g., "America/New_York")',
48
- },
49
- locale: {
50
- type: 'string',
51
- description: 'Locale identifier (e.g., "en-US")',
52
- },
143
+ packageName: { type: 'string' },
53
144
  },
145
+ required: ['packageName'],
54
146
  },
55
147
  },
56
148
  {
57
- name: 'calculator',
58
- description: 'Perform mathematical calculations with support for basic and advanced operations',
149
+ name: 'npmTypes',
150
+ description: 'Check TypeScript types availability and version for a package',
151
+ parameters: z.object({
152
+ packageName: z.string().describe('The name of the package'),
153
+ }),
59
154
  inputSchema: {
60
155
  type: 'object',
61
156
  properties: {
62
- expression: {
63
- type: 'string',
64
- description: 'Mathematical expression to evaluate (e.g., "2 + 2 * 3")',
65
- },
66
- precision: {
67
- type: 'number',
68
- description: 'Number of decimal places for the result (default: 2)',
69
- },
157
+ packageName: { type: 'string' },
70
158
  },
71
- required: ['expression'],
159
+ required: ['packageName'],
72
160
  },
73
161
  },
74
162
  {
75
- name: 'passwordGen',
76
- description: 'Generate a secure password with customizable options',
163
+ name: 'npmSize',
164
+ description: 'Get package size information including dependencies and bundle size',
165
+ parameters: z.object({
166
+ packageName: z.string().describe('The name of the package'),
167
+ }),
77
168
  inputSchema: {
78
169
  type: 'object',
79
170
  properties: {
80
- length: {
81
- type: 'number',
82
- description: 'Length of the password (default: 16)',
83
- },
84
- includeNumbers: {
85
- type: 'boolean',
86
- description: 'Include numbers in the password (default: true)',
87
- },
88
- includeSymbols: {
89
- type: 'boolean',
90
- description: 'Include special symbols in the password (default: true)',
91
- },
92
- includeUppercase: {
93
- type: 'boolean',
94
- description: 'Include uppercase letters in the password (default: true)',
95
- },
171
+ packageName: { type: 'string' },
96
172
  },
173
+ required: ['packageName'],
97
174
  },
98
175
  },
99
176
  {
100
- name: 'qrGen',
101
- description: 'Generate a QR code for the given text or URL',
177
+ name: 'npmVulnerabilities',
178
+ description: 'Check for known vulnerabilities in a package',
179
+ parameters: z.object({
180
+ packageName: z.string().describe('The name of the package'),
181
+ }),
102
182
  inputSchema: {
103
183
  type: 'object',
104
184
  properties: {
105
- text: {
106
- type: 'string',
107
- description: 'Text or URL to encode in the QR code',
108
- },
109
- size: {
110
- type: 'number',
111
- description: 'Size of the QR code in pixels (default: 200)',
112
- },
113
- dark: {
114
- type: 'string',
115
- description: 'Color for dark modules (default: "#000000")',
116
- },
117
- light: {
118
- type: 'string',
119
- description: 'Color for light modules (default: "#ffffff")',
120
- },
185
+ packageName: { type: 'string' },
121
186
  },
122
- required: ['text'],
187
+ required: ['packageName'],
123
188
  },
124
189
  },
125
190
  {
126
- name: 'kitchenConvert',
127
- description: 'Convert between common kitchen measurements and weights',
191
+ name: 'npmTrends',
192
+ description: 'Get download trends and popularity metrics for a package',
193
+ parameters: z.object({
194
+ packageName: z.string().describe('The name of the package'),
195
+ period: z.enum(['last-week', 'last-month', 'last-year']).describe('Time period for trends'),
196
+ }),
128
197
  inputSchema: {
129
198
  type: 'object',
130
199
  properties: {
131
- value: {
132
- type: 'number',
133
- description: 'Value to convert',
134
- },
135
- from: {
136
- type: 'string',
137
- description: 'Source unit (e.g., "cup", "tbsp", "g", "oz", "ml")',
138
- },
139
- to: {
200
+ packageName: { type: 'string' },
201
+ period: {
140
202
  type: 'string',
141
- description: 'Target unit (e.g., "cup", "tbsp", "g", "oz", "ml")',
203
+ enum: ['last-week', 'last-month', 'last-year'],
142
204
  },
143
- ingredient: {
144
- type: 'string',
145
- description: 'Optional ingredient for accurate volume-to-weight conversions',
205
+ },
206
+ required: ['packageName', 'period'],
207
+ },
208
+ },
209
+ {
210
+ name: 'npmCompare',
211
+ description: 'Compare multiple NPM packages based on various metrics',
212
+ parameters: z.object({
213
+ packages: z.array(z.string()).describe('List of package names to compare'),
214
+ }),
215
+ inputSchema: {
216
+ type: 'object',
217
+ properties: {
218
+ packages: {
219
+ type: 'array',
220
+ items: { type: 'string' },
146
221
  },
147
222
  },
148
- required: ['value', 'from', 'to'],
223
+ required: ['packages'],
149
224
  },
150
225
  },
151
226
  ];
152
- // Tool handlers
153
- async function handleGreeting(args) {
154
- return {
155
- content: [
156
- {
157
- type: 'text',
158
- text: `👋 Hello ${args.name}! Welcome to the MCP server!`,
159
- },
160
- ],
161
- isError: false,
162
- };
227
+ // Type guards for API responses
228
+ function isNpmPackageInfo(data) {
229
+ try {
230
+ return NpmPackageInfoSchema.parse(data) !== null;
231
+ }
232
+ catch {
233
+ return false;
234
+ }
163
235
  }
164
- async function handleCard() {
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)];
169
- return {
170
- content: [
171
- {
172
- type: 'text',
173
- text: `🎴 Drew card: ${value}${suit}`,
174
- },
175
- ],
176
- isError: false,
177
- };
236
+ function isNpmPackageData(data) {
237
+ try {
238
+ return NpmPackageDataSchema.parse(data) !== null;
239
+ }
240
+ catch {
241
+ return false;
242
+ }
178
243
  }
179
- async function handleDateTime(args) {
180
- const { timeZone = 'UTC', locale = 'en-US' } = args;
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
- };
244
+ function isBundlephobiaData(data) {
245
+ try {
246
+ return BundlephobiaDataSchema.parse(data) !== null;
247
+ }
248
+ catch {
249
+ return false;
250
+ }
251
+ }
252
+ function isNpmDownloadsData(data) {
253
+ try {
254
+ return NpmDownloadsDataSchema.parse(data) !== null;
255
+ }
256
+ catch {
257
+ return false;
258
+ }
259
+ }
260
+ async function handleNpmVersions(args) {
261
+ try {
262
+ const response = await fetch(`https://registry.npmjs.org/${args.packageName}`);
263
+ if (!response.ok) {
264
+ throw new Error(`Failed to fetch package info: ${response.statusText}`);
265
+ }
266
+ const rawData = await response.json();
267
+ if (!isNpmPackageInfo(rawData)) {
268
+ throw new Error('Invalid package info data received');
269
+ }
270
+ const versions = Object.keys(rawData.versions ?? {}).sort((a, b) => {
271
+ const [aMajor = 0, aMinor = 0, aPatch = 0] = a.split('.').map(Number);
272
+ const [bMajor = 0, bMinor = 0, bPatch = 0] = b.split('.').map(Number);
273
+ if (aMajor !== bMajor)
274
+ return aMajor - bMajor;
275
+ if (aMinor !== bMinor)
276
+ return aMinor - bMinor;
277
+ return aPatch - bPatch;
278
+ });
279
+ return {
280
+ content: [
281
+ {
282
+ type: 'text',
283
+ text: `📦 Available versions for ${args.packageName}:\n${versions.join('\n')}`,
284
+ },
285
+ ],
286
+ isError: false,
287
+ };
288
+ }
289
+ catch (error) {
290
+ return {
291
+ content: [
292
+ {
293
+ type: 'text',
294
+ text: `Error fetching package versions: ${error instanceof Error ? error.message : 'Unknown error'}`,
295
+ },
296
+ ],
297
+ isError: true,
298
+ };
299
+ }
196
300
  }
197
- async function handleCalculator(args) {
301
+ async function handleNpmLatest(args) {
198
302
  try {
199
- const sanitizedExpression = args.expression.replace(/[^0-9+\-*/().%\s]/g, '');
200
- const calculate = new Function(`return ${sanitizedExpression}`);
201
- const result = calculate();
202
- const precision = args.precision ?? 2;
203
- const formattedResult = Number.isInteger(result) ? result : Number(result.toFixed(precision));
303
+ const response = await fetch(`https://registry.npmjs.org/${args.packageName}`);
304
+ if (!response.ok) {
305
+ throw new Error(`Failed to fetch package info: ${response.statusText}`);
306
+ }
307
+ const rawData = await response.json();
308
+ if (!isNpmPackageInfo(rawData)) {
309
+ throw new Error('Invalid package info data received');
310
+ }
311
+ const latestVersion = rawData['dist-tags']?.latest;
312
+ if (!latestVersion || !rawData.versions) {
313
+ throw new Error('No latest version or versions data found');
314
+ }
315
+ const latestVersionInfo = rawData.versions[latestVersion];
316
+ const description = latestVersionInfo.description ?? '';
317
+ const repository = latestVersionInfo.repository ?? rawData.repository;
318
+ const homepage = latestVersionInfo.homepage ?? rawData.homepage;
319
+ const bugs = latestVersionInfo.bugs ?? rawData.bugs;
320
+ const text = [
321
+ `📦 Latest version of ${args.packageName}: ${latestVersion}`,
322
+ '',
323
+ description && `Description:\n${description}`,
324
+ '',
325
+ 'Links:',
326
+ homepage && `• Homepage: ${homepage}`,
327
+ repository?.url && `• Repository: ${repository.url.replace('git+', '').replace('.git', '')}`,
328
+ bugs?.url && `• Issues: ${bugs.url}`,
329
+ '',
330
+ repository?.url?.includes('github.com') &&
331
+ `You can check for updates at:\n${repository.url
332
+ .replace('git+', '')
333
+ .replace('git:', 'https:')
334
+ .replace('.git', '')}/releases`,
335
+ ]
336
+ .filter(Boolean)
337
+ .join('\n');
338
+ return {
339
+ content: [{ type: 'text', text }],
340
+ isError: false,
341
+ };
342
+ }
343
+ catch (error) {
204
344
  return {
205
345
  content: [
206
346
  {
207
347
  type: 'text',
208
- text: `🔢 Result: ${formattedResult}`,
348
+ text: `Error fetching package information: ${error instanceof Error ? error.message : 'Unknown error'}`,
209
349
  },
210
350
  ],
351
+ isError: true,
352
+ };
353
+ }
354
+ }
355
+ async function handleNpmDeps(args) {
356
+ try {
357
+ const response = await fetch(`https://registry.npmjs.org/${args.packageName}/latest`);
358
+ if (!response.ok) {
359
+ throw new Error(`Failed to fetch package info: ${response.statusText}`);
360
+ }
361
+ const rawData = await response.json();
362
+ if (!isNpmPackageData(rawData)) {
363
+ throw new Error('Invalid package data received');
364
+ }
365
+ const dependencies = rawData.dependencies ?? {};
366
+ const devDependencies = rawData.devDependencies ?? {};
367
+ const peerDependencies = rawData.peerDependencies ?? {};
368
+ const text = [
369
+ `📦 Dependencies for ${args.packageName}@${rawData.version}`,
370
+ '',
371
+ Object.keys(dependencies).length > 0 && [
372
+ 'Dependencies:',
373
+ ...Object.entries(dependencies).map(([dep, version]) => `• ${dep}: ${version}`),
374
+ '',
375
+ ],
376
+ Object.keys(devDependencies).length > 0 && [
377
+ 'Dev Dependencies:',
378
+ ...Object.entries(devDependencies).map(([dep, version]) => `• ${dep}: ${version}`),
379
+ '',
380
+ ],
381
+ Object.keys(peerDependencies).length > 0 && [
382
+ 'Peer Dependencies:',
383
+ ...Object.entries(peerDependencies).map(([dep, version]) => `• ${dep}: ${version}`),
384
+ ],
385
+ ]
386
+ .filter(Boolean)
387
+ .flat()
388
+ .join('\n');
389
+ return {
390
+ content: [{ type: 'text', text }],
211
391
  isError: false,
212
392
  };
213
393
  }
@@ -216,141 +396,340 @@ async function handleCalculator(args) {
216
396
  content: [
217
397
  {
218
398
  type: 'text',
219
- text: `Error calculating result: ${error.message}`,
399
+ text: `Error fetching dependencies: ${error instanceof Error ? error.message : 'Unknown error'}`,
220
400
  },
221
401
  ],
222
402
  isError: true,
223
403
  };
224
404
  }
225
405
  }
226
- async function handlePasswordGen(args) {
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));
406
+ async function handleNpmTypes(args) {
407
+ try {
408
+ const response = await fetch(`https://registry.npmjs.org/${args.packageName}/latest`);
409
+ if (!response.ok) {
410
+ throw new Error(`Failed to fetch package info: ${response.statusText}`);
411
+ }
412
+ const data = (await response.json());
413
+ let text = `📦 TypeScript support for ${args.packageName}@${data.version}\n\n`;
414
+ const hasTypes = Boolean(data.types || data.typings);
415
+ if (hasTypes) {
416
+ text += `✅ Package includes built-in TypeScript types\nTypes path: ${data.types || data.typings}\n\n`;
417
+ }
418
+ const typesPackage = `@types/${args.packageName.replace('@', '').replace('/', '__')}`;
419
+ const typesResponse = await fetch(`https://registry.npmjs.org/${typesPackage}/latest`).catch(() => null);
420
+ if (typesResponse?.ok) {
421
+ const typesData = (await typesResponse.json());
422
+ text += `📦 DefinitelyTyped package available: ${typesPackage}@${typesData.version}\n`;
423
+ text += `Install with: npm install -D ${typesPackage}\n`;
424
+ }
425
+ else if (!hasTypes) {
426
+ text += '❌ No TypeScript type definitions found\n';
427
+ }
428
+ return {
429
+ content: [{ type: 'text', text }],
430
+ isError: false,
431
+ };
432
+ }
433
+ catch (error) {
434
+ return {
435
+ content: [
436
+ { type: 'text', text: `Error checking TypeScript types: ${error.message}` },
437
+ ],
438
+ isError: true,
439
+ };
241
440
  }
242
- return {
243
- content: [
244
- {
245
- type: 'text',
246
- text: `🔐 Generated password: ${password}`,
247
- },
248
- ],
249
- isError: false,
250
- };
251
441
  }
252
- async function handleQRGen(args) {
253
- return {
254
- content: [
255
- {
256
- type: 'text',
257
- text: 'QR code generation is not implemented yet',
258
- },
259
- ],
260
- isError: true,
261
- };
442
+ async function handleNpmSize(args) {
443
+ try {
444
+ const response = await fetch(`https://bundlephobia.com/api/size?package=${args.packageName}`);
445
+ if (!response.ok) {
446
+ throw new Error(`Failed to fetch package size: ${response.statusText}`);
447
+ }
448
+ const rawData = await response.json();
449
+ if (!isBundlephobiaData(rawData)) {
450
+ throw new Error('Invalid response from bundlephobia');
451
+ }
452
+ const sizeInKb = Number((rawData.size / 1024).toFixed(2));
453
+ const gzipInKb = Number((rawData.gzip / 1024).toFixed(2));
454
+ return {
455
+ content: [
456
+ {
457
+ type: 'text',
458
+ text: `Package size: ${sizeInKb}KB (gzipped: ${gzipInKb}KB)`,
459
+ },
460
+ {
461
+ type: 'text',
462
+ text: `Dependencies: ${rawData.dependencyCount}`,
463
+ },
464
+ ],
465
+ isError: false,
466
+ };
467
+ }
468
+ catch (error) {
469
+ return {
470
+ content: [
471
+ {
472
+ type: 'text',
473
+ text: `Error fetching package size: ${error instanceof Error ? error.message : String(error)}`,
474
+ },
475
+ ],
476
+ isError: true,
477
+ };
478
+ }
262
479
  }
263
- async function handleKitchenConvert(args) {
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}` : ''}`,
480
+ async function handleNpmVulnerabilities(args) {
481
+ try {
482
+ const response = await fetch('https://api.osv.dev/v1/query', {
483
+ method: 'POST',
484
+ headers: {
485
+ 'Content-Type': 'application/json',
271
486
  },
272
- ],
273
- isError: false,
274
- };
487
+ body: JSON.stringify({
488
+ package: {
489
+ name: args.packageName,
490
+ ecosystem: 'npm',
491
+ },
492
+ }),
493
+ });
494
+ if (!response.ok) {
495
+ throw new Error(`Failed to fetch vulnerability info: ${response.statusText}`);
496
+ }
497
+ const data = (await response.json());
498
+ const vulns = data.vulns || [];
499
+ let text = `🔒 Security info for ${args.packageName}\n\n`;
500
+ if (vulns.length === 0) {
501
+ text += '✅ No known vulnerabilities\n';
502
+ }
503
+ else {
504
+ text += `⚠️ Found ${vulns.length} vulnerabilities:\n\n`;
505
+ for (const vuln of vulns) {
506
+ text += `- ${vuln.summary}\n`;
507
+ const severity = typeof vuln.severity === 'object'
508
+ ? vuln.severity.type || 'Unknown'
509
+ : vuln.severity || 'Unknown';
510
+ text += ` Severity: ${severity}\n`;
511
+ if (vuln.references && vuln.references.length > 0) {
512
+ text += ` More info: ${vuln.references[0].url}\n`;
513
+ }
514
+ text += '\n';
515
+ }
516
+ }
517
+ return {
518
+ content: [{ type: 'text', text }],
519
+ isError: false,
520
+ };
521
+ }
522
+ catch (error) {
523
+ return {
524
+ content: [
525
+ { type: 'text', text: `Error checking vulnerabilities: ${error.message}` },
526
+ ],
527
+ isError: true,
528
+ };
529
+ }
275
530
  }
276
- async function handleToolCall(name, args) {
277
- switch (name) {
278
- case 'greeting':
279
- return handleGreeting(args);
280
- case 'card':
281
- return handleCard();
282
- case 'datetime':
283
- return handleDateTime(args);
284
- case 'calculator':
285
- return handleCalculator(args);
286
- case 'passwordGen':
287
- return handlePasswordGen(args);
288
- case 'qrGen':
289
- return handleQRGen(args);
290
- case 'kitchenConvert':
291
- return handleKitchenConvert(args);
292
- default:
531
+ async function handleNpmTrends(args) {
532
+ try {
533
+ const period = args.period || 'last-month';
534
+ const response = await fetch(`https://api.npmjs.org/downloads/point/${period}/${args.packageName}`);
535
+ if (!response.ok) {
536
+ throw new Error(`Failed to fetch download trends: ${response.statusText}`);
537
+ }
538
+ const data = await response.json();
539
+ if (!isNpmDownloadsData(data)) {
540
+ throw new Error('Invalid response format from npm downloads API');
541
+ }
542
+ let text = `📈 Download trends for ${args.packageName}\n\n`;
543
+ text += `Period: ${period}\n`;
544
+ text += `Total downloads: ${data.downloads.toLocaleString()}\n`;
545
+ text += `Average daily downloads: ${Math.round(data.downloads / (period === 'last-week' ? 7 : period === 'last-month' ? 30 : 365)).toLocaleString()}\n`;
546
+ return {
547
+ content: [{ type: 'text', text }],
548
+ isError: false,
549
+ };
550
+ }
551
+ catch (error) {
552
+ return {
553
+ content: [
554
+ { type: 'text', text: `Error fetching download trends: ${error.message}` },
555
+ ],
556
+ isError: true,
557
+ };
558
+ }
559
+ }
560
+ async function handleNpmCompare(args) {
561
+ try {
562
+ const results = await Promise.all(args.packages.map(async (pkg) => {
563
+ const [infoRes, downloadsRes] = await Promise.all([
564
+ fetch(`https://registry.npmjs.org/${pkg}/latest`),
565
+ fetch(`https://api.npmjs.org/downloads/point/last-month/${pkg}`),
566
+ ]);
567
+ if (!infoRes.ok || !downloadsRes.ok) {
568
+ throw new Error(`Failed to fetch data for ${pkg}`);
569
+ }
570
+ const info = await infoRes.json();
571
+ const downloads = await downloadsRes.json();
572
+ if (!isNpmPackageData(info) || !isNpmDownloadsData(downloads)) {
573
+ throw new Error(`Invalid response format for ${pkg}`);
574
+ }
293
575
  return {
294
- content: [
295
- {
296
- type: 'text',
297
- text: `Unknown tool: ${name}`,
298
- },
299
- ],
300
- isError: true,
576
+ name: pkg,
577
+ version: info.version,
578
+ description: info.description,
579
+ downloads: downloads.downloads,
580
+ license: info.license,
581
+ dependencies: Object.keys(info.dependencies || {}).length,
301
582
  };
583
+ }));
584
+ let text = '📊 Package Comparison\n\n';
585
+ // Table header
586
+ text += 'Package | Version | Monthly Downloads | Dependencies | License\n';
587
+ text += '--------|---------|------------------|--------------|--------\n';
588
+ // Table rows
589
+ for (const pkg of results) {
590
+ text += `${pkg.name} | ${pkg.version} | ${pkg.downloads.toLocaleString()} | ${pkg.dependencies} | ${pkg.license || 'N/A'}\n`;
591
+ }
592
+ return {
593
+ content: [{ type: 'text', text }],
594
+ isError: false,
595
+ };
596
+ }
597
+ catch (error) {
598
+ return {
599
+ content: [{ type: 'text', text: `Error comparing packages: ${error.message}` }],
600
+ isError: true,
601
+ };
302
602
  }
303
603
  }
304
- const server = new Server({
305
- name: 'nekzus/mcp-server',
306
- version: '0.1.0',
307
- }, {
308
- capabilities: {
309
- resources: {},
310
- tools: {},
311
- },
312
- });
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
- });
320
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
321
- tools: TOOLS,
322
- }));
323
- server.setRequestHandler(CallToolRequestSchema, async (request) => handleToolCall(request.params.name, request.params.arguments ?? {}));
324
- async function runServer() {
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`);
337
- }
604
+ // Function to get package quality metrics
605
+ async function handleNpmQuality(args) {
606
+ try {
607
+ const response = await fetch(`https://api.npms.io/v2/package/${encodeURIComponent(args.packageName)}`);
608
+ if (!response.ok) {
609
+ throw new Error(`Failed to fetch quality data: ${response.statusText}`);
338
610
  }
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`);
348
- }
611
+ const data = (await response.json());
612
+ const quality = data.score.quality;
613
+ const result = NpmQualitySchema.parse({
614
+ score: Math.round(quality.score * 100) / 100,
615
+ tests: Math.round(quality.tests * 100) / 100,
616
+ coverage: Math.round(quality.coverage * 100) / 100,
617
+ linting: Math.round(quality.linting * 100) / 100,
618
+ types: Math.round(quality.types * 100) / 100,
619
+ });
620
+ return {
621
+ content: [
622
+ {
623
+ type: 'text',
624
+ text: `Quality metrics for ${args.packageName}:
625
+ - Overall Score: ${result.score}
626
+ - Tests: ${result.tests}
627
+ - Coverage: ${result.coverage}
628
+ - Linting: ${result.linting}
629
+ - Types: ${result.types}`,
630
+ },
631
+ ],
632
+ isError: false,
633
+ };
634
+ }
635
+ catch (error) {
636
+ return {
637
+ content: [
638
+ {
639
+ type: 'text',
640
+ text: `Error fetching quality metrics: ${error instanceof Error ? error.message : 'Unknown error'}`,
641
+ },
642
+ ],
643
+ isError: true,
644
+ };
645
+ }
646
+ }
647
+ // Function to get package maintenance metrics
648
+ async function handleNpmMaintenance(args) {
649
+ try {
650
+ const response = await fetch(`https://api.npms.io/v2/package/${encodeURIComponent(args.packageName)}`);
651
+ if (!response.ok) {
652
+ throw new Error(`Failed to fetch maintenance data: ${response.statusText}`);
349
653
  }
350
- });
351
- await server.connect(transport);
654
+ const data = (await response.json());
655
+ const maintenance = data.score.maintenance;
656
+ const result = NpmMaintenanceSchema.parse({
657
+ score: Math.round(maintenance.score * 100) / 100,
658
+ issuesResolutionTime: Math.round(maintenance.issuesResolutionTime * 100) / 100,
659
+ commitsFrequency: Math.round(maintenance.commitsFrequency * 100) / 100,
660
+ releaseFrequency: Math.round(maintenance.releaseFrequency * 100) / 100,
661
+ lastUpdate: new Date(maintenance.lastUpdate).toISOString(),
662
+ });
663
+ return {
664
+ content: [
665
+ {
666
+ type: 'text',
667
+ text: `Maintenance metrics for ${args.packageName}:
668
+ - Overall Score: ${result.score}
669
+ - Issues Resolution Time: ${result.issuesResolutionTime}
670
+ - Commits Frequency: ${result.commitsFrequency}
671
+ - Release Frequency: ${result.releaseFrequency}
672
+ - Last Update: ${new Date(result.lastUpdate).toLocaleDateString()}`,
673
+ },
674
+ ],
675
+ isError: false,
676
+ };
677
+ }
678
+ catch (error) {
679
+ return {
680
+ content: [
681
+ {
682
+ type: 'text',
683
+ text: `Error fetching maintenance metrics: ${error instanceof Error ? error.message : 'Unknown error'}`,
684
+ },
685
+ ],
686
+ isError: true,
687
+ };
688
+ }
352
689
  }
353
- runServer().catch(console.error);
354
- process.stdin.on('close', () => {
355
- server.close();
356
- });
690
+ // Function to get package popularity metrics
691
+ async function handleNpmPopularity(args) {
692
+ try {
693
+ const response = await fetch(`https://api.npms.io/v2/package/${encodeURIComponent(args.packageName)}`);
694
+ if (!response.ok) {
695
+ throw new Error(`Failed to fetch popularity data: ${response.statusText}`);
696
+ }
697
+ const data = (await response.json());
698
+ const popularity = data.score.popularity;
699
+ const result = NpmPopularitySchema.parse({
700
+ score: Math.round(popularity.score * 100) / 100,
701
+ stars: Math.round(popularity.stars),
702
+ downloads: Math.round(popularity.downloads),
703
+ dependents: Math.round(popularity.dependents),
704
+ communityInterest: Math.round(popularity.communityInterest * 100) / 100,
705
+ });
706
+ return {
707
+ content: [
708
+ {
709
+ type: 'text',
710
+ text: `Popularity metrics for ${args.packageName}:
711
+ - Overall Score: ${result.score}
712
+ - GitHub Stars: ${result.stars}
713
+ - Downloads: ${result.downloads}
714
+ - Dependent Packages: ${result.dependents}
715
+ - Community Interest: ${result.communityInterest}`,
716
+ },
717
+ ],
718
+ isError: false,
719
+ };
720
+ }
721
+ catch (error) {
722
+ return {
723
+ content: [
724
+ {
725
+ type: 'text',
726
+ text: `Error fetching popularity metrics: ${error instanceof Error ? error.message : 'Unknown error'}`,
727
+ },
728
+ ],
729
+ isError: true,
730
+ };
731
+ }
732
+ }
733
+ // Export functions
734
+ export { handleNpmCompare, handleNpmDeps, handleNpmLatest, handleNpmMaintenance, handleNpmPopularity, handleNpmQuality, handleNpmSize, handleNpmTrends, handleNpmTypes, handleNpmVersions, handleNpmVulnerabilities, };
735
+ //# sourceMappingURL=index.js.map