Skip to content

[FEATURE]: MCP elicitation support #23066

Description

@ojsef39

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Enable human-in-the-loop workflows for MCP tools by supporting the elicitation protocol. This allows MCP servers to request user input via forms during tool execution.

#8251
#21231
#11948
#8243
#14968

Activity

  1. added
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    on Apr 17, 2026
  2. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Apr 17, 2026
  3. removed
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  4. romanilyin commented on May 31, 2026

    @romanilyin

    Adding a concrete use case here because this looks like the main OpenCode tracking issue for MCP elicitation support. I do not want to create duplicate issues; if maintainers prefer a separate issue for this specific case, I can open one and link it back here.

    Important context: the MCP server below, Unity MCP Gateway, is not a published public tool yet. It is currently an internal/private tool in my workflow. I am not asking maintainers to run or reproduce it directly. I am sharing the implementation shape because it exercises a specific MCP client capability: server-initiated elicitation/create nested inside an already-running tools/call.

    The Gateway uses CanonicalPath, a cross-language, cross-platform path canonicalization library, as the basis for consistent path identity and scoped filesystem safety across OSes/languages. That detail matters here because the approval prompt should be shown only after the server has canonicalized paths, evaluated policy, and computed the exact command scope/hash.

    What I need

    I need OpenCode to support MCP elicitation/create as a server-to-client request during tools/call, so an MCP server can ask the human user to approve one resolved command after the server has validated the request.

    The approval I need is not a broad pre-call permission for a wrapper MCP tool. It is a post-validation, one-shot approval for the concrete command resolved by the server.

    Conceptually:

    model asks to run a Unity action
    → OpenCode calls an MCP wrapper tool
    → Gateway parses args, canonicalizes paths, evaluates policy/risk/scope/hash
    → Gateway asks the human user to approve exactly this resolved command once
    → user accepts/declines/cancels in OpenCode UI
    → Gateway either continues once or returns its safe fallback
    

    Why pre-call approval is not enough

    My internal Gateway exposes a small stable MCP tool surface such as:

    sgg_unity_call(project, command, args_json, mode)
    

    The potentially sensitive operation is not the wrapper tool name. It is inside the arguments:

    project = "..."
    command = "test.run" | "asset.write" | "package.change" | ...
    args_json = "..."
    mode = "wait" | ...
    

    A pre-call prompt like this asks the wrong question:

    Allow MCP tool `sgg_unity_call`?
    

    That is too early and too coarse. At that point the Gateway has not yet:

    • parsed the command-specific arguments;
    • resolved the Unity project;
    • canonicalized path inputs through CanonicalPath/CanonicalFs;
    • rejected traversal, absolute path, symlink/reparse, or scope escape attempts;
    • evaluated command policy as allow, ask, or deny;
    • computed the canonical scope summary;
    • computed the args_hash tied to this exact request;
    • created a one-shot pending approval.

    The user should instead see something like:

    Approve this resolved Unity Gateway command once?
    
    MCP server: sgg-unity-mcp-gateway
    Tool: sgg_unity_call
    Command: test.run
    Risk: ask
    Approval: one-shot
    Canonical scope: <scope summary>
    Args hash: <args_hash>
    Expires: <timestamp>
    

    This is why the approval needs to happen inside the command invocation, after server-side validation and policy evaluation, not before the wrapper tool is called.

    Current internal Gateway behavior

    This is the current implementation shape, not a public reproduction package.

    When a command is policy-gated as ask, the Gateway returns a structured fallback result:

    {
      "status": "error",
      "error": {
        "code": "confirmation_required"
      },
      "next_valid_actions": [
        "ask_user_for_confirmation",
        "choose_safer_command"
      ],
      "details": {
        "approval_request": {
          "project": "<project>",
          "command": "<command>",
          "risk": "ask",
          "args_hash": "<hash>",
          "scope": "<canonical scope summary>",
          "expires_at": "<timestamp>"
        },
        "pending_id": "<pending approval id>",
        "action_hint": "Ask the human user to approve this pending request, then retry the original sgg_unity_call."
      }
    }

    The Gateway deliberately keeps approvals outside the MCP tool surface:

    - there is no model-callable `sgg_unity_approve` tool;
    - `confirmation_required` writes a pending approval request under the Gateway approval store;
    - the fallback response includes `pending_id` and an action hint;
    - a local human-only CLI/UI path can list and approve pending requests;
    - approval creates a one-shot approval token only;
    - the approval does not execute the command by itself;
    - the agent must repeat the original `sgg_unity_call` after human approval;
    - the one-shot approval is tied to the resolved project/command/scope/args_hash/expiry.
    

    That fallback is safe, but the UX is poor when the MCP client cannot render a native prompt. The agent only sees an MCP tool error JSON, and the human user does not get a first-class approve/decline/cancel UI.

    Gateway-side MCP elicitation support already implemented

    The Gateway also has a server-side MCP elicitation attempt for clients that advertise capabilities.elicitation.

    Current server-side shape:

    - the MCP server uses official SDK transports where possible;
    - if the handler receives `extra.sendRequest`;
    - and if dispatching the command returns `confirmation_required`;
    - and if `pending_id` is available;
    - the Gateway sends `elicitation/create` with a boolean `approve` field;
    - on `accept` with `approve: true`, it creates a one-shot approval and retries the original call once;
    - on `decline`, `cancel`, error, or unsupported elicitation, it returns the original `confirmation_required` fallback.
    

    For HTTP, the Gateway now has an SDK Streamable HTTP path for clients that support official sessions and a compatibility path for clients that still call tools/list / tools/call without Mcp-Session-Id. The compatibility path keeps existing tool calls working, while the SDK path remains available for clients that advertise capabilities.elicitation and support nested server-to-client requests.

    Expected OpenCode behavior

    When OpenCode truly supports MCP elicitation, it should declare the client capability during MCP initialization:

    {
      "capabilities": {
        "elicitation": {}
      }
    }

    During a tools/call, OpenCode should accept server-to-client elicitation/create requests such as:

    {
      "jsonrpc": "2.0",
      "id": "approval-123",
      "method": "elicitation/create",
      "params": {
        "message": "Approve this Unity Gateway command once?",
        "requestedSchema": {
          "type": "object",
          "properties": {
            "approve": {
              "type": "boolean",
              "title": "Approve once",
              "description": "Approve exactly this resolved command once. This does not approve future calls.",
              "default": false
            }
          },
          "required": ["approve"]
        }
      }
    }

    The OpenCode UI should render a native prompt/form and return one of the standard MCP responses.

    Accept:

    {
      "jsonrpc": "2.0",
      "id": "approval-123",
      "result": {
        "action": "accept",
        "content": {
          "approve": true
        }
      }
    }

    Decline:

    {
      "jsonrpc": "2.0",
      "id": "approval-123",
      "result": {
        "action": "decline"
      }
    }

    Cancel:

    {
      "jsonrpc": "2.0",
      "id": "approval-123",
      "result": {
        "action": "cancel"
      }
    }

    UI expectations

    For this use case, the prompt should clearly show:

    MCP server: <server name>
    Tool call: sgg_unity_call
    Project: <project>
    Command: <command>
    Risk: ask
    Approval type: one-shot
    Canonical scope: <scope summary>
    Args hash: <args_hash>
    Expires: <timestamp>
    

    The actions should be explicit:

    Approve once
    Decline
    Cancel
    

    For this case, Approve always would be unsafe unless the MCP server explicitly requests a broader approval scope. The Gateway is intentionally asking for a one-shot approval tied to the resolved command details.

    Security requirements

    Please make sure the implementation preserves these properties:

    1. The model must not be able to approve its own request.
    2. The approval must come from the human user through the OpenCode client UI.
    3. The prompt must identify the requesting MCP server.
    4. The prompt must distinguish accept, decline, and cancel.
    5. The prompt should not collect secrets. In this Gateway case, it only asks for a boolean approval.
    6. The UI should show a safe summary and hash, not require displaying raw args.
    7. Repeated elicitation requests should be rate-limited or deduplicated.
    8. Non-interactive modes should fail deterministically instead of hanging forever.
    9. This should not be mapped to a model-callable question tool.
    10. This should not require exposing a model-callable approval tool.

    Acceptance criteria

    • OpenCode advertises capabilities.elicitation only when it can actually handle elicitation.
    • OpenCode can receive and answer server-to-client elicitation/create while a tools/call is in progress.
    • Boolean-only flat JSON Schema forms are supported.
    • The TUI/web/desktop UI clearly identifies the requesting MCP server.
    • Users can explicitly accept, decline, or cancel.
    • The original tool call continues or fails cleanly based on the user response.
    • Non-interactive/headless modes do not hang indefinitely.
    • The implementation does not introduce model self-approval.
    • Streamable HTTP MCP and local stdio MCP are both considered.
    • If elicitation is unsupported, the failure/fallback is explicit and discoverable.

    References

  5. rekram1-node commented on Jul 3, 2026

    @rekram1-node
    Collaborator

    adding mcp elicitation support that will ship in opencode v2 soon

  6. rekram1-node commented on Jul 8, 2026

    @rekram1-node
    Collaborator

    has been added to opencode v2 see v2 branch (will release opencode v2 in next week or so)

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions