Skip to content

Say when an MCP read leaves content out #380

Open

Description

@HMarzban

Summary

Four MCP read paths can leave content out. Three say nothing, and one gives the wrong next step. Each should say what it left out and what to do next.

Parent: #230 (moved from #328, which has no row for it). Split from #226. Build after #456 and #457. They edit the same tools, and #457 removes the structuredContent of find_documents, get_outline and list_chat_rooms.

Where

  • apps/hocuspocus.server/src/modules/mcp/tools/documentTools.ts: find_documents 176-198, get_outline 246-255, read_document 284-289.
  • apps/hocuspocus.server/src/modules/mcp/tools/chatTools.ts: list_chat_rooms 78-108.
  • apps/hocuspocus.server/src/modules/mcp/infra/documentStore.ts:31-53: listDocuments, unchanged.
  • apps/hocuspocus.server/src/modules/mcp/types.ts:12: MAX_READ_CHARS.
  • docs/mcp/reference.md:75 and :101.

What happens

Read from code and app docs, not measured.

  • get_outline returns every heading, and list_chat_rooms returns every chat room, with no cap. Connected apps cap a tool result near 150,000 characters, and a document can hold 5 MiB.
  • find_documents returns at most limit rows (default 20, max 50) and never says more exist. An owner with 60 documents who asks "list all my documents" gets 20 and treats them as all.
  • read_document with section_id that hits the cap says "read one section at a time with section_id", which the agent already did (documentTools.ts:287-289). The agent cannot read the rest, and it never sees the [n] numbers of the cut blocks, so edit_blocks cannot reach them.

Fix

Each note is docs.plus's own guidance, so it sits after the data frame, as read_document does today (documentTools.ts:286). Build every character count in a note from MAX_READ_CHARS, never from a literal.

  1. get_outline and list_chat_rooms: build the lines as today. Keep lines from the first one while size + line.length + 1 <= MAX_READ_CHARS.
    • This walk goes forward. Do not copy the read_chat_thread loop as it is (chatTools.ts:139-144): it walks from the end and keeps the newest lines.
    • For get_outline, the lines are renderOutline(outline), in tree order.
    • When lines are cut, add [Truncated: showing N of M headings.] for get_outline, or [Truncated: showing N of M rooms.] for list_chat_rooms.
  2. find_documents: call listDocuments with limit: limit + 1; infra/documentStore.ts stays a plain take. When the extra row comes back, drop it and add [More documents exist. Narrow with query, or raise limit up to 50.] When limit is already 50, the note is [More documents exist. Narrow with query.]
  3. read_document with section_id, when cut:
    • If max_chars was given and is below MAX_READ_CHARS: [Truncated: showing {cap} of {total} characters. Call again with a larger max_chars, up to {MAX_READ_CHARS}.]
    • Otherwise: [Truncated: showing {MAX_READ_CHARS} of {total} characters. This section alone is longer than {MAX_READ_CHARS}.]
    • A read without section_id keeps today's note.
  4. docs/mcp/reference.md: the §Limits "Read output" row (line 101) says the cap also applies to get_outline and list_chat_rooms. The find_documents paragraph (line 75) says a note tells the agent when more documents match.

No new test: each change is a short loop or a note string, and AGENTS.md §Test Policy makes tests opt-in.

Out of scope

  • A cursor or offset for find_documents. An owner with more than 50 documents still needs a query to reach the rest.
  • read_chat_thread: it already says when it cuts.
  • from_block for read_document (see Open decisions).

Acceptance criteria

  • get_outline keeps the first headings up to MAX_READ_CHARS, and ends with [Truncated: showing N of M headings.] after the data frame.
  • list_chat_rooms keeps the first rooms the same way, and ends with [Truncated: showing N of M rooms.] after the data frame.
  • With 21 or more documents and the default limit, find_documents returns 20 rows and the note, after the data frame. With 20 or fewer, it returns no note.
  • A cut section read gives the total. The max_chars note names a larger max_chars; the other note says the section alone is longer. Neither tells the agent to use section_id.
  • A short outline and a short list are unchanged.
  • docs/mcp/reference.md:75 and :101 describe the notes.

Verify

  • cd apps/hocuspocus.server && bun run typecheck && bun test.
  • Read the diff: the outline and room walk keeps the first lines, and each note sits after the data frame.

Open decisions

  • Add from_block to read_document so a long section can be read in parts? Recommended: no, not now. No real section near 100,000 characters has been reported.

Activity

  1. changed the title [-]Cap the get_outline and list_chat_rooms output[/-] [+]Say when an MCP read leaves content out[/+] on Oct 7, 2026
  2. HMarzban commented on Oct 7, 2026

    @HMarzban
    CollaboratorAuthor

    Maintainer ruling, 2026-10-07

    Open decision (from_block): no. docs.plus does not build a way to read long sections in parts, a search gateway, or embedding search for agents. Agents bring their own search and context tools. docs.plus only tells them, and through them the person, when something is very long.

    What to build instead, kept small and quiet:

    • No warning for ordinary content. A section under about 80,000 tokens gets no extra text beyond today's truncation note.
    • One warning for very long sections only. Estimate tokens as characters ÷ 4 (a common rule of thumb, not measured). When a section is over about 80,000 tokens (about 320,000 characters):
      • get_outline adds one short marker on that heading's line, for example (very long: about 95,000 tokens), so the agent knows before it reads.
      • The read_document truncation note for that section adds one sentence: "This section is very long. Tell the person that only the first part was read."
    • No other alert, banner or setting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions