@yawlabs/npmjs-mcp 0.11.15 → 0.12.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.
- package/dist/index.js +274 -117
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31347,12 +31347,23 @@ function translateError(res, context) {
|
|
|
31347
31347
|
...res,
|
|
31348
31348
|
error: `Rate limited${opPart}. Retried automatically and still failed \u2014 wait longer and retry, or contact npm support if this persists. Raw: ${res.error}`
|
|
31349
31349
|
};
|
|
31350
|
+
case 409:
|
|
31351
|
+
return {
|
|
31352
|
+
...res,
|
|
31353
|
+
error: `Version conflict${pkgPart}${opPart}. The package metadata changed between read and write (a concurrent publish, deprecate, or registry _rev bump). Re-run the operation \u2014 it re-fetches the current _rev each call. Raw: ${res.error}`
|
|
31354
|
+
};
|
|
31350
31355
|
case 0:
|
|
31351
31356
|
return {
|
|
31352
31357
|
...res,
|
|
31353
31358
|
error: `Network error${opPart}. Could not reach the registry. Raw: ${res.error}`
|
|
31354
31359
|
};
|
|
31355
31360
|
default:
|
|
31361
|
+
if (res.status >= 500) {
|
|
31362
|
+
return {
|
|
31363
|
+
...res,
|
|
31364
|
+
error: `Registry server error${pkgPart}${opPart} (HTTP ${res.status}). Retried automatically and still failed \u2014 the registry is likely having a transient outage. Wait and retry; check https://status.npmjs.org if it persists. Raw: ${res.error}`
|
|
31365
|
+
};
|
|
31366
|
+
}
|
|
31356
31367
|
return res;
|
|
31357
31368
|
}
|
|
31358
31369
|
}
|
|
@@ -31383,7 +31394,7 @@ var accessTools = [
|
|
|
31383
31394
|
if (authErr) return authErr;
|
|
31384
31395
|
const res = await registryGetAuth(`/-/package/${encPkg(input.name)}/collaborators`);
|
|
31385
31396
|
if (!res.ok) return translateError(res, { pkg: input.name, op: "collaborators" });
|
|
31386
|
-
const collaborators = Object.entries(res.data).map(([username, permissions]) => ({
|
|
31397
|
+
const collaborators = Object.entries(res.data ?? {}).map(([username, permissions]) => ({
|
|
31387
31398
|
username,
|
|
31388
31399
|
permissions
|
|
31389
31400
|
}));
|
|
@@ -31418,7 +31429,13 @@ var accessTools = [
|
|
|
31418
31429
|
registryGetAuth(`/-/package/${encPkg(input.name)}/access`),
|
|
31419
31430
|
registryGetAuth(`/-/package/${encPkg(input.name)}/collaborators`)
|
|
31420
31431
|
]);
|
|
31421
|
-
if (!accessRes.ok && !collabRes.ok)
|
|
31432
|
+
if (!accessRes.ok && !collabRes.ok) {
|
|
31433
|
+
return {
|
|
31434
|
+
ok: false,
|
|
31435
|
+
status: accessRes.status,
|
|
31436
|
+
error: `Both package_access requests failed. access endpoint: HTTP ${accessRes.status}${accessRes.error ? ` (${accessRes.error})` : ""}; collaborators endpoint: HTTP ${collabRes.status}${collabRes.error ? ` (${collabRes.error})` : ""}.`
|
|
31437
|
+
};
|
|
31438
|
+
}
|
|
31422
31439
|
const result = {
|
|
31423
31440
|
package: input.name,
|
|
31424
31441
|
isScoped: input.name.startsWith("@")
|
|
@@ -31431,7 +31448,7 @@ var accessTools = [
|
|
|
31431
31448
|
result.access = accessRes.data;
|
|
31432
31449
|
}
|
|
31433
31450
|
if (collabRes.ok) {
|
|
31434
|
-
result.collaborators = Object.entries(collabRes.data).map(([username, permissions]) => ({
|
|
31451
|
+
result.collaborators = Object.entries(collabRes.data ?? {}).map(([username, permissions]) => ({
|
|
31435
31452
|
username,
|
|
31436
31453
|
permissions
|
|
31437
31454
|
}));
|
|
@@ -31457,6 +31474,10 @@ var analysisTools = [
|
|
|
31457
31474
|
packages: external_exports.array(external_exports.string()).min(2).max(5).describe("Package names to compare")
|
|
31458
31475
|
}),
|
|
31459
31476
|
handler: async (input) => {
|
|
31477
|
+
for (const pkg of input.packages) {
|
|
31478
|
+
const nameErr = validatePackageName(pkg);
|
|
31479
|
+
if (nameErr) return { ok: false, status: 400, error: nameErr };
|
|
31480
|
+
}
|
|
31460
31481
|
const partials = await Promise.all(
|
|
31461
31482
|
input.packages.map(async (name) => {
|
|
31462
31483
|
const [pkgRes, dlRes] = await Promise.all([
|
|
@@ -31499,10 +31520,15 @@ var analysisTools = [
|
|
|
31499
31520
|
latest,
|
|
31500
31521
|
license: pkg.license ?? latestVersion?.license,
|
|
31501
31522
|
maintainers: pkg.maintainers?.map((m) => m.name),
|
|
31502
|
-
|
|
31523
|
+
// Guard on data presence, not just .ok: a 2xx with an empty body yields
|
|
31524
|
+
// ok:true with no data (api.ts), and dlRes.data!.downloads would throw
|
|
31525
|
+
// and crash the whole compare. Degrade to null instead.
|
|
31526
|
+
weeklyDownloads: dlRes.ok && dlRes.data ? dlRes.data.downloads : null,
|
|
31527
|
+
// versionCount includes ALL published versions (stable + pre-releases).
|
|
31503
31528
|
versionCount: versionKeys.length,
|
|
31504
31529
|
created: pkg.time.created,
|
|
31505
31530
|
lastPublish: latest ? pkg.time[latest] : void 0,
|
|
31531
|
+
// `deprecated` is false when not deprecated, or the message string when deprecated.
|
|
31506
31532
|
deprecated: latestVersion?.deprecated ?? false,
|
|
31507
31533
|
hasReadme: !!(pkg.readme && pkg.readme.length > 0),
|
|
31508
31534
|
repository: pkg.repository,
|
|
@@ -31565,12 +31591,15 @@ var analysisTools = [
|
|
|
31565
31591
|
}
|
|
31566
31592
|
avgDaysBetweenReleases = Math.round(gaps.reduce((a, b) => a + b, 0) / gaps.length);
|
|
31567
31593
|
}
|
|
31594
|
+
const STALE_DAYS = 365;
|
|
31595
|
+
const ACTIVE_DAYS = 90;
|
|
31568
31596
|
const hasLicense = !!(pkg.license ?? latestVersion?.license);
|
|
31569
31597
|
const hasReadme = !!(pkg.readme && pkg.readme.length > 0);
|
|
31570
31598
|
const hasRepo = !!pkg.repository;
|
|
31571
31599
|
const hasHomepage = !!pkg.homepage;
|
|
31572
31600
|
const isDeprecated = !!latestVersion?.deprecated;
|
|
31573
|
-
const
|
|
31601
|
+
const deprecatedMessage = latestVersion?.deprecated ?? false;
|
|
31602
|
+
const isStale = daysSinceLastPublish !== null && daysSinceLastPublish > STALE_DAYS;
|
|
31574
31603
|
return {
|
|
31575
31604
|
ok: true,
|
|
31576
31605
|
status: 200,
|
|
@@ -31578,9 +31607,12 @@ var analysisTools = [
|
|
|
31578
31607
|
name: pkg.name,
|
|
31579
31608
|
latest,
|
|
31580
31609
|
signals: {
|
|
31581
|
-
|
|
31582
|
-
|
|
31610
|
+
// Guard on data presence, not just .ok: an empty-body 2xx yields
|
|
31611
|
+
// ok:true with no data (api.ts); degrade to null rather than throw.
|
|
31612
|
+
weeklyDownloads: dlWeekRes.ok && dlWeekRes.data ? dlWeekRes.data.downloads : null,
|
|
31613
|
+
monthlyDownloads: dlMonthRes.ok && dlMonthRes.data ? dlMonthRes.data.downloads : null,
|
|
31583
31614
|
maintainerCount: pkg.maintainers?.length ?? 0,
|
|
31615
|
+
// versionCount includes ALL published versions (stable + pre-releases).
|
|
31584
31616
|
versionCount: versionKeys.length,
|
|
31585
31617
|
daysSinceLastPublish,
|
|
31586
31618
|
avgDaysBetweenReleases,
|
|
@@ -31590,7 +31622,8 @@ var analysisTools = [
|
|
|
31590
31622
|
hasReadme,
|
|
31591
31623
|
hasRepo,
|
|
31592
31624
|
hasHomepage,
|
|
31593
|
-
isDeprecated,
|
|
31625
|
+
// `isDeprecated` is false when not deprecated, or the message string when deprecated.
|
|
31626
|
+
isDeprecated: deprecatedMessage,
|
|
31594
31627
|
isStale
|
|
31595
31628
|
},
|
|
31596
31629
|
// Holistic single-string verdict layered priority-first: a deprecated
|
|
@@ -31600,7 +31633,7 @@ var analysisTools = [
|
|
|
31600
31633
|
// on the audit endpoint or a packument with no `latest` to audit) --
|
|
31601
31634
|
// better to flag the unknown than confidently report ACTIVE on
|
|
31602
31635
|
// unverified data. Then staleness, recency, and the catch-all.
|
|
31603
|
-
assessment: isDeprecated ? "DEPRECATED" : vulnerabilityCount !== null && vulnerabilityCount > 0 ? "VULNERABLE" : !auditReliable ? "AUDIT_UNKNOWN" : isStale ? "STALE" : daysSinceLastPublish !== null && daysSinceLastPublish <
|
|
31636
|
+
assessment: isDeprecated ? "DEPRECATED" : vulnerabilityCount !== null && vulnerabilityCount > 0 ? "VULNERABLE" : !auditReliable ? "AUDIT_UNKNOWN" : isStale ? "STALE" : daysSinceLastPublish !== null && daysSinceLastPublish < ACTIVE_DAYS ? "ACTIVE" : "MAINTENANCE"
|
|
31604
31637
|
}
|
|
31605
31638
|
};
|
|
31606
31639
|
}
|
|
@@ -31674,6 +31707,7 @@ var analysisTools = [
|
|
|
31674
31707
|
status: 200,
|
|
31675
31708
|
data: {
|
|
31676
31709
|
name: pkg.name,
|
|
31710
|
+
// totalVersions includes ALL published versions (stable + pre-releases).
|
|
31677
31711
|
totalVersions: Object.keys(pkg.versions).length,
|
|
31678
31712
|
analyzed: releases.length,
|
|
31679
31713
|
created: pkg.time.created,
|
|
@@ -31781,6 +31815,10 @@ var authTools = [
|
|
|
31781
31815
|
tokens: data.objects.map((t) => ({
|
|
31782
31816
|
key: t.key,
|
|
31783
31817
|
readonly: t.readonly,
|
|
31818
|
+
// Surface token class + automation flag so callers can tell automation
|
|
31819
|
+
// tokens (which bypass 2FA) from granular/legacy ones. Undefined drops out of JSON.
|
|
31820
|
+
type: t.type,
|
|
31821
|
+
automation: t.automation,
|
|
31784
31822
|
cidrWhitelist: t.cidr_whitelist,
|
|
31785
31823
|
created: t.created,
|
|
31786
31824
|
updated: t.updated
|
|
@@ -31813,12 +31851,20 @@ var authTools = [
|
|
|
31813
31851
|
error: `Token failed /-/whoami check. Token is invalid, expired, or revoked. Create a new one at https://www.npmjs.com/settings/~/tokens. Raw: ${whoami.error}`
|
|
31814
31852
|
};
|
|
31815
31853
|
}
|
|
31816
|
-
|
|
31817
|
-
|
|
31818
|
-
|
|
31819
|
-
|
|
31820
|
-
|
|
31821
|
-
|
|
31854
|
+
let tfa;
|
|
31855
|
+
if (!profile.ok) {
|
|
31856
|
+
tfa = {
|
|
31857
|
+
unknown: true,
|
|
31858
|
+
warning: `2FA status unknown: profile lookup failed (HTTP ${profile.status}). Token is valid (whoami passed) but write-readiness could not be fully assessed. Raw: ${profile.error}`
|
|
31859
|
+
};
|
|
31860
|
+
} else {
|
|
31861
|
+
const tfaData = profile.data?.tfa;
|
|
31862
|
+
tfa = tfaData ? {
|
|
31863
|
+
enabled: !tfaData.pending,
|
|
31864
|
+
mode: tfaData.mode,
|
|
31865
|
+
...tfaData.pending ? { pending: true } : {}
|
|
31866
|
+
} : { enabled: false };
|
|
31867
|
+
}
|
|
31822
31868
|
return {
|
|
31823
31869
|
ok: true,
|
|
31824
31870
|
status: 200,
|
|
@@ -31917,7 +31963,7 @@ var dependencyTools = [
|
|
|
31917
31963
|
inputSchema: external_exports.object({
|
|
31918
31964
|
name: external_exports.string().describe("Package name"),
|
|
31919
31965
|
version: external_exports.string().optional().describe("Semver version or dist-tag (default: 'latest')"),
|
|
31920
|
-
depth: external_exports.number().min(1).max(5).optional().describe("Max tree depth (default 3, max 5)")
|
|
31966
|
+
depth: external_exports.number().min(1).max(5).optional().describe("Max tree depth where the root counts as level 1 (default 3 = root + 2 transitive levels, max 5)")
|
|
31921
31967
|
}),
|
|
31922
31968
|
handler: async (input) => {
|
|
31923
31969
|
const maxDepth = input.depth ?? 3;
|
|
@@ -31933,7 +31979,18 @@ var dependencyTools = [
|
|
|
31933
31979
|
resolved.add(hintKey);
|
|
31934
31980
|
let pending = packumentCache.get(name);
|
|
31935
31981
|
if (!pending) {
|
|
31936
|
-
|
|
31982
|
+
let encodedName;
|
|
31983
|
+
try {
|
|
31984
|
+
encodedName = encPkg(name);
|
|
31985
|
+
} catch {
|
|
31986
|
+
warnings.push(`Invalid package name "${name}": skipped`);
|
|
31987
|
+
if (!failedPackages.has(name)) {
|
|
31988
|
+
failedPackages.add(name);
|
|
31989
|
+
tree[hintKey] = { version: versionHint2, dependencies: {}, failed: true };
|
|
31990
|
+
}
|
|
31991
|
+
return;
|
|
31992
|
+
}
|
|
31993
|
+
pending = runLimited(() => registryGetAbbreviated(`/${encodedName}`)).then((res) => {
|
|
31937
31994
|
if (!res.ok) {
|
|
31938
31995
|
warnings.push(`Failed to fetch ${name}: ${res.error}`);
|
|
31939
31996
|
return null;
|
|
@@ -32071,7 +32128,7 @@ var downloadTools = [
|
|
|
32071
32128
|
},
|
|
32072
32129
|
inputSchema: external_exports.object({
|
|
32073
32130
|
name: external_exports.string().describe("Package name"),
|
|
32074
|
-
period: external_exports.string().optional().describe("Period: 'last-day', 'last-week', 'last-month', 'last-year', or 'YYYY-MM-DD:YYYY-MM-DD'")
|
|
32131
|
+
period: external_exports.string().optional().describe("Period: 'last-day', 'last-week', 'last-month', 'last-year', or 'YYYY-MM-DD:YYYY-MM-DD' (default: 'last-week')")
|
|
32075
32132
|
}),
|
|
32076
32133
|
handler: async (input) => {
|
|
32077
32134
|
const period = input.period ?? "last-week";
|
|
@@ -32091,7 +32148,7 @@ var downloadTools = [
|
|
|
32091
32148
|
},
|
|
32092
32149
|
inputSchema: external_exports.object({
|
|
32093
32150
|
name: external_exports.string().describe("Package name"),
|
|
32094
|
-
period: external_exports.string().optional().describe("Period: 'last-week', 'last-month', 'last-year', or 'YYYY-MM-DD:YYYY-MM-DD'")
|
|
32151
|
+
period: external_exports.string().optional().describe("Period: 'last-week', 'last-month', 'last-year', or 'YYYY-MM-DD:YYYY-MM-DD' (default: 'last-month')")
|
|
32095
32152
|
}),
|
|
32096
32153
|
handler: async (input) => {
|
|
32097
32154
|
const period = input.period ?? "last-month";
|
|
@@ -32115,6 +32172,10 @@ var downloadTools = [
|
|
|
32115
32172
|
}),
|
|
32116
32173
|
handler: async (input) => {
|
|
32117
32174
|
const period = input.period ?? "last-week";
|
|
32175
|
+
for (const pkg of input.packages) {
|
|
32176
|
+
const nameErr = validatePackageName(pkg);
|
|
32177
|
+
if (nameErr) return { ok: false, status: 400, error: nameErr };
|
|
32178
|
+
}
|
|
32118
32179
|
const scoped = input.packages.filter((p) => p.startsWith("@"));
|
|
32119
32180
|
if (scoped.length > 0) {
|
|
32120
32181
|
return {
|
|
@@ -32243,7 +32304,17 @@ var hookTools = [
|
|
|
32243
32304
|
const authErr = requireAuth();
|
|
32244
32305
|
if (authErr) return authErr;
|
|
32245
32306
|
const qs = new URLSearchParams();
|
|
32246
|
-
if (input.package)
|
|
32307
|
+
if (input.package) {
|
|
32308
|
+
const pkgErr = validatePackageName(input.package);
|
|
32309
|
+
if (pkgErr) {
|
|
32310
|
+
return {
|
|
32311
|
+
ok: false,
|
|
32312
|
+
status: 400,
|
|
32313
|
+
error: `Invalid package filter '${input.package}': ${pkgErr}`
|
|
32314
|
+
};
|
|
32315
|
+
}
|
|
32316
|
+
qs.set("package", input.package);
|
|
32317
|
+
}
|
|
32247
32318
|
if (input.limit !== void 0) qs.set("limit", String(input.limit));
|
|
32248
32319
|
if (input.offset !== void 0) qs.set("offset", String(input.offset));
|
|
32249
32320
|
const q = qs.toString();
|
|
@@ -32736,7 +32807,7 @@ var packageTools = [
|
|
|
32736
32807
|
var provenanceTools = [
|
|
32737
32808
|
{
|
|
32738
32809
|
name: "npm_provenance",
|
|
32739
|
-
description: "
|
|
32810
|
+
description: "Retrieve Sigstore attestations for a specific package version. Shows SLSA provenance (which CI built it, from which repo/commit) and publish attestations. NOTE: this tool RETRIEVES attestations from the registry -- it does NOT perform cryptographic signature, certificate-chain, or Rekor transparency-log verification. Use a dedicated Sigstore client to cryptographically verify the bundles.",
|
|
32740
32811
|
annotations: {
|
|
32741
32812
|
title: "Package provenance",
|
|
32742
32813
|
readOnlyHint: true,
|
|
@@ -32749,11 +32820,14 @@ var provenanceTools = [
|
|
|
32749
32820
|
version: external_exports.string().describe("Exact semver version (e.g. '1.0.0')")
|
|
32750
32821
|
}),
|
|
32751
32822
|
handler: async (input) => {
|
|
32823
|
+
if (!input.version || !input.version.trim()) {
|
|
32824
|
+
return { ok: false, status: 400, error: "version is required and must not be empty" };
|
|
32825
|
+
}
|
|
32752
32826
|
const res = await registryGet(
|
|
32753
32827
|
`/-/npm/v1/attestations/${encPkg(input.name)}@${encodeURIComponent(input.version)}`
|
|
32754
32828
|
);
|
|
32755
32829
|
if (!res.ok) return translateError(res, { pkg: input.name, op: `provenance ${input.version}` });
|
|
32756
|
-
const attestations = (res.data
|
|
32830
|
+
const attestations = (res.data?.attestations ?? []).map((a) => ({
|
|
32757
32831
|
predicateType: a.predicateType,
|
|
32758
32832
|
bundle: a.bundle
|
|
32759
32833
|
}));
|
|
@@ -32764,8 +32838,10 @@ var provenanceTools = [
|
|
|
32764
32838
|
package: input.name,
|
|
32765
32839
|
version: input.version,
|
|
32766
32840
|
attestationCount: attestations.length,
|
|
32767
|
-
hasProvenance: attestations.some((a) => a.predicateType.
|
|
32768
|
-
hasPublishAttestation: attestations.some(
|
|
32841
|
+
hasProvenance: attestations.some((a) => a.predicateType.startsWith("https://slsa.dev/provenance")),
|
|
32842
|
+
hasPublishAttestation: attestations.some(
|
|
32843
|
+
(a) => a.predicateType.startsWith("https://npmjs.com/attestation")
|
|
32844
|
+
),
|
|
32769
32845
|
attestations
|
|
32770
32846
|
}
|
|
32771
32847
|
};
|
|
@@ -32801,7 +32877,7 @@ var registryTools = [
|
|
|
32801
32877
|
title: "Recent registry changes",
|
|
32802
32878
|
readOnlyHint: true,
|
|
32803
32879
|
destructiveHint: false,
|
|
32804
|
-
idempotentHint:
|
|
32880
|
+
idempotentHint: false,
|
|
32805
32881
|
openWorldHint: true
|
|
32806
32882
|
},
|
|
32807
32883
|
inputSchema: external_exports.object({
|
|
@@ -32814,7 +32890,7 @@ var registryTools = [
|
|
|
32814
32890
|
replicateGet(`/_changes?limit=${limit}&descending=true`)
|
|
32815
32891
|
]);
|
|
32816
32892
|
if (!changesRes.ok) return translateError(changesRes, { op: "recent_changes" });
|
|
32817
|
-
const changes = changesRes.data
|
|
32893
|
+
const changes = (changesRes.data?.results ?? []).map((r) => ({
|
|
32818
32894
|
package: r.id,
|
|
32819
32895
|
rev: r.changes[0]?.rev
|
|
32820
32896
|
}));
|
|
@@ -32822,7 +32898,11 @@ var registryTools = [
|
|
|
32822
32898
|
ok: true,
|
|
32823
32899
|
status: 200,
|
|
32824
32900
|
data: {
|
|
32825
|
-
|
|
32901
|
+
// `registryPackageCount` is the registry-wide doc-count from
|
|
32902
|
+
// replicate.npmjs.com (the entire npm registry, ~3M) -- NOT the
|
|
32903
|
+
// count of returned changes. `changes.length` is the per-call
|
|
32904
|
+
// result size.
|
|
32905
|
+
registryPackageCount: dbRes.data?.doc_count ?? null,
|
|
32826
32906
|
changes
|
|
32827
32907
|
}
|
|
32828
32908
|
};
|
|
@@ -32855,7 +32935,7 @@ var registryTools = [
|
|
|
32855
32935
|
tool: "mcp_tool: npm_deprecate",
|
|
32856
32936
|
requiresNpmToken: true,
|
|
32857
32937
|
messageFormat: {
|
|
32858
|
-
preferred: "Renamed to @scope/pkg
|
|
32938
|
+
preferred: "Renamed to @scope/pkg -- install that instead",
|
|
32859
32939
|
avoid: "Renamed to @scope/pkg. Install that instead.",
|
|
32860
32940
|
note: "Period-capital form has triggered 422 on at least one scoped package; use em-dash."
|
|
32861
32941
|
}
|
|
@@ -32921,6 +33001,13 @@ var searchTools = [
|
|
|
32921
33001
|
maintenance: external_exports.number().min(0).max(1).optional().describe("Weight for maintenance score (0-1)")
|
|
32922
33002
|
}),
|
|
32923
33003
|
handler: async (input) => {
|
|
33004
|
+
if (input.query.trim() === "") {
|
|
33005
|
+
return {
|
|
33006
|
+
ok: false,
|
|
33007
|
+
status: 400,
|
|
33008
|
+
error: "Query cannot be empty. Provide a search term or qualifier (e.g. 'mcp', 'keywords:react')."
|
|
33009
|
+
};
|
|
33010
|
+
}
|
|
32924
33011
|
const params = new URLSearchParams({ text: input.query });
|
|
32925
33012
|
if (input.size !== void 0) params.set("size", String(input.size));
|
|
32926
33013
|
if (input.from !== void 0) params.set("from", String(input.from));
|
|
@@ -32929,7 +33016,8 @@ var searchTools = [
|
|
|
32929
33016
|
if (input.maintenance !== void 0) params.set("maintenance", String(input.maintenance));
|
|
32930
33017
|
const res = await registryGet(`/-/v1/search?${params}`);
|
|
32931
33018
|
if (!res.ok) return translateError(res, { op: `search "${input.query}"` });
|
|
32932
|
-
const
|
|
33019
|
+
const objects = res.data?.objects ?? [];
|
|
33020
|
+
const results = objects.map((obj) => ({
|
|
32933
33021
|
name: obj.package.name,
|
|
32934
33022
|
version: obj.package.version,
|
|
32935
33023
|
description: obj.package.description,
|
|
@@ -32940,7 +33028,7 @@ var searchTools = [
|
|
|
32940
33028
|
links: obj.package.links,
|
|
32941
33029
|
score: obj.score.detail
|
|
32942
33030
|
}));
|
|
32943
|
-
return { ok: true, status: 200, data: { total: res.data.
|
|
33031
|
+
return { ok: true, status: 200, data: { total: res.data?.total ?? objects.length, results } };
|
|
32944
33032
|
}
|
|
32945
33033
|
}
|
|
32946
33034
|
];
|
|
@@ -33007,9 +33095,15 @@ var securityTools = [
|
|
|
33007
33095
|
dependencies: external_exports.record(external_exports.string(), external_exports.string()).describe('Dependencies to audit as { "package": "version" }, e.g. { "express": "4.17.1" }')
|
|
33008
33096
|
}),
|
|
33009
33097
|
handler: async (input) => {
|
|
33098
|
+
const nameErr = validatePackageName(input.name);
|
|
33099
|
+
if (nameErr) return { ok: false, status: 400, error: nameErr };
|
|
33100
|
+
const version3 = input.version ?? "1.0.0";
|
|
33101
|
+
if (!version3.trim()) {
|
|
33102
|
+
return { ok: false, status: 400, error: "version must not be empty" };
|
|
33103
|
+
}
|
|
33010
33104
|
const body = {
|
|
33011
33105
|
name: input.name,
|
|
33012
|
-
version:
|
|
33106
|
+
version: version3,
|
|
33013
33107
|
requires: input.dependencies,
|
|
33014
33108
|
dependencies: Object.fromEntries(
|
|
33015
33109
|
Object.entries(input.dependencies).map(([pkg, ver]) => [pkg, { version: ver }])
|
|
@@ -33065,12 +33159,20 @@ var trustTools = [
|
|
|
33065
33159
|
if (c.type === "github") {
|
|
33066
33160
|
result.repository = c.claims?.repository;
|
|
33067
33161
|
const workflowRef = c.claims?.workflow_ref;
|
|
33068
|
-
|
|
33162
|
+
if (typeof workflowRef === "string") {
|
|
33163
|
+
result.workflowFile = workflowRef;
|
|
33164
|
+
} else if (workflowRef && typeof workflowRef === "object") {
|
|
33165
|
+
result.workflowFile = workflowRef.file;
|
|
33166
|
+
}
|
|
33069
33167
|
result.environment = c.claims?.environment;
|
|
33070
33168
|
} else if (c.type === "gitlab") {
|
|
33071
33169
|
result.project = c.claims?.project_path;
|
|
33072
33170
|
const configRef = c.claims?.ci_config_ref_uri;
|
|
33073
|
-
|
|
33171
|
+
if (typeof configRef === "string") {
|
|
33172
|
+
result.configFile = configRef;
|
|
33173
|
+
} else if (configRef && typeof configRef === "object") {
|
|
33174
|
+
result.configFile = configRef.file;
|
|
33175
|
+
}
|
|
33074
33176
|
result.environment = c.claims?.environment;
|
|
33075
33177
|
} else if (c.type === "circleci") {
|
|
33076
33178
|
result.project = c.claims?.project_id;
|
|
@@ -33124,7 +33226,10 @@ var workflowTools = [
|
|
|
33124
33226
|
}
|
|
33125
33227
|
result.authenticated = true;
|
|
33126
33228
|
result.username = whoamiRes.data.username;
|
|
33127
|
-
const profileRes = await
|
|
33229
|
+
const [profileRes, tokensRes] = await Promise.all([
|
|
33230
|
+
registryGetAuth("/-/npm/v1/user"),
|
|
33231
|
+
registryGetAuth("/-/npm/v1/tokens")
|
|
33232
|
+
]);
|
|
33128
33233
|
if (profileRes.ok && profileRes.data) {
|
|
33129
33234
|
const tfa = profileRes.data.tfa;
|
|
33130
33235
|
if (tfa && !tfa.pending) {
|
|
@@ -33132,8 +33237,9 @@ var workflowTools = [
|
|
|
33132
33237
|
} else {
|
|
33133
33238
|
result.twoFactorAuth = "disabled";
|
|
33134
33239
|
}
|
|
33240
|
+
} else {
|
|
33241
|
+
result.twoFactorAuth = "fetch-failed";
|
|
33135
33242
|
}
|
|
33136
|
-
const tokensRes = await registryGetAuth("/-/npm/v1/tokens");
|
|
33137
33243
|
if (tokensRes.ok && tokensRes.data) {
|
|
33138
33244
|
const tokens = tokensRes.data.objects;
|
|
33139
33245
|
const hasReadWrite = tokens.some((t) => !t.readonly);
|
|
@@ -33144,6 +33250,10 @@ var workflowTools = [
|
|
|
33144
33250
|
result.canPublishHeadless = true;
|
|
33145
33251
|
result.tokenType = "any (2FA disabled)";
|
|
33146
33252
|
result.recommendation = "2FA is disabled \u2014 any valid token can publish. Consider enabling 2FA for security.";
|
|
33253
|
+
} else if (result.twoFactorAuth === "fetch-failed") {
|
|
33254
|
+
result.canPublishHeadless = null;
|
|
33255
|
+
result.tokenType = "unknown (profile fetch failed)";
|
|
33256
|
+
result.recommendation = "Could not determine 2FA status -- token may lack read permission on /-/npm/v1/user. Verify the token has at least read access and re-run npm_check_auth.";
|
|
33147
33257
|
} else {
|
|
33148
33258
|
result.tokenType = "unknown (tokens are redacted in API)";
|
|
33149
33259
|
result.canPublishHeadless = null;
|
|
@@ -33224,7 +33334,10 @@ var workflowTools = [
|
|
|
33224
33334
|
});
|
|
33225
33335
|
}
|
|
33226
33336
|
if (username) {
|
|
33227
|
-
const profileRes = await
|
|
33337
|
+
const [profileRes, tokensRes] = await Promise.all([
|
|
33338
|
+
registryGetAuth("/-/npm/v1/user"),
|
|
33339
|
+
registryGetAuth("/-/npm/v1/tokens")
|
|
33340
|
+
]);
|
|
33228
33341
|
if (profileRes.ok && profileRes.data) {
|
|
33229
33342
|
const tfa = profileRes.data.tfa;
|
|
33230
33343
|
if (tfa && !tfa.pending) {
|
|
@@ -33247,8 +33360,13 @@ var workflowTools = [
|
|
|
33247
33360
|
detail: "2FA is disabled. Any valid token can publish, but 2FA is strongly recommended for security."
|
|
33248
33361
|
});
|
|
33249
33362
|
}
|
|
33363
|
+
} else {
|
|
33364
|
+
checks.push({
|
|
33365
|
+
check: "2FA status",
|
|
33366
|
+
status: "warn",
|
|
33367
|
+
detail: "Could not determine 2FA status -- token may lack read permission on /-/npm/v1/user. Verify the token has at least read access and re-run npm_publish_preflight."
|
|
33368
|
+
});
|
|
33250
33369
|
}
|
|
33251
|
-
const tokensRes = await registryGetAuth("/-/npm/v1/tokens");
|
|
33252
33370
|
if (tokensRes.ok && tokensRes.data) {
|
|
33253
33371
|
const tokens = tokensRes.data.objects;
|
|
33254
33372
|
const totalTokens = tokensRes.data.total;
|
|
@@ -33395,15 +33513,8 @@ function parseTeamTarget(target) {
|
|
|
33395
33513
|
return { scope, team };
|
|
33396
33514
|
}
|
|
33397
33515
|
function highestVersion(versions) {
|
|
33398
|
-
const
|
|
33399
|
-
|
|
33400
|
-
if (v.includes("-")) continue;
|
|
33401
|
-
const m = v.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
33402
|
-
if (m) parsed.push([Number(m[1]), Number(m[2]), Number(m[3]), v]);
|
|
33403
|
-
}
|
|
33404
|
-
if (parsed.length === 0) return null;
|
|
33405
|
-
parsed.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]);
|
|
33406
|
-
return parsed[parsed.length - 1][3];
|
|
33516
|
+
const stable = versions.filter((v) => !v.includes("-"));
|
|
33517
|
+
return maxSatisfying(stable, "*");
|
|
33407
33518
|
}
|
|
33408
33519
|
var writeTools = [
|
|
33409
33520
|
// ───────────────────────────────────────────────────────
|
|
@@ -33421,7 +33532,7 @@ var writeTools = [
|
|
|
33421
33532
|
},
|
|
33422
33533
|
inputSchema: external_exports.object({
|
|
33423
33534
|
name: external_exports.string().describe("Package name (e.g. '@yawlabs/spend')"),
|
|
33424
|
-
message: external_exports.string().describe("Deprecation message.
|
|
33535
|
+
message: external_exports.string().describe("Deprecation message. Use npm_undeprecate to clear."),
|
|
33425
33536
|
versionRange: external_exports.string().optional().describe(
|
|
33426
33537
|
"Semver range. Omit to deprecate ALL versions. Example: '<1.0.0' or '0.3.x'. Standard semver applies \u2014 bare integers are x-ranges (e.g. '0' means '0.x.x', not exact version 0). For a single version use '=1.2.3'."
|
|
33427
33538
|
)
|
|
@@ -33431,46 +33542,50 @@ var writeTools = [
|
|
|
33431
33542
|
if (authErr) return authErr;
|
|
33432
33543
|
const problem = validateDeprecationMessage(input.message);
|
|
33433
33544
|
if (problem) return { ok: false, status: 400, error: problem };
|
|
33434
|
-
const pRes = await fetchPackument(input.name);
|
|
33435
|
-
if (!pRes.ok) return translateError(pRes, { pkg: input.name, op: "deprecate (fetch)" });
|
|
33436
|
-
const packument = pRes.data;
|
|
33437
|
-
const allVersions = Object.keys(packument.versions || {});
|
|
33438
33545
|
const range = input.versionRange ?? "*";
|
|
33439
|
-
|
|
33440
|
-
|
|
33441
|
-
return {
|
|
33442
|
-
|
|
33443
|
-
|
|
33444
|
-
|
|
33445
|
-
|
|
33446
|
-
|
|
33447
|
-
|
|
33448
|
-
|
|
33449
|
-
|
|
33450
|
-
|
|
33546
|
+
for (let attempt = 0; attempt <= 1; attempt++) {
|
|
33547
|
+
const pRes = await fetchPackument(input.name);
|
|
33548
|
+
if (!pRes.ok) return translateError(pRes, { pkg: input.name, op: "deprecate (fetch)" });
|
|
33549
|
+
const packument = pRes.data;
|
|
33550
|
+
const allVersions = Object.keys(packument.versions || {});
|
|
33551
|
+
const affected = versionsSatisfying(allVersions, range);
|
|
33552
|
+
if (affected.length === 0) {
|
|
33553
|
+
return {
|
|
33554
|
+
ok: false,
|
|
33555
|
+
status: 400,
|
|
33556
|
+
error: `No versions match range '${range}' for ${input.name}. Published versions: ${allVersions.join(", ") || "(none)"}.`
|
|
33557
|
+
};
|
|
33558
|
+
}
|
|
33559
|
+
if (!packument._rev) {
|
|
33560
|
+
return {
|
|
33561
|
+
ok: false,
|
|
33562
|
+
status: 500,
|
|
33563
|
+
error: `Packument for ${input.name} missing _rev -- cannot deprecate. Try again; this is usually transient.`
|
|
33564
|
+
};
|
|
33565
|
+
}
|
|
33566
|
+
for (const v of affected) {
|
|
33567
|
+
packument.versions[v].deprecated = input.message;
|
|
33568
|
+
}
|
|
33569
|
+
delete packument._revisions;
|
|
33570
|
+
delete packument._attachments;
|
|
33571
|
+
const putRes = await registryPutAuth(
|
|
33572
|
+
`/${encPkg(input.name)}/-rev/${encodeURIComponent(packument._rev)}`,
|
|
33573
|
+
packument
|
|
33574
|
+
);
|
|
33575
|
+
if (putRes.status === 409 && attempt === 0) continue;
|
|
33576
|
+
if (!putRes.ok) return translateError(putRes, { pkg: input.name, op: "deprecate (write)" });
|
|
33451
33577
|
return {
|
|
33452
|
-
ok:
|
|
33453
|
-
status:
|
|
33454
|
-
|
|
33578
|
+
ok: true,
|
|
33579
|
+
status: 200,
|
|
33580
|
+
data: {
|
|
33581
|
+
package: input.name,
|
|
33582
|
+
affectedVersions: affected,
|
|
33583
|
+
totalAffected: affected.length,
|
|
33584
|
+
message: input.message
|
|
33585
|
+
}
|
|
33455
33586
|
};
|
|
33456
33587
|
}
|
|
33457
|
-
|
|
33458
|
-
delete packument._attachments;
|
|
33459
|
-
const putRes = await registryPutAuth(
|
|
33460
|
-
`/${encPkg(input.name)}/-rev/${encodeURIComponent(packument._rev)}`,
|
|
33461
|
-
packument
|
|
33462
|
-
);
|
|
33463
|
-
if (!putRes.ok) return translateError(putRes, { pkg: input.name, op: "deprecate (write)" });
|
|
33464
|
-
return {
|
|
33465
|
-
ok: true,
|
|
33466
|
-
status: 200,
|
|
33467
|
-
data: {
|
|
33468
|
-
package: input.name,
|
|
33469
|
-
affectedVersions: affected,
|
|
33470
|
-
totalAffected: affected.length,
|
|
33471
|
-
message: input.message
|
|
33472
|
-
}
|
|
33473
|
-
};
|
|
33588
|
+
return { ok: false, status: 409, error: "Conflict after OCC retry -- try again." };
|
|
33474
33589
|
}
|
|
33475
33590
|
},
|
|
33476
33591
|
// ───────────────────────────────────────────────────────
|
|
@@ -33585,15 +33700,16 @@ var writeTools = [
|
|
|
33585
33700
|
return {
|
|
33586
33701
|
ok: false,
|
|
33587
33702
|
status: 500,
|
|
33588
|
-
error: `Packument for ${input.name} missing _rev
|
|
33703
|
+
error: `Packument for ${input.name} missing _rev -- cannot unpublish. Try again; this is usually transient.`
|
|
33589
33704
|
};
|
|
33590
33705
|
}
|
|
33591
33706
|
const tarballUrl = versionData.dist?.tarball;
|
|
33592
|
-
const
|
|
33707
|
+
const distTags = packument["dist-tags"] || {};
|
|
33708
|
+
const latestBefore = distTags.latest;
|
|
33593
33709
|
delete packument.versions[input.version];
|
|
33594
|
-
for (const tag of Object.keys(
|
|
33595
|
-
if (
|
|
33596
|
-
delete
|
|
33710
|
+
for (const tag of Object.keys(distTags)) {
|
|
33711
|
+
if (distTags[tag] === input.version) {
|
|
33712
|
+
delete distTags[tag];
|
|
33597
33713
|
}
|
|
33598
33714
|
}
|
|
33599
33715
|
if (latestBefore === input.version) {
|
|
@@ -33617,10 +33733,17 @@ var writeTools = [
|
|
|
33617
33733
|
const freshRev = freshRes.ok ? freshRes.data._rev : void 0;
|
|
33618
33734
|
if (freshRev) {
|
|
33619
33735
|
try {
|
|
33620
|
-
const
|
|
33621
|
-
const
|
|
33622
|
-
|
|
33623
|
-
|
|
33736
|
+
const tarballParsed = new URL(tarballUrl);
|
|
33737
|
+
const registryOrigin = new URL(
|
|
33738
|
+
(process.env.NPM_REGISTRY || "https://registry.npmjs.org").replace(/\/+$/, "")
|
|
33739
|
+
).origin;
|
|
33740
|
+
if (tarballParsed.origin !== registryOrigin) {
|
|
33741
|
+
tarballError = `tarball origin ${tarballParsed.origin} does not match registry origin ${registryOrigin} -- tarball DELETE skipped`;
|
|
33742
|
+
} else {
|
|
33743
|
+
const delRes = await registryDeleteAuth(`${tarballParsed.pathname}/-rev/${encodeURIComponent(freshRev)}`);
|
|
33744
|
+
tarballDeleted = delRes.ok;
|
|
33745
|
+
if (!delRes.ok) tarballError = delRes.error;
|
|
33746
|
+
}
|
|
33624
33747
|
} catch (err) {
|
|
33625
33748
|
tarballError = err instanceof Error ? err.message : String(err);
|
|
33626
33749
|
}
|
|
@@ -33677,7 +33800,7 @@ var writeTools = [
|
|
|
33677
33800
|
return {
|
|
33678
33801
|
ok: false,
|
|
33679
33802
|
status: 500,
|
|
33680
|
-
error: `Packument for ${input.name} missing _rev
|
|
33803
|
+
error: `Packument for ${input.name} missing _rev -- cannot unpublish.`
|
|
33681
33804
|
};
|
|
33682
33805
|
}
|
|
33683
33806
|
const delRes = await registryDeleteAuth(`/${encPkg(input.name)}/-rev/${encodeURIComponent(rev)}`);
|
|
@@ -33712,6 +33835,16 @@ var writeTools = [
|
|
|
33712
33835
|
if (authErr) return authErr;
|
|
33713
33836
|
const tagErr = validateTag(input.tag);
|
|
33714
33837
|
if (tagErr) return { ok: false, status: 400, error: tagErr };
|
|
33838
|
+
const pRes = await fetchPackument(input.name);
|
|
33839
|
+
if (!pRes.ok) return translateError(pRes, { pkg: input.name, op: `dist-tag set ${input.tag} (fetch)` });
|
|
33840
|
+
const packument = pRes.data;
|
|
33841
|
+
if (!packument.versions?.[input.version]) {
|
|
33842
|
+
return {
|
|
33843
|
+
ok: false,
|
|
33844
|
+
status: 404,
|
|
33845
|
+
error: `Version ${input.version} not found for ${input.name}. Published versions: ${Object.keys(packument.versions || {}).join(", ")}.`
|
|
33846
|
+
};
|
|
33847
|
+
}
|
|
33715
33848
|
const putRes = await registryPutAuth(
|
|
33716
33849
|
`/-/package/${encPkg(input.name)}/dist-tags/${encTag(input.tag)}`,
|
|
33717
33850
|
input.version
|
|
@@ -33817,7 +33950,7 @@ var writeTools = [
|
|
|
33817
33950
|
return {
|
|
33818
33951
|
ok: false,
|
|
33819
33952
|
status: 500,
|
|
33820
|
-
error: `Packument for ${input.name} missing _rev
|
|
33953
|
+
error: `Packument for ${input.name} missing _rev -- cannot update owners.`
|
|
33821
33954
|
};
|
|
33822
33955
|
}
|
|
33823
33956
|
const maintainers = [...owners, userRecord];
|
|
@@ -33864,14 +33997,14 @@ var writeTools = [
|
|
|
33864
33997
|
if (!pRes.ok) return translateError(pRes, { pkg: input.name, op: "owner_remove (fetch)" });
|
|
33865
33998
|
const packument = pRes.data;
|
|
33866
33999
|
const before = packument.maintainers || [];
|
|
33867
|
-
if (!before.some((m) => m.name === input.username)) {
|
|
34000
|
+
if (!before.some((m) => m.name.toLowerCase() === input.username.toLowerCase())) {
|
|
33868
34001
|
return {
|
|
33869
34002
|
ok: false,
|
|
33870
34003
|
status: 404,
|
|
33871
34004
|
error: `${input.username} is not a maintainer of ${input.name}. Current maintainers: ${before.map((m) => m.name).join(", ") || "(none)"}.`
|
|
33872
34005
|
};
|
|
33873
34006
|
}
|
|
33874
|
-
const after = before.filter((m) => m.name !== input.username);
|
|
34007
|
+
const after = before.filter((m) => m.name.toLowerCase() !== input.username.toLowerCase());
|
|
33875
34008
|
if (after.length === 0) {
|
|
33876
34009
|
return {
|
|
33877
34010
|
ok: false,
|
|
@@ -33883,7 +34016,7 @@ var writeTools = [
|
|
|
33883
34016
|
return {
|
|
33884
34017
|
ok: false,
|
|
33885
34018
|
status: 500,
|
|
33886
|
-
error: `Packument for ${input.name} missing _rev
|
|
34019
|
+
error: `Packument for ${input.name} missing _rev -- cannot update owners.`
|
|
33887
34020
|
};
|
|
33888
34021
|
}
|
|
33889
34022
|
const putRes = await registryPutAuth(`/${encPkg(input.name)}/-rev/${encodeURIComponent(packument._rev)}`, {
|
|
@@ -33908,7 +34041,7 @@ var writeTools = [
|
|
|
33908
34041
|
// ───────────────────────────────────────────────────────
|
|
33909
34042
|
{
|
|
33910
34043
|
name: "npm_access_set",
|
|
33911
|
-
description: "Set package access level: 'public'
|
|
34044
|
+
description: "Set package access level: 'public' or 'restricted' (private). 'private' is accepted as an alias for 'restricted' for ergonomics -- both map to the registry wire value 'restricted'. Unscoped packages are always public. Restricted access requires a paid npm account.",
|
|
33912
34045
|
annotations: {
|
|
33913
34046
|
title: "Set package access level",
|
|
33914
34047
|
readOnlyHint: false,
|
|
@@ -33918,17 +34051,18 @@ var writeTools = [
|
|
|
33918
34051
|
},
|
|
33919
34052
|
inputSchema: external_exports.object({
|
|
33920
34053
|
name: external_exports.string().describe("Package name"),
|
|
33921
|
-
access: external_exports.enum(["public", "private", "restricted"]).describe("Access level")
|
|
34054
|
+
access: external_exports.enum(["public", "private", "restricted"]).describe("Access level ('private' maps to 'restricted' on the wire)")
|
|
33922
34055
|
}),
|
|
33923
34056
|
handler: async (input) => {
|
|
33924
34057
|
const authErr = requireAuth();
|
|
33925
34058
|
if (authErr) return authErr;
|
|
33926
|
-
const
|
|
34059
|
+
const wireAccess = input.access === "private" ? "restricted" : input.access;
|
|
34060
|
+
const res = await registryPostAuth(`/-/package/${encPkg(input.name)}/access`, { access: wireAccess });
|
|
33927
34061
|
if (!res.ok) return translateError(res, { pkg: input.name, op: `access_set ${input.access}` });
|
|
33928
34062
|
return {
|
|
33929
34063
|
ok: true,
|
|
33930
34064
|
status: 200,
|
|
33931
|
-
data: { package: input.name, access:
|
|
34065
|
+
data: { package: input.name, access: wireAccess }
|
|
33932
34066
|
};
|
|
33933
34067
|
}
|
|
33934
34068
|
},
|
|
@@ -33984,12 +34118,16 @@ var writeTools = [
|
|
|
33984
34118
|
},
|
|
33985
34119
|
inputSchema: external_exports.object({
|
|
33986
34120
|
team: external_exports.string().describe("Team in the form '@scope:team' (e.g. '@yawlabs:devs')"),
|
|
33987
|
-
package: external_exports.string().describe(
|
|
34121
|
+
package: external_exports.string().describe(
|
|
34122
|
+
"Package name. Field is named 'package' to match the npm CLI (diverges from 'name' used elsewhere in this server)."
|
|
34123
|
+
),
|
|
33988
34124
|
permissions: external_exports.enum(["read-only", "read-write"]).describe("Permission level")
|
|
33989
34125
|
}),
|
|
33990
34126
|
handler: async (input) => {
|
|
33991
34127
|
const authErr = requireAuth();
|
|
33992
34128
|
if (authErr) return authErr;
|
|
34129
|
+
const pkgErr = validatePackageName(input.package);
|
|
34130
|
+
if (pkgErr) return { ok: false, status: 400, error: pkgErr };
|
|
33993
34131
|
const parsed = parseTeamTarget(input.team);
|
|
33994
34132
|
if ("error" in parsed) {
|
|
33995
34133
|
return { ok: false, status: 400, error: parsed.error };
|
|
@@ -34022,11 +34160,15 @@ var writeTools = [
|
|
|
34022
34160
|
},
|
|
34023
34161
|
inputSchema: external_exports.object({
|
|
34024
34162
|
team: external_exports.string().describe("Team in the form '@scope:team'"),
|
|
34025
|
-
package: external_exports.string().describe(
|
|
34163
|
+
package: external_exports.string().describe(
|
|
34164
|
+
"Package name. Field is named 'package' to match the npm CLI (diverges from 'name' used elsewhere in this server)."
|
|
34165
|
+
)
|
|
34026
34166
|
}),
|
|
34027
34167
|
handler: async (input) => {
|
|
34028
34168
|
const authErr = requireAuth();
|
|
34029
34169
|
if (authErr) return authErr;
|
|
34170
|
+
const pkgErr = validatePackageName(input.package);
|
|
34171
|
+
if (pkgErr) return { ok: false, status: 400, error: pkgErr };
|
|
34030
34172
|
const parsed = parseTeamTarget(input.team);
|
|
34031
34173
|
if ("error" in parsed) {
|
|
34032
34174
|
return { ok: false, status: 400, error: parsed.error };
|
|
@@ -34081,7 +34223,7 @@ var writeTools = [
|
|
|
34081
34223
|
// ───────────────────────────────────────────────────────
|
|
34082
34224
|
{
|
|
34083
34225
|
name: "npm_team_delete",
|
|
34084
|
-
description: "Delete a team. Team is passed as '@scope:team'. Revokes all package permissions that team held. Requires confirm: true \u2014 this removes the team and all its package grants in one call.",
|
|
34226
|
+
description: "Delete a team. Team is passed as '@scope:team'. Revokes all package permissions that team held, and team memberships are also removed. Requires confirm: true \u2014 this removes the team and all its package grants in one call. List the team's current grants with npm_team_packages first if you need to preserve them.",
|
|
34085
34227
|
annotations: {
|
|
34086
34228
|
title: "Delete team",
|
|
34087
34229
|
readOnlyHint: false,
|
|
@@ -34197,22 +34339,30 @@ var writeTools = [
|
|
|
34197
34339
|
inputSchema: external_exports.object({
|
|
34198
34340
|
org: external_exports.string().describe("Organization name (with or without leading @)"),
|
|
34199
34341
|
user: external_exports.string().describe("npm username"),
|
|
34200
|
-
role: external_exports.enum(["developer", "admin", "owner"]).optional().describe("Role to assign")
|
|
34342
|
+
role: external_exports.enum(["developer", "admin", "owner"]).optional().describe("Role to assign"),
|
|
34343
|
+
confirm: external_exports.literal(true).describe("Must be literally true. Guards against accidental org membership changes.")
|
|
34201
34344
|
}),
|
|
34202
34345
|
handler: async (input) => {
|
|
34203
34346
|
const authErr = requireAuth();
|
|
34204
34347
|
if (authErr) return authErr;
|
|
34348
|
+
if (input.confirm !== true) {
|
|
34349
|
+
return {
|
|
34350
|
+
ok: false,
|
|
34351
|
+
status: 400,
|
|
34352
|
+
error: "org_member_set requires confirm: true. This adds or changes a user's org role."
|
|
34353
|
+
};
|
|
34354
|
+
}
|
|
34205
34355
|
const orgErr = validateScope(input.org);
|
|
34206
34356
|
if (orgErr) return { ok: false, status: 400, error: orgErr };
|
|
34207
34357
|
const userErr = validateUsername(input.user);
|
|
34208
34358
|
if (userErr) return { ok: false, status: 400, error: userErr };
|
|
34209
|
-
const
|
|
34210
|
-
const
|
|
34211
|
-
const body = { user };
|
|
34359
|
+
const userBare = input.user.replace(/^@/, "");
|
|
34360
|
+
const orgBare = input.org.replace(/^@/, "");
|
|
34361
|
+
const body = { user: userBare };
|
|
34212
34362
|
if (input.role) body.role = input.role;
|
|
34213
|
-
const res = await registryPutAuth(`/-/org/${encScope(org)}/user`, body);
|
|
34214
|
-
if (!res.ok) return translateError(res, { op: `org_member_set ${
|
|
34215
|
-
const data = { org, user };
|
|
34363
|
+
const res = await registryPutAuth(`/-/org/${encScope(input.org)}/user`, body);
|
|
34364
|
+
if (!res.ok) return translateError(res, { op: `org_member_set ${orgBare}/${userBare}` });
|
|
34365
|
+
const data = { org: orgBare, user: userBare };
|
|
34216
34366
|
if (input.role) data.role = input.role;
|
|
34217
34367
|
return { ok: true, status: 200, data };
|
|
34218
34368
|
}
|
|
@@ -34249,11 +34399,11 @@ var writeTools = [
|
|
|
34249
34399
|
if (orgErr) return { ok: false, status: 400, error: orgErr };
|
|
34250
34400
|
const userErr = validateUsername(input.user);
|
|
34251
34401
|
if (userErr) return { ok: false, status: 400, error: userErr };
|
|
34252
|
-
const
|
|
34253
|
-
const
|
|
34254
|
-
const res = await registryDeleteAuth(`/-/org/${encScope(org)}/user`, { user });
|
|
34255
|
-
if (!res.ok) return translateError(res, { op: `org_member_remove ${
|
|
34256
|
-
return { ok: true, status: 200, data: { org, removedUser:
|
|
34402
|
+
const orgBare = input.org.replace(/^@/, "");
|
|
34403
|
+
const userBare = input.user.replace(/^@/, "");
|
|
34404
|
+
const res = await registryDeleteAuth(`/-/org/${encScope(input.org)}/user`, { user: userBare });
|
|
34405
|
+
if (!res.ok) return translateError(res, { op: `org_member_remove ${orgBare}/${userBare}` });
|
|
34406
|
+
return { ok: true, status: 200, data: { org: orgBare, removedUser: userBare } };
|
|
34257
34407
|
}
|
|
34258
34408
|
},
|
|
34259
34409
|
// ───────────────────────────────────────────────────────
|
|
@@ -34285,6 +34435,13 @@ var writeTools = [
|
|
|
34285
34435
|
error: "token_revoke requires confirm: true. Revoking the token in NPM_TOKEN will break the next call."
|
|
34286
34436
|
};
|
|
34287
34437
|
}
|
|
34438
|
+
if (!input.tokenKey || !/^[0-9a-f-]{8,}$/i.test(input.tokenKey)) {
|
|
34439
|
+
return {
|
|
34440
|
+
ok: false,
|
|
34441
|
+
status: 400,
|
|
34442
|
+
error: `Invalid token key '${input.tokenKey}'. Expected a UUID (hex characters and dashes, at least 8 characters). Use npm_tokens to list valid keys.`
|
|
34443
|
+
};
|
|
34444
|
+
}
|
|
34288
34445
|
const res = await registryDeleteAuth(`/-/npm/v1/tokens/token/${encodeURIComponent(input.tokenKey)}`);
|
|
34289
34446
|
if (!res.ok) return translateError(res, { op: "token_revoke" });
|
|
34290
34447
|
return { ok: true, status: 200, data: { tokenKey: input.tokenKey, revoked: true } };
|
|
@@ -34293,7 +34450,7 @@ var writeTools = [
|
|
|
34293
34450
|
];
|
|
34294
34451
|
|
|
34295
34452
|
// src/index.ts
|
|
34296
|
-
var version2 = true ? "0.
|
|
34453
|
+
var version2 = true ? "0.12.1" : (await null).createRequire(import.meta.url)("../package.json").version;
|
|
34297
34454
|
var subcommand = process.argv[2];
|
|
34298
34455
|
if (subcommand === "version" || subcommand === "--version" || subcommand === "-v" || subcommand === "-V") {
|
|
34299
34456
|
console.log(version2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yawlabs/npmjs-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"mcpName": "io.github.YawLabs/npmjs-mcp",
|
|
5
5
|
"description": "npm registry MCP server — package intelligence, security audits, and dependency analysis for AI assistants",
|
|
6
6
|
"license": "MIT",
|