@yawlabs/npmjs-mcp 0.11.0 → 0.11.1

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 +82 -13
  2. package/package.json +57 -57
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,8 +30437,9 @@ function maxSatisfying(versions, range) {
30429
30437
  for (const sub of subRanges) {
30430
30438
  const parsed = parseRange(sub);
30431
30439
  if (!parsed) continue;
30440
+ const subTargetsPrerelease = /\d+\.\d+\.\d+-/.test(sub);
30432
30441
  for (const v of versions) {
30433
- if (v.includes("-") && !sub.includes("-")) continue;
30442
+ if (v.includes("-") && !subTargetsPrerelease) continue;
30434
30443
  const vp = parseSemver(v);
30435
30444
  if (!vp) continue;
30436
30445
  if (parsed.min && cmpSemver(vp, parsed.min) < 0) continue;
@@ -31178,7 +31187,7 @@ var downloadTools = [
31178
31187
  },
31179
31188
  {
31180
31189
  name: "npm_downloads_bulk",
31181
- description: "Compare download counts for multiple packages over a period. Up to 128 packages.",
31190
+ 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
31191
  annotations: {
31183
31192
  title: "Bulk download comparison",
31184
31193
  readOnlyHint: true,
@@ -31187,11 +31196,19 @@ var downloadTools = [
31187
31196
  openWorldHint: true
31188
31197
  },
31189
31198
  inputSchema: external_exports3.object({
31190
- packages: external_exports3.array(external_exports3.string()).min(1).max(128).describe("Array of package names to compare"),
31199
+ packages: external_exports3.array(external_exports3.string()).min(1).max(128).describe("Array of package names to compare (unscoped only)"),
31191
31200
  period: external_exports3.string().optional().describe("Period (default: 'last-week')")
31192
31201
  }),
31193
31202
  handler: async (input) => {
31194
31203
  const period = input.period ?? "last-week";
31204
+ const scoped = input.packages.filter((p) => p.startsWith("@"));
31205
+ if (scoped.length > 0) {
31206
+ return {
31207
+ ok: false,
31208
+ status: 400,
31209
+ error: `npm_downloads_bulk does not support scoped packages (received: ${scoped.join(", ")}). Call npm_downloads separately for each scoped package.`
31210
+ };
31211
+ }
31195
31212
  const names = input.packages.map((p) => encPkg(p)).join(",");
31196
31213
  const res = await downloadsGet(`/downloads/point/${period}/${names}`);
31197
31214
  return res.ok ? res : translateError(res, { op: `downloads_bulk ${period}` });
@@ -31385,7 +31402,9 @@ var orgTools = [
31385
31402
  handler: async (input) => {
31386
31403
  const authErr = requireAuth();
31387
31404
  if (authErr) return authErr;
31388
- const res = await registryGetAuth(`/-/org/${encodeURIComponent(input.org)}/user`);
31405
+ const scopeErr = validateScope(input.org);
31406
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31407
+ const res = await registryGetAuth(`/-/org/${encScope(input.org)}/user`);
31389
31408
  if (!res.ok) return translateError(res, { op: `org_members ${input.org}` });
31390
31409
  const members = Object.entries(res.data).map(([username, role]) => ({ username, role }));
31391
31410
  return {
@@ -31415,7 +31434,9 @@ var orgTools = [
31415
31434
  handler: async (input) => {
31416
31435
  const authErr = requireAuth();
31417
31436
  if (authErr) return authErr;
31418
- const res = await registryGetAuth(`/-/org/${encodeURIComponent(input.org)}/package`);
31437
+ const scopeErr = validateScope(input.org);
31438
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31439
+ const res = await registryGetAuth(`/-/org/${encScope(input.org)}/package`);
31419
31440
  if (!res.ok) return translateError(res, { op: `org_packages ${input.org}` });
31420
31441
  const packages = Object.entries(res.data).map(([name, access]) => ({ name, access }));
31421
31442
  return {
@@ -31445,7 +31466,9 @@ var orgTools = [
31445
31466
  handler: async (input) => {
31446
31467
  const authErr = requireAuth();
31447
31468
  if (authErr) return authErr;
31448
- const res = await registryGetAuth(`/-/org/${encodeURIComponent(input.org)}/team`);
31469
+ const scopeErr = validateScope(input.org);
31470
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31471
+ const res = await registryGetAuth(`/-/org/${encScope(input.org)}/team`);
31449
31472
  if (!res.ok) return translateError(res, { op: `org_teams ${input.org}` });
31450
31473
  return {
31451
31474
  ok: true,
@@ -31475,8 +31498,12 @@ var orgTools = [
31475
31498
  handler: async (input) => {
31476
31499
  const authErr = requireAuth();
31477
31500
  if (authErr) return authErr;
31501
+ const scopeErr = validateScope(input.org);
31502
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31503
+ const teamErr = validateTeam(input.team);
31504
+ if (teamErr) return { ok: false, status: 400, error: teamErr };
31478
31505
  const res = await registryGetAuth(
31479
- `/-/team/${encodeURIComponent(input.org)}/${encodeURIComponent(input.team)}/package`
31506
+ `/-/team/${encScope(input.org)}/${encTeam(input.team)}/package`
31480
31507
  );
31481
31508
  if (!res.ok) return translateError(res, { op: `team_packages ${input.org}:${input.team}` });
31482
31509
  const packages = Object.entries(res.data).map(([name, permissions]) => ({ name, permissions }));
@@ -31491,6 +31518,44 @@ var orgTools = [
31491
31518
  }
31492
31519
  };
31493
31520
  }
31521
+ },
31522
+ {
31523
+ name: "npm_team_members",
31524
+ 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.",
31525
+ annotations: {
31526
+ title: "List team members",
31527
+ readOnlyHint: true,
31528
+ destructiveHint: false,
31529
+ idempotentHint: true,
31530
+ openWorldHint: true
31531
+ },
31532
+ inputSchema: external_exports3.object({
31533
+ org: external_exports3.string().describe("Organization name (without @ prefix)"),
31534
+ team: external_exports3.string().describe("Team name")
31535
+ }),
31536
+ handler: async (input) => {
31537
+ const authErr = requireAuth();
31538
+ if (authErr) return authErr;
31539
+ const scopeErr = validateScope(input.org);
31540
+ if (scopeErr) return { ok: false, status: 400, error: scopeErr };
31541
+ const teamErr = validateTeam(input.team);
31542
+ if (teamErr) return { ok: false, status: 400, error: teamErr };
31543
+ const res = await registryGetAuth(
31544
+ `/-/team/${encScope(input.org)}/${encTeam(input.team)}/user`
31545
+ );
31546
+ if (!res.ok) return translateError(res, { op: `team_members ${input.org}:${input.team}` });
31547
+ const members = Object.entries(res.data).map(([username, role]) => ({ username, role }));
31548
+ return {
31549
+ ok: true,
31550
+ status: 200,
31551
+ data: {
31552
+ org: input.org,
31553
+ team: input.team,
31554
+ memberCount: members.length,
31555
+ members
31556
+ }
31557
+ };
31558
+ }
31494
31559
  }
31495
31560
  ];
31496
31561
 
@@ -32655,8 +32720,10 @@ var writeTools = [
32655
32720
  handler: async (input) => {
32656
32721
  const authErr = requireAuth();
32657
32722
  if (authErr) return authErr;
32723
+ const tagErr = validateTag(input.tag);
32724
+ if (tagErr) return { ok: false, status: 400, error: tagErr };
32658
32725
  const putRes = await registryPutAuth(
32659
- `/-/package/${encPkg(input.name)}/dist-tags/${encodeURIComponent(input.tag)}`,
32726
+ `/-/package/${encPkg(input.name)}/dist-tags/${encTag(input.tag)}`,
32660
32727
  input.version
32661
32728
  );
32662
32729
  if (!putRes.ok) return translateError(putRes, { pkg: input.name, op: `dist-tag set ${input.tag}` });
@@ -32691,6 +32758,8 @@ var writeTools = [
32691
32758
  handler: async (input) => {
32692
32759
  const authErr = requireAuth();
32693
32760
  if (authErr) return authErr;
32761
+ const tagErr = validateTag(input.tag);
32762
+ if (tagErr) return { ok: false, status: 400, error: tagErr };
32694
32763
  if (input.tag === "latest") {
32695
32764
  return {
32696
32765
  ok: false,
@@ -32698,9 +32767,7 @@ var writeTools = [
32698
32767
  error: "The 'latest' tag cannot be removed. Use npm_dist_tag_set to reassign it to a different version."
32699
32768
  };
32700
32769
  }
32701
- const delRes = await registryDeleteAuth(
32702
- `/-/package/${encPkg(input.name)}/dist-tags/${encodeURIComponent(input.tag)}`
32703
- );
32770
+ const delRes = await registryDeleteAuth(`/-/package/${encPkg(input.name)}/dist-tags/${encTag(input.tag)}`);
32704
32771
  if (!delRes.ok) return translateError(delRes, { pkg: input.name, op: `dist-tag remove ${input.tag}` });
32705
32772
  return {
32706
32773
  ok: true,
@@ -33155,7 +33222,9 @@ var writeTools = [
33155
33222
  if (input.role) body.role = input.role;
33156
33223
  const res = await registryPutAuth(`/-/org/${encScope(org)}/user`, body);
33157
33224
  if (!res.ok) return translateError(res, { op: `org_member_set ${org}/${user}` });
33158
- return { ok: true, status: 200, data: { org, user, role: input.role } };
33225
+ const data = { org, user };
33226
+ if (input.role) data.role = input.role;
33227
+ return { ok: true, status: 200, data };
33159
33228
  }
33160
33229
  },
33161
33230
  // ───────────────────────────────────────────────────────
@@ -33234,7 +33303,7 @@ var writeTools = [
33234
33303
  ];
33235
33304
 
33236
33305
  // src/index.ts
33237
- var version2 = true ? "0.11.0" : (await null).createRequire(import.meta.url)("../package.json").version;
33306
+ var version2 = true ? "0.11.1" : (await null).createRequire(import.meta.url)("../package.json").version;
33238
33307
  var subcommand = process.argv[2];
33239
33308
  if (subcommand === "version" || subcommand === "--version") {
33240
33309
  console.log(version2);
package/package.json CHANGED
@@ -1,57 +1,57 @@
1
- {
2
- "name": "@yawlabs/npmjs-mcp",
3
- "version": "0.11.0",
4
- "description": "npm registry MCP server — package intelligence, security audits, and dependency analysis for AI assistants",
5
- "license": "MIT",
6
- "author": "YawLabs <contact@yaw.sh>",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/YawLabs/npmjs-mcp.git"
10
- },
11
- "keywords": [
12
- "npm",
13
- "npmjs",
14
- "registry",
15
- "mcp",
16
- "model-context-protocol",
17
- "ai",
18
- "packages",
19
- "security",
20
- "audit",
21
- "dependencies"
22
- ],
23
- "type": "module",
24
- "main": "dist/index.js",
25
- "bin": {
26
- "npmjs-mcp": "dist/index.js"
27
- },
28
- "files": [
29
- "dist/index.js"
30
- ],
31
- "scripts": {
32
- "build": "tsc && node build.mjs",
33
- "dev": "tsc --watch",
34
- "start": "node dist/index.js",
35
- "test": "npm run build && node --test dist/api.test.js dist/tools/tools.test.js dist/tools/handlers.test.js dist/tools/writes.test.js dist/tools/hooks.test.js",
36
- "test:ci": "npm run test",
37
- "lint": "biome check src/",
38
- "lint:fix": "biome check --write src/",
39
- "prepublishOnly": "npm run build"
40
- },
41
- "dependencies": {},
42
- "overrides": {
43
- "hono": "^4.12.14",
44
- "@hono/node-server": "^1.19.13"
45
- },
46
- "devDependencies": {
47
- "@biomejs/biome": "^2.4.12",
48
- "@modelcontextprotocol/sdk": "^1.29.0",
49
- "@types/node": "^25.6.0",
50
- "esbuild": "^0.28.0",
51
- "typescript": "^6.0.3",
52
- "zod": "^4.3.6"
53
- },
54
- "engines": {
55
- "node": ">=18"
56
- }
57
- }
1
+ {
2
+ "name": "@yawlabs/npmjs-mcp",
3
+ "version": "0.11.1",
4
+ "description": "npm registry MCP server — package intelligence, security audits, and dependency analysis for AI assistants",
5
+ "license": "MIT",
6
+ "author": "YawLabs <contact@yaw.sh>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/YawLabs/npmjs-mcp.git"
10
+ },
11
+ "keywords": [
12
+ "npm",
13
+ "npmjs",
14
+ "registry",
15
+ "mcp",
16
+ "model-context-protocol",
17
+ "ai",
18
+ "packages",
19
+ "security",
20
+ "audit",
21
+ "dependencies"
22
+ ],
23
+ "type": "module",
24
+ "main": "dist/index.js",
25
+ "bin": {
26
+ "npmjs-mcp": "dist/index.js"
27
+ },
28
+ "files": [
29
+ "dist/index.js"
30
+ ],
31
+ "scripts": {
32
+ "build": "tsc && node build.mjs",
33
+ "dev": "tsc --watch",
34
+ "start": "node dist/index.js",
35
+ "test": "npm run build && node --test dist/api.test.js dist/tools/tools.test.js dist/tools/handlers.test.js dist/tools/writes.test.js dist/tools/hooks.test.js",
36
+ "test:ci": "npm run test",
37
+ "lint": "biome check src/",
38
+ "lint:fix": "biome check --write src/",
39
+ "prepublishOnly": "npm run build"
40
+ },
41
+ "dependencies": {},
42
+ "overrides": {
43
+ "hono": "^4.12.14",
44
+ "@hono/node-server": "^1.19.13"
45
+ },
46
+ "devDependencies": {
47
+ "@biomejs/biome": "^2.4.12",
48
+ "@modelcontextprotocol/sdk": "^1.29.0",
49
+ "@types/node": "^25.6.0",
50
+ "esbuild": "^0.28.0",
51
+ "typescript": "^6.0.3",
52
+ "zod": "^4.3.6"
53
+ },
54
+ "engines": {
55
+ "node": ">=18"
56
+ }
57
+ }