@nekzus/mcp-server 1.4.2 → 1.5.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/README.md +212 -166
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +455 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1380 -288
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,6 +4,13 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
4
4
|
import fetch from 'node-fetch';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
// Zod schemas for npm package data
|
|
7
|
+
export const NpmMaintainerSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
name: z.string(),
|
|
10
|
+
email: z.string().optional(),
|
|
11
|
+
url: z.string().optional(),
|
|
12
|
+
})
|
|
13
|
+
.passthrough();
|
|
7
14
|
export const NpmPackageVersionSchema = z
|
|
8
15
|
.object({
|
|
9
16
|
name: z.string(),
|
|
@@ -47,6 +54,7 @@ export const NpmPackageInfoSchema = z
|
|
|
47
54
|
.passthrough()
|
|
48
55
|
.optional(),
|
|
49
56
|
homepage: z.string().optional(),
|
|
57
|
+
maintainers: z.array(NpmMaintainerSchema).optional(),
|
|
50
58
|
})
|
|
51
59
|
.passthrough();
|
|
52
60
|
export const NpmPackageDataSchema = z.object({
|
|
@@ -93,6 +101,98 @@ export const NpmPopularitySchema = z.object({
|
|
|
93
101
|
dependents: z.number(),
|
|
94
102
|
communityInterest: z.number(),
|
|
95
103
|
});
|
|
104
|
+
function isValidNpmsResponse(data) {
|
|
105
|
+
if (typeof data !== 'object' || data === null) {
|
|
106
|
+
console.debug('Response is not an object or is null');
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const response = data;
|
|
110
|
+
// Check score structure
|
|
111
|
+
if (!response.score ||
|
|
112
|
+
typeof response.score !== 'object' ||
|
|
113
|
+
!('final' in response.score) ||
|
|
114
|
+
typeof response.score.final !== 'number' ||
|
|
115
|
+
!('detail' in response.score) ||
|
|
116
|
+
typeof response.score.detail !== 'object') {
|
|
117
|
+
console.debug('Invalid score structure');
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
// Check score detail metrics
|
|
121
|
+
const detail = response.score.detail;
|
|
122
|
+
if (typeof detail.quality !== 'number' ||
|
|
123
|
+
typeof detail.popularity !== 'number' ||
|
|
124
|
+
typeof detail.maintenance !== 'number') {
|
|
125
|
+
console.debug('Invalid score detail metrics');
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
// Check collected data structure
|
|
129
|
+
if (!response.collected ||
|
|
130
|
+
typeof response.collected !== 'object' ||
|
|
131
|
+
!response.collected.metadata ||
|
|
132
|
+
typeof response.collected.metadata !== 'object' ||
|
|
133
|
+
typeof response.collected.metadata.name !== 'string' ||
|
|
134
|
+
typeof response.collected.metadata.version !== 'string') {
|
|
135
|
+
console.debug('Invalid collected data structure');
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
// Check npm data
|
|
139
|
+
if (!response.collected.npm ||
|
|
140
|
+
typeof response.collected.npm !== 'object' ||
|
|
141
|
+
!Array.isArray(response.collected.npm.downloads) ||
|
|
142
|
+
typeof response.collected.npm.starsCount !== 'number') {
|
|
143
|
+
console.debug('Invalid npm data structure');
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
// Optional github data check
|
|
147
|
+
if (response.collected.github) {
|
|
148
|
+
if (typeof response.collected.github !== 'object' ||
|
|
149
|
+
typeof response.collected.github.starsCount !== 'number' ||
|
|
150
|
+
typeof response.collected.github.forksCount !== 'number' ||
|
|
151
|
+
typeof response.collected.github.subscribersCount !== 'number' ||
|
|
152
|
+
!response.collected.github.issues ||
|
|
153
|
+
typeof response.collected.github.issues !== 'object' ||
|
|
154
|
+
typeof response.collected.github.issues.count !== 'number' ||
|
|
155
|
+
typeof response.collected.github.issues.openCount !== 'number') {
|
|
156
|
+
console.debug('Invalid github data structure');
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
export const NpmSearchResultSchema = z
|
|
163
|
+
.object({
|
|
164
|
+
objects: z.array(z.object({
|
|
165
|
+
package: z.object({
|
|
166
|
+
name: z.string(),
|
|
167
|
+
version: z.string(),
|
|
168
|
+
description: z.string().optional(),
|
|
169
|
+
keywords: z.array(z.string()).optional(),
|
|
170
|
+
publisher: z
|
|
171
|
+
.object({
|
|
172
|
+
username: z.string(),
|
|
173
|
+
})
|
|
174
|
+
.optional(),
|
|
175
|
+
links: z
|
|
176
|
+
.object({
|
|
177
|
+
npm: z.string().optional(),
|
|
178
|
+
homepage: z.string().optional(),
|
|
179
|
+
repository: z.string().optional(),
|
|
180
|
+
})
|
|
181
|
+
.optional(),
|
|
182
|
+
}),
|
|
183
|
+
score: z.object({
|
|
184
|
+
final: z.number(),
|
|
185
|
+
detail: z.object({
|
|
186
|
+
quality: z.number(),
|
|
187
|
+
popularity: z.number(),
|
|
188
|
+
maintenance: z.number(),
|
|
189
|
+
}),
|
|
190
|
+
}),
|
|
191
|
+
searchScore: z.number(),
|
|
192
|
+
})),
|
|
193
|
+
total: z.number(),
|
|
194
|
+
})
|
|
195
|
+
.passthrough();
|
|
96
196
|
// Logger function that uses stderr - only for critical errors
|
|
97
197
|
const log = (...args) => {
|
|
98
198
|
// Filter out server status messages
|
|
@@ -108,104 +208,152 @@ const TOOLS = [
|
|
|
108
208
|
{
|
|
109
209
|
name: 'npmVersions',
|
|
110
210
|
description: 'Get all available versions of an NPM package',
|
|
111
|
-
parameters: z.
|
|
112
|
-
|
|
113
|
-
|
|
211
|
+
parameters: z.union([
|
|
212
|
+
z.object({
|
|
213
|
+
packageName: z.string().describe('The name of the package'),
|
|
214
|
+
}),
|
|
215
|
+
z.object({
|
|
216
|
+
packages: z.array(z.string()).describe('List of package names to get versions for'),
|
|
217
|
+
}),
|
|
218
|
+
]),
|
|
114
219
|
inputSchema: {
|
|
115
220
|
type: 'object',
|
|
116
221
|
properties: {
|
|
117
222
|
packageName: { type: 'string' },
|
|
223
|
+
packages: { type: 'array', items: { type: 'string' } },
|
|
118
224
|
},
|
|
119
|
-
required: ['packageName'],
|
|
225
|
+
oneOf: [{ required: ['packageName'] }, { required: ['packages'] }],
|
|
120
226
|
},
|
|
121
227
|
},
|
|
122
228
|
{
|
|
123
229
|
name: 'npmLatest',
|
|
124
230
|
description: 'Get the latest version and changelog of an NPM package',
|
|
125
|
-
parameters: z.
|
|
126
|
-
|
|
127
|
-
|
|
231
|
+
parameters: z.union([
|
|
232
|
+
z.object({
|
|
233
|
+
packageName: z.string().describe('The name of the package'),
|
|
234
|
+
}),
|
|
235
|
+
z.object({
|
|
236
|
+
packages: z.array(z.string()).describe('List of package names to get latest versions for'),
|
|
237
|
+
}),
|
|
238
|
+
]),
|
|
128
239
|
inputSchema: {
|
|
129
240
|
type: 'object',
|
|
130
241
|
properties: {
|
|
131
242
|
packageName: { type: 'string' },
|
|
243
|
+
packages: { type: 'array', items: { type: 'string' } },
|
|
132
244
|
},
|
|
133
|
-
required: ['packageName'],
|
|
245
|
+
oneOf: [{ required: ['packageName'] }, { required: ['packages'] }],
|
|
134
246
|
},
|
|
135
247
|
},
|
|
136
248
|
{
|
|
137
249
|
name: 'npmDeps',
|
|
138
250
|
description: 'Analyze dependencies and devDependencies of an NPM package',
|
|
139
|
-
parameters: z.
|
|
140
|
-
|
|
141
|
-
|
|
251
|
+
parameters: z.union([
|
|
252
|
+
z.object({
|
|
253
|
+
packageName: z.string().describe('The name of the package'),
|
|
254
|
+
}),
|
|
255
|
+
z.object({
|
|
256
|
+
packages: z.array(z.string()).describe('List of package names to analyze dependencies for'),
|
|
257
|
+
}),
|
|
258
|
+
]),
|
|
142
259
|
inputSchema: {
|
|
143
260
|
type: 'object',
|
|
144
261
|
properties: {
|
|
145
262
|
packageName: { type: 'string' },
|
|
263
|
+
packages: { type: 'array', items: { type: 'string' } },
|
|
146
264
|
},
|
|
147
|
-
required: ['packageName'],
|
|
265
|
+
oneOf: [{ required: ['packageName'] }, { required: ['packages'] }],
|
|
148
266
|
},
|
|
149
267
|
},
|
|
150
268
|
{
|
|
151
269
|
name: 'npmTypes',
|
|
152
270
|
description: 'Check TypeScript types availability and version for a package',
|
|
153
|
-
parameters: z.
|
|
154
|
-
|
|
155
|
-
|
|
271
|
+
parameters: z.union([
|
|
272
|
+
z.object({
|
|
273
|
+
packageName: z.string().describe('The name of the package'),
|
|
274
|
+
}),
|
|
275
|
+
z.object({
|
|
276
|
+
packages: z.array(z.string()).describe('List of package names to check types for'),
|
|
277
|
+
}),
|
|
278
|
+
]),
|
|
156
279
|
inputSchema: {
|
|
157
280
|
type: 'object',
|
|
158
281
|
properties: {
|
|
159
282
|
packageName: { type: 'string' },
|
|
283
|
+
packages: { type: 'array', items: { type: 'string' } },
|
|
160
284
|
},
|
|
161
|
-
required: ['packageName'],
|
|
285
|
+
oneOf: [{ required: ['packageName'] }, { required: ['packages'] }],
|
|
162
286
|
},
|
|
163
287
|
},
|
|
164
288
|
{
|
|
165
289
|
name: 'npmSize',
|
|
166
290
|
description: 'Get package size information including dependencies and bundle size',
|
|
167
|
-
parameters: z.
|
|
168
|
-
|
|
169
|
-
|
|
291
|
+
parameters: z.union([
|
|
292
|
+
z.object({
|
|
293
|
+
packageName: z.string().describe('The name of the package'),
|
|
294
|
+
}),
|
|
295
|
+
z.object({
|
|
296
|
+
packages: z.array(z.string()).describe('List of package names to get size information for'),
|
|
297
|
+
}),
|
|
298
|
+
]),
|
|
170
299
|
inputSchema: {
|
|
171
300
|
type: 'object',
|
|
172
301
|
properties: {
|
|
173
302
|
packageName: { type: 'string' },
|
|
303
|
+
packages: { type: 'array', items: { type: 'string' } },
|
|
174
304
|
},
|
|
175
|
-
required: ['packageName'],
|
|
305
|
+
oneOf: [{ required: ['packageName'] }, { required: ['packages'] }],
|
|
176
306
|
},
|
|
177
307
|
},
|
|
178
308
|
{
|
|
179
309
|
name: 'npmVulnerabilities',
|
|
180
|
-
description: 'Check for known vulnerabilities in
|
|
181
|
-
parameters: z.
|
|
182
|
-
|
|
183
|
-
|
|
310
|
+
description: 'Check for known vulnerabilities in packages',
|
|
311
|
+
parameters: z.union([
|
|
312
|
+
z.object({
|
|
313
|
+
packageName: z.string().describe('The name of the package'),
|
|
314
|
+
}),
|
|
315
|
+
z.object({
|
|
316
|
+
packages: z
|
|
317
|
+
.array(z.string())
|
|
318
|
+
.describe('List of package names to check for vulnerabilities'),
|
|
319
|
+
}),
|
|
320
|
+
]),
|
|
184
321
|
inputSchema: {
|
|
185
322
|
type: 'object',
|
|
186
323
|
properties: {
|
|
187
324
|
packageName: { type: 'string' },
|
|
325
|
+
packages: { type: 'array', items: { type: 'string' } },
|
|
188
326
|
},
|
|
189
|
-
required: ['packageName'],
|
|
327
|
+
oneOf: [{ required: ['packageName'] }, { required: ['packages'] }],
|
|
190
328
|
},
|
|
191
329
|
},
|
|
192
330
|
{
|
|
193
331
|
name: 'npmTrends',
|
|
194
|
-
description: 'Get download trends and popularity metrics for
|
|
332
|
+
description: 'Get download trends and popularity metrics for packages. Available periods: "last-week" (7 days), "last-month" (30 days), or "last-year" (365 days)',
|
|
195
333
|
parameters: z.object({
|
|
196
|
-
|
|
197
|
-
period: z
|
|
334
|
+
packages: z.array(z.string()).describe('List of package names to get trends for'),
|
|
335
|
+
period: z
|
|
336
|
+
.enum(['last-week', 'last-month', 'last-year'])
|
|
337
|
+
.describe('Time period for trends. Options: "last-week", "last-month", "last-year"')
|
|
338
|
+
.optional()
|
|
339
|
+
.default('last-month'),
|
|
198
340
|
}),
|
|
199
341
|
inputSchema: {
|
|
200
342
|
type: 'object',
|
|
201
343
|
properties: {
|
|
202
|
-
|
|
344
|
+
packages: {
|
|
345
|
+
type: 'array',
|
|
346
|
+
items: { type: 'string' },
|
|
347
|
+
description: 'List of package names to get trends for',
|
|
348
|
+
},
|
|
203
349
|
period: {
|
|
204
350
|
type: 'string',
|
|
205
351
|
enum: ['last-week', 'last-month', 'last-year'],
|
|
352
|
+
description: 'Time period for trends. Options: "last-week" (7 days), "last-month" (30 days), or "last-year" (365 days)',
|
|
353
|
+
default: 'last-month',
|
|
206
354
|
},
|
|
207
355
|
},
|
|
208
|
-
required: ['
|
|
356
|
+
required: ['packages'],
|
|
209
357
|
},
|
|
210
358
|
},
|
|
211
359
|
{
|
|
@@ -225,15 +373,95 @@ const TOOLS = [
|
|
|
225
373
|
required: ['packages'],
|
|
226
374
|
},
|
|
227
375
|
},
|
|
376
|
+
{
|
|
377
|
+
name: 'npmMaintainers',
|
|
378
|
+
description: 'Get maintainers for an NPM package',
|
|
379
|
+
parameters: z.object({
|
|
380
|
+
packageName: z.string().describe('The name of the package'),
|
|
381
|
+
}),
|
|
382
|
+
inputSchema: {
|
|
383
|
+
type: 'object',
|
|
384
|
+
properties: {
|
|
385
|
+
packageName: { type: 'string' },
|
|
386
|
+
},
|
|
387
|
+
required: ['packageName'],
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: 'npmScore',
|
|
392
|
+
description: 'Get consolidated package score based on quality, maintenance, and popularity metrics',
|
|
393
|
+
parameters: z.union([
|
|
394
|
+
z.object({
|
|
395
|
+
packageName: z.string().describe('The name of the package'),
|
|
396
|
+
}),
|
|
397
|
+
z.object({
|
|
398
|
+
packages: z.array(z.string()).describe('List of package names to get scores for'),
|
|
399
|
+
}),
|
|
400
|
+
]),
|
|
401
|
+
inputSchema: {
|
|
402
|
+
type: 'object',
|
|
403
|
+
properties: {
|
|
404
|
+
packageName: { type: 'string' },
|
|
405
|
+
packages: { type: 'array', items: { type: 'string' } },
|
|
406
|
+
},
|
|
407
|
+
oneOf: [{ required: ['packageName'] }, { required: ['packages'] }],
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: 'npmPackageReadme',
|
|
412
|
+
description: 'Get the README for an NPM package',
|
|
413
|
+
parameters: z.union([
|
|
414
|
+
z.object({
|
|
415
|
+
packageName: z.string().describe('The name of the package'),
|
|
416
|
+
}),
|
|
417
|
+
z.object({
|
|
418
|
+
packages: z.array(z.string()).describe('List of package names to get READMEs for'),
|
|
419
|
+
}),
|
|
420
|
+
]),
|
|
421
|
+
inputSchema: {
|
|
422
|
+
type: 'object',
|
|
423
|
+
properties: {
|
|
424
|
+
packageName: { type: 'string' },
|
|
425
|
+
packages: { type: 'array', items: { type: 'string' } },
|
|
426
|
+
},
|
|
427
|
+
oneOf: [{ required: ['packageName'] }, { required: ['packages'] }],
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
name: 'npmSearch',
|
|
432
|
+
description: 'Search for NPM packages',
|
|
433
|
+
parameters: z.object({
|
|
434
|
+
query: z.string().describe('Search query for packages'),
|
|
435
|
+
limit: z
|
|
436
|
+
.number()
|
|
437
|
+
.min(1)
|
|
438
|
+
.max(50)
|
|
439
|
+
.optional()
|
|
440
|
+
.describe('Maximum number of results to return (default: 10)'),
|
|
441
|
+
}),
|
|
442
|
+
inputSchema: {
|
|
443
|
+
type: 'object',
|
|
444
|
+
properties: {
|
|
445
|
+
query: { type: 'string' },
|
|
446
|
+
limit: { type: 'number', minimum: 1, maximum: 50 },
|
|
447
|
+
},
|
|
448
|
+
required: ['query'],
|
|
449
|
+
},
|
|
450
|
+
},
|
|
228
451
|
];
|
|
229
452
|
// Type guards for API responses
|
|
230
453
|
function isNpmPackageInfo(data) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
454
|
+
return (typeof data === 'object' &&
|
|
455
|
+
data !== null &&
|
|
456
|
+
(!('maintainers' in data) ||
|
|
457
|
+
(Array.isArray(data.maintainers) &&
|
|
458
|
+
(data.maintainers?.every((m) => typeof m === 'object' &&
|
|
459
|
+
m !== null &&
|
|
460
|
+
'name' in m &&
|
|
461
|
+
'email' in m &&
|
|
462
|
+
typeof m.name === 'string' &&
|
|
463
|
+
typeof m.email === 'string') ??
|
|
464
|
+
true))));
|
|
237
465
|
}
|
|
238
466
|
function isNpmPackageData(data) {
|
|
239
467
|
try {
|
|
@@ -261,30 +489,41 @@ function isNpmDownloadsData(data) {
|
|
|
261
489
|
}
|
|
262
490
|
async function handleNpmVersions(args) {
|
|
263
491
|
try {
|
|
264
|
-
const
|
|
265
|
-
if (
|
|
266
|
-
throw new Error(
|
|
492
|
+
const packagesToProcess = args.packages || [];
|
|
493
|
+
if (packagesToProcess.length === 0) {
|
|
494
|
+
throw new Error('No package names provided');
|
|
495
|
+
}
|
|
496
|
+
const results = await Promise.all(packagesToProcess.map(async (pkg) => {
|
|
497
|
+
const response = await fetch(`https://registry.npmjs.org/${pkg}`);
|
|
498
|
+
if (!response.ok) {
|
|
499
|
+
return { name: pkg, error: `Failed to fetch package info: ${response.statusText}` };
|
|
500
|
+
}
|
|
501
|
+
const rawData = await response.json();
|
|
502
|
+
if (!isNpmPackageInfo(rawData)) {
|
|
503
|
+
return { name: pkg, error: 'Invalid package info data received' };
|
|
504
|
+
}
|
|
505
|
+
const versions = Object.keys(rawData.versions ?? {}).sort((a, b) => {
|
|
506
|
+
const [aMajor = 0, aMinor = 0, aPatch = 0] = a.split('.').map(Number);
|
|
507
|
+
const [bMajor = 0, bMinor = 0, bPatch = 0] = b.split('.').map(Number);
|
|
508
|
+
if (aMajor !== bMajor)
|
|
509
|
+
return aMajor - bMajor;
|
|
510
|
+
if (aMinor !== bMinor)
|
|
511
|
+
return aMinor - bMinor;
|
|
512
|
+
return aPatch - bPatch;
|
|
513
|
+
});
|
|
514
|
+
return { name: pkg, versions };
|
|
515
|
+
}));
|
|
516
|
+
let text = '';
|
|
517
|
+
for (const result of results) {
|
|
518
|
+
if ('error' in result) {
|
|
519
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
text += `📦 Available versions for ${result.name}:\n${result.versions.join('\n')}\n\n`;
|
|
523
|
+
}
|
|
267
524
|
}
|
|
268
|
-
const rawData = await response.json();
|
|
269
|
-
if (!isNpmPackageInfo(rawData)) {
|
|
270
|
-
throw new Error('Invalid package info data received');
|
|
271
|
-
}
|
|
272
|
-
const versions = Object.keys(rawData.versions ?? {}).sort((a, b) => {
|
|
273
|
-
const [aMajor = 0, aMinor = 0, aPatch = 0] = a.split('.').map(Number);
|
|
274
|
-
const [bMajor = 0, bMinor = 0, bPatch = 0] = b.split('.').map(Number);
|
|
275
|
-
if (aMajor !== bMajor)
|
|
276
|
-
return aMajor - bMajor;
|
|
277
|
-
if (aMinor !== bMinor)
|
|
278
|
-
return aMinor - bMinor;
|
|
279
|
-
return aPatch - bPatch;
|
|
280
|
-
});
|
|
281
525
|
return {
|
|
282
|
-
content: [
|
|
283
|
-
{
|
|
284
|
-
type: 'text',
|
|
285
|
-
text: `📦 Available versions for ${args.packageName}:\n${versions.join('\n')}`,
|
|
286
|
-
},
|
|
287
|
-
],
|
|
526
|
+
content: [{ type: 'text', text }],
|
|
288
527
|
isError: false,
|
|
289
528
|
};
|
|
290
529
|
}
|
|
@@ -302,44 +541,29 @@ async function handleNpmVersions(args) {
|
|
|
302
541
|
}
|
|
303
542
|
async function handleNpmLatest(args) {
|
|
304
543
|
try {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
544
|
+
const packages = args.packages || [];
|
|
545
|
+
let text = '';
|
|
546
|
+
for (const pkg of packages) {
|
|
547
|
+
const response = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
|
|
548
|
+
if (!response.ok) {
|
|
549
|
+
throw new Error(`Failed to fetch latest version for ${pkg}: ${response.statusText}`);
|
|
550
|
+
}
|
|
551
|
+
const data = (await response.json());
|
|
552
|
+
text += `📦 Latest version of ${pkg}\n`;
|
|
553
|
+
text += `Version: ${data.version}\n`;
|
|
554
|
+
text += `Description: ${data.description || 'No description available'}\n`;
|
|
555
|
+
text += `Author: ${data.author?.name || 'Unknown'}\n`;
|
|
556
|
+
text += `License: ${data.license || 'Unknown'}\n`;
|
|
557
|
+
text += `Homepage: ${data.homepage || 'Not specified'}\n\n`;
|
|
558
|
+
text += '---\n\n';
|
|
309
559
|
}
|
|
310
|
-
const rawData = await response.json();
|
|
311
|
-
if (!isNpmPackageInfo(rawData)) {
|
|
312
|
-
throw new Error('Invalid package info data received');
|
|
313
|
-
}
|
|
314
|
-
const latestVersion = rawData['dist-tags']?.latest;
|
|
315
|
-
if (!latestVersion || !rawData.versions?.[latestVersion]) {
|
|
316
|
-
throw new Error('No latest version found');
|
|
317
|
-
}
|
|
318
|
-
const latestVersionInfo = rawData.versions[latestVersion];
|
|
319
|
-
const description = latestVersionInfo.description ?? '';
|
|
320
|
-
const repository = latestVersionInfo.repository ?? rawData.repository;
|
|
321
|
-
const homepage = latestVersionInfo.homepage ?? rawData.homepage;
|
|
322
|
-
const bugs = latestVersionInfo.bugs ?? rawData.bugs;
|
|
323
|
-
const text = [
|
|
324
|
-
`📦 Latest version of ${args.packageName}: ${latestVersion}`,
|
|
325
|
-
'',
|
|
326
|
-
description && `Description:\n${description}`,
|
|
327
|
-
'',
|
|
328
|
-
'Links:',
|
|
329
|
-
homepage && `• Homepage: ${homepage}`,
|
|
330
|
-
repository?.url && `• Repository: ${repository.url.replace('git+', '').replace('.git', '')}`,
|
|
331
|
-
bugs?.url && `• Issues: ${bugs.url}`,
|
|
332
|
-
'',
|
|
333
|
-
repository?.url?.includes('github.com') &&
|
|
334
|
-
`You can check for updates at:\n${repository.url
|
|
335
|
-
.replace('git+', '')
|
|
336
|
-
.replace('git:', 'https:')
|
|
337
|
-
.replace('.git', '')}/releases`,
|
|
338
|
-
]
|
|
339
|
-
.filter(Boolean)
|
|
340
|
-
.join('\n');
|
|
341
560
|
return {
|
|
342
|
-
content: [
|
|
561
|
+
content: [
|
|
562
|
+
{
|
|
563
|
+
type: 'text',
|
|
564
|
+
text,
|
|
565
|
+
},
|
|
566
|
+
],
|
|
343
567
|
isError: false,
|
|
344
568
|
};
|
|
345
569
|
}
|
|
@@ -348,7 +572,7 @@ async function handleNpmLatest(args) {
|
|
|
348
572
|
content: [
|
|
349
573
|
{
|
|
350
574
|
type: 'text',
|
|
351
|
-
text: `Error fetching
|
|
575
|
+
text: `Error fetching latest version: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
352
576
|
},
|
|
353
577
|
],
|
|
354
578
|
isError: true,
|
|
@@ -357,38 +581,62 @@ async function handleNpmLatest(args) {
|
|
|
357
581
|
}
|
|
358
582
|
async function handleNpmDeps(args) {
|
|
359
583
|
try {
|
|
360
|
-
const
|
|
361
|
-
if (
|
|
362
|
-
throw new Error(
|
|
584
|
+
const packagesToProcess = args.packages || [];
|
|
585
|
+
if (packagesToProcess.length === 0) {
|
|
586
|
+
throw new Error('No package names provided');
|
|
587
|
+
}
|
|
588
|
+
const results = await Promise.all(packagesToProcess.map(async (pkg) => {
|
|
589
|
+
try {
|
|
590
|
+
const response = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
|
|
591
|
+
if (!response.ok) {
|
|
592
|
+
return { name: pkg, error: `Failed to fetch package info: ${response.statusText}` };
|
|
593
|
+
}
|
|
594
|
+
const rawData = await response.json();
|
|
595
|
+
if (!isNpmPackageData(rawData)) {
|
|
596
|
+
return { name: pkg, error: 'Invalid package data received' };
|
|
597
|
+
}
|
|
598
|
+
return {
|
|
599
|
+
name: pkg,
|
|
600
|
+
version: rawData.version,
|
|
601
|
+
dependencies: rawData.dependencies ?? {},
|
|
602
|
+
devDependencies: rawData.devDependencies ?? {},
|
|
603
|
+
peerDependencies: rawData.peerDependencies ?? {},
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
catch (error) {
|
|
607
|
+
return { name: pkg, error: error instanceof Error ? error.message : 'Unknown error' };
|
|
608
|
+
}
|
|
609
|
+
}));
|
|
610
|
+
let text = '';
|
|
611
|
+
for (const result of results) {
|
|
612
|
+
if ('error' in result) {
|
|
613
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
text += `📦 Dependencies for ${result.name}@${result.version}\n\n`;
|
|
617
|
+
if (Object.keys(result.dependencies).length > 0) {
|
|
618
|
+
text += 'Dependencies:\n';
|
|
619
|
+
for (const [dep, version] of Object.entries(result.dependencies)) {
|
|
620
|
+
text += `• ${dep}: ${version}\n`;
|
|
621
|
+
}
|
|
622
|
+
text += '\n';
|
|
623
|
+
}
|
|
624
|
+
if (Object.keys(result.devDependencies).length > 0) {
|
|
625
|
+
text += 'Dev Dependencies:\n';
|
|
626
|
+
for (const [dep, version] of Object.entries(result.devDependencies)) {
|
|
627
|
+
text += `• ${dep}: ${version}\n`;
|
|
628
|
+
}
|
|
629
|
+
text += '\n';
|
|
630
|
+
}
|
|
631
|
+
if (Object.keys(result.peerDependencies).length > 0) {
|
|
632
|
+
text += 'Peer Dependencies:\n';
|
|
633
|
+
for (const [dep, version] of Object.entries(result.peerDependencies)) {
|
|
634
|
+
text += `• ${dep}: ${version}\n`;
|
|
635
|
+
}
|
|
636
|
+
text += '\n';
|
|
637
|
+
}
|
|
638
|
+
text += '---\n\n';
|
|
363
639
|
}
|
|
364
|
-
const rawData = await response.json();
|
|
365
|
-
if (!isNpmPackageData(rawData)) {
|
|
366
|
-
throw new Error('Invalid package data received');
|
|
367
|
-
}
|
|
368
|
-
const dependencies = rawData.dependencies ?? {};
|
|
369
|
-
const devDependencies = rawData.devDependencies ?? {};
|
|
370
|
-
const peerDependencies = rawData.peerDependencies ?? {};
|
|
371
|
-
const text = [
|
|
372
|
-
`📦 Dependencies for ${args.packageName}@${rawData.version}`,
|
|
373
|
-
'',
|
|
374
|
-
Object.keys(dependencies).length > 0 && [
|
|
375
|
-
'Dependencies:',
|
|
376
|
-
...Object.entries(dependencies).map(([dep, version]) => `• ${dep}: ${version}`),
|
|
377
|
-
'',
|
|
378
|
-
],
|
|
379
|
-
Object.keys(devDependencies).length > 0 && [
|
|
380
|
-
'Dev Dependencies:',
|
|
381
|
-
...Object.entries(devDependencies).map(([dep, version]) => `• ${dep}: ${version}`),
|
|
382
|
-
'',
|
|
383
|
-
],
|
|
384
|
-
Object.keys(peerDependencies).length > 0 && [
|
|
385
|
-
'Peer Dependencies:',
|
|
386
|
-
...Object.entries(peerDependencies).map(([dep, version]) => `• ${dep}: ${version}`),
|
|
387
|
-
],
|
|
388
|
-
]
|
|
389
|
-
.filter(Boolean)
|
|
390
|
-
.flat()
|
|
391
|
-
.join('\n');
|
|
392
640
|
return {
|
|
393
641
|
content: [{ type: 'text', text }],
|
|
394
642
|
isError: false,
|
|
@@ -408,25 +656,36 @@ async function handleNpmDeps(args) {
|
|
|
408
656
|
}
|
|
409
657
|
async function handleNpmTypes(args) {
|
|
410
658
|
try {
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
659
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
660
|
+
const response = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
|
|
661
|
+
if (!response.ok) {
|
|
662
|
+
throw new Error(`Failed to fetch package info: ${response.statusText}`);
|
|
663
|
+
}
|
|
664
|
+
const data = (await response.json());
|
|
665
|
+
let text = `📦 TypeScript support for ${pkg}@${data.version}\n`;
|
|
666
|
+
const hasTypes = Boolean(data.types || data.typings);
|
|
667
|
+
if (hasTypes) {
|
|
668
|
+
text += '✅ Package includes built-in TypeScript types\n';
|
|
669
|
+
text += `Types path: ${data.types || data.typings}\n`;
|
|
670
|
+
}
|
|
671
|
+
const typesPackage = `@types/${pkg.replace('@', '').replace('/', '__')}`;
|
|
672
|
+
const typesResponse = await fetch(`https://registry.npmjs.org/${typesPackage}/latest`).catch(() => null);
|
|
673
|
+
if (typesResponse?.ok) {
|
|
674
|
+
const typesData = (await typesResponse.json());
|
|
675
|
+
text += `📦 DefinitelyTyped package available: ${typesPackage}@${typesData.version}\n`;
|
|
676
|
+
text += `Install with: npm install -D ${typesPackage}`;
|
|
677
|
+
}
|
|
678
|
+
else if (!hasTypes) {
|
|
679
|
+
text += '❌ No TypeScript type definitions found';
|
|
680
|
+
}
|
|
681
|
+
return { name: pkg, text };
|
|
682
|
+
}));
|
|
683
|
+
let text = '';
|
|
684
|
+
for (const result of results) {
|
|
685
|
+
text += `${result.text}\n\n`;
|
|
686
|
+
if (results.indexOf(result) < results.length - 1) {
|
|
687
|
+
text += '---\n\n';
|
|
688
|
+
}
|
|
430
689
|
}
|
|
431
690
|
return {
|
|
432
691
|
content: [{ type: 'text', text }],
|
|
@@ -444,27 +703,39 @@ async function handleNpmTypes(args) {
|
|
|
444
703
|
}
|
|
445
704
|
async function handleNpmSize(args) {
|
|
446
705
|
try {
|
|
447
|
-
const
|
|
448
|
-
if (
|
|
449
|
-
throw new Error(
|
|
706
|
+
const packagesToProcess = args.packages || [];
|
|
707
|
+
if (packagesToProcess.length === 0) {
|
|
708
|
+
throw new Error('No package names provided');
|
|
450
709
|
}
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
710
|
+
const results = await Promise.all(packagesToProcess.map(async (pkg) => {
|
|
711
|
+
const response = await fetch(`https://bundlephobia.com/api/size?package=${pkg}`);
|
|
712
|
+
if (!response.ok) {
|
|
713
|
+
return { name: pkg, error: `Failed to fetch package size: ${response.statusText}` };
|
|
714
|
+
}
|
|
715
|
+
const rawData = await response.json();
|
|
716
|
+
if (!isBundlephobiaData(rawData)) {
|
|
717
|
+
return { name: pkg, error: 'Invalid response from bundlephobia' };
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
name: pkg,
|
|
721
|
+
sizeInKb: Number((rawData.size / 1024).toFixed(2)),
|
|
722
|
+
gzipInKb: Number((rawData.gzip / 1024).toFixed(2)),
|
|
723
|
+
dependencyCount: rawData.dependencyCount,
|
|
724
|
+
};
|
|
725
|
+
}));
|
|
726
|
+
let text = '';
|
|
727
|
+
for (const result of results) {
|
|
728
|
+
if ('error' in result) {
|
|
729
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
text += `📦 ${result.name}\n`;
|
|
733
|
+
text += `Size: ${result.sizeInKb}KB (gzipped: ${result.gzipInKb}KB)\n`;
|
|
734
|
+
text += `Dependencies: ${result.dependencyCount}\n\n`;
|
|
735
|
+
}
|
|
454
736
|
}
|
|
455
|
-
const sizeInKb = Number((rawData.size / 1024).toFixed(2));
|
|
456
|
-
const gzipInKb = Number((rawData.gzip / 1024).toFixed(2));
|
|
457
737
|
return {
|
|
458
|
-
content: [
|
|
459
|
-
{
|
|
460
|
-
type: 'text',
|
|
461
|
-
text: `Package size: ${sizeInKb}KB (gzipped: ${gzipInKb}KB)`,
|
|
462
|
-
},
|
|
463
|
-
{
|
|
464
|
-
type: 'text',
|
|
465
|
-
text: `Dependencies: ${rawData.dependencyCount}`,
|
|
466
|
-
},
|
|
467
|
-
],
|
|
738
|
+
content: [{ type: 'text', text }],
|
|
468
739
|
isError: false,
|
|
469
740
|
};
|
|
470
741
|
}
|
|
@@ -473,7 +744,7 @@ async function handleNpmSize(args) {
|
|
|
473
744
|
content: [
|
|
474
745
|
{
|
|
475
746
|
type: 'text',
|
|
476
|
-
text: `Error fetching package
|
|
747
|
+
text: `Error fetching package sizes: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
477
748
|
},
|
|
478
749
|
],
|
|
479
750
|
isError: true,
|
|
@@ -482,40 +753,54 @@ async function handleNpmSize(args) {
|
|
|
482
753
|
}
|
|
483
754
|
async function handleNpmVulnerabilities(args) {
|
|
484
755
|
try {
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
756
|
+
const packagesToProcess = args.packages || [];
|
|
757
|
+
if (packagesToProcess.length === 0) {
|
|
758
|
+
throw new Error('No package names provided');
|
|
759
|
+
}
|
|
760
|
+
const results = await Promise.all(packagesToProcess.map(async (pkg) => {
|
|
761
|
+
const response = await fetch('https://api.osv.dev/v1/query', {
|
|
762
|
+
method: 'POST',
|
|
763
|
+
headers: {
|
|
764
|
+
'Content-Type': 'application/json',
|
|
494
765
|
},
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
766
|
+
body: JSON.stringify({
|
|
767
|
+
package: {
|
|
768
|
+
name: pkg,
|
|
769
|
+
ecosystem: 'npm',
|
|
770
|
+
},
|
|
771
|
+
}),
|
|
772
|
+
});
|
|
773
|
+
if (!response.ok) {
|
|
774
|
+
return { name: pkg, error: `Failed to fetch vulnerability info: ${response.statusText}` };
|
|
775
|
+
}
|
|
776
|
+
const data = (await response.json());
|
|
777
|
+
return { name: pkg, vulns: data.vulns || [] };
|
|
778
|
+
}));
|
|
779
|
+
let text = '🔒 Security Analysis\n\n';
|
|
780
|
+
for (const result of results) {
|
|
781
|
+
if ('error' in result) {
|
|
782
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
text += `📦 ${result.name}\n`;
|
|
786
|
+
if (result.vulns.length === 0) {
|
|
787
|
+
text += '✅ No known vulnerabilities\n\n';
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
text += `⚠️ Found ${result.vulns.length} vulnerabilities:\n\n`;
|
|
791
|
+
for (const vuln of result.vulns) {
|
|
792
|
+
text += `- ${vuln.summary}\n`;
|
|
793
|
+
const severity = typeof vuln.severity === 'object'
|
|
794
|
+
? vuln.severity.type || 'Unknown'
|
|
795
|
+
: vuln.severity || 'Unknown';
|
|
796
|
+
text += ` Severity: ${severity}\n`;
|
|
797
|
+
if (vuln.references && vuln.references.length > 0) {
|
|
798
|
+
text += ` More info: ${vuln.references[0].url}\n`;
|
|
799
|
+
}
|
|
800
|
+
text += '\n';
|
|
516
801
|
}
|
|
517
|
-
text += '\n';
|
|
518
802
|
}
|
|
803
|
+
text += '---\n\n';
|
|
519
804
|
}
|
|
520
805
|
return {
|
|
521
806
|
content: [{ type: 'text', text }],
|
|
@@ -525,7 +810,10 @@ async function handleNpmVulnerabilities(args) {
|
|
|
525
810
|
catch (error) {
|
|
526
811
|
return {
|
|
527
812
|
content: [
|
|
528
|
-
{
|
|
813
|
+
{
|
|
814
|
+
type: 'text',
|
|
815
|
+
text: `Error checking vulnerabilities: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
816
|
+
},
|
|
529
817
|
],
|
|
530
818
|
isError: true,
|
|
531
819
|
};
|
|
@@ -534,18 +822,55 @@ async function handleNpmVulnerabilities(args) {
|
|
|
534
822
|
async function handleNpmTrends(args) {
|
|
535
823
|
try {
|
|
536
824
|
const period = args.period || 'last-month';
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
825
|
+
const periodDays = {
|
|
826
|
+
'last-week': 7,
|
|
827
|
+
'last-month': 30,
|
|
828
|
+
'last-year': 365,
|
|
829
|
+
};
|
|
830
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
831
|
+
const response = await fetch(`https://api.npmjs.org/downloads/point/${period}/${pkg}`);
|
|
832
|
+
if (!response.ok) {
|
|
833
|
+
return {
|
|
834
|
+
name: pkg,
|
|
835
|
+
error: `Failed to fetch download trends: ${response.statusText}`,
|
|
836
|
+
success: false,
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
const data = await response.json();
|
|
840
|
+
if (!isNpmDownloadsData(data)) {
|
|
841
|
+
return {
|
|
842
|
+
name: pkg,
|
|
843
|
+
error: 'Invalid response format from npm downloads API',
|
|
844
|
+
success: false,
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
return {
|
|
848
|
+
name: pkg,
|
|
849
|
+
downloads: data.downloads,
|
|
850
|
+
success: true,
|
|
851
|
+
};
|
|
852
|
+
}));
|
|
853
|
+
let text = '📈 Download Trends\n\n';
|
|
854
|
+
text += `Period: ${period} (${periodDays[period]} days)\n\n`;
|
|
855
|
+
// Individual package stats
|
|
856
|
+
for (const result of results) {
|
|
857
|
+
if (!result.success) {
|
|
858
|
+
text += `❌ ${result.name}: ${result.error}\n`;
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
text += `📦 ${result.name}\n`;
|
|
862
|
+
text += `Total downloads: ${result.downloads.toLocaleString()}\n`;
|
|
863
|
+
text += `Average daily downloads: ${Math.round(result.downloads / periodDays[period]).toLocaleString()}\n\n`;
|
|
544
864
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
865
|
+
// Total stats
|
|
866
|
+
const totalDownloads = results.reduce((total, result) => {
|
|
867
|
+
if (result.success) {
|
|
868
|
+
return total + result.downloads;
|
|
869
|
+
}
|
|
870
|
+
return total;
|
|
871
|
+
}, 0);
|
|
872
|
+
text += `Total downloads across all packages: ${totalDownloads.toLocaleString()}\n`;
|
|
873
|
+
text += `Average daily downloads across all packages: ${Math.round(totalDownloads / periodDays[period]).toLocaleString()}\n`;
|
|
549
874
|
return {
|
|
550
875
|
content: [{ type: 'text', text }],
|
|
551
876
|
isError: false,
|
|
@@ -607,31 +932,83 @@ async function handleNpmCompare(args) {
|
|
|
607
932
|
// Function to get package quality metrics
|
|
608
933
|
async function handleNpmQuality(args) {
|
|
609
934
|
try {
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
935
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
936
|
+
const response = await fetch(`https://api.npms.io/v2/package/${encodeURIComponent(pkg)}`);
|
|
937
|
+
if (!response.ok) {
|
|
938
|
+
return { name: pkg, error: `Failed to fetch quality data: ${response.statusText}` };
|
|
939
|
+
}
|
|
940
|
+
const rawData = await response.json();
|
|
941
|
+
if (!isValidNpmsResponse(rawData)) {
|
|
942
|
+
return { name: pkg, error: 'Invalid response format from npms.io API' };
|
|
943
|
+
}
|
|
944
|
+
const quality = rawData.score.detail.quality;
|
|
945
|
+
return {
|
|
946
|
+
name: pkg,
|
|
947
|
+
...NpmQualitySchema.parse({
|
|
948
|
+
score: Math.round(quality * 100) / 100,
|
|
949
|
+
tests: 0, // These values are no longer available in the API
|
|
950
|
+
coverage: 0,
|
|
951
|
+
linting: 0,
|
|
952
|
+
types: 0,
|
|
953
|
+
}),
|
|
954
|
+
};
|
|
955
|
+
}));
|
|
956
|
+
let text = '📊 Quality Metrics\n\n';
|
|
957
|
+
for (const result of results) {
|
|
958
|
+
if ('error' in result) {
|
|
959
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
text += `📦 ${result.name}\n`;
|
|
963
|
+
text += `- Overall Score: ${result.score}\n`;
|
|
964
|
+
text +=
|
|
965
|
+
'- Note: Detailed metrics (tests, coverage, linting, types) are no longer provided by the API\n\n';
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
content: [{ type: 'text', text }],
|
|
969
|
+
isError: false,
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
catch (error) {
|
|
623
973
|
return {
|
|
624
974
|
content: [
|
|
625
975
|
{
|
|
626
976
|
type: 'text',
|
|
627
|
-
text: `
|
|
628
|
-
- Overall Score: ${result.score}
|
|
629
|
-
- Tests: ${result.tests}
|
|
630
|
-
- Coverage: ${result.coverage}
|
|
631
|
-
- Linting: ${result.linting}
|
|
632
|
-
- Types: ${result.types}`,
|
|
977
|
+
text: `Error fetching quality metrics: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
633
978
|
},
|
|
634
979
|
],
|
|
980
|
+
isError: true,
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
async function handleNpmMaintenance(args) {
|
|
985
|
+
try {
|
|
986
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
987
|
+
const response = await fetch(`https://api.npms.io/v2/package/${encodeURIComponent(pkg)}`);
|
|
988
|
+
if (!response.ok) {
|
|
989
|
+
return { name: pkg, error: `Failed to fetch maintenance data: ${response.statusText}` };
|
|
990
|
+
}
|
|
991
|
+
const rawData = await response.json();
|
|
992
|
+
if (!isValidNpmsResponse(rawData)) {
|
|
993
|
+
return { name: pkg, error: 'Invalid response format from npms.io API' };
|
|
994
|
+
}
|
|
995
|
+
const maintenance = rawData.score.detail.maintenance;
|
|
996
|
+
return {
|
|
997
|
+
name: pkg,
|
|
998
|
+
score: Math.round(maintenance * 100) / 100,
|
|
999
|
+
};
|
|
1000
|
+
}));
|
|
1001
|
+
let text = '🛠️ Maintenance Metrics\n\n';
|
|
1002
|
+
for (const result of results) {
|
|
1003
|
+
if ('error' in result) {
|
|
1004
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
text += `📦 ${result.name}\n`;
|
|
1008
|
+
text += `- Maintenance Score: ${result.score}\n\n`;
|
|
1009
|
+
}
|
|
1010
|
+
return {
|
|
1011
|
+
content: [{ type: 'text', text }],
|
|
635
1012
|
isError: false,
|
|
636
1013
|
};
|
|
637
1014
|
}
|
|
@@ -640,39 +1017,113 @@ async function handleNpmQuality(args) {
|
|
|
640
1017
|
content: [
|
|
641
1018
|
{
|
|
642
1019
|
type: 'text',
|
|
643
|
-
text: `Error fetching
|
|
1020
|
+
text: `Error fetching maintenance metrics: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
644
1021
|
},
|
|
645
1022
|
],
|
|
646
1023
|
isError: true,
|
|
647
1024
|
};
|
|
648
1025
|
}
|
|
649
1026
|
}
|
|
650
|
-
|
|
651
|
-
async function handleNpmMaintenance(args) {
|
|
1027
|
+
async function handleNpmPopularity(args) {
|
|
652
1028
|
try {
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
1029
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
1030
|
+
const response = await fetch(`https://api.npms.io/v2/package/${encodeURIComponent(pkg)}`);
|
|
1031
|
+
if (!response.ok) {
|
|
1032
|
+
return { name: pkg, error: `Failed to fetch popularity data: ${response.statusText}` };
|
|
1033
|
+
}
|
|
1034
|
+
const data = await response.json();
|
|
1035
|
+
if (!isValidNpmsResponse(data)) {
|
|
1036
|
+
return { name: pkg, error: 'Invalid API response format' };
|
|
1037
|
+
}
|
|
1038
|
+
const popularityScore = data.score.detail.popularity;
|
|
1039
|
+
return {
|
|
1040
|
+
name: pkg,
|
|
1041
|
+
...NpmPopularitySchema.parse({
|
|
1042
|
+
score: Math.round(popularityScore * 100) / 100,
|
|
1043
|
+
stars: 0,
|
|
1044
|
+
downloads: 0,
|
|
1045
|
+
dependents: 0,
|
|
1046
|
+
communityInterest: 0,
|
|
1047
|
+
}),
|
|
1048
|
+
};
|
|
1049
|
+
}));
|
|
1050
|
+
let text = '📈 Popularity Metrics\n\n';
|
|
1051
|
+
for (const result of results) {
|
|
1052
|
+
if ('error' in result) {
|
|
1053
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
text += `📦 ${result.name}\n`;
|
|
1057
|
+
text += `- Overall Score: ${result.score}\n`;
|
|
1058
|
+
text += '- Note: Detailed metrics are no longer provided by the API\n\n';
|
|
1059
|
+
}
|
|
1060
|
+
return {
|
|
1061
|
+
content: [{ type: 'text', text }],
|
|
1062
|
+
isError: false,
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
catch (error) {
|
|
1066
|
+
return {
|
|
1067
|
+
content: [
|
|
1068
|
+
{
|
|
1069
|
+
type: 'text',
|
|
1070
|
+
text: `Error fetching popularity metrics: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
1071
|
+
},
|
|
1072
|
+
],
|
|
1073
|
+
isError: true,
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
async function handleNpmMaintainers(args) {
|
|
1078
|
+
try {
|
|
1079
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
1080
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(pkg)}`);
|
|
1081
|
+
if (response.status === 404) {
|
|
1082
|
+
return {
|
|
1083
|
+
name: pkg,
|
|
1084
|
+
error: 'Package not found in the npm registry',
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
if (!response.ok) {
|
|
1088
|
+
throw new Error(`API request failed with status ${response.status} (${response.statusText})`);
|
|
1089
|
+
}
|
|
1090
|
+
const data = await response.json();
|
|
1091
|
+
if (!isNpmPackageInfo(data)) {
|
|
1092
|
+
throw new Error('Invalid package info data received');
|
|
1093
|
+
}
|
|
1094
|
+
return {
|
|
1095
|
+
name: pkg,
|
|
1096
|
+
maintainers: data.maintainers || [],
|
|
1097
|
+
};
|
|
1098
|
+
}));
|
|
1099
|
+
let text = '👥 Package Maintainers\n\n';
|
|
1100
|
+
for (const result of results) {
|
|
1101
|
+
if ('error' in result) {
|
|
1102
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
text += `📦 ${result.name}\n`;
|
|
1106
|
+
text += `${'-'.repeat(40)}\n`;
|
|
1107
|
+
const maintainers = result.maintainers || [];
|
|
1108
|
+
if (maintainers.length === 0) {
|
|
1109
|
+
text += '⚠️ No maintainers found.\n';
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
text += `👥 Maintainers (${maintainers.length}):\n\n`;
|
|
1113
|
+
for (const maintainer of maintainers) {
|
|
1114
|
+
text += `• ${maintainer.name}\n`;
|
|
1115
|
+
text += ` 📧 ${maintainer.email}\n\n`;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (results.indexOf(result) < results.length - 1) {
|
|
1119
|
+
text += '\n';
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
666
1122
|
return {
|
|
667
1123
|
content: [
|
|
668
1124
|
{
|
|
669
1125
|
type: 'text',
|
|
670
|
-
text
|
|
671
|
-
- Overall Score: ${result.score}
|
|
672
|
-
- Issues Resolution Time: ${result.issuesResolutionTime}
|
|
673
|
-
- Commits Frequency: ${result.commitsFrequency}
|
|
674
|
-
- Release Frequency: ${result.releaseFrequency}
|
|
675
|
-
- Last Update: ${new Date(result.lastUpdate).toLocaleDateString()}`,
|
|
1126
|
+
text,
|
|
676
1127
|
},
|
|
677
1128
|
],
|
|
678
1129
|
isError: false,
|
|
@@ -683,41 +1134,331 @@ async function handleNpmMaintenance(args) {
|
|
|
683
1134
|
content: [
|
|
684
1135
|
{
|
|
685
1136
|
type: 'text',
|
|
686
|
-
text: `Error fetching
|
|
1137
|
+
text: `Error fetching package maintainers: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
687
1138
|
},
|
|
688
1139
|
],
|
|
689
1140
|
isError: true,
|
|
690
1141
|
};
|
|
691
1142
|
}
|
|
692
1143
|
}
|
|
693
|
-
|
|
694
|
-
|
|
1144
|
+
async function handleNpmScore(args) {
|
|
1145
|
+
try {
|
|
1146
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
1147
|
+
const response = await fetch(`https://api.npms.io/v2/package/${encodeURIComponent(pkg)}`);
|
|
1148
|
+
if (response.status === 404) {
|
|
1149
|
+
return {
|
|
1150
|
+
name: pkg,
|
|
1151
|
+
error: 'Package not found in the npm registry',
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
if (!response.ok) {
|
|
1155
|
+
throw new Error(`API request failed with status ${response.status} (${response.statusText})`);
|
|
1156
|
+
}
|
|
1157
|
+
const rawData = await response.json();
|
|
1158
|
+
if (!isValidNpmsResponse(rawData)) {
|
|
1159
|
+
return {
|
|
1160
|
+
name: pkg,
|
|
1161
|
+
error: 'Invalid or incomplete response from npms.io API',
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
const { score, collected } = rawData;
|
|
1165
|
+
const { detail } = score;
|
|
1166
|
+
return {
|
|
1167
|
+
name: pkg,
|
|
1168
|
+
score,
|
|
1169
|
+
detail,
|
|
1170
|
+
collected,
|
|
1171
|
+
};
|
|
1172
|
+
}));
|
|
1173
|
+
let text = '📊 Package Scores\n\n';
|
|
1174
|
+
for (const result of results) {
|
|
1175
|
+
if ('error' in result) {
|
|
1176
|
+
text += `❌ ${result.name}: ${result.error}\n\n`;
|
|
1177
|
+
continue;
|
|
1178
|
+
}
|
|
1179
|
+
text += `📦 ${result.name}\n`;
|
|
1180
|
+
text += `${'-'.repeat(40)}\n`;
|
|
1181
|
+
text += `Overall Score: ${(result.score.final * 100).toFixed(1)}%\n\n`;
|
|
1182
|
+
text += '🎯 Quality Breakdown:\n';
|
|
1183
|
+
text += `• Quality: ${(result.detail.quality * 100).toFixed(1)}%\n`;
|
|
1184
|
+
text += `• Maintenance: ${(result.detail.maintenance * 100).toFixed(1)}%\n`;
|
|
1185
|
+
text += `• Popularity: ${(result.detail.popularity * 100).toFixed(1)}%\n\n`;
|
|
1186
|
+
if (result.collected.github) {
|
|
1187
|
+
text += '📈 GitHub Stats:\n';
|
|
1188
|
+
text += `• Stars: ${result.collected.github.starsCount.toLocaleString()}\n`;
|
|
1189
|
+
text += `• Forks: ${result.collected.github.forksCount.toLocaleString()}\n`;
|
|
1190
|
+
text += `• Watchers: ${result.collected.github.subscribersCount.toLocaleString()}\n`;
|
|
1191
|
+
text += `• Total Issues: ${result.collected.github.issues.count.toLocaleString()}\n`;
|
|
1192
|
+
text += `• Open Issues: ${result.collected.github.issues.openCount.toLocaleString()}\n\n`;
|
|
1193
|
+
}
|
|
1194
|
+
if (result.collected.npm?.downloads?.length > 0) {
|
|
1195
|
+
const lastDownloads = result.collected.npm.downloads[0];
|
|
1196
|
+
text += '📥 NPM Downloads:\n';
|
|
1197
|
+
text += `• Last day: ${lastDownloads.count.toLocaleString()} (${new Date(lastDownloads.from).toLocaleDateString()} - ${new Date(lastDownloads.to).toLocaleDateString()})\n\n`;
|
|
1198
|
+
}
|
|
1199
|
+
if (results.indexOf(result) < results.length - 1) {
|
|
1200
|
+
text += '\n';
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
// Retornar en el formato MCP estándar
|
|
1204
|
+
return {
|
|
1205
|
+
content: [
|
|
1206
|
+
{
|
|
1207
|
+
type: 'text',
|
|
1208
|
+
text,
|
|
1209
|
+
},
|
|
1210
|
+
],
|
|
1211
|
+
isError: false,
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
catch (error) {
|
|
1215
|
+
// Manejo de errores en formato MCP estándar
|
|
1216
|
+
return {
|
|
1217
|
+
content: [
|
|
1218
|
+
{
|
|
1219
|
+
type: 'text',
|
|
1220
|
+
text: `Error fetching package scores: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
1221
|
+
},
|
|
1222
|
+
],
|
|
1223
|
+
isError: true,
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
async function handleNpmPackageReadme(args) {
|
|
1228
|
+
try {
|
|
1229
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
1230
|
+
const response = await fetch(`https://registry.npmjs.org/${pkg}`);
|
|
1231
|
+
if (!response.ok) {
|
|
1232
|
+
throw new Error(`Failed to fetch package info: ${response.statusText}`);
|
|
1233
|
+
}
|
|
1234
|
+
const rawData = await response.json();
|
|
1235
|
+
if (!isNpmPackageInfo(rawData)) {
|
|
1236
|
+
throw new Error('Invalid package info data received');
|
|
1237
|
+
}
|
|
1238
|
+
const latestVersion = rawData['dist-tags']?.latest;
|
|
1239
|
+
if (!latestVersion || !rawData.versions?.[latestVersion]) {
|
|
1240
|
+
throw new Error('No latest version found');
|
|
1241
|
+
}
|
|
1242
|
+
const readme = rawData.versions[latestVersion].readme || rawData.readme;
|
|
1243
|
+
if (!readme) {
|
|
1244
|
+
return { name: pkg, version: latestVersion, text: 'No README found' };
|
|
1245
|
+
}
|
|
1246
|
+
return { name: pkg, version: latestVersion, text: readme };
|
|
1247
|
+
}));
|
|
1248
|
+
let text = '';
|
|
1249
|
+
for (const result of results) {
|
|
1250
|
+
text += `${'='.repeat(80)}\n`;
|
|
1251
|
+
text += `📖 ${result.name}@${result.version}\n`;
|
|
1252
|
+
text += `${'='.repeat(80)}\n\n`;
|
|
1253
|
+
text += result.text;
|
|
1254
|
+
if (results.indexOf(result) < results.length - 1) {
|
|
1255
|
+
text += '\n\n';
|
|
1256
|
+
text += `${'='.repeat(80)}\n\n`;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return {
|
|
1260
|
+
content: [{ type: 'text', text }],
|
|
1261
|
+
isError: false,
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
catch (error) {
|
|
1265
|
+
return {
|
|
1266
|
+
content: [
|
|
1267
|
+
{
|
|
1268
|
+
type: 'text',
|
|
1269
|
+
text: `Error fetching READMEs: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
1270
|
+
},
|
|
1271
|
+
],
|
|
1272
|
+
isError: true,
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
async function handleNpmSearch(args) {
|
|
695
1277
|
try {
|
|
696
|
-
const
|
|
1278
|
+
const limit = args.limit || 10;
|
|
1279
|
+
const response = await fetch(`https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(args.query)}&size=${limit}`);
|
|
697
1280
|
if (!response.ok) {
|
|
698
|
-
throw new Error(`Failed to
|
|
699
|
-
}
|
|
700
|
-
const
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
1281
|
+
throw new Error(`Failed to search packages: ${response.statusText}`);
|
|
1282
|
+
}
|
|
1283
|
+
const rawData = await response.json();
|
|
1284
|
+
const parseResult = NpmSearchResultSchema.safeParse(rawData);
|
|
1285
|
+
if (!parseResult.success) {
|
|
1286
|
+
throw new Error('Invalid search results data received');
|
|
1287
|
+
}
|
|
1288
|
+
const { objects, total } = parseResult.data;
|
|
1289
|
+
let text = `🔍 Search results for "${args.query}"\n`;
|
|
1290
|
+
text += `Found ${total.toLocaleString()} packages (showing top ${limit})\n\n`;
|
|
1291
|
+
for (const result of objects) {
|
|
1292
|
+
const pkg = result.package;
|
|
1293
|
+
const score = result.score;
|
|
1294
|
+
text += `📦 ${pkg.name}@${pkg.version}\n`;
|
|
1295
|
+
if (pkg.description)
|
|
1296
|
+
text += `${pkg.description}\n`;
|
|
1297
|
+
// Normalize and format score to ensure it's between 0 and 1
|
|
1298
|
+
const normalizedScore = Math.min(1, score.final / 100);
|
|
1299
|
+
const finalScore = normalizedScore.toFixed(2);
|
|
1300
|
+
text += `Score: ${finalScore} (${(normalizedScore * 100).toFixed(0)}%)\n`;
|
|
1301
|
+
if (pkg.keywords && pkg.keywords.length > 0) {
|
|
1302
|
+
text += `Keywords: ${pkg.keywords.join(', ')}\n`;
|
|
1303
|
+
}
|
|
1304
|
+
if (pkg.links) {
|
|
1305
|
+
text += 'Links:\n';
|
|
1306
|
+
if (pkg.links.npm)
|
|
1307
|
+
text += `• NPM: ${pkg.links.npm}\n`;
|
|
1308
|
+
if (pkg.links.homepage)
|
|
1309
|
+
text += `• Homepage: ${pkg.links.homepage}\n`;
|
|
1310
|
+
if (pkg.links.repository)
|
|
1311
|
+
text += `• Repository: ${pkg.links.repository}\n`;
|
|
1312
|
+
}
|
|
1313
|
+
text += '\n';
|
|
1314
|
+
}
|
|
1315
|
+
return {
|
|
1316
|
+
content: [{ type: 'text', text }],
|
|
1317
|
+
isError: false,
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
catch (error) {
|
|
1321
|
+
return {
|
|
1322
|
+
content: [
|
|
1323
|
+
{
|
|
1324
|
+
type: 'text',
|
|
1325
|
+
text: `Error searching packages: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
1326
|
+
},
|
|
1327
|
+
],
|
|
1328
|
+
isError: true,
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
// License compatibility checker
|
|
1333
|
+
async function handleNpmLicenseCompatibility(args) {
|
|
1334
|
+
try {
|
|
1335
|
+
const licenses = await Promise.all(args.packages.map(async (pkg) => {
|
|
1336
|
+
const response = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
|
|
1337
|
+
if (!response.ok) {
|
|
1338
|
+
throw new Error(`Failed to fetch license info for ${pkg}: ${response.statusText}`);
|
|
1339
|
+
}
|
|
1340
|
+
const data = (await response.json());
|
|
1341
|
+
return {
|
|
1342
|
+
package: pkg,
|
|
1343
|
+
license: data.license || 'UNKNOWN',
|
|
1344
|
+
};
|
|
1345
|
+
}));
|
|
1346
|
+
let text = '📜 License Compatibility Analysis\n\n';
|
|
1347
|
+
text += 'Packages analyzed:\n';
|
|
1348
|
+
for (const { package: pkg, license } of licenses) {
|
|
1349
|
+
text += `• ${pkg}: ${license}\n`;
|
|
1350
|
+
}
|
|
1351
|
+
text += '\n';
|
|
1352
|
+
// Basic license compatibility check
|
|
1353
|
+
const hasGPL = licenses.some(({ license }) => license?.includes('GPL'));
|
|
1354
|
+
const hasMIT = licenses.some(({ license }) => license === 'MIT');
|
|
1355
|
+
const hasApache = licenses.some(({ license }) => license?.includes('Apache'));
|
|
1356
|
+
const hasUnknown = licenses.some(({ license }) => license === 'UNKNOWN');
|
|
1357
|
+
text += 'Compatibility Analysis:\n';
|
|
1358
|
+
if (hasUnknown) {
|
|
1359
|
+
text += '⚠️ Warning: Some packages have unknown licenses. Manual review recommended.\n';
|
|
1360
|
+
}
|
|
1361
|
+
if (hasGPL) {
|
|
1362
|
+
text += '⚠️ Contains GPL licensed code. Resulting work may need to be GPL licensed.\n';
|
|
1363
|
+
if (hasMIT || hasApache) {
|
|
1364
|
+
text += '⚠️ Mixed GPL with MIT/Apache licenses. Review carefully for compliance.\n';
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
else if (hasMIT && hasApache) {
|
|
1368
|
+
text += '✅ MIT and Apache 2.0 licenses are compatible.\n';
|
|
1369
|
+
}
|
|
1370
|
+
else if (hasMIT) {
|
|
1371
|
+
text += '✅ All MIT licensed. Generally safe to use.\n';
|
|
1372
|
+
}
|
|
1373
|
+
else if (hasApache) {
|
|
1374
|
+
text += '✅ All Apache licensed. Generally safe to use.\n';
|
|
1375
|
+
}
|
|
1376
|
+
text +=
|
|
1377
|
+
'\nNote: This is a basic analysis. For legal compliance, please consult with a legal expert.\n';
|
|
1378
|
+
return {
|
|
1379
|
+
content: [{ type: 'text', text }],
|
|
1380
|
+
isError: false,
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
catch (error) {
|
|
709
1384
|
return {
|
|
710
1385
|
content: [
|
|
711
1386
|
{
|
|
712
1387
|
type: 'text',
|
|
713
|
-
text: `
|
|
714
|
-
- Overall Score: ${result.score}
|
|
715
|
-
- GitHub Stars: ${result.stars}
|
|
716
|
-
- Downloads: ${result.downloads}
|
|
717
|
-
- Dependent Packages: ${result.dependents}
|
|
718
|
-
- Community Interest: ${result.communityInterest}`,
|
|
1388
|
+
text: `Error analyzing license compatibility: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
719
1389
|
},
|
|
720
1390
|
],
|
|
1391
|
+
isError: true,
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
// Repository statistics analyzer
|
|
1396
|
+
async function handleNpmRepoStats(args) {
|
|
1397
|
+
try {
|
|
1398
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
1399
|
+
// First get the package info from npm to find the repository URL
|
|
1400
|
+
const npmResponse = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
|
|
1401
|
+
if (!npmResponse.ok) {
|
|
1402
|
+
throw new Error(`Failed to fetch npm info for ${pkg}: ${npmResponse.statusText}`);
|
|
1403
|
+
}
|
|
1404
|
+
const npmData = (await npmResponse.json());
|
|
1405
|
+
if (!npmData.repository?.url) {
|
|
1406
|
+
return { name: pkg, text: `No repository URL found for package ${pkg}` };
|
|
1407
|
+
}
|
|
1408
|
+
// Extract GitHub repo info from URL
|
|
1409
|
+
const repoUrl = npmData.repository.url;
|
|
1410
|
+
const match = repoUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
|
|
1411
|
+
if (!match) {
|
|
1412
|
+
return { name: pkg, text: `Could not parse GitHub repository URL: ${repoUrl}` };
|
|
1413
|
+
}
|
|
1414
|
+
const [, owner, repo] = match;
|
|
1415
|
+
// Fetch repository stats from GitHub API
|
|
1416
|
+
const githubResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
|
|
1417
|
+
headers: {
|
|
1418
|
+
Accept: 'application/vnd.github.v3+json',
|
|
1419
|
+
'User-Agent': 'MCP-Server',
|
|
1420
|
+
},
|
|
1421
|
+
});
|
|
1422
|
+
if (!githubResponse.ok) {
|
|
1423
|
+
throw new Error(`Failed to fetch GitHub stats: ${githubResponse.statusText}`);
|
|
1424
|
+
}
|
|
1425
|
+
const data = (await githubResponse.json());
|
|
1426
|
+
const text = [
|
|
1427
|
+
`${'='.repeat(80)}`,
|
|
1428
|
+
`📊 Repository Statistics for ${pkg}`,
|
|
1429
|
+
`${'='.repeat(80)}\n`,
|
|
1430
|
+
'🌟 Engagement Metrics',
|
|
1431
|
+
`${'─'.repeat(40)}`,
|
|
1432
|
+
`• Stars: ${data.stargazers_count.toLocaleString().padEnd(10)} ⭐`,
|
|
1433
|
+
`• Forks: ${data.forks_count.toLocaleString().padEnd(10)} 🔄`,
|
|
1434
|
+
`• Watchers: ${data.watchers_count.toLocaleString().padEnd(10)} 👀`,
|
|
1435
|
+
`• Open Issues: ${data.open_issues_count.toLocaleString().padEnd(10)} 🔍\n`,
|
|
1436
|
+
'📅 Timeline',
|
|
1437
|
+
`${'─'.repeat(40)}`,
|
|
1438
|
+
`• Created: ${new Date(data.created_at).toLocaleDateString()}`,
|
|
1439
|
+
`• Last Updated: ${new Date(data.updated_at).toLocaleDateString()}\n`,
|
|
1440
|
+
'🔧 Repository Details',
|
|
1441
|
+
`${'─'.repeat(40)}`,
|
|
1442
|
+
`• Default Branch: ${data.default_branch}`,
|
|
1443
|
+
`• Wiki Enabled: ${data.has_wiki ? 'Yes' : 'No'}\n`,
|
|
1444
|
+
'🏷️ Topics',
|
|
1445
|
+
`${'─'.repeat(40)}`,
|
|
1446
|
+
data.topics.length
|
|
1447
|
+
? data.topics.map((topic) => `• ${topic}`).join('\n')
|
|
1448
|
+
: '• No topics found',
|
|
1449
|
+
'',
|
|
1450
|
+
].join('\n');
|
|
1451
|
+
return { name: pkg, text };
|
|
1452
|
+
}));
|
|
1453
|
+
let text = '';
|
|
1454
|
+
for (const result of results) {
|
|
1455
|
+
text += result.text;
|
|
1456
|
+
if (results.indexOf(result) < results.length - 1) {
|
|
1457
|
+
text += '\n\n';
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
return {
|
|
1461
|
+
content: [{ type: 'text', text }],
|
|
721
1462
|
isError: false,
|
|
722
1463
|
};
|
|
723
1464
|
}
|
|
@@ -726,7 +1467,276 @@ async function handleNpmPopularity(args) {
|
|
|
726
1467
|
content: [
|
|
727
1468
|
{
|
|
728
1469
|
type: 'text',
|
|
729
|
-
text: `Error
|
|
1470
|
+
text: `Error analyzing repository stats: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
1471
|
+
},
|
|
1472
|
+
],
|
|
1473
|
+
isError: true,
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
async function handleNpmDeprecated(args) {
|
|
1478
|
+
try {
|
|
1479
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
1480
|
+
const response = await fetch(`https://registry.npmjs.org/${pkg}`);
|
|
1481
|
+
if (!response.ok) {
|
|
1482
|
+
throw new Error(`Failed to fetch package info: ${response.statusText}`);
|
|
1483
|
+
}
|
|
1484
|
+
const rawData = (await response.json());
|
|
1485
|
+
if (!isNpmPackageInfo(rawData)) {
|
|
1486
|
+
throw new Error('Invalid package info data received');
|
|
1487
|
+
}
|
|
1488
|
+
// Get latest version info
|
|
1489
|
+
const latestVersion = rawData['dist-tags']?.latest;
|
|
1490
|
+
if (!latestVersion || !rawData.versions?.[latestVersion]) {
|
|
1491
|
+
throw new Error('No latest version found');
|
|
1492
|
+
}
|
|
1493
|
+
const latestVersionInfo = rawData.versions[latestVersion];
|
|
1494
|
+
const dependencies = {
|
|
1495
|
+
...(latestVersionInfo.dependencies || {}),
|
|
1496
|
+
...(latestVersionInfo.devDependencies || {}),
|
|
1497
|
+
...(latestVersionInfo.peerDependencies || {}),
|
|
1498
|
+
};
|
|
1499
|
+
// Check each dependency
|
|
1500
|
+
const deprecatedDeps = [];
|
|
1501
|
+
await Promise.all(Object.entries(dependencies).map(async ([dep, version]) => {
|
|
1502
|
+
try {
|
|
1503
|
+
const depResponse = await fetch(`https://registry.npmjs.org/${dep}`);
|
|
1504
|
+
if (!depResponse.ok)
|
|
1505
|
+
return;
|
|
1506
|
+
const depData = (await depResponse.json());
|
|
1507
|
+
const depVersion = version.replace(/[^0-9.]/g, '');
|
|
1508
|
+
if (depData.versions?.[depVersion]?.deprecated) {
|
|
1509
|
+
deprecatedDeps.push({
|
|
1510
|
+
name: dep,
|
|
1511
|
+
version: depVersion,
|
|
1512
|
+
message: depData.versions[depVersion].deprecated || 'No message provided',
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
catch (error) {
|
|
1517
|
+
console.error(`Error checking ${dep}:`, error);
|
|
1518
|
+
}
|
|
1519
|
+
}));
|
|
1520
|
+
// Check if the package itself is deprecated
|
|
1521
|
+
const isDeprecated = latestVersionInfo.deprecated;
|
|
1522
|
+
let text = `📦 Deprecation Check for ${pkg}@${latestVersion}\n\n`;
|
|
1523
|
+
if (isDeprecated) {
|
|
1524
|
+
text += '⚠️ WARNING: This package is deprecated!\n';
|
|
1525
|
+
text += `Deprecation message: ${latestVersionInfo.deprecated}\n\n`;
|
|
1526
|
+
}
|
|
1527
|
+
else {
|
|
1528
|
+
text += '✅ This package is not deprecated\n\n';
|
|
1529
|
+
}
|
|
1530
|
+
if (deprecatedDeps.length > 0) {
|
|
1531
|
+
text += `Found ${deprecatedDeps.length} deprecated dependencies:\n\n`;
|
|
1532
|
+
for (const dep of deprecatedDeps) {
|
|
1533
|
+
text += `⚠️ ${dep.name}@${dep.version}\n`;
|
|
1534
|
+
text += ` Message: ${dep.message}\n\n`;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
else {
|
|
1538
|
+
text += '✅ No deprecated dependencies found\n';
|
|
1539
|
+
}
|
|
1540
|
+
return { name: pkg, text };
|
|
1541
|
+
}));
|
|
1542
|
+
let text = '';
|
|
1543
|
+
for (const result of results) {
|
|
1544
|
+
text += result.text;
|
|
1545
|
+
}
|
|
1546
|
+
return {
|
|
1547
|
+
content: [{ type: 'text', text }],
|
|
1548
|
+
isError: false,
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
catch (error) {
|
|
1552
|
+
return {
|
|
1553
|
+
content: [
|
|
1554
|
+
{
|
|
1555
|
+
type: 'text',
|
|
1556
|
+
text: `Error checking deprecated packages: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
1557
|
+
},
|
|
1558
|
+
],
|
|
1559
|
+
isError: true,
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
async function handleNpmChangelogAnalysis(args) {
|
|
1564
|
+
try {
|
|
1565
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
1566
|
+
// First get the package info from npm to find the repository URL
|
|
1567
|
+
const npmResponse = await fetch(`https://registry.npmjs.org/${pkg}`);
|
|
1568
|
+
if (!npmResponse.ok) {
|
|
1569
|
+
throw new Error(`Failed to fetch npm info for ${pkg}: ${npmResponse.statusText}`);
|
|
1570
|
+
}
|
|
1571
|
+
const npmData = await npmResponse.json();
|
|
1572
|
+
if (!isNpmPackageInfo(npmData)) {
|
|
1573
|
+
throw new Error('Invalid package info data received');
|
|
1574
|
+
}
|
|
1575
|
+
const repository = npmData.repository?.url;
|
|
1576
|
+
if (!repository) {
|
|
1577
|
+
return { name: pkg, text: `No repository found for package ${pkg}` };
|
|
1578
|
+
}
|
|
1579
|
+
// Extract GitHub repo info from URL
|
|
1580
|
+
const match = repository.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
|
|
1581
|
+
if (!match) {
|
|
1582
|
+
return { name: pkg, text: `Could not parse GitHub repository URL: ${repository}` };
|
|
1583
|
+
}
|
|
1584
|
+
const [, owner, repo] = match;
|
|
1585
|
+
// Check common changelog file names
|
|
1586
|
+
const changelogFiles = [
|
|
1587
|
+
'CHANGELOG.md',
|
|
1588
|
+
'changelog.md',
|
|
1589
|
+
'CHANGES.md',
|
|
1590
|
+
'changes.md',
|
|
1591
|
+
'HISTORY.md',
|
|
1592
|
+
'history.md',
|
|
1593
|
+
'NEWS.md',
|
|
1594
|
+
'news.md',
|
|
1595
|
+
'RELEASES.md',
|
|
1596
|
+
'releases.md',
|
|
1597
|
+
];
|
|
1598
|
+
let changelog = null;
|
|
1599
|
+
for (const file of changelogFiles) {
|
|
1600
|
+
try {
|
|
1601
|
+
const response = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/master/${file}`);
|
|
1602
|
+
if (response.ok) {
|
|
1603
|
+
changelog = await response.text();
|
|
1604
|
+
break;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
catch (error) {
|
|
1608
|
+
console.error(`Error fetching ${file}:`, error);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
// Get release information from GitHub API
|
|
1612
|
+
const githubResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases`, {
|
|
1613
|
+
headers: {
|
|
1614
|
+
Accept: 'application/vnd.github.v3+json',
|
|
1615
|
+
'User-Agent': 'MCP-Server',
|
|
1616
|
+
},
|
|
1617
|
+
});
|
|
1618
|
+
const releases = (githubResponse.ok ? await githubResponse.json() : []);
|
|
1619
|
+
let text = `📋 Changelog Analysis for ${pkg}\n\n`;
|
|
1620
|
+
// Analyze version history from npm
|
|
1621
|
+
const versions = Object.keys(npmData.versions || {}).sort((a, b) => {
|
|
1622
|
+
const [aMajor = 0, aMinor = 0] = a.split('.').map(Number);
|
|
1623
|
+
const [bMajor = 0, bMinor = 0] = b.split('.').map(Number);
|
|
1624
|
+
return bMajor - aMajor || bMinor - aMinor;
|
|
1625
|
+
});
|
|
1626
|
+
text += '📦 Version History:\n';
|
|
1627
|
+
text += `• Total versions: ${versions.length}\n`;
|
|
1628
|
+
text += `• Latest version: ${versions[0]}\n`;
|
|
1629
|
+
text += `• First version: ${versions[versions.length - 1]}\n\n`;
|
|
1630
|
+
if (changelog) {
|
|
1631
|
+
text += '📝 Changelog found!\n\n';
|
|
1632
|
+
// Extract and analyze the last few versions from changelog
|
|
1633
|
+
const recentChanges = changelog.split('\n').slice(0, 20).join('\n');
|
|
1634
|
+
text += `Recent changes:\n${recentChanges}\n...\n\n`;
|
|
1635
|
+
}
|
|
1636
|
+
else {
|
|
1637
|
+
text += '⚠️ No changelog file found in repository root\n\n';
|
|
1638
|
+
}
|
|
1639
|
+
if (releases.length > 0) {
|
|
1640
|
+
text += '🚀 Recent GitHub Releases:\n\n';
|
|
1641
|
+
for (const release of releases.slice(0, 5)) {
|
|
1642
|
+
text += `${release.tag_name || 'No tag'}\n`;
|
|
1643
|
+
if (release.name)
|
|
1644
|
+
text += `Title: ${release.name}\n`;
|
|
1645
|
+
if (release.published_at)
|
|
1646
|
+
text += `Published: ${new Date(release.published_at).toLocaleDateString()}\n`;
|
|
1647
|
+
text += '\n';
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
else {
|
|
1651
|
+
text += 'ℹ️ No GitHub releases found\n';
|
|
1652
|
+
}
|
|
1653
|
+
return { name: pkg, text };
|
|
1654
|
+
}));
|
|
1655
|
+
let text = '';
|
|
1656
|
+
for (const result of results) {
|
|
1657
|
+
text += result.text;
|
|
1658
|
+
}
|
|
1659
|
+
return {
|
|
1660
|
+
content: [{ type: 'text', text }],
|
|
1661
|
+
isError: false,
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
catch (error) {
|
|
1665
|
+
return {
|
|
1666
|
+
content: [
|
|
1667
|
+
{
|
|
1668
|
+
type: 'text',
|
|
1669
|
+
text: `Error analyzing changelog: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
1670
|
+
},
|
|
1671
|
+
],
|
|
1672
|
+
isError: true,
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
async function handleNpmAlternatives(args) {
|
|
1677
|
+
try {
|
|
1678
|
+
const results = await Promise.all(args.packages.map(async (pkg) => {
|
|
1679
|
+
const response = await fetch(`https://registry.npmjs.org/-/v1/search?text=keywords:${pkg}&size=10`);
|
|
1680
|
+
if (!response.ok) {
|
|
1681
|
+
throw new Error(`Failed to search for alternatives: ${response.statusText}`);
|
|
1682
|
+
}
|
|
1683
|
+
const data = (await response.json());
|
|
1684
|
+
const alternatives = data.objects;
|
|
1685
|
+
const downloadCounts = await Promise.all(alternatives.map(async (alt) => {
|
|
1686
|
+
try {
|
|
1687
|
+
const response = await fetch(`https://api.npmjs.org/downloads/point/last-month/${alt.package.name}`);
|
|
1688
|
+
if (!response.ok)
|
|
1689
|
+
return 0;
|
|
1690
|
+
const downloadData = (await response.json());
|
|
1691
|
+
return downloadData.downloads;
|
|
1692
|
+
}
|
|
1693
|
+
catch (error) {
|
|
1694
|
+
console.error(`Error fetching download count for ${alt.package.name}:`, error);
|
|
1695
|
+
return 0;
|
|
1696
|
+
}
|
|
1697
|
+
}));
|
|
1698
|
+
// Get original package downloads for comparison
|
|
1699
|
+
const originalDownloads = await fetch(`https://api.npmjs.org/downloads/point/last-month/${pkg}`)
|
|
1700
|
+
.then((res) => res.json())
|
|
1701
|
+
.then((data) => data.downloads)
|
|
1702
|
+
.catch(() => 0);
|
|
1703
|
+
let text = `🔄 Alternative Packages to ${pkg}\n\n`;
|
|
1704
|
+
text += 'Original package:\n';
|
|
1705
|
+
text += `📦 ${pkg}\n`;
|
|
1706
|
+
text += `Downloads: ${originalDownloads.toLocaleString()}/month\n`;
|
|
1707
|
+
text += `Keywords: ${alternatives[0].package.keywords?.join(', ')}\n\n`;
|
|
1708
|
+
text += 'Alternative packages found:\n\n';
|
|
1709
|
+
alternatives.forEach((alt, index) => {
|
|
1710
|
+
const downloads = downloadCounts[index];
|
|
1711
|
+
const score = alt.score.final;
|
|
1712
|
+
text += `${index + 1}. 📦 ${alt.package.name}\n`;
|
|
1713
|
+
if (alt.package.description)
|
|
1714
|
+
text += ` ${alt.package.description}\n`;
|
|
1715
|
+
text += ` Downloads: ${downloads.toLocaleString()}/month\n`;
|
|
1716
|
+
text += ` Score: ${(score * 100).toFixed(0)}%\n`;
|
|
1717
|
+
if (alt.package.links?.repository)
|
|
1718
|
+
text += ` Repo: ${alt.package.links.repository}\n`;
|
|
1719
|
+
if (alt.package.keywords?.length)
|
|
1720
|
+
text += ` Keywords: ${alt.package.keywords.join(', ')}\n`;
|
|
1721
|
+
text += '\n';
|
|
1722
|
+
});
|
|
1723
|
+
return { name: pkg, text };
|
|
1724
|
+
}));
|
|
1725
|
+
let text = '';
|
|
1726
|
+
for (const result of results) {
|
|
1727
|
+
text += result.text;
|
|
1728
|
+
}
|
|
1729
|
+
return {
|
|
1730
|
+
content: [{ type: 'text', text }],
|
|
1731
|
+
isError: false,
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
catch (error) {
|
|
1735
|
+
return {
|
|
1736
|
+
content: [
|
|
1737
|
+
{
|
|
1738
|
+
type: 'text',
|
|
1739
|
+
text: `Error finding alternatives: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
730
1740
|
},
|
|
731
1741
|
],
|
|
732
1742
|
isError: true,
|
|
@@ -739,32 +1749,114 @@ const server = new McpServer({
|
|
|
739
1749
|
version: '1.0.0',
|
|
740
1750
|
});
|
|
741
1751
|
// Add NPM tools
|
|
742
|
-
server.tool('npmVersions', {
|
|
743
|
-
|
|
1752
|
+
server.tool('npmVersions', {
|
|
1753
|
+
packages: z.array(z.string()).describe('List of package names to get versions for'),
|
|
1754
|
+
}, async (args) => {
|
|
1755
|
+
return await handleNpmVersions(args);
|
|
744
1756
|
});
|
|
745
|
-
server.tool('npmLatest', {
|
|
746
|
-
|
|
1757
|
+
server.tool('npmLatest', {
|
|
1758
|
+
packages: z.array(z.string()).describe('List of package names to get latest versions for'),
|
|
1759
|
+
}, async (args) => {
|
|
1760
|
+
return await handleNpmLatest(args);
|
|
747
1761
|
});
|
|
748
|
-
server.tool('npmDeps', {
|
|
749
|
-
|
|
1762
|
+
server.tool('npmDeps', {
|
|
1763
|
+
packages: z.array(z.string()).describe('List of package names to analyze dependencies for'),
|
|
1764
|
+
}, async (args) => {
|
|
1765
|
+
return await handleNpmDeps(args);
|
|
750
1766
|
});
|
|
751
|
-
server.tool('npmTypes', {
|
|
752
|
-
|
|
1767
|
+
server.tool('npmTypes', {
|
|
1768
|
+
packages: z.array(z.string()).describe('List of package names to check types for'),
|
|
1769
|
+
}, async (args) => {
|
|
1770
|
+
return await handleNpmTypes(args);
|
|
753
1771
|
});
|
|
754
|
-
server.tool('npmSize', {
|
|
755
|
-
|
|
1772
|
+
server.tool('npmSize', {
|
|
1773
|
+
packages: z.array(z.string()).describe('List of package names to get size information for'),
|
|
1774
|
+
}, async (args) => {
|
|
1775
|
+
return await handleNpmSize(args);
|
|
756
1776
|
});
|
|
757
|
-
server.tool('npmVulnerabilities', {
|
|
758
|
-
|
|
1777
|
+
server.tool('npmVulnerabilities', {
|
|
1778
|
+
packages: z.array(z.string()).describe('List of package names to check for vulnerabilities'),
|
|
1779
|
+
}, async (args) => {
|
|
1780
|
+
return await handleNpmVulnerabilities(args);
|
|
759
1781
|
});
|
|
760
1782
|
server.tool('npmTrends', {
|
|
761
|
-
|
|
762
|
-
period: z
|
|
763
|
-
|
|
764
|
-
|
|
1783
|
+
packages: z.array(z.string()).describe('List of package names to get trends for'),
|
|
1784
|
+
period: z
|
|
1785
|
+
.enum(['last-week', 'last-month', 'last-year'])
|
|
1786
|
+
.describe('Time period for trends. Options: "last-week", "last-month", "last-year"')
|
|
1787
|
+
.optional()
|
|
1788
|
+
.default('last-month'),
|
|
1789
|
+
}, async (args) => {
|
|
1790
|
+
return await handleNpmTrends(args);
|
|
1791
|
+
});
|
|
1792
|
+
server.tool('npmCompare', {
|
|
1793
|
+
packages: z.array(z.string()).describe('List of package names to compare'),
|
|
1794
|
+
}, async (args) => {
|
|
1795
|
+
return await handleNpmCompare(args);
|
|
1796
|
+
});
|
|
1797
|
+
server.tool('npmMaintainers', {
|
|
1798
|
+
packages: z.array(z.string()).describe('List of package names to get maintainers for'),
|
|
1799
|
+
}, async (args) => {
|
|
1800
|
+
return await handleNpmMaintainers(args);
|
|
1801
|
+
});
|
|
1802
|
+
server.tool('npmScore', {
|
|
1803
|
+
packages: z.array(z.string()).describe('List of package names to get scores for'),
|
|
1804
|
+
}, async (args) => {
|
|
1805
|
+
return await handleNpmScore(args);
|
|
1806
|
+
});
|
|
1807
|
+
server.tool('npmPackageReadme', {
|
|
1808
|
+
packages: z.array(z.string()).describe('List of package names to get READMEs for'),
|
|
1809
|
+
}, async (args) => {
|
|
1810
|
+
return await handleNpmPackageReadme(args);
|
|
1811
|
+
});
|
|
1812
|
+
server.tool('npmSearch', {
|
|
1813
|
+
query: z.string().describe('Search query for packages'),
|
|
1814
|
+
limit: z
|
|
1815
|
+
.number()
|
|
1816
|
+
.min(1)
|
|
1817
|
+
.max(50)
|
|
1818
|
+
.optional()
|
|
1819
|
+
.describe('Maximum number of results to return (default: 10)'),
|
|
1820
|
+
}, async (args) => {
|
|
1821
|
+
return await handleNpmSearch(args);
|
|
1822
|
+
});
|
|
1823
|
+
server.tool('npmLicenseCompatibility', {
|
|
1824
|
+
packages: z
|
|
1825
|
+
.array(z.string())
|
|
1826
|
+
.min(1)
|
|
1827
|
+
.describe('List of package names to check for license compatibility'),
|
|
1828
|
+
}, async (args) => {
|
|
1829
|
+
return await handleNpmLicenseCompatibility(args);
|
|
1830
|
+
});
|
|
1831
|
+
server.tool('npmRepoStats', {
|
|
1832
|
+
packages: z.array(z.string()).describe('List of package names to get repository stats for'),
|
|
1833
|
+
}, async (args) => {
|
|
1834
|
+
return await handleNpmRepoStats(args);
|
|
1835
|
+
});
|
|
1836
|
+
server.tool('npmDeprecated', {
|
|
1837
|
+
packages: z.array(z.string()).describe('List of package names to check for deprecation'),
|
|
1838
|
+
}, async (args) => {
|
|
1839
|
+
return await handleNpmDeprecated(args);
|
|
1840
|
+
});
|
|
1841
|
+
server.tool('npmChangelogAnalysis', {
|
|
1842
|
+
packages: z.array(z.string()).describe('List of package names to analyze changelogs for'),
|
|
1843
|
+
}, async (args) => {
|
|
1844
|
+
return await handleNpmChangelogAnalysis(args);
|
|
1845
|
+
});
|
|
1846
|
+
server.tool('npmAlternatives', {
|
|
1847
|
+
packages: z.array(z.string()).describe('List of package names to find alternatives for'),
|
|
1848
|
+
}, async (args) => {
|
|
1849
|
+
return await handleNpmAlternatives(args);
|
|
1850
|
+
});
|
|
1851
|
+
server.tool('npmQuality', {
|
|
1852
|
+
packages: z.array(z.string()).describe('List of package names to analyze'),
|
|
1853
|
+
}, async (args) => {
|
|
1854
|
+
return await handleNpmQuality(args);
|
|
765
1855
|
});
|
|
766
|
-
server.tool('
|
|
767
|
-
|
|
1856
|
+
server.tool('npmMaintenance', {
|
|
1857
|
+
packages: z.array(z.string()).describe('List of package names to analyze'),
|
|
1858
|
+
}, async (args) => {
|
|
1859
|
+
return await handleNpmMaintenance(args);
|
|
768
1860
|
});
|
|
769
1861
|
// Start receiving messages on stdin and sending messages on stdout
|
|
770
1862
|
const transport = new StdioServerTransport();
|