@konneal/engine 0.2.150 → 0.2.153

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.
@@ -94,6 +94,44 @@ export function buildTools(): McpTool[] {
94
94
  ];
95
95
  }
96
96
 
97
+ // The tool-result budget (the cloudflare/mcp pattern, 2026-10-19): an
98
+ // MCP client spends its own context reading tool results, so an
99
+ // unbounded payload (a broad db.find over the register, a large
100
+ // retrieve) can eat an agent's window. An oversized result comes back
101
+ // size-bounded and still valid JSON: short fields (names, numbers,
102
+ // metadata) stay whole, long strings clip, every cut is marked.
103
+ export const TOOL_RESULT_CHAR_CAP = 24000; // ≈6k tokens
104
+ export const TRUNCATION_MARK = "--- TRUNCATED ---";
105
+
106
+ const jsonLen = (x: unknown): number => JSON.stringify(x)?.length ?? 0;
107
+
108
+ function clipStrings(value: unknown, budget: number): unknown {
109
+ if (typeof value === "string") return value.length <= budget ? value : `${value.slice(0, budget)}\n${TRUNCATION_MARK}`;
110
+ if (Array.isArray(value)) return value.map((v) => clipStrings(v, budget));
111
+ if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, clipStrings(v, budget)]));
112
+ return value;
113
+ }
114
+
115
+ export function capToolResult(payload: unknown, cap: number = TOOL_RESULT_CHAR_CAP): unknown {
116
+ if (jsonLen(payload) <= cap) return payload;
117
+ // the per-string budget shrinks until the whole payload fits
118
+ let lo = 200;
119
+ let hi = cap;
120
+ let best = clipStrings(payload, lo);
121
+ while (lo <= hi) {
122
+ const mid = (lo + hi) >> 1;
123
+ const candidate = clipStrings(payload, mid);
124
+ if (jsonLen(candidate) <= cap) {
125
+ best = candidate;
126
+ lo = mid + 1;
127
+ } else {
128
+ hi = mid - 1;
129
+ }
130
+ }
131
+ if (jsonLen(best) <= cap) return best;
132
+ return { truncation: `the tool result exceeded the ${cap}-character budget and was withheld` };
133
+ }
134
+
97
135
  export type McpResult =
98
136
  | { ok: true; result: unknown }
99
137
  | { ok: true; accepted: true }
@@ -120,7 +158,7 @@ export function dispatch(
120
158
  return Promise.resolve({ ok: false, code: -32602, message: `unknown tool: ${name}` });
121
159
  }
122
160
  return callTool(registryName(name), params?.arguments ?? {}).then(
123
- (payload) => ({ ok: true, result: { content: [{ type: "text", text: JSON.stringify(payload) }] } }) as McpResult,
161
+ (payload) => ({ ok: true, result: { content: [{ type: "text", text: JSON.stringify(capToolResult(payload)) }] } }) as McpResult,
124
162
  );
125
163
  }
126
164
  default: