@yawlabs/npmjs-mcp 0.11.0 → 0.11.2

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 +103 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -30174,6 +30174,9 @@ function validateUsername(username) {
30174
30174
  function validateTeam(team) {
30175
30175
  return validateIdent(team, "Team name");
30176
30176
  }
30177
+ function validateTag(tag) {
30178
+ return validateIdent(tag, "Tag name");
30179
+ }
30177
30180
  function encPkg(name) {
30178
30181
  const err = validatePackageName(name);
30179
30182
  if (err) throw new Error(err);
@@ -30194,6 +30197,11 @@ function encTeam(team) {
30194
30197
  if (err) throw new Error(err);
30195
30198
  return encodeURIComponent(team);
30196
30199
  }
30200
+ function encTag(tag) {
30201
+ const err = validateTag(tag);
30202
+ if (err) throw new Error(err);
30203
+ return encodeURIComponent(tag);
30204
+ }
30197
30205
  function isAuthenticated() {
30198
30206
  return !!process.env.NPM_TOKEN;
30199
30207
  }
@@ -30429,10 +30437,15 @@ function maxSatisfying(versions, range) {
30429
30437
  for (const sub of subRanges) {
30430
30438
  const parsed = parseRange(sub);
30431
30439
  if (!parsed) continue;
30440
+ const anchorMatch = sub.match(/(\d+)\.(\d+)\.(\d+)-/);
30441
+ const prereleaseAnchor = anchorMatch ? [Number(anchorMatch[1]), Number(anchorMatch[2]), Number(anchorMatch[3])] : null;
30432
30442
  for (const v of versions) {
30433
- if (v.includes("-") && !sub.includes("-")) continue;
30434
30443
  const vp = parseSemver(v);
30435
30444
  if (!vp) continue;
30445
+ if (v.includes("-")) {
30446
+ if (!prereleaseAnchor) continue;
30447
+ if (vp[0] !== prereleaseAnchor[0] || vp[1] !== prereleaseAnchor[1] || vp[2] !== prereleaseAnchor[2]) continue;
30448
+ }
30436
30449
  if (parsed.min && cmpSemver(vp, parsed.min) < 0) continue;
30437
30450
  if (parsed.max && cmpSemver(vp, parsed.max) >= 0) continue;
30438
30451
  if (!bestParsed || cmpSemver(vp, bestParsed) > 0) {
@@ -31178,7 +31191,7 @@ var downloadTools = [
31178
31191
  },
31179
31192
  {
31180
31193
  name: "npm_downloads_bulk",
31181
- description: "Compare download counts for multiple packages over a period. Up to 128 packages.",
31194
+ description: "Compare download counts for multiple packages over a period. Up to 128 packages. Scoped packages (@scope/name) are NOT supported by the bulk endpoint \u2014 call npm_downloads separately for each scoped package.",
31182
31195
  annotations: {
31183
31196
  title: "Bulk download comparison",
31184
31197
  readOnlyHint: true,
@@ -31187,11 +31200,19 @@ var downloadTools = [
31187
31200
  openWorldHint: true
31188
31201
  },
31189
31202
  inputSchema: external_exports3.object({
31190
- packages: external_exports3.array(external_exports3.string()).min(1).max(128).describe("Array of package names to compare"),
31203
+ packages: external_exports3.array(external_exports3.string()).min(1).max(128).describe("Array of package names to compare (unscoped only)"),
31191
31204
  period: external_exports3.string().optional().describe("Period (default: 'last-week')")
31192
31205
  }),
31193
31206
  handler: async (input) => {
31194
31207
  const period = input.period ?? "last-week";
31208
+ const scoped = input.packages.filter((p) => p.startsWith("@"));
31209
+ if (scoped.length > 0) {
31210
+ return {
31211
+ ok: false,
31212
+ status: 400,
31213
+ error: `npm_downloads_bulk does not support scoped packages (received: ${scoped.join(", ")}). Call npm_downloads separately for each scoped package.`
31214
+ };
31215
+ }
31195
31216
  const names = input.packages.map((p) => encPkg(p)).join(",");
31196
31217
  const res = await downloadsGet(`/downloads/point/${period}/${names}`);
31197
31218
  return res.ok ? res : translateError(res, { op: `downloads_bulk ${period}` });
@@ -31225,6 +31246,7 @@ function classifyHookTarget(target) {
31225
31246
  if (/^@[^/]+$/.test(target)) return { type: "scope", name: target };
31226
31247
  return { type: "package", name: target };
31227
31248
  }
31249
+ var httpsEndpoint = external_exports3.string().url().refine((u) => u.startsWith("https://"), { message: "Endpoint must use https://" });
31228
31250
  function stripSecrets(data) {
31229
31251
  if (Array.isArray(data)) {
31230
31252
  return data.map((item) => stripSecrets(item));
@@ -31252,7 +31274,7 @@ var hookTools = [
31252
31274
  },
31253
31275
  inputSchema: external_exports3.object({
31254
31276
  target: external_exports3.string().describe("Hook target: 'pkg', '@scope/pkg', '@scope', or '~user'"),
31255
- endpoint: external_exports3.string().url().describe("HTTPS URL that will receive POST events"),
31277
+ endpoint: httpsEndpoint.describe("HTTPS URL that will receive POST events"),
31256
31278
  secret: external_exports3.string().describe("Secret used to HMAC-sign webhook payloads")
31257
31279
  }),
31258
31280
  handler: async (input) => {
@@ -31330,7 +31352,7 @@ var hookTools = [
31330
31352
  },
31331
31353
  inputSchema: external_exports3.object({
31332
31354
  id: external_exports3.string().describe("Hook ID"),
31333
- endpoint: external_exports3.string().url().describe("New HTTPS URL"),
31355
+ endpoint: httpsEndpoint.describe("New HTTPS URL"),
31334
31356
  secret: external_exports3.string().describe("New signing secret")
31335
31357
  }),
31336
31358
  handler: async (input) => {
@@ -31385,7 +31407,9 @@ var orgTools = [
31385
31407
  handler: async (input) => {
31386
31408
  const authErr = requireAuth();
31387
31409
  if (authErr) return authErr;
31388
- const res = await registryGetAuth(`/-/org/${encodeURIComponent(input.org)}/user`);
31410
+ const scopeErr = validateScope(input.org);
31411
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31412
+ const res = await registryGetAuth(`/-/org/${encScope(input.org)}/user`);
31389
31413
  if (!res.ok) return translateError(res, { op: `org_members ${input.org}` });
31390
31414
  const members = Object.entries(res.data).map(([username, role]) => ({ username, role }));
31391
31415
  return {
@@ -31415,7 +31439,9 @@ var orgTools = [
31415
31439
  handler: async (input) => {
31416
31440
  const authErr = requireAuth();
31417
31441
  if (authErr) return authErr;
31418
- const res = await registryGetAuth(`/-/org/${encodeURIComponent(input.org)}/package`);
31442
+ const scopeErr = validateScope(input.org);
31443
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31444
+ const res = await registryGetAuth(`/-/org/${encScope(input.org)}/package`);
31419
31445
  if (!res.ok) return translateError(res, { op: `org_packages ${input.org}` });
31420
31446
  const packages = Object.entries(res.data).map(([name, access]) => ({ name, access }));
31421
31447
  return {
@@ -31445,7 +31471,9 @@ var orgTools = [
31445
31471
  handler: async (input) => {
31446
31472
  const authErr = requireAuth();
31447
31473
  if (authErr) return authErr;
31448
- const res = await registryGetAuth(`/-/org/${encodeURIComponent(input.org)}/team`);
31474
+ const scopeErr = validateScope(input.org);
31475
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31476
+ const res = await registryGetAuth(`/-/org/${encScope(input.org)}/team`);
31449
31477
  if (!res.ok) return translateError(res, { op: `org_teams ${input.org}` });
31450
31478
  return {
31451
31479
  ok: true,
@@ -31475,8 +31503,12 @@ var orgTools = [
31475
31503
  handler: async (input) => {
31476
31504
  const authErr = requireAuth();
31477
31505
  if (authErr) return authErr;
31506
+ const scopeErr = validateScope(input.org);
31507
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31508
+ const teamErr = validateTeam(input.team);
31509
+ if (teamErr) return { ok: false, status: 400, error: teamErr };
31478
31510
  const res = await registryGetAuth(
31479
- `/-/team/${encodeURIComponent(input.org)}/${encodeURIComponent(input.team)}/package`
31511
+ `/-/team/${encScope(input.org)}/${encTeam(input.team)}/package`
31480
31512
  );
31481
31513
  if (!res.ok) return translateError(res, { op: `team_packages ${input.org}:${input.team}` });
31482
31514
  const packages = Object.entries(res.data).map(([name, permissions]) => ({ name, permissions }));
@@ -31491,6 +31523,44 @@ var orgTools = [
31491
31523
  }
31492
31524
  };
31493
31525
  }
31526
+ },
31527
+ {
31528
+ name: "npm_team_members",
31529
+ description: "List all members of a team with their roles (e.g. 'developer'). Complements npm_team_member_add and npm_team_member_remove \u2014 use this to audit who is currently on the team before adding or removing members.",
31530
+ annotations: {
31531
+ title: "List team members",
31532
+ readOnlyHint: true,
31533
+ destructiveHint: false,
31534
+ idempotentHint: true,
31535
+ openWorldHint: true
31536
+ },
31537
+ inputSchema: external_exports3.object({
31538
+ org: external_exports3.string().describe("Organization name (without @ prefix)"),
31539
+ team: external_exports3.string().describe("Team name")
31540
+ }),
31541
+ handler: async (input) => {
31542
+ const authErr = requireAuth();
31543
+ if (authErr) return authErr;
31544
+ const scopeErr = validateScope(input.org);
31545
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31546
+ const teamErr = validateTeam(input.team);
31547
+ if (teamErr) return { ok: false, status: 400, error: teamErr };
31548
+ const res = await registryGetAuth(
31549
+ `/-/team/${encScope(input.org)}/${encTeam(input.team)}/user`
31550
+ );
31551
+ if (!res.ok) return translateError(res, { op: `team_members ${input.org}:${input.team}` });
31552
+ const members = Object.entries(res.data).map(([username, role]) => ({ username, role }));
31553
+ return {
31554
+ ok: true,
31555
+ status: 200,
31556
+ data: {
31557
+ org: input.org,
31558
+ team: input.team,
31559
+ memberCount: members.length,
31560
+ members
31561
+ }
31562
+ };
31563
+ }
31494
31564
  }
31495
31565
  ];
31496
31566
 
@@ -31608,20 +31678,21 @@ var packageTools = [
31608
31678
  if (!res.ok) return translateError(res, { pkg: input.name, op: "versions" });
31609
31679
  const pkg = res.data;
31610
31680
  const limit = input.limit ?? 50;
31611
- const allVersions = Object.keys(pkg.versions).map((v) => ({
31681
+ const totalPublished = Object.keys(pkg.versions).length;
31682
+ const dated = Object.keys(pkg.versions).map((v) => ({
31612
31683
  version: v,
31613
31684
  date: pkg.time[v],
31614
31685
  deprecated: pkg.versions[v].deprecated,
31615
31686
  npmUser: pkg.versions[v]._npmUser?.name
31616
- })).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
31617
- const versions = limit > 0 ? allVersions.slice(0, limit) : allVersions;
31687
+ })).filter((r) => r.date).sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
31688
+ const versions = limit > 0 ? dated.slice(0, limit) : dated;
31618
31689
  return {
31619
31690
  ok: true,
31620
31691
  status: 200,
31621
31692
  data: {
31622
31693
  name: pkg.name,
31623
31694
  distTags: pkg["dist-tags"],
31624
- total: allVersions.length,
31695
+ total: totalPublished,
31625
31696
  showing: versions.length,
31626
31697
  versions
31627
31698
  }
@@ -32307,6 +32378,12 @@ var workflowTools = [
32307
32378
  detail: `"${input.name}" exists. Maintainers: ${maintainers.join(", ") || "unknown"}. Authenticate to check your access.`
32308
32379
  });
32309
32380
  }
32381
+ } else {
32382
+ checks.push({
32383
+ check: "Package name availability",
32384
+ status: "warn",
32385
+ detail: `Could not verify whether "${input.name}" is available or whether you have access (registry returned HTTP ${pkgRes.status}). Re-run npm_publish_preflight before publishing.`
32386
+ });
32310
32387
  }
32311
32388
  if (twoFactorAuth && twoFactorAuth !== "disabled" && canPublishHeadless !== true) {
32312
32389
  actions.push({
@@ -32374,6 +32451,7 @@ function parseTeamTarget(target) {
32374
32451
  function highestVersion(versions) {
32375
32452
  const parsed = [];
32376
32453
  for (const v of versions) {
32454
+ if (v.includes("-")) continue;
32377
32455
  const m = v.match(/^(\d+)\.(\d+)\.(\d+)/);
32378
32456
  if (m) parsed.push([Number(m[1]), Number(m[2]), Number(m[3]), v]);
32379
32457
  }
@@ -32575,6 +32653,7 @@ var writeTools = [
32575
32653
  tarballError = "could not re-fetch packument for tarball DELETE rev";
32576
32654
  }
32577
32655
  }
32656
+ const complete = !tarballUrl || tarballDeleted;
32578
32657
  return {
32579
32658
  ok: true,
32580
32659
  status: 200,
@@ -32582,6 +32661,7 @@ var writeTools = [
32582
32661
  package: input.name,
32583
32662
  unpublishedVersion: input.version,
32584
32663
  remainingVersions: Object.keys(packument.versions),
32664
+ complete,
32585
32665
  tarballDeleted,
32586
32666
  ...tarballError ? { tarballWarning: tarballError } : {}
32587
32667
  }
@@ -32655,8 +32735,10 @@ var writeTools = [
32655
32735
  handler: async (input) => {
32656
32736
  const authErr = requireAuth();
32657
32737
  if (authErr) return authErr;
32738
+ const tagErr = validateTag(input.tag);
32739
+ if (tagErr) return { ok: false, status: 400, error: tagErr };
32658
32740
  const putRes = await registryPutAuth(
32659
- `/-/package/${encPkg(input.name)}/dist-tags/${encodeURIComponent(input.tag)}`,
32741
+ `/-/package/${encPkg(input.name)}/dist-tags/${encTag(input.tag)}`,
32660
32742
  input.version
32661
32743
  );
32662
32744
  if (!putRes.ok) return translateError(putRes, { pkg: input.name, op: `dist-tag set ${input.tag}` });
@@ -32691,6 +32773,8 @@ var writeTools = [
32691
32773
  handler: async (input) => {
32692
32774
  const authErr = requireAuth();
32693
32775
  if (authErr) return authErr;
32776
+ const tagErr = validateTag(input.tag);
32777
+ if (tagErr) return { ok: false, status: 400, error: tagErr };
32694
32778
  if (input.tag === "latest") {
32695
32779
  return {
32696
32780
  ok: false,
@@ -32698,9 +32782,7 @@ var writeTools = [
32698
32782
  error: "The 'latest' tag cannot be removed. Use npm_dist_tag_set to reassign it to a different version."
32699
32783
  };
32700
32784
  }
32701
- const delRes = await registryDeleteAuth(
32702
- `/-/package/${encPkg(input.name)}/dist-tags/${encodeURIComponent(input.tag)}`
32703
- );
32785
+ const delRes = await registryDeleteAuth(`/-/package/${encPkg(input.name)}/dist-tags/${encTag(input.tag)}`);
32704
32786
  if (!delRes.ok) return translateError(delRes, { pkg: input.name, op: `dist-tag remove ${input.tag}` });
32705
32787
  return {
32706
32788
  ok: true,
@@ -33155,7 +33237,9 @@ var writeTools = [
33155
33237
  if (input.role) body.role = input.role;
33156
33238
  const res = await registryPutAuth(`/-/org/${encScope(org)}/user`, body);
33157
33239
  if (!res.ok) return translateError(res, { op: `org_member_set ${org}/${user}` });
33158
- return { ok: true, status: 200, data: { org, user, role: input.role } };
33240
+ const data = { org, user };
33241
+ if (input.role) data.role = input.role;
33242
+ return { ok: true, status: 200, data };
33159
33243
  }
33160
33244
  },
33161
33245
  // ───────────────────────────────────────────────────────
@@ -33234,7 +33318,7 @@ var writeTools = [
33234
33318
  ];
33235
33319
 
33236
33320
  // src/index.ts
33237
- var version2 = true ? "0.11.0" : (await null).createRequire(import.meta.url)("../package.json").version;
33321
+ var version2 = true ? "0.11.2" : (await null).createRequire(import.meta.url)("../package.json").version;
33238
33322
  var subcommand = process.argv[2];
33239
33323
  if (subcommand === "version" || subcommand === "--version") {
33240
33324
  console.log(version2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/npmjs-mcp",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
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>",