Recipes

MCP task input requests answered from the Studio agent chat

Discovered from README cross-links · today

Software & DevOpsWork & Operations

Suspend MCP tool calls that need input, then answer them in chat.

What they did

The author implemented a feature in the Mastra monorepo so that an MCP task which reports `input_required` can be answered directly from the agent chat in Studio, instead of failing the tool call. In `@mastra/mcp`, when a task-backed tool call reports `input_required`, no `inputRequests` handler is configured, and the call runs inside an agent or workflow, `MCPClient` now suspends the call with a payload of `{ type: 'mcp-input-request', serverName, toolName, taskId, createdAt, requests }` and leaves the remote task open. On resume, answers in `resumeData.inputResponses` are sent via `tasks/update` to the same task, and the client keeps waiting on it rather than re-issuing `tools/call`. If the server asks again, the call suspends again. A configured `inputRequests` handler still takes precedence, and without a suspend host the call fails with `MCP_CLIENT_TOOL_TASK_INPUT_UNHANDLED` as before. New errors were added: `MCP_CLIENT_TOOL_TASK_RESUME_INVALID` (validated before contacting the server) and `MCP_CLIENT_TOOL_TASK_CANCELLED`. In `@mastra/playground-ui`, a new card on the tool call shows each request: form requests render a form with Accept, Decline and Cancel, link requests show the link with Done and Cancel. Deferred background calls resume via `client.resumeBackgroundTask(id, { inputResponses }, { suspendedAt })`, while foreground calls use the existing chat resume. The card persists across reloads, closes if someone else answers first, and shows a newer request if the task asked again. The task chip shows `input required` while open. Studio only renders forms for the flat `requestedSchema` the MCP spec allows (primitive fields, at most 100, no `pattern`), so a server cannot force deep or huge schema conversion or custom regex. Core also records suspension payloads on deferred tool calls, clears them on resume or cancellation, supports stale-suspension checks, and can notify an agent when a deferred task finishes. A server route `POST /background-tasks/:backgroundTaskId/resume` and a JS client `resumeBackgroundTask()` were added.

How it went

The feature works end to end in a playground app with an MCP server and a client agent in Studio: running `approve_and_send` with message hello shows the approval card live while the chat stays usable, the request survives a reload, Accept completes the server task with the note, and the agent wakes to report the result. The MCP client suite runs against a real in-process `MCPServer` covering suspend/resume without a second `tools/call`, a second question round, handler precedence, already-ended tasks, invalid answers, and an agent-level background test. Playground UI has 13 MSW tests driving the real client-js and React Query stack, each change reverted once to confirm a failing test. Screenshots cover desktop, tablet and mobile.

Worth knowing

Direct `tool.execute()` calls without a suspend host still cannot handle input requests and fail as before. Studio only renders forms for the flat `requestedSchema` the MCP spec allows; other schemas show as unanswerable requests. The feature depends on the merged #28998 background-task resume work and is based on `main`. The post does not include setup steps for wiring an MCP server into a playground app, and the resume route is authenticated, so callers need credentials.

Wiring

Work spans two packages plus core and server. In `@mastra/mcp`, `MCPClient` detects `input_required` on task-backed tool calls, checks for a configured `inputRequests` handler, and if none exists and a suspend host is present (agent or workflow), returns a suspension payload instead of failing. Resume data flows through `resumeData.inputResponses` into `tasks/update` against the stored `taskId`, with validation raising `MCP_CLIENT_TOOL_TASK_RESUME_INVALID` before any network call. Core records suspension payloads on deferred tool calls, clears them on resume or cancellation, supports stale-suspension checks, and can notify an agent when a deferred task finishes. The server exposes an authenticated `POST /background-tasks/:backgroundTaskId/resume` route, and the JS client wraps it as `resumeBackgroundTask(id, { inputResponses }, { suspendedAt })`. In `@mastra/playground-ui`, the tool-call card reads the suspension payload, renders a form or link request, and on submit calls the background-task resume for deferred calls or the existing chat resume for foreground calls. The card subscribes to task state so it survives reloads, closes on external answers, and swaps to newer requests. Schema rendering is gated to flat primitive `requestedSchema` with at most 100 fields and no `pattern`. Tests run the MCP client suite against a real in-process `MCPServer` and the UI suite through MSW with client-js and React Query.

Try it yourself

In this repo, add support for answering MCP task input requests from the agent chat. In the MCP client package, when a task-backed tool call reports `input_required`, no `inputRequests` handler is configured, and the call runs in an agent or workflow, suspend the call with a payload `{ type: 'mcp-input-request', serverName, toolName, taskId, createdAt, requests }` and leave the remote task open. On resume, send `resumeData.inputResponses` via `tasks/update` to the same task and keep waiting; never re-issue `tools/call`. Suspend again if the server asks again. Keep a configured `inputRequests` handler as precedence, and keep failing with `MCP_CLIENT_TOOL_TASK_INPUT_UNHANDLED` when there is no suspend host. Add errors `MCP_CLIENT_TOOL_TASK_RESUME_INVALID` (validate before contacting the server) and `MCP_CLIENT_TOOL_TASK_CANCELLED`. In the playground UI, add a card on the tool call that shows each request: form requests render a form with Accept, Decline and Cancel; link requests show the link with Done and Cancel. Resume deferred calls with `client.resumeBackgroundTask(id, { inputResponses }, { suspendedAt })` and foreground calls with the existing chat resume. Keep the card visible across reloads, close it if someone else answers first, and show a newer request if the task asked again. Show `input required` on the task chip. Only render forms for the flat `requestedSchema` the MCP spec allows (primitive fields, at most 100, no `pattern`). Add tests against a real in-process `MCPServer` and MSW tests for the UI.

Source

Tools in this stack