Problem
Kubernetes API errors returned by core resource tools are currently reduced to server-specific text in an MCP CallToolResult with isError: true. For example, the resources_create_or_update and resources_get handlers wrap the underlying Kubernetes error with fmt.Errorf and return it through api.NewToolCallResult.
That loses the machine-readable fields from metav1.Status / apierrors.StatusError, including:
- HTTP status code
- Kubernetes reason (
NotFound, Forbidden, Invalid, Conflict, etc.)
- resource and name details
- field causes and other structured details
Downstream clients must parse prose such as field is immutable or not found. This is brittle across server versions, Kubernetes libraries, alternate MCP servers, and gateway formatting.
Proposal
When a Kubernetes operation fails, include a structured Kubernetes Status object in the MCP tool result structuredContent, while retaining the existing text content and isError: true response for backward compatibility.
Example:
{
"apiVersion": "v1",
"kind": "Status",
"status": "Failure",
"reason": "Invalid",
"code": 422,
"message": "...",
"details": {
"group": "batch",
"kind": "Job",
"name": "example"
}
}
The structured payload should preserve the original Kubernetes status information where available, including details.causes for validation failures. It should apply consistently to the core resource operations (resources_get, resources_list, resources_create_or_update, resources_delete, and resources_scale).
Compatibility and semantics
- Keep the current human-readable
TextContent response for existing clients.
- Keep
isError: true for tool execution failures.
- Do not reinterpret
resources_create_or_update errors as AlreadyExists: that tool is documented as server-side apply and should retain the actual Kubernetes reason/code. A separate create-only tool would be a distinct enhancement if Kubernetes Create semantics are needed.
- Add tests covering at least
NotFound, Forbidden, Invalid, and conflict/status payloads.
- Document the structured error contract so clients do not need to parse server-specific prose.
Downstream motivation
Kubernaut currently needs to translate remote MCP errors into controller-runtime-compatible errors. The downstream hardening is tracked in PR #2435, but a structured upstream response would remove the need for any text-based fallback.
References
Problem
Kubernetes API errors returned by core resource tools are currently reduced to server-specific text in an MCP
CallToolResultwithisError: true. For example, theresources_create_or_updateandresources_gethandlers wrap the underlying Kubernetes error withfmt.Errorfand return it throughapi.NewToolCallResult.That loses the machine-readable fields from
metav1.Status/apierrors.StatusError, including:NotFound,Forbidden,Invalid,Conflict, etc.)Downstream clients must parse prose such as
field is immutableornot found. This is brittle across server versions, Kubernetes libraries, alternate MCP servers, and gateway formatting.Proposal
When a Kubernetes operation fails, include a structured Kubernetes
Statusobject in the MCP tool resultstructuredContent, while retaining the existing text content andisError: trueresponse for backward compatibility.Example:
{ "apiVersion": "v1", "kind": "Status", "status": "Failure", "reason": "Invalid", "code": 422, "message": "...", "details": { "group": "batch", "kind": "Job", "name": "example" } }The structured payload should preserve the original Kubernetes status information where available, including
details.causesfor validation failures. It should apply consistently to the core resource operations (resources_get,resources_list,resources_create_or_update,resources_delete, andresources_scale).Compatibility and semantics
TextContentresponse for existing clients.isError: truefor tool execution failures.resources_create_or_updateerrors asAlreadyExists: that tool is documented as server-side apply and should retain the actual Kubernetes reason/code. A separate create-only tool would be a distinct enhancement if KubernetesCreatesemantics are needed.NotFound,Forbidden,Invalid, and conflict/status payloads.Downstream motivation
Kubernaut currently needs to translate remote MCP errors into controller-runtime-compatible errors. The downstream hardening is tracked in PR #2435, but a structured upstream response would remove the need for any text-based fallback.
References
metav1.Status: https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Status