@gleanwork/mcp-server-tester 1.1.0 → 1.1.1
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 +1 -1
- package/dist/fixtures/mcp.js +1 -1
- package/dist/fixtures/mcp.js.map +1 -1
- package/dist/{index-BcUWzQCx.d.cts → index-BlqLvjnP.d.cts} +42 -2
- package/dist/{index-BcUWzQCx.d.ts → index-BlqLvjnP.d.ts} +42 -2
- package/dist/index.cjs +76 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +76 -15
- package/dist/index.js.map +1 -1
- package/dist/reporters/mcpReporter.cjs +19 -1
- package/dist/reporters/mcpReporter.cjs.map +1 -1
- package/dist/reporters/mcpReporter.d.cts +1 -0
- package/dist/reporters/mcpReporter.d.ts +1 -0
- package/dist/reporters/mcpReporter.js +19 -1
- package/dist/reporters/mcpReporter.js.map +1 -1
- package/dist/reporters/ui-dist/app.js +16 -16
- package/dist/reporters/ui-dist/styles.css +1 -1
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -2087,6 +2087,8 @@ interface LLMToolCall {
|
|
|
2087
2087
|
arguments: Record<string, unknown>;
|
|
2088
2088
|
/** Optional tool call ID (for tracking) */
|
|
2089
2089
|
id?: string;
|
|
2090
|
+
/** Tool result text, when the host surfaces it (paired to this call) */
|
|
2091
|
+
output?: string;
|
|
2090
2092
|
}
|
|
2091
2093
|
/**
|
|
2092
2094
|
* Result from an MCP host simulation
|
|
@@ -2102,10 +2104,17 @@ interface MCPHostSimulationResult {
|
|
|
2102
2104
|
error?: string;
|
|
2103
2105
|
/** The scenario prompt that was given to the LLM */
|
|
2104
2106
|
scenario?: string;
|
|
2105
|
-
/**
|
|
2107
|
+
/**
|
|
2108
|
+
* The conversation turns for attribution analysis.
|
|
2109
|
+
*
|
|
2110
|
+
* Tool turns reference their call via `toolCallId` rather than inlining the
|
|
2111
|
+
* (potentially large) result — hydrate the output from the matching
|
|
2112
|
+
* `toolCalls[]` entry. `content` holds assistant/user text.
|
|
2113
|
+
*/
|
|
2106
2114
|
conversationHistory?: Array<{
|
|
2107
2115
|
role: 'user' | 'assistant' | 'tool';
|
|
2108
|
-
content
|
|
2116
|
+
content?: string;
|
|
2117
|
+
toolCallId?: string;
|
|
2109
2118
|
}>;
|
|
2110
2119
|
/**
|
|
2111
2120
|
* Milliseconds spent waiting for LLM responses
|
|
@@ -3272,6 +3281,37 @@ interface MCPEvalRunData {
|
|
|
3272
3281
|
* Server capabilities discovered via listTools (optional)
|
|
3273
3282
|
*/
|
|
3274
3283
|
serverCapabilities?: MCPServerCapabilitiesData[];
|
|
3284
|
+
/**
|
|
3285
|
+
* Summary of a tool-metadata variant experiment (runVariantExperiment),
|
|
3286
|
+
* present when the run was produced by one. The `results` above reflect the
|
|
3287
|
+
* winning variant; this records how the experiment got there.
|
|
3288
|
+
*/
|
|
3289
|
+
variantExperiment?: MCPVariantExperimentData;
|
|
3290
|
+
}
|
|
3291
|
+
/**
|
|
3292
|
+
* Compact summary of a `runVariantExperiment` run, for the reporter UI.
|
|
3293
|
+
*/
|
|
3294
|
+
interface MCPVariantExperimentData {
|
|
3295
|
+
/** Metric optimized: passRate | toolF1 | toolPrecision | toolRecall. */
|
|
3296
|
+
metric: string;
|
|
3297
|
+
/** Baseline metric value (0-1), before any variant. */
|
|
3298
|
+
baselineValue: number;
|
|
3299
|
+
/** Best metric value achieved (the winner, or best attempt) (0-1). */
|
|
3300
|
+
bestValue: number;
|
|
3301
|
+
/** The best candidate from each round, in order. */
|
|
3302
|
+
rounds: Array<{
|
|
3303
|
+
round: number;
|
|
3304
|
+
variantId: string;
|
|
3305
|
+
metricValue: number;
|
|
3306
|
+
metricDelta: number;
|
|
3307
|
+
disqualified: boolean;
|
|
3308
|
+
}>;
|
|
3309
|
+
/** Winning variant id, if a non-regressing candidate beat the baseline. */
|
|
3310
|
+
winnerVariantId?: string;
|
|
3311
|
+
/** apply | reject | inconclusive */
|
|
3312
|
+
recommendation?: string;
|
|
3313
|
+
/** Why the experiment stopped. */
|
|
3314
|
+
reason: string;
|
|
3275
3315
|
}
|
|
3276
3316
|
/**
|
|
3277
3317
|
* Historical summary for trend charts
|
|
@@ -2087,6 +2087,8 @@ interface LLMToolCall {
|
|
|
2087
2087
|
arguments: Record<string, unknown>;
|
|
2088
2088
|
/** Optional tool call ID (for tracking) */
|
|
2089
2089
|
id?: string;
|
|
2090
|
+
/** Tool result text, when the host surfaces it (paired to this call) */
|
|
2091
|
+
output?: string;
|
|
2090
2092
|
}
|
|
2091
2093
|
/**
|
|
2092
2094
|
* Result from an MCP host simulation
|
|
@@ -2102,10 +2104,17 @@ interface MCPHostSimulationResult {
|
|
|
2102
2104
|
error?: string;
|
|
2103
2105
|
/** The scenario prompt that was given to the LLM */
|
|
2104
2106
|
scenario?: string;
|
|
2105
|
-
/**
|
|
2107
|
+
/**
|
|
2108
|
+
* The conversation turns for attribution analysis.
|
|
2109
|
+
*
|
|
2110
|
+
* Tool turns reference their call via `toolCallId` rather than inlining the
|
|
2111
|
+
* (potentially large) result — hydrate the output from the matching
|
|
2112
|
+
* `toolCalls[]` entry. `content` holds assistant/user text.
|
|
2113
|
+
*/
|
|
2106
2114
|
conversationHistory?: Array<{
|
|
2107
2115
|
role: 'user' | 'assistant' | 'tool';
|
|
2108
|
-
content
|
|
2116
|
+
content?: string;
|
|
2117
|
+
toolCallId?: string;
|
|
2109
2118
|
}>;
|
|
2110
2119
|
/**
|
|
2111
2120
|
* Milliseconds spent waiting for LLM responses
|
|
@@ -3272,6 +3281,37 @@ interface MCPEvalRunData {
|
|
|
3272
3281
|
* Server capabilities discovered via listTools (optional)
|
|
3273
3282
|
*/
|
|
3274
3283
|
serverCapabilities?: MCPServerCapabilitiesData[];
|
|
3284
|
+
/**
|
|
3285
|
+
* Summary of a tool-metadata variant experiment (runVariantExperiment),
|
|
3286
|
+
* present when the run was produced by one. The `results` above reflect the
|
|
3287
|
+
* winning variant; this records how the experiment got there.
|
|
3288
|
+
*/
|
|
3289
|
+
variantExperiment?: MCPVariantExperimentData;
|
|
3290
|
+
}
|
|
3291
|
+
/**
|
|
3292
|
+
* Compact summary of a `runVariantExperiment` run, for the reporter UI.
|
|
3293
|
+
*/
|
|
3294
|
+
interface MCPVariantExperimentData {
|
|
3295
|
+
/** Metric optimized: passRate | toolF1 | toolPrecision | toolRecall. */
|
|
3296
|
+
metric: string;
|
|
3297
|
+
/** Baseline metric value (0-1), before any variant. */
|
|
3298
|
+
baselineValue: number;
|
|
3299
|
+
/** Best metric value achieved (the winner, or best attempt) (0-1). */
|
|
3300
|
+
bestValue: number;
|
|
3301
|
+
/** The best candidate from each round, in order. */
|
|
3302
|
+
rounds: Array<{
|
|
3303
|
+
round: number;
|
|
3304
|
+
variantId: string;
|
|
3305
|
+
metricValue: number;
|
|
3306
|
+
metricDelta: number;
|
|
3307
|
+
disqualified: boolean;
|
|
3308
|
+
}>;
|
|
3309
|
+
/** Winning variant id, if a non-regressing candidate beat the baseline. */
|
|
3310
|
+
winnerVariantId?: string;
|
|
3311
|
+
/** apply | reject | inconclusive */
|
|
3312
|
+
recommendation?: string;
|
|
3313
|
+
/** Why the experiment stopped. */
|
|
3314
|
+
reason: string;
|
|
3275
3315
|
}
|
|
3276
3316
|
/**
|
|
3277
3317
|
* Historical summary for trend charts
|
package/dist/index.cjs
CHANGED
|
@@ -4425,7 +4425,7 @@ function escapeHtml(text) {
|
|
|
4425
4425
|
|
|
4426
4426
|
// package.json
|
|
4427
4427
|
var package_default = {
|
|
4428
|
-
version: "1.1.
|
|
4428
|
+
version: "1.1.1"};
|
|
4429
4429
|
|
|
4430
4430
|
// src/mcp/clientFactory.ts
|
|
4431
4431
|
function getRetryAfterDelayMs(err) {
|
|
@@ -6838,8 +6838,8 @@ Hint: run \`getMissingDependencyMessage('${provider}')\` or check docs/mcp-host.
|
|
|
6838
6838
|
Hint: check your API key environment variable (e.g. ANTHROPIC_API_KEY, GOOGLE_APPLICATION_CREDENTIALS).`;
|
|
6839
6839
|
}
|
|
6840
6840
|
if (raw.includes("404") || raw.includes("Not Found") || raw.toLowerCase().includes("model") && raw.toLowerCase().includes("not found")) {
|
|
6841
|
-
return `MCP host simulation failed:
|
|
6842
|
-
Hint:
|
|
6841
|
+
return `MCP host simulation failed: ${raw}
|
|
6842
|
+
Hint: a 404 usually means the model id is wrong or retired, or a base-URL override (e.g. ANTHROPIC_BASE_URL / OPENAI_BASE_URL pointing at a gateway) is routing requests somewhere that doesn't serve this model. Verify the model id and that no unexpected *_BASE_URL is set.`;
|
|
6843
6843
|
}
|
|
6844
6844
|
if (raw.includes("ENOTFOUND") || raw.includes("fetch failed") || raw.includes("ECONNREFUSED")) {
|
|
6845
6845
|
return `MCP host simulation failed: network error.
|
|
@@ -6937,12 +6937,18 @@ function createVercelOrchestrator() {
|
|
|
6937
6937
|
tools[toolName] = {
|
|
6938
6938
|
description: mcpTool.description ?? "",
|
|
6939
6939
|
inputSchema: jsonSchema2(rawSchema),
|
|
6940
|
-
execute: async (args) => {
|
|
6940
|
+
execute: async (args, opts) => {
|
|
6941
6941
|
const mcpStart = Date.now();
|
|
6942
6942
|
const result2 = await mcp.callTool(toolName, args);
|
|
6943
6943
|
mcpDurationMs += Date.now() - mcpStart;
|
|
6944
|
-
|
|
6945
|
-
|
|
6944
|
+
const output = extractText(result2);
|
|
6945
|
+
allToolCalls.push({
|
|
6946
|
+
id: opts?.toolCallId,
|
|
6947
|
+
name: toolName,
|
|
6948
|
+
arguments: args,
|
|
6949
|
+
output
|
|
6950
|
+
});
|
|
6951
|
+
return output;
|
|
6946
6952
|
}
|
|
6947
6953
|
};
|
|
6948
6954
|
}
|
|
@@ -6964,10 +6970,17 @@ function createVercelOrchestrator() {
|
|
|
6964
6970
|
totalCostUsd: 0,
|
|
6965
6971
|
durationMs: llmDurationMs
|
|
6966
6972
|
} : void 0;
|
|
6967
|
-
const conversationHistory = (result.steps ?? []).
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6973
|
+
const conversationHistory = (result.steps ?? []).flatMap((step) => {
|
|
6974
|
+
if (step.toolCalls?.length > 0) {
|
|
6975
|
+
return step.toolCalls.map(
|
|
6976
|
+
(tc) => ({
|
|
6977
|
+
role: "tool",
|
|
6978
|
+
toolCallId: tc.toolCallId
|
|
6979
|
+
})
|
|
6980
|
+
);
|
|
6981
|
+
}
|
|
6982
|
+
return step.text ? [{ role: "assistant", content: step.text }] : [];
|
|
6983
|
+
});
|
|
6971
6984
|
return {
|
|
6972
6985
|
success: true,
|
|
6973
6986
|
toolCalls: allToolCalls,
|
|
@@ -7023,7 +7036,13 @@ function parseStreamJson(stdout) {
|
|
|
7023
7036
|
for (const block of event.message.content) {
|
|
7024
7037
|
if (block.type === "tool_result") {
|
|
7025
7038
|
const content = typeof block.content === "string" ? block.content : JSON.stringify(block.content);
|
|
7026
|
-
|
|
7039
|
+
const call = block.tool_use_id ? toolCalls.find((tc) => tc.id === block.tool_use_id) : void 0;
|
|
7040
|
+
if (call) {
|
|
7041
|
+
call.output = content;
|
|
7042
|
+
conversationHistory.push({ role: "tool", toolCallId: call.id });
|
|
7043
|
+
} else {
|
|
7044
|
+
conversationHistory.push({ role: "tool", content });
|
|
7045
|
+
}
|
|
7027
7046
|
}
|
|
7028
7047
|
}
|
|
7029
7048
|
}
|
|
@@ -7979,6 +7998,7 @@ async function getGitHash() {
|
|
|
7979
7998
|
const result = await execFileNoThrow("git", ["rev-parse", "HEAD"]);
|
|
7980
7999
|
return result.status === 0 ? result.stdout.trim() : void 0;
|
|
7981
8000
|
}
|
|
8001
|
+
var warnedNoTestInfo = false;
|
|
7982
8002
|
async function runEvalDataset(options, context) {
|
|
7983
8003
|
const {
|
|
7984
8004
|
dataset,
|
|
@@ -8149,7 +8169,8 @@ async function runEvalDataset(options, context) {
|
|
|
8149
8169
|
contentType: "application/json",
|
|
8150
8170
|
body: Buffer.from(JSON.stringify({ caseResults }))
|
|
8151
8171
|
});
|
|
8152
|
-
} else if (caseResults.length > 0) {
|
|
8172
|
+
} else if (caseResults.length > 0 && !warnedNoTestInfo) {
|
|
8173
|
+
warnedNoTestInfo = true;
|
|
8153
8174
|
console.warn(
|
|
8154
8175
|
"[mcp-server-tester] runEvalDataset: testInfo not provided \u2014 results will not appear in the MCP reporter.\nTo enable reporting, pass testInfo from the Playwright test function:\n await runEvalDataset({ dataset }, { mcp, testInfo });"
|
|
8155
8176
|
);
|
|
@@ -8429,9 +8450,13 @@ async function runVariantExperiment(options, context) {
|
|
|
8429
8450
|
const maxRounds = options.maxRounds ?? 1;
|
|
8430
8451
|
const minImprovement = options.minImprovement ?? 0;
|
|
8431
8452
|
const allowRegressions = options.allowRegressions ?? false;
|
|
8453
|
+
const internalContext = {
|
|
8454
|
+
mcp: context.mcp,
|
|
8455
|
+
expect: context.expect
|
|
8456
|
+
};
|
|
8432
8457
|
const baseline = await runEvalDataset(
|
|
8433
8458
|
buildRunOptions(options, void 0),
|
|
8434
|
-
|
|
8459
|
+
internalContext
|
|
8435
8460
|
);
|
|
8436
8461
|
const baselineValue = readMetric(baseline, metric);
|
|
8437
8462
|
if (baselineValue === void 0) {
|
|
@@ -8459,7 +8484,7 @@ async function runVariantExperiment(options, context) {
|
|
|
8459
8484
|
for (const variant of variants) {
|
|
8460
8485
|
const candidate = await scoreVariant(
|
|
8461
8486
|
options,
|
|
8462
|
-
|
|
8487
|
+
internalContext,
|
|
8463
8488
|
baseline,
|
|
8464
8489
|
baselineValue,
|
|
8465
8490
|
metric,
|
|
@@ -8486,7 +8511,7 @@ async function runVariantExperiment(options, context) {
|
|
|
8486
8511
|
const winner = bestSoFar;
|
|
8487
8512
|
const proposalSource = winner ?? bestAttempted;
|
|
8488
8513
|
const proposal = proposalSource ? buildProposal(metric, baselineValue, proposalSource, winner !== void 0) : void 0;
|
|
8489
|
-
|
|
8514
|
+
const result = {
|
|
8490
8515
|
metric,
|
|
8491
8516
|
baseline,
|
|
8492
8517
|
rounds,
|
|
@@ -8495,6 +8520,42 @@ async function runVariantExperiment(options, context) {
|
|
|
8495
8520
|
converged: true,
|
|
8496
8521
|
reason
|
|
8497
8522
|
};
|
|
8523
|
+
if (context.testInfo) {
|
|
8524
|
+
const surfaceRun = winner?.result ?? bestAttempted?.result ?? baseline;
|
|
8525
|
+
await context.testInfo.attach("mcp-test-results", {
|
|
8526
|
+
contentType: "application/json",
|
|
8527
|
+
body: Buffer.from(
|
|
8528
|
+
JSON.stringify({ caseResults: surfaceRun.caseResults })
|
|
8529
|
+
)
|
|
8530
|
+
});
|
|
8531
|
+
await context.testInfo.attach("mcp-variant-experiment", {
|
|
8532
|
+
contentType: "application/json",
|
|
8533
|
+
body: Buffer.from(
|
|
8534
|
+
JSON.stringify(buildExperimentData(result, baselineValue))
|
|
8535
|
+
)
|
|
8536
|
+
});
|
|
8537
|
+
}
|
|
8538
|
+
return result;
|
|
8539
|
+
}
|
|
8540
|
+
function buildExperimentData(result, baselineValue) {
|
|
8541
|
+
return {
|
|
8542
|
+
metric: result.metric,
|
|
8543
|
+
baselineValue,
|
|
8544
|
+
bestValue: result.winner?.metricValue ?? result.proposal?.candidateValue ?? baselineValue,
|
|
8545
|
+
rounds: result.rounds.map((round) => {
|
|
8546
|
+
const best = round.best ?? round.candidates[0];
|
|
8547
|
+
return {
|
|
8548
|
+
round: round.round,
|
|
8549
|
+
variantId: best?.variant.id ?? "(none)",
|
|
8550
|
+
metricValue: best?.metricValue ?? baselineValue,
|
|
8551
|
+
metricDelta: best?.metricDelta ?? 0,
|
|
8552
|
+
disqualified: best?.disqualified ?? false
|
|
8553
|
+
};
|
|
8554
|
+
}),
|
|
8555
|
+
winnerVariantId: result.winner?.variant.id,
|
|
8556
|
+
recommendation: result.proposal?.recommendation,
|
|
8557
|
+
reason: result.reason
|
|
8558
|
+
};
|
|
8498
8559
|
}
|
|
8499
8560
|
async function gatherVariants(options, context) {
|
|
8500
8561
|
if (context.round === 0 && options.variants && options.variants.length > 0) {
|