Skip to content

[BUG] MCP clients get no tools from v0.3.0: kubescape_get_vulnerability_details outputSchema is not an object #87

Open

Description

@djannot

Bug · Severity: Medium · default install · no workaround short of disabling the Kubescape tools
Affects: kagent-tools v0.3.0 (bundled with kagent v0.10.3) · Component: MCP server, tools/list
Introduced by: #66 "Migrate to GO SDK" (merged 2026-09-23)

What breaks

As a user I point an MCP client (MCP Inspector, VS Code, Cursor, Claude Desktop, anything on the TypeScript SDK) at the kagent-tools server. On v0.2.1 the client lists all 124 tools. On v0.3.0, which serves 126, I get none: one tool advertises an invalid outputSchema, and the client rejects the entire tools/list response.

Failed to list tools: [{"code":"invalid_value","values":["object"],
  "path":["tools",113,"outputSchema","type"],"message":"Invalid input: expected \"object\""}]

Tool 113 is kubescape_get_vulnerability_details. Its outputSchema is {"type": ["null","array"]}. The MCP spec (2025-06-18) requires a tool's outputSchema to have type: "object", because structured content is always a JSON object.

Reproduce

Docker only, no cluster needed:

docker run -d --name kt --platform linux/amd64 -p 18084:8084 ghcr.io/kagent-dev/kagent/tools:0.3.0 --port 8084
npx @modelcontextprotocol/inspector@0.21.2 --cli http://localhost:18084/mcp --transport http --method tools/list

The same command against ghcr.io/kagent-dev/kagent/tools:0.2.1 lists all 124 tools and exits 0.

Expected vs actual

  • Expected: every tool's outputSchema is an object schema, and tools/list succeeds on spec-compliant clients.
  • Actual: kubescape_get_vulnerability_details advertises an array schema, and strict clients get no tools at all.

Ask

Wrap that tool's output in an object, and make the existing schema test enforce type: "object" so the next slice-returning handler can't regress it. It's a one-type change. Clients stay broken until a release ships it.

Root cause: the handler's typed output is a slice, so go-sdk infers an array schema

pkg/kubescape/kubescape.go (v0.3.0, unchanged on main):

func (k *KubescapeTool) handleGetVulnerabilityDetails(ctx context.Context, request *sdkmcp.CallToolRequest,
    in getVulnerabilityDetailsInput) (*sdkmcp.CallToolResult, []v1beta1.Match, error)

go-sdk (v1.8.0) infers the output schema from the Out type parameter. A nil-able slice becomes {"type":["null","array"]}.

Suggested fix:

type vulnerabilityDetailsOutput struct {
    Matches []v1beta1.Match `json:"matches"`
}

cmd/tools_output_schema_test.go (TestEveryToolHasValidOutputSchema) only checks that each schema is non-nil and JSON-serializable, so this passed CI. Adding require.Equal(t, "object", tool.OutputSchema.(map[string]any)["type"]) (or the equivalent on the typed schema) would catch it.

Scope: exactly one of 126 tools is affected, and the server itself works

Raw JSON-RPC against the v0.3.0 image, with no client-side validation, lists every tool whose schema isn't an object:

H='-H Content-Type:application/json -H Accept:application/json,text/event-stream'
SID=$(curl -s -D - -o /dev/null $H http://localhost:18084/mcp \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
  | awk -F': ' 'tolower($1)=="mcp-session-id"{print $2}' | tr -d '\r')
curl -s $H -H "Mcp-Session-Id: $SID" http://localhost:18084/mcp -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
curl -s $H -H "Mcp-Session-Id: $SID" -H "MCP-Protocol-Version: 2025-06-18" http://localhost:18084/mcp \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | sed -n 's/^data: //p' \
  | jq -c '.result.tools|to_entries[]|select(.value.outputSchema.type!="object")|{i:.key,name:.value.name,type:.value.outputSchema.type}'
# {"i":113,"name":"kubescape_get_vulnerability_details","type":["null","array"]}
  • Reproduced with image digest sha256:cb89eb76216195a90cac73846b447525b6e6e33e45154d1ded9621bf11340315, both locally and in a kind cluster running kagent v0.10.3. The 0.2.1 image, run the same way, passes with 124 tools.
  • Not a client-version artifact: the image is the only thing that changed between a passing and a failing run (kagent v0.10.2 → v0.10.3, kagent-tools 0.2.1 → 0.3.0). The Inspector version was pinned at 0.21.2 in both.
  • Not affected: kagent agents themselves. Their MCP clients don't validate outputSchema.type, so the RemoteMCPServer stays Accepted and agents still call tools. Only strict, spec-validating clients break.

Activity

  1. nishanthchandr4 commented on Oct 7, 2026

    @nishanthchandr4

    /assign

  2. nishanthchandr4 commented on Oct 7, 2026

    @nishanthchandr4

    Hi! I'd like to work on this. Plan: wrap the kubescape_get_vulnerability_details output in a struct (e.g. vulnerabilityDetailsOutput{Matches []v1beta1.Match}) so the inferred outputSchema is type "object", and tighten TestEveryToolHasValidOutputSchema to require type "object" for every tool.

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

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