@automattic/mcp-wordpress-remote 0.5.1 → 0.5.3

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/proxy.js CHANGED
@@ -4939,7 +4939,7 @@ async function toFormData(Body2, ct) {
4939
4939
  let headerValue2;
4940
4940
  let entryValue;
4941
4941
  let entryName;
4942
- let contentType;
4942
+ let contentType2;
4943
4943
  let filename;
4944
4944
  const entryChunks = [];
4945
4945
  const formData = new FormData();
@@ -4950,7 +4950,7 @@ async function toFormData(Body2, ct) {
4950
4950
  entryChunks.push(ui8a);
4951
4951
  };
4952
4952
  const appendFileToFormData = () => {
4953
- const file2 = new file_default(entryChunks, filename, { type: contentType });
4953
+ const file2 = new file_default(entryChunks, filename, { type: contentType2 });
4954
4954
  formData.append(entryName, file2);
4955
4955
  };
4956
4956
  const appendEntryToFormData = () => {
@@ -4965,7 +4965,7 @@ async function toFormData(Body2, ct) {
4965
4965
  headerValue2 = "";
4966
4966
  entryValue = "";
4967
4967
  entryName = "";
4968
- contentType = "";
4968
+ contentType2 = "";
4969
4969
  filename = null;
4970
4970
  entryChunks.length = 0;
4971
4971
  };
@@ -4989,7 +4989,7 @@ async function toFormData(Body2, ct) {
4989
4989
  parser.onPartEnd = appendFileToFormData;
4990
4990
  }
4991
4991
  } else if (headerField === "content-type") {
4992
- contentType = headerValue2;
4992
+ contentType2 = headerValue2;
4993
4993
  }
4994
4994
  headerValue2 = "";
4995
4995
  headerField = "";
@@ -5749,9 +5749,9 @@ var init_response = __esm({
5749
5749
  const status = options.status != null ? options.status : 200;
5750
5750
  const headers = new Headers2(options.headers);
5751
5751
  if (body !== null && !headers.has("Content-Type")) {
5752
- const contentType = extractContentType(body, this);
5753
- if (contentType) {
5754
- headers.append("Content-Type", contentType);
5752
+ const contentType2 = extractContentType(body, this);
5753
+ if (contentType2) {
5754
+ headers.append("Content-Type", contentType2);
5755
5755
  }
5756
5756
  }
5757
5757
  this[INTERNALS2] = {
@@ -6073,9 +6073,9 @@ var init_request = __esm({
6073
6073
  });
6074
6074
  const headers = new Headers2(init.headers || input.headers || {});
6075
6075
  if (inputBody !== null && !headers.has("Content-Type")) {
6076
- const contentType = extractContentType(inputBody, this);
6077
- if (contentType) {
6078
- headers.set("Content-Type", contentType);
6076
+ const contentType2 = extractContentType(inputBody, this);
6077
+ if (contentType2) {
6078
+ headers.set("Content-Type", contentType2);
6079
6079
  }
6080
6080
  }
6081
6081
  let signal = isRequest(input) ? input.signal : null;
@@ -6547,6 +6547,110 @@ var init_src = __esm({
6547
6547
  }
6548
6548
  });
6549
6549
 
6550
+ // node_modules/content-type/index.js
6551
+ var require_content_type = __commonJS({
6552
+ "node_modules/content-type/index.js"(exports) {
6553
+ "use strict";
6554
+ var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g;
6555
+ var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/;
6556
+ var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
6557
+ var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g;
6558
+ var QUOTE_REGEXP = /([\\"])/g;
6559
+ var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
6560
+ exports.format = format;
6561
+ exports.parse = parse3;
6562
+ function format(obj) {
6563
+ if (!obj || typeof obj !== "object") {
6564
+ throw new TypeError("argument obj is required");
6565
+ }
6566
+ var parameters = obj.parameters;
6567
+ var type = obj.type;
6568
+ if (!type || !TYPE_REGEXP.test(type)) {
6569
+ throw new TypeError("invalid type");
6570
+ }
6571
+ var string4 = type;
6572
+ if (parameters && typeof parameters === "object") {
6573
+ var param;
6574
+ var params = Object.keys(parameters).sort();
6575
+ for (var i2 = 0; i2 < params.length; i2++) {
6576
+ param = params[i2];
6577
+ if (!TOKEN_REGEXP.test(param)) {
6578
+ throw new TypeError("invalid parameter name");
6579
+ }
6580
+ string4 += "; " + param + "=" + qstring(parameters[param]);
6581
+ }
6582
+ }
6583
+ return string4;
6584
+ }
6585
+ function parse3(string4) {
6586
+ if (!string4) {
6587
+ throw new TypeError("argument string is required");
6588
+ }
6589
+ var header = typeof string4 === "object" ? getcontenttype(string4) : string4;
6590
+ if (typeof header !== "string") {
6591
+ throw new TypeError("argument string is required to be a string");
6592
+ }
6593
+ var index = header.indexOf(";");
6594
+ var type = index !== -1 ? header.slice(0, index).trim() : header.trim();
6595
+ if (!TYPE_REGEXP.test(type)) {
6596
+ throw new TypeError("invalid media type");
6597
+ }
6598
+ var obj = new ContentType(type.toLowerCase());
6599
+ if (index !== -1) {
6600
+ var key;
6601
+ var match;
6602
+ var value;
6603
+ PARAM_REGEXP.lastIndex = index;
6604
+ while (match = PARAM_REGEXP.exec(header)) {
6605
+ if (match.index !== index) {
6606
+ throw new TypeError("invalid parameter format");
6607
+ }
6608
+ index += match[0].length;
6609
+ key = match[1].toLowerCase();
6610
+ value = match[2];
6611
+ if (value.charCodeAt(0) === 34) {
6612
+ value = value.slice(1, -1);
6613
+ if (value.indexOf("\\") !== -1) {
6614
+ value = value.replace(QESC_REGEXP, "$1");
6615
+ }
6616
+ }
6617
+ obj.parameters[key] = value;
6618
+ }
6619
+ if (index !== header.length) {
6620
+ throw new TypeError("invalid parameter format");
6621
+ }
6622
+ }
6623
+ return obj;
6624
+ }
6625
+ function getcontenttype(obj) {
6626
+ var header;
6627
+ if (typeof obj.getHeader === "function") {
6628
+ header = obj.getHeader("content-type");
6629
+ } else if (typeof obj.headers === "object") {
6630
+ header = obj.headers && obj.headers["content-type"];
6631
+ }
6632
+ if (typeof header !== "string") {
6633
+ throw new TypeError("content-type header is missing from object");
6634
+ }
6635
+ return header;
6636
+ }
6637
+ function qstring(val) {
6638
+ var str = String(val);
6639
+ if (TOKEN_REGEXP.test(str)) {
6640
+ return str;
6641
+ }
6642
+ if (str.length > 0 && !TEXT_REGEXP.test(str)) {
6643
+ throw new TypeError("invalid parameter value");
6644
+ }
6645
+ return '"' + str.replace(QUOTE_REGEXP, "\\$1") + '"';
6646
+ }
6647
+ function ContentType(type) {
6648
+ this.parameters = /* @__PURE__ */ Object.create(null);
6649
+ this.type = type;
6650
+ }
6651
+ }
6652
+ });
6653
+
6550
6654
  // node_modules/ms/index.js
6551
6655
  var require_ms = __commonJS({
6552
6656
  "node_modules/ms/index.js"(exports, module) {
@@ -21818,7 +21922,7 @@ var require_mime_types = __commonJS({
21818
21922
  var TEXT_TYPE_REGEXP = /^text\//i;
21819
21923
  exports.charset = charset;
21820
21924
  exports.charsets = { lookup: charset };
21821
- exports.contentType = contentType;
21925
+ exports.contentType = contentType2;
21822
21926
  exports.extension = extension;
21823
21927
  exports.extensions = /* @__PURE__ */ Object.create(null);
21824
21928
  exports.lookup = lookup;
@@ -21839,7 +21943,7 @@ var require_mime_types = __commonJS({
21839
21943
  }
21840
21944
  return false;
21841
21945
  }
21842
- function contentType(str) {
21946
+ function contentType2(str) {
21843
21947
  if (!str || typeof str !== "string") {
21844
21948
  return false;
21845
21949
  }
@@ -21981,7 +22085,7 @@ var require_media_typer = __commonJS({
21981
22085
  var require_type_is = __commonJS({
21982
22086
  "node_modules/type-is/index.js"(exports, module) {
21983
22087
  "use strict";
21984
- var contentType = require_dist();
22088
+ var contentType2 = require_dist();
21985
22089
  var mime = require_mime_types();
21986
22090
  var typer = require_media_typer();
21987
22091
  module.exports = typeofrequest;
@@ -22062,7 +22166,7 @@ var require_type_is = __commonJS({
22062
22166
  }
22063
22167
  function normalizeType(value) {
22064
22168
  if (!value) return null;
22065
- var type = contentType.parse(value, { parameters: false }).type;
22169
+ var type = contentType2.parse(value, { parameters: false }).type;
22066
22170
  return typer.test(type) ? type : null;
22067
22171
  }
22068
22172
  }
@@ -22212,7 +22316,7 @@ var require_utils = __commonJS({
22212
22316
  "node_modules/body-parser/lib/utils.js"(exports, module) {
22213
22317
  "use strict";
22214
22318
  var bytes = require_bytes();
22215
- var contentType = require_dist2();
22319
+ var contentType2 = require_dist2();
22216
22320
  var typeis = require_type_is();
22217
22321
  module.exports = {
22218
22322
  getCharset,
@@ -22222,7 +22326,7 @@ var require_utils = __commonJS({
22222
22326
  function getCharset(req) {
22223
22327
  const header = req.headers["content-type"];
22224
22328
  if (!header) return void 0;
22225
- return contentType.parse(header).parameters.charset?.toLowerCase();
22329
+ return contentType2.parse(header).parameters.charset?.toLowerCase();
22226
22330
  }
22227
22331
  function typeChecker(type) {
22228
22332
  return function checkType(req) {
@@ -25646,110 +25750,6 @@ var require_view = __commonJS({
25646
25750
  }
25647
25751
  });
25648
25752
 
25649
- // node_modules/content-type/index.js
25650
- var require_content_type = __commonJS({
25651
- "node_modules/content-type/index.js"(exports) {
25652
- "use strict";
25653
- var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g;
25654
- var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/;
25655
- var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
25656
- var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g;
25657
- var QUOTE_REGEXP = /([\\"])/g;
25658
- var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
25659
- exports.format = format;
25660
- exports.parse = parse3;
25661
- function format(obj) {
25662
- if (!obj || typeof obj !== "object") {
25663
- throw new TypeError("argument obj is required");
25664
- }
25665
- var parameters = obj.parameters;
25666
- var type = obj.type;
25667
- if (!type || !TYPE_REGEXP.test(type)) {
25668
- throw new TypeError("invalid type");
25669
- }
25670
- var string4 = type;
25671
- if (parameters && typeof parameters === "object") {
25672
- var param;
25673
- var params = Object.keys(parameters).sort();
25674
- for (var i2 = 0; i2 < params.length; i2++) {
25675
- param = params[i2];
25676
- if (!TOKEN_REGEXP.test(param)) {
25677
- throw new TypeError("invalid parameter name");
25678
- }
25679
- string4 += "; " + param + "=" + qstring(parameters[param]);
25680
- }
25681
- }
25682
- return string4;
25683
- }
25684
- function parse3(string4) {
25685
- if (!string4) {
25686
- throw new TypeError("argument string is required");
25687
- }
25688
- var header = typeof string4 === "object" ? getcontenttype(string4) : string4;
25689
- if (typeof header !== "string") {
25690
- throw new TypeError("argument string is required to be a string");
25691
- }
25692
- var index = header.indexOf(";");
25693
- var type = index !== -1 ? header.slice(0, index).trim() : header.trim();
25694
- if (!TYPE_REGEXP.test(type)) {
25695
- throw new TypeError("invalid media type");
25696
- }
25697
- var obj = new ContentType(type.toLowerCase());
25698
- if (index !== -1) {
25699
- var key;
25700
- var match;
25701
- var value;
25702
- PARAM_REGEXP.lastIndex = index;
25703
- while (match = PARAM_REGEXP.exec(header)) {
25704
- if (match.index !== index) {
25705
- throw new TypeError("invalid parameter format");
25706
- }
25707
- index += match[0].length;
25708
- key = match[1].toLowerCase();
25709
- value = match[2];
25710
- if (value.charCodeAt(0) === 34) {
25711
- value = value.slice(1, -1);
25712
- if (value.indexOf("\\") !== -1) {
25713
- value = value.replace(QESC_REGEXP, "$1");
25714
- }
25715
- }
25716
- obj.parameters[key] = value;
25717
- }
25718
- if (index !== header.length) {
25719
- throw new TypeError("invalid parameter format");
25720
- }
25721
- }
25722
- return obj;
25723
- }
25724
- function getcontenttype(obj) {
25725
- var header;
25726
- if (typeof obj.getHeader === "function") {
25727
- header = obj.getHeader("content-type");
25728
- } else if (typeof obj.headers === "object") {
25729
- header = obj.headers && obj.headers["content-type"];
25730
- }
25731
- if (typeof header !== "string") {
25732
- throw new TypeError("content-type header is missing from object");
25733
- }
25734
- return header;
25735
- }
25736
- function qstring(val) {
25737
- var str = String(val);
25738
- if (TOKEN_REGEXP.test(str)) {
25739
- return str;
25740
- }
25741
- if (str.length > 0 && !TEXT_REGEXP.test(str)) {
25742
- throw new TypeError("invalid parameter value");
25743
- }
25744
- return '"' + str.replace(QUOTE_REGEXP, "\\$1") + '"';
25745
- }
25746
- function ContentType(type) {
25747
- this.parameters = /* @__PURE__ */ Object.create(null);
25748
- this.type = type;
25749
- }
25750
- }
25751
- });
25752
-
25753
25753
  // node_modules/etag/index.js
25754
25754
  var require_etag = __commonJS({
25755
25755
  "node_modules/etag/index.js"(exports, module) {
@@ -35204,7 +35204,7 @@ var require_mime_types2 = __commonJS({
35204
35204
  var TEXT_TYPE_REGEXP = /^text\//i;
35205
35205
  exports.charset = charset;
35206
35206
  exports.charsets = { lookup: charset };
35207
- exports.contentType = contentType;
35207
+ exports.contentType = contentType2;
35208
35208
  exports.extension = extension;
35209
35209
  exports.extensions = /* @__PURE__ */ Object.create(null);
35210
35210
  exports.lookup = lookup;
@@ -35225,7 +35225,7 @@ var require_mime_types2 = __commonJS({
35225
35225
  }
35226
35226
  return false;
35227
35227
  }
35228
- function contentType(str) {
35228
+ function contentType2(str) {
35229
35229
  if (!str || typeof str !== "string") {
35230
35230
  return false;
35231
35231
  }
@@ -36149,7 +36149,7 @@ var require_utils3 = __commonJS({
36149
36149
  "node_modules/express/lib/utils.js"(exports) {
36150
36150
  "use strict";
36151
36151
  var { METHODS } = __require("node:http");
36152
- var contentType = require_content_type();
36152
+ var contentType2 = require_content_type();
36153
36153
  var etag = require_etag();
36154
36154
  var mime = require_mime_types2();
36155
36155
  var proxyaddr = require_proxy_addr();
@@ -36253,9 +36253,9 @@ var require_utils3 = __commonJS({
36253
36253
  if (!type || !charset) {
36254
36254
  return type;
36255
36255
  }
36256
- var parsed = contentType.parse(type);
36256
+ var parsed = contentType2.parse(type);
36257
36257
  parsed.parameters.charset = charset;
36258
- return contentType.format(parsed);
36258
+ return contentType2.format(parsed);
36259
36259
  };
36260
36260
  function createETagGenerator(options) {
36261
36261
  return function generateETag(body, encoding) {
@@ -37800,14 +37800,14 @@ var require_dist4 = __commonJS({
37800
37800
  var require_accept = __commonJS({
37801
37801
  "node_modules/negotiator/lib/accept.js"(exports, module) {
37802
37802
  "use strict";
37803
- var contentType = require_dist4();
37803
+ var contentType2 = require_dist4();
37804
37804
  module.exports = parseAccept;
37805
37805
  function parseAccept(header) {
37806
37806
  var values = [];
37807
37807
  var index = 0;
37808
37808
  while (index < header.length) {
37809
37809
  var start = skipOptionalWhitespace(header, index);
37810
- var parsed = contentType.parse(header, { comma: true, start });
37810
+ var parsed = contentType2.parse(header, { comma: true, start });
37811
37811
  parsed.type = header.slice(start, start + parsed.type.length);
37812
37812
  values.push(parsed);
37813
37813
  index = parsed.index + 1;
@@ -38000,7 +38000,7 @@ var require_encoding = __commonJS({
38000
38000
  var require_language = __commonJS({
38001
38001
  "node_modules/negotiator/lib/language.js"(exports, module) {
38002
38002
  "use strict";
38003
- var contentType = require_dist4();
38003
+ var contentType2 = require_dist4();
38004
38004
  var parseAccept = require_accept();
38005
38005
  module.exports = preferredLanguages;
38006
38006
  module.exports.preferredLanguages = preferredLanguages;
@@ -38037,7 +38037,7 @@ var require_language = __commonJS({
38037
38037
  return priority;
38038
38038
  }
38039
38039
  function specify(language, spec, index) {
38040
- var p = formatLanguage(contentType.parse(language), 0);
38040
+ var p = formatLanguage(contentType2.parse(language), 0);
38041
38041
  if (!p) return null;
38042
38042
  var s2 = 0;
38043
38043
  if (spec.full.toLowerCase() === p.full.toLowerCase()) {
@@ -38084,7 +38084,7 @@ var require_language = __commonJS({
38084
38084
  var require_mediaType = __commonJS({
38085
38085
  "node_modules/negotiator/lib/mediaType.js"(exports, module) {
38086
38086
  "use strict";
38087
- var contentType = require_dist4();
38087
+ var contentType2 = require_dist4();
38088
38088
  var parseAcceptHeader = require_accept();
38089
38089
  module.exports = preferredMediaTypes;
38090
38090
  module.exports.preferredMediaTypes = preferredMediaTypes;
@@ -38121,7 +38121,7 @@ var require_mediaType = __commonJS({
38121
38121
  return priority;
38122
38122
  }
38123
38123
  function specify(type, spec, index) {
38124
- var p = formatMediaType(contentType.parse(type), 0);
38124
+ var p = formatMediaType(contentType2.parse(type), 0);
38125
38125
  var s2 = 0;
38126
38126
  if (!p) {
38127
38127
  return null;
@@ -47645,7 +47645,7 @@ var require_mime_types3 = __commonJS({
47645
47645
  var TEXT_TYPE_REGEXP = /^text\//i;
47646
47646
  exports.charset = charset;
47647
47647
  exports.charsets = { lookup: charset };
47648
- exports.contentType = contentType;
47648
+ exports.contentType = contentType2;
47649
47649
  exports.extension = extension;
47650
47650
  exports.extensions = /* @__PURE__ */ Object.create(null);
47651
47651
  exports.lookup = lookup;
@@ -47666,7 +47666,7 @@ var require_mime_types3 = __commonJS({
47666
47666
  }
47667
47667
  return false;
47668
47668
  }
47669
- function contentType(str) {
47669
+ function contentType2(str) {
47670
47670
  if (!str || typeof str !== "string") {
47671
47671
  return false;
47672
47672
  }
@@ -57927,7 +57927,7 @@ var require_mime_types4 = __commonJS({
57927
57927
  var TEXT_TYPE_REGEXP = /^text\//i;
57928
57928
  exports.charset = charset;
57929
57929
  exports.charsets = { lookup: charset };
57930
- exports.contentType = contentType;
57930
+ exports.contentType = contentType2;
57931
57931
  exports.extension = extension;
57932
57932
  exports.extensions = /* @__PURE__ */ Object.create(null);
57933
57933
  exports.lookup = lookup;
@@ -57948,7 +57948,7 @@ var require_mime_types4 = __commonJS({
57948
57948
  }
57949
57949
  return false;
57950
57950
  }
57951
- function contentType(str) {
57951
+ function contentType2(str) {
57952
57952
  if (!str || typeof str !== "string") {
57953
57953
  return false;
57954
57954
  }
@@ -58809,7 +58809,7 @@ var require_response = __commonJS({
58809
58809
  var fullPath = !opts.root ? resolve(path7) : path7;
58810
58810
  return this.sendFile(fullPath, opts, done);
58811
58811
  };
58812
- res.contentType = res.type = function contentType(type) {
58812
+ res.contentType = res.type = function contentType2(type) {
58813
58813
  var ct = type.indexOf("/") === -1 ? mime.contentType(type) || "application/octet-stream" : type;
58814
58814
  return this.set("Content-Type", ct);
58815
58815
  };
@@ -73726,10 +73726,9 @@ var ProgressTokenSchema = union([string2(), number2().int()]);
73726
73726
  var CursorSchema = string2();
73727
73727
  var TaskCreationParamsSchema = looseObject({
73728
73728
  /**
73729
- * Time in milliseconds to keep task results available after completion.
73730
- * If null, the task has unlimited lifetime until manually cleaned up.
73729
+ * Requested duration in milliseconds to retain task from creation.
73731
73730
  */
73732
- ttl: union([number2(), _null3()]).optional(),
73731
+ ttl: number2().optional(),
73733
73732
  /**
73734
73733
  * Time in milliseconds to wait between task status requests.
73735
73734
  */
@@ -74026,7 +74025,11 @@ var ClientCapabilitiesSchema = object({
74026
74025
  /**
74027
74026
  * Present if the client supports task creation.
74028
74027
  */
74029
- tasks: ClientTasksCapabilitySchema.optional()
74028
+ tasks: ClientTasksCapabilitySchema.optional(),
74029
+ /**
74030
+ * Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
74031
+ */
74032
+ extensions: record(string2(), AssertObjectSchema).optional()
74030
74033
  });
74031
74034
  var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
74032
74035
  /**
@@ -74087,7 +74090,11 @@ var ServerCapabilitiesSchema = object({
74087
74090
  /**
74088
74091
  * Present if the server supports task creation.
74089
74092
  */
74090
- tasks: ServerTasksCapabilitySchema.optional()
74093
+ tasks: ServerTasksCapabilitySchema.optional(),
74094
+ /**
74095
+ * Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
74096
+ */
74097
+ extensions: record(string2(), AssertObjectSchema).optional()
74091
74098
  });
74092
74099
  var InitializeResultSchema = ResultSchema.extend({
74093
74100
  /**
@@ -74280,6 +74287,12 @@ var ResourceSchema = object({
74280
74287
  * The MIME type of this resource, if known.
74281
74288
  */
74282
74289
  mimeType: optional(string2()),
74290
+ /**
74291
+ * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.
74292
+ *
74293
+ * This can be used by Hosts to display file sizes and estimate context window usage.
74294
+ */
74295
+ size: optional(number2()),
74283
74296
  /**
74284
74297
  * Optional annotations for the client.
74285
74298
  */
@@ -75221,8 +75234,17 @@ var UrlElicitationRequiredError = class extends McpError {
75221
75234
  };
75222
75235
 
75223
75236
  // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
75237
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
75224
75238
  var ReadBuffer = class {
75239
+ constructor(options) {
75240
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
75241
+ }
75225
75242
  append(chunk) {
75243
+ const newSize = (this._buffer?.length ?? 0) + chunk.length;
75244
+ if (newSize > this._maxBufferSize) {
75245
+ this.clear();
75246
+ throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
75247
+ }
75226
75248
  this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
75227
75249
  }
75228
75250
  readMessage() {
@@ -75250,18 +75272,24 @@ function serializeMessage(message) {
75250
75272
 
75251
75273
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
75252
75274
  var StdioServerTransport = class {
75253
- constructor(_stdin = process3.stdin, _stdout = process3.stdout) {
75275
+ constructor(_stdin = process3.stdin, _stdout = process3.stdout, options) {
75254
75276
  this._stdin = _stdin;
75255
75277
  this._stdout = _stdout;
75256
- this._readBuffer = new ReadBuffer();
75257
75278
  this._started = false;
75258
75279
  this._ondata = (chunk) => {
75259
- this._readBuffer.append(chunk);
75260
- this.processReadBuffer();
75280
+ try {
75281
+ this._readBuffer.append(chunk);
75282
+ this.processReadBuffer();
75283
+ } catch (error51) {
75284
+ this.onerror?.(error51);
75285
+ this.close().catch(() => {
75286
+ });
75287
+ }
75261
75288
  };
75262
75289
  this._onerror = (error51) => {
75263
75290
  this.onerror?.(error51);
75264
75291
  };
75292
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
75265
75293
  }
75266
75294
  /**
75267
75295
  * Starts listening for messages on stdin.
@@ -75580,7 +75608,12 @@ var OAuthTokensSchema = object({
75580
75608
  token_type: string2(),
75581
75609
  expires_in: coerce_exports.number().optional(),
75582
75610
  scope: string2().optional(),
75583
- refresh_token: string2().optional()
75611
+ refresh_token: string2().optional(),
75612
+ /**
75613
+ * Not part of the wire format: the authorization server this value was obtained from, added by
75614
+ * the client's `auth()` before it is stored and compared when it is read back.
75615
+ */
75616
+ issuer: string2().optional().catch(void 0)
75584
75617
  }).strip();
75585
75618
  var OAuthErrorResponseSchema = object({
75586
75619
  error: string2(),
@@ -75610,7 +75643,12 @@ var OAuthClientInformationSchema = object({
75610
75643
  client_id: string2(),
75611
75644
  client_secret: string2().optional(),
75612
75645
  client_id_issued_at: number2().optional(),
75613
- client_secret_expires_at: number2().optional()
75646
+ client_secret_expires_at: number2().optional(),
75647
+ /**
75648
+ * Not part of the wire format: the authorization server this value was obtained from, added by
75649
+ * the client's `auth()` before it is stored and compared when it is read back.
75650
+ */
75651
+ issuer: string2().optional().catch(void 0)
75614
75652
  }).strip();
75615
75653
  var OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(OAuthClientInformationSchema);
75616
75654
  var OAuthClientRegistrationErrorSchema = object({
@@ -75750,12 +75788,12 @@ var AUTHORIZATION_CODE_RESPONSE_TYPE = "code";
75750
75788
  var AUTHORIZATION_CODE_CHALLENGE_METHOD = "S256";
75751
75789
  function selectClientAuthMethod(clientInformation, supportedMethods) {
75752
75790
  const hasClientSecret = clientInformation.client_secret !== void 0;
75753
- if (supportedMethods.length === 0) {
75754
- return hasClientSecret ? "client_secret_post" : "none";
75755
- }
75756
- if ("token_endpoint_auth_method" in clientInformation && clientInformation.token_endpoint_auth_method && isClientAuthMethod(clientInformation.token_endpoint_auth_method) && supportedMethods.includes(clientInformation.token_endpoint_auth_method)) {
75791
+ if ("token_endpoint_auth_method" in clientInformation && clientInformation.token_endpoint_auth_method && isClientAuthMethod(clientInformation.token_endpoint_auth_method) && (supportedMethods.length === 0 || supportedMethods.includes(clientInformation.token_endpoint_auth_method))) {
75757
75792
  return clientInformation.token_endpoint_auth_method;
75758
75793
  }
75794
+ if (supportedMethods.length === 0) {
75795
+ return hasClientSecret ? "client_secret_basic" : "none";
75796
+ }
75759
75797
  if (hasClientSecret && supportedMethods.includes("client_secret_basic")) {
75760
75798
  return "client_secret_basic";
75761
75799
  }
@@ -75812,6 +75850,26 @@ async function parseErrorResponse(input) {
75812
75850
  return new ServerError(errorMessage);
75813
75851
  }
75814
75852
  }
75853
+ function issuersMatch(a, b) {
75854
+ let [x2, y] = [a, b];
75855
+ try {
75856
+ [x2, y] = [new URL(a).href, new URL(b).href];
75857
+ } catch {
75858
+ }
75859
+ return x2 === y || x2.endsWith("/") && x2.slice(0, -1) === y || y.endsWith("/") && y.slice(0, -1) === x2;
75860
+ }
75861
+ function discardIfIssuerMismatch(stored, issuer) {
75862
+ if (!stored)
75863
+ return void 0;
75864
+ if (typeof stored.issuer !== "string")
75865
+ return stored.issuer == null ? stored : { ...stored, issuer: void 0 };
75866
+ return issuersMatch(stored.issuer, issuer) ? stored : void 0;
75867
+ }
75868
+ var TokenResponseSchema = OAuthTokensSchema.omit({ issuer: true });
75869
+ var RegistrationResponseSchema = OAuthClientInformationFullSchema.omit({ issuer: true });
75870
+ function boundElsewhereError(stored, issuer) {
75871
+ return new Error(`OAuth client information is bound to authorization server ${stored.issuer} and is not presented to ${issuer}. Clear the stored client information, or correct \`expectedIssuer\`, if the authorization server has moved.`);
75872
+ }
75815
75873
  async function auth(provider, options) {
75816
75874
  try {
75817
75875
  return await authInternal(provider, options);
@@ -75827,23 +75885,62 @@ async function auth(provider, options) {
75827
75885
  }
75828
75886
  }
75829
75887
  async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn }) {
75888
+ const cachedState = await provider.discoveryState?.();
75830
75889
  let resourceMetadata;
75831
75890
  let authorizationServerUrl;
75832
- try {
75833
- resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl }, fetchFn);
75834
- if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
75835
- authorizationServerUrl = resourceMetadata.authorization_servers[0];
75891
+ let metadata;
75892
+ let effectiveResourceMetadataUrl = resourceMetadataUrl;
75893
+ if (!effectiveResourceMetadataUrl && cachedState?.resourceMetadataUrl) {
75894
+ effectiveResourceMetadataUrl = new URL(cachedState.resourceMetadataUrl);
75895
+ }
75896
+ if (cachedState?.authorizationServerUrl) {
75897
+ authorizationServerUrl = cachedState.authorizationServerUrl;
75898
+ resourceMetadata = cachedState.resourceMetadata;
75899
+ metadata = cachedState.authorizationServerMetadata ?? await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn });
75900
+ if (!resourceMetadata) {
75901
+ try {
75902
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl }, fetchFn);
75903
+ } catch {
75904
+ }
75836
75905
  }
75837
- } catch {
75838
- }
75839
- if (!authorizationServerUrl) {
75840
- authorizationServerUrl = new URL("/", serverUrl);
75906
+ if (metadata !== cachedState.authorizationServerMetadata || resourceMetadata !== cachedState.resourceMetadata) {
75907
+ await provider.saveDiscoveryState?.({
75908
+ authorizationServerUrl: String(authorizationServerUrl),
75909
+ resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
75910
+ resourceMetadata,
75911
+ authorizationServerMetadata: metadata
75912
+ });
75913
+ }
75914
+ } else {
75915
+ const serverInfo = await discoverOAuthServerInfo(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl, fetchFn });
75916
+ authorizationServerUrl = serverInfo.authorizationServerUrl;
75917
+ metadata = serverInfo.authorizationServerMetadata;
75918
+ resourceMetadata = serverInfo.resourceMetadata;
75919
+ await provider.saveDiscoveryState?.({
75920
+ authorizationServerUrl: String(authorizationServerUrl),
75921
+ resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
75922
+ resourceMetadata,
75923
+ authorizationServerMetadata: metadata
75924
+ });
75841
75925
  }
75842
- const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
75843
- const metadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, {
75844
- fetchFn
75845
- });
75846
- let clientInformation = await Promise.resolve(provider.clientInformation());
75926
+ const issuer = String(authorizationServerUrl);
75927
+ const selectedResource = await selectResourceURL(serverUrl, provider, resourceMetadata);
75928
+ const resource = selectedResource && resourceMetadata && !provider.validateResourceURL ? resourceMetadata.resource : selectedResource;
75929
+ const resolvedScope = scope || resourceMetadata?.scopes_supported?.join(" ") || provider.clientMetadata.scope;
75930
+ const storedClientInformation = await Promise.resolve(provider.clientInformation());
75931
+ let clientInformation = discardIfIssuerMismatch(storedClientInformation, issuer);
75932
+ const canRegisterAgain = provider.saveClientInformation !== void 0 && !!provider.redirectUrl;
75933
+ if (storedClientInformation && !clientInformation && !canRegisterAgain) {
75934
+ throw boundElsewhereError(storedClientInformation, issuer);
75935
+ }
75936
+ const unstampedClientInformation = clientInformation?.issuer == null ? clientInformation : void 0;
75937
+ const bindClientInformation = async () => {
75938
+ try {
75939
+ if (unstampedClientInformation)
75940
+ await provider.saveClientInformation?.({ ...unstampedClientInformation, issuer });
75941
+ } catch {
75942
+ }
75943
+ };
75847
75944
  if (!clientInformation) {
75848
75945
  if (authorizationCode !== void 0) {
75849
75946
  throw new Error("Existing OAuth client information is required when exchanging an authorization code");
@@ -75855,9 +75952,7 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
75855
75952
  }
75856
75953
  const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;
75857
75954
  if (shouldUseUrlBasedClientId) {
75858
- clientInformation = {
75859
- client_id: clientMetadataUrl
75860
- };
75955
+ clientInformation = { client_id: clientMetadataUrl, issuer };
75861
75956
  await provider.saveClientInformation?.(clientInformation);
75862
75957
  } else {
75863
75958
  if (!provider.saveClientInformation) {
@@ -75866,10 +75961,11 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
75866
75961
  const fullInformation = await registerClient(authorizationServerUrl, {
75867
75962
  metadata,
75868
75963
  clientMetadata: provider.clientMetadata,
75964
+ scope: resolvedScope,
75869
75965
  fetchFn
75870
75966
  });
75871
- await provider.saveClientInformation(fullInformation);
75872
- clientInformation = fullInformation;
75967
+ clientInformation = { ...fullInformation, issuer };
75968
+ await provider.saveClientInformation(clientInformation);
75873
75969
  }
75874
75970
  }
75875
75971
  const nonInteractiveFlow = !provider.redirectUrl;
@@ -75880,10 +75976,14 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
75880
75976
  authorizationCode,
75881
75977
  fetchFn
75882
75978
  });
75883
- await provider.saveTokens(tokens2);
75979
+ await bindClientInformation();
75980
+ await provider.saveTokens({ ...tokens2, issuer });
75884
75981
  return "AUTHORIZED";
75885
75982
  }
75886
- const tokens = await provider.tokens();
75983
+ const tokens = discardIfIssuerMismatch(await provider.tokens(), issuer);
75984
+ if (tokens?.refresh_token && tokens.issuer == null) {
75985
+ console.warn("[mcp-sdk] stored OAuth tokens have no 'issuer' property (saved by an earlier version, or by a provider that does not keep it) and are used as-is; make sure your OAuthClientProvider stores what saveTokens() and saveClientInformation() receive unchanged.");
75986
+ }
75887
75987
  if (tokens?.refresh_token) {
75888
75988
  try {
75889
75989
  const newTokens = await refreshAuthorization(authorizationServerUrl, {
@@ -75894,7 +75994,8 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
75894
75994
  addClientAuthentication: provider.addClientAuthentication,
75895
75995
  fetchFn
75896
75996
  });
75897
- await provider.saveTokens(newTokens);
75997
+ await bindClientInformation();
75998
+ await provider.saveTokens({ ...newTokens, issuer });
75898
75999
  return "AUTHORIZED";
75899
76000
  } catch (error51) {
75900
76001
  if (!(error51 instanceof OAuthError) || error51 instanceof ServerError) {
@@ -75909,7 +76010,7 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
75909
76010
  clientInformation,
75910
76011
  state,
75911
76012
  redirectUrl: provider.redirectUrl,
75912
- scope: scope || resourceMetadata?.scopes_supported?.join(" ") || provider.clientMetadata.scope,
76013
+ scope: resolvedScope,
75913
76014
  resource
75914
76015
  });
75915
76016
  await provider.saveCodeVerifier(codeVerifier);
@@ -76097,6 +76198,29 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fet
76097
76198
  }
76098
76199
  return void 0;
76099
76200
  }
76201
+ async function discoverOAuthServerInfo(serverUrl, opts) {
76202
+ let resourceMetadata;
76203
+ let authorizationServerUrl;
76204
+ try {
76205
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: opts?.resourceMetadataUrl }, opts?.fetchFn);
76206
+ if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
76207
+ authorizationServerUrl = resourceMetadata.authorization_servers[0];
76208
+ }
76209
+ } catch {
76210
+ }
76211
+ if (!authorizationServerUrl) {
76212
+ authorizationServerUrl = String(new URL("/", serverUrl));
76213
+ }
76214
+ const authorizationServerMetadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn: opts?.fetchFn });
76215
+ return {
76216
+ authorizationServerUrl,
76217
+ authorizationServerMetadata,
76218
+ resourceMetadata
76219
+ };
76220
+ }
76221
+ function resourceIndicatorToString(resource) {
76222
+ return typeof resource === "string" ? resource : resource.href;
76223
+ }
76100
76224
  async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) {
76101
76225
  let authorizationUrl;
76102
76226
  if (metadata) {
@@ -76128,7 +76252,7 @@ async function startAuthorization(authorizationServerUrl, { metadata, clientInfo
76128
76252
  authorizationUrl.searchParams.append("prompt", "consent");
76129
76253
  }
76130
76254
  if (resource) {
76131
- authorizationUrl.searchParams.set("resource", resource.href);
76255
+ authorizationUrl.searchParams.set("resource", resourceIndicatorToString(resource));
76132
76256
  }
76133
76257
  return { authorizationUrl, codeVerifier };
76134
76258
  }
@@ -76147,7 +76271,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
76147
76271
  Accept: "application/json"
76148
76272
  });
76149
76273
  if (resource) {
76150
- tokenRequestParams.set("resource", resource.href);
76274
+ tokenRequestParams.set("resource", resourceIndicatorToString(resource));
76151
76275
  }
76152
76276
  if (addClientAuthentication) {
76153
76277
  await addClientAuthentication(headers, tokenRequestParams, tokenUrl, metadata);
@@ -76164,7 +76288,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
76164
76288
  if (!response.ok) {
76165
76289
  throw await parseErrorResponse(response);
76166
76290
  }
76167
- return OAuthTokensSchema.parse(await response.json());
76291
+ return TokenResponseSchema.parse(await response.json());
76168
76292
  }
76169
76293
  async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
76170
76294
  const tokenRequestParams = new URLSearchParams({
@@ -76182,6 +76306,15 @@ async function refreshAuthorization(authorizationServerUrl, { metadata, clientIn
76182
76306
  return { refresh_token: refreshToken, ...tokens };
76183
76307
  }
76184
76308
  async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
76309
+ const readClientInformation = async () => {
76310
+ const storedClientInformation = await provider.clientInformation();
76311
+ const checked = discardIfIssuerMismatch(storedClientInformation, String(authorizationServerUrl));
76312
+ if (storedClientInformation && !checked) {
76313
+ throw boundElsewhereError(storedClientInformation, String(authorizationServerUrl));
76314
+ }
76315
+ return checked;
76316
+ };
76317
+ let clientInformation = await readClientInformation();
76185
76318
  const scope = provider.clientMetadata.scope;
76186
76319
  let tokenRequestParams;
76187
76320
  if (provider.prepareTokenRequest) {
@@ -76197,17 +76330,17 @@ async function fetchToken(provider, authorizationServerUrl, { metadata, resource
76197
76330
  const codeVerifier = await provider.codeVerifier();
76198
76331
  tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, provider.redirectUrl);
76199
76332
  }
76200
- const clientInformation = await provider.clientInformation();
76333
+ clientInformation ?? (clientInformation = await readClientInformation());
76201
76334
  return executeTokenRequest(authorizationServerUrl, {
76202
76335
  metadata,
76203
76336
  tokenRequestParams,
76204
- clientInformation: clientInformation ?? void 0,
76337
+ clientInformation,
76205
76338
  addClientAuthentication: provider.addClientAuthentication,
76206
76339
  resource,
76207
76340
  fetchFn
76208
76341
  });
76209
76342
  }
76210
- async function registerClient(authorizationServerUrl, { metadata, clientMetadata, fetchFn }) {
76343
+ async function registerClient(authorizationServerUrl, { metadata, clientMetadata, scope, fetchFn }) {
76211
76344
  let registrationUrl;
76212
76345
  if (metadata) {
76213
76346
  if (!metadata.registration_endpoint) {
@@ -76222,12 +76355,15 @@ async function registerClient(authorizationServerUrl, { metadata, clientMetadata
76222
76355
  headers: {
76223
76356
  "Content-Type": "application/json"
76224
76357
  },
76225
- body: JSON.stringify(clientMetadata)
76358
+ body: JSON.stringify({
76359
+ ...clientMetadata,
76360
+ ...scope !== void 0 ? { scope } : {}
76361
+ })
76226
76362
  });
76227
76363
  if (!response.ok) {
76228
76364
  throw await parseErrorResponse(response);
76229
76365
  }
76230
- return OAuthClientInformationFullSchema.parse(await response.json());
76366
+ return RegistrationResponseSchema.parse(await response.json());
76231
76367
  }
76232
76368
 
76233
76369
  // src/lib/utils.ts
@@ -76245,7 +76381,6 @@ import crypto3 from "crypto";
76245
76381
  // src/lib/persistent-auth-config.ts
76246
76382
  import path from "path";
76247
76383
  import fs from "fs/promises";
76248
- import fsSync from "fs";
76249
76384
  import crypto2 from "crypto";
76250
76385
  function getConfigDir() {
76251
76386
  const baseConfigDir = CONFIG.WP_MCP_CONFIG_DIR;
@@ -76342,21 +76477,25 @@ async function writeTokens(serverUrlHash, tokens) {
76342
76477
  ...tokens,
76343
76478
  obtained_at: Date.now()
76344
76479
  };
76345
- await writeJsonFile(serverUrlHash, "tokens.json", tokensWithTimestamp);
76480
+ await ensureConfigDir();
76481
+ const file2 = getConfigFilePath(serverUrlHash, "tokens.json");
76482
+ const temporary = `${file2}.${crypto2.randomUUID()}.tmp`;
76483
+ try {
76484
+ await fs.writeFile(temporary, JSON.stringify(tokensWithTimestamp, null, 2), {
76485
+ encoding: "utf-8",
76486
+ mode: 384,
76487
+ flag: "wx"
76488
+ });
76489
+ await fs.rename(temporary, file2);
76490
+ } finally {
76491
+ await fs.rm(temporary, { force: true });
76492
+ }
76346
76493
  logger.info(`Stored tokens for server hash: ${serverUrlHash}`, "AUTH");
76347
76494
  } catch (error51) {
76348
76495
  logger.error(`Error writing tokens for ${serverUrlHash}`, "AUTH", error51);
76349
76496
  throw error51;
76350
76497
  }
76351
76498
  }
76352
- async function deleteTokens(serverUrlHash) {
76353
- try {
76354
- await deleteConfigFile(serverUrlHash, "tokens.json");
76355
- logger.info(`Deleted tokens for server hash: ${serverUrlHash}`, "AUTH");
76356
- } catch (error51) {
76357
- logger.error(`Error deleting tokens for ${serverUrlHash}`, "AUTH", error51);
76358
- }
76359
- }
76360
76499
  async function readClientInfo(serverUrlHash) {
76361
76500
  try {
76362
76501
  const clientInfo = await readJsonFile(serverUrlHash, "client_info.json");
@@ -76383,7 +76522,7 @@ function isTokenValid(tokens) {
76383
76522
  if (!tokens?.access_token) {
76384
76523
  return { isValid: false, error: "No access token" };
76385
76524
  }
76386
- if (!tokens.expires_in || !tokens.obtained_at) {
76525
+ if (tokens.expires_in === void 0 || tokens.obtained_at === void 0) {
76387
76526
  return { isValid: true };
76388
76527
  }
76389
76528
  const now = Date.now();
@@ -76415,30 +76554,6 @@ async function getValidTokens(serverUrlHash) {
76415
76554
  }
76416
76555
  return tokens;
76417
76556
  }
76418
- async function cleanupExpiredTokens() {
76419
- try {
76420
- const configDir = getConfigDir();
76421
- if (!fsSync.existsSync(configDir)) {
76422
- return;
76423
- }
76424
- const files = await fs.readdir(configDir);
76425
- const tokenFiles = files.filter((file2) => file2.endsWith("_tokens.json"));
76426
- let cleaned = 0;
76427
- for (const file2 of tokenFiles) {
76428
- const serverHash = file2.replace("_tokens.json", "");
76429
- const tokens = await readTokens(serverHash);
76430
- if (tokens && !isTokenValid(tokens).isValid) {
76431
- await deleteTokens(serverHash);
76432
- cleaned++;
76433
- }
76434
- }
76435
- if (cleaned > 0) {
76436
- logger.info(`Cleaned up ${cleaned} expired token files`, "AUTH");
76437
- }
76438
- } catch (error51) {
76439
- logger.error("Error during token cleanup", "AUTH", error51);
76440
- }
76441
- }
76442
76557
 
76443
76558
  // src/lib/port-utils.ts
76444
76559
  var OAuthClientInfoSchema = external_exports.object({
@@ -76446,12 +76561,12 @@ var OAuthClientInfoSchema = external_exports.object({
76446
76561
  client_id: external_exports.string().optional(),
76447
76562
  client_secret: external_exports.string().optional()
76448
76563
  });
76449
- async function findAvailablePort(preferredPort) {
76564
+ async function findAvailablePort(preferredPort, host = "127.0.0.1") {
76450
76565
  return new Promise((resolve, reject) => {
76451
76566
  const server = net.createServer();
76452
76567
  server.on("error", (err) => {
76453
76568
  if (err.code === "EADDRINUSE") {
76454
- server.listen(0);
76569
+ server.listen(0, host);
76455
76570
  } else {
76456
76571
  reject(err);
76457
76572
  }
@@ -76462,7 +76577,7 @@ async function findAvailablePort(preferredPort) {
76462
76577
  resolve(port);
76463
76578
  });
76464
76579
  });
76465
- server.listen(preferredPort || 0);
76580
+ server.listen(preferredPort || 0, host);
76466
76581
  });
76467
76582
  }
76468
76583
  function calculateDefaultPort(serverUrlHash) {
@@ -76490,24 +76605,17 @@ async function findExistingClientPort(serverUrlHash) {
76490
76605
  function getServerUrlHash(serverUrl) {
76491
76606
  return crypto3.createHash("md5").update(serverUrl).digest("hex");
76492
76607
  }
76493
- async function selectCallbackPort(serverUrl, specifiedPort, _unused) {
76608
+ async function selectCallbackPort(serverUrl, specifiedPort, _unused, host = "127.0.0.1") {
76494
76609
  const serverUrlHash = getServerUrlHash(serverUrl);
76495
76610
  if (specifiedPort) {
76496
76611
  return specifiedPort;
76497
76612
  }
76498
- const defaultPort = calculateDefaultPort(serverUrlHash);
76499
- const [existingClientPort, availablePort] = await Promise.all([
76500
- findExistingClientPort(serverUrlHash),
76501
- findAvailablePort(defaultPort)
76502
- ]);
76503
- if (existingClientPort) {
76504
- return existingClientPort;
76505
- }
76506
- return availablePort;
76613
+ const existingClientPort = await findExistingClientPort(serverUrlHash);
76614
+ return findAvailablePort(existingClientPort ?? calculateDefaultPort(serverUrlHash), host);
76507
76615
  }
76508
76616
 
76509
76617
  // src/lib/config.ts
76510
- var MCP_WORDPRESS_REMOTE_VERSION = "0.5.1";
76618
+ var MCP_WORDPRESS_REMOTE_VERSION = "0.5.3";
76511
76619
  function parsePositiveIntEnv(value, defaultValue) {
76512
76620
  if (!value) {
76513
76621
  return defaultValue;
@@ -76708,8 +76816,8 @@ async function getOAuthCallbackPort() {
76708
76816
  return selectCallbackPort(
76709
76817
  CONFIG.WP_API_URL,
76710
76818
  CONFIG.OAUTH_CALLBACK_PORT,
76711
- false
76712
- // Always treat as self-hosted for port selection
76819
+ false,
76820
+ CONFIG.OAUTH_HOST
76713
76821
  );
76714
76822
  }
76715
76823
  function formatConfigError(error51) {
@@ -77080,6 +77188,23 @@ async function proxyFetch(url2, init) {
77080
77188
  // src/lib/wordpress-transport.ts
77081
77189
  import { Readable } from "node:stream";
77082
77190
 
77191
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/mediaType.js
77192
+ var import_content_type = __toESM(require_content_type(), 1);
77193
+ function mediaTypeEssence(header) {
77194
+ if (!header) {
77195
+ return void 0;
77196
+ }
77197
+ try {
77198
+ return import_content_type.default.parse(header).type;
77199
+ } catch {
77200
+ const essence = (header.split(";", 1)[0] ?? "").trim().toLowerCase();
77201
+ if (essence === "" || header.slice(essence.length).includes(",")) {
77202
+ return void 0;
77203
+ }
77204
+ return essence;
77205
+ }
77206
+ }
77207
+
77083
77208
  // node_modules/eventsource-parser/dist/stream.js
77084
77209
  var EventSourceParserStream = class extends TransformStream {
77085
77210
  constructor({ onError, onRetry, onComment } = {}) {
@@ -77227,13 +77352,13 @@ var StreamableHTTPClientTransport = class {
77227
77352
  this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));
77228
77353
  return;
77229
77354
  }
77230
- const delay = this._getNextReconnectionDelay(attemptCount);
77355
+ const delay2 = this._getNextReconnectionDelay(attemptCount);
77231
77356
  this._reconnectionTimeout = setTimeout(() => {
77232
77357
  this._startOrAuthSse(options).catch((error51) => {
77233
77358
  this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error51 instanceof Error ? error51.message : String(error51)}`));
77234
77359
  this._scheduleReconnection(options, attemptCount + 1);
77235
77360
  });
77236
- }, delay);
77361
+ }, delay2);
77237
77362
  }
77238
77363
  _handleSseStream(stream, options, isReconnectable) {
77239
77364
  if (!stream) {
@@ -77420,11 +77545,12 @@ var StreamableHTTPClientTransport = class {
77420
77545
  }
77421
77546
  const messages = Array.isArray(message) ? message : [message];
77422
77547
  const hasRequests = messages.filter((msg) => "method" in msg && "id" in msg && msg.id !== void 0).length > 0;
77423
- const contentType = response.headers.get("content-type");
77548
+ const contentType2 = response.headers.get("content-type");
77549
+ const responseMediaType = mediaTypeEssence(contentType2);
77424
77550
  if (hasRequests) {
77425
- if (contentType?.includes("text/event-stream")) {
77551
+ if (responseMediaType === "text/event-stream") {
77426
77552
  this._handleSseStream(response.body, { onresumptiontoken }, false);
77427
- } else if (contentType?.includes("application/json")) {
77553
+ } else if (responseMediaType === "application/json") {
77428
77554
  const data = await response.json();
77429
77555
  const responseMessages = Array.isArray(data) ? data.map((msg) => JSONRPCMessageSchema.parse(msg)) : [JSONRPCMessageSchema.parse(data)];
77430
77556
  for (const msg of responseMessages) {
@@ -77432,7 +77558,7 @@ var StreamableHTTPClientTransport = class {
77432
77558
  }
77433
77559
  } else {
77434
77560
  await response.body?.cancel();
77435
- throw new StreamableHTTPError(-1, `Unexpected content type: ${contentType}`);
77561
+ throw new StreamableHTTPError(-1, `Unexpected content type: ${contentType2}`);
77436
77562
  }
77437
77563
  } else {
77438
77564
  await response.body?.cancel();
@@ -78138,10 +78264,48 @@ defineLazyProperty(apps, "browserPrivate", () => "browserPrivate");
78138
78264
  var open_default = open;
78139
78265
 
78140
78266
  // src/lib/mcp-oauth-provider.ts
78267
+ import lockfile from "proper-lockfile";
78268
+ import { setTimeout as delay } from "node:timers/promises";
78141
78269
  import { EventEmitter } from "node:events";
78142
78270
 
78143
78271
  // src/lib/mcp-oauth-utils.ts
78144
78272
  import crypto4 from "crypto";
78273
+
78274
+ // src/lib/oauth-http.ts
78275
+ function validateOAuthUrl(value) {
78276
+ let url2;
78277
+ try {
78278
+ url2 = new URL(value);
78279
+ } catch {
78280
+ throw new OAuthError2("Invalid OAuth URL", "INVALID_OAUTH_URL");
78281
+ }
78282
+ const loopback = url2.hostname === "localhost" || url2.hostname === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(url2.hostname);
78283
+ if (url2.protocol !== "https:" && !(url2.protocol === "http:" && loopback) || url2.username || url2.password || url2.hash) {
78284
+ throw new OAuthError2(
78285
+ "OAuth URLs require HTTPS (HTTP is allowed only on loopback)",
78286
+ "INSECURE_OAUTH_URL"
78287
+ );
78288
+ }
78289
+ return url2;
78290
+ }
78291
+ async function oauthFetch(value, init = {}) {
78292
+ const url2 = validateOAuthUrl(value);
78293
+ const headers = new Headers(
78294
+ url2.origin === new URL(CONFIG.WP_API_URL).origin ? getCustomHeaders() : {}
78295
+ );
78296
+ new Headers(init.headers).forEach((value2, key) => headers.set(key, value2));
78297
+ const response = await proxyFetch(url2.toString(), {
78298
+ ...init,
78299
+ headers,
78300
+ redirect: "error",
78301
+ signal: init.signal ?? AbortSignal.timeout(CONFIG.OAUTH_TIMEOUT)
78302
+ });
78303
+ if (response.status >= 300 && response.status < 400)
78304
+ throw new OAuthError2("OAuth endpoint redirects are not accepted", "OAUTH_REDIRECT");
78305
+ return response;
78306
+ }
78307
+
78308
+ // src/lib/mcp-oauth-utils.ts
78145
78309
  function generatePKCE() {
78146
78310
  const codeVerifier = crypto4.randomBytes(64).toString("base64url").substring(0, 128);
78147
78311
  const codeChallenge = crypto4.createHash("sha256").update(codeVerifier).digest("base64url");
@@ -78169,118 +78333,99 @@ function generateCanonicalResourceURI(serverUrl) {
78169
78333
  }
78170
78334
  }
78171
78335
  async function discoverAuthorizationServerMetadata2(authorizationServerUrl) {
78172
- try {
78173
- const metadataUrl = new URL("/.well-known/oauth-authorization-server", authorizationServerUrl);
78174
- logger.oauth(`Discovering authorization server metadata: ${metadataUrl}`);
78175
- const customHeaders = getCustomHeaders();
78176
- const response = await proxyFetch(metadataUrl.toString(), {
78177
- headers: {
78178
- Accept: "application/json",
78179
- ...customHeaders
78180
- }
78181
- });
78182
- if (!response.ok) {
78336
+ const issuer = validateOAuthUrl(authorizationServerUrl);
78337
+ if (issuer.search) throw new OAuthError2("Issuer must not contain a query", "INVALID_METADATA");
78338
+ for (const { url: url2 } of buildDiscoveryUrls(issuer)) {
78339
+ const response = await oauthFetch(url2.toString(), { headers: { Accept: "application/json" } });
78340
+ if ([404, 405].includes(response.status)) continue;
78341
+ if (!response.ok)
78183
78342
  throw new OAuthError2(
78184
- `Failed to fetch authorization server metadata: ${response.status}`,
78343
+ `Metadata request failed (${response.status})`,
78185
78344
  "METADATA_DISCOVERY_FAILED"
78186
78345
  );
78187
- }
78188
- const metadata = await response.json();
78189
- if (!metadata.authorization_endpoint || !metadata.token_endpoint) {
78346
+ const parsed = OAuthMetadataSchema.safeParse(await response.json());
78347
+ if (!parsed.success || parsed.data.issuer !== authorizationServerUrl) {
78190
78348
  throw new OAuthError2(
78191
- "Authorization server metadata missing required endpoints",
78349
+ "Authorization metadata has an invalid or mismatched issuer",
78192
78350
  "INVALID_METADATA"
78193
78351
  );
78194
78352
  }
78195
- logger.oauth("Authorization server metadata discovered successfully");
78353
+ const metadata = parsed.data;
78354
+ validateOAuthUrl(metadata.issuer);
78355
+ validateOAuthUrl(metadata.authorization_endpoint);
78356
+ validateOAuthUrl(metadata.token_endpoint);
78357
+ if (metadata.registration_endpoint) validateOAuthUrl(metadata.registration_endpoint);
78196
78358
  return metadata;
78197
- } catch (error51) {
78198
- if (error51 instanceof OAuthError2) {
78199
- throw error51;
78200
- }
78201
- throw new OAuthError2(
78202
- `Error discovering authorization server metadata: ${error51 instanceof Error ? error51.message : String(error51)}`,
78203
- "METADATA_DISCOVERY_ERROR"
78204
- );
78205
78359
  }
78360
+ throw new OAuthError2("No authorization server metadata found", "METADATA_DISCOVERY_FAILED");
78206
78361
  }
78207
- async function discoverProtectedResourceMetadata(resourceUrl) {
78208
- try {
78209
- const url2 = new URL(resourceUrl);
78210
- const metadataUrl = new URL("/.well-known/oauth-protected-resource", url2.origin);
78211
- logger.oauth(`Discovering protected resource metadata: ${metadataUrl}`);
78212
- const customHeaders = getCustomHeaders();
78213
- const response = await proxyFetch(metadataUrl.toString(), {
78214
- headers: {
78215
- Accept: "application/json",
78216
- ...customHeaders
78362
+ async function discoverProtectedResourceMetadata(resourceUrl, resourceMetadataUrl) {
78363
+ const resource = validateOAuthUrl(resourceUrl);
78364
+ const candidates = resourceMetadataUrl ? [{ url: resourceMetadataUrl, expected: resourceUrl }] : [
78365
+ ...resource.pathname !== "/" ? [
78366
+ {
78367
+ url: `${resource.origin}/.well-known/oauth-protected-resource${resource.pathname}`,
78368
+ expected: resourceUrl
78217
78369
  }
78218
- });
78219
- if (!response.ok) {
78370
+ ] : [],
78371
+ {
78372
+ url: `${resource.origin}/.well-known/oauth-protected-resource`,
78373
+ expected: resource.pathname === "/" ? resourceUrl : resource.origin
78374
+ }
78375
+ ];
78376
+ for (const candidate of candidates) {
78377
+ const response = await oauthFetch(candidate.url, { headers: { Accept: "application/json" } });
78378
+ if (!resourceMetadataUrl && [404, 405].includes(response.status)) continue;
78379
+ if (!response.ok)
78220
78380
  throw new OAuthError2(
78221
- `Failed to fetch protected resource metadata: ${response.status}`,
78381
+ `Resource metadata request failed (${response.status})`,
78222
78382
  "RESOURCE_METADATA_DISCOVERY_FAILED"
78223
78383
  );
78224
- }
78225
- const metadata = await response.json();
78226
- if (!metadata.authorization_servers || metadata.authorization_servers.length === 0) {
78384
+ const parsed = OAuthProtectedResourceMetadataSchema.safeParse(await response.json());
78385
+ if (!parsed.success || parsed.data.resource !== candidate.expected || !parsed.data.authorization_servers?.length) {
78227
78386
  throw new OAuthError2(
78228
- "Protected resource metadata missing authorization servers",
78387
+ "Protected resource metadata has an invalid or mismatched resource",
78229
78388
  "INVALID_RESOURCE_METADATA"
78230
78389
  );
78231
78390
  }
78232
- logger.oauth("Protected resource metadata discovered successfully");
78233
- return metadata;
78234
- } catch (error51) {
78235
- if (error51 instanceof OAuthError2) {
78236
- throw error51;
78237
- }
78238
- throw new OAuthError2(
78239
- `Error discovering protected resource metadata: ${error51 instanceof Error ? error51.message : String(error51)}`,
78240
- "RESOURCE_METADATA_DISCOVERY_ERROR"
78241
- );
78391
+ for (const issuer of parsed.data.authorization_servers) validateOAuthUrl(issuer);
78392
+ return parsed.data;
78242
78393
  }
78394
+ throw new OAuthError2(
78395
+ "No protected resource metadata found",
78396
+ "RESOURCE_METADATA_DISCOVERY_FAILED"
78397
+ );
78243
78398
  }
78244
78399
  function parseWWWAuthenticateHeader(headerValue2) {
78245
- const parts = headerValue2.split(/\s+/);
78246
- const scheme = parts[0];
78247
- const result = { scheme };
78248
- const paramString = parts.slice(1).join(" ");
78249
- const paramMatches = paramString.matchAll(/(\w+)="([^"]+)"/g);
78250
- for (const match of paramMatches) {
78251
- const [, key, value] = match;
78252
- switch (key) {
78253
- case "realm":
78254
- result.realm = value;
78255
- break;
78256
- case "scope":
78257
- result.scope = value;
78258
- break;
78259
- case "error":
78260
- result.error = value;
78261
- break;
78262
- case "error_description":
78263
- result.error_description = value;
78264
- break;
78265
- case "error_uri":
78266
- result.error_uri = value;
78267
- break;
78268
- case "resource_metadata_url":
78269
- result.resource_metadata_url = value;
78270
- break;
78271
- }
78400
+ const bearer = /(?:^|,)\s*Bearer(?:\s+|$)/i.exec(headerValue2);
78401
+ const result = { scheme: bearer ? "Bearer" : headerValue2.split(/\s+/)[0] };
78402
+ if (!bearer) return result;
78403
+ const parameters = headerValue2.slice(bearer.index + bearer[0].length).split(/,\s*(?=[A-Za-z][\w-]*\s+(?![=]))/)[0];
78404
+ const known = /* @__PURE__ */ new Set([
78405
+ "realm",
78406
+ "scope",
78407
+ "error",
78408
+ "error_description",
78409
+ "error_uri",
78410
+ "resource_metadata"
78411
+ ]);
78412
+ for (const match of parameters.matchAll(/([\w-]+)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^,\s]+))/g)) {
78413
+ const key = match[1].toLowerCase();
78414
+ if (known.has(key))
78415
+ result[key] = (match[2] ?? match[3]).replace(
78416
+ /\\(.)/g,
78417
+ "$1"
78418
+ );
78272
78419
  }
78273
78420
  return result;
78274
78421
  }
78275
78422
  async function registerDynamicClient(registrationEndpoint, registrationRequest) {
78276
78423
  try {
78277
78424
  logger.oauth(`Attempting dynamic client registration: ${registrationEndpoint}`);
78278
- const customHeaders = getCustomHeaders();
78279
- const response = await proxyFetch(registrationEndpoint, {
78425
+ const response = await oauthFetch(registrationEndpoint, {
78280
78426
  method: "POST",
78281
78427
  headers: {
78282
- "Content-Type": "application/json",
78283
- ...customHeaders
78428
+ "Content-Type": "application/json"
78284
78429
  },
78285
78430
  body: JSON.stringify(registrationRequest)
78286
78431
  });
@@ -78292,12 +78437,26 @@ async function registerDynamicClient(registrationEndpoint, registrationRequest)
78292
78437
  );
78293
78438
  }
78294
78439
  const registrationResponse = await response.json();
78295
- if (!registrationResponse.client_id) {
78440
+ if (typeof registrationResponse.client_id !== "string" || !registrationResponse.client_id) {
78296
78441
  throw new OAuthError2(
78297
78442
  "Dynamic client registration response missing client_id",
78298
78443
  "INVALID_REGISTRATION_RESPONSE"
78299
78444
  );
78300
78445
  }
78446
+ if (registrationResponse.token_endpoint_auth_method && registrationResponse.token_endpoint_auth_method !== registrationRequest.token_endpoint_auth_method) {
78447
+ throw new OAuthError2(
78448
+ "Authorization server changed the requested client authentication method",
78449
+ "INVALID_REGISTRATION_RESPONSE"
78450
+ );
78451
+ }
78452
+ if (registrationResponse.redirect_uris && (!Array.isArray(registrationResponse.redirect_uris) || !registrationRequest.redirect_uris.every(
78453
+ (uri) => registrationResponse.redirect_uris.includes(uri)
78454
+ ))) {
78455
+ throw new OAuthError2(
78456
+ "Authorization server changed the registered callback URI",
78457
+ "INVALID_REGISTRATION_RESPONSE"
78458
+ );
78459
+ }
78301
78460
  logger.oauth("Dynamic client registration successful");
78302
78461
  logger.debug("Client ID obtained", "OAUTH", {
78303
78462
  client_id: registrationResponse.client_id,
@@ -78327,12 +78486,10 @@ async function exchangeAuthorizationCode(tokenEndpoint, code, redirectUri, clien
78327
78486
  if (resource) {
78328
78487
  params.set("resource", resource);
78329
78488
  }
78330
- const customHeaders = getCustomHeaders();
78331
- const response = await proxyFetch(tokenEndpoint, {
78489
+ const response = await oauthFetch(tokenEndpoint, {
78332
78490
  method: "POST",
78333
78491
  headers: {
78334
- "Content-Type": "application/x-www-form-urlencoded",
78335
- ...customHeaders
78492
+ "Content-Type": "application/x-www-form-urlencoded"
78336
78493
  },
78337
78494
  body: params.toString()
78338
78495
  });
@@ -78343,7 +78500,9 @@ async function exchangeAuthorizationCode(tokenEndpoint, code, redirectUri, clien
78343
78500
  "TOKEN_EXCHANGE_FAILED"
78344
78501
  );
78345
78502
  }
78346
- const tokenResponse = await response.json();
78503
+ const tokenResponse = OAuthTokensSchema.parse(await response.json());
78504
+ if (tokenResponse.token_type.toLowerCase() !== "bearer")
78505
+ throw new OAuthError2("Unsupported token type", "INVALID_TOKEN_RESPONSE");
78347
78506
  if (!tokenResponse.access_token) {
78348
78507
  throw new OAuthError2("Token response missing access_token", "INVALID_TOKEN_RESPONSE");
78349
78508
  }
@@ -78372,15 +78531,18 @@ function buildAuthorizationUrl(authorizationEndpoint, clientId, redirectUri, sco
78372
78531
  // OAuth 2.1 uses authorization code flow
78373
78532
  client_id: clientId,
78374
78533
  redirect_uri: redirectUri,
78375
- scope: scopes.join(" "),
78376
78534
  state,
78377
78535
  code_challenge: codeChallenge,
78378
78536
  code_challenge_method: "S256"
78379
78537
  });
78538
+ if (scopes.length) params.set("scope", scopes.join(" "));
78380
78539
  if (resource) {
78381
78540
  params.set("resource", resource);
78382
78541
  }
78383
- return `${authorizationEndpoint}?${params.toString()}`;
78542
+ validateOAuthUrl(authorizationEndpoint);
78543
+ const url2 = new URL(authorizationEndpoint);
78544
+ params.forEach((value, key) => url2.searchParams.set(key, value));
78545
+ return url2.toString();
78384
78546
  }
78385
78547
  function generateSecureState() {
78386
78548
  return crypto4.randomBytes(32).toString("base64url");
@@ -78461,110 +78623,26 @@ var AUTHORIZATION_CODE_HTML = `
78461
78623
  </div>
78462
78624
 
78463
78625
  <script>
78464
- function displayStatus(message, type = 'loading') {
78465
- const statusEl = document.getElementById('status');
78466
- const spinnerEl = document.getElementById('spinner');
78467
-
78468
- statusEl.textContent = message;
78469
- statusEl.className = type;
78470
-
78471
- if (type !== 'loading') {
78472
- spinnerEl.style.display = 'none';
78473
- }
78474
- }
78475
-
78476
- function displayDetails(message) {
78477
- document.getElementById('details').textContent = message;
78478
- }
78479
-
78480
- // Handle both OAuth 2.1 authorization code flow and implicit flow
78481
- const urlParams = new URLSearchParams(window.location.search);
78482
- const hashParams = new URLSearchParams(window.location.hash.substring(1));
78483
-
78484
- // Check for authorization code first (OAuth 2.1 flow)
78485
- if (urlParams.has('code')) {
78486
- const authData = {
78487
- code: urlParams.get('code'),
78488
- state: urlParams.get('state'),
78489
- // Additional parameters for validation
78490
- iss: urlParams.get('iss'),
78491
- };
78492
-
78493
- // Send authorization code to server for token exchange
78494
- fetch('/oauth/callback', {
78495
- method: 'POST',
78496
- headers: {
78497
- 'Content-Type': 'application/json'
78498
- },
78499
- body: JSON.stringify(authData)
78500
- })
78501
- .then(response => {
78502
- if (response.ok) {
78503
- displayStatus('\u2705 Authorization successful!', 'success');
78504
- displayDetails('You can safely close this window.');
78505
- } else {
78506
- return response.json().then(err => {
78507
- throw new Error(err.error || 'Failed to exchange authorization code');
78508
- });
78509
- }
78510
- })
78511
- .catch(error => {
78512
- displayStatus('\u274C Error completing authorization', 'error');
78513
- displayDetails(error.message + '. Please try again.');
78514
- });
78515
- }
78516
- // Check for access token in URL fragment (implicit flow)
78517
- else if (hashParams.has('access_token')) {
78518
- const tokens = {
78519
- access_token: hashParams.get('access_token'),
78520
- token_type: hashParams.get('token_type') || 'Bearer',
78521
- expires_in: hashParams.get('expires_in') ? parseInt(hashParams.get('expires_in')) : 3600,
78522
- scope: hashParams.get('scope'),
78523
- state: hashParams.get('state'),
78524
- obtained_at: Date.now()
78525
- };
78526
-
78527
- // Send tokens to server for storage
78528
- fetch('/oauth/tokens', {
78529
- method: 'POST',
78530
- headers: {
78531
- 'Content-Type': 'application/json'
78532
- },
78533
- body: JSON.stringify(tokens)
78534
- })
78535
- .then(response => {
78536
- if (response.ok) {
78537
- displayStatus('\u2705 Authorization successful!', 'success');
78538
- displayDetails('You can safely close this window.');
78539
- } else {
78540
- return response.json().then(err => {
78541
- throw new Error(err.error || 'Failed to store access token');
78542
- });
78543
- }
78544
- })
78545
- .catch(error => {
78546
- displayStatus('\u274C Error completing authorization', 'error');
78547
- displayDetails(error.message + '. Please try again.');
78548
- });
78549
- }
78550
- // Check for errors in query parameters
78551
- else if (urlParams.has('error')) {
78552
- const error = urlParams.get('error');
78553
- const errorDescription = urlParams.get('error_description');
78554
- displayStatus('\u274C Authorization failed', 'error');
78555
- displayDetails(\`Error: \${error}\${errorDescription ? ' - ' + errorDescription : ''}\`);
78556
- }
78557
- // Check for errors in URL fragment (implicit flow errors)
78558
- else if (hashParams.has('error')) {
78559
- const error = hashParams.get('error');
78560
- const errorDescription = hashParams.get('error_description');
78561
- displayStatus('\u274C Authorization failed', 'error');
78562
- displayDetails(\`Error: \${error}\${errorDescription ? ' - ' + errorDescription : ''}\`);
78563
- }
78564
- // No authorization code or access token found
78565
- else {
78566
- displayStatus('\u274C No authorization code or access token received', 'error');
78567
- displayDetails('Please try the authorization process again.');
78626
+ const query = new URLSearchParams(window.location.search);
78627
+ const fragment = new URLSearchParams(window.location.hash.substring(1));
78628
+ const status = document.getElementById('status');
78629
+ const details = document.getElementById('details');
78630
+ const data = Object.fromEntries(query.has('code') || query.has('error') ? query : fragment);
78631
+ const endpoint = data.access_token ? '/oauth/tokens' : '/oauth/callback';
78632
+ if (data.code || data.error || data.access_token) {
78633
+ fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) })
78634
+ .then(async response => {
78635
+ const result = await response.json();
78636
+ if (!response.ok) throw new Error(result.error || 'Authorization response was rejected');
78637
+ status.textContent = data.access_token ? 'Authorization successful' : 'Authorization code received';
78638
+ status.className = 'success';
78639
+ details.textContent = data.access_token ? 'You can close this window.' : 'Return to your app to check the connection. Token exchange may still fail.';
78640
+ })
78641
+ .catch(error => { status.textContent = 'Authorization failed'; status.className = 'error'; details.textContent = error.message; })
78642
+ .finally(() => { document.getElementById('spinner').style.display = 'none'; });
78643
+ } else {
78644
+ status.textContent = 'No authorization response received';
78645
+ document.getElementById('spinner').style.display = 'none';
78568
78646
  }
78569
78647
  </script>
78570
78648
  </body>
@@ -78579,6 +78657,17 @@ var OAuthCallbackServer = class {
78579
78657
  this.setupRoutes();
78580
78658
  }
78581
78659
  setupRoutes() {
78660
+ this.app.use((req, res, next) => {
78661
+ const host = this.options.host === "::1" ? "[::1]" : this.options.host;
78662
+ const origin = req.headers.origin;
78663
+ const port = this.server?.address();
78664
+ const expectedOrigin = `http://${host}:${port && typeof port !== "string" ? port.port : this.options.port}`;
78665
+ if (req.hostname !== host || origin && origin !== expectedOrigin) {
78666
+ res.status(403).json({ error: "Untrusted callback origin" });
78667
+ return;
78668
+ }
78669
+ next();
78670
+ });
78582
78671
  this.app.use(import_express.default.json());
78583
78672
  this.app.get("/oauth/callback", (req, res) => {
78584
78673
  logger.oauth("OAuth 2.1 authorization callback page requested");
@@ -78586,46 +78675,47 @@ var OAuthCallbackServer = class {
78586
78675
  });
78587
78676
  this.app.post("/oauth/callback", async (req, res) => {
78588
78677
  try {
78589
- const { code, state, iss } = req.body;
78590
- if (!code) {
78591
- throw new OAuthError2("No authorization code received");
78592
- }
78593
- logger.oauth("OAuth 2.1 authorization code received");
78594
- logger.debug("Authorization code details", "OAUTH", {
78595
- codeLength: code.length,
78596
- state: state || "none",
78597
- issuer: iss || "none"
78598
- });
78678
+ const { code, state, iss, error: error51 } = req.body;
78679
+ this.validateResponse(state, iss);
78680
+ if (error51) throw new OAuthError2("Authorization denied or failed", "AUTH_DENIED");
78681
+ if (this.options.flow === "implicit")
78682
+ throw new OAuthError2("Authorization-code callback is disabled", "WRONG_FLOW");
78683
+ if (typeof code !== "string" || !code)
78684
+ throw new OAuthError2("No authorization code received", "INVALID_CODE");
78599
78685
  this.events.emit("oauth-code-received", { code, state, iss });
78600
- res.json({ success: true, message: "Authorization code received successfully" });
78686
+ res.json({
78687
+ success: true,
78688
+ message: "Authorization code received; check connection status in the app"
78689
+ });
78601
78690
  } catch (error51) {
78602
- logger.error("Error processing authorization code", "OAUTH", error51);
78603
- const errorMessage = error51 instanceof Error ? error51.message : "Unknown error";
78604
- this.events.emit("oauth-error", new OAuthError2(errorMessage));
78605
- res.status(400).json({ error: errorMessage });
78691
+ const failure = error51 instanceof OAuthError2 ? error51 : new OAuthError2("Invalid authorization response");
78692
+ if (failure.code !== "INVALID_STATE") this.events.emit("oauth-error", failure);
78693
+ res.status(400).json({ error: failure.message });
78606
78694
  }
78607
78695
  });
78608
78696
  this.app.post("/oauth/tokens", async (req, res) => {
78697
+ if (this.options.flow !== "implicit") {
78698
+ res.status(404).json({ error: "Implicit token callback is disabled" });
78699
+ return;
78700
+ }
78609
78701
  try {
78610
- const tokens = req.body;
78611
- if (!tokens.access_token) {
78612
- throw new OAuthError2("No access token received");
78613
- }
78614
- logger.oauth("Legacy OAuth tokens received (implicit flow)");
78615
- logger.debug("Token details", "OAUTH", {
78616
- tokenType: tokens.token_type,
78617
- expiresIn: tokens.expires_in,
78618
- scope: tokens.scope,
78619
- tokenLength: tokens.access_token.length
78620
- });
78702
+ this.validateResponse(req.body.state, req.body.iss);
78703
+ if (typeof req.body.access_token !== "string" || !req.body.access_token || req.body.token_type && req.body.token_type.toLowerCase() !== "bearer")
78704
+ throw new OAuthError2("Invalid implicit token response");
78705
+ const tokens = {
78706
+ access_token: req.body.access_token,
78707
+ token_type: "Bearer",
78708
+ expires_in: req.body.expires_in === void 0 ? void 0 : Number(req.body.expires_in),
78709
+ scope: req.body.scope,
78710
+ obtained_at: Date.now()
78711
+ };
78621
78712
  await writeTokens(this.options.serverUrlHash, tokens);
78622
78713
  this.events.emit("oauth-success", tokens);
78623
- res.json({ success: true, message: "Tokens saved successfully" });
78714
+ res.json({ success: true });
78624
78715
  } catch (error51) {
78625
- logger.error("Error processing OAuth tokens", "OAUTH", error51);
78626
- const errorMessage = error51 instanceof Error ? error51.message : "Unknown error";
78627
- this.events.emit("oauth-error", new OAuthError2(errorMessage));
78628
- res.status(400).json({ error: errorMessage });
78716
+ const failure = error51 instanceof OAuthError2 ? error51 : new OAuthError2("Invalid implicit response");
78717
+ if (failure.code !== "INVALID_STATE") this.events.emit("oauth-error", failure);
78718
+ res.status(400).json({ error: failure.message });
78629
78719
  }
78630
78720
  });
78631
78721
  this.app.get("/oauth/health", (req, res) => {
@@ -78642,21 +78732,33 @@ var OAuthCallbackServer = class {
78642
78732
  }
78643
78733
  );
78644
78734
  }
78735
+ validateResponse(state, issuer) {
78736
+ if (!this.options.state || state !== this.options.state)
78737
+ throw new OAuthError2("Invalid callback state", "INVALID_STATE");
78738
+ if ((issuer !== void 0 || this.options.requireIssuer) && issuer !== this.options.issuer)
78739
+ throw new OAuthError2("Invalid callback issuer", "INVALID_ISSUER");
78740
+ }
78645
78741
  async start() {
78646
78742
  return new Promise((resolve, reject) => {
78647
78743
  try {
78648
- this.server = this.app.listen(this.options.port, this.options.host, () => {
78744
+ this.server = this.app.listen(this.options.port, this.options.host, (error51) => {
78745
+ clearTimeout(this.startupTimer);
78746
+ if (error51) {
78747
+ reject(error51);
78748
+ return;
78749
+ }
78649
78750
  logger.oauth(
78650
78751
  `OAuth callback server listening on http://${this.options.host}:${this.options.port}`
78651
78752
  );
78652
78753
  resolve();
78653
78754
  });
78654
78755
  this.server.on("error", (error51) => {
78756
+ clearTimeout(this.startupTimer);
78655
78757
  logger.error("OAuth callback server error", "OAUTH", error51);
78656
78758
  reject(error51);
78657
78759
  });
78658
78760
  if (this.options.timeout) {
78659
- setTimeout(() => {
78761
+ this.startupTimer = setTimeout(() => {
78660
78762
  if (!this.server?.listening) {
78661
78763
  reject(new OAuthError2("OAuth callback server startup timeout", "TIMEOUT"));
78662
78764
  }
@@ -78668,6 +78770,7 @@ var OAuthCallbackServer = class {
78668
78770
  });
78669
78771
  }
78670
78772
  async stop() {
78773
+ clearTimeout(this.startupTimer);
78671
78774
  if (!this.server) return;
78672
78775
  const server = this.server;
78673
78776
  this.server = null;
@@ -78699,7 +78802,9 @@ var MCPOAuthProvider = class {
78699
78802
  this.authServerMetadata = null;
78700
78803
  this.resourceMetadata = null;
78701
78804
  const serverUrl = options.serverUrl || CONFIG.WP_API_URL;
78702
- const defaultScopes = getDefaultOAuthScopes();
78805
+ const defaultScopes = CONFIG.OAUTH_SCOPES ? getDefaultOAuthScopes() : [];
78806
+ this.explicitScopes = options.scopes ?? defaultScopes;
78807
+ this.configuredClientId = options.clientId || CONFIG.WP_OAUTH_CLIENT_ID || void 0;
78703
78808
  this.config = {
78704
78809
  serverUrl,
78705
78810
  resource: generateCanonicalResourceURI(serverUrl),
@@ -78758,187 +78863,217 @@ var MCPOAuthProvider = class {
78758
78863
  /**
78759
78864
  * Discover OAuth 2.1 server endpoints using MCP-compliant discovery
78760
78865
  */
78761
- async discoverOAuthEndpoints() {
78762
- try {
78763
- logger.oauth("Starting MCP-compliant OAuth endpoint discovery");
78764
- try {
78765
- this.resourceMetadata = await discoverProtectedResourceMetadata(this.config.resource);
78766
- logger.oauth("Protected resource metadata discovered");
78767
- } catch (error51) {
78768
- logger.warn(
78769
- "Protected resource metadata discovery failed, trying 401 response method",
78770
- "OAUTH"
78771
- );
78772
- await this.discoverVia401Response();
78773
- }
78774
- const authServerUrl = this.getAuthorizationServerUrl();
78775
- if (authServerUrl) {
78776
- this.authServerMetadata = await discoverAuthorizationServerMetadata2(authServerUrl);
78777
- logger.oauth("Authorization server metadata discovered");
78778
- this.config.authorizationEndpoint = this.authServerMetadata.authorization_endpoint;
78779
- this.config.tokenEndpoint = this.authServerMetadata.token_endpoint;
78780
- this.config.registrationEndpoint = this.authServerMetadata.registration_endpoint;
78781
- } else {
78782
- throw new OAuthError2("Could not determine authorization server URL", "DISCOVERY_FAILED");
78783
- }
78784
- } catch (error51) {
78785
- logger.error("OAuth endpoint discovery failed", "OAUTH", error51);
78786
- throw error51;
78787
- }
78788
- }
78789
- /**
78790
- * Discover endpoints via 401 Unauthorized response (RFC 9728 Section 5.1)
78791
- */
78792
- async discoverVia401Response() {
78793
- try {
78794
- logger.oauth("Attempting discovery via 401 response method");
78795
- const customHeaders = getCustomHeaders();
78796
- const response = await proxyFetch(this.config.serverUrl, {
78797
- headers: {
78798
- Accept: "application/json",
78799
- ...customHeaders
78800
- }
78801
- });
78802
- if (response.status === 401) {
78803
- const wwwAuthHeader = response.headers.get("WWW-Authenticate");
78804
- if (wwwAuthHeader) {
78805
- const authInfo = parseWWWAuthenticateHeader(wwwAuthHeader);
78806
- logger.oauth("WWW-Authenticate header parsed successfully");
78807
- if (authInfo.resource_metadata_url) {
78808
- const metadataResponse = await proxyFetch(authInfo.resource_metadata_url, {
78809
- headers: {
78810
- Accept: "application/json",
78811
- ...customHeaders
78812
- }
78813
- });
78814
- if (metadataResponse.ok) {
78815
- this.resourceMetadata = await metadataResponse.json();
78816
- logger.oauth("Protected resource metadata obtained via 401 response");
78817
- }
78818
- }
78819
- }
78820
- }
78821
- } catch (error51) {
78822
- logger.warn("401 response discovery method failed", "OAUTH", error51);
78823
- }
78866
+ async discoverOAuthEndpoints(challenge) {
78867
+ this.resourceMetadata = await discoverProtectedResourceMetadata(
78868
+ this.config.serverUrl,
78869
+ challenge?.resource_metadata
78870
+ );
78871
+ this.authServerMetadata = await discoverAuthorizationServerMetadata2(
78872
+ this.resourceMetadata.authorization_servers[0]
78873
+ );
78874
+ this.config.resource = this.resourceMetadata.resource;
78875
+ this.config.authorizationEndpoint = this.authServerMetadata.authorization_endpoint;
78876
+ this.config.tokenEndpoint = this.authServerMetadata.token_endpoint;
78877
+ this.config.registrationEndpoint = this.authServerMetadata.registration_endpoint;
78878
+ this.config.clientId = this.configuredClientId;
78879
+ const challenged = challenge?.scope?.split(/\s+/).filter(Boolean);
78880
+ this.config.scopes = [
78881
+ ...new Set(
78882
+ challenged ?? (this.explicitScopes.length ? this.explicitScopes : this.resourceMetadata.scopes_supported ?? [])
78883
+ )
78884
+ ];
78824
78885
  }
78825
- /**
78826
- * Get authorization server URL from discovered metadata
78827
- */
78828
78886
  getAuthorizationServerUrl() {
78829
- if (this.resourceMetadata?.authorization_servers?.length) {
78830
- return this.resourceMetadata.authorization_servers[0];
78831
- }
78832
- try {
78833
- const url2 = new URL(this.config.serverUrl);
78834
- return url2.origin;
78835
- } catch {
78836
- return null;
78837
- }
78887
+ return this.authServerMetadata?.issuer ?? this.resourceMetadata?.authorization_servers[0] ?? new URL(this.config.serverUrl).origin;
78838
78888
  }
78839
78889
  /**
78840
78890
  * Perform dynamic client registration if needed and supported
78841
78891
  */
78842
78892
  async ensureClientRegistration() {
78893
+ const issuer = this.getAuthorizationServerUrl();
78894
+ const stored = await readClientInfo(this.serverUrlHash);
78843
78895
  if (this.config.clientId) {
78844
- logger.oauth("Using provided client ID, skipping dynamic registration");
78896
+ if (stored?.issuer && stored.issuer !== issuer && stored.client_id === this.config.clientId) {
78897
+ throw new OAuthError2(
78898
+ "Configured client belongs to another authorization server; update its registration",
78899
+ "CLIENT_ISSUER_MISMATCH"
78900
+ );
78901
+ }
78902
+ await writeClientInfo(this.serverUrlHash, { client_id: this.config.clientId, issuer });
78845
78903
  return;
78846
78904
  }
78847
- const storedClientInfo = await readClientInfo(this.serverUrlHash);
78848
- if (typeof storedClientInfo?.client_id === "string" && storedClientInfo.client_id.length > 0) {
78849
- this.config.clientId = storedClientInfo.client_id;
78850
- logger.oauth("Reusing persisted client registration, skipping dynamic registration");
78905
+ if (stored?.issuer === issuer && typeof stored.client_id === "string" && stored.client_id.length > 0) {
78906
+ this.config.clientId = stored.client_id;
78851
78907
  return;
78852
78908
  }
78853
- if (!CONFIG.OAUTH_DYNAMIC_REGISTRATION) {
78909
+ if (!CONFIG.OAUTH_DYNAMIC_REGISTRATION)
78854
78910
  throw new OAuthError2(
78855
- "No client ID provided and dynamic registration is disabled",
78911
+ "No issuer-bound client registration; configure a client ID or enable registration",
78856
78912
  "NO_CLIENT_ID"
78857
78913
  );
78858
- }
78859
- if (!this.authServerMetadata?.registration_endpoint) {
78914
+ if (!this.authServerMetadata?.registration_endpoint)
78860
78915
  throw new OAuthError2(
78861
- "Dynamic client registration not supported by authorization server",
78916
+ "Authorization server does not support dynamic registration; configure its client ID",
78862
78917
  "NO_DYNAMIC_REGISTRATION"
78863
78918
  );
78864
- }
78865
- try {
78866
- logger.oauth("Performing dynamic client registration");
78867
- const registrationRequest = {
78868
- redirect_uris: [this.config.redirectUri],
78869
- token_endpoint_auth_method: "none",
78870
- // Public client
78871
- grant_types: ["authorization_code"],
78872
- response_types: ["code"],
78873
- client_name: "WordPress MCP Remote",
78874
- scope: this.config.scopes.join(" ")
78875
- };
78876
- const registrationResponse = await registerDynamicClient(
78877
- this.authServerMetadata.registration_endpoint,
78878
- registrationRequest
78879
- );
78880
- const clientInfo = {
78881
- client_id: registrationResponse.client_id,
78882
- client_secret: registrationResponse.client_secret
78883
- };
78884
- await writeClientInfo(this.serverUrlHash, clientInfo);
78885
- this.config.clientId = registrationResponse.client_id;
78886
- logger.oauth("Dynamic client registration completed successfully");
78887
- } catch (error51) {
78888
- logger.error("Dynamic client registration failed", "OAUTH", error51);
78889
- throw error51;
78890
- }
78919
+ const request = {
78920
+ redirect_uris: [this.config.redirectUri],
78921
+ application_type: "native",
78922
+ token_endpoint_auth_method: "none",
78923
+ grant_types: ["authorization_code", "refresh_token"],
78924
+ response_types: ["code"],
78925
+ client_name: "WordPress MCP Remote",
78926
+ ...this.config.scopes.length ? { scope: this.config.scopes.join(" ") } : {}
78927
+ };
78928
+ const registered = await registerDynamicClient(
78929
+ this.authServerMetadata.registration_endpoint,
78930
+ request
78931
+ );
78932
+ await writeClientInfo(this.serverUrlHash, {
78933
+ ...request,
78934
+ client_id: registered.client_id,
78935
+ issuer
78936
+ });
78937
+ this.config.clientId = registered.client_id;
78891
78938
  }
78892
78939
  /**
78893
78940
  * Initiate MCP-compliant OAuth 2.1 authorization flow
78894
78941
  */
78895
- async authorize() {
78896
- if (this.authPromise) {
78897
- logger.oauth("Authorization already in progress, waiting...");
78898
- await this.authPromise;
78899
- return;
78900
- }
78901
- const existingTokens = await this.tokens();
78902
- if (existingTokens) {
78903
- logger.oauth("Already have valid tokens, skipping authorization");
78904
- return;
78942
+ satisfies(tokens, request) {
78943
+ return !!tokens && tokens.access_token !== request.rejectedToken && (!request.scopes?.length || request.scopes.every((scope) => tokens.scope?.split(" ").includes(scope)));
78944
+ }
78945
+ async authorize(request = {}) {
78946
+ if (this.authPromise) await this.authPromise;
78947
+ if (!this.authPromise) this.authPromise = this.authorizeWithLock(request);
78948
+ const pending = this.authPromise;
78949
+ try {
78950
+ await pending;
78951
+ } finally {
78952
+ if (this.authPromise === pending) this.authPromise = null;
78905
78953
  }
78906
- logger.oauth("Starting MCP-compliant OAuth 2.1 authorization flow");
78907
- this.authPromise = this.performAuthorization();
78954
+ }
78955
+ async authorizeWithLock(request) {
78956
+ const valid = await this.tokens();
78957
+ if (this.satisfies(valid, request)) return valid;
78958
+ await ensureConfigDir();
78959
+ const file2 = getConfigFilePath(this.serverUrlHash, "oauth");
78960
+ let release;
78908
78961
  try {
78909
- await this.authPromise;
78910
- logger.oauth("OAuth 2.1 authorization completed successfully");
78962
+ release = await lockfile.lock(file2, { realpath: false });
78911
78963
  } catch (error51) {
78912
- logger.error("OAuth 2.1 authorization failed", "OAUTH", error51);
78913
- throw error51;
78964
+ if (error51.code !== "ELOCKED") throw error51;
78965
+ const deadline = Date.now() + this.config.timeout;
78966
+ while (Date.now() < deadline) {
78967
+ const tokens = await this.tokens();
78968
+ if (this.satisfies(tokens, request)) return tokens;
78969
+ if (!await lockfile.check(file2, { realpath: false })) {
78970
+ const completed = await this.tokens();
78971
+ if (this.satisfies(completed, request)) return completed;
78972
+ throw new OAuthError2("Authentication in another process did not complete", "AUTH_FAILED");
78973
+ }
78974
+ await delay(Math.min(100, Math.max(0, deadline - Date.now())));
78975
+ }
78976
+ throw new OAuthError2("Timeout waiting for authentication in another process", "LOCK_TIMEOUT");
78977
+ }
78978
+ try {
78979
+ const completed = await this.tokens();
78980
+ if (this.satisfies(completed, request)) return completed;
78981
+ await this.discoverOAuthEndpoints(request.challenge);
78982
+ const issuer = this.getAuthorizationServerUrl();
78983
+ const stored = await readTokens(this.serverUrlHash);
78984
+ const client = await readClientInfo(this.serverUrlHash);
78985
+ if (this.configuredClientId && client?.issuer && client.issuer !== issuer && client.client_id === this.configuredClientId) {
78986
+ throw new OAuthError2(
78987
+ "Configured client belongs to another authorization server",
78988
+ "CLIENT_ISSUER_MISMATCH"
78989
+ );
78990
+ }
78991
+ this.config.scopes = [
78992
+ .../* @__PURE__ */ new Set([
78993
+ ...stored?.issuer === issuer ? stored.scope?.split(" ").filter(Boolean) ?? [] : [],
78994
+ ...this.config.scopes,
78995
+ ...request.scopes ?? []
78996
+ ])
78997
+ ];
78998
+ const needsScope = !!request.scopes?.some(
78999
+ (scope) => !stored?.scope?.split(" ").includes(scope)
79000
+ );
79001
+ const clientId = this.configuredClientId || client?.client_id;
79002
+ if (stored?.issuer === issuer && client?.issuer === issuer && stored.refresh_token && clientId && (!this.configuredClientId || this.configuredClientId === client.client_id) && !needsScope) {
79003
+ try {
79004
+ const renewed = await refreshAuthorization(issuer, {
79005
+ metadata: { ...this.authServerMetadata },
79006
+ clientInformation: { client_id: clientId },
79007
+ refreshToken: stored.refresh_token,
79008
+ resource: this.resourceIndicator(),
79009
+ fetchFn: (url2, init) => oauthFetch(String(url2), init)
79010
+ });
79011
+ if (renewed.token_type.toLowerCase() !== "bearer")
79012
+ throw new OAuthError2("Unsupported token type", "INVALID_TOKEN_RESPONSE");
79013
+ const tokens = {
79014
+ ...renewed,
79015
+ issuer,
79016
+ scope: renewed.scope ?? stored.scope,
79017
+ obtained_at: Date.now()
79018
+ };
79019
+ await writeTokens(this.serverUrlHash, tokens);
79020
+ return tokens;
79021
+ } catch (error51) {
79022
+ if (!(error51 instanceof InvalidGrantError)) throw error51;
79023
+ await deleteConfigFile(this.serverUrlHash, "tokens.json");
79024
+ }
79025
+ }
79026
+ return await this.performAuthorization();
78914
79027
  } finally {
78915
- this.authPromise = null;
79028
+ await release();
78916
79029
  }
78917
79030
  }
79031
+ resourceIndicator() {
79032
+ if (!CONFIG.OAUTH_RESOURCE_INDICATOR)
79033
+ throw new OAuthError2(
79034
+ "MCP OAuth requires resource indicators; remove OAUTH_RESOURCE_INDICATOR=false",
79035
+ "RESOURCE_REQUIRED"
79036
+ );
79037
+ return this.config.resource;
79038
+ }
78918
79039
  /**
78919
79040
  * Perform the complete OAuth 2.1 authorization flow
78920
79041
  */
78921
79042
  async performAuthorization() {
78922
79043
  try {
78923
- await this.discoverOAuthEndpoints();
78924
- const callbackPort = this.config.callbackPort === 0 ? await getOAuthCallbackPort() : this.config.callbackPort;
79044
+ if (!this.authServerMetadata) await this.discoverOAuthEndpoints();
79045
+ if (!this.authServerMetadata?.code_challenge_methods_supported?.includes("S256"))
79046
+ throw new OAuthError2(
79047
+ "Authorization server must advertise PKCE S256 support",
79048
+ "PKCE_REQUIRED"
79049
+ );
79050
+ this.resourceIndicator();
79051
+ if (!["127.0.0.1", "localhost", "::1"].includes(this.config.host))
79052
+ throw new OAuthError2(
79053
+ "Native OAuth callback must bind to loopback",
79054
+ "INVALID_CALLBACK_HOST"
79055
+ );
79056
+ this.currentPKCE = generatePKCE();
79057
+ this.currentState = generateSecureState();
79058
+ const callbackPort = this.config.callbackPort === 0 ? await selectCallbackPort(this.config.serverUrl, void 0, false, this.config.host) : this.config.callbackPort;
78925
79059
  const callbackServer = setupWPOAuthCallbackServer(
78926
79060
  {
78927
79061
  port: callbackPort,
78928
79062
  host: this.config.host,
78929
79063
  serverUrlHash: this.serverUrlHash,
78930
- timeout: this.config.timeout
79064
+ timeout: this.config.timeout,
79065
+ state: this.currentState,
79066
+ issuer: this.authServerMetadata.issuer,
79067
+ requireIssuer: this.authServerMetadata.authorization_response_iss_parameter_supported
78931
79068
  },
78932
79069
  this.events
78933
79070
  );
78934
79071
  await callbackServer.start();
78935
79072
  logger.oauth("OAuth callback server started");
78936
79073
  try {
78937
- const actualRedirectUri = `http://${this.config.host}:${callbackPort}/oauth/callback`;
79074
+ const actualRedirectUri = `http://${this.config.host === "::1" ? "[::1]" : this.config.host}:${callbackPort}/oauth/callback`;
78938
79075
  this.config.redirectUri = actualRedirectUri;
78939
79076
  await this.ensureClientRegistration();
78940
- this.currentPKCE = generatePKCE();
78941
- this.currentState = generateSecureState();
78942
79077
  await writeTextFile(this.serverUrlHash, "pkce_verifier.txt", this.currentPKCE.codeVerifier);
78943
79078
  await writeTextFile(this.serverUrlHash, "oauth_state.txt", this.currentState);
78944
79079
  const authUrl = buildAuthorizationUrl(
@@ -78948,10 +79083,13 @@ var MCPOAuthProvider = class {
78948
79083
  this.config.scopes,
78949
79084
  this.currentState,
78950
79085
  this.currentPKCE.codeChallenge,
78951
- CONFIG.OAUTH_RESOURCE_INDICATOR ? this.config.resource : void 0
79086
+ this.resourceIndicator()
78952
79087
  );
78953
79088
  logger.oauth("Built OAuth 2.1 authorization URL");
78954
79089
  logger.debug("Authorization URL", "OAUTH", { url: authUrl });
79090
+ const authorizationCode = this.waitForAuthorizationCode();
79091
+ void authorizationCode.catch(() => {
79092
+ });
78955
79093
  try {
78956
79094
  await open_default(authUrl);
78957
79095
  logger.oauth("Browser opened successfully");
@@ -78962,13 +79100,20 @@ var MCPOAuthProvider = class {
78962
79100
  logger.info(`${authUrl}`);
78963
79101
  logger.info("===============================\n");
78964
79102
  }
78965
- const authCode = await this.waitForAuthorizationCode();
78966
- const tokens = await this.exchangeCodeForTokens(authCode, actualRedirectUri);
79103
+ const authCode = await authorizationCode;
79104
+ const response = await this.exchangeCodeForTokens(authCode, actualRedirectUri);
79105
+ const tokens = {
79106
+ ...response,
79107
+ issuer: this.getAuthorizationServerUrl(),
79108
+ scope: response.scope ?? this.config.scopes.join(" ")
79109
+ };
78967
79110
  await writeTokens(this.serverUrlHash, tokens);
78968
79111
  logger.oauth("OAuth 2.1 tokens stored successfully");
78969
79112
  return tokens;
78970
79113
  } finally {
78971
79114
  await callbackServer.stop();
79115
+ await deleteConfigFile(this.serverUrlHash, "pkce_verifier.txt");
79116
+ await deleteConfigFile(this.serverUrlHash, "oauth_state.txt");
78972
79117
  }
78973
79118
  } catch (error51) {
78974
79119
  logger.error("Error during OAuth 2.1 authorization flow", "OAUTH", error51);
@@ -78989,12 +79134,16 @@ var MCPOAuthProvider = class {
78989
79134
  this.events.removeAllListeners("oauth-error");
78990
79135
  clearTimeout(timeout);
78991
79136
  };
78992
- this.events.once("oauth-code-received", async ({ code, state }) => {
79137
+ this.events.once("oauth-code-received", async ({ code, state, iss }) => {
78993
79138
  cleanup();
78994
79139
  if (state !== this.currentState) {
78995
79140
  reject(new OAuthError2("State parameter mismatch", "INVALID_STATE"));
78996
79141
  return;
78997
79142
  }
79143
+ if ((iss !== void 0 || this.authServerMetadata?.authorization_response_iss_parameter_supported) && iss !== this.authServerMetadata?.issuer) {
79144
+ reject(new OAuthError2("Authorization response issuer mismatch", "INVALID_ISSUER"));
79145
+ return;
79146
+ }
78998
79147
  logger.oauth("Valid authorization code received");
78999
79148
  resolve(code);
79000
79149
  });
@@ -79020,7 +79169,7 @@ var MCPOAuthProvider = class {
79020
79169
  redirectUri,
79021
79170
  this.config.clientId,
79022
79171
  codeVerifier,
79023
- CONFIG.OAUTH_RESOURCE_INDICATOR ? this.config.resource : void 0
79172
+ this.resourceIndicator()
79024
79173
  );
79025
79174
  await deleteConfigFile(this.serverUrlHash, "pkce_verifier.txt");
79026
79175
  await deleteConfigFile(this.serverUrlHash, "oauth_state.txt");
@@ -79086,6 +79235,7 @@ import * as path5 from "node:path";
79086
79235
  import * as os3 from "node:os";
79087
79236
 
79088
79237
  // src/lib/persistent-oauth-client-provider.ts
79238
+ import { randomBytes } from "node:crypto";
79089
79239
  import { EventEmitter as EventEmitter2 } from "node:events";
79090
79240
  var PersistentWPOAuthClientProvider = class {
79091
79241
  constructor(options) {
@@ -79243,7 +79393,10 @@ var PersistentWPOAuthClientProvider = class {
79243
79393
  */
79244
79394
  async performAuthorization() {
79245
79395
  const callbackPort = this.options.callbackPort === 0 ? await getOAuthCallbackPort() : this.options.callbackPort;
79396
+ const state = this.generateState();
79246
79397
  const callbackServerOptions = {
79398
+ flow: "implicit",
79399
+ state,
79247
79400
  port: callbackPort,
79248
79401
  host: this.options.host,
79249
79402
  serverUrlHash: this.serverUrlHash,
@@ -79255,12 +79408,14 @@ var PersistentWPOAuthClientProvider = class {
79255
79408
  logger.oauth("Starting callback server...");
79256
79409
  await callbackServer.start();
79257
79410
  logger.oauth("Callback server started successfully");
79258
- const state = this.generateState();
79259
79411
  logger.debug(`Generated state parameter: ${state}`, "OAUTH");
79260
79412
  const authUrl = this.buildAuthorizationUrl(callbackServer.getCallbackUrl(), state);
79261
79413
  logger.oauth(`Built authorization URL: ${authUrl}`);
79262
79414
  logger.debug(`Callback URL: ${callbackServer.getCallbackUrl()}`, "OAUTH");
79263
79415
  logger.oauth("Attempting to open browser...");
79416
+ const authorization = this.waitForAuthorizationResult();
79417
+ void authorization.catch(() => {
79418
+ });
79264
79419
  try {
79265
79420
  await open_default(authUrl);
79266
79421
  logger.oauth("Browser opened successfully");
@@ -79272,7 +79427,7 @@ var PersistentWPOAuthClientProvider = class {
79272
79427
  logger.info("===============================\n");
79273
79428
  }
79274
79429
  logger.oauth("Waiting for authorization result...");
79275
- const tokens = await this.waitForAuthorizationResult();
79430
+ const tokens = await authorization;
79276
79431
  logger.oauth("Authorization result received");
79277
79432
  logger.oauth("Saving tokens to persistent storage...");
79278
79433
  await this.saveTokens(tokens);
@@ -79325,7 +79480,7 @@ var PersistentWPOAuthClientProvider = class {
79325
79480
  * Generate a secure state parameter
79326
79481
  */
79327
79482
  generateState() {
79328
- return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
79483
+ return randomBytes(32).toString("base64url");
79329
79484
  }
79330
79485
  /**
79331
79486
  * Wait for authorization result from callback server
@@ -79720,6 +79875,12 @@ function constructApiUrl(baseUrl, defaultEndpoint) {
79720
79875
  return new URL(`/?rest_route=${defaultEndpoint}`, cleanUrl).toString();
79721
79876
  }
79722
79877
  }
79878
+ function getMCPOAuthProvider() {
79879
+ return mcpOAuthProvider ??= new MCPOAuthProvider({
79880
+ serverUrl: getRequestUrl(),
79881
+ clientId: CONFIG.WP_OAUTH_CLIENT_ID
79882
+ });
79883
+ }
79723
79884
  async function getOAuthTokens() {
79724
79885
  try {
79725
79886
  if (!CONFIG.OAUTH_ENABLED) {
@@ -79727,7 +79888,7 @@ async function getOAuthTokens() {
79727
79888
  return null;
79728
79889
  }
79729
79890
  logger.auth("Attempting to get OAuth tokens for WordPress API (MCP-compliant)...");
79730
- const serverUrl = CONFIG.WP_API_URL;
79891
+ const serverUrl = getRequestUrl();
79731
79892
  const serverUrlHash = generateServerUrlHash(serverUrl);
79732
79893
  const existingTokens = await getValidTokens(serverUrlHash);
79733
79894
  if (existingTokens) {
@@ -79738,16 +79899,10 @@ async function getOAuthTokens() {
79738
79899
  logger.auth("Starting MCP-compliant OAuth 2.1 authentication flow");
79739
79900
  logger.auth("Your browser should open automatically for authentication");
79740
79901
  if (CONFIG.OAUTH_FLOW_TYPE === "authorization_code" && CONFIG.OAUTH_USE_PKCE) {
79741
- if (!mcpOAuthProvider) {
79742
- mcpOAuthProvider = new MCPOAuthProvider({
79743
- serverUrl,
79744
- clientId: CONFIG.WP_OAUTH_CLIENT_ID,
79745
- scopes: getDefaultOAuthScopes()
79746
- });
79747
- }
79748
79902
  logger.auth("Using MCP-compliant OAuth 2.1 authorization code flow with PKCE");
79749
- await mcpOAuthProvider.authorize();
79750
- const tokens = await mcpOAuthProvider.tokens();
79903
+ const provider = getMCPOAuthProvider();
79904
+ await provider.authorize();
79905
+ const tokens = await provider.tokens();
79751
79906
  if (tokens) {
79752
79907
  logger.auth("MCP OAuth 2.1 tokens obtained for WordPress API access");
79753
79908
  return tokens;
@@ -79788,6 +79943,22 @@ async function getOAuthTokens() {
79788
79943
  return null;
79789
79944
  }
79790
79945
  }
79946
+ async function recoverOAuthAuthorization(authorization, challenge, status = 401) {
79947
+ if (!CONFIG.OAUTH_ENABLED || CONFIG.JWT_TOKEN || CONFIG.OAUTH_FLOW_TYPE !== "authorization_code" || !CONFIG.OAUTH_USE_PKCE)
79948
+ return false;
79949
+ if (authorization && !/^Bearer /i.test(authorization)) return false;
79950
+ const scopes = status === 401 || challenge?.error === "insufficient_scope" ? challenge?.scope?.split(/\s+/).filter(Boolean) : void 0;
79951
+ if (status === 403 && !scopes?.length) return false;
79952
+ const current = await getValidTokens(generateServerUrlHash(getRequestUrl()));
79953
+ if (status === 403 && scopes?.every((scope) => current?.scope?.split(" ").includes(scope)))
79954
+ return current?.access_token !== authorization?.slice(7);
79955
+ await getMCPOAuthProvider().authorize({
79956
+ rejectedToken: status === 401 ? authorization?.slice(7) : void 0,
79957
+ challenge,
79958
+ scopes
79959
+ });
79960
+ return true;
79961
+ }
79791
79962
  function getCurrentApiUrl() {
79792
79963
  return process.env.WP_API_URL || CONFIG.WP_API_URL;
79793
79964
  }
@@ -79806,7 +79977,7 @@ function bindWordPressRequest(request) {
79806
79977
  if (shared[LIVE_REQUEST_KEY] === request) delete shared[LIVE_REQUEST_KEY];
79807
79978
  };
79808
79979
  }
79809
- async function getWordPressHeaders(requestData) {
79980
+ async function getWordPressHeaders(requestData, options = {}) {
79810
79981
  validateEnvironment();
79811
79982
  let authHeader = "";
79812
79983
  if (CONFIG.JWT_TOKEN) {
@@ -79815,7 +79986,7 @@ async function getWordPressHeaders(requestData) {
79815
79986
  logger.debug(`Token length: ${CONFIG.JWT_TOKEN.length}`, "AUTH");
79816
79987
  } else if (CONFIG.OAUTH_ENABLED) {
79817
79988
  logger.auth("OAuth is the primary authentication method - attempting to get tokens...");
79818
- const oauthTokens = await getOAuthTokens();
79989
+ const oauthTokens = options.allowUnauthenticated && !CONFIG.WP_API_USERNAME ? await getValidTokens(generateServerUrlHash(getRequestUrl())) : await getOAuthTokens();
79819
79990
  if (oauthTokens) {
79820
79991
  authHeader = `Bearer ${oauthTokens.access_token}`;
79821
79992
  logger.auth("Using OAuth token authentication for WordPress API");
@@ -79855,7 +80026,7 @@ async function getWordPressHeaders(requestData) {
79855
80026
  if (name.toLowerCase() === "mcp-protocol-version") delete customHeaders[name];
79856
80027
  }
79857
80028
  const hasCustomHeaders = Object.keys(customHeaders).length > 0;
79858
- if (!authHeader && !hasCustomHeaders) {
80029
+ if (!authHeader && !hasCustomHeaders && !(options.allowUnauthenticated && CONFIG.OAUTH_ENABLED)) {
79859
80030
  throw new AuthError(
79860
80031
  "No authentication method available. Please configure JWT_TOKEN, OAuth, Basic Auth (WP_API_USERNAME+WP_API_PASSWORD), or CUSTOM_HEADERS.",
79861
80032
  "NO_AUTH_METHOD"
@@ -79887,6 +80058,9 @@ function collectHeaderParams(schema, path6 = []) {
79887
80058
  return [...own, ...collectHeaderParams(child, [...path6, key])];
79888
80059
  });
79889
80060
  }
80061
+ function isBearerChallenge(challenge) {
80062
+ return challenge === null || parseWWWAuthenticateHeader(challenge).scheme === "Bearer";
80063
+ }
79890
80064
  function valueAtPath(value, path6) {
79891
80065
  return path6.reduce(
79892
80066
  (node, key) => node && typeof node === "object" ? node[key] : void 0,
@@ -79896,25 +80070,26 @@ function valueAtPath(value, path6) {
79896
80070
  function createWordPressTransport(onFailure) {
79897
80071
  const inflight = /* @__PURE__ */ new Map();
79898
80072
  const toolHeaders = /* @__PURE__ */ new Map();
79899
- const fetch3 = async (url2, init) => {
80073
+ const scopeAttempts = /* @__PURE__ */ new Set();
80074
+ const scopeCounts = /* @__PURE__ */ new Map();
80075
+ const send = async (url2, init, retried) => {
79900
80076
  const message = typeof init?.body === "string" ? JSON.parse(init.body) : void 0;
79901
80077
  const request = message && "method" in message && "id" in message ? message : void 0;
79902
80078
  const controller = new AbortController();
79903
- const abort = () => controller.abort();
79904
80079
  let cancelled = false;
79905
80080
  const cancel = () => {
79906
80081
  cancelled = true;
79907
- abort();
80082
+ controller.abort();
79908
80083
  };
79909
80084
  if (request) inflight.set(request.id, cancel);
79910
- init?.signal?.addEventListener("abort", abort, { once: true });
79911
- if (init?.signal?.aborted) abort();
80085
+ init?.signal?.addEventListener("abort", cancel, { once: true });
80086
+ if (init?.signal?.aborted) cancel();
79912
80087
  const timeoutMs = message && "method" in message && message.method === "initialize" ? CONFIG.WP_API_INIT_TIMEOUT : CONFIG.WP_API_TIMEOUT;
79913
80088
  let timedOut = false;
79914
80089
  let timer;
79915
80090
  const cleanup = () => {
79916
80091
  clearTimeout(timer);
79917
- init?.signal?.removeEventListener("abort", abort);
80092
+ init?.signal?.removeEventListener("abort", cancel);
79918
80093
  if (request && inflight.get(request.id) === cancel) inflight.delete(request.id);
79919
80094
  };
79920
80095
  const failure = (error51) => {
@@ -79930,7 +80105,9 @@ function createWordPressTransport(onFailure) {
79930
80105
  return apiError;
79931
80106
  };
79932
80107
  try {
79933
- const headers = new Headers(await getWordPressHeaders(message));
80108
+ const headers = new Headers(
80109
+ await getWordPressHeaders(message, { allowUnauthenticated: !retried })
80110
+ );
79934
80111
  if (init?.method !== "GET" && request?.method !== "subscriptions/listen") {
79935
80112
  timer = setTimeout(() => {
79936
80113
  timedOut = true;
@@ -79963,17 +80140,55 @@ function createWordPressTransport(onFailure) {
79963
80140
  headers,
79964
80141
  signal: controller.signal
79965
80142
  });
80143
+ const challenge = parseWWWAuthenticateHeader(response.headers.get("WWW-Authenticate") ?? "");
80144
+ const operationKey = JSON.stringify([
80145
+ request?.method,
80146
+ request?.params?.name ?? request?.params?.uri
80147
+ ]);
80148
+ const scopeKey = JSON.stringify([
80149
+ request?.method,
80150
+ request?.params?.name,
80151
+ challenge.scope?.split(/\s+/).sort()
80152
+ ]);
80153
+ const canRecover = !retried && (response.status === 401 && isBearerChallenge(response.headers.get("WWW-Authenticate")) || response.status === 403 && challenge.error === "insufficient_scope" && !!challenge.scope && !scopeAttempts.has(scopeKey) && (scopeCounts.get(operationKey) ?? 0) < 2);
80154
+ if (canRecover) {
80155
+ if (response.status === 403) {
80156
+ scopeAttempts.add(scopeKey);
80157
+ scopeCounts.set(operationKey, (scopeCounts.get(operationKey) ?? 0) + 1);
80158
+ }
80159
+ clearTimeout(timer);
80160
+ const recovered = await recoverOAuthAuthorization(
80161
+ headers.get("Authorization"),
80162
+ challenge,
80163
+ response.status
80164
+ );
80165
+ if (recovered) {
80166
+ const unread = response.body;
80167
+ if (typeof unread?.cancel === "function") await unread.cancel();
80168
+ else unread?.destroy?.();
80169
+ cleanup();
80170
+ if (cancelled) return new Response(null, { status: 202 });
80171
+ return send(url2, init, true);
80172
+ }
80173
+ timer = setTimeout(() => {
80174
+ timedOut = true;
80175
+ controller.abort();
80176
+ }, timeoutMs);
80177
+ }
79966
80178
  const body = response.body && typeof response.body.getReader !== "function" ? Readable.toWeb(response.body) : response.body;
79967
80179
  const reader = body?.getReader();
80180
+ let released = false;
79968
80181
  const stream = reader ? new ReadableStream({
79969
80182
  async pull(output) {
79970
80183
  try {
79971
80184
  const { value, done } = await reader.read();
80185
+ if (released) return;
79972
80186
  if (done) {
79973
80187
  cleanup();
79974
80188
  output.close();
79975
80189
  } else output.enqueue(value);
79976
80190
  } catch (error51) {
80191
+ if (released) return;
79977
80192
  if (cancelled) {
79978
80193
  cleanup();
79979
80194
  output.close();
@@ -79981,6 +80196,7 @@ function createWordPressTransport(onFailure) {
79981
80196
  }
79982
80197
  },
79983
80198
  async cancel(reason) {
80199
+ released = true;
79984
80200
  cleanup();
79985
80201
  await reader.cancel(reason);
79986
80202
  }
@@ -80019,6 +80235,7 @@ function createWordPressTransport(onFailure) {
80019
80235
  throw failure(error51);
80020
80236
  }
80021
80237
  };
80238
+ const fetch3 = (url2, init) => send(url2, init, false);
80022
80239
  return Object.assign(new StreamableHTTPClientTransport(new URL(getRequestUrl()), { fetch: fetch3 }), {
80023
80240
  cancelRequest(id) {
80024
80241
  inflight.get(id)?.();
@@ -80246,11 +80463,6 @@ async function startPassThrough(local, remote) {
80246
80463
  validateNodeVersion(18);
80247
80464
  async function WordPressProxy() {
80248
80465
  await setupFetchPolyfill();
80249
- try {
80250
- await cleanupExpiredTokens();
80251
- } catch (error51) {
80252
- logger.warn("Error cleaning up expired tokens", "PROXY", error51);
80253
- }
80254
80466
  const local = new StdioServerTransport();
80255
80467
  let bridge;
80256
80468
  const remote = createWordPressTransport((message, error51) => bridge?.fail(message, error51));
@@ -80285,6 +80497,13 @@ formdata-polyfill/esm.min.js:
80285
80497
  node-domexception/index.js:
80286
80498
  (*! node-domexception. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> *)
80287
80499
 
80500
+ content-type/index.js:
80501
+ (*!
80502
+ * content-type
80503
+ * Copyright(c) 2015 Douglas Christopher Wilson
80504
+ * MIT Licensed
80505
+ *)
80506
+
80288
80507
  depd/index.js:
80289
80508
  (*!
80290
80509
  * depd
@@ -80482,13 +80701,6 @@ express/lib/view.js:
80482
80701
  * MIT Licensed
80483
80702
  *)
80484
80703
 
80485
- content-type/index.js:
80486
- (*!
80487
- * content-type
80488
- * Copyright(c) 2015 Douglas Christopher Wilson
80489
- * MIT Licensed
80490
- *)
80491
-
80492
80704
  etag/index.js:
80493
80705
  (*!
80494
80706
  * etag