Skip to content

Nested schema validation errors are not serialized or logged in a useful way #235

@dfalling

Description

@dfalling

When calling my server's tool with nested fields or embed_one's, any validation errors are not properly serialized:

My caller only receives this:

MCP error -32602: Invalid params

My logs show no failures or debug output. I have to override handle_request to debug:

  def handle_request(%{"method" => "tools/call", "params" => %{"name" => name, "arguments" => args}} = req, frame) do
    require Logger

    Logger.info("=== Tool Call: #{name} ===")
    Logger.info("Arguments: #{inspect(args, pretty: true, limit: :infinity)}")

    registered_tools = Hermes.Server.Handlers.get_server_tools(__MODULE__, frame)

    case Enum.find(registered_tools, &(&1.name == name)) do
      nil ->
        Logger.error("Tool not found: #{name}")

      tool ->
        case tool.validate_input.(args) do
          {:ok, _validated} ->
            Logger.info("✓ Validation passed")

          {:error, raw_errors} ->
            Logger.error("✗ RAW Validation errors:")
            Logger.error(inspect(raw_errors, pretty: true, limit: :infinity))
        end
    end

    result = Hermes.Server.Handlers.handle(req, __MODULE__, frame)

    case result do
      {:error, error, _frame} ->
        Logger.error("Tool call failed: #{inspect(error, pretty: true)}")

      _ ->
        Logger.info("Tool call succeeded")
    end

That gets me access (in my logs) to much more useful debug info:

[error] ✗ RAW Validation errors:
[error] [
  %Peri.Error{
    path: [:location],
    key: :location,
    content: nil,
    message: nil,
    errors: [
      %Peri.Error{
        path: [:location, :latitude],
        key: :latitude,
        content: %{actual: "32.5355", expected: :number},
        message: "expected type of :number received 32.5355 value",
        errors: nil
      }
    ]
  },
  %Peri.Error{
    path: [:location],
    key: :location,
    content: nil,
    message: nil,
    errors: [
      %Peri.Error{
        path: [:location, :longitude],
        key: :longitude,
        content: %{actual: "44.4275", expected: :number},
        message: "expected type of :number received 44.4275 value",
        errors: nil
      }
    ]
  }
]

I finally found my specific issue was because :number was being coerced into string, and then erroring. When I changed :number to :float, it worked fine.

Metadata

Metadata

Assignees

No one assigned

    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