@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.js
CHANGED
|
@@ -3836,22 +3836,22 @@ async function runAnthropicComputerUseHitl(options) {
|
|
|
3836
3836
|
"HITL check"
|
|
3837
3837
|
);
|
|
3838
3838
|
}
|
|
3839
|
-
async function runComputerUseDriver(query2, options, mode,
|
|
3839
|
+
async function runComputerUseDriver(query2, options, mode, label2) {
|
|
3840
3840
|
const env = {
|
|
3841
3841
|
...process.env,
|
|
3842
3842
|
...options.env,
|
|
3843
3843
|
...options.model ? { MST_COWORK_CUA_MODEL: options.model } : {}
|
|
3844
3844
|
};
|
|
3845
3845
|
if (options.deadlineAt <= Date.now())
|
|
3846
|
-
throw new Error(`Computer Use ${
|
|
3846
|
+
throw new Error(`Computer Use ${label2} deadline exceeded; not retrying.`);
|
|
3847
3847
|
const python = await ensureCoworkPython(env);
|
|
3848
3848
|
const DRIVER_PATH = resolveDriverPath(env);
|
|
3849
3849
|
const timeoutMs = options.deadlineAt - Date.now();
|
|
3850
3850
|
if (timeoutMs <= 0)
|
|
3851
|
-
throw new Error(`Computer Use ${
|
|
3851
|
+
throw new Error(`Computer Use ${label2} deadline exceeded; not retrying.`);
|
|
3852
3852
|
const maxActions = options.maxActions ?? (mode === "hitl" ? 12 : 24);
|
|
3853
3853
|
diagnostic(
|
|
3854
|
-
`starting Computer Use ${
|
|
3854
|
+
`starting Computer Use ${label2} (script=${DRIVER_PATH}, maxActions=${maxActions}, timeoutMs=${timeoutMs})`
|
|
3855
3855
|
);
|
|
3856
3856
|
let stdout = "";
|
|
3857
3857
|
try {
|
|
@@ -3882,26 +3882,26 @@ async function runComputerUseDriver(query2, options, mode, label) {
|
|
|
3882
3882
|
telemetry3
|
|
3883
3883
|
);
|
|
3884
3884
|
}
|
|
3885
|
-
diagnostic(`Computer Use ${
|
|
3885
|
+
diagnostic(`Computer Use ${label2} failed: ${details}`);
|
|
3886
3886
|
throw new ComputerUseDriverError(
|
|
3887
|
-
`Computer Use ${
|
|
3887
|
+
`Computer Use ${label2} failed: ${details}`,
|
|
3888
3888
|
telemetry3
|
|
3889
3889
|
);
|
|
3890
3890
|
}
|
|
3891
3891
|
diagnostic(
|
|
3892
|
-
`Computer Use ${
|
|
3892
|
+
`Computer Use ${label2} exited successfully (stdoutBytes=${stdout.length})`
|
|
3893
3893
|
);
|
|
3894
3894
|
const record = parseLastJsonLine(stdout);
|
|
3895
3895
|
const expectedStatus = mode === "submit" ? "submitted" : "hitl_checked";
|
|
3896
3896
|
if (record?.status !== expectedStatus) {
|
|
3897
3897
|
throw new ComputerUseDriverError(
|
|
3898
|
-
`Computer Use ${
|
|
3898
|
+
`Computer Use ${label2} did not reach ${expectedStatus}.`,
|
|
3899
3899
|
parseTelemetry(record?.telemetry, env, "partial")
|
|
3900
3900
|
);
|
|
3901
3901
|
}
|
|
3902
3902
|
if (!isCount(record.action_count) || !isSafeModel(record.model, env)) {
|
|
3903
3903
|
throw new ComputerUseDriverError(
|
|
3904
|
-
`Computer Use ${
|
|
3904
|
+
`Computer Use ${label2} returned invalid result fields.`,
|
|
3905
3905
|
parseTelemetry(record.telemetry, env, "partial")
|
|
3906
3906
|
);
|
|
3907
3907
|
}
|
|
@@ -3912,7 +3912,7 @@ async function runComputerUseDriver(query2, options, mode, label) {
|
|
|
3912
3912
|
...telemetry2 ? { telemetry: telemetry2 } : {}
|
|
3913
3913
|
};
|
|
3914
3914
|
diagnostic(
|
|
3915
|
-
`Computer Use ${
|
|
3915
|
+
`Computer Use ${label2} completed (actions=${common.action_count})`
|
|
3916
3916
|
);
|
|
3917
3917
|
if (mode === "hitl") return { status: "hitl_checked", ...common };
|
|
3918
3918
|
const submission = asRecord(record.submission_action);
|
|
@@ -9053,10 +9053,57 @@ function escapeHtml(text) {
|
|
|
9053
9053
|
|
|
9054
9054
|
// package.json
|
|
9055
9055
|
var package_default = {
|
|
9056
|
-
version: "2.0.0-beta.
|
|
9056
|
+
version: "2.0.0-beta.4"};
|
|
9057
|
+
|
|
9058
|
+
// src/mcp/connectionDiagnostics.ts
|
|
9059
|
+
function classifyMCPConnectionFailure(error) {
|
|
9060
|
+
if (typeof error !== "object" || error === null) return "connection_failed";
|
|
9061
|
+
const details = error;
|
|
9062
|
+
const status = details.response?.status ?? details.status ?? details.code;
|
|
9063
|
+
if (typeof status === "number" && Number.isInteger(status) && status >= 400 && status <= 599) {
|
|
9064
|
+
return `http_${status}`;
|
|
9065
|
+
}
|
|
9066
|
+
const message = error instanceof Error ? error.message : "";
|
|
9067
|
+
const httpStatus = /\b(?:HTTP|status(?: code)?|code:)\s*[:=(]?\s*([45]\d{2})\b/i.exec(
|
|
9068
|
+
message
|
|
9069
|
+
)?.[1];
|
|
9070
|
+
if (httpStatus) return `http_${httpStatus}`;
|
|
9071
|
+
for (const code of ["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND"]) {
|
|
9072
|
+
if (details.code === code || message.toUpperCase().includes(code)) {
|
|
9073
|
+
return code.toLowerCase();
|
|
9074
|
+
}
|
|
9075
|
+
}
|
|
9076
|
+
if (/timed out|timeout/i.test(message)) return "timeout";
|
|
9077
|
+
if (/network|socket hang up|fetch failed/i.test(message))
|
|
9078
|
+
return "network_error";
|
|
9079
|
+
return "connection_failed";
|
|
9080
|
+
}
|
|
9081
|
+
var MCPHttpConnectionError = class extends Error {
|
|
9082
|
+
constructor(streamableError, sseError, retryable, retryAfterMs) {
|
|
9083
|
+
const streamableHttpFailure = classifyMCPConnectionFailure(streamableError);
|
|
9084
|
+
const sseFailure = classifyMCPConnectionFailure(sseError);
|
|
9085
|
+
super(
|
|
9086
|
+
`MCP connection failed: streamableHttp=${streamableHttpFailure}; sse=${sseFailure}`
|
|
9087
|
+
);
|
|
9088
|
+
this.retryable = retryable;
|
|
9089
|
+
this.retryAfterMs = retryAfterMs;
|
|
9090
|
+
this.name = "MCPHttpConnectionError";
|
|
9091
|
+
this.streamableHttpFailure = streamableHttpFailure;
|
|
9092
|
+
this.sseFailure = sseFailure;
|
|
9093
|
+
}
|
|
9094
|
+
streamableHttpFailure;
|
|
9095
|
+
sseFailure;
|
|
9096
|
+
};
|
|
9097
|
+
function formatMCPConnectionFailure(error) {
|
|
9098
|
+
if (error instanceof MCPHttpConnectionError) {
|
|
9099
|
+
return `MCP connection failed: streamableHttp=${error.streamableHttpFailure}; sse=${error.sseFailure}`;
|
|
9100
|
+
}
|
|
9101
|
+
return classifyMCPConnectionFailure(error);
|
|
9102
|
+
}
|
|
9057
9103
|
|
|
9058
9104
|
// src/mcp/clientFactory.ts
|
|
9059
9105
|
function getRetryAfterDelayMs(err) {
|
|
9106
|
+
if (err instanceof MCPHttpConnectionError) return err.retryAfterMs;
|
|
9060
9107
|
const response = err?.response;
|
|
9061
9108
|
const retryAfter = response?.headers?.get?.("Retry-After");
|
|
9062
9109
|
if (retryAfter) {
|
|
@@ -9075,6 +9122,7 @@ function isTransientNetworkError(err) {
|
|
|
9075
9122
|
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");
|
|
9076
9123
|
}
|
|
9077
9124
|
function isRetryableError(err) {
|
|
9125
|
+
if (err instanceof MCPHttpConnectionError) return err.retryable;
|
|
9078
9126
|
return isTransientNetworkError(err) || isRateLimitError(err);
|
|
9079
9127
|
}
|
|
9080
9128
|
async function retryWithBackoff(fn, maxAttempts) {
|
|
@@ -9092,7 +9140,7 @@ async function retryWithBackoff(fn, maxAttempts) {
|
|
|
9092
9140
|
attempt + 1,
|
|
9093
9141
|
maxAttempts + 1,
|
|
9094
9142
|
delayMs,
|
|
9095
|
-
err
|
|
9143
|
+
formatMCPConnectionFailure(err)
|
|
9096
9144
|
);
|
|
9097
9145
|
await new Promise((resolve8) => setTimeout(resolve8, delayMs));
|
|
9098
9146
|
} else {
|
|
@@ -9249,15 +9297,24 @@ async function createMCPClientForConfig(config, options) {
|
|
|
9249
9297
|
} catch (err) {
|
|
9250
9298
|
debugHttp(
|
|
9251
9299
|
"streamableHttp failed (%s), falling back to SSE",
|
|
9252
|
-
err
|
|
9300
|
+
formatMCPConnectionFailure(err)
|
|
9253
9301
|
);
|
|
9254
9302
|
debugClient("Streamable HTTP failed, falling back to SSE transport");
|
|
9255
9303
|
debugHttp("Attempting transport: sse");
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9304
|
+
try {
|
|
9305
|
+
const sseTransport = new SSEClientTransport(url, {
|
|
9306
|
+
requestInit,
|
|
9307
|
+
...options?.authProvider ? { authProvider: options.authProvider } : {}
|
|
9308
|
+
});
|
|
9309
|
+
await client.connect(sseTransport, connectOptions);
|
|
9310
|
+
} catch (sseError) {
|
|
9311
|
+
throw new MCPHttpConnectionError(
|
|
9312
|
+
err,
|
|
9313
|
+
sseError,
|
|
9314
|
+
isRetryableError(err) || isRetryableError(sseError),
|
|
9315
|
+
getRetryAfterDelayMs(err) ?? getRetryAfterDelayMs(sseError)
|
|
9316
|
+
);
|
|
9317
|
+
}
|
|
9261
9318
|
debugClient("Connected via SSE");
|
|
9262
9319
|
debugHttp("Connection established via sse");
|
|
9263
9320
|
}
|
|
@@ -13715,6 +13772,86 @@ async function getCoworkPlatform(provider) {
|
|
|
13715
13772
|
);
|
|
13716
13773
|
}
|
|
13717
13774
|
|
|
13775
|
+
// src/evals/cowork/mcpReadiness.ts
|
|
13776
|
+
init_config();
|
|
13777
|
+
var PREFLIGHT_TIMEOUT_MS = 3e4;
|
|
13778
|
+
var CoworkMcpReadinessError = class extends Error {
|
|
13779
|
+
servers;
|
|
13780
|
+
constructor(servers) {
|
|
13781
|
+
super(
|
|
13782
|
+
`Cowork MCP preflight failed; no task was submitted. ${servers.map(
|
|
13783
|
+
(server) => `${server.label}=${server.status}${server.error ? `(${server.error})` : ""}(${server.elapsedMs}ms)`
|
|
13784
|
+
).join(", ")}`
|
|
13785
|
+
);
|
|
13786
|
+
this.name = "CoworkMcpReadinessError";
|
|
13787
|
+
this.servers = servers;
|
|
13788
|
+
}
|
|
13789
|
+
};
|
|
13790
|
+
function label(server, index) {
|
|
13791
|
+
return server.label ?? `server-${index + 1}`;
|
|
13792
|
+
}
|
|
13793
|
+
async function withTimeout(promise, timeoutMs) {
|
|
13794
|
+
let timer;
|
|
13795
|
+
try {
|
|
13796
|
+
return await Promise.race([
|
|
13797
|
+
promise,
|
|
13798
|
+
new Promise((_, reject) => {
|
|
13799
|
+
timer = setTimeout(
|
|
13800
|
+
() => reject(new Error("MCP preflight timed out")),
|
|
13801
|
+
timeoutMs
|
|
13802
|
+
);
|
|
13803
|
+
})
|
|
13804
|
+
]);
|
|
13805
|
+
} finally {
|
|
13806
|
+
if (timer) clearTimeout(timer);
|
|
13807
|
+
}
|
|
13808
|
+
}
|
|
13809
|
+
function resolveServer(server, env) {
|
|
13810
|
+
if (!isHttpConfig(server)) return server;
|
|
13811
|
+
const [coworkServer] = toCoworkServers([server]);
|
|
13812
|
+
if (!coworkServer) throw new Error("Invalid Cowork MCP configuration.");
|
|
13813
|
+
const headers = resolveCoworkMcpHeaders([coworkServer], env);
|
|
13814
|
+
return {
|
|
13815
|
+
...server,
|
|
13816
|
+
// Use the same validated runtime headers as Cowork setup.
|
|
13817
|
+
auth: void 0,
|
|
13818
|
+
headers: headers[coworkServer.label]
|
|
13819
|
+
};
|
|
13820
|
+
}
|
|
13821
|
+
async function verifyCoworkMcpServers(servers, env) {
|
|
13822
|
+
const results = await Promise.all(
|
|
13823
|
+
servers.map(async (server, index) => {
|
|
13824
|
+
const started = Date.now();
|
|
13825
|
+
let client;
|
|
13826
|
+
try {
|
|
13827
|
+
client = await createMCPClientForConfig(resolveServer(server, env));
|
|
13828
|
+
const tools = await withTimeout(
|
|
13829
|
+
client.listTools(),
|
|
13830
|
+
PREFLIGHT_TIMEOUT_MS
|
|
13831
|
+
);
|
|
13832
|
+
return {
|
|
13833
|
+
label: label(server, index),
|
|
13834
|
+
status: "connected",
|
|
13835
|
+
toolCount: tools.tools.length,
|
|
13836
|
+
elapsedMs: Date.now() - started
|
|
13837
|
+
};
|
|
13838
|
+
} catch (error) {
|
|
13839
|
+
return {
|
|
13840
|
+
label: label(server, index),
|
|
13841
|
+
status: "failed",
|
|
13842
|
+
elapsedMs: Date.now() - started,
|
|
13843
|
+
error: formatMCPConnectionFailure(error)
|
|
13844
|
+
};
|
|
13845
|
+
} finally {
|
|
13846
|
+
if (client) await closeMCPClient(client).catch(() => void 0);
|
|
13847
|
+
}
|
|
13848
|
+
})
|
|
13849
|
+
);
|
|
13850
|
+
if (results.some((result) => result.status !== "connected"))
|
|
13851
|
+
throw new CoworkMcpReadinessError(results);
|
|
13852
|
+
return results;
|
|
13853
|
+
}
|
|
13854
|
+
|
|
13718
13855
|
// src/evals/coworkHost.ts
|
|
13719
13856
|
init_driver();
|
|
13720
13857
|
var OptionsSchema = z.object({
|
|
@@ -13797,6 +13934,14 @@ async function runBatch(requests, context, selectedPlatform) {
|
|
|
13797
13934
|
env,
|
|
13798
13935
|
model: config.model
|
|
13799
13936
|
});
|
|
13937
|
+
if (servers.length) {
|
|
13938
|
+
const readiness = await verifyCoworkMcpServers(servers, env);
|
|
13939
|
+
process.stderr.write(
|
|
13940
|
+
`[mst:cowork] MCP preflight ready: ${readiness.map(
|
|
13941
|
+
(server) => `${server.label}(${server.toolCount ?? 0} tools, ${server.elapsedMs}ms)`
|
|
13942
|
+
).join(", ")}\\n`
|
|
13943
|
+
);
|
|
13944
|
+
}
|
|
13800
13945
|
for (const [index, request] of requests.entries()) {
|
|
13801
13946
|
const caseStartedAt = Date.now();
|
|
13802
13947
|
const computerUse = {
|
|
@@ -14306,8 +14451,8 @@ function claudeCliHost(options) {
|
|
|
14306
14451
|
if (options.servers) {
|
|
14307
14452
|
for (const key of Object.keys(mcpServers)) delete mcpServers[key];
|
|
14308
14453
|
for (const entry of options.servers) {
|
|
14309
|
-
const
|
|
14310
|
-
mcpServers[
|
|
14454
|
+
const label2 = entry.label ?? "mcp-server";
|
|
14455
|
+
mcpServers[label2] = entry.transport === "http" ? {
|
|
14311
14456
|
type: "http",
|
|
14312
14457
|
url: entry.serverUrl,
|
|
14313
14458
|
headers: {
|
|
@@ -16261,9 +16406,9 @@ function buildArmDeltas(arms) {
|
|
|
16261
16406
|
);
|
|
16262
16407
|
}
|
|
16263
16408
|
function redactServerForReport(server) {
|
|
16264
|
-
const
|
|
16409
|
+
const label2 = server.label ? { label: server.label } : {};
|
|
16265
16410
|
if (server.transport === "stdio") {
|
|
16266
|
-
return { transport: "stdio", command: server.command, ...
|
|
16411
|
+
return { transport: "stdio", command: server.command, ...label2 };
|
|
16267
16412
|
}
|
|
16268
16413
|
const url = new URL(server.serverUrl);
|
|
16269
16414
|
url.username = "";
|
|
@@ -16273,7 +16418,7 @@ function redactServerForReport(server) {
|
|
|
16273
16418
|
return {
|
|
16274
16419
|
transport: "http",
|
|
16275
16420
|
serverUrl: url.toString(),
|
|
16276
|
-
...
|
|
16421
|
+
...label2,
|
|
16277
16422
|
...server.auth?.accessTokenEnv ? { auth: { accessTokenEnv: server.auth.accessTokenEnv } } : {}
|
|
16278
16423
|
};
|
|
16279
16424
|
}
|