@yawlabs/npmjs-mcp 0.2.0 → 0.4.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.
Files changed (2) hide show
  1. package/dist/index.js +295 -100
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21087,6 +21087,78 @@ function downloadsGet(path) {
21087
21087
  function replicateGet(path) {
21088
21088
  return request(REPLICATE_URL, path);
21089
21089
  }
21090
+ function parseSemver(v) {
21091
+ const m = v.match(/^(\d+)\.(\d+)\.(\d+)/);
21092
+ return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
21093
+ }
21094
+ function cmpSemver(a, b) {
21095
+ for (let i = 0; i < 3; i++) {
21096
+ if (a[i] < b[i]) return -1;
21097
+ if (a[i] > b[i]) return 1;
21098
+ }
21099
+ return 0;
21100
+ }
21101
+ function maxSatisfying(versions, range) {
21102
+ const r = range.trim().replace(/^v/, "");
21103
+ if (versions.includes(r)) return r;
21104
+ let minInclusive = null;
21105
+ let maxExclusive = null;
21106
+ if (r.startsWith("^")) {
21107
+ const base = parseSemver(r.slice(1));
21108
+ if (!base) return null;
21109
+ minInclusive = base;
21110
+ if (base[0] > 0) maxExclusive = [base[0] + 1, 0, 0];
21111
+ else if (base[1] > 0) maxExclusive = [0, base[1] + 1, 0];
21112
+ else maxExclusive = [0, 0, base[2] + 1];
21113
+ } else if (r.startsWith("~")) {
21114
+ const base = parseSemver(r.slice(1));
21115
+ if (!base) return null;
21116
+ minInclusive = base;
21117
+ maxExclusive = [base[0], base[1] + 1, 0];
21118
+ } else if (r.startsWith(">=")) {
21119
+ const base = parseSemver(r.slice(2));
21120
+ if (!base) return null;
21121
+ minInclusive = base;
21122
+ } else if (r.startsWith("<=")) {
21123
+ const base = parseSemver(r.slice(2));
21124
+ if (!base) return null;
21125
+ maxExclusive = [base[0], base[1], base[2] + 1];
21126
+ } else {
21127
+ const xm = r.match(/^(\d+)(?:\.(\d+|x)(?:\.(\d+|x))?)?$/);
21128
+ if (xm) {
21129
+ const major = Number(xm[1]);
21130
+ const minor = xm[2] !== void 0 && xm[2] !== "x" ? Number(xm[2]) : null;
21131
+ if (minor === null) {
21132
+ minInclusive = [major, 0, 0];
21133
+ maxExclusive = [major + 1, 0, 0];
21134
+ } else {
21135
+ const patch = xm[3] !== void 0 && xm[3] !== "x" ? Number(xm[3]) : null;
21136
+ if (patch === null) {
21137
+ minInclusive = [major, minor, 0];
21138
+ maxExclusive = [major, minor + 1, 0];
21139
+ } else {
21140
+ return null;
21141
+ }
21142
+ }
21143
+ } else {
21144
+ return null;
21145
+ }
21146
+ }
21147
+ let best = null;
21148
+ let bestParsed = null;
21149
+ for (const v of versions) {
21150
+ if (v.includes("-") && !r.includes("-")) continue;
21151
+ const parsed = parseSemver(v);
21152
+ if (!parsed) continue;
21153
+ if (minInclusive && cmpSemver(parsed, minInclusive) < 0) continue;
21154
+ if (maxExclusive && cmpSemver(parsed, maxExclusive) >= 0) continue;
21155
+ if (!bestParsed || cmpSemver(parsed, bestParsed) > 0) {
21156
+ best = v;
21157
+ bestParsed = parsed;
21158
+ }
21159
+ }
21160
+ return best;
21161
+ }
21090
21162
 
21091
21163
  // src/tools/access.ts
21092
21164
  var accessTools = [
@@ -21184,12 +21256,9 @@ var analysisTools = [
21184
21256
  handler: async (input) => {
21185
21257
  const results = await Promise.all(
21186
21258
  input.packages.map(async (name) => {
21187
- const [pkgRes, dlRes, auditRes] = await Promise.all([
21259
+ const [pkgRes, dlRes] = await Promise.all([
21188
21260
  registryGet(`/${encPkg(name)}`),
21189
- downloadsGet(`/downloads/point/last-week/${encPkg(name)}`),
21190
- registryPost("/-/npm/v1/security/advisories/bulk", {
21191
- [name]: ["latest"]
21192
- })
21261
+ downloadsGet(`/downloads/point/last-week/${encPkg(name)}`)
21193
21262
  ]);
21194
21263
  if (!pkgRes.ok) {
21195
21264
  return { name, error: pkgRes.error };
@@ -21198,6 +21267,15 @@ var analysisTools = [
21198
21267
  const latest = pkg["dist-tags"]?.latest;
21199
21268
  const latestVersion = latest ? pkg.versions[latest] : void 0;
21200
21269
  const versionKeys = Object.keys(pkg.versions);
21270
+ let vulnerabilities = 0;
21271
+ if (latest) {
21272
+ const auditRes = await registryPost("/-/npm/v1/security/advisories/bulk", {
21273
+ [name]: [latest]
21274
+ });
21275
+ if (auditRes.ok && auditRes.data?.[name]) {
21276
+ vulnerabilities = auditRes.data[name].length;
21277
+ }
21278
+ }
21201
21279
  return {
21202
21280
  name,
21203
21281
  description: pkg.description,
@@ -21212,7 +21290,7 @@ var analysisTools = [
21212
21290
  hasReadme: !!(pkg.readme && pkg.readme.length > 0),
21213
21291
  repository: pkg.repository,
21214
21292
  homepage: pkg.homepage,
21215
- vulnerabilities: auditRes.ok && auditRes.data?.[name] ? auditRes.data[name].length : 0
21293
+ vulnerabilities
21216
21294
  };
21217
21295
  })
21218
21296
  );
@@ -21243,6 +21321,16 @@ var analysisTools = [
21243
21321
  const latest = pkg["dist-tags"]?.latest;
21244
21322
  const latestVersion = latest ? pkg.versions[latest] : void 0;
21245
21323
  const versionKeys = Object.keys(pkg.versions);
21324
+ let vulnerabilityCount = null;
21325
+ if (latest) {
21326
+ const auditRes = await registryPost("/-/npm/v1/security/advisories/bulk", {
21327
+ [input.name]: [latest]
21328
+ });
21329
+ if (auditRes.ok) {
21330
+ const advisories = auditRes.data?.[input.name];
21331
+ vulnerabilityCount = Array.isArray(advisories) ? advisories.length : 0;
21332
+ }
21333
+ }
21246
21334
  const publishDates = versionKeys.map((v) => pkg.time[v]).filter(Boolean).map((d) => new Date(d).getTime()).sort((a, b) => b - a);
21247
21335
  const now = Date.now();
21248
21336
  const daysSinceLastPublish = publishDates.length > 0 ? Math.floor((now - publishDates[0]) / 864e5) : null;
@@ -21274,6 +21362,7 @@ var analysisTools = [
21274
21362
  versionCount: versionKeys.length,
21275
21363
  daysSinceLastPublish,
21276
21364
  avgDaysBetweenReleases,
21365
+ vulnerabilityCount,
21277
21366
  hasLicense,
21278
21367
  hasReadme,
21279
21368
  hasRepo,
@@ -21454,7 +21543,6 @@ var authTools = [
21454
21543
  data: {
21455
21544
  total: data.total,
21456
21545
  tokens: data.objects.map((t) => ({
21457
- token: t.token,
21458
21546
  key: t.key,
21459
21547
  readonly: t.readonly,
21460
21548
  cidrWhitelist: t.cidr_whitelist,
@@ -21464,6 +21552,38 @@ var authTools = [
21464
21552
  }
21465
21553
  };
21466
21554
  }
21555
+ },
21556
+ {
21557
+ name: "npm_user_packages",
21558
+ description: "List all packages published by a specific npm user. Shows package names and the user's access level for each. Requires authentication.",
21559
+ annotations: {
21560
+ title: "List user packages",
21561
+ readOnlyHint: true,
21562
+ destructiveHint: false,
21563
+ idempotentHint: true,
21564
+ openWorldHint: true
21565
+ },
21566
+ inputSchema: external_exports.object({
21567
+ username: external_exports.string().describe("npm username")
21568
+ }),
21569
+ handler: async (input) => {
21570
+ const authErr = requireAuth();
21571
+ if (authErr) return authErr;
21572
+ const res = await registryGetAuth(
21573
+ `/-/user/org.couchdb.user:${encodeURIComponent(input.username)}/package`
21574
+ );
21575
+ if (!res.ok) return res;
21576
+ const packages = Object.entries(res.data).map(([name, access]) => ({ name, access }));
21577
+ return {
21578
+ ok: true,
21579
+ status: 200,
21580
+ data: {
21581
+ username: input.username,
21582
+ packageCount: packages.length,
21583
+ packages
21584
+ }
21585
+ };
21586
+ }
21467
21587
  }
21468
21588
  ];
21469
21589
 
@@ -21541,8 +21661,8 @@ var dependencyTools = [
21541
21661
  else queue.push(run);
21542
21662
  });
21543
21663
  }
21544
- async function resolve(name, versionHint, currentDepth) {
21545
- const hintKey = `${name}@${versionHint}`;
21664
+ async function resolve(name, versionHint2, currentDepth) {
21665
+ const hintKey = `${name}@${versionHint2}`;
21546
21666
  if (resolved.has(hintKey) || currentDepth > maxDepth) return;
21547
21667
  resolved.add(hintKey);
21548
21668
  let pkg = packumentCache.get(name);
@@ -21550,19 +21670,21 @@ var dependencyTools = [
21550
21670
  const res = await runLimited(() => registryGetAbbreviated(`/${encPkg(name)}`));
21551
21671
  if (!res.ok) {
21552
21672
  warnings.push(`Failed to fetch ${name}: ${res.error}`);
21553
- tree[hintKey] = { version: versionHint, dependencies: {} };
21673
+ tree[hintKey] = { version: versionHint2, dependencies: {} };
21554
21674
  return;
21555
21675
  }
21556
21676
  pkg = res.data;
21557
21677
  packumentCache.set(name, pkg);
21558
21678
  }
21559
21679
  let resolvedVersion;
21560
- if (pkg.versions[versionHint]) {
21561
- resolvedVersion = versionHint;
21562
- } else if (pkg["dist-tags"][versionHint]) {
21563
- resolvedVersion = pkg["dist-tags"][versionHint];
21680
+ if (pkg.versions[versionHint2]) {
21681
+ resolvedVersion = versionHint2;
21682
+ } else if (pkg["dist-tags"][versionHint2]) {
21683
+ resolvedVersion = pkg["dist-tags"][versionHint2];
21564
21684
  } else {
21565
- resolvedVersion = pkg["dist-tags"]?.latest ?? versionHint;
21685
+ const available = Object.keys(pkg.versions);
21686
+ const matched = maxSatisfying(available, versionHint2);
21687
+ resolvedVersion = matched ?? pkg["dist-tags"]?.latest ?? versionHint2;
21566
21688
  }
21567
21689
  const resolvedKey = `${name}@${resolvedVersion}`;
21568
21690
  if (tree[resolvedKey]) return;
@@ -21578,12 +21700,14 @@ var dependencyTools = [
21578
21700
  await Promise.all(tasks);
21579
21701
  }
21580
21702
  }
21581
- await resolve(input.name, input.version ?? "latest", 1);
21703
+ const versionHint = input.version ?? "latest";
21704
+ await resolve(input.name, versionHint, 1);
21705
+ const rootKey = Object.keys(tree).find((k) => k.startsWith(`${input.name}@`)) ?? `${input.name}@${versionHint}`;
21582
21706
  return {
21583
21707
  ok: true,
21584
21708
  status: 200,
21585
21709
  data: {
21586
- root: `${input.name}@${input.version ?? "latest"}`,
21710
+ root: rootKey,
21587
21711
  depth: maxDepth,
21588
21712
  totalPackages: Object.keys(tree).length,
21589
21713
  tree,
@@ -21604,14 +21728,17 @@ var dependencyTools = [
21604
21728
  },
21605
21729
  inputSchema: external_exports.object({
21606
21730
  name: external_exports.string().describe("Package name"),
21607
- version: external_exports.string().optional().describe("Semver version or dist-tag (default: 'latest')")
21731
+ version: external_exports.string().optional().describe("Semver version or dist-tag (default: 'latest')"),
21732
+ allowed: external_exports.array(external_exports.string()).optional().describe(
21733
+ "SPDX license identifiers to treat as allowed (default: MIT, ISC, BSD-2-Clause, BSD-3-Clause, Apache-2.0, 0BSD, Unlicense)"
21734
+ )
21608
21735
  }),
21609
21736
  handler: async (input) => {
21610
21737
  const ver = input.version ?? "latest";
21611
21738
  const res = await registryGet(`/${encPkg(input.name)}/${ver}`);
21612
21739
  if (!res.ok) return res;
21613
21740
  const pkg = res.data;
21614
- const deps = Object.keys(pkg.dependencies ?? {});
21741
+ const depEntries = Object.entries(pkg.dependencies ?? {});
21615
21742
  const MAX_CONCURRENT = 10;
21616
21743
  let active = 0;
21617
21744
  const queue = [];
@@ -21629,25 +21756,32 @@ var dependencyTools = [
21629
21756
  });
21630
21757
  }
21631
21758
  const depLicenses = await Promise.all(
21632
- deps.map(async (depName) => {
21633
- const depRes = await runLimited(() => registryGet(`/${encPkg(depName)}/latest`));
21759
+ depEntries.map(async ([depName, depRange]) => {
21760
+ const abbrevRes = await runLimited(() => registryGetAbbreviated(`/${encPkg(depName)}`));
21761
+ if (!abbrevRes.ok) return { name: depName, version: depRange, license: "FETCH_ERROR" };
21762
+ const abbrev = abbrevRes.data;
21763
+ const available = Object.keys(abbrev.versions);
21764
+ const resolved = maxSatisfying(available, depRange) ?? abbrev["dist-tags"]?.latest;
21765
+ if (!resolved) return { name: depName, version: depRange, license: "UNKNOWN" };
21766
+ const verRes = await runLimited(() => registryGet(`/${encPkg(depName)}/${resolved}`));
21634
21767
  return {
21635
21768
  name: depName,
21636
- license: depRes.ok ? depRes.data?.license ?? "UNKNOWN" : "FETCH_ERROR"
21769
+ version: resolved,
21770
+ license: verRes.ok ? verRes.data?.license ?? "UNKNOWN" : "FETCH_ERROR"
21637
21771
  };
21638
21772
  })
21639
21773
  );
21640
- const PERMISSIVE = /* @__PURE__ */ new Set(["MIT", "ISC", "BSD-2-Clause", "BSD-3-Clause", "Apache-2.0", "0BSD", "Unlicense"]);
21641
- const results = [
21642
- { name: pkg.name, version: pkg.version, license: pkg.license ?? "UNKNOWN" },
21643
- ...depLicenses.map((d) => ({ name: d.name, version: "latest", license: d.license }))
21644
- ];
21645
- const flagged = results.filter((r) => !PERMISSIVE.has(r.license));
21774
+ const allowedSet = new Set(
21775
+ input.allowed ?? ["MIT", "ISC", "BSD-2-Clause", "BSD-3-Clause", "Apache-2.0", "0BSD", "Unlicense"]
21776
+ );
21777
+ const results = [{ name: pkg.name, version: pkg.version, license: pkg.license ?? "UNKNOWN" }, ...depLicenses];
21778
+ const flagged = results.filter((r) => !allowedSet.has(r.license));
21646
21779
  return {
21647
21780
  ok: true,
21648
21781
  status: 200,
21649
21782
  data: {
21650
21783
  total: results.length,
21784
+ allowed: [...allowedSet],
21651
21785
  flagged: flagged.length,
21652
21786
  packages: results,
21653
21787
  issues: flagged.length > 0 ? flagged : void 0
@@ -21728,61 +21862,12 @@ var downloadTools = [
21728
21862
  openWorldHint: true
21729
21863
  },
21730
21864
  inputSchema: external_exports.object({
21731
- name: external_exports.string().describe("Package name")
21732
- }),
21733
- handler: async (input) => {
21734
- return downloadsGet(`/versions/${encPkg(input.name)}/last-week`);
21735
- }
21736
- }
21737
- ];
21738
-
21739
- // src/tools/hooks.ts
21740
- var hookTools = [
21741
- {
21742
- name: "npm_hooks",
21743
- description: "List all npm webhooks configured for the authenticated user. Shows hook type (package, scope, or owner), endpoint URL, delivery status, and last response code.",
21744
- annotations: {
21745
- title: "List npm hooks",
21746
- readOnlyHint: true,
21747
- destructiveHint: false,
21748
- idempotentHint: true,
21749
- openWorldHint: true
21750
- },
21751
- inputSchema: external_exports.object({
21752
- package: external_exports.string().optional().describe("Filter hooks by package name"),
21753
- limit: external_exports.number().min(1).max(100).optional().describe("Max results (default: 25)"),
21754
- offset: external_exports.number().min(0).optional().describe("Pagination offset")
21865
+ name: external_exports.string().describe("Package name"),
21866
+ period: external_exports.string().optional().describe("Period: 'last-day', 'last-week', 'last-month' (default: 'last-week')")
21755
21867
  }),
21756
21868
  handler: async (input) => {
21757
- const authErr = requireAuth();
21758
- if (authErr) return authErr;
21759
- const params = new URLSearchParams();
21760
- if (input.package) params.set("package", input.package);
21761
- if (input.limit !== void 0) params.set("limit", String(input.limit));
21762
- if (input.offset !== void 0) params.set("offset", String(input.offset));
21763
- const qs = params.toString();
21764
- const path = `/-/npm/v1/hooks${qs ? `?${qs}` : ""}`;
21765
- const res = await registryGetAuth(path);
21766
- if (!res.ok) return res;
21767
- const data = res.data;
21768
- return {
21769
- ok: true,
21770
- status: 200,
21771
- data: {
21772
- total: data.total,
21773
- hooks: data.objects.map((h) => ({
21774
- id: h.id,
21775
- type: h.type,
21776
- name: h.name,
21777
- endpoint: h.endpoint,
21778
- status: h.status,
21779
- lastDelivery: h.last_delivery,
21780
- lastResponseCode: h.response_code,
21781
- created: h.created,
21782
- updated: h.updated
21783
- }))
21784
- }
21785
- };
21869
+ const period = input.period ?? "last-week";
21870
+ return downloadsGet(`/versions/${encPkg(input.name)}/${period}`);
21786
21871
  }
21787
21872
  }
21788
21873
  ];
@@ -22011,7 +22096,7 @@ var packageTools = [
22011
22096
  },
22012
22097
  {
22013
22098
  name: "npm_versions",
22014
- description: "List all published versions of a package with their publish dates, ordered newest first.",
22099
+ description: "List published versions of a package with their publish dates, ordered newest first. Returns up to `limit` versions (default 50). Set limit=0 to return all.",
22015
22100
  annotations: {
22016
22101
  title: "List versions",
22017
22102
  readOnlyHint: true,
@@ -22020,25 +22105,29 @@ var packageTools = [
22020
22105
  openWorldHint: true
22021
22106
  },
22022
22107
  inputSchema: external_exports.object({
22023
- name: external_exports.string().describe("Package name")
22108
+ name: external_exports.string().describe("Package name"),
22109
+ limit: external_exports.number().min(0).optional().describe("Max versions to return, newest first (default 50, 0 = all)")
22024
22110
  }),
22025
22111
  handler: async (input) => {
22026
22112
  const res = await registryGet(`/${encPkg(input.name)}`);
22027
22113
  if (!res.ok) return res;
22028
22114
  const pkg = res.data;
22029
- const versions = Object.keys(pkg.versions).map((v) => ({
22115
+ const limit = input.limit ?? 50;
22116
+ const allVersions = Object.keys(pkg.versions).map((v) => ({
22030
22117
  version: v,
22031
22118
  date: pkg.time[v],
22032
22119
  deprecated: pkg.versions[v].deprecated,
22033
22120
  npmUser: pkg.versions[v]._npmUser?.name
22034
22121
  })).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
22122
+ const versions = limit > 0 ? allVersions.slice(0, limit) : allVersions;
22035
22123
  return {
22036
22124
  ok: true,
22037
22125
  status: 200,
22038
22126
  data: {
22039
22127
  name: pkg.name,
22040
22128
  distTags: pkg["dist-tags"],
22041
- total: versions.length,
22129
+ total: allVersions.length,
22130
+ showing: versions.length,
22042
22131
  versions
22043
22132
  }
22044
22133
  };
@@ -22083,6 +22172,61 @@ var packageTools = [
22083
22172
  handler: async (input) => {
22084
22173
  return registryGet(`/-/package/${encPkg(input.name)}/dist-tags`);
22085
22174
  }
22175
+ },
22176
+ {
22177
+ name: "npm_types",
22178
+ description: "Check TypeScript type support for a package \u2014 whether it ships built-in types (types/typings field) or has a DefinitelyTyped companion (@types/* package).",
22179
+ annotations: {
22180
+ title: "Check TypeScript types",
22181
+ readOnlyHint: true,
22182
+ destructiveHint: false,
22183
+ idempotentHint: true,
22184
+ openWorldHint: true
22185
+ },
22186
+ inputSchema: external_exports.object({
22187
+ name: external_exports.string().describe("Package name (e.g. 'express' or '@anthropic-ai/sdk')"),
22188
+ version: external_exports.string().optional().describe("Semver version or dist-tag (default: 'latest')")
22189
+ }),
22190
+ handler: async (input) => {
22191
+ const ver = input.version ?? "latest";
22192
+ let typesPackage;
22193
+ if (input.name.startsWith("@")) {
22194
+ const withoutAt = input.name.slice(1);
22195
+ typesPackage = `@types/${withoutAt.replace("/", "__")}`;
22196
+ } else {
22197
+ typesPackage = `@types/${input.name}`;
22198
+ }
22199
+ const [versionRes, typesRes] = await Promise.all([
22200
+ registryGet(`/${encPkg(input.name)}/${ver}`),
22201
+ registryGet(`/${encPkg(typesPackage)}`)
22202
+ ]);
22203
+ if (!versionRes.ok) return versionRes;
22204
+ const v = versionRes.data;
22205
+ const hasBuiltinTypes = !!(v.types || v.typings);
22206
+ const typesEntry = hasBuiltinTypes ? v.types ?? v.typings : void 0;
22207
+ const hasTypesPackage = typesRes.ok;
22208
+ const typesPackageLatest = hasTypesPackage ? typesRes.data?.["dist-tags"]?.latest : void 0;
22209
+ let recommendation;
22210
+ if (hasBuiltinTypes) {
22211
+ recommendation = "Built-in types included \u2014 no additional install needed.";
22212
+ } else if (hasTypesPackage) {
22213
+ recommendation = `Install types separately: npm install -D ${typesPackage}`;
22214
+ } else {
22215
+ recommendation = "No TypeScript types available (built-in or @types).";
22216
+ }
22217
+ return {
22218
+ ok: true,
22219
+ status: 200,
22220
+ data: {
22221
+ name: v.name,
22222
+ version: v.version,
22223
+ builtinTypes: hasBuiltinTypes,
22224
+ typesEntry,
22225
+ typesPackage: hasTypesPackage ? { name: typesPackage, latest: typesPackageLatest } : null,
22226
+ recommendation
22227
+ }
22228
+ };
22229
+ }
22086
22230
  }
22087
22231
  ];
22088
22232
 
@@ -22149,7 +22293,7 @@ var registryTools = [
22149
22293
  },
22150
22294
  {
22151
22295
  name: "npm_recent_changes",
22152
- description: "Get the most recent package publishes/updates from the npm registry via the CouchDB changes feed.",
22296
+ description: "Get the most recent package publishes/updates from the npm registry via the CouchDB changes feed. Note: uses replicate.npmjs.com which may have intermittent availability.",
22153
22297
  annotations: {
22154
22298
  title: "Recent registry changes",
22155
22299
  readOnlyHint: true,
@@ -22162,21 +22306,20 @@ var registryTools = [
22162
22306
  }),
22163
22307
  handler: async (input) => {
22164
22308
  const limit = input.limit ?? 25;
22165
- const dbRes = await replicateGet("/");
22166
- if (!dbRes.ok) return dbRes;
22167
- const since = dbRes.data.update_seq - limit;
22168
- const changesRes = await replicateGet(`/_changes?since=${since}&limit=${limit}&descending=false`);
22309
+ const [dbRes, changesRes] = await Promise.all([
22310
+ replicateGet("/"),
22311
+ replicateGet(`/_changes?limit=${limit}&descending=true`)
22312
+ ]);
22169
22313
  if (!changesRes.ok) return changesRes;
22170
22314
  const changes = changesRes.data.results.map((r) => ({
22171
22315
  package: r.id,
22172
- seq: r.seq,
22173
22316
  rev: r.changes[0]?.rev
22174
22317
  }));
22175
22318
  return {
22176
22319
  ok: true,
22177
22320
  status: 200,
22178
22321
  data: {
22179
- totalPackages: dbRes.data.doc_count,
22322
+ totalPackages: dbRes.ok ? dbRes.data.doc_count : null,
22180
22323
  changes
22181
22324
  }
22182
22325
  };
@@ -22219,12 +22362,10 @@ var searchTools = [
22219
22362
  description: obj.package.description,
22220
22363
  date: obj.package.date,
22221
22364
  license: obj.package.license,
22222
- publisher: obj.package.publisher?.username,
22365
+ publisher: obj.package.publisher,
22223
22366
  keywords: obj.package.keywords,
22224
22367
  links: obj.package.links,
22225
- score: obj.score.detail,
22226
- downloads: obj.downloads,
22227
- dependents: obj.dependents
22368
+ score: obj.score.detail
22228
22369
  }));
22229
22370
  return { ok: true, status: 200, data: { total: res.data.total, results } };
22230
22371
  }
@@ -22235,7 +22376,7 @@ var searchTools = [
22235
22376
  var securityTools = [
22236
22377
  {
22237
22378
  name: "npm_audit",
22238
- description: "Check specific packages and versions for known vulnerabilities. Returns advisories with severity, CVEs, and patched versions.",
22379
+ description: "Quick vulnerability check for specific packages and versions using the bulk advisory API. Returns matching advisories with severity, CVEs, and patched versions. For richer detail (CVSS scores, CWEs, fix recommendations), use npm_audit_deep instead.",
22239
22380
  annotations: {
22240
22381
  title: "Audit packages",
22241
22382
  readOnlyHint: true,
@@ -22252,7 +22393,7 @@ var securityTools = [
22252
22393
  },
22253
22394
  {
22254
22395
  name: "npm_audit_deep",
22255
- description: "Run a full security audit on a set of dependencies. Returns detailed advisories with CVSS scores, CWEs, fix recommendations, and vulnerability metadata.",
22396
+ description: "Full security audit on a dependency set \u2014 returns detailed advisories with CVSS scores, CWEs, affected version ranges, fix recommendations, and full vulnerability metadata. Uses the npm audit v1 endpoint which provides richer detail than the bulk advisory API (npm_audit). Requires you to provide the dependency map (use npm_dependencies to get it first).",
22256
22397
  annotations: {
22257
22398
  title: "Deep security audit",
22258
22399
  readOnlyHint: true,
@@ -22294,6 +22435,60 @@ var securityTools = [
22294
22435
  }
22295
22436
  ];
22296
22437
 
22438
+ // src/tools/trust.ts
22439
+ var trustTools = [
22440
+ {
22441
+ name: "npm_trusted_publishers",
22442
+ description: "List trusted publishing configurations for a package. Shows OIDC trust relationships with CI/CD providers (GitHub Actions, GitLab CI, CircleCI) that allow tokenless publishing. Requires authentication with write access to the package.",
22443
+ annotations: {
22444
+ title: "List trusted publishers",
22445
+ readOnlyHint: true,
22446
+ destructiveHint: false,
22447
+ idempotentHint: true,
22448
+ openWorldHint: true
22449
+ },
22450
+ inputSchema: external_exports.object({
22451
+ name: external_exports.string().describe("Package name (e.g. 'express' or '@yawlabs/npmjs-mcp')")
22452
+ }),
22453
+ handler: async (input) => {
22454
+ const authErr = requireAuth();
22455
+ if (authErr) return authErr;
22456
+ const res = await registryGetAuth(`/-/package/${encPkg(input.name)}/trust`);
22457
+ if (!res.ok) return res;
22458
+ const configs = (res.data ?? []).map((c) => {
22459
+ const result = {
22460
+ id: c.id,
22461
+ provider: c.type
22462
+ };
22463
+ if (c.type === "github") {
22464
+ result.repository = c.claims?.repository;
22465
+ const workflowRef = c.claims?.workflow_ref;
22466
+ result.workflowFile = workflowRef?.file;
22467
+ result.environment = c.claims?.environment;
22468
+ } else if (c.type === "gitlab") {
22469
+ result.project = c.claims?.project_path;
22470
+ const configRef = c.claims?.ci_config_ref_uri;
22471
+ result.configFile = configRef?.file;
22472
+ result.environment = c.claims?.environment;
22473
+ } else if (c.type === "circleci") {
22474
+ result.project = c.claims?.project_id;
22475
+ result.context = c.claims?.context_ids;
22476
+ }
22477
+ return result;
22478
+ });
22479
+ return {
22480
+ ok: true,
22481
+ status: 200,
22482
+ data: {
22483
+ package: input.name,
22484
+ trustedPublisherCount: configs.length,
22485
+ trustedPublishers: configs
22486
+ }
22487
+ };
22488
+ }
22489
+ }
22490
+ ];
22491
+
22297
22492
  // src/tools/workflows.ts
22298
22493
  var workflowTools = [
22299
22494
  {
@@ -22568,7 +22763,7 @@ var workflowTools = [
22568
22763
  ];
22569
22764
 
22570
22765
  // src/index.ts
22571
- var version2 = true ? "0.2.0" : (await null).createRequire(import.meta.url)("../package.json").version;
22766
+ var version2 = true ? "0.4.0" : (await null).createRequire(import.meta.url)("../package.json").version;
22572
22767
  var subcommand = process.argv[2];
22573
22768
  if (subcommand === "version" || subcommand === "--version") {
22574
22769
  console.log(version2);
@@ -22586,7 +22781,7 @@ var allTools = [
22586
22781
  ...orgTools,
22587
22782
  ...accessTools,
22588
22783
  ...provenanceTools,
22589
- ...hookTools,
22784
+ ...trustTools,
22590
22785
  ...workflowTools
22591
22786
  ];
22592
22787
  var server = new McpServer({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/npmjs-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "npm registry MCP server — package intelligence, security audits, and dependency analysis for AI assistants",
5
5
  "license": "MIT",
6
6
  "author": "YawLabs <contact@yaw.sh>",