fix(mcp-chat): loop tool calls until a final answer#9472
Open
Ernstsen wants to merge 1 commit into
Open
Conversation
Contributor
Changed Packages
|
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR fixes multi-round tool-calling behavior in processQuery by iterating through tool calls until the model returns a final answer (or a max-iteration cap is reached), preventing later rounds from losing access to tool definitions.
Changes:
- Reworked
processQueryto loop across multiple LLM/tool-execution rounds with a max-iteration safeguard and a fallback reply. - Extracted tool execution + conversation-history updates into a helper (
executeAndRecordToolCall). - Added tests validating tools are passed on every round and that max-iteration fallback triggers.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| workspaces/mcp-chat/plugins/mcp-chat-backend/src/services/MCPClientServiceImpl.ts | Implements multi-round tool loop, iteration cap, and refactors tool execution/message recording. |
| workspaces/mcp-chat/plugins/mcp-chat-backend/src/services/MCPClientServiceImpl.test.ts | Adds regression tests for multi-round tool passing and max-iteration fallback behavior. |
| workspaces/mcp-chat/.changeset/fix-multi-round-tool-loop.md | Adds a patch changeset describing the multi-step tool-call fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
processQuery ran a single round of tool calls, then made a follow-up LLM request WITHOUT tools. When a question needed more than one tool call, the model attempted another call in that tool-less follow-up; OpenAI-compatible servers don't parse tool calls when `tools` is absent, so the model's raw tool-call tokens leaked into the response content (and the follow-up content discarded the real answer). Replace the single round with a capped agentic loop (max 8 iterations) that always passes the tools on every sendMessage call and executes returned tool calls until the model responds with a final text answer, with a graceful fallback if the cap is reached. The per-call execute/ record logic is extracted into a private helper. Adds tests covering the multi-round loop and the iteration cap. Signed-off-by: Johannes Sjølander Ernstsen <jsje@ramboll.com>
e035364 to
208ef52
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Enable multi-round tool calls for MCP-Chat plugin
Issue:
When an llm-request in the MCP-Chat required trailing tool-calls the response would look like this:
Cause:
processQuery ran a single round of tool calls, then made a follow-up LLM request WITHOUT tools. When a question needed more than one tool call, the model attempted another call in that tool-less follow-up; OpenAI-compatible servers don't parse tool calls when
toolsis absent, so the model's raw tool-call tokens leaked into the response content (and the follow-up content discarded the real answer).Replace the single round with a capped agentic loop (max 8 iterations) that always passes the tools on every sendMessage call and executes returned tool calls until the model responds with a final text answer, with a graceful fallback if the cap is reached.
The per-call execute/ record logic is extracted into a private helper, for readability.
Adds tests covering the multi-round loop and the iteration cap.
✔️ Checklist
Signed-off-byline in the message. (more info)