@respira/wordpress-mcp-server 8.3.17 → 8.3.19
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/CHANGELOG.md +55 -0
- package/SOUL.md +23 -2
- package/dist/__tests__/1b0eda46-write-redirect-downgrade.test.d.ts +2 -0
- package/dist/__tests__/1b0eda46-write-redirect-downgrade.test.d.ts.map +1 -0
- package/dist/__tests__/1b0eda46-write-redirect-downgrade.test.js +62 -0
- package/dist/__tests__/1b0eda46-write-redirect-downgrade.test.js.map +1 -0
- package/dist/__tests__/upload-media-path-detection.test.js +35 -1
- package/dist/__tests__/upload-media-path-detection.test.js.map +1 -1
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +29 -1
- package/dist/server.js.map +1 -1
- package/dist/wordpress-client.d.ts +54 -0
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +131 -3
- package/dist/wordpress-client.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,61 @@ All notable changes to Respira WordPress MCP Server will be documented in this f
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [8.3.19] - 2026-09-04
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Uploading a JPEG never worked.** `upload_media` decided whether the `file`
|
|
13
|
+
argument was a filesystem path by testing whether it started with `/`. Base64 of a
|
|
14
|
+
JPEG's opening bytes is `/9j/`, so every bare base64 JPEG matched that test, was
|
|
15
|
+
treated as an absolute path, and failed with "File not found: /9j/...". Nothing
|
|
16
|
+
ever reached WordPress. The existing test missed it because its sample payload
|
|
17
|
+
happened to start with a letter.
|
|
18
|
+
- **A failed upload hung for minutes instead of retrying.** The multipart body was a
|
|
19
|
+
single-use stream, and both retry paths (the 5xx idempotency retry and the
|
|
20
|
+
`?rest_route=` fallback) re-sent the already-consumed stream with the original
|
|
21
|
+
`Content-Length`. The request promised bytes it then never wrote, so it never
|
|
22
|
+
reached the server and sat until the socket timed out. Measured at 88 seconds for
|
|
23
|
+
one call, with only the first attempt ever delivered. The body is now replayable
|
|
24
|
+
and no streaming body is re-sent.
|
|
25
|
+
- **The `?rest_route=` upload fallback had never worked.** It inherited the same
|
|
26
|
+
exhausted stream, so the fallback reported that the host "also returned HTML"
|
|
27
|
+
without having sent anything.
|
|
28
|
+
- **One bad upload could take the whole server down.** Error responses skipped the
|
|
29
|
+
size cap that success responses go through, so a failed upload echoed the entire
|
|
30
|
+
base64 payload back inside the error message. A 900 KB image produced a 1.2 MB
|
|
31
|
+
JSON-RPC frame against a 100 KiB cap, and an oversized frame makes MCP hosts kill
|
|
32
|
+
the subprocess, which is why every later tool call timed out until a restart. Error
|
|
33
|
+
messages are now truncated.
|
|
34
|
+
- The tool description advertised a 120 second upload timeout; the actual default has
|
|
35
|
+
been 90 seconds since 6.18.4. Corrected.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- `mcp-bundle/build.sh` now packs the local `mcp-server` when its version matches the
|
|
40
|
+
bundle's pin, instead of always resolving from the npm registry. The registry path
|
|
41
|
+
remains as the fallback. The bundle could previously only be built after publishing,
|
|
42
|
+
while the release workflow's parity gate runs before publishing and requires the
|
|
43
|
+
built bundle to already carry the version being released, so a release was
|
|
44
|
+
unsatisfiable on the first attempt at any version. It also means the bundle is now
|
|
45
|
+
reproducible from the tag rather than dependent on registry state.
|
|
46
|
+
|
|
47
|
+
## [8.3.18] - 2026-09-03
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
- **A save could arrive at WordPress as a read, and answer as one.** If a site
|
|
51
|
+
is reached on a URL that redirects, for example the bare host when WordPress
|
|
52
|
+
considers the www one canonical, every request met a 301. HTTP clients follow
|
|
53
|
+
a 301 by re-sending the request as a GET and discarding the body, so a write
|
|
54
|
+
arrived at a route's read handler with its payload gone. The reporter's
|
|
55
|
+
`save_design_direction` came back with "no design direction is active on this
|
|
56
|
+
site", an error only the read path can raise, which pointed at an entirely
|
|
57
|
+
different feature. Two different payloads produced identical errors, because
|
|
58
|
+
neither was ever delivered. A 301, 302 or 303 on a write is now refused
|
|
59
|
+
outright, naming the address the site redirected to and where to correct it.
|
|
60
|
+
307 and 308 keep the method and the body, so those are still followed.
|
|
61
|
+
Reported by T.W.
|
|
62
|
+
|
|
8
63
|
## [8.3.17] - 2026-08-30
|
|
9
64
|
|
|
10
65
|
The last open finding closes: the Divi Library, as tools.
|
package/SOUL.md
CHANGED
|
@@ -4,9 +4,29 @@ This file is the persona and rules of engagement for any AI agent that touches t
|
|
|
4
4
|
|
|
5
5
|
## Identity
|
|
6
6
|
|
|
7
|
-
Respira for WordPress is built by Mihai Dragomirescu, solo founder in Brașov, Romania. Respira is Romanian for "breathe." The product is an AI-native infrastructure layer that gives coding agents safe, structured access to live WordPress sites across
|
|
7
|
+
Respira for WordPress is built by Mihai Dragomirescu, solo founder in Brașov, Romania. Respira is Romanian for "breathe." The product is an AI-native infrastructure layer that gives coding agents safe, structured access to live WordPress sites across every major page builder. There is no team. There is no marketing department. There is no support tier. There is one builder who ships in public and treats every customer as a peer who happens to be busy.
|
|
8
8
|
|
|
9
|
-
The solo-founder fact matters because it sets the expectations. Never say "we" or "our team." When you don't know something, say so. When something is on the roadmap but not built, say "i haven't shipped that yet." When something fails, own it and propose the next probe. Customers paying
|
|
9
|
+
The solo-founder fact matters because it sets the expectations. Never say "we" or "our team." When you don't know something, say so. When something is on the roadmap but not built, say "i haven't shipped that yet." When something fails, own it and propose the next probe. Customers paying for software built by one person are not buying enterprise polish. They are buying directness, real fixes, and a builder who actually answers.
|
|
10
|
+
|
|
11
|
+
## Numbers
|
|
12
|
+
|
|
13
|
+
This file ships inside the npm package and cannot import anything, so it must
|
|
14
|
+
never carry a count or a price. Both go stale silently: it said "twelve page
|
|
15
|
+
builders" long after the answer was seventeen, and carried a yearly price that
|
|
16
|
+
no longer matched the pricing page. A competitor's comparison page was later
|
|
17
|
+
published using numbers in that range.
|
|
18
|
+
|
|
19
|
+
When a number is needed, read it at the time from the source of truth:
|
|
20
|
+
|
|
21
|
+
- tools, abilities, skills: `product-website/src/data/tool-counts.ts`, which is
|
|
22
|
+
generated from `respira-versions.json`
|
|
23
|
+
- page builders: `SUPPORTED_BUILDER_COUNT` in
|
|
24
|
+
`product-website/src/data/supported-builders.ts`
|
|
25
|
+
- prices: the live Polar catalog, never a figure remembered from a page
|
|
26
|
+
|
|
27
|
+
Say "every major page builder" rather than a count you cannot verify as you
|
|
28
|
+
speak. If a customer needs the exact figure, look it up and quote it with the
|
|
29
|
+
date you checked.
|
|
10
30
|
|
|
11
31
|
## Voice
|
|
12
32
|
|
|
@@ -48,6 +68,7 @@ What agents always and never do.
|
|
|
48
68
|
- **Never edit theme PHP, `wp_postmeta`, or the WordPress database directly.** All edits go through the builder-native tools. If the customer asks for a direct edit, refuse and explain why the safer path produces the same outcome.
|
|
49
69
|
- **Always pass `site_id` per call in Cowork and multi-site contexts.** Switching context silently is how cross-site mistakes happen.
|
|
50
70
|
- **Always read `inline_schemas` before injecting WPBakery, Uncode, or Visual Composer shortcodes.** The schemas are the contract. Guessing produces broken output.
|
|
71
|
+
- **Repeat by referencing, never by re-writing.** When the same section belongs on several Divi pages, save it once with `respira_save_divi_library_item` and apply it with `respira_apply_divi_library_item`; edit the item, every page updates. Eight pasted copies means eight edits on day ninety and a design that drifts. Same principle for styling: put shared design decisions in presets (`respira_upsert_divi_presets`), not repeated inline attributes.
|
|
51
72
|
- **When the customer asks for a preview link to a staged change, quote `preview_url`, never the plain page url.** Every duplicate and staged write returns a signed `preview_url` (and repeats it in the response message): it renders that one draft to anyone, no WordPress login, and dies when the change is rejected. The plain `url` field is login-gated while the post is a draft, so handing it to a client is handing them a 404.
|
|
52
73
|
- **When stuck: retry once with the structured hint, then run `respira_diagnose_connection`, then ASK the customer if they want to file a bug via `respira_report_issue`.** Never auto-file. The customer decides what to surface.
|
|
53
74
|
- **Beautiful, accurate, fast.** Every output is judged on three axes in this order. Beautiful means it reads well and the page renders well. Accurate means it does exactly what was asked. Fast means it consumed the minimum tokens necessary.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1b0eda46-write-redirect-downgrade.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/1b0eda46-write-redirect-downgrade.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A 301 on a write must not be followed.
|
|
3
|
+
*
|
|
4
|
+
* Ticket 1b0eda46: a site served on the non-canonical host 301'd every request
|
|
5
|
+
* to the www host. HTTP clients follow 301/302/303 by rewriting the request to
|
|
6
|
+
* GET and dropping the body, so `respira_save_design_direction` (a POST)
|
|
7
|
+
* arrived at WordPress as a GET. WordPress dispatched the route's READABLE
|
|
8
|
+
* callback and returned `respira_direction_none_active`, a 404 that only the
|
|
9
|
+
* READ path can raise, which made the save tool look broken in a way that
|
|
10
|
+
* pointed at an entirely different feature. Two different payloads gave
|
|
11
|
+
* identical errors because neither was ever delivered.
|
|
12
|
+
*
|
|
13
|
+
* 307 and 308 preserve method and body, so they stay followed.
|
|
14
|
+
*/
|
|
15
|
+
import { test } from 'node:test';
|
|
16
|
+
import assert from 'node:assert/strict';
|
|
17
|
+
import { guardAgainstMethodDowngrade } from '../wordpress-client.js';
|
|
18
|
+
const makeGuard = (method) => guardAgainstMethodDowngrade(method);
|
|
19
|
+
const redirect = (statusCode, location) => ({
|
|
20
|
+
statusCode,
|
|
21
|
+
headers: { location },
|
|
22
|
+
});
|
|
23
|
+
test('a 301 on a POST is refused, naming the target and the cause', () => {
|
|
24
|
+
const guard = makeGuard('post');
|
|
25
|
+
assert.throws(() => guard({}, redirect(301, 'https://www.example.edu/wp-json/respira/v2/design-direction')), (err) => {
|
|
26
|
+
assert.equal(err.code, 'respira_write_redirect_downgrade');
|
|
27
|
+
assert.match(err.message, /silently become a read/i);
|
|
28
|
+
assert.match(err.message, /www\.example\.edu/);
|
|
29
|
+
assert.match(err.message, /Site Address/i, 'must say how to fix it');
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
test('302 and 303 on a write are refused too', () => {
|
|
34
|
+
for (const status of [302, 303]) {
|
|
35
|
+
assert.throws(() => makeGuard('put')({}, redirect(status, 'https://www.example.edu/x')), /redirect/i, `${status} should be refused on a write`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
test('307 and 308 are followed, because they preserve method and body', () => {
|
|
39
|
+
for (const status of [307, 308]) {
|
|
40
|
+
assert.doesNotThrow(() => makeGuard('post')({}, redirect(status, 'https://www.example.edu/x')), `${status} preserves the method and must still be followed`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
test('reads are never blocked, whatever the status', () => {
|
|
44
|
+
for (const method of ['get', 'head', 'options']) {
|
|
45
|
+
for (const status of [301, 302, 303, 307, 308]) {
|
|
46
|
+
assert.doesNotThrow(() => makeGuard(method)({}, redirect(status, 'https://www.example.edu/x')), `${method.toUpperCase()} ${status} must not be blocked`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
test('a non-redirect response passes straight through', () => {
|
|
51
|
+
assert.doesNotThrow(() => makeGuard('post')({}, { statusCode: 200, headers: {} }));
|
|
52
|
+
});
|
|
53
|
+
test('both axios instances install the guard', async () => {
|
|
54
|
+
const { readFileSync } = await import('node:fs');
|
|
55
|
+
const { fileURLToPath } = await import('node:url');
|
|
56
|
+
const { dirname, join } = await import('node:path');
|
|
57
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
58
|
+
const src = readFileSync(join(here, '..', '..', 'src', 'wordpress-client.ts'), 'utf8');
|
|
59
|
+
const installs = src.match(/beforeRedirect = guardAgainstMethodDowngrade/g) || [];
|
|
60
|
+
assert.equal(installs.length, 2, 'client and rootClient must both be guarded');
|
|
61
|
+
});
|
|
62
|
+
//# sourceMappingURL=1b0eda46-write-redirect-downgrade.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1b0eda46-write-redirect-downgrade.test.js","sourceRoot":"","sources":["../../src/__tests__/1b0eda46-write-redirect-downgrade.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
|
-
import { isLocalFilePath, resolveLocalFilePath } from '../wordpress-client.js';
|
|
7
|
+
import { isLocalFilePath, looksLikeBase64Payload, resolveLocalFilePath } from '../wordpress-client.js';
|
|
8
8
|
// Ticket 5a19d816: on Windows, respira_upload_media(file="C:/Users/.../photo.jpg")
|
|
9
9
|
// fell through the local-file detection (which only matched '/', './', '../',
|
|
10
10
|
// '~') into the base64 branch. Buffer.from(nonBase64, 'base64') doesn't throw,
|
|
@@ -33,6 +33,40 @@ test('isLocalFilePath: base64 payloads and data:/http(s): URLs are still NOT tre
|
|
|
33
33
|
assert.equal(isLocalFilePath('http://example.com/photo.jpg'), false);
|
|
34
34
|
assert.equal(isLocalFilePath('https://example.com/photo.jpg'), false);
|
|
35
35
|
});
|
|
36
|
+
// Ticket 7c1cd8ac: every JPEG begins with the bytes FF D8 FF, which base64-encode
|
|
37
|
+
// to the four characters "/9j/". So a bare base64 JPEG always starts with a
|
|
38
|
+
// slash and was matched by the POSIX-absolute arm, sending the upload down the
|
|
39
|
+
// local-file branch. It never reached WordPress: it died with
|
|
40
|
+
// "File not found: <the whole payload>", which then echoed the entire image
|
|
41
|
+
// back to the agent inside the error message. The pre-existing base64 case in
|
|
42
|
+
// this file missed it only because its sample blob starts with a letter.
|
|
43
|
+
function base64JpegOfLength(bytes) {
|
|
44
|
+
const buf = Buffer.alloc(bytes);
|
|
45
|
+
buf[0] = 0xff;
|
|
46
|
+
buf[1] = 0xd8;
|
|
47
|
+
buf[2] = 0xff;
|
|
48
|
+
buf[3] = 0xe0;
|
|
49
|
+
for (let i = 4; i < bytes; i++)
|
|
50
|
+
buf[i] = (i * 37) & 0xff;
|
|
51
|
+
return buf.toString('base64');
|
|
52
|
+
}
|
|
53
|
+
test('isLocalFilePath: a bare base64 JPEG is a payload, not a path (starts with "/9j/")', () => {
|
|
54
|
+
const jpeg = base64JpegOfLength(7500);
|
|
55
|
+
assert.equal(jpeg.startsWith('/9j/'), true, 'precondition: JPEG base64 starts with /9j/');
|
|
56
|
+
assert.equal(isLocalFilePath(jpeg), false);
|
|
57
|
+
assert.equal(looksLikeBase64Payload(jpeg), true);
|
|
58
|
+
});
|
|
59
|
+
test('looksLikeBase64Payload: real paths are never mistaken for a payload', () => {
|
|
60
|
+
const longPosix = '/home/user/' + 'directory/'.repeat(40) + 'photo.jpg';
|
|
61
|
+
assert.equal(looksLikeBase64Payload(longPosix), false);
|
|
62
|
+
assert.equal(isLocalFilePath(longPosix), true);
|
|
63
|
+
// No extension, no dots, long: still excluded by the character-mix rule.
|
|
64
|
+
const noExtension = '/home/user/' + 'directory/'.repeat(40) + 'photo';
|
|
65
|
+
assert.equal(looksLikeBase64Payload(noExtension), false);
|
|
66
|
+
assert.equal(isLocalFilePath(noExtension), true);
|
|
67
|
+
// Short blobs stay conservative: not long enough to claim they are a payload.
|
|
68
|
+
assert.equal(looksLikeBase64Payload('/9j/4AAQSkZJRg=='), false);
|
|
69
|
+
});
|
|
36
70
|
test('resolveLocalFilePath: Windows drive-letter paths pass through unchanged (no cwd corruption)', () => {
|
|
37
71
|
// Regression guard: node:path's resolve() is POSIX on non-Windows hosts and
|
|
38
72
|
// does not recognize "C:\..." as absolute, so running it through resolve()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload-media-path-detection.test.js","sourceRoot":"","sources":["../../src/__tests__/upload-media-path-detection.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"upload-media-path-detection.test.js","sourceRoot":"","sources":["../../src/__tests__/upload-media-path-detection.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEvG,mFAAmF;AACnF,8EAA8E;AAC9E,+EAA+E;AAC/E,2EAA2E;AAC3E,mEAAmE;AAEnE,IAAI,CAAC,qFAAqF,EAAE,GAAG,EAAE;IAC/F,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,wBAAwB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9D,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,2BAA2B,CAAC,EAAE,IAAI,CAAC,CAAC;IACjE,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,qBAAqB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,2BAA2B,CAAC,EAAE,IAAI,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oFAAoF,EAAE,GAAG,EAAE;IAC9F,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,gCAAgC,CAAC,EAAE,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,4BAA4B,CAAC,EAAE,IAAI,CAAC,CAAC;AACpE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8EAA8E,EAAE,GAAG,EAAE;IACxF,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,yBAAyB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,qBAAqB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,oBAAoB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+FAA+F,EAAE,GAAG,EAAE;IACzG,oDAAoD;IACpD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,sCAAsC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7E,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,oCAAoC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3E,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,8BAA8B,CAAC,EAAE,KAAK,CAAC,CAAC;IACrE,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,+BAA+B,CAAC,EAAE,KAAK,CAAC,CAAC;AACxE,CAAC,CAAC,CAAC;AAEH,kFAAkF;AAClF,4EAA4E;AAC5E,+EAA+E;AAC/E,8DAA8D;AAC9D,4EAA4E;AAC5E,8EAA8E;AAC9E,yEAAyE;AAEzE,SAAS,kBAAkB,CAAC,KAAa;IACvC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC3D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IACzD,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,IAAI,CAAC,mFAAmF,EAAE,GAAG,EAAE;IAC7F,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,4CAA4C,CAAC,CAAC;IAC1F,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qEAAqE,EAAE,GAAG,EAAE;IAC/E,MAAM,SAAS,GAAG,aAAa,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC;IACxE,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;IACvD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/C,yEAAyE;IACzE,MAAM,WAAW,GAAG,aAAa,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;IACtE,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,CAAC;IACzD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,CAAC;IACjD,8EAA8E;IAC9E,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,kBAAkB,CAAC,EAAE,KAAK,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6FAA6F,EAAE,GAAG,EAAE;IACvG,4EAA4E;IAC5E,2EAA2E;IAC3E,0EAA0E;IAC1E,wEAAwE;IACxE,2EAA2E;IAC3E,wCAAwC;IACxC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,2BAA2B,CAAC,EAAE,2BAA2B,CAAC,CAAC;IAC7F,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,wBAAwB,CAAC,EAAE,wBAAwB,CAAC,CAAC;AACzF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oEAAoE,EAAE,GAAG,EAAE;IAC9E,MAAM,QAAQ,GAAG,oBAAoB,CAAC,gCAAgC,CAAC,CAAC;IACxE,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uFAAuF,EAAE,GAAG,EAAE;IACjG,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,4BAA4B,CAAC,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACxC,aAAa,CAAC,QAAQ,EAAE,sCAAsC,CAAC,CAAC;IAChE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uFAAuF,EAAE,GAAG,EAAE;IACjG,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IACpC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,aAAa,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,CAAC;IACvE,2EAA2E;IAC3E,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;AACtF,CAAC,CAAC,CAAC"}
|
package/dist/server.d.ts
CHANGED
|
@@ -36,6 +36,13 @@ export declare function pickActiveSiteIdForConfig(state: RespiraMcpState, namesp
|
|
|
36
36
|
* versions and the migration path above both stay correct.
|
|
37
37
|
*/
|
|
38
38
|
export declare function applyActiveSiteIdForConfig(state: RespiraMcpState, namespace: string, siteId: string): RespiraMcpState;
|
|
39
|
+
/**
|
|
40
|
+
* Hard ceiling on the text of an error response, so no single tool failure can
|
|
41
|
+
* push an oversized JSON-RPC frame at the host. Middle-truncated: the head
|
|
42
|
+
* carries the reason, the tail carries whatever hint the thrower appended.
|
|
43
|
+
* Override with RESPIRA_MAX_ERROR_MESSAGE_CHARS (0 disables).
|
|
44
|
+
*/
|
|
45
|
+
export declare function truncateErrorMessage(message: string): string;
|
|
39
46
|
/**
|
|
40
47
|
* v7.2.1: hoist plugin-side dropped-styling warnings to the TOP of a build
|
|
41
48
|
* write result so the agent sees them before it inspects anything else.
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAwCzE,MAAM,MAAM,eAAe,GAAG;IAC5B,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAkBf;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,eAAe,CAUjB;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -183,6 +183,22 @@ class ToolTimeoutError extends Error {
|
|
|
183
183
|
this.name = 'ToolTimeoutError';
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Hard ceiling on the text of an error response, so no single tool failure can
|
|
188
|
+
* push an oversized JSON-RPC frame at the host. Middle-truncated: the head
|
|
189
|
+
* carries the reason, the tail carries whatever hint the thrower appended.
|
|
190
|
+
* Override with RESPIRA_MAX_ERROR_MESSAGE_CHARS (0 disables).
|
|
191
|
+
*/
|
|
192
|
+
export function truncateErrorMessage(message) {
|
|
193
|
+
const override = parseInt(process.env.RESPIRA_MAX_ERROR_MESSAGE_CHARS || '', 10);
|
|
194
|
+
const cap = Number.isFinite(override) && override >= 0 ? override : 4000;
|
|
195
|
+
if (cap === 0 || message.length <= cap)
|
|
196
|
+
return message;
|
|
197
|
+
const head = message.slice(0, Math.floor(cap * 0.75));
|
|
198
|
+
const tail = message.slice(-Math.floor(cap * 0.25));
|
|
199
|
+
return `${head}\n\n[... ${message.length - cap} characters of this error message omitted. ` +
|
|
200
|
+
`The full value was most likely an argument echoed back verbatim; it is not needed to act on the error. ...]\n\n${tail}`;
|
|
201
|
+
}
|
|
186
202
|
function getMaxToolTimeoutMs() {
|
|
187
203
|
return Math.max(5000, parseInt(process.env.RESPIRA_MAX_TOOL_TIMEOUT_MS || '120000', 10));
|
|
188
204
|
}
|
|
@@ -2023,6 +2039,18 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
2023
2039
|
}
|
|
2024
2040
|
}
|
|
2025
2041
|
}
|
|
2042
|
+
// Ticket 7c1cd8ac. capResponseSize() guards the SUCCESS path only, so
|
|
2043
|
+
// every error envelope left this server uncapped. A single
|
|
2044
|
+
// respira_upload_media call whose payload was echoed back inside the
|
|
2045
|
+
// message ("File not found: <the whole base64 image>") produced a
|
|
2046
|
+
// 1.2 MB JSON-RPC frame in testing, against a 100 KiB cap that exists
|
|
2047
|
+
// precisely because MCP hosts stop reading — and then answer nothing
|
|
2048
|
+
// else on that connection — when one frame is too large. That is the
|
|
2049
|
+
// "every later tool call times out until I restart the server" half of
|
|
2050
|
+
// this ticket. An error message has no business being longer than a
|
|
2051
|
+
// paragraph; truncate in the middle so both the reason and any trailing
|
|
2052
|
+
// hint survive.
|
|
2053
|
+
errorMessage = truncateErrorMessage(errorMessage);
|
|
2026
2054
|
const errorWithUpdateNotice = await this.appendUpdateNoticeToError(errorMessage);
|
|
2027
2055
|
const activeSite = this.getActiveSiteSummary(args);
|
|
2028
2056
|
// B-17 (v6.18.7): gate the JS stack trace behind RESPIRA_DEBUG.
|
|
@@ -3091,7 +3119,7 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
3091
3119
|
},
|
|
3092
3120
|
{
|
|
3093
3121
|
name: 'wordpress_upload_media',
|
|
3094
|
-
description: 'Upload a media file (image, document, video) to WordPress. Supports base64 encoded files, file URLs, file paths, or raw SVG markup strings (starting with <svg or <?xml). Fails fast with a clear error if the file exceeds 50MB (override via RESPIRA_MAX_UPLOAD_MB) or the upload does not complete within
|
|
3122
|
+
description: 'Upload a media file (image, document, video) to WordPress. Supports base64 encoded files, file URLs, file paths, or raw SVG markup strings (starting with <svg or <?xml). Fails fast with a clear error if the file exceeds 50MB (override via RESPIRA_MAX_UPLOAD_MB) or the upload does not complete within 90 seconds (override via RESPIRA_UPLOAD_TIMEOUT_MS).',
|
|
3095
3123
|
inputSchema: {
|
|
3096
3124
|
type: 'object',
|
|
3097
3125
|
properties: {
|