@diviops/mcp-server 1.4.0 → 1.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 +128 -299
- package/dist/envelope.d.ts +117 -0
- package/dist/envelope.js +171 -0
- package/dist/index.js +846 -428
- package/dist/wp-cli.d.ts +8 -0
- package/dist/wp-cli.js +81 -20
- package/dist/wp-client.d.ts +38 -0
- package/dist/wp-client.js +147 -2
- package/package.json +1 -1
package/dist/envelope.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized response envelope (#489).
|
|
3
|
+
*
|
|
4
|
+
* Every diviops tool — server-local or plugin-routed — returns:
|
|
5
|
+
* success: { ok: true, data: T }
|
|
6
|
+
* error: { ok: false, error: { code: string, message: string, hint?: string } }
|
|
7
|
+
*
|
|
8
|
+
* Adoption is per-namespace (#489 + per-namespace follow-ups). Tools that
|
|
9
|
+
* have not adopted yet still pass legacy raw payloads through to consumers;
|
|
10
|
+
* the helpers here coexist with that shape during the rollout window.
|
|
11
|
+
*
|
|
12
|
+
* HTTP status stays orthogonal to envelope shape on the wire — the plugin
|
|
13
|
+
* emits 200 on ok:true and the real status (400/404/409/412/500) on ok:false.
|
|
14
|
+
* The server-side `wp.request` raises on non-2xx today, so error envelopes
|
|
15
|
+
* arrive as thrown errors carrying the JSON body; `wrapResponse` is the
|
|
16
|
+
* normalizer that maps both branches to the typed `DiviopsResponse<T>`.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Standard error code vocabulary. Plugin and server emit only these codes
|
|
20
|
+
* for the matching condition; namespace-specific extensions use the
|
|
21
|
+
* `<namespace>.<reason>` form (e.g. `library.import_conflict`).
|
|
22
|
+
*/
|
|
23
|
+
export const ErrorCodes = {
|
|
24
|
+
/** Target ID does not resolve. HTTP 404. */
|
|
25
|
+
NOT_FOUND: "not_found",
|
|
26
|
+
/** Schema violation, malformed args. HTTP 400. */
|
|
27
|
+
INVALID_INPUT: "invalid_input",
|
|
28
|
+
/** Underlying WordPress error (wraps WP_Error or REST framework error). HTTP 500. */
|
|
29
|
+
WP_ERROR: "wp_error",
|
|
30
|
+
/** Divi-specific error (block parser, validator, etc.). HTTP 500. */
|
|
31
|
+
DIVI_ERROR: "divi_error",
|
|
32
|
+
/** Plugin version below required for this tool (#486 handshake miss). HTTP 412. */
|
|
33
|
+
CAPABILITY_MISSING: "capability_missing",
|
|
34
|
+
/** validate_blocks-detected shape error in submitted markup. HTTP 400. */
|
|
35
|
+
VALIDATION_FAILED: "validation_failed",
|
|
36
|
+
/** Uniqueness collision (#542 / #543). HTTP 409. */
|
|
37
|
+
CONFLICT: "conflict",
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Typed throw used inside server-local handlers to short-circuit with a
|
|
41
|
+
* specific envelope error code. `wrapResponse` catches it and re-emits.
|
|
42
|
+
*/
|
|
43
|
+
export class DiviopsError extends Error {
|
|
44
|
+
code;
|
|
45
|
+
hint;
|
|
46
|
+
data;
|
|
47
|
+
constructor(code, message, hint, data) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = "DiviopsError";
|
|
50
|
+
this.code = code;
|
|
51
|
+
this.hint = hint;
|
|
52
|
+
this.data = data;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Throw a `DiviopsError`. Helper for ergonomics — `withCode(...)` reads
|
|
57
|
+
* better at call sites than `throw new DiviopsError(...)` and matches the
|
|
58
|
+
* kickoff's helper-API sketch. Pass `data` when the failure carries a
|
|
59
|
+
* structured payload (e.g. wp-cli exit code + captured streams).
|
|
60
|
+
*/
|
|
61
|
+
export function withCode(code, message, hint, data) {
|
|
62
|
+
throw new DiviopsError(code, message, hint, data);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Type guard: is `value` already shaped as a `DiviopsResponse`?
|
|
66
|
+
*
|
|
67
|
+
* Plugin-routed tools return the envelope; server-local tools may return
|
|
68
|
+
* raw values. `wrapResponse` uses this to avoid double-wrapping.
|
|
69
|
+
*/
|
|
70
|
+
export function isEnveloped(value) {
|
|
71
|
+
if (typeof value !== "object" || value === null)
|
|
72
|
+
return false;
|
|
73
|
+
const v = value;
|
|
74
|
+
if (v.ok === true && "data" in v)
|
|
75
|
+
return true;
|
|
76
|
+
if (v.ok === false && typeof v.error === "object" && v.error !== null) {
|
|
77
|
+
const e = v.error;
|
|
78
|
+
return typeof e.code === "string" && typeof e.message === "string";
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Run an async producer and normalize its outcome to a `DiviopsResponse<T>`.
|
|
84
|
+
*
|
|
85
|
+
* Behavior:
|
|
86
|
+
* - Producer returns an already-enveloped value → pass through unchanged
|
|
87
|
+
* (no double-wrap). Plugin-routed tools land here.
|
|
88
|
+
* - Producer returns a raw value → wrap as `{ok: true, data: <value>}`.
|
|
89
|
+
* Server-local tools land here.
|
|
90
|
+
* - Producer throws `DiviopsError` → emit `{ok: false, error: {code, message, hint?}}`.
|
|
91
|
+
* - Producer throws anything else → emit `{ok: false, error: {code: 'wp_error', message: e.message}}`.
|
|
92
|
+
* The fallback covers thrown HTTP errors from `wp.request` whose body
|
|
93
|
+
* is the plugin's envelope JSON; we attempt to parse the message and
|
|
94
|
+
* promote the embedded code/hint when present.
|
|
95
|
+
*/
|
|
96
|
+
export async function wrapResponse(producer) {
|
|
97
|
+
try {
|
|
98
|
+
const result = await producer();
|
|
99
|
+
if (isEnveloped(result)) {
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
return { ok: true, data: result };
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
if (e instanceof DiviopsError) {
|
|
106
|
+
const error = { code: e.code, message: e.message };
|
|
107
|
+
if (e.hint)
|
|
108
|
+
error.hint = e.hint;
|
|
109
|
+
if (e.data !== undefined)
|
|
110
|
+
error.data = e.data;
|
|
111
|
+
return { ok: false, error };
|
|
112
|
+
}
|
|
113
|
+
return { ok: false, error: parseThrownError(e) };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Extract envelope error info from a thrown value.
|
|
118
|
+
*
|
|
119
|
+
* `wp.request` throws Errors of the form
|
|
120
|
+
* `WordPress API error (NNN): <body>`
|
|
121
|
+
* where `<body>` may be the plugin's envelope JSON. We try to recover the
|
|
122
|
+
* embedded code/hint in that case so re-thrown plugin errors don't lose
|
|
123
|
+
* their structured shape on round-trip through `wrapResponse`.
|
|
124
|
+
*/
|
|
125
|
+
function parseThrownError(e) {
|
|
126
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
127
|
+
const bodyMatch = message.match(/^WordPress API error \(\d+\):\s*(.*)$/s);
|
|
128
|
+
if (bodyMatch) {
|
|
129
|
+
try {
|
|
130
|
+
const parsed = JSON.parse(bodyMatch[1]);
|
|
131
|
+
if (parsed &&
|
|
132
|
+
typeof parsed === "object" &&
|
|
133
|
+
parsed.ok === false &&
|
|
134
|
+
parsed.error &&
|
|
135
|
+
typeof parsed.error === "object" &&
|
|
136
|
+
typeof parsed.error.code === "string" &&
|
|
137
|
+
typeof parsed.error.message === "string") {
|
|
138
|
+
const out = {
|
|
139
|
+
code: parsed.error.code,
|
|
140
|
+
message: parsed.error.message,
|
|
141
|
+
};
|
|
142
|
+
if (typeof parsed.error.hint === "string")
|
|
143
|
+
out.hint = parsed.error.hint;
|
|
144
|
+
if (parsed.error.data !== undefined)
|
|
145
|
+
out.data = parsed.error.data;
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Body wasn't JSON, fall through to generic wp_error.
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { code: ErrorCodes.WP_ERROR, message };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Map the data of a `DiviopsResponse` through `fn` (success branch only).
|
|
157
|
+
* Errors pass through unchanged. Use to post-process a wrapped result
|
|
158
|
+
* (e.g. shrink schema, project fields) without unwrapping by hand.
|
|
159
|
+
*/
|
|
160
|
+
export function envelopeMap(response, fn) {
|
|
161
|
+
if (response.ok)
|
|
162
|
+
return { ok: true, data: fn(response.data) };
|
|
163
|
+
return response;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Serialize a `DiviopsResponse` as the JSON string an MCP tool emits in its
|
|
167
|
+
* `content[0].text` slot. Single emit point keeps the wire shape consistent.
|
|
168
|
+
*/
|
|
169
|
+
export function serializeEnvelope(response) {
|
|
170
|
+
return JSON.stringify(response);
|
|
171
|
+
}
|