Repository navigation
fix(sequentialthinking): set readOnlyHint and idempotentHint to false - #5015
Conversation
Every sequentialthinking call appends to the server's in-memory thoughtHistory (and branches) and returns a growing thoughtHistoryLength, so advertising readOnlyHint and idempotentHint as true was wrong: a client may skip a confirmation, or retry a call, on the strength of those hints. Set both to false, explicitly, with a comment saying why. destructiveHint and openWorldHint stay false: the tool only appends, and touches nothing outside the process. The values stay explicit rather than being dropped to the spec defaults: AGENTS.md asks for the hints to be set on every tool, and an explicit false reads as a decision rather than an omission. Flip the tools-list pinning test to the corrected annotations and drop its KNOWN BUG #4721 marker. Add a patch changeset. Ported from #4722 (against main), rewritten by hand because the Prettier reformat on v2/main keeps it from applying cleanly. The regression assertion lives in the existing tools-list suite instead of input-schema.test.ts. Fixes #4721 Co-authored-by: Jack11111eee <Jack11111eee@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
🦋 Changeset detectedLatest commit: 7a24a11 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Comments and release notes inaccurately state that every call mutates branch state, and the test file’s purpose header remains stale.
Review effort: Balanced
Findings: 3
Open (3)
What changed in this PR
Corrects the sequentialthinking tool’s statefulness annotations and updates regression coverage.
Changes:
- Sets
readOnlyHintandidempotentHinttofalse. - Updates the protocol-level annotation assertion.
- Adds a patch changeset.
| File | Description |
|---|---|
src/sequentialthinking/index.ts |
Corrects tool annotations. |
src/sequentialthinking/__tests__/tools-list.test.ts |
Updates annotation regression coverage. |
.changeset/sequentialthinking-annotations.md |
Documents the published fix. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Copilot round 1 on #5015: the comments and the changeset said every call appends to the branch map, but only a call with both branchFromThought and branchId does. Separate the unconditional history append from the conditional branch append, and update the tools-list file header, which still described the #4721 annotations as a pinned known-wrong value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
|
Copilot round 1: 3 findings, all fixed in 7a24a11 (wording only, no behavior change).
No suppressed comments. |
|
Copilot round 2: clean (no findings; all 3 round-1 findings shown as resolved). Review loop stopped on the clean-round exit. |

Closes #4721
Description
The
sequentialthinkingtool advertisedreadOnlyHint: trueandidempotentHint: true. Both are wrong: every call appends to the server's in-memorythoughtHistory(andbranches) and returns a growingthoughtHistoryLength, so the tool is stateful and non-idempotent. A client that trusts the hints may skip a confirmation, or retry a call, and get a duplicated history.This PR sets both hints to
false, with a comment saying why.destructiveHintandopenWorldHintstayfalse: the tool only appends, and touches nothing outside the process.Explicit
false, not omitted. The spec defaults for a missingreadOnlyHintandidempotentHintare alreadyfalse, so dropping them would give clients the same value. They stay explicit anyway:AGENTS.mdasks for the hints to be set on every tool, and an explicitfalsereads as a decision rather than an omission. It is also what the corrected pinning test asserts withtoEqual.Part of Wave 4 (
sequentialthinking) of #5004. The pinning test in__tests__/tools-list.test.tsnow asserts the corrected annotations, and itsKNOWN BUG #4721marker is gone (git grep "KNOWN BUG #4721"returns nothing).Credit. Ported from #4722 by @Jack11111eee (against
main), the first of the four candidate PRs (#4722, #4747, #4749, #4784). They all make the same two-value change. #4722 was picked because it was first and adds the explanatory comment. A plain cherry-pick does not apply: the root Prettier reformat onv2/mainchangedindex.ts's indentation, and the existing tools-list suite already pins the annotations, so the regression assertion goes there rather than intoinput-schema.test.ts. The commit carries aCo-authored-by:trailer for the original author.Server Details
sequentialthinkingsequentialthinkingtool's annotations (index.ts), its pinning test (__tests__/tools-list.test.ts), and a patch changeset. The tool's schemas, behavior and results are unchanged.Motivation and Context
#4721. Tool annotations are hints that clients act on. Advertising a stateful tool as read-only and idempotent invites unsafe retries and confirmation skipping.
How Has This Been Tested?
Inspector CLI 2.9.0, legacy era (2025-11-25):
tools/list, readingtools[0].annotations.v2/main(before){"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}Inspector CLI, modern era (2026-07-28):
--protocol-era modernfails as expected, withVersion negotiation failed: the server did not offer pinned protocol version 2026-07-28 via server/discover (no fallback in pin mode)and exit 1. This server is still on SDK 1.x and does not yet speak 2026-07-28 (that is #4852). It is not a regression from this change.LLM client (Claude Code 2.1.289, headless,
--strict-mcp-configagainst the local build). This client has no era switch, so it negotiates the SDK 1.x server's legacy era. It was asked: "Call the sequentialthinking tool of the seq server twice with identical arguments: thought='probe', thoughtNumber=1, totalThoughts=1, nextThoughtNeeded=false. Then reply with exactly one line: the thoughtHistoryLength from each call, comma separated." It returned1,2. Two identical calls give different results, which shows the tool is not idempotent, as the new hints now say.Tests:
npx vitest run --coverageinsrc/sequentialthinking: 7 files, 99 tests pass. Per-file coverage:lib.ts100% lines and 94.44% branches, so the 90% gate holds.npm run local:gatepasses (exit 0).Breaking Changes
None. No client configuration changes. Clients that read the annotations will now treat the tool as non-read-only and non-idempotent, which is the correct behavior.
Types of changes
Checklist
npm run changeset) if this changes what a TypeScript server publishes🤖 Generated with Claude Code