Files
reverse-engineering-anything/src/server/toolResult.ts
T
Kaoru0822andClaude Opus 5.5 0ecf3f2438 perf(mcp): keep Playwright and type-only imports off MCP startup (#1691)
* fix(build): emit no runtime imports for type-only bindings

Under verbatimModuleSyntax, an import whose specifiers all carry inline
`type` modifiers compiles to `import {} from "..."`, a side-effect import
that still resolves and evaluates the module. 78 compiled modules carried
one, including `import {} from "playwright-core"` in the browser scenario
session, which loaded Playwright's module graph at MCP startup.

Use top-level `import type` for these bindings and enable oxlint's
typescript/no-import-type-side-effects rule so the pattern cannot return.

Refs #1685

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(mcp): load Playwright when a scenario launches, not at startup

The browser scenario and Electron active providers are constructed before
the stdio server serves, and both bound Playwright's launchers at module
load. Resolve `chromium` and `_electron` from playwright-core when a
capture opens instead, as the web module resolver already does.

MCP startup now resolves 1,312 modules (4,902 resolutions) instead of
1,598 (6,076) before reading the first request, and no longer loads
playwright-core. Provider construction, the tool catalog, and capability
inventory are unchanged.

A boundary test spawns the stdio server, initializes, lists tools, and
asserts playwright-core and isomorphic-git were not resolved; the CLI
probe now exercises the real launch path to prove detection.

Closes #1685

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:04:05 -07:00

151 lines
5.5 KiB
TypeScript

import type { CallToolResult } from "@modelcontextprotocol/server";
import { STDIO_DEFAULT_MAX_BUFFER_SIZE } from "@modelcontextprotocol/server";
import type { ToolContract } from "../contracts/toolContractTypes.js";
import type { Evidence } from "../domain/evidence.js";
import {
projectAnalysisError,
type AnalysisErrorProjection,
} from "../domain/analysisErrorProjection.js";
import type { AnalysisError } from "../domain/analysisErrorBase.js";
import type { JsonValue } from "../domain/jsonValue.js";
import type { Result } from "../domain/result.js";
import { AnalysisResourceConstraintError } from "../domain/analysisErrorCore.js";
import {
encodeToolResult,
MCP_RESULT_STRING_LIMIT,
} from "./toolResultEncoding.js";
/** Create one delivery policy from the already selected, parsed response budget. */
export const createToolResultDelivery = (
maximumResponseBytes: number | undefined,
): ToolResultDelivery =>
new ToolResultDelivery(maximumResponseBytes ?? STDIO_DEFAULT_MAX_BUFFER_SIZE);
/** Immutable encoding and recovery policy shared by one MCP server. */
export class ToolResultDelivery {
readonly resultBudgetBytes: number;
constructor(maximumResponseBytes: number) {
this.resultBudgetBytes = maximumResponseBytes - 1024;
Object.freeze(this);
}
/** Serialize a plain application result or its actionable error. */
toCallToolResult(
result: Result<JsonValue, AnalysisError>,
contract: ToolContract,
): CallToolResult {
return result.ok
? this.successResult(
contract.kind === "session" ? { result: result.value } : result.value,
contract,
)
: this.toErrorToolResult(result.error);
}
/** Deliver Evidence using the producer's explicit recording acknowledgment. */
toEvidenceToolResult(
evidence: Evidence,
contract: ToolContract,
recorded: Result<unknown, AnalysisError> | undefined,
): CallToolResult {
return recorded !== undefined && !recorded.ok
? this.toErrorToolResult(recorded.error)
: this.successResult(evidence, contract, {
evidence_id: evidence.evidence_id,
...(recorded === undefined
? {}
: {
evidence_reference: {
kind: "retained-evidence",
evidence_id: evidence.evidence_id,
},
}),
});
}
/** Project an error without allocating oversized repeated MCP text. */
toErrorToolResult(error: AnalysisError): CallToolResult {
return this.projectedErrorResult(projectAnalysisError(error));
}
/** Deliver the original failure with the producer-acknowledged partial Evidence reference. */
toRecordedPartialErrorToolResult(
error: AnalysisError,
evidenceId: string,
recorded: Result<unknown, AnalysisError>,
): CallToolResult {
if (!recorded.ok) return this.toErrorToolResult(error);
const projected = projectAnalysisError(error);
return this.projectedErrorResult({
...projected,
details: {
...projected.details,
partial_observation: {
kind: "retained-evidence",
evidence_id: evidenceId,
},
},
});
}
private projectedErrorResult(
projected: AnalysisErrorProjection,
): CallToolResult {
const structuredContent = { error: projected };
// Errors reach the wire as text only; the structured copy is a private
// carrier the transport removes after its own budget check.
const encoded = encodeToolResult(
structuredContent,
this.resultBudgetBytes,
"text",
);
// The transport retains an oversized projected error before replacing it
// with a recoverable delivery constraint, using this same budget.
return {
content: encoded.ok ? [{ type: "text", text: encoded.text }] : [],
structuredContent,
isError: true,
};
}
private successResult(
value: JsonValue,
contract: ToolContract,
recovery: Readonly<Record<string, JsonValue>> = {},
): CallToolResult {
const encoded = encodeToolResult(value, this.resultBudgetBytes);
if (encoded.ok)
return {
content: [{ type: "text", text: encoded.text }],
structuredContent: value,
};
const retainedEvidence = recovery.evidence_reference !== undefined;
const recoveryAdvice = retainedEvidence
? "Export retained evidence to consume the complete analysis, or use complete CLI JSON output."
: encoded.constraint === "string-length"
? "Use complete CLI JSON output to consume the analysis."
: "Use a larger MCP response budget with a matching client receive buffer, or use complete CLI JSON output to consume the analysis.";
return this.toErrorToolResult(
new AnalysisResourceConstraintError(
contract.name,
"transport",
encoded.constraint === "string-length"
? `The operation completed, but its complete MCP response exceeds Node's single-string representation limit. ${recoveryAdvice}`
: `The operation completed, but its complete MCP response exceeds the stdio response budget. The analysis result was not truncated. ${recoveryAdvice}`,
{
boundary: "mcp-response",
default_receive_buffer_bytes: STDIO_DEFAULT_MAX_BUFFER_SIZE,
result_budget_bytes: this.resultBudgetBytes,
max_string_code_units: MCP_RESULT_STRING_LIMIT,
response_bytes_at_least: encoded.bytesAtLeast,
response_code_units_at_least: encoded.codeUnitsAtLeast,
constraint: encoded.constraint,
...recovery,
},
),
);
}
}