microsoft/semantic-kernel · error · McpError

INTERNAL_ERROR

INTERNAL_ERROR

Error message

Function {function_name} returned no result

What it means

Raised inside the MCP server's `@server.call_tool()` handler (`_call_tool`) after a Semantic Kernel function is invoked through the MCP bridge. The kernel function call returned a falsy result (`if result:` at mcp.py:1133 was False), so there is nothing to serialize into MCP content types, and the server responds with an MCP error carrying code INTERNAL_ERROR. It signals that the wrapped tool executed but produced no usable return value (None, empty FunctionResult, or a value that evaluates to False).

Source

Thrown at python/semantic_kernel/connectors/mcp.py:1158

                        match item:
                            case (
                                TextContent() | ImageContent() | BinaryContent() | AudioContent() | ChatMessageContent()
                            ):
                                messages.extend(_kernel_content_to_mcp_content_types(item))
                            case _:
                                messages.append(
                                    types.TextContent(type="text", text=str(item)),
                                )
                else:
                    match value:
                        case TextContent() | ImageContent() | BinaryContent() | AudioContent() | ChatMessageContent():
                            messages.extend(_kernel_content_to_mcp_content_types(value))
                        case _:
                            messages.append(
                                types.TextContent(type="text", text=str(value)),
                            )
                return messages
            raise McpError(
                error=types.ErrorData(
                    code=types.INTERNAL_ERROR,
                    message=f"Function {function_name} returned no result",
                ),
            )

    if prompts:

        @server.list_prompts()
        async def _list_prompts() -> list[types.Prompt]:
            """List all prompts in the kernel."""
            mcp_prompts = []
            for prompt in prompts:
                mcp_prompts.append(
                    types.Prompt(
                        name=prompt.prompt_template_config.name,
                        description=prompt.prompt_template_config.description,
                        arguments=[

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Ensure the registered kernel function always returns a non-empty, truthy value (a string, a KernelContent item, or a non-empty list) on every code path.
  2. Inspect the function's implementation for branches that implicitly return None and add an explicit return with meaningful content.
  3. If a no-op result is legitimate, wrap the return so the MCP client receives at least one TextContent instead of triggering the error.
  4. Add logging/tests asserting the function returns a truthy FunctionResult for representative inputs before registering it as an MCP tool.

Example fix

// before
@kernel_function
async def summarize(text: str):
    if not text:
        return  # falsy -> MCP INTERNAL_ERROR
    return llm.summarize(text)

// after
@kernel_function
async def summarize(text: str) -> str:
    if not text:
        return "(nothing to summarize)"
    return llm.summarize(text)
Defensive patterns

Strategy: validation

Validate before calling

async def safe_call(kernel, function_name, arguments):
    result = await kernel.invoke(function_name, **arguments)
    if not result or result.value is None or result.value == []:
        # guarantee a non-empty payload before the MCP bridge sees it
        return ["(no result)"]
    return result

# register only functions known to return truthy values

Type guard

def has_result(result) -> bool:
    """True when a FunctionResult is safe to expose over MCP."""
    return bool(result) and not (getattr(result, "value", None) in (None, [], ""))

Try / catch

from mcp.shared.exceptions import McpError
try:
    out = await mcp_client.call_tool("my_tool", {"x": 1})
except McpError as e:
    if "returned no result" in str(e):
        # tool produced no output; degrade gracefully
        out = []
    else:
        raise

Prevention

When it happens

Trigger: An MCP client sends a `tools/call` request for a registered kernel function; the underlying kernel function returns `None`, an empty result, or raises-and-is-swallowed such that `_call_kernel_function` yields a falsy `FunctionResult`. The handler skips the `if result:` branch at line 1133 and falls through to the `raise McpError(...)` at line 1158.

Common situations: A kernel function registered via the MCP server has no explicit `return` statement, returns `None` on some code path, returns an empty list/dict, or the function's plugin logic intentionally yields no output for certain inputs. Also occurs when a function throws an exception that the bridge converts into a None result, or when the function is misconfigured and returns a void coroutine result.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/b1c92fdfc768c93f. Report an issue: GitHub.