Skip to content

Add external tools MCP server#15

Merged
arkadeepsen merged 1 commit intoovn-kubernetes:mainfrom
Meina-rh:external_tools
Jan 16, 2026
Merged

Add external tools MCP server#15
arkadeepsen merged 1 commit intoovn-kubernetes:mainfrom
Meina-rh:external_tools

Conversation

@Meina-rh
Copy link
Contributor

@Meina-rh Meina-rh commented Nov 28, 2025

This PR add network external tools: tcpdump and pwru

  • tcpdump: Capture and analyze network packets with BPF filters
  • pwru: allows fine-grained introspection of kernel state to facilitate debugging network connectivity issues.

Test with Claude Code, it can work well

● I'll run tcpdump on the ovn-worker node with the parameters you specified.

● ovn-kubernetes - tcpdump (MCP)(target_type: "node", node_name: "ovn-worker", node_image: "docker.io/nicolaka/netshoot:latest", interface: "any", packet_count: 100, duration: 10)
  ⎿ {                                                                                                                                                                                                          
      "output": "07:22:58.893687 lo    In  IP (tos 0x0, ttl 64, id 12134, offset 0, flags [DF], proto TCP (6), length 56)\n    127.0.0.1.39215 > 127.0.0.1.53410: Flags [P.], cksum 0xfe2c (incorrect -> 0x9946
    ), seq 760435588:760435592, ack 2125033155, win 64, options [nop,nop,TS val 1524508863 ecr 1524508817], length 4\n07:22:58.893739 lo    In  IP (tos 0x0, ttl 64, id 12135, offset 0, flags [DF], proto TCP
    … +131 lines (ctrl+o to expand)


● Perfect! The tcpdump completed successfully on the ovn-worker node. I captured 100 packets over the 10-second duration.

  Summary of captured traffic:

  - Loopback (lo): TCP traffic on 127.0.0.1 (local connections)
  - breth0: OVN bridge interface traffic between nodes (172.18.0.3 ↔ 172.18.0.4)
  - eth0: Physical network interface traffic

  Key observations:
  - Port 10250: Kubelet API traffic (172.18.0.3 → 172.18.0.4)
  - Port 6443: Kubernetes API server traffic (172.18.0.3 → 172.18.0.4)
  - TCP connections showing normal cluster communication patterns
● I'll analyze network packets on the ovn-worker node using pwru to trace TCP traffic to destination port 8080.

● ovn-kubernetes - pwru (MCP)(node_name: "ovn-worker", pcap_filter: "tcp and dst port 8080", output_limit_lines: 100)
  ⎿ {                                                                                                                                                                                                          
      "output": "SKB                CPU PROCESS          NETNS      MARK/x        IFACE       PROTO  MTU   LEN   TUPLE FUNC\n0xffff88c6ca1962e8 8   <empty>:37034    4026533011 0               0         0x000
    0 65536 60    127.0.0.1:37608->127.0.0.1:8080(tcp) __ip_local_out\n0xffff88c6ca1962e8 8   <empty>:37034    4026533011 0               0         0x0800 65536 60    127.0.0.1:37608->127.0.0.1:8080(tcp) nf_
    … +75 lines (ctrl+o to expand)



● I've successfully traced TCP packets to port 8080 on the ovn-worker node using pwru. Here's what the analysis reveals:

  Traffic Characteristics

  - Connection: Localhost traffic from 127.0.0.1:37608 → 127.0.0.1:8080
  - Network Namespace: Traffic originates in netns 4026533011, then transitions to root netns (0)
  - Protocol: TCP connection establishment (SYN packets visible)

  Kernel Path Analysis

  Outbound Path

  1. __ip_local_out - Local IP output initialization
  2. nf_hook_slow - Netfilter hooks (OUTPUT chain)
  3. ip_output → ip_finish_output - IP layer processing
  4. __dev_queue_xmit - Queuing for transmission
  5. loopback_xmit - Loopback device transmission

  Inbound Path

  1. __netif_rx - Receive packet from device
  2. __netif_receive_skb - Core receive processing
  3. ip_rcv - IP layer receive
  4. ip_local_deliver - Deliver to local stack
  5. tcp_v4_rcv - TCP layer processing

  TCP Processing

  - tcp_conn_request - Connection request handling
  - tcp_rcv_state_process - TCP state machine
  - __inet_lookup_listener - Socket lookup for listening port 8080
  - reuseport_select_sock - SO_REUSEPORT socket selection

  Security Checks

  - Multiple SELinux checkpoints: selinux_ip_output, selinux_socket_sock_rcv_skb, selinux_inet_conn_request
  - eBPF LSM hooks: bpf_lsm_socket_sock_rcv_skb, bpf_lsm_inet_conn_request

  The trace shows healthy TCP connection establishment to a local service listening on port 8080, with all packets successfully traversing the kernel networking stack without drops.

Summary by CodeRabbit

  • New Features
    • Added tcpdump and pwru network diagnostic tools (node/pod targets) with MCP registration and image configuration via new CLI flags.
  • Bug Fixes / Security
    • Stronger validation for paths, interfaces, BPF filters, and numeric bounds; reduced debug pod timeout.
  • API
    • Exposed parameter structures and standardized command result format for network diagnostics.
  • Tests
    • Added comprehensive unit tests for validation utilities and the command-builder.

✏️ Tip: You can customize this high-level summary in your review settings.

@Meina-rh Meina-rh force-pushed the external_tools branch 2 times, most recently from e025b45 to 655d563 Compare December 15, 2025 03:13
@Meina-rh
Copy link
Contributor Author

/cc @arkadeepsen
I also added pwru tool in this PR. PTAL, thanks

@arkadeepsen
Copy link
Contributor

/cc @arkadeepsen I also added pwru tool in this PR. PTAL, thanks

Sure @Meina-rh. Can your remove the .idea directory which got added in the latest commit.

@arkadeepsen
Copy link
Contributor

/cc @arkadeepsen I also added pwru tool in this PR. PTAL, thanks

Sure @Meina-rh. Can your remove the .idea directory which got added in the latest commit.

Also remove pkg/external_tools/mcp.test file.

return nil, types.CommandResult{}, fmt.Errorf("tcpdump %w", err)
}

if err := validateIntMax(in.Duration, MaxTcpdumpDuration, "duration", "seconds"); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duration is validated but never used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized that we don't need Duration param, it doesn't support in tcpdump. We can use packet_count to limit directly, so I delete Duration param

Comment on lines 59 to 173
// runPwruDebugNode creates a specialized pwru debug pod and runs a command in it
func (s *MCPServer) runPwruDebugNode(ctx context.Context, req *mcp.CallToolRequest, nodeName string, command []string) (*mcp.CallToolResult, types.CommandResult, error) {
if nodeName == "" {
return nil, types.CommandResult{}, fmt.Errorf("node_name is required for pwru")
}

namespace := metav1.NamespaceDefault
debugPodName, cleanupPod, err := s.createPwruPod(ctx, nodeName, namespace)
if err != nil {
return nil, types.CommandResult{}, err
}

if cleanupPod != nil {
defer cleanupPod()
}

_, output, err := s.k8sMcpServer.ExecPod(ctx, req, k8stypes.ExecPodParams{
NamespacedNameParams: k8stypes.NamespacedNameParams{
Name: debugPodName,
Namespace: namespace,
},
Container: "pwru",
Command: command,
})
if err != nil {
return nil, types.CommandResult{}, fmt.Errorf("failed to execute command in pwru debug pod: %w", err)
}

return nil, types.CommandResult{Output: output.Stdout}, nil
}

// createPwruPod creates a specialized debug pod for pwru with required eBPF capabilities.
// This pod requires:
// - cilium/pwru:latest image
// - /sys/kernel/debug mounted for eBPF
// - privileged security context
// - hostNetwork and hostPID for packet tracing
func (s *MCPServer) createPwruPod(ctx context.Context, node, namespace string) (string, func(), error) {
hostPathType := corev1.HostPathDirectoryOrCreate
sleepCommand := []string{"sleep", "3600"}

// Create a specialized pwru debug pod
debugPod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "pwru-",
Namespace: namespace,
},
Spec: corev1.PodSpec{
NodeSelector: map[string]string{
"kubernetes.io/hostname": node,
},
RestartPolicy: corev1.RestartPolicyNever,
Tolerations: []corev1.Toleration{
{
Operator: corev1.TolerationOpExists,
},
},
HostNetwork: true,
HostPID: true,
Volumes: []corev1.Volume{
{
Name: "sys-kernel-debug",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/sys/kernel/debug",
Type: &hostPathType,
},
},
},
},
Containers: []corev1.Container{
{
Name: "pwru",
Image: "docker.io/cilium/pwru:latest",
Command: sleepCommand,
SecurityContext: &corev1.SecurityContext{
Privileged: ptr.To(true),
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "sys-kernel-debug",
MountPath: "/sys/kernel/debug",
},
},
}},
},
}

clientSet := s.k8sMcpServer.GetClientSet().GetKubernetesClientSet()
createdDebugPod, err := clientSet.CoreV1().Pods(namespace).Create(ctx, debugPod, metav1.CreateOptions{})
if err != nil {
return "", nil, fmt.Errorf("failed to create pwru debug pod: %w", err)
}

cleanupPod := func() {
err := clientSet.CoreV1().Pods(namespace).Delete(ctx, createdDebugPod.Name, metav1.DeleteOptions{})
if err != nil {
log.Printf("failed to cleanup pwru debug pod: %v", err)
}
}

err = wait.PollUntilContextTimeout(ctx, time.Millisecond*500, time.Minute*1, true, func(ctx context.Context) (bool, error) {
pod, err := clientSet.CoreV1().Pods(namespace).Get(ctx, createdDebugPod.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
return pod.Status.Phase == corev1.PodRunning, nil
})
if err != nil {
cleanupPod()
return "", nil, fmt.Errorf("pwru debug pod did not reach running state within timeout of 1 minute: %w", err)
}

return createdDebugPod.Name, cleanupPod, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need additonal parameterization of k8s layer's DebugNode functionality, then lets do that. Ideally we should keep the operations specific for a layer separate.

Description: `Capture network packets with strict safety controls.

Parameters:
- Must specify at least 2 of: packet_count, bpf_filter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wording is a little confusing. This kind of translates to "at least 2 of 2". Better to mention both fields are required.

@Meina-rh Meina-rh force-pushed the external_tools branch 2 times, most recently from 64e49b6 to 3a31f18 Compare December 18, 2025 09:47
@Meina-rh
Copy link
Contributor Author

@arkadeepsen I've made some updates in tcpdump and pwru according to above comments. could you help review again? thanks!

@tssurya
Copy link
Contributor

tssurya commented Dec 18, 2025

@coderabbitai: please review

@coderabbitai
Copy link

coderabbitai bot commented Dec 18, 2025

@tssurya: I'll review the changes in this PR.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Dec 18, 2025

📝 Walkthrough

Walkthrough

Integrates nettools (tcpdump, pwru) as MCP tools, adds validation/helpers/tests and nettools types, extends Kubernetes debug APIs to accept/validate configurable host/mount paths, and wires nettools into ovnk-mcp-server with new --pwru-image and --tcpdump-image flags.

Changes

Cohort / File(s) Summary
Server wiring
cmd/ovnk-mcp-server/main.go
Adds --pwru-image and --tcpdump-image flags; extends MCPServerConfig with PwruImage and TcpdumpImage; initializes nettoolsmcp.NewMCPServer(..., PwruImage, TcpdumpImage) and registers nettools via AddTools.
Nettools MCP core
pkg/nettools/mcp/mcp.go, pkg/nettools/mcp/tcpdump.go, pkg/nettools/mcp/pwru.go
New MCPServer type/constructor; registers tcpdump and pwru tools. Handlers validate inputs, apply defaults/limits, build commands, and invoke debug-node / exec-pod flows.
Nettools utils & tests
pkg/nettools/mcp/utils.go, pkg/nettools/mcp/utils_test.go
Adds helpers: runDebugNode, runExecPod, commandBuilder, validators (interface, BPF, int max), default helper; extensive unit tests for these utilities.
Nettools types
pkg/nettools/types/manifest.go
New public types: BaseNetworkDiagParams, TcpdumpParams, PwruParams, and CommandResult for tool inputs/results.
Kubernetes client changes
pkg/kubernetes/client/nodes.go
DebugNode and createPod signatures extended to accept hostPath and mountPath; pod spec, mounts and env vars made configurable with defaults (/ and /host); debug timeout shortened to 1 minute.
Kubernetes MCP validation
pkg/kubernetes/mcp/nodes.go
Adds validatePath(path, pathType string) to enforce absolute, non-traversing, safe mount paths; DebugNode validates HostPath/MountPath and forwards them to the client.
Kubernetes types update
pkg/kubernetes/types/nodes.go
Adds exported HostPath and MountPath fields to DebugNodeParams (json:"hostPath,omitempty", json:"mountPath,omitempty").

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant OVN_MCP_Server
    participant NetTools_MCP
    participant K8s_MCP
    participant Node_Pod

    Client->>OVN_MCP_Server: CallTool(tcpdump|pwru, params)
    OVN_MCP_Server->>NetTools_MCP: Route request
    NetTools_MCP->>NetTools_MCP: Validate params (paths, iface, bpf, limits)
    alt validation fails
        NetTools_MCP-->>OVN_MCP_Server: Return validation error
    else
        NetTools_MCP->>K8s_MCP: DebugNode / ExecPod (image, hostPath, mountPath, pod/container)
        K8s_MCP->>Node_Pod: Create/exec pod and run command
        Node_Pod-->>K8s_MCP: stdout/stderr
        K8s_MCP-->>NetTools_MCP: CommandResult
        NetTools_MCP-->>OVN_MCP_Server: CallToolResult
    end
    OVN_MCP_Server-->>Client: Tool execution result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • pperiyasamy
  • arkadeepsen

Poem

🐰 I hopped through code with whiskers bright,

Tcpdump and pwru dance in the night,
Paths are checked, images set just right,
Pods and nodes hum diagnostics light,
A rabbit cheers — observability takes flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add external tools MCP server' directly aligns with the PR's core objective of adding tcpdump and pwru network tools to the MCP server.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
pkg/nettools/mcp/utils.go (1)

100-116: Consider adding backslash to the dangerous characters list.

The filter blocks most shell metacharacters but backslash (\) could be used for escape sequences in some contexts. While BPF filters don't typically use backslashes, adding it would provide defense-in-depth.

♻️ Optional enhancement
-	dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00", ">", "<", "'", "\""}
+	dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00", ">", "<", "'", "\"", "\\"}

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ace57e and 165c0b1.

📒 Files selected for processing (10)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/pwru.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/kubernetes/client/nodes.go
  • pkg/nettools/types/manifest.go
  • pkg/kubernetes/types/nodes.go
🧰 Additional context used
🧬 Code graph analysis (3)
cmd/ovnk-mcp-server/main.go (3)
pkg/kubernetes/mcp/mcp.go (2)
  • Config (11-13)
  • NewMCPServer (19-42)
pkg/nettools/mcp/mcp.go (1)
  • NewMCPServer (16-22)
pkg/ovn/mcp/mcp.go (1)
  • NewMCPServer (19-23)
pkg/nettools/mcp/utils.go (5)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-13)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
pkg/nettools/types/manifest.go (1)
  • CommandResult (38-42)
pkg/kubernetes/types/pods.go (1)
  • ExecPodParams (17-21)
pkg/kubernetes/types/common.go (1)
  • NamespacedNameParams (37-40)
pkg/nettools/mcp/pwru.go (3)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-13)
pkg/nettools/types/manifest.go (2)
  • PwruParams (30-35)
  • CommandResult (38-42)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (21)
pkg/kubernetes/mcp/nodes.go (2)

13-46: Path validation logic properly addresses prior feedback.

The traversal check now correctly inspects the original path elements (not cleaned), and the character whitelist includes ~ for legitimate Kubernetes paths like kubernetes.io~secret. The validation is appropriately defensive for mount path security.


48-65: LGTM!

Path validation is correctly applied to both hostPath and mountPath before invocation, following a fail-fast pattern. The error handling is consistent with the rest of the method.

pkg/nettools/mcp/utils_test.go (5)

1-100: LGTM! Comprehensive boundary testing for validateIntMax.

The test cases cover zero, minimum valid, below max, at max, over max, negative values, and the unit/no-unit error message variations. Good coverage of edge cases.


102-143: LGTM! stringWithDefault tests are straightforward and complete.

The test cases correctly verify empty-returns-default, non-empty-returns-value, both-empty, and whitespace-is-not-empty behaviors.


145-298: LGTM! Thorough interface name validation tests.

Good coverage including:

  • Valid patterns: eth0, vlan interfaces (eth0.100), underscores, hyphens, loopback
  • Linux IFNAMSIZ limit (15 chars max)
  • Injection/shell metacharacter rejection (semicolon, pipe, ampersand, backtick, dollar sign, newline, etc.)
  • Invalid starting characters (hyphen, dot, underscore)

300-489: LGTM! Excellent security-focused packet filter validation tests.

The test suite covers:

  • Valid BPF filter expressions (tcp, udp, icmp, host, net, portrange, vlan, etc.)
  • Command injection attempts (semicolon, pipe, ampersand, backtick, dollar sign, command substitution, newline, null byte)
  • Length boundary testing at 1024 chars

The past review comment about using strings.Repeat instead of make([]byte, N) has been addressed.


491-568: LGTM! Command builder fluent API tests are well-structured.

Tests cover the builder pattern including add, addIf, addIfNotEmpty, chaining, and multiple base arguments. The table-driven approach with builder functions is clean.

cmd/ovnk-mcp-server/main.go (2)

17-17: LGTM! Import added for nettools MCP package.

The import alias nettoolsmcp is consistent with the naming pattern used for other MCP packages in this file.


52-55: LGTM! Network tools server integration follows the established pattern.

The initialization and tool registration mirrors the pattern used for other MCP servers (OVN, OVS, Kernel), maintaining consistency.

pkg/nettools/mcp/utils.go (5)

14-14: LGTM! Interface name regex pattern is correct.

The pattern ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ correctly enforces:

  • Must start with alphanumeric
  • Can contain alphanumerics, dots, underscores, hyphens after first char
  • This aligns with Linux interface naming conventions

17-29: LGTM! runDebugNode validates required fields before execution.

The function correctly validates Name and Image are non-empty for node-level debugging, then delegates to the underlying k8s MCP server.


32-44: LGTM! runExecPod with sensible default namespace handling.

Defaulting to "default" namespace when not specified is a reasonable UX choice for Kubernetes tooling.


46-81: LGTM! Clean fluent builder pattern implementation.

The commandBuilder provides a readable API for constructing commands with conditional arguments. The addIf and addIfNotEmpty methods reduce boilerplate in the calling code.


118-138: LGTM! Simple and effective validation helpers.

validateIntMax handles both negative and upper-bound validation with optional unit in error messages. stringWithDefault is a straightforward utility.

pkg/nettools/mcp/pwru.go (1)

20-49: LGTM! Pwru implementation is clean and secure.

The method:

  • Properly defaults and validates outputLimitLines
  • Validates BPF filter for injection attacks
  • Correctly mounts /sys/kernel/debug for eBPF access
  • Uses the fluent command builder pattern

The concern about negative values was addressed - validateIntMax handles that check.

pkg/nettools/mcp/mcp.go (3)

8-22: LGTM! MCPServer struct and constructor are well-designed.

The struct correctly holds separate image references for pwru and tcpdump, addressing the earlier feedback about needing distinct images for each tool.


26-52: LGTM! Tcpdump tool registration with comprehensive documentation.

The tool description clearly documents:

  • Both node-level and pod-level capture modes
  • All parameters with defaults and max values
  • The --tcpdump-image flag for configuration
  • Practical examples for different use cases

53-76: LGTM! Pwru tool registration with clear documentation.

The tool description explains:

  • eBPF-based kernel packet tracing purpose
  • Required node-level execution (per earlier discussion about /sys/kernel/debug access)
  • The --pwru-image flag for configuration
  • Practical examples for different trace scenarios
pkg/nettools/mcp/tcpdump.go (3)

1-22: LGTM!

The package structure, imports, and constants are well-organized. The constants are appropriately chosen for diagnostic purposes, and the comments clearly explain the rationale for DefaultSnaplen (96 bytes for headers) and MaxSnaplen (1500 bytes for standard MTU).


24-47: LGTM!

The validation logic correctly validates interface and BPF filter inputs, applies sensible defaults for zero values, and validates against maximum limits. As confirmed in prior discussion, validateIntMax also rejects negative values, so the validation is complete.


49-82: LGTM!

The command construction correctly follows tcpdump syntax with appropriate flags. The use of s.tcpdumpImage for the hardcoded image (rather than user-provided NodeImage) addresses the security concerns discussed in prior review. The switch-case cleanly handles both node and pod targets, and the validation of required fields (node name, pod name/namespace) is properly centralized in runDebugNode and runExecPod as confirmed in past discussions.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/kubernetes/client/nodes.go (1)

128-128: Fix the timeout duration in the error message.

The error message mentions "5 minutes" but the actual timeout on Line 118 is time.Minute*1 (1 minute). The message should state "1 minute" for accuracy.

🔎 Apply this diff to fix the error message:
-		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 5 minutes: %w", err)
+		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 1 minute: %w", err)
♻️ Duplicate comments (1)
pkg/kubernetes/types/nodes.go (1)

12-13: Address the previous review feedback about volume fields.

A past review comment suggested using simpler HostPath and MountPath string fields instead of full Volumes and VolumeMounts arrays, noting that debug pods are host-networked and only need basic host path mounting. The comment recommended both fields should be empty or both set, with defaults to "/" and "/host" respectively.

The current implementation uses the full Kubernetes Volume/VolumeMount types, which appears to diverge from that feedback. Please clarify if this design decision was intentional and addresses the security/simplicity concerns raised.

Based on learnings, a maintainer previously requested this change.

🧹 Nitpick comments (7)
pkg/external_tools/mcp/mcp.go (1)

14-18: Consider adding nil check for the k8sMcpServer parameter.

If k8sMcpServer is nil, calling methods like Tcpdump or Pwru that use s.k8sMcpServer will cause a nil pointer dereference at runtime. Adding defensive validation here would fail fast with a clear error message.

🔎 Suggested fix
 // NewMCPServer creates a new MCP server instance
-func NewMCPServer(k8sMcpServer *kubernetesmcp.MCPServer) *MCPServer {
+func NewMCPServer(k8sMcpServer *kubernetesmcp.MCPServer) (*MCPServer, error) {
+	if k8sMcpServer == nil {
+		return nil, fmt.Errorf("k8sMcpServer cannot be nil")
+	}
 	return &MCPServer{
 		k8sMcpServer: k8sMcpServer,
-	}
+	}, nil
 }
pkg/external_tools/mcp/utils_test.go (1)

212-225: Consider using slices.Equal for cleaner slice comparison.

The manual element-by-element comparison works correctly but can be simplified with the standard library.

🔎 Suggested fix
+import "slices"
+
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			got := tt.builder().build()
-			if len(got) != len(tt.want) {
-				t.Errorf("commandBuilder.build() length = %v, want %v", len(got), len(tt.want))
-				return
-			}
-			for i := range got {
-				if got[i] != tt.want[i] {
-					t.Errorf("commandBuilder.build()[%d] = %v, want %v", i, got[i], tt.want[i])
-				}
+			if !slices.Equal(got, tt.want) {
+				t.Errorf("commandBuilder.build() = %v, want %v", got, tt.want)
 			}
 		})
 	}
pkg/external_tools/mcp/tcpdump.go (1)

75-77: Remove extraneous blank line before closing brace.

Minor style issue: there's an unnecessary blank line at line 76.

🔎 Suggested fix
 	default:
 		return nil, types.CommandResult{}, fmt.Errorf("invalid target_type: %s (must be 'node' or 'pod')", in.TargetType)
-
 	}
pkg/external_tools/mcp/pwru.go (1)

56-61: Consider validating NodeName is not empty.

If in.NodeName is empty, the error from runDebugNode may not be immediately clear. Early validation would provide a better error message.

🔎 Suggested fix
+	if in.NodeName == "" {
+		return nil, types.CommandResult{}, fmt.Errorf("node_name is required")
+	}
+
 	target := types.DebugNodeParams{
 		NodeName:     in.NodeName,
 		NodeImage:    "docker.io/cilium/pwru:v1.0.10",

This would require adding "fmt" to the imports.

pkg/external_tools/mcp/tcpdump_test.go (1)

429-431: Consider the default packet count behavior.

Similar to the pwru test, when packetCount is 0, it defaults to MaxPacketCount. However, Line 443 shows snaplen defaults to DefaultSnaplen (96 bytes), which seems more user-friendly. Consider whether packet count should also default to a reasonable value rather than the maximum.

pkg/external_tools/mcp/utils.go (2)

24-34: Consider including stderr in the command result.

The function returns only output.Stdout (Line 34) but discards output.Stderr. For debugging purposes, stderr often contains valuable diagnostic information. Consider including both in the CommandResult or at least logging stderr when it's non-empty.


38-56: Good validation and defaulting logic.

The namespace defaulting to "default" (Lines 42-44) and validation checks are appropriate. However, similar to runDebugNode, consider including stderr in the output for better debugging support.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb4e39b and 3a31f18.

📒 Files selected for processing (12)
  • cmd/ovnk-mcp-server/main.go (2 hunks)
  • pkg/external_tools/mcp/mcp.go (1 hunks)
  • pkg/external_tools/mcp/pwru.go (1 hunks)
  • pkg/external_tools/mcp/pwru_test.go (1 hunks)
  • pkg/external_tools/mcp/tcpdump.go (1 hunks)
  • pkg/external_tools/mcp/tcpdump_test.go (1 hunks)
  • pkg/external_tools/mcp/utils.go (1 hunks)
  • pkg/external_tools/mcp/utils_test.go (1 hunks)
  • pkg/external_tools/types/manifest.go (1 hunks)
  • pkg/kubernetes/client/nodes.go (4 hunks)
  • pkg/kubernetes/mcp/nodes.go (1 hunks)
  • pkg/kubernetes/types/nodes.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
pkg/external_tools/mcp/tcpdump.go (2)
pkg/external_tools/types/manifest.go (4)
  • TcpdumpParams (24-41)
  • CommandResult (54-58)
  • DebugNodeParams (8-13)
  • ExecPodParams (16-20)
pkg/external_tools/mcp/utils.go (2)
  • ValidateInterface (97-111)
  • ValidateBPFFilter (114-129)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
pkg/external_tools/mcp/tcpdump_test.go (2)
pkg/external_tools/mcp/tcpdump.go (3)
  • MaxPacketCount (13-13)
  • MaxSnaplen (15-15)
  • DefaultSnaplen (14-14)
pkg/external_tools/mcp/utils.go (2)
  • ValidateInterface (97-111)
  • ValidateBPFFilter (114-129)
pkg/external_tools/mcp/utils.go (4)
pkg/external_tools/mcp/mcp.go (1)
  • MCPServer (9-11)
pkg/external_tools/types/manifest.go (3)
  • DebugNodeParams (8-13)
  • CommandResult (54-58)
  • ExecPodParams (16-20)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (8-14)
pkg/kubernetes/types/common.go (1)
  • NamespacedNameParams (37-40)
🔇 Additional comments (21)
pkg/external_tools/mcp/mcp.go (1)

21-59: LGTM! Tool registration with comprehensive descriptions.

The tool descriptions are well-structured with clear parameter documentation, constraints (defaults/max values), and practical examples. The pwru image version is now correctly specified as v1.0.10 (addressing the past review comment about the "latest tag").

cmd/ovnk-mcp-server/main.go (1)

50-53: LGTM! Clean integration following established patterns.

The external tools server is wired in consistently with the other MCP servers (OVS, kernel). The placement after kernel tools and the logging convention match the existing code structure.

pkg/external_tools/mcp/utils_test.go (2)

7-98: LGTM! Comprehensive boundary testing for validateIntMax.

Excellent coverage including zero, minimum valid, boundary (equals max, just over), and negative value cases. The table-driven approach is clean and maintainable.


100-141: LGTM! Good edge case coverage for stringWithDefault.

The whitespace test case (lines 126-130) is particularly valuable—it correctly documents that whitespace-only strings are not treated as empty.

pkg/external_tools/mcp/tcpdump.go (2)

21-27: LGTM! Input validation is properly ordered.

Interface and BPF filter validation using the centralized helpers ensures consistent security checks before any command execution.


61-77: The implementation is correct. The method signature follows the official MCP SDK's generic tool handler pattern (ToolHandlerFor[In, Out]), which returns (*mcp.CallToolResult, Out, error). Returning nil for CallToolResult while using the generic output type (types.CommandResult) as the result carrier is the standard and documented pattern in the official Go SDK. This same pattern is consistently applied across all tool handlers in the codebase (tcpdump, pwru, DebugNode, ExecPod). No changes are needed.

Likely an incorrect or invalid review comment.

pkg/external_tools/mcp/pwru.go (2)

28-30: PcapFilter validation now implemented.

This addresses the past review comment requesting validation of the PcapFilter field before command execution.


35-54: LGTM! Volume configuration for eBPF debugging.

The /sys/kernel/debug mount with HostPathDirectoryOrCreate is correctly configured for pwru's eBPF requirements. The volume naming is clear and the mount path matches the host path appropriately.

pkg/kubernetes/mcp/nodes.go (1)

12-12: LGTM!

The parameter pass-through correctly forwards the new volume configuration fields to the underlying client.

pkg/external_tools/mcp/pwru_test.go (2)

7-257: Excellent test coverage!

The test cases comprehensively cover valid BPF filters, injection attack vectors, and output limit boundaries. The security-focused validation tests provide strong protection against command injection attempts.


272-274: No action needed - implementation is correct.

The default behavior is sound. When outputLimitLines is 0, it defaults to MaxOutputLimitLines, which is defined as 1000. This is a reasonable default, not an edge case to the maximum. The test correctly reflects the implementation in pwru.go.

Likely an incorrect or invalid review comment.

pkg/kubernetes/client/nodes.go (2)

15-17: LGTM!

The signature updates correctly extend both DebugNode and createPod to accept volume configuration, maintaining consistency across the call chain.


39-62: Well-designed defaulting logic.

The nil checks ensure safe defaults (mounting host "/" to "/host"), which aligns well with typical debug pod requirements while still allowing custom volume configurations when needed.

pkg/external_tools/mcp/tcpdump_test.go (1)

7-405: Comprehensive validation test suite.

Excellent coverage of interface validation, BPF filter security checks, and numeric boundary conditions. The injection attack tests provide strong protection against command injection vulnerabilities.

pkg/external_tools/types/manifest.go (2)

11-12: Appropriate use of json:"-" tags.

Excluding Volumes and VolumeMounts from JSON serialization is correct since these are populated internally by the MCP server rather than provided by users in the API request.


37-50: jsonschema max values are correctly aligned with implementation constants.

Verified that all three max values in the jsonschema annotations match their corresponding implementation constants:

  • packet_count max 1000 matches MaxPacketCount in tcpdump.go
  • snaplen max 262 matches MaxSnaplen in tcpdump.go
  • output_limit_lines max 1000 matches MaxOutputLimitLines in pwru.go
pkg/external_tools/mcp/utils.go (5)

59-94: Well-designed command builder.

The fluent interface provides a clean, readable way to construct commands with conditional arguments. The pattern helps prevent common mistakes in command construction.


96-111: Solid interface name validation.

The 15-character limit matches Linux IFNAMSIZ and the pattern correctly enforces safe interface naming conventions while preventing injection attacks.


113-129: Robust BPF filter validation.

The validation effectively prevents command injection by blocking dangerous shell metacharacters. The comprehensive list (semicolon, pipe, ampersand, backtick, dollar, command substitution, newline, null byte) provides strong security.

Note: The list includes both "$" and "$(" separately, which provides defense in depth but is slightly redundant since "$" alone would catch both patterns.


131-143: Clean validation helper.

The function provides clear error messages and handles both unit-based and unitless numeric validation appropriately.


145-151: LGTM!

Simple, readable helper for string defaulting that improves command construction clarity.

Copy link
Contributor

@tssurya tssurya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testing approval won't merge this yet.

Copy link
Contributor

@tssurya tssurya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice test works, removing approval.

@Meina-rh Meina-rh requested a review from a team December 19, 2025 03:08
Copy link
Contributor

@arkadeepsen arkadeepsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .idea directory again got added. It's better to add .idea/ in the .gitignore file so that the directory is ignored while pushing the commits.

Check the comments by @coderabbitai as well and try to incorporate the suggestions.

- bpf_filter: BPF filter expression (optional)
- snaplen: Snapshot length in bytes (default: 96, max: 262)
- interface: Network interface name or 'any' (optional, uses default if not specified)
- target_type: 'node' or 'pod' (optional, uses default interface if not specified)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The target type field is not optional. The validation checks whether the value is set to either node or pod, otherwise an error is returned.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, I deleted optional. And it will check the value if it's set to node or pod in tcpdump.go switch in.TargetType

Comment on lines 410 to 448
err := ValidateInterface(tt.iface)
if err != nil {
if !tt.wantError {
t.Errorf("ValidateInterface(%q) error = %v, wantError %v", tt.iface, err, tt.wantError)
}
return
}

// Validate BPF filter
err = ValidateBPFFilter(tt.filter)
if err != nil {
if !tt.wantError {
t.Errorf("ValidateBPFFilter(%q) error = %v, wantError %v", tt.filter, err, tt.wantError)
}
return
}

// Validate packet count
packetCount := tt.packetCount
if packetCount == 0 {
packetCount = MaxPacketCount
}
err = validateIntMax(packetCount, MaxPacketCount, "packet_count", "")
if err != nil {
if !tt.wantError {
t.Errorf("validateIntMax(packetCount=%d) error = %v, wantError %v", tt.packetCount, err, tt.wantError)
}
return
}

// Validate snaplen
snaplen := tt.snaplen
if snaplen == 0 {
snaplen = DefaultSnaplen
}
err = validateIntMax(snaplen, MaxSnaplen, "snaplen", "bytes")
if (err != nil) != tt.wantError {
t.Errorf("validateIntMax(snaplen=%d) error = %v, wantError %v", tt.snaplen, err, tt.wantError)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just test all the validation functions in the utils_test.go file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arkadeepsen I've removed all the validation functions in utils_test.go. But seems like pwru and tcpdump don't need extra unit tests after removing. I just left some comments in pwru_test.go and tcpdump_test.go

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the files altogether if there are no tests in them.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pkg/kubernetes/client/nodes.go (2)

90-96: Hardcoded HOST environment variable may conflict with custom mountPath.

The HOST environment variable is hardcoded to "/host", but the actual mount path is now configurable. If a caller sets a different mountPath, tools relying on the HOST env var (like sosreport) may look in the wrong location.

🔎 Proposed fix: Use mountPath for HOST env var
 Env: []corev1.EnvVar{
     {
         // to collect more sos report requires this env var is set
         Name:  "HOST",
-        Value: "/host",
+        Value: mountPath,
     },
 },

126-126: Error message mentions 5 minutes but timeout is 1 minute.

The error message says "within timeout of 5 minutes" but Line 116 sets timeout to time.Minute*1.

🔎 Proposed fix
-return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 5 minutes: %w", err)
+return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 1 minute: %w", err)
🧹 Nitpick comments (3)
pkg/external_tools/mcp/utils_test.go (1)

499-582: Command builder tests cover key scenarios.

Tests verify the fluent API including base args, chaining, conditional adds, and addIfNotEmpty. Consider using reflect.DeepEqual or slices.Equal for slice comparison instead of manual loop.

🔎 Optional improvement using slices.Equal
+import "slices"
+
 for _, tt := range tests {
     t.Run(tt.name, func(t *testing.T) {
         got := tt.builder().build()
-        if len(got) != len(tt.want) {
-            t.Errorf("commandBuilder.build() length = %v, want %v", len(got), len(tt.want))
-            return
-        }
-        for i := range got {
-            if got[i] != tt.want[i] {
-                t.Errorf("commandBuilder.build()[%d] = %v, want %v", i, got[i], tt.want[i])
-            }
-        }
+        if !slices.Equal(got, tt.want) {
+            t.Errorf("commandBuilder.build() = %v, want %v", got, tt.want)
+        }
     })
 }
pkg/external_tools/mcp/pwru.go (1)

32-33: Clarify addIfNotEmpty usage.

The call addIfNotEmpty(in.PcapFilter, in.PcapFilter) works but is not immediately intuitive. Consider adding a brief comment or using a more explicit pattern.

🔎 Optional: More explicit approach
 cmd := newCommand("pwru", "--output-limit-lines", strconv.Itoa(outputLimitLines))
-cmd.addIfNotEmpty(in.PcapFilter, in.PcapFilter)
+if in.PcapFilter != "" {
+    cmd.add(in.PcapFilter)
+}
pkg/external_tools/mcp/utils.go (1)

113-129: Packet filter validation provides command injection protection.

The blocklist approach covers common shell injection vectors. Consider whether additional patterns like >, <, or ' (single quotes) should be blocked.

🔎 Consider expanding dangerous patterns
-dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00"}
+dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00", ">", "<", "'", "\"", "\\"}

This would block output redirection (>, <), quote escapes (', "), and backslash escapes that could be used in injection attempts.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 221fa1d and 0a18efd.

📒 Files selected for processing (12)
  • cmd/ovnk-mcp-server/main.go
  • pkg/external_tools/mcp/mcp.go
  • pkg/external_tools/mcp/pwru.go
  • pkg/external_tools/mcp/pwru_test.go
  • pkg/external_tools/mcp/tcpdump.go
  • pkg/external_tools/mcp/tcpdump_test.go
  • pkg/external_tools/mcp/utils.go
  • pkg/external_tools/mcp/utils_test.go
  • pkg/external_tools/types/manifest.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/external_tools/mcp/pwru_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/kubernetes/types/nodes.go
  • pkg/external_tools/mcp/tcpdump_test.go
  • pkg/external_tools/mcp/tcpdump.go
🧰 Additional context used
🧬 Code graph analysis (3)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
pkg/external_tools/mcp/utils.go (4)
pkg/external_tools/mcp/mcp.go (1)
  • MCPServer (9-11)
pkg/external_tools/types/manifest.go (3)
  • DebugNodeParams (4-9)
  • CommandResult (50-54)
  • ExecPodParams (12-16)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
pkg/kubernetes/types/common.go (1)
  • NamespacedNameParams (37-40)
cmd/ovnk-mcp-server/main.go (5)
pkg/external_tools/mcp/mcp.go (1)
  • NewMCPServer (14-18)
pkg/kernel/mcp/mcp.go (1)
  • NewMCPServer (18-22)
pkg/ovs/mcp/mcp.go (1)
  • NewMCPServer (20-24)
pkg/sosreport/mcp/mcp.go (1)
  • NewMCPServer (15-17)
pkg/kubernetes/mcp/mcp.go (1)
  • NewMCPServer (19-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (25)
pkg/external_tools/mcp/utils_test.go (4)

7-98: Well-structured boundary tests for validateIntMax.

Good coverage of zero, minimum valid, boundary, over-limit, and negative value cases with clear test naming.


143-296: Comprehensive interface validation tests.

Excellent coverage of valid patterns (eth0, VLAN interfaces, hyphens, underscores) and security-critical invalid patterns (injection characters, length limits, invalid starting characters).


298-497: Thorough packet filter validation tests with injection prevention.

Good coverage of command injection attempts (semicolon, pipe, backtick, dollar sign, command substitution) and length validation. The test logic at lines 483-488 correctly handles the length test cases by replacing null bytes with valid characters.


1-5: The package declaration is consistent with all other files in the pkg/external_tools/mcp/ directory. All files in this directory declare package external_tools, not mcp. The directory structure and package naming follow standard Go conventions where the package name doesn't need to match the directory name.

Likely an incorrect or invalid review comment.

cmd/ovnk-mcp-server/main.go (2)

14-14: New import follows existing conventions.

The import alias externaltoolsmcp is consistent with other MCP package imports in this file.


50-53: External tools server integration follows established pattern.

The initialization and tool registration mirrors the pattern used for kernel and OVS servers. Clean integration into the live-cluster flow.

pkg/kubernetes/mcp/nodes.go (1)

11-12: Parameter forwarding correctly implemented.

The HostPath and MountPath fields from DebugNodeParams are now correctly passed to the client's DebugNode method.

pkg/external_tools/mcp/mcp.go (3)

13-18: Constructor follows established pattern.

The NewMCPServer function matches the pattern used by kernel and OVS MCP servers (accepts *kubernetesmcp.MCPServer and stores it).


21-67: Tool descriptions are comprehensive and well-documented.

The tool descriptions include clear parameter documentation with types, defaults, and constraints, plus practical examples. The pwru image is correctly pinned to v1.0.10 rather than latest.


1-6: No changes needed. The package name external_tools is correct and follows the established codebase convention where packages in subdirectories are named after their parent directory (matching kernel, kubernetes, and other top-level package names). Only ovs/mcp uses mcp as an exception.

Likely an incorrect or invalid review comment.

pkg/external_tools/mcp/pwru.go (3)

11-14: Sensible defaults and limits for output.

Default of 100 lines and max of 1000 are reasonable constraints for trace output.


19-30: Input validation properly implemented.

The method validates OutputLimitLines against the maximum and validates PcapFilter for dangerous characters. This addresses the previous review comment about validating PcapFilter before use.


35-49: Debug node setup with eBPF mounts looks correct.

The /sys/kernel/debug mount is required for eBPF operations, and the pinned pwru image version ensures reproducibility.

pkg/kubernetes/client/nodes.go (2)

15-17: Extended signature correctly propagates new parameters.

The DebugNode method now accepts and forwards hostPath and mountPath to createPod.


39-45: Sensible defaults for path parameters.

Defaulting hostPath to "/" and mountPath to "/host" maintains backward compatibility while allowing customization.

pkg/external_tools/mcp/utils.go (6)

14-14: Regex compiled at package level for performance.

Good practice to compile the regex once rather than on each validation call.


16-35: runDebugNode validates required parameters and delegates correctly.

Properly checks for NodeName and NodeImage before delegating to the Kubernetes MCP server with structured parameters.


37-57: runExecPod handles namespace defaulting properly.

Good defensive coding to default PodNamespace to "default" if empty.


59-94: Fluent command builder API is clean and reusable.

The builder pattern with add, addIf, and addIfNotEmpty provides a clear API for constructing commands.


96-111: Interface validation covers Linux naming constraints.

Correctly validates length (max 15 chars per IFNAMSIZ), allows "any" keyword, and uses regex to enforce valid characters while blocking dangerous ones.


131-151: Utility functions are straightforward and correct.

Both validateIntMax and stringWithDefault are simple helpers with clear semantics.

pkg/external_tools/types/manifest.go (4)

18-37: Well-structured multi-target design.

The TcpdumpParams structure effectively handles both node-level and pod-level packet capture scenarios through the TargetType discriminator. The separation of target-specific fields with appropriate omitempty tags for optional parameters is clean and maintainable.


39-47: LGTM: Clean parameter structure.

The PwruParams structure is appropriately scoped for the pwru tool with a required NodeName field and optional filtering/output parameters. The documentation clearly explains the tool's purpose.


49-54: LGTM: Standard result structure.

The CommandResult structure follows conventional patterns for representing command execution outcomes with output, exit code, and error information.


4-16: Address code duplication between k8s layer and external_tools type definitions.

These types are redefined in both locations but with significant differences:

  • DebugNodeParams: k8s version (pkg/kubernetes/types/nodes.go:4) includes Command field and uses "hostPath,omitempty" JSON tags, while this version omits Command and uses json:"-" tags. Field names also differ (Name/Image vs NodeName/NodeImage).
  • ExecPodParams: k8s version (pkg/kubernetes/types/pods.go:17) includes Command field and embeds NamespacedNameParams, while this version defines fields explicitly as PodName/PodNamespace and lacks Command.

This duplication creates a maintenance burden and risks divergence. Either consolidate to use the k8s layer types (adjusting if needed), or document why separate definitions are necessary and ensure both stay synchronized.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Fix all issues with AI Agents 🤖
In @pkg/external_tools/mcp/utils.go:
- Line 17: The function signature for runDebugNode currently returns an unused
first value (*mcp.CallToolResult) that is always nil; remove this unused return
from runDebugNode (and any sibling functions with the same pattern on lines
~25-29) so the signature returns only (types.CommandResult, error), update all
callers to expect the new two-value return, and adjust any documentation/tests
to reflect the changed return type; if there is intended logic to populate
*mcp.CallToolResult instead, implement that population in runDebugNode rather
than keeping a consistently nil return.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/kubernetes/client/nodes.go (1)

90-95: Potential inconsistency: HOST env var hardcoded while mountPath is configurable.

The HOST environment variable is hardcoded to /host (line 94), but the actual mount path can now be configured via the mountPath parameter (line 87). If a caller sets a custom mountPath, tools relying on the HOST env var may reference an incorrect path.

Consider using the mountPath variable for the env var value to maintain consistency:

🔎 Suggested fix
 					Env: []corev1.EnvVar{
 						{
 							// to collect more sos report requires this env var is set
 							Name:  "HOST",
-							Value: "/host",
+							Value: mountPath,
 						},
 					},
♻️ Duplicate comments (1)
pkg/external_tools/mcp/utils.go (1)

33-33: Unused first return value in function signature.

Similar to runDebugNode, this function always returns nil as the first value. The same design issue applies here - either populate the value or remove it from the signature to avoid confusion.

🔎 Suggested fix: Remove unused return value
-func (s *MCPServer) runExecPod(ctx context.Context, req *mcp.CallToolRequest, target k8stypes.ExecPodParams, command []string) (*mcp.CallToolResult, types.CommandResult, error) {
+func (s *MCPServer) runExecPod(ctx context.Context, req *mcp.CallToolRequest, target k8stypes.ExecPodParams, command []string) (types.CommandResult, error) {
 	if target.NamespacedNameParams.Name == "" {
-		return nil, types.CommandResult{}, fmt.Errorf("pod_name is required when target_type is 'pod'")
+		return types.CommandResult{}, fmt.Errorf("pod_name is required when target_type is 'pod'")
 	}
 	if target.NamespacedNameParams.Namespace == "" {
 		target.NamespacedNameParams.Namespace = "default"
 	}
 	target.Command = command
 	_, output, err := s.k8sMcpServer.ExecPod(ctx, req, target)
 	if err != nil {
-		return nil, types.CommandResult{}, err
+		return types.CommandResult{}, err
 	}
-	return nil, types.CommandResult{Output: output.Stdout}, nil
+	return types.CommandResult{Output: output.Stdout}, nil
 }

Also applies to: 41-45

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a18efd and 45bfd77.

📒 Files selected for processing (12)
  • cmd/ovnk-mcp-server/main.go
  • pkg/external_tools/mcp/mcp.go
  • pkg/external_tools/mcp/pwru.go
  • pkg/external_tools/mcp/pwru_test.go
  • pkg/external_tools/mcp/tcpdump.go
  • pkg/external_tools/mcp/tcpdump_test.go
  • pkg/external_tools/mcp/utils.go
  • pkg/external_tools/mcp/utils_test.go
  • pkg/external_tools/types/manifest.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
✅ Files skipped from review due to trivial changes (1)
  • pkg/external_tools/mcp/pwru_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • cmd/ovnk-mcp-server/main.go
  • pkg/external_tools/mcp/tcpdump_test.go
  • pkg/kubernetes/types/nodes.go
  • pkg/external_tools/mcp/tcpdump.go
  • pkg/kubernetes/mcp/nodes.go
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
pkg/external_tools/mcp/pwru.go (3)
pkg/external_tools/mcp/mcp.go (1)
  • MCPServer (9-11)
pkg/external_tools/types/manifest.go (2)
  • PwruParams (26-32)
  • CommandResult (35-39)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (14)
pkg/kubernetes/client/nodes.go (1)

39-45: LGTM! Clear defaulting logic for mount parameters.

The defaulting logic ensures safe fallback values when hostPath or mountPath are not provided. The choice of "/" for hostPath (entire host filesystem) and "/host" for mountPath (conventional mount point) are sensible defaults for debug containers.

pkg/external_tools/mcp/mcp.go (3)

8-18: LGTM! Clean wrapper design.

The MCPServer wrapper provides a clean abstraction around the Kubernetes MCP server, with a straightforward constructor pattern.


20-46: Comprehensive tcpdump tool description with clear examples.

The tool description effectively documents parameters, requirements, and usage patterns for both node-level and pod-level packet capture scenarios.


47-68: The cilium/pwru:v1.0.10 image version is verified as current and secure. This is the latest stable release with no known pwru-specific CVEs or security advisories. Docker images are actively maintained and published to docker.io/cilium/pwru. Note that kernel-level eBPF vulnerabilities (e.g., CVE-2025-37959) may affect pwru behavior, but these are infrastructure concerns requiring kernel or distro patches—not pwru issues.

pkg/external_tools/mcp/utils_test.go (1)

1-582: Excellent test coverage with strong security focus.

This test suite demonstrates thorough validation testing:

  • Comprehensive boundary and edge case coverage for integer validation
  • Extensive interface name validation preventing command injection
  • Strong packet filter validation with explicit injection attack scenarios
  • Clean command builder API tests

The security-conscious approach (testing against semicolons, pipes, backticks, dollar signs, etc.) is particularly commendable for network tool wrappers.

pkg/external_tools/mcp/pwru.go (3)

12-15: LGTM! Constants align with tool description.

The default (100) and maximum (1000) output line limits match the descriptions in pkg/external_tools/mcp/mcp.go (line 61).


20-31: Solid validation with proper defaulting.

The method correctly:

  • Defaults outputLimitLines to 100 when zero
  • Validates against the maximum limit of 1000
  • Validates the PcapFilter for safety (preventing injection attacks)

36-41: Correct mount configuration for eBPF tracing.

The configuration appropriately:

  • Mounts /sys/kernel/debug (required for eBPF programs to access kernel debug filesystem)
  • Uses the same path for both HostPath and MountPath (direct access to kernel debug fs)
  • Specifies the pwru image version explicitly
pkg/external_tools/types/manifest.go (1)

1-39: LGTM! Well-structured type definitions.

The parameter and result types are cleanly designed:

  • Appropriate use of omitempty for optional fields
  • Required fields (e.g., NodeName in PwruParams, TargetType in TcpdumpParams) correctly lack omitempty
  • Field types match their intended usage
  • Clear separation of concerns between tcpdump, pwru, and result types
pkg/external_tools/mcp/utils.go (5)

48-83: LGTM! Clean fluent interface for command building.

The commandBuilder provides a clear, fluent API for constructing command arguments with conditional logic. The implementation is straightforward and will help maintain readable code when building complex commands for tcpdump and pwru.


85-100: LGTM! Proper interface name validation.

The validation correctly enforces Linux interface name constraints (15 character limit, valid character set) and appropriately handles the special "any" value used by packet capture tools.


120-132: LGTM! Clear validation with descriptive errors.

The function provides appropriate bounds checking with context-aware error messages that will help users understand validation failures.


134-140: LGTM! Simple utility function.

Clear helper for handling default values.


102-118: The code already implements proper defense-in-depth for packet filter handling.

The validatePacketFilter blacklist correctly rejects shell metacharacters. Additionally, both tcpdump.go (line 49) and pwru.go (line 34) pass filters as separate command arguments via the commandBuilder pattern rather than shell string concatenation. This means filters are processed by exec.Command-like mechanisms without shell interpretation, providing a second layer of defense. No changes needed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
pkg/external_tools/mcp/pwru_test.go (1)

1-9: Consider removing this empty test file or adding Pwru-specific integration tests.

While the comment explains that validation functions are tested in utils_test.go, having an empty test file serves no purpose. Consider either:

  1. Removing this file entirely (recommended)
  2. Adding integration tests specific to the Pwru method (e.g., testing the complete flow, command construction, or edge cases specific to pwru behavior)
pkg/external_tools/mcp/tcpdump.go (1)

20-86: Unused first return value in function signature.

The function always returns nil for *mcp.CallToolResult (lines 70, 82). This pattern was already addressed in runDebugNode and runExecPod (per past review comments), which now return only two values. For consistency and API clarity, consider removing the unused return value from the Tcpdump signature as well.

🔎 Proposed signature update
-func (s *MCPServer) Tcpdump(ctx context.Context, req *mcp.CallToolRequest, in types.TcpdumpParams) (*mcp.CallToolResult, types.CommandResult, error) {
+func (s *MCPServer) Tcpdump(ctx context.Context, req *mcp.CallToolRequest, in types.TcpdumpParams) (types.CommandResult, error) {

Then update return statements:

 	case "node":
 		result, err := s.runDebugNode(ctx, req, k8stypes.DebugNodeParams{
 			Name:  in.NodeName,
 			Image: in.NodeImage,
 		}, cmd.build())
 		if err != nil {
-			return nil, types.CommandResult{}, err
+			return types.CommandResult{}, err
 		}
-		return nil, result, nil
+		return result, nil
 	case "pod":
 		result, err := s.runExecPod(ctx, req, k8stypes.ExecPodParams{
 			NamespacedNameParams: k8stypes.NamespacedNameParams{
 				Name:      in.PodName,
 				Namespace: in.PodNamespace,
 			},
 			Container: in.ContainerName,
 		}, cmd.build())
 		if err != nil {
-			return nil, types.CommandResult{}, err
+			return types.CommandResult{}, err
 		}
-		return nil, result, nil
+		return result, nil
 	default:
-		return nil, types.CommandResult{}, fmt.Errorf("invalid target_type: %s (must be 'node' or 'pod')", in.TargetType)
+		return types.CommandResult{}, fmt.Errorf("invalid target_type: %s (must be 'node' or 'pod')", in.TargetType)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45bfd77 and 16e0cbf.

📒 Files selected for processing (12)
  • cmd/ovnk-mcp-server/main.go
  • pkg/external_tools/mcp/mcp.go
  • pkg/external_tools/mcp/pwru.go
  • pkg/external_tools/mcp/pwru_test.go
  • pkg/external_tools/mcp/tcpdump.go
  • pkg/external_tools/mcp/tcpdump_test.go
  • pkg/external_tools/mcp/utils.go
  • pkg/external_tools/mcp/utils_test.go
  • pkg/external_tools/types/manifest.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/kubernetes/types/nodes.go
  • pkg/external_tools/mcp/utils_test.go
  • pkg/external_tools/mcp/tcpdump_test.go
  • pkg/kubernetes/client/nodes.go
🧰 Additional context used
🧬 Code graph analysis (2)
cmd/ovnk-mcp-server/main.go (5)
pkg/external_tools/mcp/mcp.go (1)
  • NewMCPServer (14-18)
pkg/kernel/mcp/mcp.go (1)
  • NewMCPServer (18-22)
pkg/ovs/mcp/mcp.go (1)
  • NewMCPServer (20-24)
pkg/sosreport/mcp/mcp.go (1)
  • NewMCPServer (15-17)
pkg/kubernetes/mcp/mcp.go (1)
  • NewMCPServer (19-42)
pkg/external_tools/mcp/pwru.go (3)
pkg/external_tools/mcp/mcp.go (1)
  • MCPServer (9-11)
pkg/external_tools/types/manifest.go (2)
  • PwruParams (26-32)
  • CommandResult (35-39)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
🔇 Additional comments (17)
cmd/ovnk-mcp-server/main.go (1)

14-14: LGTM! Clean integration following established patterns.

The external tools server integration follows the same pattern as the existing Kernel and OVS MCP servers, maintaining consistency across the codebase.

Also applies to: 51-53

pkg/kubernetes/mcp/nodes.go (1)

12-12: LGTM! Proper parameter propagation.

The addition of hostPath and mountPath parameters correctly propagates the new configuration options from the input to the client method, enabling flexible volume mounting for debug operations.

pkg/external_tools/mcp/mcp.go (2)

13-18: LGTM! Constructor follows established pattern.

The constructor correctly follows the same pattern used by Kernel, OVS, and other MCP servers in the codebase, maintaining consistency.


20-68: LGTM! Comprehensive tool registration.

The tool registration provides clear, detailed descriptions with examples for both tcpdump and pwru. The documentation properly distinguishes required from optional parameters and provides practical usage examples.

pkg/external_tools/mcp/pwru.go (4)

21-27: LGTM! Proper validation with sensible defaults.

The output limit validation correctly defaults to 100 and enforces a maximum of 1000, providing reasonable bounds for trace output.


29-31: LGTM! Filter validation added.

The PcapFilter validation correctly addresses the past review comment requesting validation before using the filter.


36-41: LGTM! Appropriate debug configuration for eBPF tracing.

The DebugNodeParams configuration is correct:

  • Uses a specific, versioned pwru image
  • Mounts /sys/kernel/debug which is required for eBPF-based packet tracing

33-34: The code at line 34 is correct; no changes needed.

The addIfNotEmpty method signature is func (cb *commandBuilder) addIfNotEmpty(value string, args ...string), which checks if value is non-empty and appends args... to the command if so. The call cmd.addIfNotEmpty(in.PcapFilter, in.PcapFilter) correctly adds the PcapFilter value if non-empty, following the same pattern used elsewhere in the codebase (e.g., cmd.addIfNotEmpty(in.BPFFilter, in.BPFFilter) in tcpdump.go and cmd.addIfNotEmpty(in.Options, in.Options) in ip.go).

pkg/external_tools/mcp/tcpdump.go (4)

13-18: LGTM!

The constants are well-chosen: default packet count of 100 and snaplen of 96 bytes are reasonable for typical captures, while the maximums prevent excessive resource usage.


22-43: LGTM!

The validation logic is thorough and well-structured: input validation occurs first, sensible defaults are applied for zero values, and bounds checking prevents abuse. Error propagation is clean.


45-59: LGTM!

The command construction correctly follows tcpdump syntax: the interface flag pair (-i <interface>) and bare BPF filter are added conditionally, and output format handling appropriately selects verbose mode for text or stdout redirection for pcap.


61-85: LGTM!

The dispatch logic correctly routes execution to node or pod targets with appropriate parameter mapping. Error handling is consistent, and the default case provides a clear error message for invalid target types.

pkg/external_tools/types/manifest.go (1)

1-39: LGTM!

The type definitions are clean and well-structured: TcpdumpParams flexibly supports both node and pod targets, PwruParams focuses on node-level kernel tracing, and CommandResult appropriately captures execution results. JSON tags and omitempty usage are correct.

pkg/external_tools/mcp/utils.go (4)

16-46: LGTM!

The execution helpers are well-implemented: runDebugNode validates required node parameters, runExecPod sensibly defaults namespace to "default" when unspecified, and both provide clear error messages with clean delegation to the Kubernetes layer.


48-83: LGTM!

The commandBuilder provides a clean fluent interface for constructing command arguments: the conditional methods (addIf, addIfNotEmpty) reduce boilerplate, and the unexported type is appropriately scoped for internal use.


85-118: LGTM!

The validation functions provide solid security boundaries: validateInterface correctly enforces the 15-character Linux interface name limit and pattern matching, while validatePacketFilter defensively blocks shell metacharacters. The length limits prevent resource abuse.


120-140: LGTM!

The utility functions are straightforward and correct: validateIntMax provides clear bounds checking with optional unit formatting, and stringWithDefault handles empty-string defaulting cleanly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/kubernetes/client/nodes.go (1)

121-132: Timeout message is inconsistent with actual timeout value.

The error message states "5 minutes" but PollUntilContextTimeout is configured with time.Minute*1 (1 minute).

🐛 Proposed fix
 	if err != nil {
 		cleanupPod()
-		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 5 minutes: %w", err)
+		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 1 minute: %w", err)
 	}
♻️ Duplicate comments (1)
pkg/kubernetes/mcp/nodes.go (1)

35-43: Character whitelist may reject legitimate Kubernetes paths.

The whitelist allows only a-z, A-Z, 0-9, /, -, _, . but Kubernetes volume paths can contain ~ (e.g., kubernetes.io~secret used in projected volumes). Consider adding ~ to the allowed characters.

♻️ Proposed fix
 	// Check for dangerous characters (null bytes, control characters, shell special characters)
 	for i, r := range path {
-		// Allow alphanumeric, /, -, _, .
+		// Allow alphanumeric, /, -, _, ., ~
 		if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') ||
-			r == '/' || r == '-' || r == '_' || r == '.' {
+			r == '/' || r == '-' || r == '_' || r == '.' || r == '~' {
 			continue
 		}
 		return fmt.Errorf("%s contains unsafe character at position %d: %c (U+%04X)", pathType, i, r, r)
 	}
🧹 Nitpick comments (1)
pkg/nettools/mcp/utils_test.go (1)

468-497: Consider simplifying the length validation test cases.

The workaround at lines 483-490 replaces null bytes with valid characters, but this adds unnecessary complexity. The past review suggested a cleaner approach that directly defines valid character strings in the test cases.

♻️ Proposed simplification
 		// Invalid filters - length validation
 		{
 			name:      "filter at max length (1024 chars)",
-			filter:    string(make([]byte, 1024)),
+			filter:    strings.Repeat("a", 1024),
 			wantError: false,
 		},
 		{
 			name:      "filter exceeds max length (1025 chars)",
-			filter:    string(make([]byte, 1025)),
+			filter:    strings.Repeat("a", 1025),
 			wantError: true,
 		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			// For the length tests, fill with valid characters
-			filter := tt.filter
-			if len(filter) >= 1024 {
-				validFilter := make([]byte, len(filter))
-				for i := range validFilter {
-					validFilter[i] = 'a'
-				}
-				filter = string(validFilter)
-			}
-
-			err := validatePacketFilter(filter)
+			err := validatePacketFilter(tt.filter)

Note: You'll need to add "strings" to the imports.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf11863 and 92aea73.

📒 Files selected for processing (10)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/pwru.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/tcpdump.go
  • cmd/ovnk-mcp-server/main.go
  • pkg/nettools/types/manifest.go
  • pkg/nettools/mcp/pwru.go
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
pkg/kubernetes/mcp/nodes.go (3)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-12)
pkg/kubernetes/mcp/mcp.go (1)
  • MCPServer (15-17)
pkg/kubernetes/types/nodes.go (2)
  • DebugNodeParams (4-10)
  • DebugNodeResult (13-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (11)
pkg/kubernetes/types/nodes.go (1)

3-10: LGTM!

The struct properly defines the new optional HostPath and MountPath fields with appropriate omitempty JSON tags. Path validation is correctly delegated to the MCP layer (pkg/kubernetes/mcp/nodes.go), keeping the type definition clean.

pkg/kubernetes/mcp/nodes.go (1)

48-65: LGTM!

The validation flow correctly validates both paths before invoking the client. Error handling properly returns early on validation failures.

pkg/kubernetes/client/nodes.go (3)

15-33: LGTM!

The DebugNode method correctly delegates to createPod with the new path parameters and properly handles cleanup via defer.


35-56: LGTM!

The defaulting logic and conditional HOST environment variable handling are correctly implemented. The HOST env var now properly uses the dynamic mountPath value as suggested in prior reviews.


58-104: LGTM!

The pod specification correctly uses the dynamic hostPath and mountPath values for volume configuration. The privileged debug pod setup with host network, PID, and IPC namespaces is appropriate for node debugging purposes.

pkg/nettools/mcp/utils_test.go (4)

8-99: LGTM!

Good boundary testing for validateIntMax covering zero, positive values at/near max, exceeds max, and negative values.


101-142: LGTM!

Proper coverage of the default value fallback behavior, including the edge case where whitespace is treated as a non-empty value.


144-297: LGTM!

Comprehensive test coverage for interface name validation including the Linux IFNAMSIZ limit (15 chars) and shell metacharacter injection prevention.


500-577: LGTM!

Good coverage of the fluent command builder API including conditional additions and method chaining.

pkg/nettools/mcp/mcp.go (2)

8-20: LGTM!

Clean struct design wrapping the Kubernetes MCP server with the node image configuration.


24-51: LGTM!

Comprehensive tool description with clear parameter documentation and practical examples for node-level and pod-level captures.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/kubernetes/client/nodes.go (1)

120-132: Fix inconsistent timeout value in error message.

The error message states "5 minutes" but the actual timeout is 1 minute (time.Minute*1).

🐛 Proposed fix
 	if err != nil {
 		cleanupPod()
-		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 5 minutes: %w", err)
+		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 1 minute: %w", err)
 	}
♻️ Duplicate comments (3)
pkg/kubernetes/mcp/nodes.go (1)

35-43: Character allowlist should include ~ to allow legitimate kubelet paths.

Kubernetes kubelet uses ~ as a separator in volume plugin directory names (e.g., kubernetes.io~secret, kubernetes.io~empty-dir). The current allowlist rejects this character, which would fail validation if paths containing these standard kubelet patterns are passed to validatePath.

Proposed fix
 	for i, r := range path {
-		// Allow alphanumeric, /, -, _, .
+		// Allow alphanumeric, /, -, _, ., ~
 		if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') ||
-			r == '/' || r == '-' || r == '_' || r == '.' {
+			r == '/' || r == '-' || r == '_' || r == '.' || r == '~' {
 			continue
 		}
pkg/nettools/mcp/mcp.go (1)

52-75: Image configuration conflict between tcpdump and pwru remains unresolved.

This concern was raised in a previous review: tcpdump requires nicolaka/netshoot:v0.13 while pwru requires docker.io/cilium/pwru:v1.0.10. With a single nodeImage field, users cannot run both tools with their correct images in the same session.

Consider:

  1. Separate image fields (tcpdumpImage, pwruImage) in MCPServer
  2. Per-tool image parameters in the tool input
  3. Documentation clarifying this limitation
pkg/nettools/mcp/tcpdump.go (1)

55-63: Binary pcap output will be corrupted when stored in string field.

When outputFormat is "pcap", tcpdump -w - outputs raw binary pcap data. Storing this in CommandResult.Output (a string field) will corrupt the binary data due to UTF-8 encoding/decoding.

Consider:

  1. Base64-encode the pcap output and document the encoding
  2. Use MCP's blob/binary content mechanism
  3. Temporarily restrict to text output until binary handling is implemented
🧹 Nitpick comments (3)
cmd/ovnk-mcp-server/main.go (1)

144-144: Consider documenting that --node-image is required for network tools.

The empty default means the pwru tool will fail at runtime if not provided. This is acceptable for flexibility, but adding a note in the help text like "Container image for node operations (required for pwru)" would improve discoverability.

pkg/nettools/mcp/utils_test.go (1)

468-490: Simplify the length validation tests by using valid characters directly.

The test cases at lines 469-476 use make([]byte, N) which creates null bytes, then lines 483-490 replace the filter with 'a' characters. This indirection makes the test intent less clear. Consider defining the test data with valid characters directly.

Suggested simplification
 		// Invalid filters - length validation
 		{
 			name:      "filter at max length (1024 chars)",
-			filter:    string(make([]byte, 1024)),
+			filter:    strings.Repeat("a", 1024),
 			wantError: false,
 		},
 		{
 			name:      "filter exceeds max length (1025 chars)",
-			filter:    string(make([]byte, 1025)),
+			filter:    strings.Repeat("a", 1025),
 			wantError: true,
 		},

Then remove lines 482-490 (the replacement logic).

pkg/nettools/mcp/utils.go (1)

100-116: Good security validation for shell injection prevention.

The blocklist covers critical shell metacharacters. Consider whether > and < should also be blocked to prevent potential redirect injection if the command is ever constructed with shell interpretation.

♻️ Optional: extend dangerous pattern list
-	dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00"}
+	dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00", ">", "<"}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92aea73 and fe5ebe7.

📒 Files selected for processing (10)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/pwru.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/kubernetes/types/nodes.go
🧰 Additional context used
🧬 Code graph analysis (6)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
pkg/nettools/mcp/pwru.go (3)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-12)
pkg/nettools/types/manifest.go (2)
  • PwruParams (31-36)
  • CommandResult (39-43)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
pkg/nettools/mcp/tcpdump.go (5)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-12)
pkg/nettools/types/manifest.go (2)
  • TcpdumpParams (10-27)
  • CommandResult (39-43)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
pkg/kubernetes/types/pods.go (1)
  • ExecPodParams (17-21)
pkg/kubernetes/types/common.go (1)
  • NamespacedNameParams (37-40)
cmd/ovnk-mcp-server/main.go (1)
pkg/nettools/mcp/mcp.go (1)
  • NewMCPServer (15-20)
pkg/nettools/mcp/utils.go (5)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-12)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
pkg/nettools/types/manifest.go (1)
  • CommandResult (39-43)
pkg/kubernetes/types/pods.go (1)
  • ExecPodParams (17-21)
pkg/kubernetes/types/common.go (1)
  • NamespacedNameParams (37-40)
pkg/kubernetes/mcp/nodes.go (1)
pkg/kubernetes/types/nodes.go (2)
  • DebugNodeParams (4-10)
  • DebugNodeResult (13-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (23)
pkg/nettools/types/manifest.go (1)

1-43: LGTM! Well-structured type definitions.

The data structures are cleanly organized with appropriate JSON tags and embedding for shared fields. The separation between TcpdumpParams (supporting both node and pod targets) and PwruParams (node-only) correctly reflects the different capabilities of each tool.

cmd/ovnk-mcp-server/main.go (1)

46-49: LGTM! Network tools server integration follows established patterns.

The wiring correctly follows the same structure as other MCP servers (kernel, OVS). The nodeImage is passed through and validated at tool execution time in pwru.go.

pkg/nettools/mcp/utils_test.go (3)

1-99: LGTM! Comprehensive validation tests with good boundary coverage.

The TestValidateIntMax tests cover all important boundary cases including zero, minimum valid, below max, equals max, just over max, and negative values.


144-297: LGTM! Thorough interface validation testing.

Excellent coverage of valid interface patterns (eth0, lo, br-int, veth, wlan) and security-sensitive invalid inputs (injection characters, length limits, control characters).


500-577: LGTM! Well-designed command builder tests.

The fluent API testing covers all builder methods with clear expectations for chained operations.

pkg/kubernetes/mcp/nodes.go (1)

48-65: LGTM! Path validation properly integrated before debug node execution.

The validation correctly occurs before the client call, and the error handling appropriately returns early with empty results on validation failures.

pkg/nettools/mcp/pwru.go (2)

13-16: LGTM! Sensible defaults and limits for output lines.

The constants provide reasonable bounds: 100 lines default keeps output manageable while 1000 max prevents excessive resource usage.


21-53: LGTM! Comprehensive validation before execution.

The method properly validates all inputs (node image, output limit, BPF filter) before constructing the command. The hardcoded paths for /sys/kernel/debug are appropriate since pwru requires kernel debug filesystem access.

pkg/nettools/mcp/utils.go (7)

17-29: LGTM on validation logic.

The validation correctly ensures required fields are present before delegating to the Kubernetes MCP server. The function appropriately returns only stdout in the CommandResult.


32-44: LGTM!

Good practice defaulting the namespace to "default" when not specified, aligning with Kubernetes conventions.


46-81: LGTM!

Clean fluent builder pattern implementation for command construction. The methods are intuitive and enable readable command building.


83-98: LGTM!

The validation correctly enforces Linux interface naming constraints (max 15 characters, valid character set) and properly handles the special "any" value used by tcpdump.


118-130: LGTM!

The validation correctly enforces both non-negativity and the upper bound, with clear error messages.


132-138: LGTM!

Simple and useful utility for defaulting empty strings.


1-12: All files in pkg/nettools/mcp/ consistently use package nettools, but this deviates from Go convention.

The package name should match the directory name. Either rename the package to package mcp or rename the directory to align with the package semantics. This affects all files in the directory (mcp.go, pwru.go, tcpdump.go, utils.go, utils_test.go), not just utils.go.

pkg/nettools/mcp/mcp.go (2)

8-20: LGTM on struct and constructor.

The MCPServer struct cleanly encapsulates the Kubernetes MCP server dependency and node image configuration.


24-51: LGTM on tcpdump tool registration.

The tool description is comprehensive with clear parameter documentation and practical examples.

pkg/kubernetes/client/nodes.go (3)

15-33: LGTM on DebugNode signature expansion.

The method now properly forwards hostPath and mountPath parameters, enabling configurable volume mounting for different tool requirements.


35-56: LGTM on defaulting and environment variable setup.

The HOST environment variable now correctly uses the actual mountPath value, addressing the previous review feedback.


75-101: LGTM on pod spec configuration.

The volume and mount configuration correctly uses the dynamic hostPath and mountPath variables.

pkg/nettools/mcp/tcpdump.go (3)

13-22: LGTM on constants definition.

The constants are well-documented with clear rationale for the default and maximum values. MaxSnaplen at 1500 (standard Ethernet MTU) is appropriate.


25-47: LGTM on validation logic.

The validation correctly applies defaults and validates bounds. The validateIntMax function handles negative value rejection as confirmed in previous reviews.


65-94: LGTM on target type dispatch.

The dispatch logic correctly validates nodeImage for node targets and delegates validation of other required fields to runDebugNode and runExecPod. The error handling is consistent.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@Meina-rh
Copy link
Contributor Author

@pperiyasamy I think I've resolved all comments. could you help look again? thanks

flag.StringVar(&cfg.Transport, "transport", "stdio", "Transport to use: stdio or http")
flag.StringVar(&cfg.Port, "port", "8080", "Port to use")
flag.StringVar(&cfg.Kubernetes.Kubeconfig, "kubeconfig", "", "Path to the kubeconfig file")
flag.StringVar(&cfg.NodeImage, "node-image", "", "Container image for node operations")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't you need two different container images for tcpdump and pwru commands ? I think this is what you had earlier.
why don't we initialize these images with default ones ? the empty string is passed now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pperiyasamy do you know if LLM model will request these tools at the same time and use the same configuration? I just thought if we describe in detail, LLM model will not set the same images

Copy link
Contributor Author

@Meina-rh Meina-rh Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we initialize these images with default ones ? the empty string is passed now.

I added empty string and set default in tcpdump.go and pwru.go respectively

@pperiyasamy
Copy link
Contributor

@pperiyasamy I think I've resolved all comments. could you help look again? thanks

@Meina-rh can you also address coderabbit's duplicate and nitpick comments posted here ?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/nettools/mcp/mcp.go`:
- Around line 22-74: The tool descriptions in AddTools for the tcpdump and pwru
mcp.Tool entries should explicitly state that the --node-image flag is global
and overrides the per-tool defaults; update the Description string for the
tcpdump tool (Name: "tcpdump") and the pwru tool (Name: "pwru") to add a
sentence like: "The node image can be configured via the --node-image flag
(applies to all tools) or will default to [tool-specific default image]." Ensure
this exact clarification is added without changing any other behavior.
🧹 Nitpick comments (5)
pkg/nettools/mcp/utils_test.go (1)

483-491: Remove redundant filter replacement logic.

Lines 471 and 476 already use strings.Repeat("a", N) to create valid-character filters for length testing. The replacement logic at lines 485-491 is now redundant and can be removed to simplify the test.

♻️ Suggested cleanup
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			// For the length tests, fill with valid characters
-			filter := tt.filter
-			if len(filter) >= 1024 {
-				validFilter := make([]byte, len(filter))
-				for i := range validFilter {
-					validFilter[i] = 'a'
-				}
-				filter = string(validFilter)
-			}
-
-			err := validatePacketFilter(filter)
+			err := validatePacketFilter(tt.filter)
 			if (err != nil) != tt.wantError {
-				t.Errorf("validatePacketFilter(%q) error = %v, wantError %v", filter, err, tt.wantError)
+				t.Errorf("validatePacketFilter(%q) error = %v, wantError %v", tt.filter, err, tt.wantError)
 			}
 		})
 	}
pkg/nettools/mcp/utils.go (2)

16-29: Consider returning stderr for debugging purposes.

runDebugNode returns only output.Stdout (line 28), discarding output.Stderr. For network debugging tools, stderr often contains valuable diagnostic information (warnings, connection details). Consider including stderr in the result or logging it.

♻️ Optional: Include stderr in CommandResult

If the CommandResult type supports it, consider:

-	return types.CommandResult{Output: output.Stdout}, nil
+	return types.CommandResult{Output: output.Stdout, Stderr: output.Stderr}, nil

Or at minimum, log stderr if non-empty to aid debugging.


31-44: Same consideration for runExecPod—stderr is discarded.

Similar to runDebugNode, this function only returns stdout. For consistency and debugging utility, consider preserving stderr.

pkg/nettools/mcp/pwru.go (1)

18-55: Implementation looks correct with proper validation and defaults.

The method correctly:

  • Falls back to a secure default image when s.nodeImage is empty
  • Validates output limit lines (both default assignment and max validation)
  • Validates BPF filter before use
  • Mounts the required /sys/kernel/debug path for eBPF tracing

One minor observation on line 40: cmd.addIfNotEmpty(in.BPFFilter, in.BPFFilter) passes the same value twice. This appears intentional (condition and value are the same), but consider whether a single-argument variant like cmd.addIfNotEmpty(in.BPFFilter) would be cleaner if the helper supports it.

pkg/nettools/types/manifest.go (1)

37-42: CommandResult provides a clean result structure.

Minor consideration: ExitCode uses omitempty, so a successful exit (code 0) won't be serialized in JSON output. This is typically fine, but if consumers need to distinguish between "exit code not set" vs "exit code is 0", consider removing omitempty from ExitCode to always include it explicitly.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ef539d and cb75b36.

📒 Files selected for processing (10)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/pwru.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/nettools/mcp/tcpdump.go
🧰 Additional context used
🧬 Code graph analysis (1)
pkg/kubernetes/mcp/nodes.go (3)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-12)
pkg/kubernetes/mcp/mcp.go (1)
  • MCPServer (15-17)
pkg/kubernetes/types/nodes.go (2)
  • DebugNodeParams (4-10)
  • DebugNodeResult (13-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (17)
pkg/kubernetes/types/nodes.go (1)

5-9: LGTM!

The struct properly defines the optional HostPath and MountPath fields with appropriate JSON tags and omitempty for optional handling. This aligns with the requested simplification from previous review comments.

pkg/kubernetes/mcp/nodes.go (2)

13-46: LGTM!

The path validation implementation correctly addresses the previous review feedback:

  1. Uses the original path (not filepath.Clean(path)) for the ".." traversal check at line 29, which properly catches traversal attempts.
  2. Includes ~ in the allowed characters (line 39) to support Kubernetes volume paths like kubernetes.io~secret.
  3. Validates absolute paths and rejects dangerous characters.

48-65: LGTM!

The DebugNode method properly validates both HostPath and MountPath before delegating to the underlying client, ensuring security checks are enforced at the MCP layer.

pkg/nettools/mcp/utils_test.go (4)

9-100: Good test coverage for validation utilities.

The TestValidateIntMax function provides comprehensive boundary testing including zero, boundary values, over-max, and negative value cases. Well-structured table-driven tests.


145-298: Thorough interface validation tests.

Excellent coverage of valid interface names (eth0, lo, br-int, vlan interfaces) and invalid patterns (too long, shell metacharacters, leading special chars). The 15-character limit aligns with Linux IFNAMSIZ constraints.


300-479: Comprehensive packet filter validation tests.

Good coverage of valid BPF filter expressions and command injection attempts (semicolon, pipe, backtick, $(), newline, null byte). This provides defense against shell injection attacks.


501-578: LGTM!

The TestCommandBuilder tests validate the fluent API methods (add, addIf, addIfNotEmpty) and chained operations correctly.

pkg/nettools/mcp/mcp.go (1)

8-20: LGTM!

The MCPServer struct and constructor are straightforward. The nodeImage field provides a configurable default for debug pods.

pkg/nettools/mcp/utils.go (5)

14-14: LGTM!

The regex ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ correctly validates Linux interface names—must start with alphanumeric and can contain dots, underscores, and hyphens.


46-81: LGTM!

The commandBuilder provides a clean fluent API for constructing commands. The addIf and addIfNotEmpty methods are useful for conditional argument building.


83-98: LGTM!

The interface validation correctly:

  • Allows empty string (no interface specified)
  • Allows "any" as a special case for tcpdump
  • Enforces 15-character limit (IFNAMSIZ - 1)
  • Validates against a safe character pattern

100-116: LGTM!

The packet filter validation provides good defense against shell injection by blocking dangerous characters (;, |, &, `, $, $(, \n, \x00, >, <). The 1024-character limit is reasonable for BPF filters.


118-138: LGTM!

The validateIntMax and stringWithDefault helper functions are straightforward and well-implemented.

pkg/nettools/mcp/pwru.go (1)

1-16: LGTM on package setup and constants.

The imports are appropriate, and the constants establish sensible defaults and limits for output lines. The hardcoded default pwru image version (v1.0.10) is acceptable per the discussion in prior comments about security concerns with arbitrary images.

pkg/nettools/types/manifest.go (3)

3-6: Base params type is well-designed.

BaseNetworkDiagParams provides a clean abstraction for the shared BPF filter parameter, enabling consistent validation across both tcpdump and pwru tools.


8-26: TcpdumpParams struct is well-organized.

The type correctly:

  • Embeds BaseNetworkDiagParams for shared BPF filter
  • Separates node-specific and pod-specific fields with clear comments
  • Uses omitempty appropriately for optional fields
  • Makes TargetType required to drive conditional validation logic

The conditional requirement of NodeName vs pod fields (PodName, PodNamespace) based on TargetType should be validated at runtime in the handler—ensure that validation exists in the tcpdump implementation.


28-35: PwruParams correctly enforces node-level targeting.

NodeName is appropriately required (no omitempty) since pwru needs access to /sys/kernel/debug and only operates at the node level. This aligns with the implementation in pwru.go.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/kubernetes/client/nodes.go (1)

120-132: Fix: Error message timeout does not match actual timeout value.

The error message states "5 minutes" but the actual timeout is 1 minute (time.Minute*1 on line 121).

🐛 Proposed fix
-		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 5 minutes: %w", err)
+		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 1 minute: %w", err)
🧹 Nitpick comments (5)
cmd/ovnk-mcp-server/main.go (1)

144-144: Consider documenting fallback behavior in flag help text.

The empty default for --node-image relies on tool-specific fallbacks (netshoot for tcpdump, cilium/pwru for pwru). Consider expanding the description to clarify this, e.g., "Container image for node operations (defaults to tool-specific images if empty)".

Suggested improvement
-	flag.StringVar(&cfg.NodeImage, "node-image", "", "Container image for node operations")
+	flag.StringVar(&cfg.NodeImage, "node-image", "", "Container image for node operations (defaults to tool-specific images if empty)")
pkg/nettools/mcp/mcp.go (1)

22-74: Tool registrations look good.

The tool descriptions are comprehensive with:

  • Clear parameter documentation
  • Helpful examples for common use cases
  • Clarification that --node-image overrides tool-specific defaults (addressing prior review feedback)

Minor note: Line 33 and 62 are missing a period at the end ("The image must contain the tcpdump utility" / "The image must contain the pwru utility").

pkg/nettools/mcp/utils.go (3)

17-29: Consider including stderr in the result for diagnostic completeness.

The function discards stderr from the debug node execution. For network diagnostic tools like tcpdump, error output may contain valuable information (warnings, interface issues, permission errors).

♻️ Proposed change
-	return types.CommandResult{Output: output.Stdout}, nil
+	result := types.CommandResult{Output: output.Stdout}
+	if output.Stderr != "" {
+		result.Error = output.Stderr
+	}
+	return result, nil

31-44: Same stderr consideration applies here.

For consistency with runDebugNode, consider capturing stderr in the Error field of CommandResult.


100-116: Consider adding backslash to the dangerous characters list.

The dangerous pattern list is comprehensive for shell injection prevention, but \ (backslash) is missing and could be used for escape sequence attacks depending on how the filter is passed to the shell.

♻️ Proposed addition
-	dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00", ">", "<"}
+	dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00", ">", "<", "\\"}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb75b36 and 54c1a75.

📒 Files selected for processing (10)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/pwru.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/nettools/mcp/pwru.go
🧰 Additional context used
🧬 Code graph analysis (5)
pkg/nettools/mcp/utils.go (5)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-12)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
pkg/nettools/types/manifest.go (1)
  • CommandResult (38-42)
pkg/kubernetes/types/pods.go (1)
  • ExecPodParams (17-21)
pkg/kubernetes/types/common.go (1)
  • NamespacedNameParams (37-40)
pkg/nettools/mcp/tcpdump.go (5)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-12)
pkg/nettools/types/manifest.go (2)
  • TcpdumpParams (10-26)
  • CommandResult (38-42)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
pkg/kubernetes/types/pods.go (1)
  • ExecPodParams (17-21)
pkg/kubernetes/types/common.go (1)
  • NamespacedNameParams (37-40)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
cmd/ovnk-mcp-server/main.go (1)
pkg/nettools/mcp/mcp.go (1)
  • NewMCPServer (15-20)
pkg/kubernetes/mcp/nodes.go (3)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-12)
pkg/kubernetes/mcp/mcp.go (1)
  • MCPServer (15-17)
pkg/kubernetes/types/nodes.go (2)
  • DebugNodeParams (4-10)
  • DebugNodeResult (13-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (22)
pkg/kubernetes/types/nodes.go (1)

5-9: LGTM!

The HostPath and MountPath fields are correctly added with omitempty tags, allowing optional configuration while defaulting behavior is handled elsewhere. This aligns with the simplified approach requested in past reviews.

cmd/ovnk-mcp-server/main.go (1)

46-49: LGTM!

The nettools MCP server initialization follows the established pattern used for other tool servers (OVS, kernel). The nodeImage parameter is correctly passed through from the CLI flag.

pkg/kubernetes/mcp/nodes.go (2)

18-46: Path validation implementation looks correct.

The validatePath function properly:

  1. Allows empty paths (defaults handled downstream)
  2. Requires absolute paths
  3. Checks the original path for .. elements (correctly addressing the prior review feedback about filepath.Clean masking traversal)
  4. Validates characters against a reasonable allowlist including ~ for Kubernetes-style paths

50-59: LGTM!

Path validation is correctly applied before invoking the underlying client, ensuring invalid paths are rejected early with clear error messages.

pkg/nettools/mcp/mcp.go (1)

8-20: LGTM!

The MCPServer struct and constructor are well-designed, holding a reference to the Kubernetes MCP server for node operations and the optional nodeImage configuration.

pkg/nettools/mcp/utils_test.go (3)

9-100: Good boundary testing for integer validation.

Tests cover zero, minimum, boundary (equal to max, just over max), and negative values with clear naming.


300-489: Comprehensive security testing for packet filters.

Excellent coverage of:

  • Valid BPF filter expressions (protocols, hosts, ports, operators)
  • Injection attempts (semicolon, pipe, backtick, command substitution, null bytes)
  • Length boundary validation (1024 max)

The prior review feedback about using strings.Repeat() for length tests has been correctly addressed.


491-568: LGTM!

The commandBuilder tests verify the fluent API works correctly for building commands with conditional arguments.

pkg/nettools/mcp/utils.go (4)

1-12: LGTM!

The imports and package structure are well-organized. The utility functions are appropriately scoped as unexported helpers for the MCP server.


46-81: LGTM!

The fluent command builder pattern is well-implemented and provides a clean API for constructing command arguments.


83-98: LGTM!

The interface validation correctly enforces the Linux IFNAMSIZ limit of 15 characters and uses an appropriate regex pattern. The special case for "any" (tcpdump wildcard) is properly handled.


118-138: LGTM!

Both utility functions are correct. validateIntMax properly validates both negative values and upper bounds, while stringWithDefault provides a clean null-coalescing pattern.

pkg/kubernetes/client/nodes.go (2)

15-33: LGTM!

The signature expansion to accept hostPath and mountPath parameters provides good flexibility for the debug node functionality while maintaining backward compatibility through defaults.


35-56: The HOST environment variable fix is correctly implemented.

The HOST environment variable now correctly uses the mountPath value (line 53) instead of the previously hardcoded "/host". This ensures tools relying on the HOST variable will look in the correct location when custom mount paths are used.

pkg/nettools/mcp/tcpdump.go (4)

13-23: LGTM!

The constants are well-documented and use sensible defaults:

  • DefaultSnaplen = 96 captures headers for diagnostics
  • MaxSnaplen = 1500 aligns with standard Ethernet MTU
  • DefaultTcpdumpImage uses a pinned version for security (as discussed in past reviews)

26-48: LGTM!

The validation flow is correct: interface and BPF filter validation first, then defaults are applied before bounds checking. The validateIntMax function handles negative value checks as confirmed in past reviews.


50-54: LGTM!

The tcpdump command is correctly constructed:

  • -n disables DNS lookups for faster output
  • -v enables verbose mode
  • -s and -c set snaplen and packet count
  • Interface and BPF filter are conditionally added

56-86: LGTM with minor observation.

The dispatch logic correctly handles both node and pod target types with appropriate fallback for the node image. The validation for required fields (node name, pod name) is handled in runDebugNode and runExecPod as confirmed in past reviews.

Note: HostPath and MountPath are not exposed in TcpdumpParams, so they use defaults. This is likely intentional since tcpdump primarily needs network access rather than filesystem access.

pkg/nettools/types/manifest.go (4)

1-6: LGTM!

The BaseNetworkDiagParams type provides a clean abstraction for shared parameters across network diagnostic tools.


8-26: LGTM!

The TcpdumpParams type is well-structured with clear separation between node-specific and pod-specific fields. The TargetType field correctly lacks omitempty since it's required to determine the execution path.


28-35: LGTM!

The PwruParams type correctly requires NodeName (no omitempty) since pwru is an eBPF-based tool that only runs at the node level, unlike tcpdump which supports both node and pod targets.


37-42: LGTM!

The CommandResult type provides a clean structure for command execution results. The Output field appropriately lacks omitempty to always be present in responses, while ExitCode and Error are optional.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/kubernetes/client/nodes.go (1)

121-132: Timeout error message is inconsistent with actual timeout value.

The error message on line 131 states "5 minutes" but the actual timeout configured on line 121 is time.Minute*1 (1 minute).

Proposed fix
 	if err != nil {
 		cleanupPod()
-		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 5 minutes: %w", err)
+		return "", nil, fmt.Errorf("debug pod did not reach running state within timeout of 1 minute: %w", err)
 	}
🧹 Nitpick comments (4)
pkg/nettools/mcp/mcp.go (1)

15-22: Consider adding nil check for k8sMcpServer parameter.

If k8sMcpServer is nil, the MCPServer will be created but will panic when tools try to use it. While the current call site in main.go always provides a valid server, defensive validation could prevent subtle bugs if this constructor is reused.

Proposed defensive check
 // NewMCPServer creates a new MCP server instance
 func NewMCPServer(k8sMcpServer *kubernetesmcp.MCPServer, pwruImage, tcpdumpImage string) *MCPServer {
+	if k8sMcpServer == nil {
+		panic("k8sMcpServer cannot be nil")
+	}
 	return &MCPServer{
 		k8sMcpServer: k8sMcpServer,
 		pwruImage:    pwruImage,
 		tcpdumpImage: tcpdumpImage,
 	}
 }
cmd/ovnk-mcp-server/main.go (1)

145-146: Update the netshoot container image to a more recent version.

The docker.io/cilium/pwru:v1.0.10 is the latest available version and appropriate. However, nicolaka/netshoot:v0.13 is outdated; the latest tag was updated on January 9, 2026, and should be used or the pinned version should be updated to a more recent release.

pkg/nettools/mcp/utils.go (1)

100-116: Consider adding single/double quotes to the dangerous patterns list.

The validation blocks common shell metacharacters, but single (') and double (") quotes can also be used for injection in certain contexts. While the current list covers the most critical vectors, adding quotes would provide defense-in-depth.

Suggested improvement
 func validatePacketFilter(filter string) error {
 	if filter == "" {
 		return nil
 	}
 	if len(filter) > 1024 {
 		return fmt.Errorf("packet filter too long (max 1024 characters)")
 	}

-	dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00", ">", "<"}
+	dangerous := []string{";", "|", "&", "`", "$", "$(", "\n", "\x00", ">", "<", "'", "\""}
 	for _, pattern := range dangerous {
 		if strings.Contains(filter, pattern) {
 			return fmt.Errorf("packet filter contains potentially dangerous characters")
 		}
 	}
 	return nil
 }
pkg/nettools/mcp/pwru.go (1)

34-35: Clarify command construction for BPF filter argument.

The addIfNotEmpty call adds the in.BPFFilter value directly as a command argument. For pwru, the filter should be passed as a positional argument (which this does correctly), but consider adding a comment to clarify that this is intentional behavior since pwru expects the pcap filter as the final positional argument(s).

Suggested clarity improvement
 	cmd := newCommand("pwru", "--output-limit-lines", strconv.Itoa(outputLimitLines))
+	// pwru accepts pcap filter as positional argument(s)
 	cmd.addIfNotEmpty(in.BPFFilter, in.BPFFilter)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54c1a75 and 474f2db.

📒 Files selected for processing (10)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/pwru.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🧰 Additional context used
🧬 Code graph analysis (3)
pkg/kubernetes/mcp/nodes.go (2)
pkg/kubernetes/mcp/mcp.go (1)
  • MCPServer (15-17)
pkg/kubernetes/types/nodes.go (2)
  • DebugNodeParams (4-10)
  • DebugNodeResult (13-16)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
pkg/nettools/mcp/pwru.go (3)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-13)
pkg/nettools/types/manifest.go (2)
  • PwruParams (30-35)
  • CommandResult (38-42)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (21)
pkg/kubernetes/types/nodes.go (1)

5-9: LGTM!

The DebugNodeParams struct is well-defined with appropriate JSON tags. The optional HostPath and MountPath fields use omitempty correctly, and validation is properly delegated to the MCP layer as indicated in the AI summary.

pkg/kubernetes/client/nodes.go (2)

39-56: Default values and environment variable handling look correct.

The defaulting logic properly handles empty paths, and the HOST environment variable now correctly uses the mountPath value (addressing the previous review feedback).


75-101: Pod volume configuration is well-structured.

The pod spec correctly uses the dynamic hostPath and mountPath values for the volume source and container mount, enabling flexible host filesystem access for debug operations.

pkg/kubernetes/mcp/nodes.go (2)

18-46: Path validation implementation addresses previous feedback correctly.

The traversal check now operates on the original path (not filepath.Clean'd), correctly detecting explicit .. elements. The character whitelist appropriately includes ~ for Kubernetes-style paths like kubernetes.io~secret.


49-64: LGTM!

The DebugNode method properly validates both paths before delegating to the client, following a fail-fast pattern that prevents pod creation with invalid paths.

cmd/ovnk-mcp-server/main.go (1)

47-51: Network tools integration follows the established pattern.

The nettools MCP server is properly instantiated with separate images for pwru and tcpdump, and tools are registered consistently with other MCP servers in the codebase.

pkg/nettools/mcp/mcp.go (1)

25-76: Tool descriptions are comprehensive and well-documented.

Both tools have clear parameter documentation, practical examples, and correctly reference the CLI flags (--tcpdump-image, --pwru-image) for image configuration. This addresses the previous review feedback about clarifying image configuration.

pkg/nettools/mcp/utils.go (7)

1-12: LGTM!

Package declaration and imports are appropriate for the utilities provided.


14-14: LGTM!

The regex pattern correctly validates Linux interface names: starts with alphanumeric, followed by alphanumeric, dots, underscores, or hyphens.


17-29: LGTM!

The runDebugNode helper properly validates required fields (Name and Image) and delegates to the Kubernetes MCP server. This centralized validation addresses concerns raised in previous reviews about empty field validation.


32-44: LGTM!

The runExecPod helper validates the required Name field and sensibly defaults Namespace to "default" following Kubernetes conventions.


46-81: LGTM!

The fluent commandBuilder pattern provides a clean, readable way to construct command arguments with conditional additions.


84-98: LGTM!

Interface validation correctly enforces the Linux IFNAMSIZ limit (15 characters), allows the special "any" value, and validates the character pattern.


118-138: LGTM!

The validateIntMax function correctly handles both negative values and upper bounds with clear error messages. The stringWithDefault utility is straightforward.

pkg/nettools/mcp/pwru.go (3)

1-16: LGTM!

Package declaration, imports, and constants are well-defined. The DefaultPwruImage constant addresses previous review feedback about making the image configurable while providing a sensible default.


18-32: LGTM!

The validation logic correctly handles the outputLimitLines parameter with proper defaulting and bounds checking via validateIntMax (which handles negative values as confirmed in past reviews). The BPF filter validation reuses the shared validatePacketFilter helper.


37-49: LGTM!

The DebugNodeParams construction correctly configures the debug pod with:

  • Node name and image from parameters
  • Required /sys/kernel/debug mount for eBPF functionality
  • The constructed command

The execution flow through runDebugNode ensures proper validation of required fields.

pkg/nettools/mcp/tcpdump.go (4)

1-23: LGTM!

Package declaration, imports, and constants are well-documented. The snaplen defaults and limits are appropriately chosen:

  • DefaultSnaplen = 96 captures headers while minimizing data
  • MaxSnaplen = 1500 allows full packet capture when needed

26-48: LGTM!

The validation and defaulting logic for interface, BPF filter, packet count, and snaplen is comprehensive. All concerns about negative values are addressed by the centralized validateIntMax function.


50-54: LGTM!

The tcpdump command construction uses appropriate flags:

  • -n: Numeric output (no DNS resolution)
  • -v: Verbose output
  • -s: Snaplen for packet capture size
  • -c: Packet count limit
  • -i: Interface (conditional)
  • BPF filter as positional argument (conditional)

56-82: LGTM!

The dispatch logic cleanly handles both target types:

  • "node": Uses runDebugNode with the configured image
  • "pod": Uses runExecPod with namespace/name/container

The field validation (node name, pod name, etc.) is correctly delegated to the helper functions as confirmed in past review discussions. The default case provides a clear error for invalid target types.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/kubernetes/client/nodes.go`:
- Line 131: The error message returned by the function that formats the pod
timeout (the fmt.Errorf call producing "debug pod did not reach running state
within timeout of 1 minutes: %w") has a grammar mistake; change "1 minutes" to
"1 minute" in that fmt.Errorf string so the message reads "debug pod did not
reach running state within timeout of 1 minute: %w".
🧹 Nitpick comments (2)
cmd/ovnk-mcp-server/main.go (1)

145-146: Consider centralizing default image constants.

The default images are defined both here in the CLI flags and in the respective tool files (DefaultPwruImage in pwru.go, DefaultTcpdumpImage in tcpdump.go). While this works correctly, you could reference the constants from the tool packages to avoid duplication:

Optional: Reference constants from tool packages
-	flag.StringVar(&cfg.PwruImage, "pwru-image", "docker.io/cilium/pwru:v1.0.10", "Container image for pwru operations")
-	flag.StringVar(&cfg.TcpdumpImage, "tcpdump-image", "nicolaka/netshoot:v0.13", "Container image for tcpdump operations")
+	flag.StringVar(&cfg.PwruImage, "pwru-image", nettoolsmcp.DefaultPwruImage, "Container image for pwru operations")
+	flag.StringVar(&cfg.TcpdumpImage, "tcpdump-image", nettoolsmcp.DefaultTcpdumpImage, "Container image for tcpdump operations")
pkg/nettools/mcp/utils_test.go (1)

1-1: Package naming violates Go conventions, but is consistent throughout the directory.

All .go files in pkg/nettools/mcp/ (including utils.go, mcp.go, pwru.go, and tcpdump.go) declare package nettools rather than package mcp. While this is intentional and consistent with the rest of the codebase in that directory, it violates the Go convention that package names should match their directory names. Either accept this as a deliberate design choice for white-box testing of unexported functions, or refactor to use package mcp for standard Go conventions.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 474f2db and 23fe355.

📒 Files selected for processing (10)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/pwru.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/utils.go
  • pkg/kubernetes/mcp/nodes.go
🧰 Additional context used
🧬 Code graph analysis (3)
cmd/ovnk-mcp-server/main.go (2)
pkg/kubernetes/mcp/mcp.go (2)
  • Config (11-13)
  • NewMCPServer (19-42)
pkg/nettools/mcp/mcp.go (1)
  • NewMCPServer (16-22)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
pkg/nettools/mcp/tcpdump.go (5)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-13)
pkg/nettools/types/manifest.go (2)
  • TcpdumpParams (10-26)
  • CommandResult (38-42)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
pkg/kubernetes/types/pods.go (1)
  • ExecPodParams (17-21)
pkg/kubernetes/types/common.go (1)
  • NamespacedNameParams (37-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (10)
pkg/kubernetes/client/nodes.go (1)

39-56: Defaulting and HOST env var logic looks correct.

The defaults for hostPath and mountPath are sensible, and the HOST environment variable now correctly uses mountPath instead of being hardcoded to "/host". This ensures tools relying on the HOST variable will reference the actual mount location.

pkg/nettools/types/manifest.go (1)

1-42: Type definitions are well-structured.

The parameter types provide clean separation between base, tcpdump-specific, and pwru-specific fields. The CommandResult struct appropriately captures command execution output with optional error information. The JSON tags are consistent.

pkg/nettools/mcp/pwru.go (1)

18-51: Pwru implementation is well-structured.

The method properly:

  • Applies defaults for outputLimitLines and validates bounds
  • Validates the BPF filter before use
  • Sets appropriate HostPath and MountPath to /sys/kernel/debug (required for eBPF tracing)
  • Uses the configurable s.pwruImage as discussed in prior reviews
pkg/nettools/mcp/tcpdump.go (1)

25-83: Tcpdump implementation is well-structured and addresses prior feedback.

The implementation:

  • Validates interface and BPF filter upfront
  • Applies sensible defaults for packetCount and snaplen with proper bounds checking
  • Uses text output (-v) instead of binary pcap, avoiding the string corruption issue raised previously
  • Uses configurable s.tcpdumpImage as discussed in prior reviews
  • Cleanly dispatches between node and pod execution paths
  • Centralizes validation for required fields (node_name, pod_name, etc.) in runDebugNode/runExecPod as confirmed in earlier comments
cmd/ovnk-mcp-server/main.go (1)

47-50: Network tools integration follows established patterns.

The netToolsServer initialization and AddTools call follow the same pattern as other MCP servers (ovsServer, kernelMcpServer), maintaining consistency.

pkg/nettools/mcp/utils_test.go (5)

9-100: LGTM!

Comprehensive boundary testing for validateIntMax covering zero, positive bounds, over-max values, and negative inputs. The table-driven structure is idiomatic and maintainable.


102-143: LGTM!

Test cases cover the key scenarios including the deliberate behavior that whitespace-only strings are treated as non-empty values.


145-298: LGTM!

Thorough validation test suite covering valid Linux interface naming conventions, length boundaries (IFNAMSIZ constraint), and comprehensive shell injection vectors. The security-focused test cases for special characters are well-designed.


300-489: LGTM!

Comprehensive packet filter validation tests with excellent coverage of valid BPF filter syntax and security-focused injection tests. The length boundary tests correctly use strings.Repeat() to generate valid character sequences.


491-568: LGTM!

Well-structured tests for the fluent command builder API, covering all methods (add, addIf, addIfNotEmpty) and method chaining. The test cases effectively validate both positive and negative conditional additions.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/nettools/mcp/pwru.go`:
- Around line 1-16: Replace the hardcoded pwru image string used when
registering the flag with the DefaultPwruImage constant: locate the flag
registration that calls flag.StringVar to populate cfg.PwruImage (the call
currently passes the literal "docker.io/cilium/pwru:v1.0.10") and change it to
pass nettoolsmcp.DefaultPwruImage (using the package alias where nettools is
imported) so the default comes from the defined DefaultPwruImage constant.
🧹 Nitpick comments (1)
pkg/kubernetes/client/nodes.go (1)

112-118: Consider using a background context for cleanup.

The cleanup function captures the ctx from createPod. If the context is cancelled (e.g., due to timeout during command execution), the pod deletion may fail silently. Using context.Background() for cleanup ensures the pod is deleted even if the original context is cancelled.

♻️ Suggested improvement
 	cleanupPod := func() {
 		// Delete the pod.
-		err := c.clientSet.CoreV1().Pods(namespace).Delete(ctx, createdDebugPod.Name, metav1.DeleteOptions{})
+		err := c.clientSet.CoreV1().Pods(namespace).Delete(context.Background(), createdDebugPod.Name, metav1.DeleteOptions{})
 		if err != nil {
 			log.Printf("failed to cleanup debug pod: %v", err)
 		}
 	}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23fe355 and 5ace57e.

📒 Files selected for processing (10)
  • cmd/ovnk-mcp-server/main.go
  • pkg/kubernetes/client/nodes.go
  • pkg/kubernetes/mcp/nodes.go
  • pkg/kubernetes/types/nodes.go
  • pkg/nettools/mcp/mcp.go
  • pkg/nettools/mcp/pwru.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
  • pkg/nettools/mcp/utils_test.go
  • pkg/nettools/types/manifest.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/kubernetes/types/nodes.go
  • cmd/ovnk-mcp-server/main.go
  • pkg/nettools/mcp/tcpdump.go
  • pkg/nettools/mcp/utils.go
🧰 Additional context used
🧬 Code graph analysis (3)
pkg/kubernetes/client/nodes.go (1)
pkg/kubernetes/client/client.go (1)
  • OVNKMCPServerClientSet (13-20)
pkg/kubernetes/mcp/nodes.go (3)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-13)
pkg/kubernetes/mcp/mcp.go (1)
  • MCPServer (15-17)
pkg/kubernetes/types/nodes.go (2)
  • DebugNodeParams (4-10)
  • DebugNodeResult (13-16)
pkg/nettools/mcp/pwru.go (3)
pkg/nettools/mcp/mcp.go (1)
  • MCPServer (9-13)
pkg/nettools/types/manifest.go (2)
  • PwruParams (30-35)
  • CommandResult (38-42)
pkg/kubernetes/types/nodes.go (1)
  • DebugNodeParams (4-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (16)
pkg/kubernetes/client/nodes.go (3)

15-33: LGTM!

The updated DebugNode signature correctly accepts and forwards the new hostPath and mountPath parameters to createPod. The function structure remains clean with proper error handling and cleanup.


35-56: LGTM!

The default handling for hostPath and mountPath is well-implemented. The HOST environment variable is now correctly set to mountPath (addressing the previous review feedback), which ensures tools relying on this variable will reference the correct mount location.


58-104: LGTM!

The pod specification correctly incorporates the dynamic hostPath and mountPath values. The privileged security context and host networking configuration are appropriate for a debug pod that needs full access to node resources.

pkg/kubernetes/mcp/nodes.go (2)

13-46: LGTM!

The validatePath function correctly validates paths for safe mounting:

  • Checks for absolute paths
  • Properly detects path traversal by checking the original input (addressing previous feedback about filepath.Clean masking "..")
  • Character whitelist includes necessary special characters (~ for Kubernetes volume names like kubernetes.io~secret)

48-65: LGTM!

The DebugNode method correctly validates both paths before creating the debug pod. The validation-first approach ensures malicious or malformed paths are rejected early, preventing potential security issues.

pkg/nettools/types/manifest.go (1)

1-42: LGTM!

The type definitions are well-structured:

  • BaseNetworkDiagParams provides shared BPF filter functionality
  • TcpdumpParams correctly distinguishes between node and pod targets with conditional required fields
  • PwruParams appropriately marks NodeName as required (no omitempty) since pwru only operates at node level
  • CommandResult captures all necessary output information
pkg/nettools/mcp/utils_test.go (5)

1-100: LGTM!

TestValidateIntMax provides comprehensive coverage of boundary conditions including zero, minimum valid, at-max, over-max, and negative values. The test structure is clean and readable.


102-143: LGTM!

TestStringWithDefault correctly tests the fallback behavior for empty strings while preserving whitespace-only values, which is the expected behavior for this utility.


145-298: LGTM!

TestValidateInterface provides excellent coverage of Linux interface naming rules:

  • Valid patterns (alphanumeric, dots, hyphens, underscores)
  • Length constraints (max 15 characters per IFNAMSIZ)
  • Invalid starting characters and dangerous shell metacharacters

300-489: LGTM!

TestValidatePacketFilter provides thorough security testing:

  • Valid BPF filter expressions including complex nested filters
  • Command injection vectors (semicolon, pipe, backtick, $(), &&, ||)
  • Null byte injection test
  • Length boundary tests now correctly use strings.Repeat("a", N) (addressing previous feedback)

491-568: LGTM!

TestCommandBuilder validates the fluent builder API comprehensively, covering base commands, argument addition, conditional additions (addIf, addIfNotEmpty), and method chaining.

pkg/nettools/mcp/mcp.go (2)

8-22: LGTM!

The MCPServer struct correctly separates pwruImage and tcpdumpImage fields (addressing previous feedback about the single nodeImage field causing confusion). The NewMCPServer constructor properly initializes all fields.


24-76: LGTM!

The AddTools method provides comprehensive tool registrations:

  • Clear documentation of supported parameters with defaults and constraints
  • Practical examples for common use cases
  • Correct separation of image configuration flags (--tcpdump-image and --pwru-image)
  • Tool descriptions align with the parameter types defined in pkg/nettools/types/manifest.go
pkg/nettools/mcp/pwru.go (3)

22-28: Output limit handling is correct.

The defaulting logic (when outputLimitLines == 0) follows the standard Go pattern for optional int fields, and the validation via validateIntMax properly enforces both negative value rejection and upper bound as confirmed in prior review comments.


38-50: Target construction and execution look good.

The DebugNodeParams correctly:

  • Uses s.pwruImage for configurability
  • Mounts /sys/kernel/debug required for eBPF/pwru access
  • Passes the built command

The execution flow and error handling are straightforward.


34-36: No changes needed. The addIfNotEmpty API usage is correct and idiomatic.

The method signature is addIfNotEmpty(value string, args ...string), where the first parameter is a condition to check and the remaining parameters are arguments to append only if the value is non-empty. Passing in.BPFFilter for both parameters is the standard pattern throughout the codebase—the same approach is used in tcpdump.go (line 54), iptables.go (line 40), and ip.go (line 37). This usage is explicitly tested in utils_test.go (line 528) and correctly adds the BPF filter as a positional argument when it is not empty, which matches the intent of the comment.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

This commit add network external tools: tcpdump and pwru
- tcpdump: Capture and analyze network packets with BPF filters
- pwru: allows fine-grained introspection of kernel state to facilitate debugging network connectivity issues

Assisted-By: Claude Code
Signed-off-by: Meina-rh <meinli@redhat.com>
@Meina-rh
Copy link
Contributor Author

@pperiyasamy I've resolved conflict and both tools can work well in Claude

❯ could you help me capture ovn-worker node's traffic passing by 172.18.0.2? 

● I'll help you capture traffic on the ovn-worker node for IP address 172.18.0.2.

● ovn-kubernetes-nettools - tcpdump (MCP)(target_type: "node", node_name: "ovn-worker", interface: "any", packet_count: 100, bpf_filter: "host 172.18.0.2")
  ⎿ {                                                                                                                                                                                                          
      "output": "02:35:58.901769 breth0 Out IP (tos 0x0, ttl 64, id 4217, offset 0, flags [DF], proto TCP (6), length 78)\n    172.18.0.4.10250 > 172.18.0.2.60902: Flags [P.], seq 3513036894:3513036920, ack
    169426371, win 73, options [nop,nop,TS val 3945473174 ecr 310450131], length 26\n02:35:58.901790 eth0  Out IP (tos 0x0, ttl 64, id 4217, offset 0, flags [DF], proto TCP (6), length 78)\n    172.18.0.4.10
    … +129 lines (ctrl+o to expand)


● I've successfully captured 100 packets on the ovn-worker node filtering for traffic involving IP 172.18.0.2. The capture shows:

  Key Traffic Patterns:
  - Port 10250: Kubelet API traffic between 172.18.0.4 and 172.18.0.2
  - Port 6443: Kubernetes API server traffic between the same IPs
  - Interfaces: Traffic visible on both breth0 and eth0

  Sample Traffic:
  - TCP connections with various flags (PSH+ACK, ACK)
  - Sequence/acknowledgment numbers showing active data transfer
  - Multiple packet sizes ranging from 52 bytes (ACKs) to over 4KB (data transfers)
  - Timestamps showing continuous communication

  The traffic appears to be normal Kubernetes control plane communication with the kubelet and API server exchanging data. All packets show proper TCP handshaking and data acknowledgments.

❯ could you help me capture ovn-worker linux kernel trace 

● I'll help you capture Linux kernel networking trace on the ovn-worker node using pwru (packet, where are you?), which uses eBPF to trace packets through the kernel networking stack.

● ovn-kubernetes-nettools - pwru (MCP)(node_name: "ovn-worker", bpf_filter: "host 172.18.0.2", output_limit_lines: 100)
  ⎿ {                                                                                                                                                                                                          
      "output": "SKB                CPU PROCESS          NETNS      MARK/x        IFACE       PROTO  MTU   LEN   TUPLE FUNC\n0xffff8991e4d67ce8 7   <empty>:227635   0          0               0         0x080
    0 0     85    172.18.0.2:6443->172.18.0.1:50330(tcp) napi_consume_skb\n0xffff8991e4d67ce8 7   <empty>:227635   0          0               0         0x0800 0     85    172.18.0.2:6443->172.18.0.1:50330(tc
    … +76 lines (ctrl+o to expand)



● I've successfully captured Linux kernel networking trace on the ovn-worker node. The pwru eBPF trace shows detailed packet processing through the kernel stack for traffic involving 172.18.0.2.

  Key Kernel Functions Traced:

  SKB (Socket Buffer) Lifecycle:
  - napi_consume_skb - Freeing network buffers
  - skb_clone - Cloning packets for multiple consumers
  - skb_release_head_state, skb_release_data - Releasing SKB resources
  - kfree_skbmem - Freeing SKB memory

  IP Stack Processing:
  - __ip_local_out - Starting local IP output
  - ip_output → ip_finish_output → __ip_finish_output - IP output pipeline
  - ip_finish_output2 - Layer 2 transmission preparation

  Netfilter/Security:
  - nf_hook_slow - Netfilter hook processing
  - selinux_ip_output, selinux_ip_postroute - SELinux security hooks
  - nf_checksum, nf_ip_checksum - Checksum validation

  Device Transmission:
  - __dev_queue_xmit - Queueing packets for transmission
  - dev_hard_start_xmit - Starting hardware transmission
  - validate_xmit_skb - Validating packets before transmission
  - netif_skb_features, skb_csum_hwoffload_help - Hardware offload features

  Bridge/Forwarding:
  - __dev_forward_skb - Forwarding between interfaces
  - eth_type_trans - Ethernet protocol handling
  - __netif_rx → enqueue_to_backlog - Receiving packets

  Network Namespace Transitions:
  - NETNS 4026533412 → 4026531840 → 4026533024 (crossing namespace boundaries)

  Traffic Observed:
  - Kubelet traffic: 172.18.0.4:10250 → 172.18.0.2:46652
  - API server traffic: 172.18.0.2:6443 → 172.18.0.1:50330
  - Interfaces: breth0 (5) → eth0 (2) showing bridge forwarding

  This trace is valuable for debugging packet drops, routing issues, and understanding the complete kernel networking path.

@pperiyasamy pperiyasamy requested a review from tssurya January 16, 2026 10:12
Copy link
Contributor

@pperiyasamy pperiyasamy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@arkadeepsen arkadeepsen dismissed tssurya’s stale review January 16, 2026 10:24

Dismissing Surya's review to merge the PR as this was a test review.

@arkadeepsen arkadeepsen merged commit f3d2e80 into ovn-kubernetes:main Jan 16, 2026
4 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants