Skip to content

CallToolResult Serialization Format Violation #202

Description

@bettercallsaulj

What's the Issue

The handleCallTool function in mcp_server.cc was manually building a JSON-RPC response with an incorrect format that violates the MCP protocol specification.

Incorrect format produced:

{
  "content": "Hello, World!",
  "isError": false
}

Correct MCP format required:

{
  "content": [
    {
      "type": "text",
      "text": "Hello, World!"
    }
  ],
  "isError": false
}

The MCP specification requires content to be an array of content blocks, where each block has a type field (e.g., "text", "image") and corresponding data fields. The incorrect implementation was returning content as a plain string, causing MCP clients (like Claude.ai) to fail with "Error occurred during tool execution".

How to Reproduce

  1. Set up an MCP server using gopher-mcp/mcp-cpp-sdk
  2. Register a tool that returns text content:
    CallToolResult result;
    result.content.push_back(ExtendedContentBlock(TextContent("Hello")));
    result.isError = false;
    return result;
  3. Connect an MCP client (e.g., Claude.ai via MCP Inspector)
  4. Call the tool
  5. Expected: Tool returns successfully with content
  6. Actual: Client shows "Error occurred during tool execution" because it cannot parse the malformed response

How to Fix

Replace the manual response building with proper serialization using json::to_json():

Before (broken):

// Extract text content manually - WRONG
std::string content_text;
for (const auto& content_block : result.content) {
  if (holds_alternative<TextContent>(content_block)) {
    content_text += get<TextContent>(content_block).text;
  }
}

auto response_metadata = make<Metadata>()
    .add("content", content_text)
    .add("isError", result.isError)
    .build();

return jsonrpc::Response::success(request.id,
    jsonrpc::ResponseResult(response_metadata));

After (correct):

// Use proper MCP serialization
auto result_json = json::to_json(result);

return jsonrpc::Response::success(request.id,
    jsonrpc::ResponseResult(result_json));

The json::to_json(CallToolResult) function correctly serializes the result with:

  • content as an array of content blocks
  • Each block with proper type and data fields
  • isError boolean field

Files changed:

  • src/server/mcp_server.cc - Fix serialization in handleCallTool()

Tests added:

  • tests/json/test_mcp_serialization_extensive.cc
    • CallToolResultMcpFormatCompliance - Verifies single content block format
    • CallToolResultMultipleContentBlocks - Verifies multiple content blocks
    • CallToolResultWithError - Verifies error case serialization

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions