@automattic/mcp-wordpress-remote 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Your Name
3
+ Copyright (c) 2025 Automattic Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/lib.d.ts CHANGED
@@ -15,7 +15,7 @@ interface WordPressResponse {
15
15
  [key: string]: any;
16
16
  }
17
17
 
18
- declare const MCP_WORDPRESS_REMOTE_VERSION = "0.3.4";
18
+ declare const MCP_WORDPRESS_REMOTE_VERSION = "0.4.0";
19
19
  /**
20
20
  * Centralized configuration for MCP WordPress Remote
21
21
  * All default values are defined here and can be overridden via environment variables
@@ -327,4 +327,47 @@ declare function isProxyConfigured(): boolean;
327
327
  */
328
328
  declare function getProxyType(): 'pac' | 'env' | 'none';
329
329
 
330
- export { APIError, AuthError, CONFIG, ConfigError, LogLevel, MCP_WORDPRESS_REMOTE_VERSION, OAuthError, type TokenValidationResult, type WPClientInfo, type WPTokens, type WordPressRequestParams, type WordPressResponse, getAgentForUrl, getConfig, getCustomHeaders, getDefaultOAuthScopes, getFetchInfo, getProxyInfo, getProxyType, initializeProxy, isAPIError, isAuthError, isConfigError, isFetchAvailable, isOAuthError, isProxyConfigured, log, logger, parseCustomHeaders, proxyFetch, setupFetchPolyfill, validateConfig, wpRequest };
330
+ /**
331
+ * MCP Request Types for WordPress Remote Proxy
332
+ *
333
+ * Type definitions for all MCP JSON-RPC request schemas
334
+ */
335
+
336
+ interface WPRequestParams {
337
+ method: string;
338
+ [key: string]: unknown;
339
+ }
340
+
341
+ /**
342
+ * Tool-call hooks for MCP WordPress Remote.
343
+ *
344
+ * Lets an embedding package observe completed `tools/call` requests and
345
+ * piggyback on the proxy's live, authenticated session — for example to send
346
+ * usage telemetry — without forking the proxy.
347
+ *
348
+ * The registry lives on a global symbol so it is a single instance even when a
349
+ * consumer registers through the `/lib` bundle while the handlers run in the
350
+ * separate `proxy` bundle.
351
+ */
352
+
353
+ /** Context passed to each hook after a tool call completes. */
354
+ interface ToolCallContext {
355
+ /** Name of the tool the client just called. */
356
+ name: string;
357
+ /**
358
+ * Dispatch an additional request over the proxy's live, authenticated
359
+ * session. The request is prepared for the session's detected transport
360
+ * (JSON-RPC envelope or simple format) before it is sent, e.g.
361
+ * `wpRequest({ method: 'tools/call', name, arguments })`.
362
+ */
363
+ wpRequest: (params: WPRequestParams) => Promise<WordPressResponse>;
364
+ }
365
+ /** A hook fired after each completed `tools/call`. May be async. */
366
+ type ToolCallHook = (context: ToolCallContext) => void | Promise<void>;
367
+ /**
368
+ * Register a hook fired after each completed `tools/call`. Returns a function
369
+ * that unregisters it.
370
+ */
371
+ declare function registerToolCallHook(hook: ToolCallHook): () => void;
372
+
373
+ export { APIError, AuthError, CONFIG, ConfigError, LogLevel, MCP_WORDPRESS_REMOTE_VERSION, OAuthError, type TokenValidationResult, type ToolCallContext, type ToolCallHook, type WPClientInfo, type WPTokens, type WordPressRequestParams, type WordPressResponse, getAgentForUrl, getConfig, getCustomHeaders, getDefaultOAuthScopes, getFetchInfo, getProxyInfo, getProxyType, initializeProxy, isAPIError, isAuthError, isConfigError, isFetchAvailable, isOAuthError, isProxyConfigured, log, logger, parseCustomHeaders, proxyFetch, registerToolCallHook, setupFetchPolyfill, validateConfig, wpRequest };
package/dist/lib.js CHANGED
@@ -20760,12 +20760,15 @@ var require_json = __commonJS({
20760
20760
  var JSON_SYNTAX_REGEXP = /#+/g;
20761
20761
  function json2(options) {
20762
20762
  var opts = options || {};
20763
- var limit = typeof opts.limit !== "number" ? bytes.parse(opts.limit || "100kb") : opts.limit;
20763
+ var limit = typeof opts.limit === "undefined" || opts.limit === null ? 102400 : bytes.parse(opts.limit);
20764
20764
  var inflate = opts.inflate !== false;
20765
20765
  var reviver = opts.reviver;
20766
20766
  var strict = opts.strict !== false;
20767
20767
  var type = opts.type || "application/json";
20768
20768
  var verify = opts.verify || false;
20769
+ if (limit === null) {
20770
+ throw new TypeError('option limit "' + String(opts.limit) + '" is invalid');
20771
+ }
20769
20772
  if (verify !== false && typeof verify !== "function") {
20770
20773
  throw new TypeError("option verify must be function");
20771
20774
  }
@@ -20887,9 +20890,12 @@ var require_raw = __commonJS({
20887
20890
  function raw(options) {
20888
20891
  var opts = options || {};
20889
20892
  var inflate = opts.inflate !== false;
20890
- var limit = typeof opts.limit !== "number" ? bytes.parse(opts.limit || "100kb") : opts.limit;
20893
+ var limit = typeof opts.limit === "undefined" || opts.limit === null ? 102400 : bytes.parse(opts.limit);
20891
20894
  var type = opts.type || "application/octet-stream";
20892
20895
  var verify = opts.verify || false;
20896
+ if (limit === null) {
20897
+ throw new TypeError('option limit "' + String(opts.limit) + '" is invalid');
20898
+ }
20893
20899
  if (verify !== false && typeof verify !== "function") {
20894
20900
  throw new TypeError("option verify must be function");
20895
20901
  }
@@ -20945,9 +20951,12 @@ var require_text = __commonJS({
20945
20951
  var opts = options || {};
20946
20952
  var defaultCharset = opts.defaultCharset || "utf-8";
20947
20953
  var inflate = opts.inflate !== false;
20948
- var limit = typeof opts.limit !== "number" ? bytes.parse(opts.limit || "100kb") : opts.limit;
20954
+ var limit = typeof opts.limit === "undefined" || opts.limit === null ? 102400 : bytes.parse(opts.limit);
20949
20955
  var type = opts.type || "text/plain";
20950
20956
  var verify = opts.verify || false;
20957
+ if (limit === null) {
20958
+ throw new TypeError('option limit "' + String(opts.limit) + '" is invalid');
20959
+ }
20951
20960
  if (verify !== false && typeof verify !== "function") {
20952
20961
  throw new TypeError("option verify must be function");
20953
20962
  }
@@ -23556,9 +23565,12 @@ var require_urlencoded = __commonJS({
23556
23565
  }
23557
23566
  var extended = opts.extended !== false;
23558
23567
  var inflate = opts.inflate !== false;
23559
- var limit = typeof opts.limit !== "number" ? bytes.parse(opts.limit || "100kb") : opts.limit;
23568
+ var limit = typeof opts.limit === "undefined" || opts.limit === null ? 102400 : bytes.parse(opts.limit);
23560
23569
  var type = opts.type || "application/x-www-form-urlencoded";
23561
23570
  var verify = opts.verify || false;
23571
+ if (limit === null) {
23572
+ throw new TypeError('option limit "' + String(opts.limit) + '" is invalid');
23573
+ }
23562
23574
  if (verify !== false && typeof verify !== "function") {
23563
23575
  throw new TypeError("option verify must be function");
23564
23576
  }
@@ -47325,7 +47337,7 @@ async function selectCallbackPort(serverUrl, specifiedPort, _unused) {
47325
47337
  }
47326
47338
 
47327
47339
  // src/lib/config.ts
47328
- var MCP_WORDPRESS_REMOTE_VERSION = "0.3.4";
47340
+ var MCP_WORDPRESS_REMOTE_VERSION = "0.4.0";
47329
47341
  function parsePositiveIntEnv(value, defaultValue) {
47330
47342
  if (!value) {
47331
47343
  return defaultValue;
@@ -47760,13 +47772,19 @@ async function getAgentForUrl(url2) {
47760
47772
  const socksMatch = directive.match(/SOCKS5?\s+(\S+):(\d+)/i);
47761
47773
  if (socksMatch) {
47762
47774
  const proxyUrl = `socks5h://${socksMatch[1]}:${socksMatch[2]}`;
47763
- logger.debug(`PAC returned SOCKS proxy for ${url2}: ${sanitizeProxyUrl(proxyUrl)}`, "PROXY");
47775
+ logger.debug(
47776
+ `PAC returned SOCKS proxy for ${url2}: ${sanitizeProxyUrl(proxyUrl)}`,
47777
+ "PROXY"
47778
+ );
47764
47779
  return new SocksProxyAgent(proxyUrl);
47765
47780
  }
47766
47781
  const proxyMatch = directive.match(/PROXY\s+(\S+):(\d+)/i);
47767
47782
  if (proxyMatch) {
47768
47783
  const proxyUrl = `http://${proxyMatch[1]}:${proxyMatch[2]}`;
47769
- logger.debug(`PAC returned HTTP proxy for ${url2}: ${sanitizeProxyUrl(proxyUrl)}`, "PROXY");
47784
+ logger.debug(
47785
+ `PAC returned HTTP proxy for ${url2}: ${sanitizeProxyUrl(proxyUrl)}`,
47786
+ "PROXY"
47787
+ );
47770
47788
  return new HttpsProxyAgent(proxyUrl);
47771
47789
  }
47772
47790
  }
@@ -48443,7 +48461,7 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl) {
48443
48461
  const customHeaders = getCustomHeaders();
48444
48462
  const response = await proxyFetch(metadataUrl.toString(), {
48445
48463
  headers: {
48446
- "Accept": "application/json",
48464
+ Accept: "application/json",
48447
48465
  ...customHeaders
48448
48466
  }
48449
48467
  });
@@ -48480,7 +48498,7 @@ async function discoverProtectedResourceMetadata(resourceUrl) {
48480
48498
  const customHeaders = getCustomHeaders();
48481
48499
  const response = await proxyFetch(metadataUrl.toString(), {
48482
48500
  headers: {
48483
- "Accept": "application/json",
48501
+ Accept: "application/json",
48484
48502
  ...customHeaders
48485
48503
  }
48486
48504
  });
@@ -49075,7 +49093,7 @@ var MCPOAuthProvider = class {
49075
49093
  if (authInfo.resource_metadata_url) {
49076
49094
  const metadataResponse = await proxyFetch(authInfo.resource_metadata_url, {
49077
49095
  headers: {
49078
- "Accept": "application/json",
49096
+ Accept: "application/json",
49079
49097
  ...customHeaders
49080
49098
  }
49081
49099
  });
@@ -49112,6 +49130,12 @@ var MCPOAuthProvider = class {
49112
49130
  logger.oauth("Using provided client ID, skipping dynamic registration");
49113
49131
  return;
49114
49132
  }
49133
+ const storedClientInfo = await readClientInfo(this.serverUrlHash);
49134
+ if (typeof storedClientInfo?.client_id === "string" && storedClientInfo.client_id.length > 0) {
49135
+ this.config.clientId = storedClientInfo.client_id;
49136
+ logger.oauth("Reusing persisted client registration, skipping dynamic registration");
49137
+ return;
49138
+ }
49115
49139
  if (!CONFIG.OAUTH_DYNAMIC_REGISTRATION) {
49116
49140
  throw new OAuthError2(
49117
49141
  "No client ID provided and dynamic registration is disabled",
@@ -50035,10 +50059,7 @@ async function getOAuthTokens() {
50035
50059
  return null;
50036
50060
  }
50037
50061
  } else {
50038
- logger.warn(
50039
- "Using legacy OAuth provider. Consider enabling PKCE for MCP compliance",
50040
- "AUTH"
50041
- );
50062
+ logger.warn("Using legacy OAuth provider. Consider enabling PKCE for MCP compliance", "AUTH");
50042
50063
  if (!authCoordinator) {
50043
50064
  if (!globalEvents) {
50044
50065
  globalEvents = new EventEmitter3();
@@ -50281,7 +50302,10 @@ async function executeWordPressRequest(requestData, useJsonRpc, sessionIdUsed) {
50281
50302
  }
50282
50303
  async function refreshSession(failedSessionId) {
50283
50304
  if (failedSessionId && globalSessionId && globalSessionId !== failedSessionId) {
50284
- logger.info("Detected newer session while handling invalid-session error; skipping refresh", "SESSION");
50305
+ logger.info(
50306
+ "Detected newer session while handling invalid-session error; skipping refresh",
50307
+ "SESSION"
50308
+ );
50285
50309
  return;
50286
50310
  }
50287
50311
  if (sessionRefreshPromise) {
@@ -50335,11 +50359,7 @@ async function wpRequest(requestData, useJsonRpc = true, options = {}) {
50335
50359
  sessionIdUsed: sessionIdUsed || "none"
50336
50360
  });
50337
50361
  await refreshSession(sessionIdUsed);
50338
- const retriedResult = await executeWordPressRequest(
50339
- requestData,
50340
- useJsonRpc,
50341
- globalSessionId
50342
- );
50362
+ const retriedResult = await executeWordPressRequest(requestData, useJsonRpc, globalSessionId);
50343
50363
  return retriedResult.responseData;
50344
50364
  }
50345
50365
  if (error51 instanceof APIError) {
@@ -50351,6 +50371,25 @@ async function wpRequest(requestData, useJsonRpc = true, options = {}) {
50351
50371
  throw new APIError(errorMessage, 0, getRequestUrl(), void 0, code);
50352
50372
  }
50353
50373
  }
50374
+
50375
+ // src/lib/tool-call-hooks.ts
50376
+ var REGISTRY_KEY = Symbol.for("@automattic/mcp-wordpress-remote:tool-call-hooks");
50377
+ function getRegistry() {
50378
+ const globalObject = globalThis;
50379
+ let registry2 = globalObject[REGISTRY_KEY];
50380
+ if (!registry2) {
50381
+ registry2 = /* @__PURE__ */ new Set();
50382
+ globalObject[REGISTRY_KEY] = registry2;
50383
+ }
50384
+ return registry2;
50385
+ }
50386
+ function registerToolCallHook(hook) {
50387
+ const registry2 = getRegistry();
50388
+ registry2.add(hook);
50389
+ return () => {
50390
+ registry2.delete(hook);
50391
+ };
50392
+ }
50354
50393
  export {
50355
50394
  APIError,
50356
50395
  AuthError,
@@ -50377,6 +50416,7 @@ export {
50377
50416
  logger,
50378
50417
  parseCustomHeaders,
50379
50418
  proxyFetch,
50419
+ registerToolCallHook,
50380
50420
  setupFetchPolyfill,
50381
50421
  validateConfig,
50382
50422
  wpRequest
package/dist/proxy.js CHANGED
@@ -3775,6 +3775,7 @@ var require_fast_uri = __commonJS({
3775
3775
  return uriTokens.join("");
3776
3776
  }
3777
3777
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3778
+ var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
3778
3779
  function getParseError(parsed, matches) {
3779
3780
  if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
3780
3781
  return 'URI path must start with "/" when authority is present.';
@@ -3804,6 +3805,11 @@ var require_fast_uri = __commonJS({
3804
3805
  uri = "//" + uri;
3805
3806
  }
3806
3807
  }
3808
+ const authorityMatch = uri.match(AUTHORITY_PREFIX);
3809
+ if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
3810
+ parsed.error = "URI authority must not contain a literal backslash.";
3811
+ malformedAuthorityOrPort = true;
3812
+ }
3807
3813
  const matches = uri.match(URI_PARSE);
3808
3814
  if (matches) {
3809
3815
  parsed.scheme = matches[1];
@@ -3847,7 +3853,7 @@ var require_fast_uri = __commonJS({
3847
3853
  if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3848
3854
  if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP2 === false && nonSimpleDomain(parsed.host)) {
3849
3855
  try {
3850
- parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
3856
+ parsed.host = new URL("http://" + parsed.host).hostname;
3851
3857
  } catch (e2) {
3852
3858
  parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e2;
3853
3859
  }
@@ -27617,12 +27623,15 @@ var require_json = __commonJS({
27617
27623
  var JSON_SYNTAX_REGEXP = /#+/g;
27618
27624
  function json2(options) {
27619
27625
  var opts = options || {};
27620
- var limit = typeof opts.limit !== "number" ? bytes.parse(opts.limit || "100kb") : opts.limit;
27626
+ var limit = typeof opts.limit === "undefined" || opts.limit === null ? 102400 : bytes.parse(opts.limit);
27621
27627
  var inflate = opts.inflate !== false;
27622
27628
  var reviver = opts.reviver;
27623
27629
  var strict = opts.strict !== false;
27624
27630
  var type = opts.type || "application/json";
27625
27631
  var verify = opts.verify || false;
27632
+ if (limit === null) {
27633
+ throw new TypeError('option limit "' + String(opts.limit) + '" is invalid');
27634
+ }
27626
27635
  if (verify !== false && typeof verify !== "function") {
27627
27636
  throw new TypeError("option verify must be function");
27628
27637
  }
@@ -27744,9 +27753,12 @@ var require_raw = __commonJS({
27744
27753
  function raw(options) {
27745
27754
  var opts = options || {};
27746
27755
  var inflate = opts.inflate !== false;
27747
- var limit = typeof opts.limit !== "number" ? bytes.parse(opts.limit || "100kb") : opts.limit;
27756
+ var limit = typeof opts.limit === "undefined" || opts.limit === null ? 102400 : bytes.parse(opts.limit);
27748
27757
  var type = opts.type || "application/octet-stream";
27749
27758
  var verify = opts.verify || false;
27759
+ if (limit === null) {
27760
+ throw new TypeError('option limit "' + String(opts.limit) + '" is invalid');
27761
+ }
27750
27762
  if (verify !== false && typeof verify !== "function") {
27751
27763
  throw new TypeError("option verify must be function");
27752
27764
  }
@@ -27802,9 +27814,12 @@ var require_text = __commonJS({
27802
27814
  var opts = options || {};
27803
27815
  var defaultCharset = opts.defaultCharset || "utf-8";
27804
27816
  var inflate = opts.inflate !== false;
27805
- var limit = typeof opts.limit !== "number" ? bytes.parse(opts.limit || "100kb") : opts.limit;
27817
+ var limit = typeof opts.limit === "undefined" || opts.limit === null ? 102400 : bytes.parse(opts.limit);
27806
27818
  var type = opts.type || "text/plain";
27807
27819
  var verify = opts.verify || false;
27820
+ if (limit === null) {
27821
+ throw new TypeError('option limit "' + String(opts.limit) + '" is invalid');
27822
+ }
27808
27823
  if (verify !== false && typeof verify !== "function") {
27809
27824
  throw new TypeError("option verify must be function");
27810
27825
  }
@@ -30413,9 +30428,12 @@ var require_urlencoded = __commonJS({
30413
30428
  }
30414
30429
  var extended = opts.extended !== false;
30415
30430
  var inflate = opts.inflate !== false;
30416
- var limit = typeof opts.limit !== "number" ? bytes.parse(opts.limit || "100kb") : opts.limit;
30431
+ var limit = typeof opts.limit === "undefined" || opts.limit === null ? 102400 : bytes.parse(opts.limit);
30417
30432
  var type = opts.type || "application/x-www-form-urlencoded";
30418
30433
  var verify = opts.verify || false;
30434
+ if (limit === null) {
30435
+ throw new TypeError('option limit "' + String(opts.limit) + '" is invalid');
30436
+ }
30419
30437
  if (verify !== false && typeof verify !== "function") {
30420
30438
  throw new TypeError("option verify must be function");
30421
30439
  }
@@ -55942,7 +55960,7 @@ async function selectCallbackPort(serverUrl, specifiedPort, _unused) {
55942
55960
  }
55943
55961
 
55944
55962
  // src/lib/config.ts
55945
- var MCP_WORDPRESS_REMOTE_VERSION = "0.3.4";
55963
+ var MCP_WORDPRESS_REMOTE_VERSION = "0.4.0";
55946
55964
  function parsePositiveIntEnv(value, defaultValue) {
55947
55965
  if (!value) {
55948
55966
  return defaultValue;
@@ -56387,13 +56405,19 @@ async function getAgentForUrl(url2) {
56387
56405
  const socksMatch = directive.match(/SOCKS5?\s+(\S+):(\d+)/i);
56388
56406
  if (socksMatch) {
56389
56407
  const proxyUrl = `socks5h://${socksMatch[1]}:${socksMatch[2]}`;
56390
- logger.debug(`PAC returned SOCKS proxy for ${url2}: ${sanitizeProxyUrl(proxyUrl)}`, "PROXY");
56408
+ logger.debug(
56409
+ `PAC returned SOCKS proxy for ${url2}: ${sanitizeProxyUrl(proxyUrl)}`,
56410
+ "PROXY"
56411
+ );
56391
56412
  return new SocksProxyAgent(proxyUrl);
56392
56413
  }
56393
56414
  const proxyMatch = directive.match(/PROXY\s+(\S+):(\d+)/i);
56394
56415
  if (proxyMatch) {
56395
56416
  const proxyUrl = `http://${proxyMatch[1]}:${proxyMatch[2]}`;
56396
- logger.debug(`PAC returned HTTP proxy for ${url2}: ${sanitizeProxyUrl(proxyUrl)}`, "PROXY");
56417
+ logger.debug(
56418
+ `PAC returned HTTP proxy for ${url2}: ${sanitizeProxyUrl(proxyUrl)}`,
56419
+ "PROXY"
56420
+ );
56397
56421
  return new HttpsProxyAgent(proxyUrl);
56398
56422
  }
56399
56423
  }
@@ -56574,7 +56598,7 @@ function mapHttpStatusToMcpCode(statusCode) {
56574
56598
  RESOURCE_NOT_FOUND: -32002,
56575
56599
  // Resource not found
56576
56600
  TOOL_NOT_FOUND: -32003,
56577
- // Tool not found
56601
+ // Tool not found
56578
56602
  PROMPT_NOT_FOUND: -32004,
56579
56603
  // Prompt not found
56580
56604
  PERMISSION_DENIED: -32008,
@@ -56642,7 +56666,11 @@ function mcpErrorParts(error51) {
56642
56666
  if (wpError) {
56643
56667
  return { code: wpError.code, message: wpError.message ?? error51.message, data: wpError.data };
56644
56668
  }
56645
- return { code: deriveMcpErrorCode(error51), message: error51.message, data: buildApiErrorData(error51) };
56669
+ return {
56670
+ code: deriveMcpErrorCode(error51),
56671
+ message: error51.message,
56672
+ data: buildApiErrorData(error51)
56673
+ };
56646
56674
  }
56647
56675
  function convertAPIErrorToMcpError(error51) {
56648
56676
  return { error: mcpErrorParts(error51) };
@@ -57130,7 +57158,7 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl) {
57130
57158
  const customHeaders = getCustomHeaders();
57131
57159
  const response = await proxyFetch(metadataUrl.toString(), {
57132
57160
  headers: {
57133
- "Accept": "application/json",
57161
+ Accept: "application/json",
57134
57162
  ...customHeaders
57135
57163
  }
57136
57164
  });
@@ -57167,7 +57195,7 @@ async function discoverProtectedResourceMetadata(resourceUrl) {
57167
57195
  const customHeaders = getCustomHeaders();
57168
57196
  const response = await proxyFetch(metadataUrl.toString(), {
57169
57197
  headers: {
57170
- "Accept": "application/json",
57198
+ Accept: "application/json",
57171
57199
  ...customHeaders
57172
57200
  }
57173
57201
  });
@@ -57762,7 +57790,7 @@ var MCPOAuthProvider = class {
57762
57790
  if (authInfo.resource_metadata_url) {
57763
57791
  const metadataResponse = await proxyFetch(authInfo.resource_metadata_url, {
57764
57792
  headers: {
57765
- "Accept": "application/json",
57793
+ Accept: "application/json",
57766
57794
  ...customHeaders
57767
57795
  }
57768
57796
  });
@@ -57799,6 +57827,12 @@ var MCPOAuthProvider = class {
57799
57827
  logger.oauth("Using provided client ID, skipping dynamic registration");
57800
57828
  return;
57801
57829
  }
57830
+ const storedClientInfo = await readClientInfo(this.serverUrlHash);
57831
+ if (typeof storedClientInfo?.client_id === "string" && storedClientInfo.client_id.length > 0) {
57832
+ this.config.clientId = storedClientInfo.client_id;
57833
+ logger.oauth("Reusing persisted client registration, skipping dynamic registration");
57834
+ return;
57835
+ }
57802
57836
  if (!CONFIG.OAUTH_DYNAMIC_REGISTRATION) {
57803
57837
  throw new OAuthError2(
57804
57838
  "No client ID provided and dynamic registration is disabled",
@@ -58722,10 +58756,7 @@ async function getOAuthTokens() {
58722
58756
  return null;
58723
58757
  }
58724
58758
  } else {
58725
- logger.warn(
58726
- "Using legacy OAuth provider. Consider enabling PKCE for MCP compliance",
58727
- "AUTH"
58728
- );
58759
+ logger.warn("Using legacy OAuth provider. Consider enabling PKCE for MCP compliance", "AUTH");
58729
58760
  if (!authCoordinator) {
58730
58761
  if (!globalEvents) {
58731
58762
  globalEvents = new EventEmitter3();
@@ -58971,7 +59002,10 @@ async function executeWordPressRequest(requestData, useJsonRpc, sessionIdUsed) {
58971
59002
  }
58972
59003
  async function refreshSession(failedSessionId) {
58973
59004
  if (failedSessionId && globalSessionId && globalSessionId !== failedSessionId) {
58974
- logger.info("Detected newer session while handling invalid-session error; skipping refresh", "SESSION");
59005
+ logger.info(
59006
+ "Detected newer session while handling invalid-session error; skipping refresh",
59007
+ "SESSION"
59008
+ );
58975
59009
  return;
58976
59010
  }
58977
59011
  if (sessionRefreshPromise) {
@@ -59025,11 +59059,7 @@ async function wpRequest(requestData, useJsonRpc = true, options = {}) {
59025
59059
  sessionIdUsed: sessionIdUsed || "none"
59026
59060
  });
59027
59061
  await refreshSession(sessionIdUsed);
59028
- const retriedResult = await executeWordPressRequest(
59029
- requestData,
59030
- useJsonRpc,
59031
- globalSessionId
59032
- );
59062
+ const retriedResult = await executeWordPressRequest(requestData, useJsonRpc, globalSessionId);
59033
59063
  return retriedResult.responseData;
59034
59064
  }
59035
59065
  if (error51 instanceof APIError) {
@@ -59117,7 +59147,10 @@ async function waitForInit(context, timeoutMs = INIT_TIMEOUT_MS) {
59117
59147
 
59118
59148
  // src/lib/transport-detection.ts
59119
59149
  async function detectTransportType(context, initParams) {
59120
- logger.info("\u{1F50D} Starting transport detection and WordPress API initialization...", "TRANSPORT_DETECT");
59150
+ logger.info(
59151
+ "\u{1F50D} Starting transport detection and WordPress API initialization...",
59152
+ "TRANSPORT_DETECT"
59153
+ );
59121
59154
  let init;
59122
59155
  let transportType = null;
59123
59156
  const enhancedParams = initParams ? { ...initParams } : {};
@@ -59144,11 +59177,15 @@ async function detectTransportType(context, initParams) {
59144
59177
  proxyVersion: MCP_WORDPRESS_REMOTE_VERSION
59145
59178
  });
59146
59179
  } else {
59147
- logger.warn("clientInfo is present but not a valid object, skipping enhancement", "TRANSPORT_DETECT", {
59148
- clientInfoType: typeof enhancedParams.clientInfo,
59149
- isArray: Array.isArray(enhancedParams.clientInfo),
59150
- isNull: enhancedParams.clientInfo === null
59151
- });
59180
+ logger.warn(
59181
+ "clientInfo is present but not a valid object, skipping enhancement",
59182
+ "TRANSPORT_DETECT",
59183
+ {
59184
+ clientInfoType: typeof enhancedParams.clientInfo,
59185
+ isArray: Array.isArray(enhancedParams.clientInfo),
59186
+ isNull: enhancedParams.clientInfo === null
59187
+ }
59188
+ );
59152
59189
  }
59153
59190
  } else {
59154
59191
  logger.debug("No clientInfo provided in initialize parameters", "TRANSPORT_DETECT");
@@ -59202,8 +59239,14 @@ async function detectTransportType(context, initParams) {
59202
59239
  logger.info("\u2705 Simple transport detected and working", "TRANSPORT_DETECT");
59203
59240
  logger.debug("Simple initialization response:", "TRANSPORT_DETECT", init);
59204
59241
  } catch (simpleError) {
59205
- logger.error("\u274C Both JSON-RPC and simple transports failed", "TRANSPORT_DETECT", simpleError);
59206
- const connectionError = new Error("Unable to establish connection with either transport type");
59242
+ logger.error(
59243
+ "\u274C Both JSON-RPC and simple transports failed",
59244
+ "TRANSPORT_DETECT",
59245
+ simpleError
59246
+ );
59247
+ const connectionError = new Error(
59248
+ "Unable to establish connection with either transport type"
59249
+ );
59207
59250
  connectionError.cause = simpleError;
59208
59251
  throw connectionError;
59209
59252
  }
@@ -59216,7 +59259,10 @@ async function detectTransportType(context, initParams) {
59216
59259
  } else {
59217
59260
  logger.warn("No session ID received from WordPress - continuing without session", "PROXY");
59218
59261
  }
59219
- logger.info(`WordPress connection initialized successfully using ${transportType} transport`, "PROXY");
59262
+ logger.info(
59263
+ `WordPress connection initialized successfully using ${transportType} transport`,
59264
+ "PROXY"
59265
+ );
59220
59266
  return {
59221
59267
  transportType,
59222
59268
  initResult: init,
@@ -59224,6 +59270,31 @@ async function detectTransportType(context, initParams) {
59224
59270
  };
59225
59271
  }
59226
59272
 
59273
+ // src/lib/tool-call-hooks.ts
59274
+ var REGISTRY_KEY = Symbol.for("@automattic/mcp-wordpress-remote:tool-call-hooks");
59275
+ function getRegistry() {
59276
+ const globalObject = globalThis;
59277
+ let registry2 = globalObject[REGISTRY_KEY];
59278
+ if (!registry2) {
59279
+ registry2 = /* @__PURE__ */ new Set();
59280
+ globalObject[REGISTRY_KEY] = registry2;
59281
+ }
59282
+ return registry2;
59283
+ }
59284
+ function runToolCallHooks(context) {
59285
+ const hooks = [...getRegistry()];
59286
+ if (hooks.length === 0) return;
59287
+ setImmediate(() => {
59288
+ for (const hook of hooks) {
59289
+ void Promise.resolve().then(() => hook(context)).catch((error51) => {
59290
+ const message = error51 instanceof Error ? error51.message : String(error51);
59291
+ logger.error(`Tool-call hook failed: ${message}`, "HOOKS");
59292
+ }).catch(() => {
59293
+ });
59294
+ }
59295
+ });
59296
+ }
59297
+
59227
59298
  // src/lib/request-handler-factory.ts
59228
59299
  function createRequestHandler(config2, context) {
59229
59300
  return async (request) => {
@@ -59245,6 +59316,10 @@ function createRequestHandler(config2, context) {
59245
59316
  const wpParams = config2.paramMapper(request);
59246
59317
  const requestData = prepareRequest(wpParams, request, context);
59247
59318
  const response = await wpRequest(requestData, context.transportType === "jsonrpc");
59319
+ if (config2.method === "tools/call") {
59320
+ const hookWpRequest = (params) => wpRequest(prepareRequest(params, {}, context), context.transportType === "jsonrpc");
59321
+ runToolCallHooks({ name: String(wpParams.name ?? ""), wpRequest: hookWpRequest });
59322
+ }
59248
59323
  return response;
59249
59324
  };
59250
59325
  }
@@ -59285,19 +59360,27 @@ function createWrappedHandler(config2, context) {
59285
59360
  });
59286
59361
  if (isAPIError(error51)) {
59287
59362
  if (context.transportType === "simple") {
59288
- logger.debug(`Converting APIError to MCP error format for ${config2.name} (simple transport)`, "MCP", {
59363
+ logger.debug(
59364
+ `Converting APIError to MCP error format for ${config2.name} (simple transport)`,
59365
+ "MCP",
59366
+ {
59367
+ statusCode: error51.statusCode,
59368
+ endpoint: error51.endpoint,
59369
+ message: error51.message
59370
+ }
59371
+ );
59372
+ return convertAPIErrorToMcpError(error51);
59373
+ }
59374
+ logger.debug(
59375
+ `Converting APIError to McpError for ${config2.name} (jsonrpc transport)`,
59376
+ "MCP",
59377
+ {
59289
59378
  statusCode: error51.statusCode,
59379
+ code: error51.code,
59290
59380
  endpoint: error51.endpoint,
59291
59381
  message: error51.message
59292
- });
59293
- return convertAPIErrorToMcpError(error51);
59294
- }
59295
- logger.debug(`Converting APIError to McpError for ${config2.name} (jsonrpc transport)`, "MCP", {
59296
- statusCode: error51.statusCode,
59297
- code: error51.code,
59298
- endpoint: error51.endpoint,
59299
- message: error51.message
59300
- });
59382
+ }
59383
+ );
59301
59384
  throw apiErrorToMcpError(error51);
59302
59385
  }
59303
59386
  throw error51;
@@ -59467,7 +59550,9 @@ async function WordPressProxy() {
59467
59550
  version: MCP_WORDPRESS_REMOTE_VERSION
59468
59551
  },
59469
59552
  capabilities: {
59470
- experimental: { connectionFailed: connectionError.code ? { code: connectionError.code } : {} }
59553
+ experimental: {
59554
+ connectionFailed: connectionError.code ? { code: connectionError.code } : {}
59555
+ }
59471
59556
  },
59472
59557
  instructions: `MCP WordPress Remote Proxy Server (Connection Failed${connectionError.code ? `: ${connectionError.code}` : ""})`
59473
59558
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/mcp-wordpress-remote",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "MCP WordPress Remote proxy server",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -12,6 +12,7 @@
12
12
  "proxy"
13
13
  ],
14
14
  "author": "Automattic Inc.",
15
+ "license": "MIT",
15
16
  "repository": {
16
17
  "type": "git",
17
18
  "url": "git+https://github.com/Automattic/mcp-wordpress-remote.git"
@@ -39,7 +40,7 @@
39
40
  "scripts": {
40
41
  "build": "tsup",
41
42
  "build:watch": "tsup --watch",
42
- "check": "prettier --check . && tsc",
43
+ "check": "prettier --check src && tsc --noEmit",
43
44
  "start": "NODE_ENV=development tsup --watch",
44
45
  "dev": "NODE_ENV=development node dist/proxy.js",
45
46
  "test": "jest",