Skip to content

DX-3022: mcp-toolkit: isError fails the task, keep failure details from the model - #62

Merged
CahidArda merged 3 commits into
mainfrom
DX-3022-mcp-toolkit-release-prep
Oct 8, 2026
Merged

CahidArda merged 3 commits into
mainfrom
DX-3022-mcp-toolkit-release-prep

Conversation

@CahidArda

@CahidArda CahidArda commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #38, before the first @upstash/mcp-toolkit release.

Tasks

  • A handler that returns { isError: true } now settles the task failed, with no retry. task_status returns the handler's content with isError, the same as the synchronous tool would. Before this, the task read completed and isError was dropped.
  • structuredContent now carries only error.code and error.message. error.data (the DLQ id, the failing response, Workflow's failResponse) stays in Redis and is no longer sent to the model.
  • A dispatch failure is now logged and answered with a generic tool error. Before, the transport's own error message was rethrown to the client.

Workflow dispatcher

  • task_cancel now actually cancels the run. Client.trigger turns the workflowRunId it's given into wfr_<id>, but Client.cancel takes the id as is. So we were triggering wfr_<uuid> and cancelling <uuid>, which matched no run, and the failure was silent. The task record read cancelled, but every remaining step and side effect still ran. Cancel now sends wfr_<taskId>. The new test uses the real @upstash/workflow client with only fetch stubbed, and fails on the old code.
  • failureFunction now logs failResponse (the message of the error the handler threw) instead of storing it in error.data, so it never reaches Redis or the task's owner. Checked end to end: before this change, failed Workflow tasks in Redis held the exception text; after it, they hold only the run id.

README now matches the docs and the blog: lib/auth.ts holds verifyToken next to principal, the route uses mcp-handler's withMcpAuth, and QSTASH_URL is listed.

Changeset: the unreleased mcp-toolkit-initial entry now mentions the failure behavior, so the "Version Packages" PR (#61) picks it up.

Checked: lint, typecheck (mcp-toolkit and the demo), 134/134 tests including the Redis-backed suites, and the demo's e2e run on both the QStash and Workflow servers.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Gtucop3k8CNVLjgvoDTC8n

…om the model; README matches the docs

- A handler returning { isError: true } now settles the task failed (no retry); task_status returns its content with isError, as the synchronous tool would
- structuredContent carries only error.code and error.message: error.data (DLQ id, the failing response, Workflow's failResponse) stays in the store
- A dispatch failure is logged and answered with a generic tool error instead of rethrowing the transport's message
- README: lib/auth.ts with verifyToken + principal, mcp-handler withMcpAuth route, QSTASH_URL; changeset mentions the failure behavior

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gtucop3k8CNVLjgvoDTC8n
@linear-code

linear-code Bot commented Oct 8, 2026

Copy link
Copy Markdown

DX-3022

CahidArda and others added 2 commits October 8, 2026 22:18
…re what the handler threw

- Client.trigger turns workflowRunId into wfr_<id> but Client.cancel takes the id as is, so cancel sent the bare task id and matched no run: the run kept executing. Cancel now sends runIdOf(taskId).
- Tested against the real @upstash/workflow client with fetch stubbed (fails on the old code), not a stubbed client.
- failureFunction logs failResponse (the thrown error's message) instead of storing it in error.data, so it reaches neither Redis nor, via task_status, the task's owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gtucop3k8CNVLjgvoDTC8n
@CahidArda
CahidArda merged commit c7aeab8 into main Oct 8, 2026
1 check passed
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.

1 participant