Skip to content

fix(sequentialthinking): count header rows toward the log box's row cap - #5535

Merged
cliffhall merged 2 commits into
v2/mainfrom
v2/fix/5526-sequentialthinking-branchid-header-cap
Nov 12, 2026
Merged

cliffhall merged 2 commits into
v2/mainfrom
v2/fix/5526-sequentialthinking-branchid-header-cap

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Closes #5526

Description

The sequentialthinking server's stderr thought box capped the thought's rows at MAX_BOX_ROWS (1000, #5433), but not the header's. A long branchId wraps onto header rows of its own, so it could draw past the cap: a 200,000-character branchId drew 2,001 header rows for a one-line thought.

The header's rows now count toward MAX_BOX_ROWS. The thought gets whatever rows the header leaves. A header too long for every row is cut: it keeps MAX_BOX_ROWS - 1 rows, and its last row says how many characters it left out. Only the part of the header that can fit is wrapped, so a huge branchId also does no more work than the cap allows. This is the issue's preferred fix, "in the component's existing style"; the input schema is unchanged, so no client sending a long branchId is newly rejected.

Server Details

  • Server: sequentialthinking
  • Changed: the stderr thought box (formatThought in lib.ts), its README paragraph, and a patch changeset. Tools, schema and tool results are unchanged.

Motivation and Context

#5526: the header passthrough reproduced, through the header, the output amplification that #5433 closed for the body.

How Has This Been Tested?

Tests: thought-logging.test.ts adds a test that a wrapped header's rows count toward the cap, and one that a header longer than the cap is cut with the "characters not shown" row and a box of bounded size. The four existing cap tests now give the thought MAX_BOX_ROWS - 1 rows, because the one-row header counts. npm run local:gate exits 0. lib.ts coverage: 100% lines, statements and functions; 98% branches.

Inspector CLI (@modelcontextprotocol/inspector@2.12.0 --cli), in both eras (--protocol-era legacy and --protocol-era modern). Each run makes one tools/call to sequentialthinking with thought: "t", branchFromThought: 1 and a 200,000-character branchId. The table counts the │ rows of the stderr box:

Build Era Exit Box rows stderr bytes Rows that say what was cut Result
v2/main legacy 0 2002 223230 none thoughtHistoryLength: 1, one branch of 200,000 chars
v2/main modern 0 2002 223230 none same
this branch legacy 0 1001 112123 … 100136 more characters not shown, … 1 more line not shown same
this branch modern 0 1001 112123 same same

The 1,001 rows are the 1,000-row cap plus the thought's summary row. The tool result is identical before and after.

LLM client (Claude Code, claude -p, with the built server over stdio). Prompt: "Use the sequentialthinking tool to think in 3 steps about whether to cache a slow API. Make thought 3 a branch from thought 1 with branchId 'no-cache'. Then report, verbatim, the 'branches' and 'thoughtHistoryLength' fields from the last tool result." The model made three calls, with thought 3 branched as asked, and reported "branches":["no-cache"] and "thoughtHistoryLength":3. The change is to stderr only, which an LLM client does not see, so this run is a regression check that ordinary branch thoughts still work.

Breaking Changes

None. No client configuration changes. The stderr box of an ordinary thought now draws one fewer thought row before its summary, because the header row counts toward the 1000-row cap.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Protocol Documentation
  • My changes follow MCP security best practices
  • I have updated the server's README accordingly
  • I have added a changeset (npm run changeset) if this changes what a TypeScript server publishes
  • I have tested this with an LLM client
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling (not applicable: no new failure path; the box drawing is unchanged apart from the row cap)
  • I have documented all environment variables and configuration options (not applicable: none added)

Additional context

Out of scope, per the issue's chosen fix: the branchId is still echoed whole in each result's branches list and kept whole in the history (bounded in count by MAX_THOUGHT_HISTORY, #2912). Capping its length in the input schema would be an interface change for clients, and the issue offered it only as an alternative.

🤖 Generated with Claude Code

A long branchId wraps onto header rows that MAX_BOX_ROWS did not count,
so it could draw past the cap #5433 set for the thought's rows. The
header's rows now count toward the cap, and a header too long for it is
cut, its last row saying how many characters it left out.

Closes #5526

Signed-off-by: cliffhall <cliff@futurescale.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cliffhall cliffhall added the v2 label Nov 12, 2026
@changeset-bot

changeset-bot Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1fde546

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@modelcontextprotocol/server-sequential-thinking Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

sequentialthinking: MCP interface diff

Base: 9914133c0557 (Merge pull request #5532 from modelcontextprotocol/v2/docs/5524-everything-get-env-masking-wording)
Head: the build in this checkout, at fe22bef8b6aa (Merge 1fde546 into 1f8badb)

Diffed because it was named (--server).

✅ No interface changes detected.

Tools Prompts Resources Resource templates
Base 1 0 0 0
Head 1 0 0 0

Updated by this run for 1fde546c6d63a7867756c5a8ca9adc13df30d6af.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A critical issue remains: embedded newlines in branch IDs bypass the output row cap, requiring header newline handling and a regression test.

1 open issue
What changed in this PR

This PR addresses #5526 by including header rows in the sequentialthinking server’s stderr log-box budget, without changing tool inputs, results, or stored thoughts.

Changes:

  • Count wrapped header rows toward the shared 1,000-row cap.
  • Truncate oversized headers with an omitted-character summary.
  • Update regression tests, logging documentation, and patch release metadata.
File Description
src/​sequentialthinking/​README.md Documents the shared row cap and header truncation.
src/​sequentialthinking/​lib.ts Budgets header and thought rows together and truncates oversized headers.
src/​sequentialthinking/​__tests__/​thought-logging.test.ts Adjusts cap expectations and tests long headers.
.changeset/​sequentialthinking-header-row-cap.md Records the logging fix for a patch release.

Review effort: Balanced

Comment thread src/sequentialthinking/lib.ts Resolved
A line ending in a branchId started a line of stderr output that the
row cap never counted, so 2,000 newlines still drew about 2,000 lines.
The header now shows each as \r or \n, keeping it on rows the box draws
and MAX_BOX_ROWS counts. From Copilot's first review of #5535.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 1: one finding, fixed in 1fde546.

  • Embedded newlines in branch IDs bypass the row cap (high): valid, and a gap in what this PR adds. Line endings in a branchId are now escaped as \r/\n in the header, with a regression test. The README and the changeset say so too. The gate passes.

@github-actions

Copy link
Copy Markdown
Contributor

git: MCP interface diff

Base: 9914133c0557 (Merge pull request #5532 from modelcontextprotocol/v2/docs/5524-everything-get-env-masking-wording)
Head: the build in this checkout, at fe22bef8b6aa (Merge 1fde546 into 1f8badb)

Diffed because it was named (--server).

⚠️ Interface changes detected in 1 part(s) of the interface. Review them below to confirm they are intended.

Tools Prompts Resources Resource templates
Base 14 2 0 0
Head 14 2 0 0

tools

--- base/tools.json
+++ target/tools.json

- tools[git_remote].description: "Lists the repository's remotes with their fetch and push URLs, like `git remote -v`. Credentials emb..."
+ tools[git_remote].description: "Lists the repository's remotes with their fetch and push URLs, like `git remote -v`. Credentials emb..."

Updated by this run for 1fde546c6d63a7867756c5a8ca9adc13df30d6af.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues block approval of the stderr logging row-cap change.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 2: clean ("No open issues"; the round-1 finding is marked resolved). The review loop has converged.

@cliffhall
cliffhall merged commit 09eb689 into v2/main Nov 12, 2026
48 checks passed
@cliffhall
cliffhall deleted the v2/fix/5526-sequentialthinking-branchid-header-cap branch November 12, 2026 05:12
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.

sequentialthinking: long branchId values bypass the log-box row cap via uncounted header lines (same shape as the #5433 fix)

2 participants