mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 09:55:46 +00:00
Retain complete authenticated application Evidence when the repeated MCP representation exceeds the default receive frame or Node string limit. Return a typed transport constraint, keep useful followup queries available, and stream full canonical bundles to atomic destinations. Verify complete Notion and Obsidian workflows on Linux and native Windows, full CLI/Evidence parity, and real large Chrome screenshot recovery. Preserve complete success contracts and caller-selected evidence paths. Refs #1065, #1053.
56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
import { STDIO_DEFAULT_MAX_BUFFER_SIZE } from "@modelcontextprotocol/server";
|
|
import { constants as bufferConstants } from "node:buffer";
|
|
|
|
import type { JsonValue } from "../domain/jsonValue.js";
|
|
import { bufferedJsonParts, jsonParts } from "../domain/jsonSerialization.js";
|
|
|
|
// Leave room for the SDK's JSON-RPC envelope and protocol metadata. This budget
|
|
// follows the pinned client's actual default framing limit, not a result-row cap.
|
|
export const MCP_RESULT_BUDGET_BYTES = STDIO_DEFAULT_MAX_BUFFER_SIZE - 1024;
|
|
export const MCP_RESULT_STRING_LIMIT = bufferConstants.MAX_STRING_LENGTH - 1024;
|
|
|
|
const RESULT_ENVELOPE_BYTES =
|
|
Buffer.byteLength(
|
|
JSON.stringify({
|
|
content: [{ type: "text", text: "" }],
|
|
structuredContent: null,
|
|
}),
|
|
) - 4;
|
|
|
|
/** Result of encoding the complete repeated MCP representations within a wire budget. */
|
|
export type ToolResultEncoding =
|
|
| { readonly ok: true; readonly text: string; readonly bytes: number }
|
|
| {
|
|
readonly ok: false;
|
|
readonly bytesAtLeast: number;
|
|
readonly codeUnitsAtLeast: number;
|
|
readonly constraint: "receive-buffer" | "string-length";
|
|
};
|
|
|
|
/** Account for structured JSON and escaped text before allocating the complete text. */
|
|
export const encodeToolResult = (
|
|
value: JsonValue,
|
|
budgetBytes = MCP_RESULT_BUDGET_BYTES,
|
|
): ToolResultEncoding => {
|
|
const parts: string[] = [];
|
|
let bytes = RESULT_ENVELOPE_BYTES;
|
|
let codeUnits = RESULT_ENVELOPE_BYTES;
|
|
for (const part of bufferedJsonParts(jsonParts(value))) {
|
|
const escapedPart = JSON.stringify(part);
|
|
bytes += Buffer.byteLength(part) + Buffer.byteLength(escapedPart) - 2;
|
|
codeUnits += part.length + escapedPart.length - 2;
|
|
if (bytes > budgetBytes || codeUnits > MCP_RESULT_STRING_LIMIT)
|
|
return {
|
|
ok: false,
|
|
bytesAtLeast: bytes,
|
|
codeUnitsAtLeast: codeUnits,
|
|
constraint:
|
|
codeUnits > MCP_RESULT_STRING_LIMIT
|
|
? "string-length"
|
|
: "receive-buffer",
|
|
};
|
|
parts.push(part);
|
|
}
|
|
return { ok: true, text: parts.join(""), bytes };
|
|
};
|