mirror of
https://github.com/morluto/rea.git
synced 2026-11-10 16:57:19 +00:00
fix(mcp): retain complete application results across response limits
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.
This commit is contained in:
1 parent
c701376777
commit
a1ed4232de
43 files changed
+1314
-186
No files matched your search
@@ -0,0 +1,55 @@
|
||||
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 };
|
||||
};
|
||||
Reference in new issue
Block a user