@automattic/mcp-wordpress-remote 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/proxy.js +81 -9
  2. package/package.json +1 -1
package/dist/proxy.js CHANGED
@@ -34,6 +34,10 @@ var CONFIG = {
34
34
  // Resource Indicators (RFC 8707)
35
35
  OAUTH_RESOURCE_INDICATOR: process.env.OAUTH_RESOURCE_INDICATOR !== "false",
36
36
  // Resource parameter support
37
+ // OAuth Scopes Configuration
38
+ // OAUTH_SCOPES: Comma-separated list of OAuth scopes (e.g., "read,write" or "global")
39
+ // If empty, uses defaults: "global" for WordPress.com, "read,write" for self-hosted
40
+ OAUTH_SCOPES: process.env.OAUTH_SCOPES || "",
37
41
  // Timeout Configuration (in milliseconds)
38
42
  OAUTH_TIMEOUT: 3e4,
39
43
  // 30 seconds
@@ -52,6 +56,12 @@ var CONFIG = {
52
56
  // Environment
53
57
  NODE_ENV: process.env.NODE_ENV || "development"
54
58
  };
59
+ function parseOAuthScopes(envScopes, defaultScopes) {
60
+ if (!envScopes || envScopes.trim() === "") {
61
+ return defaultScopes;
62
+ }
63
+ return envScopes.split(",").map((scope) => scope.trim()).filter((scope) => scope.length > 0);
64
+ }
55
65
  function isWordPressComSite(url) {
56
66
  try {
57
67
  const siteUrl = new URL(url);
@@ -64,15 +74,18 @@ function isWordPressComSite(url) {
64
74
  }
65
75
  function getRecommendedOAuthConfig(siteUrl) {
66
76
  if (isWordPressComSite(siteUrl)) {
77
+ const defaultScopes = ["global"];
67
78
  return {
68
79
  flowType: "implicit",
69
80
  usePKCE: false,
70
81
  useResourceIndicator: false,
71
82
  authorizationEndpoint: "https://public-api.wordpress.com/oauth2/authorize",
72
83
  tokenEndpoint: "https://public-api.wordpress.com/oauth2/token",
84
+ scopes: parseOAuthScopes(CONFIG.OAUTH_SCOPES, defaultScopes),
73
85
  description: "WordPress.com OAuth2 (compatible mode)"
74
86
  };
75
87
  } else {
88
+ const defaultScopes = ["read", "write"];
76
89
  return {
77
90
  flowType: "authorization_code",
78
91
  usePKCE: true,
@@ -81,6 +94,7 @@ function getRecommendedOAuthConfig(siteUrl) {
81
94
  // Will be discovered
82
95
  tokenEndpoint: void 0,
83
96
  // Will be discovered
97
+ scopes: parseOAuthScopes(CONFIG.OAUTH_SCOPES, defaultScopes),
84
98
  description: "MCP-compliant OAuth 2.1"
85
99
  };
86
100
  }
@@ -707,9 +721,11 @@ var AUTHORIZATION_CODE_HTML = `
707
721
  document.getElementById('details').textContent = message;
708
722
  }
709
723
 
710
- // Extract authorization code and state from URL query parameters (OAuth 2.1)
724
+ // Handle both OAuth 2.1 authorization code flow and implicit flow
711
725
  const urlParams = new URLSearchParams(window.location.search);
726
+ const hashParams = new URLSearchParams(window.location.hash.substring(1));
712
727
 
728
+ // Check for authorization code first (OAuth 2.1 flow)
713
729
  if (urlParams.has('code')) {
714
730
  const authData = {
715
731
  code: urlParams.get('code'),
@@ -745,13 +761,63 @@ var AUTHORIZATION_CODE_HTML = `
745
761
  displayStatus('\u274C Error completing authorization', 'error');
746
762
  displayDetails(error.message + '. Please try again.');
747
763
  });
748
- } else if (urlParams.has('error')) {
764
+ }
765
+ // Check for access token in URL fragment (implicit flow - WordPress.com)
766
+ else if (hashParams.has('access_token')) {
767
+ const tokens = {
768
+ access_token: hashParams.get('access_token'),
769
+ token_type: hashParams.get('token_type') || 'Bearer',
770
+ expires_in: hashParams.get('expires_in') ? parseInt(hashParams.get('expires_in')) : 3600,
771
+ scope: hashParams.get('scope'),
772
+ state: hashParams.get('state'),
773
+ obtained_at: Date.now()
774
+ };
775
+
776
+ // Send tokens to server for storage
777
+ fetch('/oauth/tokens', {
778
+ method: 'POST',
779
+ headers: {
780
+ 'Content-Type': 'application/json'
781
+ },
782
+ body: JSON.stringify(tokens)
783
+ })
784
+ .then(response => {
785
+ if (response.ok) {
786
+ displayStatus('\u2705 Authorization successful!', 'success');
787
+ displayDetails('WordPress.com OAuth authentication completed. You can now close this window.');
788
+ // Auto-close after 3 seconds
789
+ setTimeout(() => {
790
+ displayDetails('Closing window...');
791
+ setTimeout(() => window.close(), 1000);
792
+ }, 3000);
793
+ } else {
794
+ return response.json().then(err => {
795
+ throw new Error(err.error || 'Failed to store access token');
796
+ });
797
+ }
798
+ })
799
+ .catch(error => {
800
+ displayStatus('\u274C Error completing authorization', 'error');
801
+ displayDetails(error.message + '. Please try again.');
802
+ });
803
+ }
804
+ // Check for errors in query parameters
805
+ else if (urlParams.has('error')) {
749
806
  const error = urlParams.get('error');
750
807
  const errorDescription = urlParams.get('error_description');
751
808
  displayStatus('\u274C Authorization failed', 'error');
752
809
  displayDetails(\`Error: \${error}\${errorDescription ? ' - ' + errorDescription : ''}\`);
753
- } else {
754
- displayStatus('\u274C No authorization code received', 'error');
810
+ }
811
+ // Check for errors in URL fragment (implicit flow errors)
812
+ else if (hashParams.has('error')) {
813
+ const error = hashParams.get('error');
814
+ const errorDescription = hashParams.get('error_description');
815
+ displayStatus('\u274C Authorization failed', 'error');
816
+ displayDetails(\`Error: \${error}\${errorDescription ? ' - ' + errorDescription : ''}\`);
817
+ }
818
+ // No authorization code or access token found
819
+ else {
820
+ displayStatus('\u274C No authorization code or access token received', 'error');
755
821
  displayDetails('Please try the authorization process again.');
756
822
  }
757
823
  </script>
@@ -889,15 +955,17 @@ var MCPOAuthProvider = class {
889
955
  // Discovered metadata
890
956
  this.authServerMetadata = null;
891
957
  this.resourceMetadata = null;
958
+ const serverUrl = options.serverUrl || CONFIG.WP_API_URL;
959
+ const recommendedConfig = getRecommendedOAuthConfig(serverUrl);
892
960
  this.config = {
893
- serverUrl: options.serverUrl || CONFIG.WP_API_URL,
894
- resource: generateCanonicalResourceURI(options.serverUrl || CONFIG.WP_API_URL),
961
+ serverUrl,
962
+ resource: generateCanonicalResourceURI(serverUrl),
895
963
  responseType: "code",
896
964
  // OAuth 2.1 uses authorization code flow
897
965
  usePKCE: true,
898
966
  // PKCE is required for OAuth 2.1
899
967
  redirectUri: `http://${CONFIG.OAUTH_HOST}:${CONFIG.OAUTH_CALLBACK_PORT}/oauth/callback`,
900
- scopes: options.scopes || ["read", "write"],
968
+ scopes: options.scopes || recommendedConfig.scopes,
901
969
  clientId: options.clientId || CONFIG.WP_OAUTH_CLIENT_ID,
902
970
  callbackPort: CONFIG.OAUTH_CALLBACK_PORT,
903
971
  host: CONFIG.OAUTH_HOST,
@@ -1260,6 +1328,7 @@ import { EventEmitter as EventEmitter2 } from "node:events";
1260
1328
  var WP_PERSISTENT_CONFIG = {
1261
1329
  authorizeEndpoint: "/wp-admin/admin.php?page=mcp-oauth-authorize",
1262
1330
  scopes: ["read", "write"],
1331
+ // Default for self-hosted, will be overridden for WordPress.com
1263
1332
  callbackPort: CONFIG.OAUTH_CALLBACK_PORT,
1264
1333
  host: CONFIG.OAUTH_HOST
1265
1334
  };
@@ -1270,7 +1339,9 @@ var PersistentWPOAuthClientProvider = class {
1270
1339
  const recommendedConfig = getRecommendedOAuthConfig(options.serverUrl || "");
1271
1340
  this.options = {
1272
1341
  ...WP_PERSISTENT_CONFIG,
1273
- ...options
1342
+ ...options,
1343
+ // Use recommended scopes for the site type
1344
+ scopes: options.scopes || recommendedConfig.scopes
1274
1345
  };
1275
1346
  if (isWpCom && recommendedConfig.authorizationEndpoint) {
1276
1347
  this.options.authorizeEndpoint = recommendedConfig.authorizationEndpoint;
@@ -1911,10 +1982,11 @@ async function getOAuthTokens() {
1911
1982
  const shouldUseMCPFlow = !isWPCom && CONFIG.OAUTH_FLOW_TYPE === "authorization_code" && CONFIG.OAUTH_USE_PKCE;
1912
1983
  if (shouldUseMCPFlow) {
1913
1984
  if (!mcpOAuthProvider) {
1985
+ const recommendedConfig2 = getRecommendedOAuthConfig(serverUrl);
1914
1986
  mcpOAuthProvider = new MCPOAuthProvider({
1915
1987
  serverUrl,
1916
1988
  clientId: CONFIG.WP_OAUTH_CLIENT_ID,
1917
- scopes: ["read", "write"]
1989
+ scopes: recommendedConfig2.scopes
1918
1990
  });
1919
1991
  }
1920
1992
  logger.auth("Using MCP-compliant OAuth 2.1 authorization code flow with PKCE");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/mcp-wordpress-remote",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "MCP WordPress Remote proxy server",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"