@yawlabs/npmjs-mcp 0.3.0 → 0.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.
Files changed (3) hide show
  1. package/README.md +7 -5
  2. package/dist/index.js +346 -126
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -65,7 +65,7 @@ Add to `claude_desktop_config.json`:
65
65
  }
66
66
  ```
67
67
 
68
- ## Tools (35)
68
+ ## Tools (37)
69
69
 
70
70
  ### Search
71
71
  - `npm_search` — Search the npm registry with qualifiers (keywords, author, scope)
@@ -76,6 +76,7 @@ Add to `claude_desktop_config.json`:
76
76
  - `npm_versions` — List all published versions with dates
77
77
  - `npm_readme` — Get README content
78
78
  - `npm_dist_tags` — Get dist-tags (latest, next, beta, etc)
79
+ - `npm_types` — Check TypeScript type support (built-in types or @types/*)
79
80
 
80
81
  ### Dependencies
81
82
  - `npm_dependencies` — Get dependency lists (prod, dev, peer, optional)
@@ -106,10 +107,14 @@ Add to `claude_desktop_config.json`:
106
107
  ### Provenance
107
108
  - `npm_provenance` — Get Sigstore provenance attestations (SLSA, publish)
108
109
 
110
+ ### Trusted Publishers (requires NPM_TOKEN)
111
+ - `npm_trusted_publishers` — List OIDC trust relationships with CI/CD providers
112
+
109
113
  ### Auth (requires NPM_TOKEN)
110
114
  - `npm_whoami` — Check authenticated user
111
115
  - `npm_profile` — Get profile, email, 2FA status
112
116
  - `npm_tokens` — List access tokens
117
+ - `npm_user_packages` — List packages published by a user
113
118
 
114
119
  ### Access (requires NPM_TOKEN)
115
120
  - `npm_collaborators` — List package collaborators and permissions
@@ -121,16 +126,13 @@ Add to `claude_desktop_config.json`:
121
126
  - `npm_org_teams` — List org teams
122
127
  - `npm_team_packages` — List team package permissions
123
128
 
124
- ### Hooks (requires NPM_TOKEN)
125
- - `npm_hooks` — List npm webhooks
126
-
127
129
  ### Workflows
128
130
  - `npm_check_auth` — Auth health check with headless publish feasibility
129
131
  - `npm_publish_preflight` — Pre-publish validation checklist
130
132
 
131
133
  ## Features
132
134
 
133
- - **35 tools** covering search, packages, deps, downloads, security, analysis, auth, orgs, provenance, and publish workflows
135
+ - **37 tools** covering search, packages, deps, downloads, security, analysis, auth, orgs, provenance, trust, and publish workflows
134
136
  - **No API key required** for read-only tools — authenticated tools opt-in via NPM_TOKEN
135
137
  - **Zero runtime dependencies** — Single bundled file for instant `npx` startup
136
138
  - **Agent-aware publish tools** — Detects non-interactive context, provides human hand-off actions instead of unworkable retries
package/dist/index.js CHANGED
@@ -21087,6 +21087,141 @@ function downloadsGet(path) {
21087
21087
  function replicateGet(path) {
21088
21088
  return request(REPLICATE_URL, path);
21089
21089
  }
21090
+ function createLimiter(max) {
21091
+ let active = 0;
21092
+ const queue = [];
21093
+ return function runLimited(fn) {
21094
+ return new Promise((resolve, reject) => {
21095
+ const run = () => {
21096
+ active++;
21097
+ fn().then(resolve, reject).finally(() => {
21098
+ active--;
21099
+ if (queue.length > 0) queue.shift()();
21100
+ });
21101
+ };
21102
+ if (active < max) run();
21103
+ else queue.push(run);
21104
+ });
21105
+ };
21106
+ }
21107
+ function parseSemver(v) {
21108
+ const m = v.match(/^(\d+)\.(\d+)\.(\d+)/);
21109
+ return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
21110
+ }
21111
+ function cmpSemver(a, b) {
21112
+ for (let i = 0; i < 3; i++) {
21113
+ if (a[i] < b[i]) return -1;
21114
+ if (a[i] > b[i]) return 1;
21115
+ }
21116
+ return 0;
21117
+ }
21118
+ function parseSingleConstraint(r) {
21119
+ if (r === "*" || r === "") {
21120
+ return { min: null, max: null };
21121
+ }
21122
+ if (r.startsWith("^")) {
21123
+ const base = parseSemver(r.slice(1));
21124
+ if (!base) return null;
21125
+ let max;
21126
+ if (base[0] > 0) max = [base[0] + 1, 0, 0];
21127
+ else if (base[1] > 0) max = [0, base[1] + 1, 0];
21128
+ else max = [0, 0, base[2] + 1];
21129
+ return { min: base, max };
21130
+ }
21131
+ if (r.startsWith("~")) {
21132
+ const base = parseSemver(r.slice(1));
21133
+ if (!base) return null;
21134
+ return { min: base, max: [base[0], base[1] + 1, 0] };
21135
+ }
21136
+ if (r.startsWith(">=")) {
21137
+ const base = parseSemver(r.slice(2));
21138
+ if (!base) return null;
21139
+ return { min: base, max: null };
21140
+ }
21141
+ if (r.startsWith(">")) {
21142
+ const base = parseSemver(r.slice(1));
21143
+ if (!base) return null;
21144
+ return { min: [base[0], base[1], base[2] + 1], max: null };
21145
+ }
21146
+ if (r.startsWith("<=")) {
21147
+ const base = parseSemver(r.slice(2));
21148
+ if (!base) return null;
21149
+ return { min: null, max: [base[0], base[1], base[2] + 1] };
21150
+ }
21151
+ if (r.startsWith("<")) {
21152
+ const base = parseSemver(r.slice(1));
21153
+ if (!base) return null;
21154
+ return { min: null, max: base };
21155
+ }
21156
+ if (r.startsWith("=")) {
21157
+ const base = parseSemver(r.slice(1));
21158
+ if (!base) return null;
21159
+ return { min: base, max: [base[0], base[1], base[2] + 1] };
21160
+ }
21161
+ const xm = r.match(/^(\d+)(?:\.(\d+|x|\*)(?:\.(\d+|x|\*))?)?$/);
21162
+ if (xm) {
21163
+ const major = Number(xm[1]);
21164
+ const minor = xm[2] !== void 0 && xm[2] !== "x" && xm[2] !== "*" ? Number(xm[2]) : null;
21165
+ if (minor === null) {
21166
+ return { min: [major, 0, 0], max: [major + 1, 0, 0] };
21167
+ }
21168
+ const patch = xm[3] !== void 0 && xm[3] !== "x" && xm[3] !== "*" ? Number(xm[3]) : null;
21169
+ if (patch === null) {
21170
+ return { min: [major, minor, 0], max: [major, minor + 1, 0] };
21171
+ }
21172
+ return null;
21173
+ }
21174
+ return null;
21175
+ }
21176
+ function parseRange(r) {
21177
+ const hyphenMatch = r.match(/^(\d+\.\d+\.\d+)\s+-\s+(\d+\.\d+\.\d+)$/);
21178
+ if (hyphenMatch) {
21179
+ const low = parseSemver(hyphenMatch[1]);
21180
+ const high = parseSemver(hyphenMatch[2]);
21181
+ if (low && high) return { min: low, max: [high[0], high[1], high[2] + 1] };
21182
+ return null;
21183
+ }
21184
+ const parts = r.trim().split(/\s+/);
21185
+ if (parts.length > 1) {
21186
+ let min = null;
21187
+ let max = null;
21188
+ for (const part of parts) {
21189
+ const constraint = parseSingleConstraint(part);
21190
+ if (!constraint) return null;
21191
+ if (constraint.min) {
21192
+ if (!min || cmpSemver(constraint.min, min) > 0) min = constraint.min;
21193
+ }
21194
+ if (constraint.max) {
21195
+ if (!max || cmpSemver(constraint.max, max) < 0) max = constraint.max;
21196
+ }
21197
+ }
21198
+ return { min, max };
21199
+ }
21200
+ return parseSingleConstraint(r.trim());
21201
+ }
21202
+ function maxSatisfying(versions, range) {
21203
+ const r = range.trim().replace(/^v/, "");
21204
+ if (versions.includes(r)) return r;
21205
+ const subRanges = r.split("||").map((s) => s.trim());
21206
+ let best = null;
21207
+ let bestParsed = null;
21208
+ for (const sub of subRanges) {
21209
+ const parsed = parseRange(sub);
21210
+ if (!parsed) continue;
21211
+ for (const v of versions) {
21212
+ if (v.includes("-") && !sub.includes("-")) continue;
21213
+ const vp = parseSemver(v);
21214
+ if (!vp) continue;
21215
+ if (parsed.min && cmpSemver(vp, parsed.min) < 0) continue;
21216
+ if (parsed.max && cmpSemver(vp, parsed.max) >= 0) continue;
21217
+ if (!bestParsed || cmpSemver(vp, bestParsed) > 0) {
21218
+ best = v;
21219
+ bestParsed = vp;
21220
+ }
21221
+ }
21222
+ }
21223
+ return best;
21224
+ }
21090
21225
 
21091
21226
  // src/tools/access.ts
21092
21227
  var accessTools = [
@@ -21184,12 +21319,9 @@ var analysisTools = [
21184
21319
  handler: async (input) => {
21185
21320
  const results = await Promise.all(
21186
21321
  input.packages.map(async (name) => {
21187
- const [pkgRes, dlRes, auditRes] = await Promise.all([
21322
+ const [pkgRes, dlRes] = await Promise.all([
21188
21323
  registryGet(`/${encPkg(name)}`),
21189
- downloadsGet(`/downloads/point/last-week/${encPkg(name)}`),
21190
- registryPost("/-/npm/v1/security/advisories/bulk", {
21191
- [name]: ["latest"]
21192
- })
21324
+ downloadsGet(`/downloads/point/last-week/${encPkg(name)}`)
21193
21325
  ]);
21194
21326
  if (!pkgRes.ok) {
21195
21327
  return { name, error: pkgRes.error };
@@ -21198,6 +21330,15 @@ var analysisTools = [
21198
21330
  const latest = pkg["dist-tags"]?.latest;
21199
21331
  const latestVersion = latest ? pkg.versions[latest] : void 0;
21200
21332
  const versionKeys = Object.keys(pkg.versions);
21333
+ let vulnerabilities = 0;
21334
+ if (latest) {
21335
+ const auditRes = await registryPost("/-/npm/v1/security/advisories/bulk", {
21336
+ [name]: [latest]
21337
+ });
21338
+ if (auditRes.ok && auditRes.data?.[name]) {
21339
+ vulnerabilities = auditRes.data[name].length;
21340
+ }
21341
+ }
21201
21342
  return {
21202
21343
  name,
21203
21344
  description: pkg.description,
@@ -21212,7 +21353,7 @@ var analysisTools = [
21212
21353
  hasReadme: !!(pkg.readme && pkg.readme.length > 0),
21213
21354
  repository: pkg.repository,
21214
21355
  homepage: pkg.homepage,
21215
- vulnerabilities: auditRes.ok && auditRes.data?.[name] ? auditRes.data[name].length : 0
21356
+ vulnerabilities
21216
21357
  };
21217
21358
  })
21218
21359
  );
@@ -21243,6 +21384,16 @@ var analysisTools = [
21243
21384
  const latest = pkg["dist-tags"]?.latest;
21244
21385
  const latestVersion = latest ? pkg.versions[latest] : void 0;
21245
21386
  const versionKeys = Object.keys(pkg.versions);
21387
+ let vulnerabilityCount = null;
21388
+ if (latest) {
21389
+ const auditRes = await registryPost("/-/npm/v1/security/advisories/bulk", {
21390
+ [input.name]: [latest]
21391
+ });
21392
+ if (auditRes.ok) {
21393
+ const advisories = auditRes.data?.[input.name];
21394
+ vulnerabilityCount = Array.isArray(advisories) ? advisories.length : 0;
21395
+ }
21396
+ }
21246
21397
  const publishDates = versionKeys.map((v) => pkg.time[v]).filter(Boolean).map((d) => new Date(d).getTime()).sort((a, b) => b - a);
21247
21398
  const now = Date.now();
21248
21399
  const daysSinceLastPublish = publishDates.length > 0 ? Math.floor((now - publishDates[0]) / 864e5) : null;
@@ -21274,6 +21425,7 @@ var analysisTools = [
21274
21425
  versionCount: versionKeys.length,
21275
21426
  daysSinceLastPublish,
21276
21427
  avgDaysBetweenReleases,
21428
+ vulnerabilityCount,
21277
21429
  hasLicense,
21278
21430
  hasReadme,
21279
21431
  hasRepo,
@@ -21454,7 +21606,6 @@ var authTools = [
21454
21606
  data: {
21455
21607
  total: data.total,
21456
21608
  tokens: data.objects.map((t) => ({
21457
- token: t.token,
21458
21609
  key: t.key,
21459
21610
  readonly: t.readonly,
21460
21611
  cidrWhitelist: t.cidr_whitelist,
@@ -21464,6 +21615,38 @@ var authTools = [
21464
21615
  }
21465
21616
  };
21466
21617
  }
21618
+ },
21619
+ {
21620
+ name: "npm_user_packages",
21621
+ description: "List all packages published by a specific npm user. Shows package names and the user's access level for each. Requires authentication.",
21622
+ annotations: {
21623
+ title: "List user packages",
21624
+ readOnlyHint: true,
21625
+ destructiveHint: false,
21626
+ idempotentHint: true,
21627
+ openWorldHint: true
21628
+ },
21629
+ inputSchema: external_exports.object({
21630
+ username: external_exports.string().describe("npm username")
21631
+ }),
21632
+ handler: async (input) => {
21633
+ const authErr = requireAuth();
21634
+ if (authErr) return authErr;
21635
+ const res = await registryGetAuth(
21636
+ `/-/user/org.couchdb.user:${encodeURIComponent(input.username)}/package`
21637
+ );
21638
+ if (!res.ok) return res;
21639
+ const packages = Object.entries(res.data).map(([name, access]) => ({ name, access }));
21640
+ return {
21641
+ ok: true,
21642
+ status: 200,
21643
+ data: {
21644
+ username: input.username,
21645
+ packageCount: packages.length,
21646
+ packages
21647
+ }
21648
+ };
21649
+ }
21467
21650
  }
21468
21651
  ];
21469
21652
 
@@ -21521,28 +21704,13 @@ var dependencyTools = [
21521
21704
  }),
21522
21705
  handler: async (input) => {
21523
21706
  const maxDepth = input.depth ?? 3;
21524
- const MAX_CONCURRENT = 10;
21707
+ const runLimited = createLimiter(10);
21525
21708
  const packumentCache = /* @__PURE__ */ new Map();
21526
21709
  const resolved = /* @__PURE__ */ new Set();
21527
21710
  const tree = {};
21528
21711
  const warnings = [];
21529
- let active = 0;
21530
- const queue = [];
21531
- function runLimited(fn) {
21532
- return new Promise((resolve2, reject) => {
21533
- const run = () => {
21534
- active++;
21535
- fn().then(resolve2, reject).finally(() => {
21536
- active--;
21537
- if (queue.length > 0) queue.shift()();
21538
- });
21539
- };
21540
- if (active < MAX_CONCURRENT) run();
21541
- else queue.push(run);
21542
- });
21543
- }
21544
- async function resolve(name, versionHint, currentDepth) {
21545
- const hintKey = `${name}@${versionHint}`;
21712
+ async function resolve(name, versionHint2, currentDepth) {
21713
+ const hintKey = `${name}@${versionHint2}`;
21546
21714
  if (resolved.has(hintKey) || currentDepth > maxDepth) return;
21547
21715
  resolved.add(hintKey);
21548
21716
  let pkg = packumentCache.get(name);
@@ -21550,19 +21718,21 @@ var dependencyTools = [
21550
21718
  const res = await runLimited(() => registryGetAbbreviated(`/${encPkg(name)}`));
21551
21719
  if (!res.ok) {
21552
21720
  warnings.push(`Failed to fetch ${name}: ${res.error}`);
21553
- tree[hintKey] = { version: versionHint, dependencies: {} };
21721
+ tree[hintKey] = { version: versionHint2, dependencies: {} };
21554
21722
  return;
21555
21723
  }
21556
21724
  pkg = res.data;
21557
21725
  packumentCache.set(name, pkg);
21558
21726
  }
21559
21727
  let resolvedVersion;
21560
- if (pkg.versions[versionHint]) {
21561
- resolvedVersion = versionHint;
21562
- } else if (pkg["dist-tags"][versionHint]) {
21563
- resolvedVersion = pkg["dist-tags"][versionHint];
21728
+ if (pkg.versions[versionHint2]) {
21729
+ resolvedVersion = versionHint2;
21730
+ } else if (pkg["dist-tags"][versionHint2]) {
21731
+ resolvedVersion = pkg["dist-tags"][versionHint2];
21564
21732
  } else {
21565
- resolvedVersion = pkg["dist-tags"]?.latest ?? versionHint;
21733
+ const available = Object.keys(pkg.versions);
21734
+ const matched = maxSatisfying(available, versionHint2);
21735
+ resolvedVersion = matched ?? pkg["dist-tags"]?.latest ?? versionHint2;
21566
21736
  }
21567
21737
  const resolvedKey = `${name}@${resolvedVersion}`;
21568
21738
  if (tree[resolvedKey]) return;
@@ -21578,12 +21748,14 @@ var dependencyTools = [
21578
21748
  await Promise.all(tasks);
21579
21749
  }
21580
21750
  }
21581
- await resolve(input.name, input.version ?? "latest", 1);
21751
+ const versionHint = input.version ?? "latest";
21752
+ await resolve(input.name, versionHint, 1);
21753
+ const rootKey = Object.keys(tree).find((k) => k.startsWith(`${input.name}@`)) ?? `${input.name}@${versionHint}`;
21582
21754
  return {
21583
21755
  ok: true,
21584
21756
  status: 200,
21585
21757
  data: {
21586
- root: `${input.name}@${input.version ?? "latest"}`,
21758
+ root: rootKey,
21587
21759
  depth: maxDepth,
21588
21760
  totalPackages: Object.keys(tree).length,
21589
21761
  tree,
@@ -21614,39 +21786,28 @@ var dependencyTools = [
21614
21786
  const res = await registryGet(`/${encPkg(input.name)}/${ver}`);
21615
21787
  if (!res.ok) return res;
21616
21788
  const pkg = res.data;
21617
- const deps = Object.keys(pkg.dependencies ?? {});
21618
- const MAX_CONCURRENT = 10;
21619
- let active = 0;
21620
- const queue = [];
21621
- function runLimited(fn) {
21622
- return new Promise((resolve, reject) => {
21623
- const run = () => {
21624
- active++;
21625
- fn().then(resolve, reject).finally(() => {
21626
- active--;
21627
- if (queue.length > 0) queue.shift()();
21628
- });
21629
- };
21630
- if (active < MAX_CONCURRENT) run();
21631
- else queue.push(run);
21632
- });
21633
- }
21789
+ const depEntries = Object.entries(pkg.dependencies ?? {});
21790
+ const runLimited = createLimiter(10);
21634
21791
  const depLicenses = await Promise.all(
21635
- deps.map(async (depName) => {
21636
- const depRes = await runLimited(() => registryGet(`/${encPkg(depName)}/latest`));
21792
+ depEntries.map(async ([depName, depRange]) => {
21793
+ const abbrevRes = await runLimited(() => registryGetAbbreviated(`/${encPkg(depName)}`));
21794
+ if (!abbrevRes.ok) return { name: depName, version: depRange, license: "FETCH_ERROR" };
21795
+ const abbrev = abbrevRes.data;
21796
+ const available = Object.keys(abbrev.versions);
21797
+ const resolved = maxSatisfying(available, depRange) ?? abbrev["dist-tags"]?.latest;
21798
+ if (!resolved) return { name: depName, version: depRange, license: "UNKNOWN" };
21799
+ const verRes = await runLimited(() => registryGet(`/${encPkg(depName)}/${resolved}`));
21637
21800
  return {
21638
21801
  name: depName,
21639
- license: depRes.ok ? depRes.data?.license ?? "UNKNOWN" : "FETCH_ERROR"
21802
+ version: resolved,
21803
+ license: verRes.ok ? verRes.data?.license ?? "UNKNOWN" : "FETCH_ERROR"
21640
21804
  };
21641
21805
  })
21642
21806
  );
21643
21807
  const allowedSet = new Set(
21644
21808
  input.allowed ?? ["MIT", "ISC", "BSD-2-Clause", "BSD-3-Clause", "Apache-2.0", "0BSD", "Unlicense"]
21645
21809
  );
21646
- const results = [
21647
- { name: pkg.name, version: pkg.version, license: pkg.license ?? "UNKNOWN" },
21648
- ...depLicenses.map((d) => ({ name: d.name, version: "latest", license: d.license }))
21649
- ];
21810
+ const results = [{ name: pkg.name, version: pkg.version, license: pkg.license ?? "UNKNOWN" }, ...depLicenses];
21650
21811
  const flagged = results.filter((r) => !allowedSet.has(r.license));
21651
21812
  return {
21652
21813
  ok: true,
@@ -21744,57 +21905,6 @@ var downloadTools = [
21744
21905
  }
21745
21906
  ];
21746
21907
 
21747
- // src/tools/hooks.ts
21748
- var hookTools = [
21749
- {
21750
- name: "npm_hooks",
21751
- 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.",
21752
- annotations: {
21753
- title: "List npm hooks",
21754
- readOnlyHint: true,
21755
- destructiveHint: false,
21756
- idempotentHint: true,
21757
- openWorldHint: true
21758
- },
21759
- inputSchema: external_exports.object({
21760
- package: external_exports.string().optional().describe("Filter hooks by package name"),
21761
- limit: external_exports.number().min(1).max(100).optional().describe("Max results (default: 25)"),
21762
- offset: external_exports.number().min(0).optional().describe("Pagination offset")
21763
- }),
21764
- handler: async (input) => {
21765
- const authErr = requireAuth();
21766
- if (authErr) return authErr;
21767
- const params = new URLSearchParams();
21768
- if (input.package) params.set("package", input.package);
21769
- if (input.limit !== void 0) params.set("limit", String(input.limit));
21770
- if (input.offset !== void 0) params.set("offset", String(input.offset));
21771
- const qs = params.toString();
21772
- const path = `/-/npm/v1/hooks${qs ? `?${qs}` : ""}`;
21773
- const res = await registryGetAuth(path);
21774
- if (!res.ok) return res;
21775
- const data = res.data;
21776
- return {
21777
- ok: true,
21778
- status: 200,
21779
- data: {
21780
- total: data.total,
21781
- hooks: data.objects.map((h) => ({
21782
- id: h.id,
21783
- type: h.type,
21784
- name: h.name,
21785
- endpoint: h.endpoint,
21786
- status: h.status,
21787
- lastDelivery: h.last_delivery,
21788
- lastResponseCode: h.response_code,
21789
- created: h.created,
21790
- updated: h.updated
21791
- }))
21792
- }
21793
- };
21794
- }
21795
- }
21796
- ];
21797
-
21798
21908
  // src/tools/orgs.ts
21799
21909
  var orgTools = [
21800
21910
  {
@@ -22019,7 +22129,7 @@ var packageTools = [
22019
22129
  },
22020
22130
  {
22021
22131
  name: "npm_versions",
22022
- description: "List all published versions of a package with their publish dates, ordered newest first.",
22132
+ 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.",
22023
22133
  annotations: {
22024
22134
  title: "List versions",
22025
22135
  readOnlyHint: true,
@@ -22028,25 +22138,29 @@ var packageTools = [
22028
22138
  openWorldHint: true
22029
22139
  },
22030
22140
  inputSchema: external_exports.object({
22031
- name: external_exports.string().describe("Package name")
22141
+ name: external_exports.string().describe("Package name"),
22142
+ limit: external_exports.number().min(0).optional().describe("Max versions to return, newest first (default 50, 0 = all)")
22032
22143
  }),
22033
22144
  handler: async (input) => {
22034
22145
  const res = await registryGet(`/${encPkg(input.name)}`);
22035
22146
  if (!res.ok) return res;
22036
22147
  const pkg = res.data;
22037
- const versions = Object.keys(pkg.versions).map((v) => ({
22148
+ const limit = input.limit ?? 50;
22149
+ const allVersions = Object.keys(pkg.versions).map((v) => ({
22038
22150
  version: v,
22039
22151
  date: pkg.time[v],
22040
22152
  deprecated: pkg.versions[v].deprecated,
22041
22153
  npmUser: pkg.versions[v]._npmUser?.name
22042
22154
  })).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
22155
+ const versions = limit > 0 ? allVersions.slice(0, limit) : allVersions;
22043
22156
  return {
22044
22157
  ok: true,
22045
22158
  status: 200,
22046
22159
  data: {
22047
22160
  name: pkg.name,
22048
22161
  distTags: pkg["dist-tags"],
22049
- total: versions.length,
22162
+ total: allVersions.length,
22163
+ showing: versions.length,
22050
22164
  versions
22051
22165
  }
22052
22166
  };
@@ -22091,6 +22205,61 @@ var packageTools = [
22091
22205
  handler: async (input) => {
22092
22206
  return registryGet(`/-/package/${encPkg(input.name)}/dist-tags`);
22093
22207
  }
22208
+ },
22209
+ {
22210
+ name: "npm_types",
22211
+ 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).",
22212
+ annotations: {
22213
+ title: "Check TypeScript types",
22214
+ readOnlyHint: true,
22215
+ destructiveHint: false,
22216
+ idempotentHint: true,
22217
+ openWorldHint: true
22218
+ },
22219
+ inputSchema: external_exports.object({
22220
+ name: external_exports.string().describe("Package name (e.g. 'express' or '@anthropic-ai/sdk')"),
22221
+ version: external_exports.string().optional().describe("Semver version or dist-tag (default: 'latest')")
22222
+ }),
22223
+ handler: async (input) => {
22224
+ const ver = input.version ?? "latest";
22225
+ let typesPackage;
22226
+ if (input.name.startsWith("@")) {
22227
+ const withoutAt = input.name.slice(1);
22228
+ typesPackage = `@types/${withoutAt.replace("/", "__")}`;
22229
+ } else {
22230
+ typesPackage = `@types/${input.name}`;
22231
+ }
22232
+ const [versionRes, typesRes] = await Promise.all([
22233
+ registryGet(`/${encPkg(input.name)}/${ver}`),
22234
+ registryGet(`/${encPkg(typesPackage)}`)
22235
+ ]);
22236
+ if (!versionRes.ok) return versionRes;
22237
+ const v = versionRes.data;
22238
+ const hasBuiltinTypes = !!(v.types || v.typings);
22239
+ const typesEntry = hasBuiltinTypes ? v.types ?? v.typings : void 0;
22240
+ const hasTypesPackage = typesRes.ok;
22241
+ const typesPackageLatest = hasTypesPackage ? typesRes.data?.["dist-tags"]?.latest : void 0;
22242
+ let recommendation;
22243
+ if (hasBuiltinTypes) {
22244
+ recommendation = "Built-in types included \u2014 no additional install needed.";
22245
+ } else if (hasTypesPackage) {
22246
+ recommendation = `Install types separately: npm install -D ${typesPackage}`;
22247
+ } else {
22248
+ recommendation = "No TypeScript types available (built-in or @types).";
22249
+ }
22250
+ return {
22251
+ ok: true,
22252
+ status: 200,
22253
+ data: {
22254
+ name: v.name,
22255
+ version: v.version,
22256
+ builtinTypes: hasBuiltinTypes,
22257
+ typesEntry,
22258
+ typesPackage: hasTypesPackage ? { name: typesPackage, latest: typesPackageLatest } : null,
22259
+ recommendation
22260
+ }
22261
+ };
22262
+ }
22094
22263
  }
22095
22264
  ];
22096
22265
 
@@ -22157,7 +22326,7 @@ var registryTools = [
22157
22326
  },
22158
22327
  {
22159
22328
  name: "npm_recent_changes",
22160
- description: "Get the most recent package publishes/updates from the npm registry via the CouchDB changes feed.",
22329
+ 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.",
22161
22330
  annotations: {
22162
22331
  title: "Recent registry changes",
22163
22332
  readOnlyHint: true,
@@ -22170,21 +22339,20 @@ var registryTools = [
22170
22339
  }),
22171
22340
  handler: async (input) => {
22172
22341
  const limit = input.limit ?? 25;
22173
- const dbRes = await replicateGet("/");
22174
- if (!dbRes.ok) return dbRes;
22175
- const since = dbRes.data.update_seq - limit;
22176
- const changesRes = await replicateGet(`/_changes?since=${since}&limit=${limit}&descending=false`);
22342
+ const [dbRes, changesRes] = await Promise.all([
22343
+ replicateGet("/"),
22344
+ replicateGet(`/_changes?limit=${limit}&descending=true`)
22345
+ ]);
22177
22346
  if (!changesRes.ok) return changesRes;
22178
22347
  const changes = changesRes.data.results.map((r) => ({
22179
22348
  package: r.id,
22180
- seq: r.seq,
22181
22349
  rev: r.changes[0]?.rev
22182
22350
  }));
22183
22351
  return {
22184
22352
  ok: true,
22185
22353
  status: 200,
22186
22354
  data: {
22187
- totalPackages: dbRes.data.doc_count,
22355
+ totalPackages: dbRes.ok ? dbRes.data.doc_count : null,
22188
22356
  changes
22189
22357
  }
22190
22358
  };
@@ -22230,9 +22398,7 @@ var searchTools = [
22230
22398
  publisher: obj.package.publisher,
22231
22399
  keywords: obj.package.keywords,
22232
22400
  links: obj.package.links,
22233
- score: obj.score.detail,
22234
- downloads: obj.downloads,
22235
- dependents: obj.dependents
22401
+ score: obj.score.detail
22236
22402
  }));
22237
22403
  return { ok: true, status: 200, data: { total: res.data.total, results } };
22238
22404
  }
@@ -22243,7 +22409,7 @@ var searchTools = [
22243
22409
  var securityTools = [
22244
22410
  {
22245
22411
  name: "npm_audit",
22246
- description: "Check specific packages and versions for known vulnerabilities. Returns advisories with severity, CVEs, and patched versions.",
22412
+ 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.",
22247
22413
  annotations: {
22248
22414
  title: "Audit packages",
22249
22415
  readOnlyHint: true,
@@ -22260,7 +22426,7 @@ var securityTools = [
22260
22426
  },
22261
22427
  {
22262
22428
  name: "npm_audit_deep",
22263
- description: "Run a full security audit on a set of dependencies. Returns detailed advisories with CVSS scores, CWEs, fix recommendations, and vulnerability metadata.",
22429
+ 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).",
22264
22430
  annotations: {
22265
22431
  title: "Deep security audit",
22266
22432
  readOnlyHint: true,
@@ -22302,6 +22468,60 @@ var securityTools = [
22302
22468
  }
22303
22469
  ];
22304
22470
 
22471
+ // src/tools/trust.ts
22472
+ var trustTools = [
22473
+ {
22474
+ name: "npm_trusted_publishers",
22475
+ 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.",
22476
+ annotations: {
22477
+ title: "List trusted publishers",
22478
+ readOnlyHint: true,
22479
+ destructiveHint: false,
22480
+ idempotentHint: true,
22481
+ openWorldHint: true
22482
+ },
22483
+ inputSchema: external_exports.object({
22484
+ name: external_exports.string().describe("Package name (e.g. 'express' or '@yawlabs/npmjs-mcp')")
22485
+ }),
22486
+ handler: async (input) => {
22487
+ const authErr = requireAuth();
22488
+ if (authErr) return authErr;
22489
+ const res = await registryGetAuth(`/-/package/${encPkg(input.name)}/trust`);
22490
+ if (!res.ok) return res;
22491
+ const configs = (res.data ?? []).map((c) => {
22492
+ const result = {
22493
+ id: c.id,
22494
+ provider: c.type
22495
+ };
22496
+ if (c.type === "github") {
22497
+ result.repository = c.claims?.repository;
22498
+ const workflowRef = c.claims?.workflow_ref;
22499
+ result.workflowFile = workflowRef?.file;
22500
+ result.environment = c.claims?.environment;
22501
+ } else if (c.type === "gitlab") {
22502
+ result.project = c.claims?.project_path;
22503
+ const configRef = c.claims?.ci_config_ref_uri;
22504
+ result.configFile = configRef?.file;
22505
+ result.environment = c.claims?.environment;
22506
+ } else if (c.type === "circleci") {
22507
+ result.project = c.claims?.project_id;
22508
+ result.context = c.claims?.context_ids;
22509
+ }
22510
+ return result;
22511
+ });
22512
+ return {
22513
+ ok: true,
22514
+ status: 200,
22515
+ data: {
22516
+ package: input.name,
22517
+ trustedPublisherCount: configs.length,
22518
+ trustedPublishers: configs
22519
+ }
22520
+ };
22521
+ }
22522
+ }
22523
+ ];
22524
+
22305
22525
  // src/tools/workflows.ts
22306
22526
  var workflowTools = [
22307
22527
  {
@@ -22576,7 +22796,7 @@ var workflowTools = [
22576
22796
  ];
22577
22797
 
22578
22798
  // src/index.ts
22579
- var version2 = true ? "0.3.0" : (await null).createRequire(import.meta.url)("../package.json").version;
22799
+ var version2 = true ? "0.5.0" : (await null).createRequire(import.meta.url)("../package.json").version;
22580
22800
  var subcommand = process.argv[2];
22581
22801
  if (subcommand === "version" || subcommand === "--version") {
22582
22802
  console.log(version2);
@@ -22594,7 +22814,7 @@ var allTools = [
22594
22814
  ...orgTools,
22595
22815
  ...accessTools,
22596
22816
  ...provenanceTools,
22597
- ...hookTools,
22817
+ ...trustTools,
22598
22818
  ...workflowTools
22599
22819
  ];
22600
22820
  var server = new McpServer({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/npmjs-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.5.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>",