@respira/wordpress-mcp-server 7.5.1 → 7.5.2
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 +18 -2
- package/dist/__tests__/usage-emitter-privacy.test.d.ts +2 -0
- package/dist/__tests__/usage-emitter-privacy.test.d.ts.map +1 -0
- package/dist/__tests__/usage-emitter-privacy.test.js +16 -0
- package/dist/__tests__/usage-emitter-privacy.test.js.map +1 -0
- package/dist/__tests__/write-retry-safety.test.d.ts +2 -0
- package/dist/__tests__/write-retry-safety.test.d.ts.map +1 -0
- package/dist/__tests__/write-retry-safety.test.js +36 -0
- package/dist/__tests__/write-retry-safety.test.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +35 -2
- package/dist/server.js.map +1 -1
- package/dist/usage-emitter.d.ts +12 -6
- package/dist/usage-emitter.d.ts.map +1 -1
- package/dist/usage-emitter.js +89 -31
- package/dist/usage-emitter.js.map +1 -1
- package/dist/wordpress-client.d.ts +15 -0
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +72 -7
- package/dist/wordpress-client.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,6 +35,22 @@ Other WordPress MCP servers wrap the REST API. They can create posts and pages,
|
|
|
35
35
|
|
|
36
36
|
Respira includes a WordPress plugin that gives AI **native access** to 16 page builders, plus element-level precision, full page creation from structure, HTML-to-builder conversion, storefront design intelligence, stock image search, and bulk operations across hundreds of pages.
|
|
37
37
|
|
|
38
|
+
### New in v7.5.2: reconnect-safe writes and drop diagnostics
|
|
39
|
+
|
|
40
|
+
- **Writes carry a stable idempotency key.** The connector automatically retries transient reads. It retries a write only when WordPress explicitly confirms replay support; an ambiguous timeout/reset is reported as `respira_write_outcome_unknown` so the agent verifies the target before trying again.
|
|
41
|
+
- **Telemetry queues and retries independently of tool execution.** A dashboard or network interruption cannot block a WordPress call, and failed metadata batches are retained with bounded exponential backoff.
|
|
42
|
+
- **Local API keys stay local.** With Respira for WordPress 7.5.46+, a local `respira_*` key is exchanged inside WordPress for a revocable, site-scoped telemetry bearer. The central service never receives that local key.
|
|
43
|
+
- **The quality dashboard can separate exact retries from editing sweeps.** It records stable session/call identity, transport, incomplete calls, connector/plugin versions, total vs tool time, and privacy-safe stage durations. Target comparison uses a secret HMAC; arguments, results, prompts, and WordPress content are never sent.
|
|
44
|
+
- **Native URL-connected clients gain timeout-safe writes.** Plugin 7.5.46 adds `_respira_async: true`, `respira_get_job_status`, stable `Mcp-Session-Id` responses, replay-safe `_respira_call_id`, and a native `respira_diagnose_connection` tool. The native endpoint is stateless HTTP—independent POST requests, not a persistent “write channel.”
|
|
45
|
+
|
|
46
|
+
### Also shipped since the README's previous v7.1 refresh
|
|
47
|
+
|
|
48
|
+
- **v7.2:** cascade session rollback; dropped-styling/no-op guidance; page-parent schemas; Oxygen 6 build guidance; completed live-edit confirmation parameters; bounded heavy-write/docs timeouts; and route-aware capability negotiation that hides tools an older plugin cannot execute.
|
|
49
|
+
- **v7.3:** the WooCommerce add-on expanded from 21 to 56 tools across catalog, pricing, stock, storefront, brands, variations/attributes, coupons, customers, refunds, and AI-readiness.
|
|
50
|
+
- **v7.4:** Agent-Ready Commerce expanded WooCommerce to 79 tools with feeds + store `llms.txt`, readiness fixes, signed cart links/attribution, and Subscriptions, Bookings, and Memberships writes. v7.4.1 then made newly connected dashboard sites self-heal into a running connector session.
|
|
51
|
+
- **v7.5.0:** eight STAGGS configurator tools brought the WooCommerce total to 87.
|
|
52
|
+
- **v7.5.1:** fixed normalized ACF tool routing and preserved dashboard-token 401 machine codes—the two fixes shipped immediately before this reliability release.
|
|
53
|
+
|
|
38
54
|
### New in v7.1 "Roots"
|
|
39
55
|
|
|
40
56
|
The biggest release since launch.
|
|
@@ -43,7 +59,7 @@ The biggest release since launch.
|
|
|
43
59
|
- **Make a page responsive in one call** on Bricks, Elementor, Divi 4, Beaver Builder, and WPBakery.
|
|
44
60
|
- **Playbooks.** Teach an agent a multi-step workflow once and rerun it as a single callable WordPress Ability. Five tools (create / list / get / update / delete), with static cycle detection and a runtime depth cap.
|
|
45
61
|
- **Agent-creatable Custom Post Types, taxonomies, and ACF field groups** (twelve tools), stored in options and registered on `init`. No PHP files, server-side slug allowlist, conservative deletes that report orphans.
|
|
46
|
-
- **Native in-WP MCP endpoint.** Point a client straight at `your-site
|
|
62
|
+
- **Native in-WP MCP endpoint.** Point a client straight at `your-site/wp-json/respira/v1/mcp` (Streamable HTTP JSON-RPC; `?rest_route=` fallback supported) with an API key, no npx process required. One-click connect-by-link mints and installs the key for you.
|
|
47
63
|
- **System certificate-store trust.** The connector trusts the OS certificate store at startup (verification stays on) with a bundled Mozilla CA fallback, so valid public certs stop being rejected on Claude Desktop's bundled Node.
|
|
48
64
|
- **Bricks design-token import + section presets**, the **Elementor 4 atomic write path** (partial: 20 atomic element types with `$$type` envelopes, per-breakpoint + per-state styling, and a clear 422 on unsupported types instead of a silent drop), and **`respira_generate_activity_report`** for client-ready reports.
|
|
49
65
|
|
|
@@ -61,7 +77,7 @@ The triage rule, baked into the handshake instructions: tool error → apply hin
|
|
|
61
77
|
|
|
62
78
|
### v6.16.0: usage telemetry ships by default
|
|
63
79
|
|
|
64
|
-
|
|
80
|
+
v6.16 introduced default per-tool telemetry so customers did not have to configure an OTEL bearer. As of v7.5.2 with plugin 7.5.46+, local WordPress API keys are never sent to the central ingest: the connector asks WordPress for a revocable, site-scoped telemetry token instead. Privacy remains metadata-only—no prompts, raw tool arguments, tool results, or WordPress content. `RESPIRA_USAGE_OPT_OUT=1` disables the entire stream.
|
|
65
81
|
|
|
66
82
|
### v6.3 — MCP Protocol Compliance
|
|
67
83
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-emitter-privacy.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/usage-emitter-privacy.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import { isDirectTelemetryBearer } from '../usage-emitter.js';
|
|
4
|
+
describe('usage emitter credential privacy', () => {
|
|
5
|
+
it('never treats a local WordPress key as a central-ingest bearer', () => {
|
|
6
|
+
assert.equal(isDirectTelemetryBearer('respira_local-secret'), false);
|
|
7
|
+
assert.equal(isDirectTelemetryBearer('respira_0123456789abcdef'), false);
|
|
8
|
+
});
|
|
9
|
+
it('accepts only explicitly central-scoped bearer families', () => {
|
|
10
|
+
assert.equal(isDirectTelemetryBearer('respira_site_abc'), true);
|
|
11
|
+
assert.equal(isDirectTelemetryBearer('rsp_at_abc'), true);
|
|
12
|
+
assert.equal(isDirectTelemetryBearer('rp_mcp_abc'), true);
|
|
13
|
+
assert.equal(isDirectTelemetryBearer('rp_otel_abc'), false);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=usage-emitter-privacy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-emitter-privacy.test.js","sourceRoot":"","sources":["../../src/__tests__/usage-emitter-privacy.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,sBAAsB,CAAC,EAAE,KAAK,CAAC,CAAC;QACrE,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,0BAA0B,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,kBAAkB,CAAC,EAAE,IAAI,CAAC,CAAC;QAChE,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-retry-safety.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/write-retry-safety.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { ensureWriteIdempotencyKey, isReplaySafeRetry, } from "../wordpress-client.js";
|
|
4
|
+
test("mutations receive one stable idempotency key while reads do not", () => {
|
|
5
|
+
const write = { method: "post", headers: {} };
|
|
6
|
+
ensureWriteIdempotencyKey(write, () => "call_12345678");
|
|
7
|
+
ensureWriteIdempotencyKey(write, () => "should_not_replace");
|
|
8
|
+
assert.equal(write.headers["Idempotency-Key"], "call_12345678");
|
|
9
|
+
const read = { method: "get", headers: {} };
|
|
10
|
+
ensureWriteIdempotencyKey(read, () => "should_not_exist");
|
|
11
|
+
assert.equal(read.headers["Idempotency-Key"], undefined);
|
|
12
|
+
});
|
|
13
|
+
test("reads retry transient responses and connection failures", () => {
|
|
14
|
+
assert.equal(isReplaySafeRetry({ method: "GET", status: 500 }), true);
|
|
15
|
+
assert.equal(isReplaySafeRetry({ method: "GET", connectionCode: "ECONNRESET" }), true);
|
|
16
|
+
assert.equal(isReplaySafeRetry({ method: "GET", status: 404 }), false);
|
|
17
|
+
});
|
|
18
|
+
test("writes retry only explicit replay-safe gateway responses", () => {
|
|
19
|
+
assert.equal(isReplaySafeRetry({
|
|
20
|
+
method: "POST",
|
|
21
|
+
status: 503,
|
|
22
|
+
idempotencySupported: true,
|
|
23
|
+
}), true);
|
|
24
|
+
assert.equal(isReplaySafeRetry({
|
|
25
|
+
method: "POST",
|
|
26
|
+
status: 503,
|
|
27
|
+
idempotencySupported: false,
|
|
28
|
+
}), false);
|
|
29
|
+
assert.equal(isReplaySafeRetry({
|
|
30
|
+
method: "POST",
|
|
31
|
+
status: 500,
|
|
32
|
+
idempotencySupported: true,
|
|
33
|
+
}), false);
|
|
34
|
+
assert.equal(isReplaySafeRetry({ method: "POST", connectionCode: "ECONNRESET" }), false);
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=write-retry-safety.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-retry-safety.test.js","sourceRoot":"","sources":["../../src/__tests__/write-retry-safety.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAEhC,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC3E,MAAM,KAAK,GAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACnD,yBAAyB,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC;IACxD,yBAAyB,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,oBAAoB,CAAC,CAAC;IAC7D,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,eAAe,CAAC,CAAC;IAEhE,MAAM,IAAI,GAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACjD,yBAAyB,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,SAAS,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yDAAyD,EAAE,GAAG,EAAE;IACnE,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,EAClE,IAAI,CACL,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;AACzE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACpE,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC;QAChB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,GAAG;QACX,oBAAoB,EAAE,IAAI;KAC3B,CAAC,EACF,IAAI,CACL,CAAC;IACF,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC;QAChB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,GAAG;QACX,oBAAoB,EAAE,KAAK;KAC5B,CAAC,EACF,KAAK,CACN,CAAC;IACF,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC;QAChB,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,GAAG;QACX,oBAAoB,EAAE,IAAI;KAC3B,CAAC,EACF,KAAK,CACN,CAAC;IACF,MAAM,CAAC,KAAK,CACV,iBAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,EACnE,KAAK,CACN,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
package/dist/server.d.ts
CHANGED
|
@@ -203,6 +203,8 @@ export declare class RespiraWordPressServer {
|
|
|
203
203
|
*/
|
|
204
204
|
private normalizeArgsForBackCompat;
|
|
205
205
|
private handleToolCall;
|
|
206
|
+
/** Privacy-safe target HMAC: arguments never leave this process. */
|
|
207
|
+
private telemetryArgsHash;
|
|
206
208
|
/**
|
|
207
209
|
* v6.18.2: enforce a response-size cap on tool results.
|
|
208
210
|
*
|
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;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAqBH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAsQzE,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,YAAY,CAA4B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,YAAY,CAA4B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,mBAAmB,CAAS;IACpC,iFAAiF;IACjF,OAAO,CAAC,iBAAiB,CAAK;IAC9B,iFAAiF;IACjF,OAAO,CAAC,mBAAmB,CAAgC;IAE3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAsB;IAEhE;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;gBA4Bb,WAAW,EAAE,mBAAmB,EAAE,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE;IAiVvE,OAAO,CAAC,cAAc;IAItB;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IA4BrB,gEAAgE;IAChE,OAAO,CAAC,aAAa;IAUrB;;;;;;OAMG;YACW,UAAU;IA2CxB;;;;;;;;;;;;;;OAcG;YACW,WAAW;IAiJzB;;;;;;;;;OASG;YACW,kBAAkB;IAiHhC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;YACW,qBAAqB;YAiBrB,kBAAkB;IA6FhC,yFAAyF;IACzF,OAAO,CAAC,gBAAgB;IASxB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,6BAA6B;IA0BrC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IA+BjC,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,aAAa;YAmQP,kBAAkB;YA6BlB,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YAyBd,QAAQ;IA+vFtB;;;;;;OAMG;IACH,yEAAyE;IACzE,OAAO,CAAC,mBAAmB,CAAoD;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;YAEpC,oBAAoB;YAqDpB,2BAA2B;IAazC;;;;OAIG;YACW,cAAc;IAY5B,OAAO,CAAC,mBAAmB;IA2uC3B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAe3C;IAEF;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;YAmCpB,cAAc;IA4G5B,oEAAoE;IACpE,OAAO,CAAC,iBAAiB;IAoBzB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,eAAe;YAuCT,gBAAgB;IAimC9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAwUzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6UxB,GAAG;CAyCV"}
|
package/dist/server.js
CHANGED
|
@@ -11,12 +11,16 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
|
11
11
|
import { homedir } from 'os';
|
|
12
12
|
import { dirname, join, resolve } from 'path';
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
|
+
import { createHmac, randomUUID } from 'node:crypto';
|
|
14
15
|
import { WordPressClient } from './wordpress-client.js';
|
|
15
16
|
import { RespiraVersionChecker } from './version-checker.js';
|
|
16
17
|
import { getBricksTools, dispatchBricksTool } from './bricks-tools.js';
|
|
17
18
|
import { getElementorTools, dispatchElementorTool } from './elementor-tools.js';
|
|
18
19
|
import { getAcfTools, resolveAcfToolName } from './acf-tools.js';
|
|
19
20
|
import { getUsageEmitter, deriveToolKind } from './usage-emitter.js';
|
|
21
|
+
// Process-local secret keeps target hashes useful for same-session retry
|
|
22
|
+
// detection without making low-entropy WordPress ids reversible centrally.
|
|
23
|
+
const TELEMETRY_HASH_SECRET = randomUUID();
|
|
20
24
|
/**
|
|
21
25
|
* Read the server version from package.json at module load time so the
|
|
22
26
|
* MCP handshake, the `instructions` block, and the version-checker all
|
|
@@ -617,7 +621,7 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
617
621
|
// gracefully when neither is.
|
|
618
622
|
if (config.apiKey && config.url) {
|
|
619
623
|
try {
|
|
620
|
-
getUsageEmitter().registerSiteToken(config.url, config.apiKey);
|
|
624
|
+
getUsageEmitter().registerSiteToken(config.url, config.apiKey, () => client.getTelemetryToken());
|
|
621
625
|
}
|
|
622
626
|
catch {
|
|
623
627
|
// Best-effort; usage telemetry never blocks server startup.
|
|
@@ -1161,7 +1165,7 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
1161
1165
|
this.defaultSiteId = s.id;
|
|
1162
1166
|
}
|
|
1163
1167
|
try {
|
|
1164
|
-
getUsageEmitter().registerSiteToken(s.url, s.apiKey);
|
|
1168
|
+
getUsageEmitter().registerSiteToken(s.url, s.apiKey, () => client.getTelemetryToken());
|
|
1165
1169
|
}
|
|
1166
1170
|
catch {
|
|
1167
1171
|
// usage telemetry never blocks
|
|
@@ -5784,13 +5788,16 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
5784
5788
|
finally {
|
|
5785
5789
|
if (emitter.isEnabled()) {
|
|
5786
5790
|
let siteUrl = null;
|
|
5791
|
+
let pluginVersion = null;
|
|
5787
5792
|
try {
|
|
5788
5793
|
if (!SITE_AGNOSTIC_TOOLS.has(canonical)) {
|
|
5789
5794
|
const client = this.resolveClient(args);
|
|
5790
5795
|
siteUrl = client.getSiteUrl();
|
|
5796
|
+
pluginVersion = client.pluginVersion;
|
|
5791
5797
|
}
|
|
5792
5798
|
else if (this.currentSite) {
|
|
5793
5799
|
siteUrl = this.currentSite.getSiteUrl();
|
|
5800
|
+
pluginVersion = this.currentSite.pluginVersion;
|
|
5794
5801
|
}
|
|
5795
5802
|
}
|
|
5796
5803
|
catch {
|
|
@@ -5845,6 +5852,10 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
5845
5852
|
durationMs: Date.now() - startedAt,
|
|
5846
5853
|
success: dispatchError === null,
|
|
5847
5854
|
errorCode,
|
|
5855
|
+
targetArgsHash: this.telemetryArgsHash(canonical, args),
|
|
5856
|
+
stageTimings: result?.write_diag?.stage_timings || result?.stage_timings || null,
|
|
5857
|
+
pluginVersion,
|
|
5858
|
+
mcpVersion: MCP_SERVER_VERSION,
|
|
5848
5859
|
});
|
|
5849
5860
|
}
|
|
5850
5861
|
}
|
|
@@ -5857,6 +5868,28 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
5857
5868
|
}
|
|
5858
5869
|
return this.capResponseSize(canonical, result);
|
|
5859
5870
|
}
|
|
5871
|
+
/** Privacy-safe target HMAC: arguments never leave this process. */
|
|
5872
|
+
telemetryArgsHash(toolName, args) {
|
|
5873
|
+
try {
|
|
5874
|
+
const canonicalize = (value) => {
|
|
5875
|
+
if (Array.isArray(value))
|
|
5876
|
+
return value.map(canonicalize);
|
|
5877
|
+
if (value && typeof value === 'object') {
|
|
5878
|
+
return Object.keys(value).sort().reduce((out, key) => {
|
|
5879
|
+
out[key] = canonicalize(value[key]);
|
|
5880
|
+
return out;
|
|
5881
|
+
}, {});
|
|
5882
|
+
}
|
|
5883
|
+
return value;
|
|
5884
|
+
};
|
|
5885
|
+
return createHmac('sha256', TELEMETRY_HASH_SECRET)
|
|
5886
|
+
.update(`${toolName}|${JSON.stringify(canonicalize(args))}`)
|
|
5887
|
+
.digest('hex');
|
|
5888
|
+
}
|
|
5889
|
+
catch {
|
|
5890
|
+
return null;
|
|
5891
|
+
}
|
|
5892
|
+
}
|
|
5860
5893
|
/**
|
|
5861
5894
|
* v6.18.2: enforce a response-size cap on tool results.
|
|
5862
5895
|
*
|