@gleanwork/mcp-server-tester 2.0.0-beta.3 → 2.0.0-beta.4
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/cli/index.js +172 -25
- package/dist/fixtures/mcp.js +65 -8
- package/dist/fixtures/mcp.js.map +1 -1
- package/dist/index.cjs +168 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +168 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3867,22 +3867,22 @@ async function runAnthropicComputerUseHitl(options) {
|
|
|
3867
3867
|
"HITL check"
|
|
3868
3868
|
);
|
|
3869
3869
|
}
|
|
3870
|
-
async function runComputerUseDriver(query2, options, mode,
|
|
3870
|
+
async function runComputerUseDriver(query2, options, mode, label2) {
|
|
3871
3871
|
const env = {
|
|
3872
3872
|
...process.env,
|
|
3873
3873
|
...options.env,
|
|
3874
3874
|
...options.model ? { MST_COWORK_CUA_MODEL: options.model } : {}
|
|
3875
3875
|
};
|
|
3876
3876
|
if (options.deadlineAt <= Date.now())
|
|
3877
|
-
throw new Error(`Computer Use ${
|
|
3877
|
+
throw new Error(`Computer Use ${label2} deadline exceeded; not retrying.`);
|
|
3878
3878
|
const python = await ensureCoworkPython(env);
|
|
3879
3879
|
const DRIVER_PATH = resolveDriverPath(env);
|
|
3880
3880
|
const timeoutMs = options.deadlineAt - Date.now();
|
|
3881
3881
|
if (timeoutMs <= 0)
|
|
3882
|
-
throw new Error(`Computer Use ${
|
|
3882
|
+
throw new Error(`Computer Use ${label2} deadline exceeded; not retrying.`);
|
|
3883
3883
|
const maxActions = options.maxActions ?? (mode === "hitl" ? 12 : 24);
|
|
3884
3884
|
diagnostic(
|
|
3885
|
-
`starting Computer Use ${
|
|
3885
|
+
`starting Computer Use ${label2} (script=${DRIVER_PATH}, maxActions=${maxActions}, timeoutMs=${timeoutMs})`
|
|
3886
3886
|
);
|
|
3887
3887
|
let stdout = "";
|
|
3888
3888
|
try {
|
|
@@ -3913,26 +3913,26 @@ async function runComputerUseDriver(query2, options, mode, label) {
|
|
|
3913
3913
|
telemetry3
|
|
3914
3914
|
);
|
|
3915
3915
|
}
|
|
3916
|
-
diagnostic(`Computer Use ${
|
|
3916
|
+
diagnostic(`Computer Use ${label2} failed: ${details}`);
|
|
3917
3917
|
throw new ComputerUseDriverError(
|
|
3918
|
-
`Computer Use ${
|
|
3918
|
+
`Computer Use ${label2} failed: ${details}`,
|
|
3919
3919
|
telemetry3
|
|
3920
3920
|
);
|
|
3921
3921
|
}
|
|
3922
3922
|
diagnostic(
|
|
3923
|
-
`Computer Use ${
|
|
3923
|
+
`Computer Use ${label2} exited successfully (stdoutBytes=${stdout.length})`
|
|
3924
3924
|
);
|
|
3925
3925
|
const record = parseLastJsonLine(stdout);
|
|
3926
3926
|
const expectedStatus = mode === "submit" ? "submitted" : "hitl_checked";
|
|
3927
3927
|
if (record?.status !== expectedStatus) {
|
|
3928
3928
|
throw new ComputerUseDriverError(
|
|
3929
|
-
`Computer Use ${
|
|
3929
|
+
`Computer Use ${label2} did not reach ${expectedStatus}.`,
|
|
3930
3930
|
parseTelemetry(record?.telemetry, env, "partial")
|
|
3931
3931
|
);
|
|
3932
3932
|
}
|
|
3933
3933
|
if (!isCount(record.action_count) || !isSafeModel(record.model, env)) {
|
|
3934
3934
|
throw new ComputerUseDriverError(
|
|
3935
|
-
`Computer Use ${
|
|
3935
|
+
`Computer Use ${label2} returned invalid result fields.`,
|
|
3936
3936
|
parseTelemetry(record.telemetry, env, "partial")
|
|
3937
3937
|
);
|
|
3938
3938
|
}
|
|
@@ -3943,7 +3943,7 @@ async function runComputerUseDriver(query2, options, mode, label) {
|
|
|
3943
3943
|
...telemetry2 ? { telemetry: telemetry2 } : {}
|
|
3944
3944
|
};
|
|
3945
3945
|
diagnostic(
|
|
3946
|
-
`Computer Use ${
|
|
3946
|
+
`Computer Use ${label2} completed (actions=${common.action_count})`
|
|
3947
3947
|
);
|
|
3948
3948
|
if (mode === "hitl") return { status: "hitl_checked", ...common };
|
|
3949
3949
|
const submission = asRecord(record.submission_action);
|
|
@@ -9084,10 +9084,57 @@ function escapeHtml(text) {
|
|
|
9084
9084
|
|
|
9085
9085
|
// package.json
|
|
9086
9086
|
var package_default = {
|
|
9087
|
-
version: "2.0.0-beta.
|
|
9087
|
+
version: "2.0.0-beta.4"};
|
|
9088
|
+
|
|
9089
|
+
// src/mcp/connectionDiagnostics.ts
|
|
9090
|
+
function classifyMCPConnectionFailure(error) {
|
|
9091
|
+
if (typeof error !== "object" || error === null) return "connection_failed";
|
|
9092
|
+
const details = error;
|
|
9093
|
+
const status = details.response?.status ?? details.status ?? details.code;
|
|
9094
|
+
if (typeof status === "number" && Number.isInteger(status) && status >= 400 && status <= 599) {
|
|
9095
|
+
return `http_${status}`;
|
|
9096
|
+
}
|
|
9097
|
+
const message = error instanceof Error ? error.message : "";
|
|
9098
|
+
const httpStatus = /\b(?:HTTP|status(?: code)?|code:)\s*[:=(]?\s*([45]\d{2})\b/i.exec(
|
|
9099
|
+
message
|
|
9100
|
+
)?.[1];
|
|
9101
|
+
if (httpStatus) return `http_${httpStatus}`;
|
|
9102
|
+
for (const code of ["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND"]) {
|
|
9103
|
+
if (details.code === code || message.toUpperCase().includes(code)) {
|
|
9104
|
+
return code.toLowerCase();
|
|
9105
|
+
}
|
|
9106
|
+
}
|
|
9107
|
+
if (/timed out|timeout/i.test(message)) return "timeout";
|
|
9108
|
+
if (/network|socket hang up|fetch failed/i.test(message))
|
|
9109
|
+
return "network_error";
|
|
9110
|
+
return "connection_failed";
|
|
9111
|
+
}
|
|
9112
|
+
var MCPHttpConnectionError = class extends Error {
|
|
9113
|
+
constructor(streamableError, sseError, retryable, retryAfterMs) {
|
|
9114
|
+
const streamableHttpFailure = classifyMCPConnectionFailure(streamableError);
|
|
9115
|
+
const sseFailure = classifyMCPConnectionFailure(sseError);
|
|
9116
|
+
super(
|
|
9117
|
+
`MCP connection failed: streamableHttp=${streamableHttpFailure}; sse=${sseFailure}`
|
|
9118
|
+
);
|
|
9119
|
+
this.retryable = retryable;
|
|
9120
|
+
this.retryAfterMs = retryAfterMs;
|
|
9121
|
+
this.name = "MCPHttpConnectionError";
|
|
9122
|
+
this.streamableHttpFailure = streamableHttpFailure;
|
|
9123
|
+
this.sseFailure = sseFailure;
|
|
9124
|
+
}
|
|
9125
|
+
streamableHttpFailure;
|
|
9126
|
+
sseFailure;
|
|
9127
|
+
};
|
|
9128
|
+
function formatMCPConnectionFailure(error) {
|
|
9129
|
+
if (error instanceof MCPHttpConnectionError) {
|
|
9130
|
+
return `MCP connection failed: streamableHttp=${error.streamableHttpFailure}; sse=${error.sseFailure}`;
|
|
9131
|
+
}
|
|
9132
|
+
return classifyMCPConnectionFailure(error);
|
|
9133
|
+
}
|
|
9088
9134
|
|
|
9089
9135
|
// src/mcp/clientFactory.ts
|
|
9090
9136
|
function getRetryAfterDelayMs(err) {
|
|
9137
|
+
if (err instanceof MCPHttpConnectionError) return err.retryAfterMs;
|
|
9091
9138
|
const response = err?.response;
|
|
9092
9139
|
const retryAfter = response?.headers?.get?.("Retry-After");
|
|
9093
9140
|
if (retryAfter) {
|
|
@@ -9106,6 +9153,7 @@ function isTransientNetworkError(err) {
|
|
|
9106
9153
|
return msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("etimedout") || msg.includes("enotfound") || msg.includes("network") || msg.includes("socket hang up") || msg.includes("fetch failed");
|
|
9107
9154
|
}
|
|
9108
9155
|
function isRetryableError(err) {
|
|
9156
|
+
if (err instanceof MCPHttpConnectionError) return err.retryable;
|
|
9109
9157
|
return isTransientNetworkError(err) || isRateLimitError(err);
|
|
9110
9158
|
}
|
|
9111
9159
|
async function retryWithBackoff(fn, maxAttempts) {
|
|
@@ -9123,7 +9171,7 @@ async function retryWithBackoff(fn, maxAttempts) {
|
|
|
9123
9171
|
attempt + 1,
|
|
9124
9172
|
maxAttempts + 1,
|
|
9125
9173
|
delayMs,
|
|
9126
|
-
err
|
|
9174
|
+
formatMCPConnectionFailure(err)
|
|
9127
9175
|
);
|
|
9128
9176
|
await new Promise((resolve8) => setTimeout(resolve8, delayMs));
|
|
9129
9177
|
} else {
|
|
@@ -9280,15 +9328,24 @@ async function createMCPClientForConfig(config, options) {
|
|
|
9280
9328
|
} catch (err) {
|
|
9281
9329
|
debugHttp(
|
|
9282
9330
|
"streamableHttp failed (%s), falling back to SSE",
|
|
9283
|
-
err
|
|
9331
|
+
formatMCPConnectionFailure(err)
|
|
9284
9332
|
);
|
|
9285
9333
|
debugClient("Streamable HTTP failed, falling back to SSE transport");
|
|
9286
9334
|
debugHttp("Attempting transport: sse");
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9335
|
+
try {
|
|
9336
|
+
const sseTransport = new sse_js.SSEClientTransport(url, {
|
|
9337
|
+
requestInit,
|
|
9338
|
+
...options?.authProvider ? { authProvider: options.authProvider } : {}
|
|
9339
|
+
});
|
|
9340
|
+
await client.connect(sseTransport, connectOptions);
|
|
9341
|
+
} catch (sseError) {
|
|
9342
|
+
throw new MCPHttpConnectionError(
|
|
9343
|
+
err,
|
|
9344
|
+
sseError,
|
|
9345
|
+
isRetryableError(err) || isRetryableError(sseError),
|
|
9346
|
+
getRetryAfterDelayMs(err) ?? getRetryAfterDelayMs(sseError)
|
|
9347
|
+
);
|
|
9348
|
+
}
|
|
9292
9349
|
debugClient("Connected via SSE");
|
|
9293
9350
|
debugHttp("Connection established via sse");
|
|
9294
9351
|
}
|
|
@@ -13746,6 +13803,86 @@ async function getCoworkPlatform(provider) {
|
|
|
13746
13803
|
);
|
|
13747
13804
|
}
|
|
13748
13805
|
|
|
13806
|
+
// src/evals/cowork/mcpReadiness.ts
|
|
13807
|
+
init_config();
|
|
13808
|
+
var PREFLIGHT_TIMEOUT_MS = 3e4;
|
|
13809
|
+
var CoworkMcpReadinessError = class extends Error {
|
|
13810
|
+
servers;
|
|
13811
|
+
constructor(servers) {
|
|
13812
|
+
super(
|
|
13813
|
+
`Cowork MCP preflight failed; no task was submitted. ${servers.map(
|
|
13814
|
+
(server) => `${server.label}=${server.status}${server.error ? `(${server.error})` : ""}(${server.elapsedMs}ms)`
|
|
13815
|
+
).join(", ")}`
|
|
13816
|
+
);
|
|
13817
|
+
this.name = "CoworkMcpReadinessError";
|
|
13818
|
+
this.servers = servers;
|
|
13819
|
+
}
|
|
13820
|
+
};
|
|
13821
|
+
function label(server, index) {
|
|
13822
|
+
return server.label ?? `server-${index + 1}`;
|
|
13823
|
+
}
|
|
13824
|
+
async function withTimeout(promise, timeoutMs) {
|
|
13825
|
+
let timer;
|
|
13826
|
+
try {
|
|
13827
|
+
return await Promise.race([
|
|
13828
|
+
promise,
|
|
13829
|
+
new Promise((_, reject) => {
|
|
13830
|
+
timer = setTimeout(
|
|
13831
|
+
() => reject(new Error("MCP preflight timed out")),
|
|
13832
|
+
timeoutMs
|
|
13833
|
+
);
|
|
13834
|
+
})
|
|
13835
|
+
]);
|
|
13836
|
+
} finally {
|
|
13837
|
+
if (timer) clearTimeout(timer);
|
|
13838
|
+
}
|
|
13839
|
+
}
|
|
13840
|
+
function resolveServer(server, env) {
|
|
13841
|
+
if (!isHttpConfig(server)) return server;
|
|
13842
|
+
const [coworkServer] = toCoworkServers([server]);
|
|
13843
|
+
if (!coworkServer) throw new Error("Invalid Cowork MCP configuration.");
|
|
13844
|
+
const headers = resolveCoworkMcpHeaders([coworkServer], env);
|
|
13845
|
+
return {
|
|
13846
|
+
...server,
|
|
13847
|
+
// Use the same validated runtime headers as Cowork setup.
|
|
13848
|
+
auth: void 0,
|
|
13849
|
+
headers: headers[coworkServer.label]
|
|
13850
|
+
};
|
|
13851
|
+
}
|
|
13852
|
+
async function verifyCoworkMcpServers(servers, env) {
|
|
13853
|
+
const results = await Promise.all(
|
|
13854
|
+
servers.map(async (server, index) => {
|
|
13855
|
+
const started = Date.now();
|
|
13856
|
+
let client;
|
|
13857
|
+
try {
|
|
13858
|
+
client = await createMCPClientForConfig(resolveServer(server, env));
|
|
13859
|
+
const tools = await withTimeout(
|
|
13860
|
+
client.listTools(),
|
|
13861
|
+
PREFLIGHT_TIMEOUT_MS
|
|
13862
|
+
);
|
|
13863
|
+
return {
|
|
13864
|
+
label: label(server, index),
|
|
13865
|
+
status: "connected",
|
|
13866
|
+
toolCount: tools.tools.length,
|
|
13867
|
+
elapsedMs: Date.now() - started
|
|
13868
|
+
};
|
|
13869
|
+
} catch (error) {
|
|
13870
|
+
return {
|
|
13871
|
+
label: label(server, index),
|
|
13872
|
+
status: "failed",
|
|
13873
|
+
elapsedMs: Date.now() - started,
|
|
13874
|
+
error: formatMCPConnectionFailure(error)
|
|
13875
|
+
};
|
|
13876
|
+
} finally {
|
|
13877
|
+
if (client) await closeMCPClient(client).catch(() => void 0);
|
|
13878
|
+
}
|
|
13879
|
+
})
|
|
13880
|
+
);
|
|
13881
|
+
if (results.some((result) => result.status !== "connected"))
|
|
13882
|
+
throw new CoworkMcpReadinessError(results);
|
|
13883
|
+
return results;
|
|
13884
|
+
}
|
|
13885
|
+
|
|
13749
13886
|
// src/evals/coworkHost.ts
|
|
13750
13887
|
init_driver();
|
|
13751
13888
|
var OptionsSchema = zod.z.object({
|
|
@@ -13828,6 +13965,14 @@ async function runBatch(requests, context, selectedPlatform) {
|
|
|
13828
13965
|
env,
|
|
13829
13966
|
model: config.model
|
|
13830
13967
|
});
|
|
13968
|
+
if (servers.length) {
|
|
13969
|
+
const readiness = await verifyCoworkMcpServers(servers, env);
|
|
13970
|
+
process.stderr.write(
|
|
13971
|
+
`[mst:cowork] MCP preflight ready: ${readiness.map(
|
|
13972
|
+
(server) => `${server.label}(${server.toolCount ?? 0} tools, ${server.elapsedMs}ms)`
|
|
13973
|
+
).join(", ")}\\n`
|
|
13974
|
+
);
|
|
13975
|
+
}
|
|
13831
13976
|
for (const [index, request] of requests.entries()) {
|
|
13832
13977
|
const caseStartedAt = Date.now();
|
|
13833
13978
|
const computerUse = {
|
|
@@ -14337,8 +14482,8 @@ function claudeCliHost(options) {
|
|
|
14337
14482
|
if (options.servers) {
|
|
14338
14483
|
for (const key of Object.keys(mcpServers)) delete mcpServers[key];
|
|
14339
14484
|
for (const entry of options.servers) {
|
|
14340
|
-
const
|
|
14341
|
-
mcpServers[
|
|
14485
|
+
const label2 = entry.label ?? "mcp-server";
|
|
14486
|
+
mcpServers[label2] = entry.transport === "http" ? {
|
|
14342
14487
|
type: "http",
|
|
14343
14488
|
url: entry.serverUrl,
|
|
14344
14489
|
headers: {
|
|
@@ -16292,9 +16437,9 @@ function buildArmDeltas(arms) {
|
|
|
16292
16437
|
);
|
|
16293
16438
|
}
|
|
16294
16439
|
function redactServerForReport(server) {
|
|
16295
|
-
const
|
|
16440
|
+
const label2 = server.label ? { label: server.label } : {};
|
|
16296
16441
|
if (server.transport === "stdio") {
|
|
16297
|
-
return { transport: "stdio", command: server.command, ...
|
|
16442
|
+
return { transport: "stdio", command: server.command, ...label2 };
|
|
16298
16443
|
}
|
|
16299
16444
|
const url = new URL(server.serverUrl);
|
|
16300
16445
|
url.username = "";
|
|
@@ -16304,7 +16449,7 @@ function redactServerForReport(server) {
|
|
|
16304
16449
|
return {
|
|
16305
16450
|
transport: "http",
|
|
16306
16451
|
serverUrl: url.toString(),
|
|
16307
|
-
...
|
|
16452
|
+
...label2,
|
|
16308
16453
|
...server.auth?.accessTokenEnv ? { auth: { accessTokenEnv: server.auth.accessTokenEnv } } : {}
|
|
16309
16454
|
};
|
|
16310
16455
|
}
|