Skip to content

Keep conversation history and handle stop_reason in the clients - #281

Merged
olaservo merged 4 commits into
mainfrom
feat/history-stop-reason
Nov 4, 2026
Merged

olaservo merged 4 commits into
mainfrom
feat/history-stop-reason

Conversation

@a-akimov

@a-akimov a-akimov commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Motivation and Context

Closes #274 and #275

All five clients now keep the message list for the whole session, tool_use and tool_result pairs included, so a follow-up like "and the alerts for that state?" has the earlier turn to work from. A new clear command starts a fresh conversation.

Every request sends the same short system prompt, and the loop checks stop_reason after each call:

  • max_tokens: print the partial text and a notice.
  • refusal: print a notice and drop the query from history, so a follow-up doesn't send it again.

A reply that was cut off, refused or stopped at the tool-turn limit is not added to history, so the next request stays valid. The loop now calls the API in one place instead of two.

The tool-loop test now types seven queries plus clear. It checks the system prompt and the history on every request, and adds a refusal, a max_tokens cut-off and a query after clear.

How Has This Been Tested?

Locally

Breaking Changes

N/a

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 Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Assisted by Claude.

Keep conversation history and handle stop_reason in the clients

All five clients now keep the message list for the whole session,
tool_use and tool_result pairs included, so a follow-up like "and the
alerts for that state?" has the earlier turn to work from. A new
`clear` command starts a fresh conversation.

Every request sends the same short system prompt, and the loop checks
stop_reason after each call:

- max_tokens: print the partial text and a notice.
- refusal: print a notice and drop the query from history, so a
  follow-up doesn't send it again.

A reply that was cut off, refused or stopped at the tool-turn limit is
not added to history, so the next request stays valid. The loop now
calls the API in one place instead of two.

The tool-loop test now types seven queries plus `clear`. It checks the
system prompt and the history on every request, and adds a refusal, a
max_tokens cut-off and a query after `clear`.
Copilot AI balanced review requested due to automatic review settings November 3, 2026 22:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Blank input can leave invalid messages in Python and TypeScript chat histories, breaking subsequent queries, while the cross-query test misses discarded tool-use/result pairs.

3 open issues
What changed in this PR

Updates all five MCP tutorial clients to retain conversation context and explain interrupted responses, addressing issues #274 and #275.

Changes:

  • Adds session history, including tool exchanges, and a clear command.
  • Sends a shared system prompt and handles refusal and max_tokens stop reasons.
  • Expands scripted client tests and documents the new behavior.
File Description
tests/​smoke-test.sh Updates comments describing the expanded scenarios.
tests/​README.md Documents history and stop-reason checks.
tests/​helpers/​tool-loop-test.ts Adds seven-query coverage for history, refusals, truncation and clearing.
README.md Describes session-wide conversation history.
mcp-client-typescript/​README.md Documents history, clearing and stop notices.
mcp-client-typescript/​index.ts Adds persistent history and stop-reason handling.
mcp-client-rust/​src/​main.rs Adds history, system prompts and response stop reasons.
mcp-client-rust/​README.md Documents the updated chat behavior.
mcp-client-ruby/​README.md Documents the updated chat behavior.
mcp-client-ruby/​client.rb Adds persistent history and consolidates API calls.
mcp-client-python/​README.md Documents the updated chat behavior.
mcp-client-python/​client.py Adds persistent history and stop-reason handling.
mcp-client-go/​README.md Documents the updated chat behavior.
mcp-client-go/​main.go Adds persistent history and consolidates API calls.

Review effort: Balanced

Comment thread mcp-client-python/client.py Resolved
Comment thread mcp-client-typescript/index.ts Resolved
Comment thread tests/helpers/tool-loop-test.ts Resolved
Copilot AI balanced review requested due to automatic review settings November 3, 2026 23:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Across all five clients, API failures leave rejected queries in history and empty completed responses are saved, causing subsequent requests to fail.

No open issues 🎉

2 previously missed issues

In code that hasn't changed since last review

Medium severity Roll back failed queries from session history

mcp-client-python/​client.py:120

An API error leaves the current query in session history, so a query rejected for exceeding the input limit is resent with every short follow-up and keeps failing. Restore the saved history length before propagating the error, preserving earlier conversation turns. Apply the same rollback in mcp-client-typescript/index.ts:148, mcp-client-go/main.go:258, mcp-client-rust/src/main.rs:262, and mcp-client-ruby/client.rb:130.

Medium severity Skip empty completed responses in conversation history

mcp-client-python/​client.py:134

An end_turn response can have an empty content list; saving it makes the next query replay an empty non-final assistant message, which the Messages API rejects. Skip appending empty completed responses while retaining the earlier tool-use/result pairs. Apply the same guard in mcp-client-typescript/index.ts:172, mcp-client-go/main.go:280, mcp-client-rust/src/main.rs:287, and mcp-client-ruby/client.rb:152.

3 resolved since last review

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 4, 2026 18:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The TypeScript client does not trim input before checking clear, so leading or trailing spaces send it to the model instead of resetting conversation history, unlike the other clients.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Trim input before checking the clear command

mcp-client-typescript/​index.ts:264

A trailing or leading space makes clear go to the model as a query instead of resetting the conversation. The other four clients trim input before checking commands (for example, mcp-client-python/client.py:189 and mcp-client-go/main.go:357), so the same input clears history there but retains it here. Trim the input for this comparison to keep the command's behavior consistent across clients.

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 4, 2026 18:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Human assessment is needed because clearing history before the next API request masks whether a truncated assistant response was incorrectly retained.

No open issues 🎉

Review effort: Balanced

@olaservo olaservo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Read through all five clients and the test. The loop is the same everywhere: the query goes into session history, each response is checked for refusal, max_tokens, a final answer and the tool-turn cap, and only complete replies and tool rounds are kept. A refusal or an API error rolls history back to where the query started, so a follow-up never resends a bad request. The new scenarios cover each path, and the history validator on every request is a good guard. CI is green with the new lint step. Merging.

Two small notes, neither blocking:

  • The test cannot confirm that a max_tokens reply is left out of history, because clear comes right after it. I checked the code path in each client and none of them appends it, so this is a coverage gap only.
  • The description says seven queries plus clear; the test now types nine. Worth a one-line edit for anyone reading the history later.

Since this changes code the build-client tutorial quotes, a matching docs PR per the new AGENTS.md would be good to link when it exists.


Generated by Claude Code

@olaservo
olaservo merged commit 2400cda into main Nov 4, 2026
10 checks passed
@olaservo
olaservo deleted the feat/history-stop-reason branch November 4, 2026 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a system prompt and stop_reason handling to the clients

3 participants