Repository navigation
Conversation
withPersistence hands generic interrupt resolutions to the chat engine through resumeToolState. The engine copied them into resumeGenericInterrupts, but only the ephemeral path mapped mcp_input_<toolCallId> answers into resumeInputResponses. A durable resume therefore ran the pending server tool with ctx.inputResponse undefined, so the tool asked for input again. Apply the mapping in applyResumeToolState, which both resume paths use, and run it after the restored history and tools exist. The ephemeral path keeps the same behaviour and the old duplicate mapping goes away. Fixes TanStack#1885
🦋 Changeset detectedLatest commit: 27858f9 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 |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Match durable responses to pending tools packages/ai/src/activities/chat/index.ts, docs/persistence/chat-persistence-advanced.md, .changeset/durable-mcp-input-response.md |
Matching MCP input resolutions are stored by tool-call ID for pending executable server tools. The docs and changeset describe this response behavior during persistence resume. |
Test persistence resume cases packages/ai-persistence/tests/interrupts.test.ts |
Tests cover resolved and cancelled responses for one or multiple pending calls. They also check that an unknown tool-call response does not invoke the adapter or clear the pending interrupt. |
Exercise the resume flow through the E2E API testing/e2e/src/routes/api.durable-mcp-input.ts, testing/e2e/src/routeTree.gen.ts, testing/e2e/tests/durable-mcp-input.spec.ts |
A new route accepts resolved or cancelled responses and returns run events and persistence state. The E2E test checks that both response types complete the run and clear pending interrupts. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix · Severity of issue fixed: Medium
Suggested reviewers: alemtuzlak
Merge Risk: 
Minimal · up to 27858
-
The durable MCP resume behavior has no established merge-blocking issue.
Pre-merge checks: 
5
-
Full details: Description check
-
Explanation
The description is complete and directly aligned with the changes. It explains the root cause, fix, alternatives, testing, linked issue, risk, rollback, and release impact. It also records that the full test suite and some E2E tests were not run.
Full details: Linked Issues check
-
Explanation
Issue
#1885requires durablewithPersistenceresume withmessages: []to pass the matching MCP input answer to the pending server tool.applyResumeToolStatenow mapsmcp_input_<toolCallId>resolutions toresumeInputResponsesafter middleware restores messages and tools. Persistence tests cover resolved, cancelled, multiple pending tools, and unknown tool-call IDs. The E2E test covers the durable HTTP flow.
Full details: Out of Scope Changes check
-
Explanation
The changes stay within issue
#1885. The source change fixes resume-state mapping. The persistence and E2E tests verify the regression. The documentation and changeset describe the published behavior. The generated route changes support the new E2E route. No unrelated product change is demonstrated.
-
Finishing Touches
-
🧪 Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
|
Thanks for the PR, @TPSJASPER! 🙌 @tombeckenham will take a look. Automated pre-review checks
Automated triage — a human review follows. |
Durable MCP answers arrive later from DURABLE_RESUME, after messages and tools exist. Moving this call did not deliver them.
|
View your CI Pipeline Execution ↗ for commit 27858f9
☁️ Nx Cloud last updated this comment at |
@tanstack/ai
@tanstack/ai-acp
@tanstack/ai-angular
@tanstack/ai-anthropic
@tanstack/ai-bedrock
@tanstack/ai-byteplus
@tanstack/ai-claude-code
@tanstack/ai-client
@tanstack/ai-cloudflare
@tanstack/ai-code-mode
@tanstack/ai-code-mode-snippets
@tanstack/ai-codex
@tanstack/ai-cohere
@tanstack/ai-compaction
@tanstack/ai-computer-local
@tanstack/ai-computer-playwright
@tanstack/ai-computer-sandbox
@tanstack/ai-computer-use
@tanstack/ai-devtools-core
@tanstack/ai-durable-stream
@tanstack/ai-elevenlabs
@tanstack/ai-event-client
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-grok-build
@tanstack/ai-groq
@tanstack/ai-harness
@tanstack/ai-harness-cli
@tanstack/ai-isolate-cloudflare
@tanstack/ai-isolate-daytona
@tanstack/ai-isolate-e2b
@tanstack/ai-isolate-node
@tanstack/ai-isolate-quickjs
@tanstack/ai-isolate-quickjs-bun
@tanstack/ai-llmgateway
@tanstack/ai-lovable
@tanstack/ai-mcp
@tanstack/ai-memory
@tanstack/ai-mistral
@tanstack/ai-neon
@tanstack/ai-octane
@tanstack/ai-ollama
@tanstack/ai-ollaya
@tanstack/ai-openai
@tanstack/ai-opencode
@tanstack/ai-openrouter
@tanstack/ai-perplexity
@tanstack/ai-persistence
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-reactor
@tanstack/ai-remix
@tanstack/ai-sandbox
@tanstack/ai-sandbox-blaxel
@tanstack/ai-sandbox-boxd
@tanstack/ai-sandbox-cloudflare
@tanstack/ai-sandbox-daytona
@tanstack/ai-sandbox-docker
@tanstack/ai-sandbox-e2b
@tanstack/ai-sandbox-local-process
@tanstack/ai-sandbox-railway
@tanstack/ai-sandbox-sprites
@tanstack/ai-sandbox-upstash-box
@tanstack/ai-sandbox-vercel
@tanstack/ai-skills
@tanstack/ai-solid
@tanstack/ai-svelte
@tanstack/ai-typesafe
@tanstack/ai-utils
@tanstack/ai-vercel-gateway
@tanstack/ai-vertex
@tanstack/ai-vue
@tanstack/ai-worldlabs
@tanstack/openai-base
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
@tanstack/svelte-ai-devtools
commit: |
Run the durable MCP cases in the persistence interrupt tests. A durable resume with an empty message list left
ctx.inputResponseundefined on the pending server tool. The tool asked for MCP input again. This PR maps the storedmcp_input_<toolCallId>answer onto that tool before it runs again.🎯 Changes
A pending server tool now receives a resolved or cancelled MCP input answer when chat resumes through persistence with
messages: []. The mapping lives inapplyResumeToolState, which both the ephemeral path and the durable path call. Docs and a patch changeset are included.✅ Checklist
pnpm run test:pr, or these tests do not apply to this pull request.docs/for this change, or this change is not user-facing.pnpm changeset), or this PR does not change a published package.🚀 Release Impact
Root cause
Issue. A pending server tool that waits for MCP input gets no answer on a durable resume with an empty message list.
Cause. Persistence applies those answers in the
DURABLE_RESUMEcallback. The old mapping lived only inapplyEphemeralInterruptResume.applyResumeToolStatecopiedgenericInterruptsand did not mapmcp_input_<toolCallId>ontoresumeInputResponses.Fix.
applyResumeToolStatemaps each pending server tool resolution intoresumeInputResponses. The durable callback calls that function after messages and tools exist. The call insideapplyMiddlewareConfigstays at the start of that function.Possible alternatives
run(). Add the same loop after the durable callback. This duplicates the ephemeral rule.inputResponsesonChatResumeToolState. This widens the middleware contract.applyMiddlewareConfig. CallapplyResumeToolStateafter the message assignment. That config object does not carrygenericInterrupts.Testing
Commands run.
packages/aitests/mcp-input-interrupt.test.ts: 14 passed.packages/ai-persistencetests/interrupts.test.ts: 35 passed.pnpm test:prwas not run.testing/e2e/tests/durable-mcp-input.spec.tswas not run.Manual test.
mcp_input_<toolCallId>. Resume throughwithPersistencewithmessages: []and a resolved payload. The tool runs again withctx.inputResponseundefined and pauses a second time.packages/ai-persistence/tests/interrupts.test.ts.{ status: 'resolved', payload }.{ status: 'cancelled' }.How this PR makes testing easy.
packages/ai-persistence/tests/interrupts.test.tscovers resolved, cancelled, and an unknown tool-call id.testing/e2e/tests/durable-mcp-input.spec.tscovers the same two statuses over HTTP.Linked issues
Fixes #1885
Risk / rollback
Risk is low. The ephemeral MCP input path still has its own tests. Revert this PR to restore the old behavior.
Public API change
The tool callback is unchanged. The value of
ctx.inputResponseon a durable resume changes.Before
On a durable resume with
messages: [],ctx.inputResponsewasundefined.After
The same resume sets
ctx.inputResponseto{ status: 'resolved', payload }or{ status: 'cancelled' }.Summary by CodeRabbit