@nekzus/mcp-server 1.16.2 → 1.18.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
@@ -699,13 +699,19 @@ export async function handleNpmDeps(args) {
699
699
  version: depVersion,
700
700
  }));
701
701
  };
702
+ const actualVersion = rawData.version || version; // Use version from response if available
703
+ const finalPackageName = `${name}@${actualVersion}`;
704
+ // Fetch transitive dependencies from deps.dev to provide deep topological insights
705
+ const transitiveGraphRaw = await fetchTransitiveDependenciesFromDepsDev(name, actualVersion);
706
+ // Erase root package from the graph to avoid self-counting if returned
707
+ const transitiveGraph = transitiveGraphRaw.filter((dep) => dep.name !== name);
702
708
  const depData = {
703
709
  dependencies: mapDeps(rawData.dependencies),
704
710
  devDependencies: mapDeps(rawData.devDependencies),
705
711
  peerDependencies: mapDeps(rawData.peerDependencies),
712
+ transitiveCount: transitiveGraph.length,
713
+ transitiveGraph: transitiveGraph,
706
714
  };
707
- const actualVersion = rawData.version || version; // Use version from response if available
708
- const finalPackageName = `${name}@${actualVersion}`;
709
715
  // Store with the actual resolved package name if 'latest' was used
710
716
  cacheSet(cacheKey, { depData, packageNameForCache: finalPackageName }, CACHE_TTL_MEDIUM);
711
717
  return {
@@ -713,7 +719,7 @@ export async function handleNpmDeps(args) {
713
719
  status: 'success',
714
720
  error: null,
715
721
  data: depData,
716
- message: `Dependencies for ${finalPackageName}`,
722
+ message: `Dependencies for ${finalPackageName} (Direct: ${depData.dependencies.length}, Transitive: ${depData.transitiveCount})`,
717
723
  };
718
724
  }
719
725
  catch (error) {
@@ -1009,23 +1015,32 @@ export async function handleNpmSize(args) {
1009
1015
  };
1010
1016
  }
1011
1017
  }
1012
- // Helper to detect dependencies
1013
- async function getPackageDependencies(pkgName, version) {
1018
+ // Helper to fetch full transitive dependency graph from deps.dev
1019
+ async function fetchTransitiveDependenciesFromDepsDev(pkgName, version) {
1014
1020
  try {
1015
- const response = await fetch(`${NPM_REGISTRY_URL}/${pkgName}/${version}`, {
1021
+ const encodedName = encodeURIComponent(pkgName);
1022
+ const encodedVersion = encodeURIComponent(version);
1023
+ const url = `https://api.deps.dev/v3/systems/npm/packages/${encodedName}/versions/${encodedVersion}:dependencies`;
1024
+ const response = await fetch(url, {
1016
1025
  headers: { Accept: 'application/json', 'User-Agent': 'NPM-Sentinel-MCP' },
1017
1026
  });
1018
- if (!response.ok)
1019
- return { dependencies: {}, devDependencies: {} };
1027
+ if (!response.ok) {
1028
+ console.warn(`deps.dev API returned ${response.status} for ${pkgName}@${version}`);
1029
+ return [];
1030
+ }
1020
1031
  const data = (await response.json());
1021
- return {
1022
- dependencies: data.dependencies || {},
1023
- devDependencies: data.devDependencies || {},
1024
- };
1032
+ if (!data.nodes || !Array.isArray(data.nodes))
1033
+ return [];
1034
+ return data.nodes
1035
+ .filter((node) => node.versionKey?.name)
1036
+ .map((node) => ({
1037
+ name: node.versionKey.name,
1038
+ version: node.versionKey.version,
1039
+ }));
1025
1040
  }
1026
1041
  catch (error) {
1027
- console.error(`Error fetching dependencies for ${pkgName}:`, error);
1028
- return { dependencies: {}, devDependencies: {} };
1042
+ console.error(`Error fetching transitive dependencies from deps.dev for ${pkgName}:`, error);
1043
+ return [];
1029
1044
  }
1030
1045
  }
1031
1046
  // Helper to resolve 'latest' tag to actual version number
@@ -1107,31 +1122,19 @@ export async function handleNpmVulnerabilities(args) {
1107
1122
  });
1108
1123
  }
1109
1124
  };
1110
- // Recursive dependency fetcher
1111
- const seenPackages = new Set();
1112
- const depthLimit = 2; // Default depth limit for transitive scanning
1113
- const processPackage = async (name, version, currentDepth) => {
1114
- const pkgKey = `${name}@${version || 'latest'}`;
1115
- if (seenPackages.has(pkgKey))
1116
- return;
1117
- seenPackages.add(pkgKey);
1118
- // Add to vulnerability query
1119
- // For the main package (depth 0), isDependency is false. For others, true.
1120
- addToQuery(name, version || 'latest', currentDepth > 0);
1121
- if (currentDepth >= depthLimit)
1122
- return;
1123
- // If version is 'latest' or undefined, we settled it in addToQuery, but for fetching deps we need a concrete version
1124
- // If we don't have one, we might need to resolve it again or let getPackageDependencies handle 'latest'
1125
- // getPackageDependencies handles 'latest' by default.
1126
- const { dependencies } = await getPackageDependencies(name, version || 'latest');
1127
- // Process children
1128
- const children = Object.entries(dependencies);
1129
- await Promise.all(children.map(async ([depName, depVersion]) => {
1130
- const cleanVersion = depVersion.replace(/[\^~]/g, '');
1131
- if (/^\d+\.\d+\.\d+/.test(cleanVersion)) {
1132
- await processPackage(depName, cleanVersion, currentDepth + 1);
1133
- }
1134
- }));
1125
+ const processPackage = async (name, version) => {
1126
+ const safeVersion = version || 'latest';
1127
+ // Always add the root package (depth 0 logic)
1128
+ addToQuery(name, safeVersion, false);
1129
+ // Try to get all transitive dependencies in a single call to deps.dev
1130
+ // They require a concrete version (or we pass exactly what we have)
1131
+ const allDeps = await fetchTransitiveDependenciesFromDepsDev(name, safeVersion);
1132
+ for (const dep of allDeps) {
1133
+ // Avoid adding the root package itself again, which is included in the graph
1134
+ if (dep.name === name)
1135
+ continue;
1136
+ addToQuery(dep.name, dep.version, true);
1137
+ }
1135
1138
  };
1136
1139
  const validationErrors = [];
1137
1140
  const validPackagesToProcess = packagesToProcess.filter((pkgInput) => {
@@ -1203,14 +1206,12 @@ export async function handleNpmVulnerabilities(args) {
1203
1206
  version = resolved;
1204
1207
  }
1205
1208
  }
1206
- // Start recursive processing
1207
- await processPackage(name, version, 0);
1209
+ // Start fetching dependencies with deps.dev
1210
+ await processPackage(name, version);
1208
1211
  // Ecosystem Check: Scan associated packages that share the same version
1209
1212
  if (ECOSYSTEM_MAP[name]) {
1210
1213
  for (const associatedPkg of ECOSYSTEM_MAP[name]) {
1211
- // We scan these as "depth 0" (explicitly requested by ecosystem logic)
1212
- // so they are checked fully.
1213
- await processPackage(associatedPkg, version, 0);
1214
+ await processPackage(associatedPkg, version);
1214
1215
  }
1215
1216
  }
1216
1217
  }));
@@ -2197,7 +2198,7 @@ export async function handleNpmPackageReadme(args) {
2197
2198
  }
2198
2199
  const processedResults = await Promise.all(packagesToProcess.map(async (pkgInput) => {
2199
2200
  let name = '';
2200
- let versionTag = undefined; // Explicitly undefined if not specified
2201
+ let versionTag; // Explicitly undefined if not specified
2201
2202
  if (typeof pkgInput === 'string') {
2202
2203
  const atIdx = pkgInput.lastIndexOf('@');
2203
2204
  if (atIdx > 0) {
@@ -2720,7 +2721,7 @@ export async function handleNpmRepoStats(args) {
2720
2721
  cacheSet(cacheKey, resultNoRepo, CACHE_TTL_LONG);
2721
2722
  return resultNoRepo;
2722
2723
  }
2723
- const githubMatch = repoUrl.match(/github\.com[:\/]([^\/]+)\/([^\/.]+)/);
2724
+ const githubMatch = repoUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
2724
2725
  if (!githubMatch) {
2725
2726
  const resultNotGitHub = {
2726
2727
  packageInput: pkgInput,
@@ -3021,7 +3022,7 @@ export async function handleNpmChangelogAnalysis(args) {
3021
3022
  }
3022
3023
  const processedResults = await Promise.all(packagesToProcess.map(async (pkgInput) => {
3023
3024
  let name = '';
3024
- let versionQueried = undefined;
3025
+ let versionQueried;
3025
3026
  if (typeof pkgInput === 'string') {
3026
3027
  const atIdx = pkgInput.lastIndexOf('@');
3027
3028
  if (atIdx > 0) {
@@ -3118,7 +3119,7 @@ export async function handleNpmChangelogAnalysis(args) {
3118
3119
  cacheSet(cacheKey, resultNoRepo, CACHE_TTL_MEDIUM);
3119
3120
  return resultNoRepo;
3120
3121
  }
3121
- const githubMatch = repositoryUrl.match(/github\.com[:\/]([^\/]+)\/([^\/.]+)/);
3122
+ const githubMatch = repositoryUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
3122
3123
  if (!githubMatch) {
3123
3124
  const resultNotGitHub = {
3124
3125
  packageInput: pkgInput,
@@ -3257,7 +3258,7 @@ export async function handleNpmAlternatives(args) {
3257
3258
  }
3258
3259
  const processedResults = await Promise.all(packagesToProcess.map(async (pkgInput) => {
3259
3260
  let originalPackageName = '';
3260
- let versionQueried = undefined;
3261
+ let versionQueried;
3261
3262
  if (typeof pkgInput === 'string') {
3262
3263
  const atIdx = pkgInput.lastIndexOf('@');
3263
3264
  if (atIdx > 0) {
@@ -3432,7 +3433,7 @@ export const configSchema = z.object({
3432
3433
  NPM_REGISTRY_URL: z.string().optional().describe('URL of the NPM registry to use'),
3433
3434
  });
3434
3435
  // Create server function for Smithery CLI
3435
- export default function createServer({ config, }) {
3436
+ export default function createServer({ config }) {
3436
3437
  // Apply config overrides
3437
3438
  if (config.NPM_REGISTRY_URL) {
3438
3439
  NPM_REGISTRY_URL = config.NPM_REGISTRY_URL.replace(/\/$/, '');