@automattic/mcp-wordpress-remote 0.2.21 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -17
- package/dist/lib.d.ts +4 -2
- package/dist/lib.js +299 -50
- package/dist/proxy.js +299 -50
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ Add to your MCP client configuration (e.g., Claude Desktop's `claude_desktop_con
|
|
|
38
38
|
"command": "npx",
|
|
39
39
|
"args": ["-y", "@automattic/mcp-wordpress-remote"],
|
|
40
40
|
"env": {
|
|
41
|
-
"WP_API_URL": "https://your-wordpress-site.com"
|
|
41
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server"
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -58,7 +58,7 @@ You can add custom headers to all API requests using the `CUSTOM_HEADERS` enviro
|
|
|
58
58
|
"command": "npx",
|
|
59
59
|
"args": ["-y", "@automattic/mcp-wordpress-remote"],
|
|
60
60
|
"env": {
|
|
61
|
-
"WP_API_URL": "https://your-wordpress-site.com",
|
|
61
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server",
|
|
62
62
|
"CUSTOM_HEADERS": "{\"X-MCP-API-Key\": \"*Ibo7tweixlbfuwaiufxgakjyefctwajcetb*\", \"X-Custom-Header\": \"value\"}"
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -75,7 +75,7 @@ You can add custom headers to all API requests using the `CUSTOM_HEADERS` enviro
|
|
|
75
75
|
"command": "npx",
|
|
76
76
|
"args": ["-y", "@automattic/mcp-wordpress-remote"],
|
|
77
77
|
"env": {
|
|
78
|
-
"WP_API_URL": "https://your-wordpress-site.com",
|
|
78
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server",
|
|
79
79
|
"CUSTOM_HEADERS": "X-MCP-API-Key:IOskncfyes78U8on3q7ry43o487tybrc,X-Custom-Header:value"
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -87,7 +87,7 @@ You can add custom headers to all API requests using the `CUSTOM_HEADERS` enviro
|
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
CUSTOM_HEADERS='{"X-MCP-API-Key": "wc_mcp_FaQduhQcW0mfVaZgP3yaaqDuXaZ3mw7j"}' \
|
|
90
|
-
WP_API_URL="https://your-site.com" \
|
|
90
|
+
WP_API_URL="https://your-site.com/wp-json/mcp/mcp-adapter-default-server" \
|
|
91
91
|
npx @automattic/mcp-wordpress-remote
|
|
92
92
|
```
|
|
93
93
|
|
|
@@ -109,7 +109,11 @@ Custom headers are included in:
|
|
|
109
109
|
|
|
110
110
|
## WordPress MCP Plugin
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
Install the [MCP Adapter](https://github.com/WordPress/mcp-adapter) plugin on your WordPress site. Once active, it registers a default MCP server at `/wp-json/mcp/mcp-adapter-default-server` — set `WP_API_URL` to that full URL (examples throughout this README show the pattern). To target a custom server id or namespace, pass the full URL of that server instead.
|
|
113
|
+
|
|
114
|
+
### Legacy `wordpress-mcp` plugin
|
|
115
|
+
|
|
116
|
+
Earlier versions of this proxy were designed for [`Automattic/wordpress-mcp`](https://github.com/Automattic/wordpress-mcp), which exposes its endpoint at `/wp-json/wp/v2/wpmcp`. That plugin is deprecated in favor of `mcp-adapter`, but existing installs continue to work — for backwards compatibility, a bare-domain `WP_API_URL` (e.g. `https://your-wordpress-site.com`) still resolves to the `wordpress-mcp` endpoint. New setups should install `mcp-adapter` and use the full URL shown above.
|
|
113
117
|
|
|
114
118
|
## Authentication Methods
|
|
115
119
|
|
|
@@ -126,7 +130,7 @@ OAuth 2.1 provides the most secure and user-friendly experience with full MCP Au
|
|
|
126
130
|
"command": "npx",
|
|
127
131
|
"args": ["-y", "@automattic/mcp-wordpress-remote"],
|
|
128
132
|
"env": {
|
|
129
|
-
"WP_API_URL": "https://your-wordpress-site.com",
|
|
133
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server",
|
|
130
134
|
"OAUTH_ENABLED": "true"
|
|
131
135
|
}
|
|
132
136
|
}
|
|
@@ -163,7 +167,7 @@ For server-to-server authentication or when OAuth is not available.
|
|
|
163
167
|
"command": "npx",
|
|
164
168
|
"args": ["-y", "@automattic/mcp-wordpress-remote"],
|
|
165
169
|
"env": {
|
|
166
|
-
"WP_API_URL": "https://your-wordpress-site.com",
|
|
170
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server",
|
|
167
171
|
"JWT_TOKEN": "your-jwt-token-here"
|
|
168
172
|
}
|
|
169
173
|
}
|
|
@@ -182,7 +186,7 @@ Uses WordPress username and application password for basic authentication.
|
|
|
182
186
|
"command": "npx",
|
|
183
187
|
"args": ["-y", "@automattic/mcp-wordpress-remote"],
|
|
184
188
|
"env": {
|
|
185
|
-
"WP_API_URL": "https://your-wordpress-site.com",
|
|
189
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server",
|
|
186
190
|
"WP_API_USERNAME": "your-username",
|
|
187
191
|
"WP_API_PASSWORD": "your-application-password",
|
|
188
192
|
"OAUTH_ENABLED": "false"
|
|
@@ -210,7 +214,7 @@ To create an application password:
|
|
|
210
214
|
"command": "npx",
|
|
211
215
|
"args": ["-y", "@automattic/mcp-wordpress-remote"],
|
|
212
216
|
"env": {
|
|
213
|
-
"WP_API_URL": "https://your-wordpress-site.com",
|
|
217
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server",
|
|
214
218
|
"OAUTH_CALLBACK_PORT": "7665",
|
|
215
219
|
"OAUTH_HOST": "127.0.0.1",
|
|
216
220
|
"WP_OAUTH_CLIENT_ID": "your-custom-client-id"
|
|
@@ -231,7 +235,7 @@ For WooCommerce-specific tools and reports:
|
|
|
231
235
|
"command": "npx",
|
|
232
236
|
"args": ["-y", "@automattic/mcp-wordpress-remote"],
|
|
233
237
|
"env": {
|
|
234
|
-
"WP_API_URL": "https://your-wordpress-site.com",
|
|
238
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server",
|
|
235
239
|
"WOO_CUSTOMER_KEY": "ck_your-consumer-key",
|
|
236
240
|
"WOO_CUSTOMER_SECRET": "cs_your-consumer-secret"
|
|
237
241
|
}
|
|
@@ -244,7 +248,7 @@ For WooCommerce-specific tools and reports:
|
|
|
244
248
|
|
|
245
249
|
| Variable | Description | Default | Required |
|
|
246
250
|
| ----------------------------- | ------------------------------------------------ | -------------------- | --------------------- |
|
|
247
|
-
| `WP_API_URL` | WordPress site URL
|
|
251
|
+
| `WP_API_URL` | WordPress site URL including the MCP endpoint path (e.g. `…/wp-json/mcp/mcp-adapter-default-server`). A bare domain resolves to the deprecated `wordpress-mcp` endpoint for backwards compatibility. | - | ✅ |
|
|
248
252
|
| `OAUTH_ENABLED` | Enable OAuth authentication | `true` | - |
|
|
249
253
|
| `OAUTH_CALLBACK_PORT` | OAuth callback port | `7665` | - |
|
|
250
254
|
| `OAUTH_HOST` | OAuth callback hostname | `127.0.0.1` | - |
|
|
@@ -317,7 +321,7 @@ Configure your MCP client to use the local version:
|
|
|
317
321
|
"command": "node",
|
|
318
322
|
"args": ["/path/to/your/mcp-wordpress-remote/dist/proxy.js"],
|
|
319
323
|
"env": {
|
|
320
|
-
"WP_API_URL": "https://your-wordpress-site.com"
|
|
324
|
+
"WP_API_URL": "https://your-wordpress-site.com/wp-json/mcp/mcp-adapter-default-server"
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
327
|
}
|
|
@@ -403,9 +407,8 @@ If you see "waiting for other instance" messages, this is normal behavior.
|
|
|
403
407
|
|
|
404
408
|
**API endpoint not found:**
|
|
405
409
|
|
|
406
|
-
- Verify
|
|
407
|
-
-
|
|
408
|
-
- Confirm `WP_API_URL` is correct
|
|
410
|
+
- Verify the [MCP Adapter](https://github.com/WordPress/mcp-adapter) plugin is installed and active
|
|
411
|
+
- Confirm `WP_API_URL` points at the server route (e.g. `…/wp-json/mcp/mcp-adapter-default-server`)
|
|
409
412
|
|
|
410
413
|
**Permission denied:**
|
|
411
414
|
|
|
@@ -469,7 +472,7 @@ Log levels:
|
|
|
469
472
|
## Requirements
|
|
470
473
|
|
|
471
474
|
- **Node.js 22+** (required for fetch API support)
|
|
472
|
-
- **WordPress site** with [
|
|
475
|
+
- **WordPress site** with the [MCP Adapter](https://github.com/WordPress/mcp-adapter) plugin installed and active
|
|
473
476
|
- **WordPress user account** with appropriate permissions
|
|
474
477
|
|
|
475
478
|
## License
|
|
@@ -484,7 +487,7 @@ Contributions welcome! This project is maintained by Automattic Inc.
|
|
|
484
487
|
|
|
485
488
|
- **Issues:** [GitHub Issues](https://github.com/Automattic/mcp-wordpress-remote/issues)
|
|
486
489
|
- **Documentation:** Check the troubleshooting section above
|
|
487
|
-
- **WordPress MCP Plugin:** [
|
|
490
|
+
- **WordPress MCP Plugin:** [WordPress/mcp-adapter](https://github.com/WordPress/mcp-adapter)
|
|
488
491
|
|
|
489
492
|
---
|
|
490
493
|
|
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.
|
|
18
|
+
declare const MCP_WORDPRESS_REMOTE_VERSION = "0.3.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
|
|
@@ -149,7 +149,9 @@ declare const logger: {
|
|
|
149
149
|
config: (message: string, level?: LogLevel, ...args: any[]) => void;
|
|
150
150
|
};
|
|
151
151
|
|
|
152
|
-
declare function wpRequest(requestData: any, useJsonRpc?: boolean
|
|
152
|
+
declare function wpRequest(requestData: any, useJsonRpc?: boolean, options?: {
|
|
153
|
+
allowSessionRecovery?: boolean;
|
|
154
|
+
}): Promise<WordPressResponse>;
|
|
153
155
|
|
|
154
156
|
/**
|
|
155
157
|
* WordPress OAuth types and error definitions
|
package/dist/lib.js
CHANGED
|
@@ -91,13 +91,13 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
91
91
|
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, factory(global2.WebStreamsPolyfill = {}));
|
|
92
92
|
})(exports, function(exports2) {
|
|
93
93
|
"use strict";
|
|
94
|
-
function
|
|
94
|
+
function noop3() {
|
|
95
95
|
return void 0;
|
|
96
96
|
}
|
|
97
97
|
function typeIsObject(x2) {
|
|
98
98
|
return typeof x2 === "object" && x2 !== null || typeof x2 === "function";
|
|
99
99
|
}
|
|
100
|
-
const rethrowAssertionErrorRejection =
|
|
100
|
+
const rethrowAssertionErrorRejection = noop3;
|
|
101
101
|
function setFunctionName(fn, name) {
|
|
102
102
|
try {
|
|
103
103
|
Object.defineProperty(fn, "name", {
|
|
@@ -2726,7 +2726,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
2726
2726
|
return newPromise((resolveRead, rejectRead) => {
|
|
2727
2727
|
ReadableStreamDefaultReaderRead(reader, {
|
|
2728
2728
|
_chunkSteps: (chunk) => {
|
|
2729
|
-
currentWrite = PerformPromiseThen(WritableStreamDefaultWriterWrite(writer, chunk), void 0,
|
|
2729
|
+
currentWrite = PerformPromiseThen(WritableStreamDefaultWriterWrite(writer, chunk), void 0, noop3);
|
|
2730
2730
|
resolveRead(false);
|
|
2731
2731
|
},
|
|
2732
2732
|
_closeSteps: () => resolveRead(true),
|
|
@@ -3407,7 +3407,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
3407
3407
|
function ReadableStreamFromIterable(asyncIterable) {
|
|
3408
3408
|
let stream;
|
|
3409
3409
|
const iteratorRecord = GetIterator(asyncIterable, "async");
|
|
3410
|
-
const startAlgorithm =
|
|
3410
|
+
const startAlgorithm = noop3;
|
|
3411
3411
|
function pullAlgorithm() {
|
|
3412
3412
|
let nextResult;
|
|
3413
3413
|
try {
|
|
@@ -3459,7 +3459,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
3459
3459
|
}
|
|
3460
3460
|
function ReadableStreamFromDefaultReader(reader) {
|
|
3461
3461
|
let stream;
|
|
3462
|
-
const startAlgorithm =
|
|
3462
|
+
const startAlgorithm = noop3;
|
|
3463
3463
|
function pullAlgorithm() {
|
|
3464
3464
|
let readPromise;
|
|
3465
3465
|
try {
|
|
@@ -3778,7 +3778,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
3778
3778
|
});
|
|
3779
3779
|
}
|
|
3780
3780
|
const sourceCancelPromise = stream._readableStreamController[CancelSteps](reason);
|
|
3781
|
-
return transformPromiseWith(sourceCancelPromise,
|
|
3781
|
+
return transformPromiseWith(sourceCancelPromise, noop3);
|
|
3782
3782
|
}
|
|
3783
3783
|
function ReadableStreamClose(stream) {
|
|
3784
3784
|
stream._state = "closed";
|
|
@@ -4999,7 +4999,7 @@ async function toFormData(Body2, ct) {
|
|
|
4999
4999
|
parser.end();
|
|
5000
5000
|
return formData;
|
|
5001
5001
|
}
|
|
5002
|
-
var s, S, f2, F, LF, CR, SPACE, HYPHEN, COLON, A, Z, lower,
|
|
5002
|
+
var s, S, f2, F, LF, CR, SPACE, HYPHEN, COLON, A, Z, lower, noop2, MultipartParser;
|
|
5003
5003
|
var init_multipart_parser = __esm({
|
|
5004
5004
|
"node_modules/node-fetch/src/utils/multipart-parser.js"() {
|
|
5005
5005
|
"use strict";
|
|
@@ -5031,7 +5031,7 @@ var init_multipart_parser = __esm({
|
|
|
5031
5031
|
A = 97;
|
|
5032
5032
|
Z = 122;
|
|
5033
5033
|
lower = (c) => c | 32;
|
|
5034
|
-
|
|
5034
|
+
noop2 = () => {
|
|
5035
5035
|
};
|
|
5036
5036
|
MultipartParser = class {
|
|
5037
5037
|
/**
|
|
@@ -5040,13 +5040,13 @@ var init_multipart_parser = __esm({
|
|
|
5040
5040
|
constructor(boundary) {
|
|
5041
5041
|
this.index = 0;
|
|
5042
5042
|
this.flags = 0;
|
|
5043
|
-
this.onHeaderEnd =
|
|
5044
|
-
this.onHeaderField =
|
|
5045
|
-
this.onHeadersEnd =
|
|
5046
|
-
this.onHeaderValue =
|
|
5047
|
-
this.onPartBegin =
|
|
5048
|
-
this.onPartData =
|
|
5049
|
-
this.onPartEnd =
|
|
5043
|
+
this.onHeaderEnd = noop2;
|
|
5044
|
+
this.onHeaderField = noop2;
|
|
5045
|
+
this.onHeadersEnd = noop2;
|
|
5046
|
+
this.onHeaderValue = noop2;
|
|
5047
|
+
this.onPartBegin = noop2;
|
|
5048
|
+
this.onPartData = noop2;
|
|
5049
|
+
this.onPartEnd = noop2;
|
|
5050
5050
|
this.boundaryChars = {};
|
|
5051
5051
|
boundary = "\r\n--" + boundary;
|
|
5052
5052
|
const ui8a = new Uint8Array(boundary.length);
|
|
@@ -7881,7 +7881,7 @@ var require_destroy = __commonJS({
|
|
|
7881
7881
|
}
|
|
7882
7882
|
if (isEventEmitter(stream) && suppress) {
|
|
7883
7883
|
stream.removeAllListeners("error");
|
|
7884
|
-
stream.addListener("error",
|
|
7884
|
+
stream.addListener("error", noop3);
|
|
7885
7885
|
}
|
|
7886
7886
|
return stream;
|
|
7887
7887
|
}
|
|
@@ -7936,7 +7936,7 @@ var require_destroy = __commonJS({
|
|
|
7936
7936
|
function isZlibStream(stream) {
|
|
7937
7937
|
return stream instanceof Zlib.Gzip || stream instanceof Zlib.Gunzip || stream instanceof Zlib.Deflate || stream instanceof Zlib.DeflateRaw || stream instanceof Zlib.Inflate || stream instanceof Zlib.InflateRaw || stream instanceof Zlib.Unzip;
|
|
7938
7938
|
}
|
|
7939
|
-
function
|
|
7939
|
+
function noop3() {
|
|
7940
7940
|
}
|
|
7941
7941
|
function onDrainClearBinding() {
|
|
7942
7942
|
this._binding.clear();
|
|
@@ -30288,6 +30288,109 @@ var require_express2 = __commonJS({
|
|
|
30288
30288
|
}
|
|
30289
30289
|
});
|
|
30290
30290
|
|
|
30291
|
+
// node_modules/eventsource-parser/dist/index.js
|
|
30292
|
+
var ParseError = class extends Error {
|
|
30293
|
+
constructor(message, options) {
|
|
30294
|
+
super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
|
|
30295
|
+
}
|
|
30296
|
+
};
|
|
30297
|
+
function noop(_arg) {
|
|
30298
|
+
}
|
|
30299
|
+
function createParser(callbacks) {
|
|
30300
|
+
if (typeof callbacks == "function")
|
|
30301
|
+
throw new TypeError(
|
|
30302
|
+
"`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?"
|
|
30303
|
+
);
|
|
30304
|
+
const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
|
|
30305
|
+
let incompleteLine = "", isFirstChunk = true, id, data = "", eventType = "";
|
|
30306
|
+
function feed(newChunk) {
|
|
30307
|
+
const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
|
|
30308
|
+
for (const line of complete)
|
|
30309
|
+
parseLine(line);
|
|
30310
|
+
incompleteLine = incomplete, isFirstChunk = false;
|
|
30311
|
+
}
|
|
30312
|
+
function parseLine(line) {
|
|
30313
|
+
if (line === "") {
|
|
30314
|
+
dispatchEvent();
|
|
30315
|
+
return;
|
|
30316
|
+
}
|
|
30317
|
+
if (line.startsWith(":")) {
|
|
30318
|
+
onComment && onComment(line.slice(line.startsWith(": ") ? 2 : 1));
|
|
30319
|
+
return;
|
|
30320
|
+
}
|
|
30321
|
+
const fieldSeparatorIndex = line.indexOf(":");
|
|
30322
|
+
if (fieldSeparatorIndex !== -1) {
|
|
30323
|
+
const field = line.slice(0, fieldSeparatorIndex), offset = line[fieldSeparatorIndex + 1] === " " ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
|
|
30324
|
+
processField(field, value, line);
|
|
30325
|
+
return;
|
|
30326
|
+
}
|
|
30327
|
+
processField(line, "", line);
|
|
30328
|
+
}
|
|
30329
|
+
function processField(field, value, line) {
|
|
30330
|
+
switch (field) {
|
|
30331
|
+
case "event":
|
|
30332
|
+
eventType = value;
|
|
30333
|
+
break;
|
|
30334
|
+
case "data":
|
|
30335
|
+
data = `${data}${value}
|
|
30336
|
+
`;
|
|
30337
|
+
break;
|
|
30338
|
+
case "id":
|
|
30339
|
+
id = value.includes("\0") ? void 0 : value;
|
|
30340
|
+
break;
|
|
30341
|
+
case "retry":
|
|
30342
|
+
/^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
|
|
30343
|
+
new ParseError(`Invalid \`retry\` value: "${value}"`, {
|
|
30344
|
+
type: "invalid-retry",
|
|
30345
|
+
value,
|
|
30346
|
+
line
|
|
30347
|
+
})
|
|
30348
|
+
);
|
|
30349
|
+
break;
|
|
30350
|
+
default:
|
|
30351
|
+
onError(
|
|
30352
|
+
new ParseError(
|
|
30353
|
+
`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
|
|
30354
|
+
{ type: "unknown-field", field, value, line }
|
|
30355
|
+
)
|
|
30356
|
+
);
|
|
30357
|
+
break;
|
|
30358
|
+
}
|
|
30359
|
+
}
|
|
30360
|
+
function dispatchEvent() {
|
|
30361
|
+
data.length > 0 && onEvent({
|
|
30362
|
+
id,
|
|
30363
|
+
event: eventType || void 0,
|
|
30364
|
+
// If the data buffer's last character is a U+000A LINE FEED (LF) character,
|
|
30365
|
+
// then remove the last character from the data buffer.
|
|
30366
|
+
data: data.endsWith(`
|
|
30367
|
+
`) ? data.slice(0, -1) : data
|
|
30368
|
+
}), id = void 0, data = "", eventType = "";
|
|
30369
|
+
}
|
|
30370
|
+
function reset(options = {}) {
|
|
30371
|
+
incompleteLine && options.consume && parseLine(incompleteLine), isFirstChunk = true, id = void 0, data = "", eventType = "", incompleteLine = "";
|
|
30372
|
+
}
|
|
30373
|
+
return { feed, reset };
|
|
30374
|
+
}
|
|
30375
|
+
function splitLines(chunk) {
|
|
30376
|
+
const lines = [];
|
|
30377
|
+
let incompleteLine = "", searchIndex = 0;
|
|
30378
|
+
for (; searchIndex < chunk.length; ) {
|
|
30379
|
+
const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
|
|
30380
|
+
`, searchIndex);
|
|
30381
|
+
let lineEnd = -1;
|
|
30382
|
+
if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = Math.min(crIndex, lfIndex) : crIndex !== -1 ? lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1) {
|
|
30383
|
+
incompleteLine = chunk.slice(searchIndex);
|
|
30384
|
+
break;
|
|
30385
|
+
} else {
|
|
30386
|
+
const line = chunk.slice(searchIndex, lineEnd);
|
|
30387
|
+
lines.push(line), searchIndex = lineEnd + 1, chunk[searchIndex - 1] === "\r" && chunk[searchIndex] === `
|
|
30388
|
+
` && searchIndex++;
|
|
30389
|
+
}
|
|
30390
|
+
}
|
|
30391
|
+
return [lines, incompleteLine];
|
|
30392
|
+
}
|
|
30393
|
+
|
|
30291
30394
|
// node_modules/zod/lib/index.mjs
|
|
30292
30395
|
var util;
|
|
30293
30396
|
(function(util2) {
|
|
@@ -35153,10 +35256,9 @@ import crypto3 from "crypto";
|
|
|
35153
35256
|
import path from "path";
|
|
35154
35257
|
import fs from "fs/promises";
|
|
35155
35258
|
import crypto2 from "crypto";
|
|
35156
|
-
var VERSION = MCP_WORDPRESS_REMOTE_VERSION;
|
|
35157
35259
|
function getConfigDir() {
|
|
35158
35260
|
const baseConfigDir = CONFIG.WP_MCP_CONFIG_DIR;
|
|
35159
|
-
return path.join(baseConfigDir, `wordpress-remote-${
|
|
35261
|
+
return path.join(baseConfigDir, `wordpress-remote-${MCP_WORDPRESS_REMOTE_VERSION}`);
|
|
35160
35262
|
}
|
|
35161
35263
|
async function ensureConfigDir() {
|
|
35162
35264
|
try {
|
|
@@ -35346,7 +35448,7 @@ function calculateDefaultPort(serverUrlHash) {
|
|
|
35346
35448
|
}
|
|
35347
35449
|
async function findExistingClientPort(serverUrlHash) {
|
|
35348
35450
|
const clientInfo = await readJsonFile(serverUrlHash, "client_info.json");
|
|
35349
|
-
if (!clientInfo) {
|
|
35451
|
+
if (!clientInfo || !clientInfo.redirect_uris) {
|
|
35350
35452
|
return void 0;
|
|
35351
35453
|
}
|
|
35352
35454
|
const localhostRedirectUri = clientInfo.redirect_uris.map((uri) => {
|
|
@@ -35382,7 +35484,7 @@ async function selectCallbackPort(serverUrl, specifiedPort, _unused) {
|
|
|
35382
35484
|
}
|
|
35383
35485
|
|
|
35384
35486
|
// src/lib/config.ts
|
|
35385
|
-
var MCP_WORDPRESS_REMOTE_VERSION = "0.
|
|
35487
|
+
var MCP_WORDPRESS_REMOTE_VERSION = "0.3.0";
|
|
35386
35488
|
var CONFIG = {
|
|
35387
35489
|
// API Configuration
|
|
35388
35490
|
WP_API_URL: process.env.WP_API_URL || "https://example.com",
|
|
@@ -36889,17 +36991,14 @@ var OAuthCallbackServer = class {
|
|
|
36889
36991
|
});
|
|
36890
36992
|
}
|
|
36891
36993
|
async stop() {
|
|
36892
|
-
|
|
36893
|
-
|
|
36894
|
-
|
|
36895
|
-
|
|
36896
|
-
|
|
36897
|
-
|
|
36898
|
-
});
|
|
36899
|
-
} else {
|
|
36900
|
-
resolve();
|
|
36901
|
-
}
|
|
36994
|
+
if (!this.server) return;
|
|
36995
|
+
const server = this.server;
|
|
36996
|
+
this.server = null;
|
|
36997
|
+
server.close((err) => {
|
|
36998
|
+
if (err) logger.error("OAuth callback server close error", "OAUTH", err);
|
|
36999
|
+
else logger.oauth("OAuth callback server stopped");
|
|
36902
37000
|
});
|
|
37001
|
+
server.closeAllConnections?.();
|
|
36903
37002
|
}
|
|
36904
37003
|
getCallbackUrl() {
|
|
36905
37004
|
return `http://${this.options.host}:${this.options.port}/oauth/callback`;
|
|
@@ -37904,6 +38003,12 @@ var mcpOAuthProvider = null;
|
|
|
37904
38003
|
var authCoordinator = null;
|
|
37905
38004
|
var globalEvents = null;
|
|
37906
38005
|
var globalSessionId = null;
|
|
38006
|
+
var lastInitializeRequest = null;
|
|
38007
|
+
var sessionRefreshPromise = null;
|
|
38008
|
+
var WP_MCP_ENDPOINT = "/wp/v2/wpmcp";
|
|
38009
|
+
var INVALID_SESSION_ERROR_CODES = /* @__PURE__ */ new Set([-32602, -32005]);
|
|
38010
|
+
var INVALID_SESSION_ERROR_MESSAGE = "Invalid or expired session";
|
|
38011
|
+
var SESSION_NOT_FOUND_ERROR_MESSAGE = "Session not found";
|
|
37907
38012
|
function validateEnvironment() {
|
|
37908
38013
|
const validation = validateConfig();
|
|
37909
38014
|
if (!validation.isValid) {
|
|
@@ -37916,6 +38021,24 @@ function validateEnvironment() {
|
|
|
37916
38021
|
function removeTrailingSlash(url) {
|
|
37917
38022
|
return url.replace(/\/+$/, "");
|
|
37918
38023
|
}
|
|
38024
|
+
function parseSSEMessage(text) {
|
|
38025
|
+
let result;
|
|
38026
|
+
let found = false;
|
|
38027
|
+
const parser = createParser({
|
|
38028
|
+
onEvent(event) {
|
|
38029
|
+
if (found) return;
|
|
38030
|
+
if (!event.event || event.event === "message") {
|
|
38031
|
+
result = JSON.parse(event.data);
|
|
38032
|
+
found = true;
|
|
38033
|
+
}
|
|
38034
|
+
}
|
|
38035
|
+
});
|
|
38036
|
+
parser.feed(text);
|
|
38037
|
+
if (!found) {
|
|
38038
|
+
throw new Error('No "message" event with data found in SSE response');
|
|
38039
|
+
}
|
|
38040
|
+
return result;
|
|
38041
|
+
}
|
|
37919
38042
|
function constructApiUrl(baseUrl, defaultEndpoint) {
|
|
37920
38043
|
const cleanUrl = removeTrailingSlash(baseUrl);
|
|
37921
38044
|
try {
|
|
@@ -38002,9 +38125,53 @@ async function getOAuthTokens() {
|
|
|
38002
38125
|
return null;
|
|
38003
38126
|
}
|
|
38004
38127
|
}
|
|
38005
|
-
|
|
38006
|
-
|
|
38007
|
-
|
|
38128
|
+
function getCurrentApiUrl() {
|
|
38129
|
+
return process.env.WP_API_URL || CONFIG.WP_API_URL;
|
|
38130
|
+
}
|
|
38131
|
+
function getRequestUrl() {
|
|
38132
|
+
return constructApiUrl(getCurrentApiUrl(), WP_MCP_ENDPOINT);
|
|
38133
|
+
}
|
|
38134
|
+
function cloneRequestData(requestData) {
|
|
38135
|
+
return JSON.parse(JSON.stringify(requestData));
|
|
38136
|
+
}
|
|
38137
|
+
function isInitializeRequest(requestData) {
|
|
38138
|
+
return requestData?.method === "initialize";
|
|
38139
|
+
}
|
|
38140
|
+
function cacheInitializeRequest(requestData, useJsonRpc) {
|
|
38141
|
+
lastInitializeRequest = {
|
|
38142
|
+
requestData: cloneRequestData(requestData),
|
|
38143
|
+
useJsonRpc
|
|
38144
|
+
};
|
|
38145
|
+
}
|
|
38146
|
+
function parseApiErrorResponse(error) {
|
|
38147
|
+
if (!error.response) {
|
|
38148
|
+
return null;
|
|
38149
|
+
}
|
|
38150
|
+
if (typeof error.response === "string") {
|
|
38151
|
+
try {
|
|
38152
|
+
return JSON.parse(error.response);
|
|
38153
|
+
} catch {
|
|
38154
|
+
return null;
|
|
38155
|
+
}
|
|
38156
|
+
}
|
|
38157
|
+
return error.response;
|
|
38158
|
+
}
|
|
38159
|
+
function isInvalidSessionError(error) {
|
|
38160
|
+
const errorResponse = parseApiErrorResponse(error);
|
|
38161
|
+
const jsonRpcError = errorResponse?.error && typeof errorResponse.error === "object" ? errorResponse.error : errorResponse;
|
|
38162
|
+
const code = typeof jsonRpcError?.code === "number" ? jsonRpcError.code : null;
|
|
38163
|
+
const message = typeof jsonRpcError?.message === "string" ? jsonRpcError.message : "";
|
|
38164
|
+
return code !== null && INVALID_SESSION_ERROR_CODES.has(code) && (message === INVALID_SESSION_ERROR_MESSAGE || message.includes(SESSION_NOT_FOUND_ERROR_MESSAGE) || message.includes(INVALID_SESSION_ERROR_MESSAGE));
|
|
38165
|
+
}
|
|
38166
|
+
function updateSessionId(sessionId) {
|
|
38167
|
+
if (globalSessionId === sessionId) {
|
|
38168
|
+
return;
|
|
38169
|
+
}
|
|
38170
|
+
globalSessionId = sessionId;
|
|
38171
|
+
logger.info(`Session ID received from WordPress: ${globalSessionId}`, "SESSION");
|
|
38172
|
+
}
|
|
38173
|
+
async function executeWordPressRequest(requestData, useJsonRpc, sessionIdUsed) {
|
|
38174
|
+
const url = getRequestUrl();
|
|
38008
38175
|
const method = "POST";
|
|
38009
38176
|
if (useJsonRpc) {
|
|
38010
38177
|
logger.api(`Request method: ${requestData.method || "unknown"} (JSON-RPC)`);
|
|
@@ -38063,13 +38230,12 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38063
38230
|
"NO_AUTH_METHOD"
|
|
38064
38231
|
);
|
|
38065
38232
|
}
|
|
38066
|
-
const currentApiUrl = process.env.WP_API_URL || CONFIG.WP_API_URL;
|
|
38067
38233
|
logger.debug(`Environment: ${CONFIG.NODE_ENV}`, "API");
|
|
38068
|
-
logger.debug(`Base API URL: ${
|
|
38069
|
-
const url = constructApiUrl(currentApiUrl, endpoint);
|
|
38234
|
+
logger.debug(`Base API URL: ${getCurrentApiUrl()}`, "API");
|
|
38070
38235
|
logger.debug(`Final requesting URL: ${url}`, "API");
|
|
38071
38236
|
const headers = {
|
|
38072
38237
|
"Content-Type": "application/json",
|
|
38238
|
+
Accept: "application/json, text/event-stream",
|
|
38073
38239
|
"MCP-Protocol-Version": "2025-06-18",
|
|
38074
38240
|
// MCP protocol version
|
|
38075
38241
|
...customHeaders
|
|
@@ -38078,8 +38244,8 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38078
38244
|
if (authHeader) {
|
|
38079
38245
|
headers.Authorization = authHeader;
|
|
38080
38246
|
}
|
|
38081
|
-
if (
|
|
38082
|
-
headers["Mcp-Session-Id"] =
|
|
38247
|
+
if (sessionIdUsed) {
|
|
38248
|
+
headers["Mcp-Session-Id"] = sessionIdUsed;
|
|
38083
38249
|
}
|
|
38084
38250
|
if (authHeader) {
|
|
38085
38251
|
logger.debug("Using Authorization header for authentication", "API");
|
|
@@ -38103,21 +38269,27 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38103
38269
|
logger.debug(`Request method: ${method}`, "API");
|
|
38104
38270
|
const response = await proxyFetch(url, fetchOptions);
|
|
38105
38271
|
logger.debug(`Response status: ${response.status}`, "API");
|
|
38272
|
+
const rawBody = await response.text();
|
|
38106
38273
|
if (!response.ok) {
|
|
38107
|
-
|
|
38108
|
-
logger.error(`API error response: ${errorText}`, "API");
|
|
38274
|
+
logger.error(`API error response: ${rawBody}`, "API");
|
|
38109
38275
|
throw new APIError(
|
|
38110
|
-
`WordPress API error (${response.status}): ${
|
|
38276
|
+
`WordPress API error (${response.status}): ${rawBody}`,
|
|
38111
38277
|
response.status,
|
|
38112
38278
|
url,
|
|
38113
|
-
|
|
38279
|
+
rawBody
|
|
38114
38280
|
);
|
|
38115
38281
|
}
|
|
38116
|
-
const
|
|
38282
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
38283
|
+
let responseData;
|
|
38284
|
+
if (contentType.includes("text/event-stream")) {
|
|
38285
|
+
responseData = parseSSEMessage(rawBody);
|
|
38286
|
+
logger.debug("Parsed text/event-stream response body", "API");
|
|
38287
|
+
} else {
|
|
38288
|
+
responseData = JSON.parse(rawBody);
|
|
38289
|
+
}
|
|
38117
38290
|
const sessionIdHeader = response.headers.get("Mcp-Session-Id");
|
|
38118
|
-
if (sessionIdHeader
|
|
38119
|
-
|
|
38120
|
-
logger.info(`Session ID received from WordPress: ${globalSessionId}`, "SESSION");
|
|
38291
|
+
if (sessionIdHeader) {
|
|
38292
|
+
updateSessionId(sessionIdHeader);
|
|
38121
38293
|
}
|
|
38122
38294
|
logger.api("Response received successfully");
|
|
38123
38295
|
logger.debug(`Response data: ${JSON.stringify(responseData)}`, "API");
|
|
@@ -38130,14 +38302,20 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38130
38302
|
`WordPress JSON-RPC error: ${jsonrpcResponse.error.message}`,
|
|
38131
38303
|
jsonrpcResponse.error.code || 500,
|
|
38132
38304
|
url,
|
|
38133
|
-
|
|
38305
|
+
jsonrpcResponse.error
|
|
38134
38306
|
);
|
|
38135
38307
|
} else if (jsonrpcResponse.result !== void 0) {
|
|
38136
|
-
return
|
|
38308
|
+
return {
|
|
38309
|
+
responseData: jsonrpcResponse.result,
|
|
38310
|
+
sessionIdUsed
|
|
38311
|
+
};
|
|
38137
38312
|
}
|
|
38138
38313
|
}
|
|
38139
38314
|
}
|
|
38140
|
-
return
|
|
38315
|
+
return {
|
|
38316
|
+
responseData,
|
|
38317
|
+
sessionIdUsed
|
|
38318
|
+
};
|
|
38141
38319
|
} catch (error) {
|
|
38142
38320
|
if (error instanceof APIError) {
|
|
38143
38321
|
throw error;
|
|
@@ -38147,6 +38325,77 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38147
38325
|
throw new APIError(errorMessage, 0, url);
|
|
38148
38326
|
}
|
|
38149
38327
|
}
|
|
38328
|
+
async function refreshSession(failedSessionId) {
|
|
38329
|
+
if (failedSessionId && globalSessionId && globalSessionId !== failedSessionId) {
|
|
38330
|
+
logger.info("Detected newer session while handling invalid-session error; skipping refresh", "SESSION");
|
|
38331
|
+
return;
|
|
38332
|
+
}
|
|
38333
|
+
if (sessionRefreshPromise) {
|
|
38334
|
+
logger.info("Waiting for in-flight WordPress session refresh", "SESSION");
|
|
38335
|
+
await sessionRefreshPromise;
|
|
38336
|
+
return;
|
|
38337
|
+
}
|
|
38338
|
+
if (!lastInitializeRequest) {
|
|
38339
|
+
throw new APIError(
|
|
38340
|
+
"Cannot refresh WordPress session before initialize has completed",
|
|
38341
|
+
0,
|
|
38342
|
+
getRequestUrl()
|
|
38343
|
+
);
|
|
38344
|
+
}
|
|
38345
|
+
sessionRefreshPromise = (async () => {
|
|
38346
|
+
logger.warn("WordPress session rejected; refreshing session via initialize", "SESSION");
|
|
38347
|
+
globalSessionId = null;
|
|
38348
|
+
await executeWordPressRequest(
|
|
38349
|
+
lastInitializeRequest.requestData,
|
|
38350
|
+
lastInitializeRequest.useJsonRpc,
|
|
38351
|
+
null
|
|
38352
|
+
);
|
|
38353
|
+
if (!globalSessionId) {
|
|
38354
|
+
throw new APIError(
|
|
38355
|
+
"WordPress initialize did not return a session ID during refresh",
|
|
38356
|
+
0,
|
|
38357
|
+
getRequestUrl()
|
|
38358
|
+
);
|
|
38359
|
+
}
|
|
38360
|
+
})();
|
|
38361
|
+
try {
|
|
38362
|
+
await sessionRefreshPromise;
|
|
38363
|
+
} finally {
|
|
38364
|
+
sessionRefreshPromise = null;
|
|
38365
|
+
}
|
|
38366
|
+
}
|
|
38367
|
+
async function wpRequest(requestData, useJsonRpc = true, options = {}) {
|
|
38368
|
+
validateEnvironment();
|
|
38369
|
+
const allowSessionRecovery = options.allowSessionRecovery !== false;
|
|
38370
|
+
if (isInitializeRequest(requestData)) {
|
|
38371
|
+
cacheInitializeRequest(requestData, useJsonRpc);
|
|
38372
|
+
}
|
|
38373
|
+
const sessionIdUsed = globalSessionId;
|
|
38374
|
+
try {
|
|
38375
|
+
const result = await executeWordPressRequest(requestData, useJsonRpc, sessionIdUsed);
|
|
38376
|
+
return result.responseData;
|
|
38377
|
+
} catch (error) {
|
|
38378
|
+
if (error instanceof APIError && allowSessionRecovery && !isInitializeRequest(requestData) && isInvalidSessionError(error)) {
|
|
38379
|
+
logger.warn("WordPress session expired; attempting one-time recovery", "SESSION", {
|
|
38380
|
+
method: requestData?.method || "unknown",
|
|
38381
|
+
sessionIdUsed: sessionIdUsed || "none"
|
|
38382
|
+
});
|
|
38383
|
+
await refreshSession(sessionIdUsed);
|
|
38384
|
+
const retriedResult = await executeWordPressRequest(
|
|
38385
|
+
requestData,
|
|
38386
|
+
useJsonRpc,
|
|
38387
|
+
globalSessionId
|
|
38388
|
+
);
|
|
38389
|
+
return retriedResult.responseData;
|
|
38390
|
+
}
|
|
38391
|
+
if (error instanceof APIError) {
|
|
38392
|
+
throw error;
|
|
38393
|
+
}
|
|
38394
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
38395
|
+
logger.error(`Error in wpRequest: ${errorMessage}`, "API");
|
|
38396
|
+
throw new APIError(errorMessage, 0, getRequestUrl());
|
|
38397
|
+
}
|
|
38398
|
+
}
|
|
38150
38399
|
export {
|
|
38151
38400
|
APIError,
|
|
38152
38401
|
AuthError,
|
package/dist/proxy.js
CHANGED
|
@@ -92,13 +92,13 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
92
92
|
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, factory(global2.WebStreamsPolyfill = {}));
|
|
93
93
|
})(exports, function(exports2) {
|
|
94
94
|
"use strict";
|
|
95
|
-
function
|
|
95
|
+
function noop3() {
|
|
96
96
|
return void 0;
|
|
97
97
|
}
|
|
98
98
|
function typeIsObject(x2) {
|
|
99
99
|
return typeof x2 === "object" && x2 !== null || typeof x2 === "function";
|
|
100
100
|
}
|
|
101
|
-
const rethrowAssertionErrorRejection =
|
|
101
|
+
const rethrowAssertionErrorRejection = noop3;
|
|
102
102
|
function setFunctionName(fn, name) {
|
|
103
103
|
try {
|
|
104
104
|
Object.defineProperty(fn, "name", {
|
|
@@ -2727,7 +2727,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
2727
2727
|
return newPromise((resolveRead, rejectRead) => {
|
|
2728
2728
|
ReadableStreamDefaultReaderRead(reader, {
|
|
2729
2729
|
_chunkSteps: (chunk) => {
|
|
2730
|
-
currentWrite = PerformPromiseThen(WritableStreamDefaultWriterWrite(writer, chunk), void 0,
|
|
2730
|
+
currentWrite = PerformPromiseThen(WritableStreamDefaultWriterWrite(writer, chunk), void 0, noop3);
|
|
2731
2731
|
resolveRead(false);
|
|
2732
2732
|
},
|
|
2733
2733
|
_closeSteps: () => resolveRead(true),
|
|
@@ -3408,7 +3408,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
3408
3408
|
function ReadableStreamFromIterable(asyncIterable) {
|
|
3409
3409
|
let stream;
|
|
3410
3410
|
const iteratorRecord = GetIterator(asyncIterable, "async");
|
|
3411
|
-
const startAlgorithm =
|
|
3411
|
+
const startAlgorithm = noop3;
|
|
3412
3412
|
function pullAlgorithm() {
|
|
3413
3413
|
let nextResult;
|
|
3414
3414
|
try {
|
|
@@ -3460,7 +3460,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
3460
3460
|
}
|
|
3461
3461
|
function ReadableStreamFromDefaultReader(reader) {
|
|
3462
3462
|
let stream;
|
|
3463
|
-
const startAlgorithm =
|
|
3463
|
+
const startAlgorithm = noop3;
|
|
3464
3464
|
function pullAlgorithm() {
|
|
3465
3465
|
let readPromise;
|
|
3466
3466
|
try {
|
|
@@ -3779,7 +3779,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
3779
3779
|
});
|
|
3780
3780
|
}
|
|
3781
3781
|
const sourceCancelPromise = stream._readableStreamController[CancelSteps](reason);
|
|
3782
|
-
return transformPromiseWith(sourceCancelPromise,
|
|
3782
|
+
return transformPromiseWith(sourceCancelPromise, noop3);
|
|
3783
3783
|
}
|
|
3784
3784
|
function ReadableStreamClose(stream) {
|
|
3785
3785
|
stream._state = "closed";
|
|
@@ -5000,7 +5000,7 @@ async function toFormData(Body2, ct) {
|
|
|
5000
5000
|
parser.end();
|
|
5001
5001
|
return formData;
|
|
5002
5002
|
}
|
|
5003
|
-
var s, S, f2, F, LF, CR, SPACE, HYPHEN, COLON, A, Z, lower,
|
|
5003
|
+
var s, S, f2, F, LF, CR, SPACE, HYPHEN, COLON, A, Z, lower, noop2, MultipartParser;
|
|
5004
5004
|
var init_multipart_parser = __esm({
|
|
5005
5005
|
"node_modules/node-fetch/src/utils/multipart-parser.js"() {
|
|
5006
5006
|
"use strict";
|
|
@@ -5032,7 +5032,7 @@ var init_multipart_parser = __esm({
|
|
|
5032
5032
|
A = 97;
|
|
5033
5033
|
Z = 122;
|
|
5034
5034
|
lower = (c) => c | 32;
|
|
5035
|
-
|
|
5035
|
+
noop2 = () => {
|
|
5036
5036
|
};
|
|
5037
5037
|
MultipartParser = class {
|
|
5038
5038
|
/**
|
|
@@ -5041,13 +5041,13 @@ var init_multipart_parser = __esm({
|
|
|
5041
5041
|
constructor(boundary) {
|
|
5042
5042
|
this.index = 0;
|
|
5043
5043
|
this.flags = 0;
|
|
5044
|
-
this.onHeaderEnd =
|
|
5045
|
-
this.onHeaderField =
|
|
5046
|
-
this.onHeadersEnd =
|
|
5047
|
-
this.onHeaderValue =
|
|
5048
|
-
this.onPartBegin =
|
|
5049
|
-
this.onPartData =
|
|
5050
|
-
this.onPartEnd =
|
|
5044
|
+
this.onHeaderEnd = noop2;
|
|
5045
|
+
this.onHeaderField = noop2;
|
|
5046
|
+
this.onHeadersEnd = noop2;
|
|
5047
|
+
this.onHeaderValue = noop2;
|
|
5048
|
+
this.onPartBegin = noop2;
|
|
5049
|
+
this.onPartData = noop2;
|
|
5050
|
+
this.onPartEnd = noop2;
|
|
5051
5051
|
this.boundaryChars = {};
|
|
5052
5052
|
boundary = "\r\n--" + boundary;
|
|
5053
5053
|
const ui8a = new Uint8Array(boundary.length);
|
|
@@ -7882,7 +7882,7 @@ var require_destroy = __commonJS({
|
|
|
7882
7882
|
}
|
|
7883
7883
|
if (isEventEmitter(stream) && suppress) {
|
|
7884
7884
|
stream.removeAllListeners("error");
|
|
7885
|
-
stream.addListener("error",
|
|
7885
|
+
stream.addListener("error", noop3);
|
|
7886
7886
|
}
|
|
7887
7887
|
return stream;
|
|
7888
7888
|
}
|
|
@@ -7937,7 +7937,7 @@ var require_destroy = __commonJS({
|
|
|
7937
7937
|
function isZlibStream(stream) {
|
|
7938
7938
|
return stream instanceof Zlib.Gzip || stream instanceof Zlib.Gunzip || stream instanceof Zlib.Deflate || stream instanceof Zlib.DeflateRaw || stream instanceof Zlib.Inflate || stream instanceof Zlib.InflateRaw || stream instanceof Zlib.Unzip;
|
|
7939
7939
|
}
|
|
7940
|
-
function
|
|
7940
|
+
function noop3() {
|
|
7941
7941
|
}
|
|
7942
7942
|
function onDrainClearBinding() {
|
|
7943
7943
|
this._binding.clear();
|
|
@@ -35686,6 +35686,109 @@ var StdioServerTransport = class {
|
|
|
35686
35686
|
}
|
|
35687
35687
|
};
|
|
35688
35688
|
|
|
35689
|
+
// node_modules/eventsource-parser/dist/index.js
|
|
35690
|
+
var ParseError = class extends Error {
|
|
35691
|
+
constructor(message, options) {
|
|
35692
|
+
super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
|
|
35693
|
+
}
|
|
35694
|
+
};
|
|
35695
|
+
function noop(_arg) {
|
|
35696
|
+
}
|
|
35697
|
+
function createParser(callbacks) {
|
|
35698
|
+
if (typeof callbacks == "function")
|
|
35699
|
+
throw new TypeError(
|
|
35700
|
+
"`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?"
|
|
35701
|
+
);
|
|
35702
|
+
const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
|
|
35703
|
+
let incompleteLine = "", isFirstChunk = true, id, data = "", eventType = "";
|
|
35704
|
+
function feed(newChunk) {
|
|
35705
|
+
const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
|
|
35706
|
+
for (const line of complete)
|
|
35707
|
+
parseLine(line);
|
|
35708
|
+
incompleteLine = incomplete, isFirstChunk = false;
|
|
35709
|
+
}
|
|
35710
|
+
function parseLine(line) {
|
|
35711
|
+
if (line === "") {
|
|
35712
|
+
dispatchEvent();
|
|
35713
|
+
return;
|
|
35714
|
+
}
|
|
35715
|
+
if (line.startsWith(":")) {
|
|
35716
|
+
onComment && onComment(line.slice(line.startsWith(": ") ? 2 : 1));
|
|
35717
|
+
return;
|
|
35718
|
+
}
|
|
35719
|
+
const fieldSeparatorIndex = line.indexOf(":");
|
|
35720
|
+
if (fieldSeparatorIndex !== -1) {
|
|
35721
|
+
const field = line.slice(0, fieldSeparatorIndex), offset = line[fieldSeparatorIndex + 1] === " " ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
|
|
35722
|
+
processField(field, value, line);
|
|
35723
|
+
return;
|
|
35724
|
+
}
|
|
35725
|
+
processField(line, "", line);
|
|
35726
|
+
}
|
|
35727
|
+
function processField(field, value, line) {
|
|
35728
|
+
switch (field) {
|
|
35729
|
+
case "event":
|
|
35730
|
+
eventType = value;
|
|
35731
|
+
break;
|
|
35732
|
+
case "data":
|
|
35733
|
+
data = `${data}${value}
|
|
35734
|
+
`;
|
|
35735
|
+
break;
|
|
35736
|
+
case "id":
|
|
35737
|
+
id = value.includes("\0") ? void 0 : value;
|
|
35738
|
+
break;
|
|
35739
|
+
case "retry":
|
|
35740
|
+
/^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
|
|
35741
|
+
new ParseError(`Invalid \`retry\` value: "${value}"`, {
|
|
35742
|
+
type: "invalid-retry",
|
|
35743
|
+
value,
|
|
35744
|
+
line
|
|
35745
|
+
})
|
|
35746
|
+
);
|
|
35747
|
+
break;
|
|
35748
|
+
default:
|
|
35749
|
+
onError(
|
|
35750
|
+
new ParseError(
|
|
35751
|
+
`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
|
|
35752
|
+
{ type: "unknown-field", field, value, line }
|
|
35753
|
+
)
|
|
35754
|
+
);
|
|
35755
|
+
break;
|
|
35756
|
+
}
|
|
35757
|
+
}
|
|
35758
|
+
function dispatchEvent() {
|
|
35759
|
+
data.length > 0 && onEvent({
|
|
35760
|
+
id,
|
|
35761
|
+
event: eventType || void 0,
|
|
35762
|
+
// If the data buffer's last character is a U+000A LINE FEED (LF) character,
|
|
35763
|
+
// then remove the last character from the data buffer.
|
|
35764
|
+
data: data.endsWith(`
|
|
35765
|
+
`) ? data.slice(0, -1) : data
|
|
35766
|
+
}), id = void 0, data = "", eventType = "";
|
|
35767
|
+
}
|
|
35768
|
+
function reset(options = {}) {
|
|
35769
|
+
incompleteLine && options.consume && parseLine(incompleteLine), isFirstChunk = true, id = void 0, data = "", eventType = "", incompleteLine = "";
|
|
35770
|
+
}
|
|
35771
|
+
return { feed, reset };
|
|
35772
|
+
}
|
|
35773
|
+
function splitLines(chunk) {
|
|
35774
|
+
const lines = [];
|
|
35775
|
+
let incompleteLine = "", searchIndex = 0;
|
|
35776
|
+
for (; searchIndex < chunk.length; ) {
|
|
35777
|
+
const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
|
|
35778
|
+
`, searchIndex);
|
|
35779
|
+
let lineEnd = -1;
|
|
35780
|
+
if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = Math.min(crIndex, lfIndex) : crIndex !== -1 ? lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1) {
|
|
35781
|
+
incompleteLine = chunk.slice(searchIndex);
|
|
35782
|
+
break;
|
|
35783
|
+
} else {
|
|
35784
|
+
const line = chunk.slice(searchIndex, lineEnd);
|
|
35785
|
+
lines.push(line), searchIndex = lineEnd + 1, chunk[searchIndex - 1] === "\r" && chunk[searchIndex] === `
|
|
35786
|
+
` && searchIndex++;
|
|
35787
|
+
}
|
|
35788
|
+
}
|
|
35789
|
+
return [lines, incompleteLine];
|
|
35790
|
+
}
|
|
35791
|
+
|
|
35689
35792
|
// node_modules/pkce-challenge/dist/index.node.js
|
|
35690
35793
|
var crypto;
|
|
35691
35794
|
crypto = globalThis.crypto?.webcrypto ?? // Node.js [18-16] REPL
|
|
@@ -35775,10 +35878,9 @@ import path from "path";
|
|
|
35775
35878
|
import fs from "fs/promises";
|
|
35776
35879
|
import fsSync from "fs";
|
|
35777
35880
|
import crypto2 from "crypto";
|
|
35778
|
-
var VERSION = MCP_WORDPRESS_REMOTE_VERSION;
|
|
35779
35881
|
function getConfigDir() {
|
|
35780
35882
|
const baseConfigDir = CONFIG.WP_MCP_CONFIG_DIR;
|
|
35781
|
-
return path.join(baseConfigDir, `wordpress-remote-${
|
|
35883
|
+
return path.join(baseConfigDir, `wordpress-remote-${MCP_WORDPRESS_REMOTE_VERSION}`);
|
|
35782
35884
|
}
|
|
35783
35885
|
async function ensureConfigDir() {
|
|
35784
35886
|
try {
|
|
@@ -36000,7 +36102,7 @@ function calculateDefaultPort(serverUrlHash) {
|
|
|
36000
36102
|
}
|
|
36001
36103
|
async function findExistingClientPort(serverUrlHash) {
|
|
36002
36104
|
const clientInfo = await readJsonFile(serverUrlHash, "client_info.json");
|
|
36003
|
-
if (!clientInfo) {
|
|
36105
|
+
if (!clientInfo || !clientInfo.redirect_uris) {
|
|
36004
36106
|
return void 0;
|
|
36005
36107
|
}
|
|
36006
36108
|
const localhostRedirectUri = clientInfo.redirect_uris.map((uri) => {
|
|
@@ -36036,7 +36138,7 @@ async function selectCallbackPort(serverUrl, specifiedPort, _unused) {
|
|
|
36036
36138
|
}
|
|
36037
36139
|
|
|
36038
36140
|
// src/lib/config.ts
|
|
36039
|
-
var MCP_WORDPRESS_REMOTE_VERSION = "0.
|
|
36141
|
+
var MCP_WORDPRESS_REMOTE_VERSION = "0.3.0";
|
|
36040
36142
|
var CONFIG = {
|
|
36041
36143
|
// API Configuration
|
|
36042
36144
|
WP_API_URL: process.env.WP_API_URL || "https://example.com",
|
|
@@ -37514,17 +37616,14 @@ var OAuthCallbackServer = class {
|
|
|
37514
37616
|
});
|
|
37515
37617
|
}
|
|
37516
37618
|
async stop() {
|
|
37517
|
-
|
|
37518
|
-
|
|
37519
|
-
|
|
37520
|
-
|
|
37521
|
-
|
|
37522
|
-
|
|
37523
|
-
});
|
|
37524
|
-
} else {
|
|
37525
|
-
resolve();
|
|
37526
|
-
}
|
|
37619
|
+
if (!this.server) return;
|
|
37620
|
+
const server = this.server;
|
|
37621
|
+
this.server = null;
|
|
37622
|
+
server.close((err) => {
|
|
37623
|
+
if (err) logger.error("OAuth callback server close error", "OAUTH", err);
|
|
37624
|
+
else logger.oauth("OAuth callback server stopped");
|
|
37527
37625
|
});
|
|
37626
|
+
server.closeAllConnections?.();
|
|
37528
37627
|
}
|
|
37529
37628
|
getCallbackUrl() {
|
|
37530
37629
|
return `http://${this.options.host}:${this.options.port}/oauth/callback`;
|
|
@@ -38529,6 +38628,12 @@ var mcpOAuthProvider = null;
|
|
|
38529
38628
|
var authCoordinator = null;
|
|
38530
38629
|
var globalEvents = null;
|
|
38531
38630
|
var globalSessionId = null;
|
|
38631
|
+
var lastInitializeRequest = null;
|
|
38632
|
+
var sessionRefreshPromise = null;
|
|
38633
|
+
var WP_MCP_ENDPOINT = "/wp/v2/wpmcp";
|
|
38634
|
+
var INVALID_SESSION_ERROR_CODES = /* @__PURE__ */ new Set([-32602, -32005]);
|
|
38635
|
+
var INVALID_SESSION_ERROR_MESSAGE = "Invalid or expired session";
|
|
38636
|
+
var SESSION_NOT_FOUND_ERROR_MESSAGE = "Session not found";
|
|
38532
38637
|
function validateEnvironment() {
|
|
38533
38638
|
const validation = validateConfig();
|
|
38534
38639
|
if (!validation.isValid) {
|
|
@@ -38541,6 +38646,24 @@ function validateEnvironment() {
|
|
|
38541
38646
|
function removeTrailingSlash(url) {
|
|
38542
38647
|
return url.replace(/\/+$/, "");
|
|
38543
38648
|
}
|
|
38649
|
+
function parseSSEMessage(text) {
|
|
38650
|
+
let result;
|
|
38651
|
+
let found = false;
|
|
38652
|
+
const parser = createParser({
|
|
38653
|
+
onEvent(event) {
|
|
38654
|
+
if (found) return;
|
|
38655
|
+
if (!event.event || event.event === "message") {
|
|
38656
|
+
result = JSON.parse(event.data);
|
|
38657
|
+
found = true;
|
|
38658
|
+
}
|
|
38659
|
+
}
|
|
38660
|
+
});
|
|
38661
|
+
parser.feed(text);
|
|
38662
|
+
if (!found) {
|
|
38663
|
+
throw new Error('No "message" event with data found in SSE response');
|
|
38664
|
+
}
|
|
38665
|
+
return result;
|
|
38666
|
+
}
|
|
38544
38667
|
function constructApiUrl(baseUrl, defaultEndpoint) {
|
|
38545
38668
|
const cleanUrl = removeTrailingSlash(baseUrl);
|
|
38546
38669
|
try {
|
|
@@ -38630,9 +38753,53 @@ async function getOAuthTokens() {
|
|
|
38630
38753
|
function getSessionId() {
|
|
38631
38754
|
return globalSessionId;
|
|
38632
38755
|
}
|
|
38633
|
-
|
|
38634
|
-
|
|
38635
|
-
|
|
38756
|
+
function getCurrentApiUrl() {
|
|
38757
|
+
return process.env.WP_API_URL || CONFIG.WP_API_URL;
|
|
38758
|
+
}
|
|
38759
|
+
function getRequestUrl() {
|
|
38760
|
+
return constructApiUrl(getCurrentApiUrl(), WP_MCP_ENDPOINT);
|
|
38761
|
+
}
|
|
38762
|
+
function cloneRequestData(requestData) {
|
|
38763
|
+
return JSON.parse(JSON.stringify(requestData));
|
|
38764
|
+
}
|
|
38765
|
+
function isInitializeRequest(requestData) {
|
|
38766
|
+
return requestData?.method === "initialize";
|
|
38767
|
+
}
|
|
38768
|
+
function cacheInitializeRequest(requestData, useJsonRpc) {
|
|
38769
|
+
lastInitializeRequest = {
|
|
38770
|
+
requestData: cloneRequestData(requestData),
|
|
38771
|
+
useJsonRpc
|
|
38772
|
+
};
|
|
38773
|
+
}
|
|
38774
|
+
function parseApiErrorResponse(error) {
|
|
38775
|
+
if (!error.response) {
|
|
38776
|
+
return null;
|
|
38777
|
+
}
|
|
38778
|
+
if (typeof error.response === "string") {
|
|
38779
|
+
try {
|
|
38780
|
+
return JSON.parse(error.response);
|
|
38781
|
+
} catch {
|
|
38782
|
+
return null;
|
|
38783
|
+
}
|
|
38784
|
+
}
|
|
38785
|
+
return error.response;
|
|
38786
|
+
}
|
|
38787
|
+
function isInvalidSessionError(error) {
|
|
38788
|
+
const errorResponse = parseApiErrorResponse(error);
|
|
38789
|
+
const jsonRpcError = errorResponse?.error && typeof errorResponse.error === "object" ? errorResponse.error : errorResponse;
|
|
38790
|
+
const code = typeof jsonRpcError?.code === "number" ? jsonRpcError.code : null;
|
|
38791
|
+
const message = typeof jsonRpcError?.message === "string" ? jsonRpcError.message : "";
|
|
38792
|
+
return code !== null && INVALID_SESSION_ERROR_CODES.has(code) && (message === INVALID_SESSION_ERROR_MESSAGE || message.includes(SESSION_NOT_FOUND_ERROR_MESSAGE) || message.includes(INVALID_SESSION_ERROR_MESSAGE));
|
|
38793
|
+
}
|
|
38794
|
+
function updateSessionId(sessionId) {
|
|
38795
|
+
if (globalSessionId === sessionId) {
|
|
38796
|
+
return;
|
|
38797
|
+
}
|
|
38798
|
+
globalSessionId = sessionId;
|
|
38799
|
+
logger.info(`Session ID received from WordPress: ${globalSessionId}`, "SESSION");
|
|
38800
|
+
}
|
|
38801
|
+
async function executeWordPressRequest(requestData, useJsonRpc, sessionIdUsed) {
|
|
38802
|
+
const url = getRequestUrl();
|
|
38636
38803
|
const method = "POST";
|
|
38637
38804
|
if (useJsonRpc) {
|
|
38638
38805
|
logger.api(`Request method: ${requestData.method || "unknown"} (JSON-RPC)`);
|
|
@@ -38691,13 +38858,12 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38691
38858
|
"NO_AUTH_METHOD"
|
|
38692
38859
|
);
|
|
38693
38860
|
}
|
|
38694
|
-
const currentApiUrl = process.env.WP_API_URL || CONFIG.WP_API_URL;
|
|
38695
38861
|
logger.debug(`Environment: ${CONFIG.NODE_ENV}`, "API");
|
|
38696
|
-
logger.debug(`Base API URL: ${
|
|
38697
|
-
const url = constructApiUrl(currentApiUrl, endpoint);
|
|
38862
|
+
logger.debug(`Base API URL: ${getCurrentApiUrl()}`, "API");
|
|
38698
38863
|
logger.debug(`Final requesting URL: ${url}`, "API");
|
|
38699
38864
|
const headers = {
|
|
38700
38865
|
"Content-Type": "application/json",
|
|
38866
|
+
Accept: "application/json, text/event-stream",
|
|
38701
38867
|
"MCP-Protocol-Version": "2025-06-18",
|
|
38702
38868
|
// MCP protocol version
|
|
38703
38869
|
...customHeaders
|
|
@@ -38706,8 +38872,8 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38706
38872
|
if (authHeader) {
|
|
38707
38873
|
headers.Authorization = authHeader;
|
|
38708
38874
|
}
|
|
38709
|
-
if (
|
|
38710
|
-
headers["Mcp-Session-Id"] =
|
|
38875
|
+
if (sessionIdUsed) {
|
|
38876
|
+
headers["Mcp-Session-Id"] = sessionIdUsed;
|
|
38711
38877
|
}
|
|
38712
38878
|
if (authHeader) {
|
|
38713
38879
|
logger.debug("Using Authorization header for authentication", "API");
|
|
@@ -38731,21 +38897,27 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38731
38897
|
logger.debug(`Request method: ${method}`, "API");
|
|
38732
38898
|
const response = await proxyFetch(url, fetchOptions);
|
|
38733
38899
|
logger.debug(`Response status: ${response.status}`, "API");
|
|
38900
|
+
const rawBody = await response.text();
|
|
38734
38901
|
if (!response.ok) {
|
|
38735
|
-
|
|
38736
|
-
logger.error(`API error response: ${errorText}`, "API");
|
|
38902
|
+
logger.error(`API error response: ${rawBody}`, "API");
|
|
38737
38903
|
throw new APIError(
|
|
38738
|
-
`WordPress API error (${response.status}): ${
|
|
38904
|
+
`WordPress API error (${response.status}): ${rawBody}`,
|
|
38739
38905
|
response.status,
|
|
38740
38906
|
url,
|
|
38741
|
-
|
|
38907
|
+
rawBody
|
|
38742
38908
|
);
|
|
38743
38909
|
}
|
|
38744
|
-
const
|
|
38910
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
38911
|
+
let responseData;
|
|
38912
|
+
if (contentType.includes("text/event-stream")) {
|
|
38913
|
+
responseData = parseSSEMessage(rawBody);
|
|
38914
|
+
logger.debug("Parsed text/event-stream response body", "API");
|
|
38915
|
+
} else {
|
|
38916
|
+
responseData = JSON.parse(rawBody);
|
|
38917
|
+
}
|
|
38745
38918
|
const sessionIdHeader = response.headers.get("Mcp-Session-Id");
|
|
38746
|
-
if (sessionIdHeader
|
|
38747
|
-
|
|
38748
|
-
logger.info(`Session ID received from WordPress: ${globalSessionId}`, "SESSION");
|
|
38919
|
+
if (sessionIdHeader) {
|
|
38920
|
+
updateSessionId(sessionIdHeader);
|
|
38749
38921
|
}
|
|
38750
38922
|
logger.api("Response received successfully");
|
|
38751
38923
|
logger.debug(`Response data: ${JSON.stringify(responseData)}`, "API");
|
|
@@ -38758,14 +38930,20 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38758
38930
|
`WordPress JSON-RPC error: ${jsonrpcResponse.error.message}`,
|
|
38759
38931
|
jsonrpcResponse.error.code || 500,
|
|
38760
38932
|
url,
|
|
38761
|
-
|
|
38933
|
+
jsonrpcResponse.error
|
|
38762
38934
|
);
|
|
38763
38935
|
} else if (jsonrpcResponse.result !== void 0) {
|
|
38764
|
-
return
|
|
38936
|
+
return {
|
|
38937
|
+
responseData: jsonrpcResponse.result,
|
|
38938
|
+
sessionIdUsed
|
|
38939
|
+
};
|
|
38765
38940
|
}
|
|
38766
38941
|
}
|
|
38767
38942
|
}
|
|
38768
|
-
return
|
|
38943
|
+
return {
|
|
38944
|
+
responseData,
|
|
38945
|
+
sessionIdUsed
|
|
38946
|
+
};
|
|
38769
38947
|
} catch (error) {
|
|
38770
38948
|
if (error instanceof APIError) {
|
|
38771
38949
|
throw error;
|
|
@@ -38775,6 +38953,77 @@ async function wpRequest(requestData, useJsonRpc = true) {
|
|
|
38775
38953
|
throw new APIError(errorMessage, 0, url);
|
|
38776
38954
|
}
|
|
38777
38955
|
}
|
|
38956
|
+
async function refreshSession(failedSessionId) {
|
|
38957
|
+
if (failedSessionId && globalSessionId && globalSessionId !== failedSessionId) {
|
|
38958
|
+
logger.info("Detected newer session while handling invalid-session error; skipping refresh", "SESSION");
|
|
38959
|
+
return;
|
|
38960
|
+
}
|
|
38961
|
+
if (sessionRefreshPromise) {
|
|
38962
|
+
logger.info("Waiting for in-flight WordPress session refresh", "SESSION");
|
|
38963
|
+
await sessionRefreshPromise;
|
|
38964
|
+
return;
|
|
38965
|
+
}
|
|
38966
|
+
if (!lastInitializeRequest) {
|
|
38967
|
+
throw new APIError(
|
|
38968
|
+
"Cannot refresh WordPress session before initialize has completed",
|
|
38969
|
+
0,
|
|
38970
|
+
getRequestUrl()
|
|
38971
|
+
);
|
|
38972
|
+
}
|
|
38973
|
+
sessionRefreshPromise = (async () => {
|
|
38974
|
+
logger.warn("WordPress session rejected; refreshing session via initialize", "SESSION");
|
|
38975
|
+
globalSessionId = null;
|
|
38976
|
+
await executeWordPressRequest(
|
|
38977
|
+
lastInitializeRequest.requestData,
|
|
38978
|
+
lastInitializeRequest.useJsonRpc,
|
|
38979
|
+
null
|
|
38980
|
+
);
|
|
38981
|
+
if (!globalSessionId) {
|
|
38982
|
+
throw new APIError(
|
|
38983
|
+
"WordPress initialize did not return a session ID during refresh",
|
|
38984
|
+
0,
|
|
38985
|
+
getRequestUrl()
|
|
38986
|
+
);
|
|
38987
|
+
}
|
|
38988
|
+
})();
|
|
38989
|
+
try {
|
|
38990
|
+
await sessionRefreshPromise;
|
|
38991
|
+
} finally {
|
|
38992
|
+
sessionRefreshPromise = null;
|
|
38993
|
+
}
|
|
38994
|
+
}
|
|
38995
|
+
async function wpRequest(requestData, useJsonRpc = true, options = {}) {
|
|
38996
|
+
validateEnvironment();
|
|
38997
|
+
const allowSessionRecovery = options.allowSessionRecovery !== false;
|
|
38998
|
+
if (isInitializeRequest(requestData)) {
|
|
38999
|
+
cacheInitializeRequest(requestData, useJsonRpc);
|
|
39000
|
+
}
|
|
39001
|
+
const sessionIdUsed = globalSessionId;
|
|
39002
|
+
try {
|
|
39003
|
+
const result = await executeWordPressRequest(requestData, useJsonRpc, sessionIdUsed);
|
|
39004
|
+
return result.responseData;
|
|
39005
|
+
} catch (error) {
|
|
39006
|
+
if (error instanceof APIError && allowSessionRecovery && !isInitializeRequest(requestData) && isInvalidSessionError(error)) {
|
|
39007
|
+
logger.warn("WordPress session expired; attempting one-time recovery", "SESSION", {
|
|
39008
|
+
method: requestData?.method || "unknown",
|
|
39009
|
+
sessionIdUsed: sessionIdUsed || "none"
|
|
39010
|
+
});
|
|
39011
|
+
await refreshSession(sessionIdUsed);
|
|
39012
|
+
const retriedResult = await executeWordPressRequest(
|
|
39013
|
+
requestData,
|
|
39014
|
+
useJsonRpc,
|
|
39015
|
+
globalSessionId
|
|
39016
|
+
);
|
|
39017
|
+
return retriedResult.responseData;
|
|
39018
|
+
}
|
|
39019
|
+
if (error instanceof APIError) {
|
|
39020
|
+
throw error;
|
|
39021
|
+
}
|
|
39022
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
39023
|
+
logger.error(`Error in wpRequest: ${errorMessage}`, "API");
|
|
39024
|
+
throw new APIError(errorMessage, 0, getRequestUrl());
|
|
39025
|
+
}
|
|
39026
|
+
}
|
|
38778
39027
|
|
|
38779
39028
|
// src/lib/session-utils.ts
|
|
38780
39029
|
function addSessionInfo(wpRequestParams, mcpRequest, context) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/mcp-wordpress-remote",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "MCP WordPress Remote proxy server",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18.0.0"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"bin": {
|
|
37
|
-
"
|
|
37
|
+
"mcp-wordpress-remote": "./dist/proxy.js"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsup",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@modelcontextprotocol/sdk": "^1.8.0",
|
|
62
62
|
"@tootallnate/quickjs-emscripten": "^0.23.0",
|
|
63
|
+
"eventsource-parser": "^3.0.1",
|
|
63
64
|
"express": "^4.18.2",
|
|
64
65
|
"https-proxy-agent": "^7.0.6",
|
|
65
66
|
"node-fetch": "^3.3.2",
|
|
@@ -100,6 +101,7 @@
|
|
|
100
101
|
"splitting": false,
|
|
101
102
|
"noExternal": [
|
|
102
103
|
"@modelcontextprotocol/sdk",
|
|
104
|
+
"eventsource-parser",
|
|
103
105
|
"express",
|
|
104
106
|
"node-fetch",
|
|
105
107
|
"open"
|