Files
OmniRoute/tests/unit/compression/mcpAccessibility.test.ts
diegosouzapw e7a4ea8c7f feat(mcp): add MCP accessibility-tree smart filter engine
Adds compression engine that collapses repeated sibling lines (≥30 items
with same indent + role prefix) into head + summary + tail, preserves
[ref=eXX] anchors required by Playwright/computer-use MCPs, and
hard-truncates oversized text with a navigation hint footer.

Reduces token usage 60-80% on browser snapshots/accessibility trees from
external MCP servers (playwright-mcp, chrome-mcp). Configurable via
settings.compression.mcpAccessibility namespace.

Changes:
- open-sse/services/compression/engines/mcpAccessibility/: new engine
  (constants.ts, collapseRepeated.ts, index.ts with smartFilterText)
- open-sse/services/compression/types.ts: re-exports McpAccessibilityConfig
- src/lib/db/compression.ts: getMcpAccessibilityConfig/setMcpAccessibilityConfig
- src/lib/db/migrations/056_mcp_accessibility_compression.sql: default settings
- open-sse/mcp-server/server.ts: apply filter to all tool result text blocks
- tests/unit/compression/mcpAccessibility.test.ts: 4 unit tests
- tests/unit/mcp/serverSmartFilter.test.ts: 4 integration tests (DB getter/setter)

Ref: 9router/src/lib/mcp/stdioSseBridge.js:14-90 (algorithm origin).
2026-05-14 21:24:05 -03:00

41 lines
1.8 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { smartFilterText } from "@omniroute/open-sse/services/compression/engines/mcpAccessibility/index.ts";
import { DEFAULT_MCP_ACCESSIBILITY_CONFIG } from "@omniroute/open-sse/services/compression/engines/mcpAccessibility/constants.ts";
test("collapses ≥30 sibling buttons into head + summary + tail", () => {
const lines = [];
for (let i = 0; i < 50; i++) {
lines.push(` - button "Item ${i}" [ref=e${i}]`);
}
const input = lines.join("\n").padEnd(3000, " ");
const out = smartFilterText(input, DEFAULT_MCP_ACCESSIBILITY_CONFIG);
assert.ok(out.includes("Item 0"), "keeps head");
assert.ok(out.includes("Item 49"), "keeps tail");
assert.ok(out.includes('similar "button" items omitted'), "summarizes middle");
assert.ok(out.length < input.length, "compressed");
});
test("preserves [ref=eXX] anchors during truncation", () => {
const huge = ' - button "X" [ref=e123]\n' + "a".repeat(60000);
const out = smartFilterText(huge, DEFAULT_MCP_ACCESSIBILITY_CONFIG);
assert.ok(out.includes("[ref=e123]"), "preserves refs even on truncate");
assert.ok(out.length <= 50500, "respects maxTextChars + footer");
});
test('removes noise lines (- generic:, - text: "")', () => {
const input = [' - button "OK"', " - generic:", ' - text: ""', ' - link "Sign in"']
.join("\n")
.padEnd(3000, " ");
const out = smartFilterText(input, DEFAULT_MCP_ACCESSIBILITY_CONFIG);
assert.ok(!out.includes("- generic:"), "drops generic noise");
assert.ok(!out.match(/- text: ""/), "drops empty text noise");
assert.ok(out.includes("button"), "keeps signal");
});
test("returns unchanged when below minLengthToProcess", () => {
const small = "tiny";
const out = smartFilterText(small, DEFAULT_MCP_ACCESSIBILITY_CONFIG);
assert.equal(out, small);
});