Skip to content

fix(agent): page postsListTool results - #2109

Open
giladresisi wants to merge 2 commits into
mainfrom
fix/agent-posts-list-cap
Open

giladresisi wants to merge 2 commits into
mainfrom
fix/agent-posts-list-cap

Conversation

@giladresisi

@giladresisi giladresisi commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

What kind of change does this PR introduce?

Bug fix (backend, agent / MCP tool postsListTool in libraries/nestjs-libraries/src/chat/tools/posts.list.tool.ts). The tool now returns its results 50 posts per page instead of the whole window at once: it sorts the posts by publishDate then id, slices the requested page, and adds an optional page input (non-negative integer, default 0) plus additive total, page and hasMore outputs. The tool description and the agent prompt line in load.tools.service.ts no longer tell the model to "pass a wide window"; they explain paging and prefer narrow windows. The existing inputs (startDate, endDate, customer) and every existing item field are unchanged, a call without page returns the first page, and PostsService.getPosts / PostsRepository.getPosts are untouched (the calendar uses them).

Why was this change needed?

A Cloud customer with many channels and roughly ten thousand posts reported that the AI agent "is constantly crashing". Sentry shows the agent chat (/agents/new) failing with context_length_exceeded and AI_APICallError: Invalid 'input[12].output': string too long. Expected a string with maximum length 10485760, but got a string with length 101152578, i.e. a single tool result of about 101 MB against the model provider's 10 MB per-output limit. The Sentry event does not name the tool; postsListTool is the suspected source because it is the only tool whose output grows with the organization's data: it returned every post in the window with full content and settings, uncapped, and both its description and the agent prompt told the model to pass a wide window (for example a year ahead). getPosts also expands every recurring post into one item per occurrence inside the window, so a wide window multiplies the row count.

Measured on production data (5.7k posts sample): a post is small (content avg 264 chars, p99 1.5 KB, max 2.6 KB; settings max 2.8 KB), so the size comes from the number of rows, not from heavy rows. That is why the fix is a row cap per response and no per-item truncation. A full page of 50 is about 25 KB.

Other information:

  • Deliberate non-change: the slice happens in the tool, so the whole window is still loaded from the DB as before. Pushing pagination into the repository would change a query the calendar shares and was left out of this fix.
  • getPosts has no orderBy, so the tool sorts before slicing to keep pages stable (posts sharing a publish date are ordered by id).
  • The tool is exposed both to the in-app agent and to external MCP clients. External clients never see the in-app prompt, so the paging instructions live in the tool description as well.
  • PR feat(posts): opt-in attachments on the posts list (API + agent tool) #2012 touches the same file (adds includeAttachments); whichever merges second needs a small conflict resolution.
  • Docs follow-up: docs(mcp): postsListTool paging postiz-docs#250 (to be merged after this deploys).

Testing done (local stack, real MCP endpoint, 120 draft posts seeded in one window, pairs of posts sharing the same publish date):

  • MCP tools/call: pages 0 / 1 / 2 returned 50 / 50 / 20 posts, total 120 on every page, hasMore true, true, false; page 3 returned an empty list with hasMore false. The 120 ids across the pages had no gaps or duplicates and were in order.
  • A call without page returned exactly page 0 with all previous item fields. page: -1 and page: 1.5 are rejected by validation.
  • In-app agent (through ask_postiz): asked for the total and the last post of the window, it paged on its own and answered correctly (120, last post content and date).
  • claude.ai custom connector (external MCP client, tool descriptions only): made three postsListTool calls (pages 0, 1, 2) and reported 120 posts with the correct first and last post.

QA

  1. Use an organization with more than 100 posts inside one date window (drafts are enough) and connect an MCP client to the local MCP endpoint, or use the in-app agent
  2. Call postsListTool with that window and no page: the result has 50 posts ordered by publish date, total equals the number of posts in the window, page is 0 and hasMore is true; every item still has id, publishDate, state, content, settings, group, integrationId, platform, integrationName
  3. Call again with the same dates and page 1, 2, ...: each page continues where the previous ended with no repeated or missing ids, and the last page has hasMore false
  4. Call with a page past the end: empty posts, same total, hasMore false. Call with page -1: a validation error, no crash
  5. In the in-app agent ask "how many posts do I have between and , and what is the last one?": the agent pages through the results and answers with the right total and post, without an error
  6. Open the calendar for the same window: it shows all the posts as before (the calendar does not use the tool and is not paged)

Checklist:

Put a "X" in the boxes below to indicate you have followed the checklist;

  • I have read the CONTRIBUTING guide.
  • I have signed the Contributor License Agreement (CLA) (ICLA for individuals, CCLA for entities).
  • I confirm I have not used AI to submit this PR or generate code for it.
  • I checked that there were no similar issues or PRs already open for this.
  • This PR fixes just ONE issue
  • I have filled in the QA section above with real steps to verify this change.

🤖 Generated with Claude Code

postsListTool returned every post in the window with full content and
settings, and its description told the model to pass a wide window. On a
large organization a single tool result grew past the model provider's
10 MB per-output limit and the agent chat failed with
context_length_exceeded / "string too long".

Results are now sorted by publish date then id and returned 50 per page,
with an optional "page" input and additive "total", "page" and "hasMore"
outputs. The description and the agent prompt explain paging and prefer
narrow windows. getPosts is unchanged, the slice happens in the tool.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@strix-security

strix-security Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Strix Security Review

No security issues found.

Updated for 26c58fd.


Reviewed by Strix
Re-run review · Configure security review settings

@postiz-contribution postiz-contribution Bot added the contribution:approved Approved contributor label Sep 21, 2026
@postiz-agent

postiz-agent Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

The paging hint said to repeat only the dates, which would drop a
"customer" filter on the following pages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant