Add aggressive content limiting to prevent MCP 25k token errors

- Implement smart content truncation at ~80k chars (~20k tokens)
- Preserve document structure when truncating (stop at natural breaks)
- Add clear truncation notices with guidance for smaller ranges
- Update chunking suggestions to use safer 8-page chunks
- Enhance recommendations to suggest 3-8 page ranges
- Prevent 29,869 > 25,000 token errors while maintaining usability
This commit is contained in:
Ryan Malloy 2025-08-21 02:50:04 -06:00
parent 9c2f299d49
commit 3dffce6904

View File

@ -375,10 +375,27 @@ async def convert_to_markdown(
if "table_of_contents" in markdown_result:
result["table_of_contents"] = markdown_result["table_of_contents"]
else:
# Include full content for smaller documents or page ranges
result["markdown"] = markdown_result["content"]
result["metadata"]["character_count"] = len(markdown_result["content"])
result["metadata"]["word_count"] = len(markdown_result["content"].split())
# Include content with automatic size limiting to prevent MCP errors
content = markdown_result["content"]
# Apply aggressive content limiting to stay under 25k token limit
# Rough estimate: ~4 chars per token, leave buffer for metadata
max_content_chars = 80000 # ~20k tokens worth of content
if len(content) > max_content_chars:
# Truncate but try to preserve structure
truncated_content = _smart_truncate_content(content, max_content_chars)
result["markdown"] = truncated_content
result["content_truncated"] = True
result["original_length"] = len(content)
result["truncated_length"] = len(truncated_content)
result["truncation_note"] = f"Content truncated to stay under MCP 25k token limit. Original: {len(content):,} chars, Shown: {len(truncated_content):,} chars. Use smaller page ranges for full content."
else:
result["markdown"] = content
result["content_truncated"] = False
result["metadata"]["character_count"] = len(content)
result["metadata"]["word_count"] = len(content.split())
# Add image info
if include_images and markdown_result.get("images"):
@ -1522,6 +1539,49 @@ def _extract_markdown_structure(content: str) -> dict[str, Any]:
return structure
def _smart_truncate_content(content: str, max_chars: int) -> str:
"""Intelligently truncate content while preserving structure and readability."""
if len(content) <= max_chars:
return content
lines = content.split('\n')
truncated_lines = []
current_length = 0
# Try to preserve structure by stopping at a natural break point
for line in lines:
line_length = len(line) + 1 # +1 for newline
# If adding this line would exceed limit
if current_length + line_length > max_chars:
# Try to find a good stopping point
if truncated_lines:
# Check if we're in the middle of a section
last_lines = '\n'.join(truncated_lines[-3:]) if len(truncated_lines) >= 3 else '\n'.join(truncated_lines)
# If we stopped mid-paragraph, remove incomplete paragraph
if not (line.strip() == '' or line.startswith('#') or line.startswith('|')):
# Remove lines until we hit a natural break
while truncated_lines and not (
truncated_lines[-1].strip() == '' or
truncated_lines[-1].startswith('#') or
truncated_lines[-1].startswith('|') or
truncated_lines[-1].startswith('-') or
truncated_lines[-1].startswith('*')
):
truncated_lines.pop()
break
truncated_lines.append(line)
current_length += line_length
# Add truncation notice
result = '\n'.join(truncated_lines)
result += f"\n\n---\n**[CONTENT TRUNCATED]**\nShowing {len(result):,} of {len(content):,} characters.\nUse smaller page ranges (e.g., 3-5 pages) for full content without truncation.\n---"
return result
def _estimate_section_length(heading_level: int) -> int:
"""Estimate how many pages a section might span based on heading level."""
# Higher level headings (H1) tend to have longer sections
@ -1598,7 +1658,8 @@ def _generate_chunking_suggestions(sections: list) -> list[dict[str, Any]]:
section_pages = section["estimated_end_page"] - section["start_page"] + 1
# If adding this section would make chunk too large, finalize current chunk
if current_chunk_pages + section_pages > 15 and chunk_sections:
# Use smaller chunks (8 pages) to prevent MCP token limit issues
if current_chunk_pages + section_pages > 8 and chunk_sections:
suggestions.append({
"chunk_number": len(suggestions) + 1,
"page_range": f"{chunk_start}-{chunk_sections[-1]['estimated_end_page']}",
@ -1761,13 +1822,15 @@ def _get_processing_recommendation(
"Consider using recommended workflow for better performance."
)
recommendation["suggested_workflow"] = [
"1. First: Call with summary_only=true to get document overview",
"2. Then: Use page_range to process specific sections (e.g., '1-10', '20-30')",
"3. Alternative: Process in chunks of 10-15 pages to avoid response limits"
"1. First: Call with summary_only=true to get document overview and TOC",
"2. Then: Use page_range to process specific sections (e.g., '1-5', '6-10', '15-20')",
"3. Recommended: Use 3-8 page chunks to stay under 25k token MCP limit",
"4. The tool auto-truncates if content is too large, but smaller ranges work better"
]
recommendation["warnings"] = [
"Full document processing may hit 25k token response limit",
"Large responses may be slow and consume significant resources"
"Page ranges >8 pages may hit 25k token response limit and get truncated",
"Use smaller page ranges (3-5 pages) for dense content documents",
"Auto-truncation preserves structure but loses content completeness"
]
# Medium document recommendations