@automattic/mcp-wordpress-remote 0.3.5 → 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.5";
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.5";
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
  });
@@ -50041,10 +50059,7 @@ async function getOAuthTokens() {
50041
50059
  return null;
50042
50060
  }
50043
50061
  } else {
50044
- logger.warn(
50045
- "Using legacy OAuth provider. Consider enabling PKCE for MCP compliance",
50046
- "AUTH"
50047
- );
50062
+ logger.warn("Using legacy OAuth provider. Consider enabling PKCE for MCP compliance", "AUTH");
50048
50063
  if (!authCoordinator) {
50049
50064
  if (!globalEvents) {
50050
50065
  globalEvents = new EventEmitter3();
@@ -50287,7 +50302,10 @@ async function executeWordPressRequest(requestData, useJsonRpc, sessionIdUsed) {
50287
50302
  }
50288
50303
  async function refreshSession(failedSessionId) {
50289
50304
  if (failedSessionId && globalSessionId && globalSessionId !== failedSessionId) {
50290
- 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
+ );
50291
50309
  return;
50292
50310
  }
50293
50311
  if (sessionRefreshPromise) {
@@ -50341,11 +50359,7 @@ async function wpRequest(requestData, useJsonRpc = true, options = {}) {
50341
50359
  sessionIdUsed: sessionIdUsed || "none"
50342
50360
  });
50343
50361
  await refreshSession(sessionIdUsed);
50344
- const retriedResult = await executeWordPressRequest(
50345
- requestData,
50346
- useJsonRpc,
50347
- globalSessionId
50348
- );
50362
+ const retriedResult = await executeWordPressRequest(requestData, useJsonRpc, globalSessionId);
50349
50363
  return retriedResult.responseData;
50350
50364
  }
50351
50365
  if (error51 instanceof APIError) {
@@ -50357,6 +50371,25 @@ async function wpRequest(requestData, useJsonRpc = true, options = {}) {
50357
50371
  throw new APIError(errorMessage, 0, getRequestUrl(), void 0, code);
50358
50372
  }
50359
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
+ }
50360
50393
  export {
50361
50394
  APIError,
50362
50395
  AuthError,
@@ -50383,6 +50416,7 @@ export {
50383
50416
  logger,
50384
50417
  parseCustomHeaders,
50385
50418
  proxyFetch,
50419
+ registerToolCallHook,
50386
50420
  setupFetchPolyfill,
50387
50421
  validateConfig,
50388
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.5";
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
  });
@@ -58728,10 +58756,7 @@ async function getOAuthTokens() {
58728
58756
  return null;
58729
58757
  }
58730
58758
  } else {
58731
- logger.warn(
58732
- "Using legacy OAuth provider. Consider enabling PKCE for MCP compliance",
58733
- "AUTH"
58734
- );
58759
+ logger.warn("Using legacy OAuth provider. Consider enabling PKCE for MCP compliance", "AUTH");
58735
58760
  if (!authCoordinator) {
58736
58761
  if (!globalEvents) {
58737
58762
  globalEvents = new EventEmitter3();
@@ -58977,7 +59002,10 @@ async function executeWordPressRequest(requestData, useJsonRpc, sessionIdUsed) {
58977
59002
  }
58978
59003
  async function refreshSession(failedSessionId) {
58979
59004
  if (failedSessionId && globalSessionId && globalSessionId !== failedSessionId) {
58980
- 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
+ );
58981
59009
  return;
58982
59010
  }
58983
59011
  if (sessionRefreshPromise) {
@@ -59031,11 +59059,7 @@ async function wpRequest(requestData, useJsonRpc = true, options = {}) {
59031
59059
  sessionIdUsed: sessionIdUsed || "none"
59032
59060
  });
59033
59061
  await refreshSession(sessionIdUsed);
59034
- const retriedResult = await executeWordPressRequest(
59035
- requestData,
59036
- useJsonRpc,
59037
- globalSessionId
59038
- );
59062
+ const retriedResult = await executeWordPressRequest(requestData, useJsonRpc, globalSessionId);
59039
59063
  return retriedResult.responseData;
59040
59064
  }
59041
59065
  if (error51 instanceof APIError) {
@@ -59123,7 +59147,10 @@ async function waitForInit(context, timeoutMs = INIT_TIMEOUT_MS) {
59123
59147
 
59124
59148
  // src/lib/transport-detection.ts
59125
59149
  async function detectTransportType(context, initParams) {
59126
- 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
+ );
59127
59154
  let init;
59128
59155
  let transportType = null;
59129
59156
  const enhancedParams = initParams ? { ...initParams } : {};
@@ -59150,11 +59177,15 @@ async function detectTransportType(context, initParams) {
59150
59177
  proxyVersion: MCP_WORDPRESS_REMOTE_VERSION
59151
59178
  });
59152
59179
  } else {
59153
- logger.warn("clientInfo is present but not a valid object, skipping enhancement", "TRANSPORT_DETECT", {
59154
- clientInfoType: typeof enhancedParams.clientInfo,
59155
- isArray: Array.isArray(enhancedParams.clientInfo),
59156
- isNull: enhancedParams.clientInfo === null
59157
- });
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
+ );
59158
59189
  }
59159
59190
  } else {
59160
59191
  logger.debug("No clientInfo provided in initialize parameters", "TRANSPORT_DETECT");
@@ -59208,8 +59239,14 @@ async function detectTransportType(context, initParams) {
59208
59239
  logger.info("\u2705 Simple transport detected and working", "TRANSPORT_DETECT");
59209
59240
  logger.debug("Simple initialization response:", "TRANSPORT_DETECT", init);
59210
59241
  } catch (simpleError) {
59211
- logger.error("\u274C Both JSON-RPC and simple transports failed", "TRANSPORT_DETECT", simpleError);
59212
- 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
+ );
59213
59250
  connectionError.cause = simpleError;
59214
59251
  throw connectionError;
59215
59252
  }
@@ -59222,7 +59259,10 @@ async function detectTransportType(context, initParams) {
59222
59259
  } else {
59223
59260
  logger.warn("No session ID received from WordPress - continuing without session", "PROXY");
59224
59261
  }
59225
- logger.info(`WordPress connection initialized successfully using ${transportType} transport`, "PROXY");
59262
+ logger.info(
59263
+ `WordPress connection initialized successfully using ${transportType} transport`,
59264
+ "PROXY"
59265
+ );
59226
59266
  return {
59227
59267
  transportType,
59228
59268
  initResult: init,
@@ -59230,6 +59270,31 @@ async function detectTransportType(context, initParams) {
59230
59270
  };
59231
59271
  }
59232
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
+
59233
59298
  // src/lib/request-handler-factory.ts
59234
59299
  function createRequestHandler(config2, context) {
59235
59300
  return async (request) => {
@@ -59251,6 +59316,10 @@ function createRequestHandler(config2, context) {
59251
59316
  const wpParams = config2.paramMapper(request);
59252
59317
  const requestData = prepareRequest(wpParams, request, context);
59253
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
+ }
59254
59323
  return response;
59255
59324
  };
59256
59325
  }
@@ -59291,19 +59360,27 @@ function createWrappedHandler(config2, context) {
59291
59360
  });
59292
59361
  if (isAPIError(error51)) {
59293
59362
  if (context.transportType === "simple") {
59294
- 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
+ {
59295
59378
  statusCode: error51.statusCode,
59379
+ code: error51.code,
59296
59380
  endpoint: error51.endpoint,
59297
59381
  message: error51.message
59298
- });
59299
- return convertAPIErrorToMcpError(error51);
59300
- }
59301
- logger.debug(`Converting APIError to McpError for ${config2.name} (jsonrpc transport)`, "MCP", {
59302
- statusCode: error51.statusCode,
59303
- code: error51.code,
59304
- endpoint: error51.endpoint,
59305
- message: error51.message
59306
- });
59382
+ }
59383
+ );
59307
59384
  throw apiErrorToMcpError(error51);
59308
59385
  }
59309
59386
  throw error51;
@@ -59473,7 +59550,9 @@ async function WordPressProxy() {
59473
59550
  version: MCP_WORDPRESS_REMOTE_VERSION
59474
59551
  },
59475
59552
  capabilities: {
59476
- experimental: { connectionFailed: connectionError.code ? { code: connectionError.code } : {} }
59553
+ experimental: {
59554
+ connectionFailed: connectionError.code ? { code: connectionError.code } : {}
59555
+ }
59477
59556
  },
59478
59557
  instructions: `MCP WordPress Remote Proxy Server (Connection Failed${connectionError.code ? `: ${connectionError.code}` : ""})`
59479
59558
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/mcp-wordpress-remote",
3
- "version": "0.3.5",
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",