Skip to content

v2: add optimistic UI while MCP prompt commands resolve #34860

Open

Description

@rekram1-node

Problem

V2 slash commands backed by MCP prompts can take noticeable time to resolve because command submission must call the MCP server's prompt resolution path before the prompt is admitted to the session.

Today there is no optimistic/pending UI for that resolution phase. If an MCP prompt command is slow, the submit experience can look like the prompt input is stalled or in an odd intermediate state before the resolved prompt appears in the session.

Expected behavior

After submitting an MCP prompt slash command, the TUI should immediately show an explicit pending/submitted command state while the server resolves the MCP prompt. The user should not be left looking at an apparently idle or stuck prompt input during that wait.

Reproduction aid

Temporarily add an artificial delay before MCP prompt evaluation:

diff --git a/packages/core/src/command.ts b/packages/core/src/command.ts
@@
-import { Context, Effect, Layer, Schema, Types } from "effect"
+import { Context, Duration, Effect, Layer, Schema, Types } from "effect"
@@
         const prompt = (yield* mcp.prompts()).find((prompt) => mcpCommandName(prompt.server, prompt.name) === input.name)
         if (!prompt) return yield* new NotFoundError({ command: input.name })
+        yield* Effect.sleep(Duration.seconds(3))
         const result = yield* mcp

Then submit an MCP prompt slash command with arguments. The UI should make the command resolution phase obvious and non-confusing during the artificial delay.

Activity

  1. zanemo109 commented on Aug 2, 2026

    @zanemo109

    I’d like to take this on. I’ve traced the current TUI submission path and plan to add a small pending/resolving state that appears immediately after an MCP-backed slash command is submitted, clears when resolution completes or fails, and is covered by a delayed-resolution regression test. Does that interaction and scope match what you had in mind?

  2. opencodejr commented on Oct 29, 2026

    @opencodejr
    Contributor

    Thanks for the detailed report. I reproduced this on v2.2.0 and on the latest dev (f43563b).

    Setup: a small local stdio MCP server, slow, offering one prompt, greet. It waits 4 seconds before answering a prompt request, which stands in for the artificial delay in the issue.

    Steps: submit /slow:greet world in the TUI.

    Result: the TUI switches to a blank "New session" view with an empty composer for the whole 4 seconds. There's no user row and no busy or interrupt indicator. Then the resolved prompt ("Say hello to world") appears and the model replies. The recording and screenshots below show this, and the script below reproduces it with opencode-drive.

    Likely cause: in packages/tui/src/component/prompt/index.tsx, the slash-command branch of submit sends session.command without calling move.startSubmit() and without the optimistic admission that plain prompts get via data.session.prompt. So nothing is rendered until the server has resolved the MCP prompt and admitted the result.

    Screenshot

    Screenshot

    Reproduction script (drive.ts)
    // Repro for #34860: no pending UI while an MCP prompt slash command resolves.
    // A local stdio MCP server (slow-mcp.mjs, next to this script) delays prompts/get by 4s.
    import { Effect, Stream } from "effect"
    import { Llm, OpenCodeDriver } from "opencode-drive"
    
    const server = `
    import { createInterface } from "node:readline"
    const send = (m) => process.stdout.write(JSON.stringify(m) + "\\n")
    createInterface({ input: process.stdin }).on("line", (line) => {
      if (!line.trim()) return
      const msg = JSON.parse(line)
      if (msg.id === undefined) return
      const ok = (result) => send({ jsonrpc: "2.0", id: msg.id, result })
      if (msg.method === "initialize") return ok({ protocolVersion: msg.params?.protocolVersion ?? "2025-06-18", capabilities: { prompts: {} }, serverInfo: { name: "slow", version: "1.0.0" } })
      if (msg.method === "prompts/list") return ok({ prompts: [{ name: "greet", description: "Slow greeting prompt", arguments: [{ name: "who", required: true }] }] })
      if (msg.method === "prompts/get") return setTimeout(() => ok({ messages: [{ role: "user", content: { type: "text", text: "Say hello to " + msg.params?.arguments?.who } }] }), 4000)
      send({ jsonrpc: "2.0", id: msg.id, error: { code: -32601, message: "Method not found" } })
    })
    `
    
    export default OpenCodeDriver.use(
      {
        ...(process.env.OC_DEV ? { opencode: { dev: process.env.OC_DEV } } : {}),
        tui: { recording: true, keypressOverlay: true },
        project: { git: true, files: { "slow-mcp.mjs": server } },
        config: { mcp: { servers: { slow: { type: "local", command: ["bun", "slow-mcp.mjs"] } } } } as any,
      },
      ({ ui, llm }) =>
        Effect.gen(function* () {
          yield* llm.serve(() => Stream.make(Llm.text("Hello, world!")))
          yield* Effect.sleep(3000) // let the MCP server connect
          yield* ui.type("/slow:greet world")
          yield* Effect.sleep(500)
          yield* ui.screenshot("01-typed")
          yield* ui.enter()
          yield* Effect.sleep(300)
          yield* ui.screenshot("02-after-submit-300ms")
          yield* Effect.sleep(1500)
          yield* ui.screenshot("03-after-submit-1800ms")
          yield* Effect.sleep(1500)
          yield* ui.screenshot("04-after-submit-3300ms")
          yield* ui.waitFor("Say hello to world")
          yield* ui.waitFor("Hello, world!")
          yield* ui.screenshot("05-resolved")
        }),
    )

    Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions