Windows review findings: reserved device names (con, nul, COM1..) and
trailing dots in server names break or desync directory creation, so
sanitize_server now escapes reserved stems, strips trailing dots, and
caps length at 64 chars. Spill writes go through a temp file + rename
so a visible file is always complete (closes a cross-process partial
read race), and eviction protection compares content-hashed file names
instead of full paths. Also drops a duplicated cfg attribute.
Route CallToolResult content through the render.rs content policy per
plans/mcp-resources-prompts-design.md §6 (T8): oversized text sliced at
TEXT_MAX_BYTES_CLAMP with a self-explaining truncation note, image/audio/
embedded blob content spilled (or inlined when UTF-8-clean) instead of
shipping base64 into model context, and structuredContent subject to the
same ceiling. Clamp server-controlled uri/mime metadata strings to the new
METADATA_MAX_BYTES bound in both the read and tool-result paths, sanitize
the terminal rendering of MCP dispatch errors while keeping raw text in
the tool_call_error payload, and surface resource audience annotations in
both mcp_search results and mcp_read metadata via the catalog.
Implements plans/mcp-resources-prompts-design.md section 4.3 (T4):
mcp_read_<server> declaration and handler wired to render.rs, RFC 6570
Level-1-only URI template expansion, defensive ResourceContents parsing,
per-item text paging with pattern filtering, blob spill metadata, an
overall 204800-byte multi-content ceiling, dispatch wiring on both
eval chains, and a render_text paging-stall guard.
Single content-policy module for MCP resource and tool content, per
plans/mcp-resources-prompts-design.md §4.5 (T3):
- render_text: UTF-8-boundary-safe paging with clamped max_bytes and
grep-style fancy-regex line filtering (2 lines of context, 1-based
line-number prefixes, merged hunks); offsets walk the filtered stream.
- render_blob/render_blob_at: streaming base64 decode with a 50 MiB
ceiling, UTF-8 sniff, sha256-named 0600 spill files under a sanitized
server dir with a fixed mime->ext allowlist, and best-effort
oldest-first eviction bounding the spill tree at 512 MiB.
Not yet wired to call sites; module carries #![allow(dead_code)] until
the read/prompt surfaces land.