Skip to content

MCP Sampling is Always Declined in Non-Interactive Mode #2882

@brian-kelley-intel

Description

@brian-kelley-intel

Describe the bug

MCP sampling (#1748) works in interactive mode, but the same MCP server reaches a denied permissions prompt in -p non-interactive mode, even when --allow-all is enabled.

With --allow-all, the CLI does invoke the MCP tool in -p mode. The tool starts, the server receives CallToolRequest, and then the sampling call back into the CLI fails with Method not found.

The same server, config, CLI version, and prompts work in interactive mode. In that control case, the CLI logs Received sampling request from sampling-repro and the tool returns successfully.

Affected version

GitHub Copilot CLI 1.0.31

Steps to reproduce the behavior

1. Create a minimal FastMCP server that uses sampling

sampling_server.py

#!/usr/bin/env python3
"""Minimal FastMCP server that exercises sampling/createMessage."""

from mcp.server.fastmcp import Context, FastMCP
from mcp.types import SamplingMessage, TextContent


mcp = FastMCP("sampling-repro")


async def sample_text(ctx: Context, prompt: str, system_prompt: str) -> str:
    result = await ctx.session.create_message(
        messages=[
            SamplingMessage(
                role="user",
                content=TextContent(type="text", text=prompt),
            )
        ],
        max_tokens=256,
        system_prompt=system_prompt,
    )

    parts = getattr(result, "content", None)
    if isinstance(parts, list):
        text = "".join(
            part.text for part in parts if getattr(part, "type", None) == "text"
        ).strip()
        if text:
            return text

    text = getattr(getattr(result, "content", None), "text", None)
    if isinstance(text, str) and text.strip():
        return text.strip()

    raise RuntimeError(f"Unexpected sampling response: {result!r}")


@mcp.tool()
async def summarize(text: str, ctx: Context) -> str:
    """Summarize the given text in one sentence."""
    summary = await sample_text(
        ctx,
        prompt=f"Summarize in exactly one sentence:\n\n{text}",
        system_prompt="You are a concise summarizer. Reply with exactly one sentence.",
    )
    return f"Summary: {summary}"


@mcp.tool()
async def duplicate(text: str, ctx: Context) -> str:
    """Repeat the given text exactly twice with a separator."""
    repeated = await sample_text(
        ctx,
        prompt=(
            "Repeat the following text exactly twice, separated by ' | ', and add no "
            f"other words:\n\n{text}"
        ),
        system_prompt="Return only the duplicated text.",
    )
    return repeated


if __name__ == "__main__":
    mcp.run()

2. Create a minimal MCP config file

mcp-config.json

{
  "mcpServers": {
    "sampling-repro": {
      "command": "/path/to/python3",
      "args": [
        "/path/to/sampling_server.py"
      ],
      "env": {}
    }
  }
}

3. Run the same prompts in -p mode

Summarize example

copilot \
  --log-level=all \
  --log-dir /path/to/logs \
  --additional-mcp-config @/path/to/mcp-config.json \
  --disable-builtin-mcps \
  --no-custom-instructions \
  --allow-all \
  -p "Use the summarize tool from the sampling-repro MCP server exactly once to summarize this text: 'The quick brown fox jumps over the lazy dog. This sentence contains every letter of the alphabet and is often used for typing practice.'"

Duplication example

copilot \
  --log-level=all \
  --log-dir /path/to/logs \
  --additional-mcp-config @/path/to/mcp-config.json \
  --disable-builtin-mcps \
  --no-custom-instructions \
  --allow-all \
  -p "Use the duplicate tool from the sampling-repro MCP server exactly once with this text: 'alpha beta'"

4. Check the outputs

Both non-interactive runs fail the same way:

✗ summarize (MCP: sampling-repro) · text: "The quick brown fox jumps over the lazy dog. This senten…
  └ MCP server 'sampling-repro': Error executing tool summarize: Method not found
✗ duplicate (MCP: sampling-repro) · text: "alpha beta"
  └ MCP server 'sampling-repro': Error executing tool duplicate: Method not found

Redacted log excerpt from the -p summarize run:

[DEBUG] Tool calls count: 2
[ERROR] [mcp server sampling-repro stderr] ... Processing request of type CallToolRequest
[DEBUG] Tool invocation result: Error executing tool summarize: Method not found

Expected behavior

If --allow-all is set, sampling/createMessage should work in -p mode the same way it works in interactive mode.

The non-interactive session cannot show approval prompts, but --allow-all already bypasses tool, path, and URL confirmations. Once the tool call is allowed to run, the CLI should expose the sampling handler to the MCP server instead of returning Method not found.

Control: interactive mode works

Run the same prompts in interactive mode:

copilot \
  --log-level=all \
  --log-dir /path/to/logs \
  --additional-mcp-config @/path/to/mcp-config.json \
  --disable-builtin-mcps \
  --no-custom-instructions \
  --allow-all \
  -i "Use the duplicate tool from the sampling-repro MCP server exactly once with this text: 'alpha beta'"
copilot \
  --log-level=all \
  --log-dir /path/to/logs \
  --additional-mcp-config @/path/to/mcp-config.json \
  --disable-builtin-mcps \
  --no-custom-instructions \
  --allow-all \
  -i "Use the summarize tool from the sampling-repro MCP server exactly once to summarize this text: 'The quick brown fox jumps over the lazy dog. This sentence contains every letter of the alphabet and is often used for typing practice.'"

These succeed with the same server and config:

duplicate (MCP: sampling-repro) · text: "alpha beta"
  └ {"result":"alpha beta | alpha beta"}
summarize (MCP: sampling-repro) · text: "The quick brown fox jumps over the lazy dog. This sentence c…"
  └ {"result":"Summary: \"The quick brown fox jumps over the lazy dog\" is a pangram containing every letter of the alphabet and is commonly used for typing practice."}

Expected behavior

MCP sampling should be allowed through non-interactive sessions when using -p and --allow-all.

Additional context

Field Value
Process log (redacted) process-1776747687370-23478-redacted.log
CLI version GitHub Copilot CLI 1.0.31
OS Linux
Shell bash

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions