@zigrivers/surface-mcp 0.1.0 → 0.2.0
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/dist/index.d.ts +10 -3
- package/dist/index.js +435 -100
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,24 +1,286 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
import path from "path";
|
|
2
3
|
import { pathToFileURL } from "url";
|
|
3
4
|
import {
|
|
4
5
|
DEFAULT_COMPOSITION_STAGE_IDS,
|
|
5
6
|
DEFAULT_SURFACE_CONFIG,
|
|
7
|
+
browserQaFlowRunsForGate,
|
|
8
|
+
browserQaFlowSeverityFromWithFlows,
|
|
9
|
+
browserQaGateTargetCli,
|
|
6
10
|
createBoundedAlternatives,
|
|
7
11
|
createTrackedFinding,
|
|
8
12
|
createSurfaceComposition,
|
|
9
|
-
createSurfaceError,
|
|
13
|
+
createSurfaceError as createSurfaceError2,
|
|
10
14
|
diffTrackedFindings,
|
|
11
|
-
err,
|
|
15
|
+
err as err2,
|
|
16
|
+
evaluateGateWithQaFlows,
|
|
12
17
|
instantiateLensExecutionPlan,
|
|
13
|
-
isOk,
|
|
14
|
-
ok,
|
|
18
|
+
isOk as isOk2,
|
|
19
|
+
ok as ok2,
|
|
15
20
|
scoreFinding,
|
|
16
21
|
selectLensExecutionPlan,
|
|
17
22
|
synthesizeBacklog,
|
|
18
|
-
toMcpError,
|
|
23
|
+
toMcpError as toMcpError2,
|
|
19
24
|
transitionTrackedFinding
|
|
20
25
|
} from "@zigrivers/surface-core";
|
|
26
|
+
import { z as z2 } from "zod";
|
|
27
|
+
|
|
28
|
+
// src/browser-qa-tools.ts
|
|
29
|
+
import {
|
|
30
|
+
createSurfaceError,
|
|
31
|
+
err,
|
|
32
|
+
isOk,
|
|
33
|
+
ok,
|
|
34
|
+
toMcpError
|
|
35
|
+
} from "@zigrivers/surface-core";
|
|
21
36
|
import { z } from "zod";
|
|
37
|
+
var BROWSER_QA_MCP_SERVER_TOOL_NAMES = [
|
|
38
|
+
"surface_qa",
|
|
39
|
+
"surface_explore",
|
|
40
|
+
"surface_flow_run",
|
|
41
|
+
"surface_flow_list",
|
|
42
|
+
"surface_flow_promote",
|
|
43
|
+
"surface_evidence",
|
|
44
|
+
"surface_replay",
|
|
45
|
+
"surface_report_qa",
|
|
46
|
+
"surface_artifact_read"
|
|
47
|
+
];
|
|
48
|
+
var TargetInputSchema = z.object({
|
|
49
|
+
kind: z.enum(["url", "localhost", "route", "screenshot", "component", "dom"]),
|
|
50
|
+
ref: z.string().min(1),
|
|
51
|
+
theme: z.enum(["light", "dark"]).optional(),
|
|
52
|
+
viewport: z.object({
|
|
53
|
+
height: z.number().int().positive(),
|
|
54
|
+
label: z.enum(["mobile", "tablet", "desktop"]),
|
|
55
|
+
width: z.number().int().positive()
|
|
56
|
+
}).strict().optional()
|
|
57
|
+
}).strict();
|
|
58
|
+
var SurfaceQaInputSchema = z.object({
|
|
59
|
+
actionPolicyRef: z.string().min(1).optional(),
|
|
60
|
+
allowedDomains: z.array(z.string().min(1)).optional(),
|
|
61
|
+
ci: z.boolean().optional(),
|
|
62
|
+
evidence: z.enum(["minimal", "failures", "full"]).optional(),
|
|
63
|
+
explore: z.boolean().optional(),
|
|
64
|
+
flows: z.array(z.string().min(1)).optional(),
|
|
65
|
+
maxActions: z.number().int().positive().optional(),
|
|
66
|
+
maxDepth: z.number().int().positive().optional(),
|
|
67
|
+
maxStates: z.number().int().positive().optional(),
|
|
68
|
+
network: z.enum(["summary", "har", "off"]).optional(),
|
|
69
|
+
scope: z.string().min(1).optional(),
|
|
70
|
+
sessionMode: z.enum(["isolated", "shared"]).optional(),
|
|
71
|
+
stateLockTimeoutMs: z.number().int().positive().optional(),
|
|
72
|
+
target: TargetInputSchema,
|
|
73
|
+
task: z.string().min(1).optional(),
|
|
74
|
+
video: z.enum(["off", "failures", "all"]).optional()
|
|
75
|
+
}).strict();
|
|
76
|
+
var SurfaceExploreInputSchema = z.object({
|
|
77
|
+
actionPolicyRef: z.string().min(1).optional(),
|
|
78
|
+
allowedDomains: z.array(z.string().min(1)).optional(),
|
|
79
|
+
evidence: z.enum(["minimal", "failures", "full"]).optional(),
|
|
80
|
+
maxActions: z.number().int().positive().optional(),
|
|
81
|
+
maxDepth: z.number().int().positive().optional(),
|
|
82
|
+
maxStates: z.number().int().positive().optional(),
|
|
83
|
+
network: z.enum(["summary", "har", "off"]).optional(),
|
|
84
|
+
scope: z.string().min(1).optional(),
|
|
85
|
+
sessionMode: z.enum(["isolated", "shared"]).optional(),
|
|
86
|
+
stateLockTimeoutMs: z.number().int().positive().optional(),
|
|
87
|
+
target: TargetInputSchema,
|
|
88
|
+
task: z.string().min(1).optional(),
|
|
89
|
+
video: z.enum(["off", "failures", "all"]).optional()
|
|
90
|
+
}).strict();
|
|
91
|
+
var SurfaceFlowRunInputSchema = z.object({
|
|
92
|
+
actionPolicyRef: z.string().min(1).optional(),
|
|
93
|
+
baseUrl: z.string().min(1).optional(),
|
|
94
|
+
ci: z.boolean().optional(),
|
|
95
|
+
flowPath: z.string().min(1),
|
|
96
|
+
localhost: z.boolean().optional(),
|
|
97
|
+
target: z.string().min(1).optional(),
|
|
98
|
+
url: z.string().min(1).optional()
|
|
99
|
+
}).strict().refine(
|
|
100
|
+
(input) => [input.target, input.url, input.localhost === true ? "localhost" : void 0].filter(
|
|
101
|
+
(value) => value !== void 0
|
|
102
|
+
).length <= 1,
|
|
103
|
+
"Pass at most one of target, url, or localhost."
|
|
104
|
+
);
|
|
105
|
+
var SurfaceFlowListInputSchema = z.object({ candidates: z.boolean().optional() }).strict();
|
|
106
|
+
var SurfaceFlowPromoteInputSchema = z.object({
|
|
107
|
+
candidateFlowId: z.string().min(1),
|
|
108
|
+
outPath: z.string().min(1)
|
|
109
|
+
}).strict();
|
|
110
|
+
var SurfaceEvidenceInputSchema = z.object({ refId: z.string().min(1) }).strict();
|
|
111
|
+
var SurfaceReplayInputSchema = z.object({
|
|
112
|
+
promoteOnRepro: z.boolean().optional(),
|
|
113
|
+
refId: z.string().min(1)
|
|
114
|
+
}).strict();
|
|
115
|
+
var SurfaceReportQaInputSchema = z.object({
|
|
116
|
+
format: z.enum(["md", "json", "manifest"]).optional(),
|
|
117
|
+
runId: z.string().min(1)
|
|
118
|
+
}).strict();
|
|
119
|
+
var SurfaceArtifactReadInputSchema = z.object({
|
|
120
|
+
artifactId: z.string().min(1),
|
|
121
|
+
maxBytes: z.number().int().positive().max(65536).optional(),
|
|
122
|
+
refId: z.string().min(1)
|
|
123
|
+
}).strict();
|
|
124
|
+
var BROWSER_QA_MCP_SERVER_INPUT_SCHEMAS = {
|
|
125
|
+
surface_qa: SurfaceQaInputSchema,
|
|
126
|
+
surface_explore: SurfaceExploreInputSchema,
|
|
127
|
+
surface_flow_run: SurfaceFlowRunInputSchema,
|
|
128
|
+
surface_flow_list: SurfaceFlowListInputSchema,
|
|
129
|
+
surface_flow_promote: SurfaceFlowPromoteInputSchema,
|
|
130
|
+
surface_evidence: SurfaceEvidenceInputSchema,
|
|
131
|
+
surface_replay: SurfaceReplayInputSchema,
|
|
132
|
+
surface_report_qa: SurfaceReportQaInputSchema,
|
|
133
|
+
surface_artifact_read: SurfaceArtifactReadInputSchema
|
|
134
|
+
};
|
|
135
|
+
var BROWSER_QA_MCP_TOOL_INPUT_SCHEMAS = BROWSER_QA_MCP_SERVER_INPUT_SCHEMAS;
|
|
136
|
+
var BROWSER_QA_MCP_SERVER_TOOL_METADATA = {
|
|
137
|
+
surface_qa: {
|
|
138
|
+
description: "Run agent-led browser QA over a target.",
|
|
139
|
+
title: "Run Browser QA"
|
|
140
|
+
},
|
|
141
|
+
surface_explore: {
|
|
142
|
+
description: "Run bounded browser QA exploration.",
|
|
143
|
+
title: "Explore Browser QA"
|
|
144
|
+
},
|
|
145
|
+
surface_flow_run: {
|
|
146
|
+
description: "Run a reviewed browser QA flow.",
|
|
147
|
+
title: "Run Browser QA Flow"
|
|
148
|
+
},
|
|
149
|
+
surface_flow_list: {
|
|
150
|
+
description: "List browser QA flow runs or candidate flows.",
|
|
151
|
+
title: "List Browser QA Flows"
|
|
152
|
+
},
|
|
153
|
+
surface_flow_promote: {
|
|
154
|
+
description: "Promote a candidate browser QA flow.",
|
|
155
|
+
title: "Promote Browser QA Flow"
|
|
156
|
+
},
|
|
157
|
+
surface_evidence: {
|
|
158
|
+
description: "Read redacted browser QA evidence metadata.",
|
|
159
|
+
title: "Read Browser QA Evidence"
|
|
160
|
+
},
|
|
161
|
+
surface_replay: {
|
|
162
|
+
description: "Replay a browser QA candidate or finding.",
|
|
163
|
+
title: "Replay Browser QA Finding"
|
|
164
|
+
},
|
|
165
|
+
surface_report_qa: {
|
|
166
|
+
description: "Render a browser QA report.",
|
|
167
|
+
title: "Render Browser QA Report"
|
|
168
|
+
},
|
|
169
|
+
surface_artifact_read: {
|
|
170
|
+
description: "Read a bounded MCP-approved browser QA artifact by registered refs.",
|
|
171
|
+
title: "Read Browser QA Artifact"
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var BROWSER_QA_MCP_SERVER_TOOL_NAME_SET = new Set(BROWSER_QA_MCP_SERVER_TOOL_NAMES);
|
|
175
|
+
var REGISTERED_REF_ID_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9_-]*$/u;
|
|
176
|
+
function createBrowserQaMcpHandlers(composition) {
|
|
177
|
+
const browserQa = browserQaFromComposition(composition);
|
|
178
|
+
const orchestrator = browserQa?.orchestrator;
|
|
179
|
+
const flowService = browserQa?.flowService;
|
|
180
|
+
const evidenceStore = browserQa?.evidenceStore;
|
|
181
|
+
return {
|
|
182
|
+
artifactRead: (input) => evidenceStore?.readArtifactByRegisteredRef(input) ?? Promise.resolve(qaMcpUnavailable("Browser QA evidence artifact reads are unavailable.")),
|
|
183
|
+
evidence: (input) => orchestrator?.readEvidence(input) ?? Promise.resolve(qaMcpUnavailable("Browser QA evidence reads are unavailable.")),
|
|
184
|
+
explore: (input) => orchestrator?.runExplore({
|
|
185
|
+
...input,
|
|
186
|
+
maxActions: input.maxActions ?? 25,
|
|
187
|
+
maxDepth: input.maxDepth ?? 2,
|
|
188
|
+
maxStates: input.maxStates ?? 10
|
|
189
|
+
}) ?? Promise.resolve(qaMcpUnavailable("Browser QA exploration is unavailable.")),
|
|
190
|
+
flowList: (input) => flowService?.listFlows(input) ?? Promise.resolve(qaMcpUnavailable("Browser QA flow listing is unavailable.")),
|
|
191
|
+
flowPromote: (input) => flowService?.promoteFlow(input) ?? Promise.resolve(qaMcpUnavailable("Browser QA flow promotion is unavailable.")),
|
|
192
|
+
flowRun: (input) => flowService?.runFlowFile({
|
|
193
|
+
...input.actionPolicyRef === void 0 ? {} : { actionPolicyRef: input.actionPolicyRef },
|
|
194
|
+
...input.ci === void 0 ? {} : { ci: input.ci },
|
|
195
|
+
flowPath: input.flowPath,
|
|
196
|
+
targetCli: targetCliForFlowRun(input)
|
|
197
|
+
}) ?? Promise.resolve(qaMcpUnavailable("Browser QA flow running is unavailable.")),
|
|
198
|
+
qa: (input) => orchestrator?.runQa(input) ?? Promise.resolve(qaMcpUnavailable("Browser QA orchestration is unavailable.")),
|
|
199
|
+
replay: (input) => orchestrator?.replay(input) ?? Promise.resolve(qaMcpUnavailable("Browser QA replay is unavailable.")),
|
|
200
|
+
reportQa: (input) => orchestrator?.reportQa(input) ?? Promise.resolve(qaMcpUnavailable("Browser QA reports are unavailable."))
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function isBrowserQaMcpServerToolName(name) {
|
|
204
|
+
return BROWSER_QA_MCP_SERVER_TOOL_NAME_SET.has(name);
|
|
205
|
+
}
|
|
206
|
+
async function callBrowserQaMcpTool(input) {
|
|
207
|
+
const parsed = parseBrowserQaToolInput(input.name, input.input);
|
|
208
|
+
if (!parsed.ok) {
|
|
209
|
+
return parsed;
|
|
210
|
+
}
|
|
211
|
+
switch (input.name) {
|
|
212
|
+
case "surface_qa":
|
|
213
|
+
return await input.handlers.qa(parsed.value);
|
|
214
|
+
case "surface_explore":
|
|
215
|
+
return await input.handlers.explore(parsed.value);
|
|
216
|
+
case "surface_flow_run":
|
|
217
|
+
return await input.handlers.flowRun(parsed.value);
|
|
218
|
+
case "surface_flow_list":
|
|
219
|
+
return await input.handlers.flowList(parsed.value);
|
|
220
|
+
case "surface_flow_promote":
|
|
221
|
+
return await input.handlers.flowPromote(parsed.value);
|
|
222
|
+
case "surface_evidence":
|
|
223
|
+
return await input.handlers.evidence(parsed.value);
|
|
224
|
+
case "surface_replay":
|
|
225
|
+
return await input.handlers.replay(parsed.value);
|
|
226
|
+
case "surface_report_qa":
|
|
227
|
+
return await input.handlers.reportQa(parsed.value);
|
|
228
|
+
case "surface_artifact_read":
|
|
229
|
+
return await callArtifactReadHandler(
|
|
230
|
+
input.handlers,
|
|
231
|
+
parsed.value
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function parseBrowserQaToolInput(name, input) {
|
|
236
|
+
const parsed = BROWSER_QA_MCP_TOOL_INPUT_SCHEMAS[name].safeParse(input);
|
|
237
|
+
if (!parsed.success) {
|
|
238
|
+
return err(
|
|
239
|
+
createSurfaceError("config_invalid", "Browser QA MCP input did not match its schema.", {
|
|
240
|
+
cause: parsed.error,
|
|
241
|
+
details: { tool: name }
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
return ok(parsed.data);
|
|
246
|
+
}
|
|
247
|
+
async function callArtifactReadHandler(handlers, input) {
|
|
248
|
+
const refCheck = validateRegisteredRef(input.refId, "refId");
|
|
249
|
+
if (!refCheck.ok) {
|
|
250
|
+
return refCheck;
|
|
251
|
+
}
|
|
252
|
+
const artifactCheck = validateRegisteredRef(input.artifactId, "artifactId");
|
|
253
|
+
if (!artifactCheck.ok) {
|
|
254
|
+
return artifactCheck;
|
|
255
|
+
}
|
|
256
|
+
return await handlers.artifactRead({ ...input, maxBytes: input.maxBytes ?? 8192 });
|
|
257
|
+
}
|
|
258
|
+
function validateRegisteredRef(value, field) {
|
|
259
|
+
if (!REGISTERED_REF_ID_PATTERN.test(value) || value.includes("..")) {
|
|
260
|
+
return err(
|
|
261
|
+
createSurfaceError("config_invalid", "Browser QA artifact reads require registered ids.", {
|
|
262
|
+
details: { field }
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
return ok(true);
|
|
267
|
+
}
|
|
268
|
+
function browserQaFromComposition(composition) {
|
|
269
|
+
return composition.browserQa;
|
|
270
|
+
}
|
|
271
|
+
function targetCliForFlowRun(input) {
|
|
272
|
+
return {
|
|
273
|
+
...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl },
|
|
274
|
+
...input.localhost === true ? { localhost: true } : {},
|
|
275
|
+
...input.target === void 0 ? {} : { target: input.target },
|
|
276
|
+
...input.url === void 0 ? {} : { url: input.url }
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function qaMcpUnavailable(message) {
|
|
280
|
+
return err(createSurfaceError("qa_unavailable", message));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/index.ts
|
|
22
284
|
var SURFACE_MCP_SERVER_NAME = "surface";
|
|
23
285
|
var SURFACE_MCP_SERVER_VERSION = "1.0.0";
|
|
24
286
|
var SURFACE_MCP_TOOL_SCHEMA_VERSION = "1.0.0";
|
|
@@ -36,62 +298,77 @@ var TOOL_ORDER = [
|
|
|
36
298
|
"surface_trace",
|
|
37
299
|
"surface_run",
|
|
38
300
|
"surface_next",
|
|
39
|
-
"surface_status"
|
|
301
|
+
"surface_status",
|
|
302
|
+
...BROWSER_QA_MCP_SERVER_TOOL_NAMES
|
|
40
303
|
];
|
|
41
|
-
var TargetSchema =
|
|
42
|
-
kind:
|
|
43
|
-
ref:
|
|
44
|
-
theme:
|
|
45
|
-
viewport:
|
|
46
|
-
height:
|
|
47
|
-
label:
|
|
48
|
-
width:
|
|
304
|
+
var TargetSchema = z2.object({
|
|
305
|
+
kind: z2.enum(["url", "localhost", "route", "screenshot", "component", "dom"]),
|
|
306
|
+
ref: z2.string().min(1),
|
|
307
|
+
theme: z2.enum(["light", "dark"]).optional(),
|
|
308
|
+
viewport: z2.object({
|
|
309
|
+
height: z2.number().int().positive(),
|
|
310
|
+
label: z2.enum(["mobile", "tablet", "desktop"]),
|
|
311
|
+
width: z2.number().int().positive()
|
|
49
312
|
}).strict().optional()
|
|
50
313
|
}).strict();
|
|
51
|
-
var AuthStateRefSchema =
|
|
52
|
-
var RunRefSchema =
|
|
53
|
-
var GatePolicyInputSchema =
|
|
314
|
+
var AuthStateRefSchema = z2.string().min(1);
|
|
315
|
+
var RunRefSchema = z2.object({ runId: z2.string().min(1) }).strict();
|
|
316
|
+
var GatePolicyInputSchema = z2.record(z2.string(), z2.unknown());
|
|
54
317
|
var TOOL_INPUT_SCHEMAS = {
|
|
55
|
-
surface_capture:
|
|
318
|
+
surface_capture: z2.object({
|
|
56
319
|
authState: AuthStateRefSchema.optional(),
|
|
57
320
|
target: TargetSchema
|
|
58
321
|
}).strict(),
|
|
59
|
-
surface_audit:
|
|
322
|
+
surface_audit: z2.object({
|
|
60
323
|
authState: AuthStateRefSchema.optional(),
|
|
61
|
-
depth:
|
|
62
|
-
persona:
|
|
63
|
-
preset:
|
|
324
|
+
depth: z2.union([z2.literal(1), z2.literal(2), z2.literal(3), z2.literal(4), z2.literal(5)]).optional(),
|
|
325
|
+
persona: z2.string().min(1).optional(),
|
|
326
|
+
preset: z2.string().min(1).optional(),
|
|
64
327
|
target: TargetSchema,
|
|
65
|
-
task:
|
|
328
|
+
task: z2.string().min(1).optional()
|
|
66
329
|
}).strict(),
|
|
67
|
-
surface_explain:
|
|
68
|
-
surface_backlog:
|
|
69
|
-
exportTarget:
|
|
70
|
-
runId:
|
|
330
|
+
surface_explain: z2.object({ findingId: z2.string().min(1) }).strict(),
|
|
331
|
+
surface_backlog: z2.object({
|
|
332
|
+
exportTarget: z2.string().min(1).optional(),
|
|
333
|
+
runId: z2.string().min(1).optional()
|
|
71
334
|
}).strict(),
|
|
72
|
-
surface_gate:
|
|
335
|
+
surface_gate: z2.object({
|
|
336
|
+
actionPolicyRef: z2.string().min(1).optional(),
|
|
337
|
+
baseUrl: z2.string().min(1).optional(),
|
|
338
|
+
ci: z2.boolean().optional(),
|
|
339
|
+
localhost: z2.boolean().optional(),
|
|
73
340
|
policy: GatePolicyInputSchema.optional(),
|
|
74
|
-
runId:
|
|
341
|
+
runId: z2.string().min(1).optional(),
|
|
342
|
+
target: z2.string().min(1).optional(),
|
|
343
|
+
url: z2.string().min(1).optional(),
|
|
344
|
+
withFlows: z2.union([z2.boolean(), z2.string().min(1)]).optional()
|
|
345
|
+
}).strict().refine(
|
|
346
|
+
(input) => [input.target, input.url, input.localhost === true ? "localhost" : void 0].filter(
|
|
347
|
+
(value) => value !== void 0
|
|
348
|
+
).length <= 1,
|
|
349
|
+
"Pass at most one of target, url, or localhost."
|
|
350
|
+
),
|
|
351
|
+
surface_validate: z2.object({ runId: z2.string().min(1) }).strict(),
|
|
352
|
+
surface_baseline: z2.object({ reason: z2.string().min(1).optional() }).strict(),
|
|
353
|
+
surface_verdict: z2.object({
|
|
354
|
+
decision: z2.enum(["accept", "reject", "correct", "defer"]),
|
|
355
|
+
findingId: z2.string().min(1),
|
|
356
|
+
promote: z2.boolean().optional(),
|
|
357
|
+
rationale: z2.string().min(1)
|
|
75
358
|
}).strict(),
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
surface_verdict: z.object({
|
|
79
|
-
decision: z.enum(["accept", "reject", "correct", "defer"]),
|
|
80
|
-
findingId: z.string().min(1),
|
|
81
|
-
rationale: z.string().min(1)
|
|
82
|
-
}).strict(),
|
|
83
|
-
surface_diff: z.object({ after: RunRefSchema, before: RunRefSchema }).strict(),
|
|
84
|
-
surface_alternatives: z.object({
|
|
359
|
+
surface_diff: z2.object({ after: RunRefSchema, before: RunRefSchema }).strict(),
|
|
360
|
+
surface_alternatives: z2.object({
|
|
85
361
|
authState: AuthStateRefSchema.optional(),
|
|
86
362
|
target: TargetSchema
|
|
87
363
|
}).strict(),
|
|
88
|
-
surface_trace:
|
|
89
|
-
surface_run:
|
|
90
|
-
step:
|
|
364
|
+
surface_trace: z2.object({ findingId: z2.string().min(1) }).strict(),
|
|
365
|
+
surface_run: z2.object({
|
|
366
|
+
step: z2.string().min(1),
|
|
91
367
|
target: TargetSchema.optional()
|
|
92
368
|
}).strict(),
|
|
93
|
-
surface_next:
|
|
94
|
-
surface_status:
|
|
369
|
+
surface_next: z2.object({}).strict(),
|
|
370
|
+
surface_status: z2.object({}).strict(),
|
|
371
|
+
...BROWSER_QA_MCP_SERVER_INPUT_SCHEMAS
|
|
95
372
|
};
|
|
96
373
|
var TOOL_METADATA = {
|
|
97
374
|
surface_capture: {
|
|
@@ -149,7 +426,8 @@ var TOOL_METADATA = {
|
|
|
149
426
|
surface_status: {
|
|
150
427
|
title: "Read Status",
|
|
151
428
|
description: "Read Surface project status."
|
|
152
|
-
}
|
|
429
|
+
},
|
|
430
|
+
...BROWSER_QA_MCP_SERVER_TOOL_METADATA
|
|
153
431
|
};
|
|
154
432
|
var INTERNAL_TOOLS = TOOL_ORDER.map((name) => {
|
|
155
433
|
const metadata = TOOL_METADATA[name];
|
|
@@ -158,7 +436,7 @@ var INTERNAL_TOOLS = TOOL_ORDER.map((name) => {
|
|
|
158
436
|
name,
|
|
159
437
|
...metadata,
|
|
160
438
|
inputZodSchema,
|
|
161
|
-
inputSchema:
|
|
439
|
+
inputSchema: z2.toJSONSchema(inputZodSchema),
|
|
162
440
|
schemaVersion: SURFACE_MCP_TOOL_SCHEMA_VERSION
|
|
163
441
|
};
|
|
164
442
|
});
|
|
@@ -180,8 +458,9 @@ function createSurfaceMcpToolRegistry() {
|
|
|
180
458
|
}
|
|
181
459
|
function createSurfaceMcpServer(options = {}) {
|
|
182
460
|
const registry = createSurfaceMcpToolRegistry();
|
|
461
|
+
const projectRoot = path.resolve(options.projectRoot ?? process.cwd());
|
|
183
462
|
const composition = options.composition ?? createSurfaceComposition(options);
|
|
184
|
-
const session = createSurfaceMcpSessionState();
|
|
463
|
+
const session = createSurfaceMcpSessionState(projectRoot);
|
|
185
464
|
return {
|
|
186
465
|
composition,
|
|
187
466
|
callTool: async (name, input) => {
|
|
@@ -226,14 +505,15 @@ function assertMcpToolSchemaCompatibility(input) {
|
|
|
226
505
|
);
|
|
227
506
|
}
|
|
228
507
|
}
|
|
229
|
-
return
|
|
508
|
+
return ok2(true);
|
|
230
509
|
}
|
|
231
|
-
function createSurfaceMcpSessionState() {
|
|
510
|
+
function createSurfaceMcpSessionState(projectRoot) {
|
|
232
511
|
return {
|
|
233
512
|
baselines: /* @__PURE__ */ new Map(),
|
|
234
513
|
baselineOrder: [],
|
|
235
514
|
runs: /* @__PURE__ */ new Map(),
|
|
236
515
|
runOrder: [],
|
|
516
|
+
projectRoot,
|
|
237
517
|
trackedByIdentity: /* @__PURE__ */ new Map(),
|
|
238
518
|
verdicts: /* @__PURE__ */ new Map(),
|
|
239
519
|
nextBaselineSequence: 1,
|
|
@@ -246,7 +526,7 @@ async function hydrateSurfaceMcpSession(composition, session) {
|
|
|
246
526
|
return state;
|
|
247
527
|
}
|
|
248
528
|
resetSurfaceMcpSessionFromState(session, state.value);
|
|
249
|
-
return
|
|
529
|
+
return ok2(void 0);
|
|
250
530
|
}
|
|
251
531
|
function resetSurfaceMcpSessionFromState(session, state) {
|
|
252
532
|
session.baselines.clear();
|
|
@@ -310,14 +590,14 @@ async function persistSurfaceMcpSession(composition, session) {
|
|
|
310
590
|
const updateState = stateSnapshotForMcpSession(session);
|
|
311
591
|
if (composition.stateStore.updateState !== void 0) {
|
|
312
592
|
const updated = await composition.stateStore.updateState(updateState);
|
|
313
|
-
return updated.ok ?
|
|
593
|
+
return updated.ok ? ok2(void 0) : updated;
|
|
314
594
|
}
|
|
315
595
|
const current = await composition.stateStore.readState();
|
|
316
596
|
if (!current.ok) {
|
|
317
597
|
return current;
|
|
318
598
|
}
|
|
319
599
|
const written = await composition.stateStore.writeState(updateState(current.value));
|
|
320
|
-
return written.ok ?
|
|
600
|
+
return written.ok ? ok2(void 0) : written;
|
|
321
601
|
}
|
|
322
602
|
function stateSnapshotForMcpSession(session) {
|
|
323
603
|
return (state) => {
|
|
@@ -372,6 +652,13 @@ function nextSequenceFromIds(ids, prefix) {
|
|
|
372
652
|
return maxSequence + 1;
|
|
373
653
|
}
|
|
374
654
|
async function callSurfaceMcpTool(input) {
|
|
655
|
+
if (isBrowserQaMcpServerToolName(input.name)) {
|
|
656
|
+
return await callBrowserQaMcpTool({
|
|
657
|
+
handlers: createBrowserQaMcpHandlers(input.composition),
|
|
658
|
+
input: input.input,
|
|
659
|
+
name: input.name
|
|
660
|
+
});
|
|
661
|
+
}
|
|
375
662
|
switch (input.name) {
|
|
376
663
|
case "surface_capture":
|
|
377
664
|
return await callSurfaceCapture(input.composition, input.input);
|
|
@@ -465,7 +752,7 @@ async function callSurfaceAudit(composition, session, rawInput) {
|
|
|
465
752
|
if (!persisted.ok) {
|
|
466
753
|
return persisted;
|
|
467
754
|
}
|
|
468
|
-
return
|
|
755
|
+
return ok2({
|
|
469
756
|
backlog: record.backlog,
|
|
470
757
|
capture: record.capture,
|
|
471
758
|
findings: record.findings,
|
|
@@ -480,20 +767,20 @@ function callSurfaceExplain(session, rawInput) {
|
|
|
480
767
|
}
|
|
481
768
|
const finding = findStoredFinding(session, parsed.value.findingId);
|
|
482
769
|
if (finding === void 0) {
|
|
483
|
-
return
|
|
484
|
-
|
|
770
|
+
return err2(
|
|
771
|
+
createSurfaceError2("finding_not_found", "No stored MCP finding matched the requested id.", {
|
|
485
772
|
details: { findingId: parsed.value.findingId }
|
|
486
773
|
})
|
|
487
774
|
);
|
|
488
775
|
}
|
|
489
776
|
if (finding.evidence.length === 0) {
|
|
490
|
-
return
|
|
491
|
-
|
|
777
|
+
return err2(
|
|
778
|
+
createSurfaceError2("evidence_missing", "Stored MCP finding has no evidence to explain.", {
|
|
492
779
|
details: { findingId: finding.id }
|
|
493
780
|
})
|
|
494
781
|
);
|
|
495
782
|
}
|
|
496
|
-
return
|
|
783
|
+
return ok2({
|
|
497
784
|
evidence: finding.evidence,
|
|
498
785
|
finding,
|
|
499
786
|
rationale: finding.rationale
|
|
@@ -506,21 +793,21 @@ async function callSurfaceBacklog(composition, session, rawInput) {
|
|
|
506
793
|
}
|
|
507
794
|
const record = runRecordFor(session, parsed.value.runId);
|
|
508
795
|
if (record === void 0) {
|
|
509
|
-
return
|
|
510
|
-
|
|
796
|
+
return err2(
|
|
797
|
+
createSurfaceError2("run_not_found", "No stored MCP run matched the requested backlog.", {
|
|
511
798
|
details: { runId: parsed.value.runId ?? null }
|
|
512
799
|
})
|
|
513
800
|
);
|
|
514
801
|
}
|
|
515
802
|
if (parsed.value.exportTarget === void 0) {
|
|
516
|
-
return
|
|
803
|
+
return ok2(record.backlog);
|
|
517
804
|
}
|
|
518
805
|
const exporter = composition.issueExporters.find(
|
|
519
806
|
(candidate) => candidate.target === parsed.value.exportTarget
|
|
520
807
|
);
|
|
521
808
|
if (exporter === void 0) {
|
|
522
|
-
return
|
|
523
|
-
|
|
809
|
+
return err2(
|
|
810
|
+
createSurfaceError2(
|
|
524
811
|
"unknown_export_target",
|
|
525
812
|
"No issue exporter matched the MCP backlog target.",
|
|
526
813
|
{
|
|
@@ -545,13 +832,13 @@ async function callSurfaceBacklog(composition, session, rawInput) {
|
|
|
545
832
|
return exported;
|
|
546
833
|
}
|
|
547
834
|
if (exported.value.status === "partial") {
|
|
548
|
-
return
|
|
549
|
-
|
|
835
|
+
return err2(
|
|
836
|
+
createSurfaceError2("export_partial", "MCP backlog export completed partially.", {
|
|
550
837
|
details: { exportId: exported.value.id, target: exported.value.target }
|
|
551
838
|
})
|
|
552
839
|
);
|
|
553
840
|
}
|
|
554
|
-
return
|
|
841
|
+
return ok2(exported.value);
|
|
555
842
|
}
|
|
556
843
|
async function callSurfaceGate(composition, session, rawInput) {
|
|
557
844
|
const parsed = parseToolInput("surface_gate", rawInput);
|
|
@@ -560,8 +847,8 @@ async function callSurfaceGate(composition, session, rawInput) {
|
|
|
560
847
|
}
|
|
561
848
|
const record = runRecordFor(session, parsed.value.runId);
|
|
562
849
|
if (record === void 0) {
|
|
563
|
-
return
|
|
564
|
-
|
|
850
|
+
return err2(
|
|
851
|
+
createSurfaceError2("run_not_found", "No stored MCP run matched the requested gate.", {
|
|
565
852
|
details: { runId: parsed.value.runId ?? null }
|
|
566
853
|
})
|
|
567
854
|
);
|
|
@@ -573,7 +860,32 @@ async function callSurfaceGate(composition, session, rawInput) {
|
|
|
573
860
|
return tracked === void 0 || !baselineIdentityKeys.has(tracked.identityKey);
|
|
574
861
|
});
|
|
575
862
|
const policy = parsed.value.policy === void 0 ? DEFAULT_SURFACE_CONFIG.reporting.gatePolicy : parsed.value.policy;
|
|
576
|
-
|
|
863
|
+
if (parsed.value.withFlows === void 0) {
|
|
864
|
+
return await composition.gateEvaluator.evaluate(findings, policy);
|
|
865
|
+
}
|
|
866
|
+
const targetCli = browserQaGateTargetCli(parsed.value);
|
|
867
|
+
if (!targetCli.ok) {
|
|
868
|
+
return targetCli;
|
|
869
|
+
}
|
|
870
|
+
const flowRuns = await browserQaFlowRunsForGate(composition, {
|
|
871
|
+
...parsed.value.actionPolicyRef === void 0 ? {} : { actionPolicyRef: parsed.value.actionPolicyRef },
|
|
872
|
+
ci: parsed.value.ci === true,
|
|
873
|
+
projectRoot: session.projectRoot,
|
|
874
|
+
...targetCli.value === void 0 ? {} : { targetCli: targetCli.value },
|
|
875
|
+
withFlows: parsed.value.withFlows
|
|
876
|
+
});
|
|
877
|
+
if (!flowRuns.ok) {
|
|
878
|
+
return flowRuns;
|
|
879
|
+
}
|
|
880
|
+
const flowAwareGate = evaluateGateWithQaFlows({
|
|
881
|
+
findings,
|
|
882
|
+
policy: {
|
|
883
|
+
...policy,
|
|
884
|
+
failOnFlowSeverityAtOrAbove: browserQaFlowSeverityFromWithFlows(parsed.value.withFlows)
|
|
885
|
+
},
|
|
886
|
+
qaFlowRuns: flowRuns.value
|
|
887
|
+
});
|
|
888
|
+
return flowAwareGate;
|
|
577
889
|
}
|
|
578
890
|
function callSurfaceValidate(session, rawInput) {
|
|
579
891
|
const parsed = parseToolInput("surface_validate", rawInput);
|
|
@@ -582,13 +894,13 @@ function callSurfaceValidate(session, rawInput) {
|
|
|
582
894
|
}
|
|
583
895
|
const record = runRecordFor(session, parsed.value.runId);
|
|
584
896
|
if (record === void 0) {
|
|
585
|
-
return
|
|
586
|
-
|
|
897
|
+
return err2(
|
|
898
|
+
createSurfaceError2("run_not_found", "No stored MCP run matched the requested validation.", {
|
|
587
899
|
details: { runId: parsed.value.runId }
|
|
588
900
|
})
|
|
589
901
|
);
|
|
590
902
|
}
|
|
591
|
-
return
|
|
903
|
+
return ok2({
|
|
592
904
|
checks: record.trackedFindings.map((trackedFinding) => ({
|
|
593
905
|
id: trackedFinding.identityKey,
|
|
594
906
|
passed: trackedFinding.status !== "identity-broken",
|
|
@@ -604,8 +916,8 @@ async function callSurfaceBaseline(composition, session, rawInput) {
|
|
|
604
916
|
}
|
|
605
917
|
const record = runRecordFor(session, void 0);
|
|
606
918
|
if (record === void 0 || record.trackedFindings.length === 0) {
|
|
607
|
-
return
|
|
608
|
-
|
|
919
|
+
return err2(
|
|
920
|
+
createSurfaceError2("no_findings_to_baseline", "No MCP findings are available to baseline.")
|
|
609
921
|
);
|
|
610
922
|
}
|
|
611
923
|
const baselineId = nextBaselineId(session);
|
|
@@ -622,7 +934,7 @@ async function callSurfaceBaseline(composition, session, rawInput) {
|
|
|
622
934
|
if (!persisted.ok) {
|
|
623
935
|
return persisted;
|
|
624
936
|
}
|
|
625
|
-
return
|
|
937
|
+
return ok2({
|
|
626
938
|
baselineId,
|
|
627
939
|
count: baseline.identityKeys.size,
|
|
628
940
|
...baseline.reason === void 0 ? {} : { reason: baseline.reason }
|
|
@@ -633,10 +945,33 @@ async function callSurfaceVerdict(composition, session, rawInput) {
|
|
|
633
945
|
if (!parsed.ok) {
|
|
634
946
|
return parsed;
|
|
635
947
|
}
|
|
948
|
+
if (parsed.value.findingId.startsWith("qfc_")) {
|
|
949
|
+
if (parsed.value.promote !== true) {
|
|
950
|
+
return err2(
|
|
951
|
+
createSurfaceError2("finding_not_found", "Browser QA candidate verdicts require promote.", {
|
|
952
|
+
details: { findingId: parsed.value.findingId }
|
|
953
|
+
})
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
const promotion = await composition.browserQa.orchestrator.promoteCandidateByVerdict({
|
|
957
|
+
refId: parsed.value.findingId,
|
|
958
|
+
reason: parsed.value.rationale,
|
|
959
|
+
verdictId: `verdict_${Date.now().toString(36)}`
|
|
960
|
+
});
|
|
961
|
+
if (!promotion.ok) {
|
|
962
|
+
return promotion;
|
|
963
|
+
}
|
|
964
|
+
return ok2({
|
|
965
|
+
decision: parsed.value.decision,
|
|
966
|
+
findingId: parsed.value.findingId,
|
|
967
|
+
promotion: promotion.value,
|
|
968
|
+
rationale: parsed.value.rationale
|
|
969
|
+
});
|
|
970
|
+
}
|
|
636
971
|
const finding = findStoredFinding(session, parsed.value.findingId);
|
|
637
972
|
if (finding === void 0) {
|
|
638
|
-
return
|
|
639
|
-
|
|
973
|
+
return err2(
|
|
974
|
+
createSurfaceError2("finding_not_found", "No stored MCP finding matched the verdict.", {
|
|
640
975
|
details: { findingId: parsed.value.findingId }
|
|
641
976
|
})
|
|
642
977
|
);
|
|
@@ -651,7 +986,7 @@ async function callSurfaceVerdict(composition, session, rawInput) {
|
|
|
651
986
|
if (!persisted.ok) {
|
|
652
987
|
return persisted;
|
|
653
988
|
}
|
|
654
|
-
return
|
|
989
|
+
return ok2(verdict);
|
|
655
990
|
}
|
|
656
991
|
function callSurfaceDiff(session, rawInput) {
|
|
657
992
|
const parsed = parseToolInput("surface_diff", rawInput);
|
|
@@ -661,13 +996,13 @@ function callSurfaceDiff(session, rawInput) {
|
|
|
661
996
|
const before = runRecordFor(session, parsed.value.before.runId);
|
|
662
997
|
const after = runRecordFor(session, parsed.value.after.runId);
|
|
663
998
|
if (before === void 0 || after === void 0) {
|
|
664
|
-
return
|
|
665
|
-
|
|
999
|
+
return err2(
|
|
1000
|
+
createSurfaceError2("run_not_found", "Both MCP diff runs must exist.", {
|
|
666
1001
|
details: { after: parsed.value.after.runId, before: parsed.value.before.runId }
|
|
667
1002
|
})
|
|
668
1003
|
);
|
|
669
1004
|
}
|
|
670
|
-
return
|
|
1005
|
+
return ok2(diffTrackedFindings(before.trackedFindings, after.trackedFindings));
|
|
671
1006
|
}
|
|
672
1007
|
async function callSurfaceAlternatives(composition, rawInput) {
|
|
673
1008
|
const parsed = parseToolInput("surface_alternatives", rawInput);
|
|
@@ -682,7 +1017,7 @@ async function callSurfaceAlternatives(composition, rawInput) {
|
|
|
682
1017
|
if (!capture.ok) {
|
|
683
1018
|
return capture;
|
|
684
1019
|
}
|
|
685
|
-
return
|
|
1020
|
+
return ok2({
|
|
686
1021
|
alternatives: createBoundedAlternatives(target)
|
|
687
1022
|
});
|
|
688
1023
|
}
|
|
@@ -693,13 +1028,13 @@ function callSurfaceTrace(session, rawInput) {
|
|
|
693
1028
|
}
|
|
694
1029
|
const trackedFinding = findStoredTrackedFinding(session, parsed.value.findingId);
|
|
695
1030
|
if (trackedFinding === void 0) {
|
|
696
|
-
return
|
|
697
|
-
|
|
1031
|
+
return err2(
|
|
1032
|
+
createSurfaceError2("finding_not_found", "No tracked MCP finding matched the requested id.", {
|
|
698
1033
|
details: { findingId: parsed.value.findingId }
|
|
699
1034
|
})
|
|
700
1035
|
);
|
|
701
1036
|
}
|
|
702
|
-
return
|
|
1037
|
+
return ok2({ trackedFinding });
|
|
703
1038
|
}
|
|
704
1039
|
async function callSurfaceRun(composition, rawInput) {
|
|
705
1040
|
const parsed = parseToolInput("surface_run", rawInput);
|
|
@@ -707,8 +1042,8 @@ async function callSurfaceRun(composition, rawInput) {
|
|
|
707
1042
|
return parsed;
|
|
708
1043
|
}
|
|
709
1044
|
if (parsed.value.step !== "all" && !isExecutableStageId(parsed.value.step)) {
|
|
710
|
-
return
|
|
711
|
-
|
|
1045
|
+
return err2(
|
|
1046
|
+
createSurfaceError2("unknown_step", `Unknown MCP pipeline step "${parsed.value.step}".`, {
|
|
712
1047
|
details: { step: parsed.value.step }
|
|
713
1048
|
})
|
|
714
1049
|
);
|
|
@@ -719,14 +1054,14 @@ async function callSurfaceRun(composition, rawInput) {
|
|
|
719
1054
|
runId
|
|
720
1055
|
});
|
|
721
1056
|
if (!run.ok) {
|
|
722
|
-
return
|
|
723
|
-
|
|
1057
|
+
return err2(
|
|
1058
|
+
createSurfaceError2("step_failed", `MCP pipeline run ${runId} failed.`, {
|
|
724
1059
|
cause: run.error,
|
|
725
1060
|
details: { runId, stage: parsed.value.step }
|
|
726
1061
|
})
|
|
727
1062
|
);
|
|
728
1063
|
}
|
|
729
|
-
return
|
|
1064
|
+
return ok2({
|
|
730
1065
|
runId: run.value.runId,
|
|
731
1066
|
stage: parsed.value.step,
|
|
732
1067
|
status: "completed"
|
|
@@ -739,7 +1074,7 @@ async function callSurfaceNext(composition) {
|
|
|
739
1074
|
}
|
|
740
1075
|
const lastCompletedStage = state.value.pipeline?.lastCompletedStage;
|
|
741
1076
|
const eligible = lastCompletedStage === void 0 ? ["run discovery", "run all"] : eligibleStagesAfter(lastCompletedStage).map((stage) => `run ${stage}`);
|
|
742
|
-
return
|
|
1077
|
+
return ok2({ eligible });
|
|
743
1078
|
}
|
|
744
1079
|
function callSurfaceStatus(session) {
|
|
745
1080
|
const runHistory = session.runOrder.map((runId) => session.runs.get(runId)).filter((record) => record !== void 0).map((record) => ({
|
|
@@ -750,7 +1085,7 @@ function callSurfaceStatus(session) {
|
|
|
750
1085
|
}));
|
|
751
1086
|
const completedRuns = runHistory.filter((entry) => entry.status === "completed").length;
|
|
752
1087
|
const failedRuns = runHistory.filter((entry) => entry.status === "failed").length;
|
|
753
|
-
return
|
|
1088
|
+
return ok2({
|
|
754
1089
|
currentStage: runHistory.at(-1)?.status === "completed" ? "completed" : "pending",
|
|
755
1090
|
progress: {
|
|
756
1091
|
completedRuns,
|
|
@@ -771,7 +1106,7 @@ async function groundingEvidenceFor(composition, capture) {
|
|
|
771
1106
|
evidence.push(...toolResult.evidence);
|
|
772
1107
|
}
|
|
773
1108
|
}
|
|
774
|
-
return
|
|
1109
|
+
return ok2(evidence);
|
|
775
1110
|
}
|
|
776
1111
|
async function findingsForPlan(composition, config, capture, evidence, plan) {
|
|
777
1112
|
const findings = [];
|
|
@@ -794,7 +1129,7 @@ async function findingsForPlan(composition, config, capture, evidence, plan) {
|
|
|
794
1129
|
findings.push(scored.value);
|
|
795
1130
|
}
|
|
796
1131
|
}
|
|
797
|
-
return
|
|
1132
|
+
return ok2(findings);
|
|
798
1133
|
}
|
|
799
1134
|
function configForAuditInput(input) {
|
|
800
1135
|
return {
|
|
@@ -832,14 +1167,14 @@ function targetForCore(input) {
|
|
|
832
1167
|
function parseToolInput(name, input) {
|
|
833
1168
|
const parsed = TOOL_INPUT_SCHEMAS[name].safeParse(input);
|
|
834
1169
|
if (!parsed.success) {
|
|
835
|
-
return
|
|
836
|
-
|
|
1170
|
+
return err2(
|
|
1171
|
+
createSurfaceError2("config_invalid", "MCP tool input did not match the registered schema.", {
|
|
837
1172
|
cause: parsed.error,
|
|
838
1173
|
details: { tool: name }
|
|
839
1174
|
})
|
|
840
1175
|
);
|
|
841
1176
|
}
|
|
842
|
-
return
|
|
1177
|
+
return ok2(parsed.data);
|
|
843
1178
|
}
|
|
844
1179
|
function nextRunId(session) {
|
|
845
1180
|
const runId = `run_mcp_${session.nextRunSequence.toString().padStart(4, "0")}`;
|
|
@@ -957,7 +1292,7 @@ async function runSurfaceMcpStdioServer(options = {}) {
|
|
|
957
1292
|
await server.connect(new StdioServerTransport());
|
|
958
1293
|
}
|
|
959
1294
|
function mcpToolCallResult(result) {
|
|
960
|
-
if (
|
|
1295
|
+
if (isOk2(result)) {
|
|
961
1296
|
return {
|
|
962
1297
|
content: [{ text: JSON.stringify(result.value, null, 2), type: "text" }],
|
|
963
1298
|
structuredContent: result.value
|
|
@@ -966,7 +1301,7 @@ function mcpToolCallResult(result) {
|
|
|
966
1301
|
return {
|
|
967
1302
|
content: [{ text: result.error.message, type: "text" }],
|
|
968
1303
|
isError: true,
|
|
969
|
-
structuredContent:
|
|
1304
|
+
structuredContent: toMcpError2(result.error)
|
|
970
1305
|
};
|
|
971
1306
|
}
|
|
972
1307
|
function publicToolDefinition(tool) {
|
|
@@ -997,8 +1332,8 @@ function majorVersion(version) {
|
|
|
997
1332
|
return Number.isInteger(major) ? major : void 0;
|
|
998
1333
|
}
|
|
999
1334
|
function mcpSchemaIncompatible(message, input, details = {}) {
|
|
1000
|
-
return
|
|
1001
|
-
|
|
1335
|
+
return err2(
|
|
1336
|
+
createSurfaceError2("mcp_schema_incompatible", message, {
|
|
1002
1337
|
details: {
|
|
1003
1338
|
current: {
|
|
1004
1339
|
name: input.current.name,
|