-
Notifications
You must be signed in to change notification settings - Fork 13
Add external tools MCP server #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,11 @@ package types | |
|
|
||
| // DebugNodeParams is a type that contains the name, image and command of a node. | ||
| type DebugNodeParams struct { | ||
| Name string `json:"name"` | ||
| Image string `json:"image"` | ||
| Command []string `json:"command"` | ||
| Name string `json:"name"` | ||
| Image string `json:"image"` | ||
| Command []string `json:"command"` | ||
| HostPath string `json:"hostPath,omitempty"` | ||
| MountPath string `json:"mountPath,omitempty"` | ||
|
Comment on lines
+5
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's view the struct definition in the types.go file
cat -n pkg/kubernetes/types/nodes.go | head -30Repository: ovn-kubernetes/ovn-kubernetes-mcp Length of output: 705 🏁 Script executed: # Search for usages of HostPath and MountPath in the codebase (excluding test files and the types file itself)
rg -n 'HostPath|MountPath' --type=go -g '!*test*.go' -g '!*_test.go' | head -50Repository: ovn-kubernetes/ovn-kubernetes-mcp Length of output: 7511 🏁 Script executed: # Also search for any validation functions that might handle these paths
rg -n 'ValidatePath|SanitizePath|ValidateMount|PathTraversal' --type=goRepository: ovn-kubernetes/ovn-kubernetes-mcp Length of output: 318 🏁 Script executed: # Search for DebugNodeParams usage (excluding vendor)
rg -n 'DebugNodeParams' --type=go -g '!vendor/**'Repository: ovn-kubernetes/ovn-kubernetes-mcp Length of output: 1033 🏁 Script executed: # Also search for files that might use HostPath or MountPath in the context of debugging
rg -n 'HostPath|MountPath' --type=go -g '!vendor/**' -g '!*test*.go'Repository: ovn-kubernetes/ovn-kubernetes-mcp Length of output: 732 🏁 Script executed: # Search for where the debug node feature is implemented
rg -n 'debug.*node|DebugNode' --type=go -i -g '!vendor/**'Repository: ovn-kubernetes/ovn-kubernetes-mcp Length of output: 2598 🏁 Script executed: # View the nodes.go client file to see how HostPath and MountPath are used
cat -n pkg/kubernetes/client/nodes.goRepository: ovn-kubernetes/ovn-kubernetes-mcp Length of output: 4508 🏁 Script executed: # Check the pwru.go file where HostPath and MountPath are set
cat -n pkg/external_tools/mcp/pwru.go | head -50Repository: ovn-kubernetes/ovn-kubernetes-mcp Length of output: 2048 Add path validation before creating debug pods. The Add validation to ensure:
This is particularly important since the fields are optional and part of the public API, making them susceptible to misuse by future callers beyond the current hardcoded usage in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Meina-rh this is a good suggestion. Let's add this validation in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @arkadeepsen added, please take a look if it's right. |
||
| } | ||
|
|
||
| // DebugNodeResult is a type that contains the stdout and stderr of the executed command. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package nettools | ||
|
|
||
| import ( | ||
| "github.com/modelcontextprotocol/go-sdk/mcp" | ||
| kubernetesmcp "github.com/ovn-kubernetes/ovn-kubernetes-mcp/pkg/kubernetes/mcp" | ||
| ) | ||
|
|
||
| // MCPServer provides MCP server functionality for network tools operations. | ||
| type MCPServer struct { | ||
| k8sMcpServer *kubernetesmcp.MCPServer | ||
| pwruImage string | ||
| tcpdumpImage string | ||
| } | ||
|
|
||
| // NewMCPServer creates a new MCP server instance | ||
| func NewMCPServer(k8sMcpServer *kubernetesmcp.MCPServer, pwruImage, tcpdumpImage string) *MCPServer { | ||
| return &MCPServer{ | ||
| k8sMcpServer: k8sMcpServer, | ||
| pwruImage: pwruImage, | ||
| tcpdumpImage: tcpdumpImage, | ||
| } | ||
| } | ||
|
|
||
| // AddTools registers network tools with the MCP server | ||
| func (s *MCPServer) AddTools(server *mcp.Server) { | ||
| mcp.AddTool(server, | ||
| &mcp.Tool{ | ||
| Name: "tcpdump", | ||
| Description: `Capture network packets on a node or inside a pod with strict safety controls. | ||
pperiyasamy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Supports both node-level and pod-level packet capture with BPF filtering. | ||
|
|
||
| This tool creates a specialized debug pod on the specified node for node-level captures. | ||
| The node image can be configured via --tcpdump-image flag or will default to nicolaka/netshoot:v0.13. | ||
| The image must contain the tcpdump utility | ||
|
|
||
| Parameters: | ||
| - target_type: 'node' or 'pod' (required) | ||
pperiyasamy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - node_name: Name of the node (required when target_type is 'node') | ||
| - pod_name: Name of the pod (required when target_type is 'pod') | ||
| - pod_namespace: Namespace of the pod (required when target_type is 'pod') | ||
| - container_name: Name of the container in the pod (optional, uses default container if not specified) | ||
| - interface: Network interface name or 'any' (optional, uses default if not specified) | ||
| - packet_count: Number of packets to capture (default: 100, max: 1000) | ||
| - bpf_filter: BPF filter expression to match packets (optional, e.g., "tcp and dst port 8080", "host 10.0.0.1") | ||
| - snaplen: Snapshot length in bytes (default: 96, max: 1500) | ||
|
|
||
| Examples: | ||
| - Capture on node: {"target_type": "node", "node_name": "worker-1", "interface": "eth0", "packet_count": 100, "bpf_filter": "tcp port 80"} | ||
| - Capture in pod: {"target_type": "pod", "pod_name": "my-pod", "pod_namespace": "default", "interface": "eth0", "packet_count": 100, "bpf_filter": "host 10.0.0.1"} | ||
| - Capture DNS: {"target_type": "node", "node_name": "worker-1", "interface": "any", "packet_count": 50, "bpf_filter": "port 53"}`, | ||
| }, s.Tcpdump) | ||
| mcp.AddTool(server, | ||
| &mcp.Tool{ | ||
| Name: "pwru", | ||
| Description: `Trace packets through the Linux kernel networking stack using eBPF. | ||
|
|
||
| pwru (packet, where are you?) shows which kernel functions process a packet, helping debug packet | ||
| drops, routing issues, and understanding the kernel's packet processing path. | ||
|
|
||
| This tool creates a specialized debug pod on the specified node with necessary eBPF capabilities | ||
| to trace packets through kernel networking functions. | ||
| The node image can be configured via --pwru-image flag or will default to docker.io/cilium/pwru:v1.0.10. | ||
| The image must contain the pwru utility | ||
|
|
||
| Parameters: | ||
| - node_name: Name of the node to run pwru on (required) | ||
| - bpf_filter: BPF filter expression to match packets (optional, e.g., "tcp and dst port 8080", "host 10.0.0.1") | ||
| - output_limit_lines: Maximum number of trace events to capture (default: 100, max: 1000) | ||
|
|
||
| Examples: | ||
| - Basic trace: {"node_name": "worker-1", "bpf_filter": "host 10.244.0.5", "output_limit_lines": 100} | ||
| - TCP traffic: {"node_name": "worker-1", "bpf_filter": "tcp and dst port 8080", "output_limit_lines": 50} | ||
| - ICMP packets: {"node_name": "worker-1", "bpf_filter": "icmp", "output_limit_lines": 100}`, | ||
| }, s.Pwru) | ||
| } | ||
Meina-rh marked this conversation as resolved.
Show resolved
Hide resolved
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| package nettools | ||
|
|
||
| import ( | ||
| "context" | ||
| "strconv" | ||
|
|
||
| "github.com/modelcontextprotocol/go-sdk/mcp" | ||
| k8stypes "github.com/ovn-kubernetes/ovn-kubernetes-mcp/pkg/kubernetes/types" | ||
| "github.com/ovn-kubernetes/ovn-kubernetes-mcp/pkg/nettools/types" | ||
| ) | ||
|
|
||
| const ( | ||
| DefaultOutputLimitLines = 100 | ||
| MaxOutputLimitLines = 1000 | ||
| ) | ||
Meina-rh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Pwru executes the pwru (packet, where are you?) tool to trace packets through the Linux kernel. | ||
| // It creates a specialized debug pod with eBPF capabilities and traces packet processing paths. | ||
| // This is useful for debugging packet drops, routing issues, and understanding kernel networking behavior. | ||
| func (s *MCPServer) Pwru(ctx context.Context, req *mcp.CallToolRequest, in types.PwruParams) (*mcp.CallToolResult, types.CommandResult, error) { | ||
| outputLimitLines := in.OutputLimitLines | ||
| if outputLimitLines == 0 { | ||
| outputLimitLines = DefaultOutputLimitLines | ||
| } | ||
| if err := validateIntMax(outputLimitLines, MaxOutputLimitLines, "output_limit_lines", ""); err != nil { | ||
| return nil, types.CommandResult{}, err | ||
| } | ||
Meina-rh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if err := validatePacketFilter(in.BPFFilter); err != nil { | ||
| return nil, types.CommandResult{}, err | ||
| } | ||
|
|
||
| cmd := newCommand("pwru", "--output-limit-lines", strconv.Itoa(outputLimitLines)) | ||
| // pwru accepts pcap filter as positional argument(s) | ||
| cmd.addIfNotEmpty(in.BPFFilter, in.BPFFilter) | ||
|
|
||
| target := k8stypes.DebugNodeParams{ | ||
| Name: in.NodeName, | ||
| Image: s.pwruImage, | ||
| HostPath: "/sys/kernel/debug", | ||
| MountPath: "/sys/kernel/debug", | ||
| Command: cmd.build(), | ||
| } | ||
|
|
||
| result, err := s.runDebugNode(ctx, req, target) | ||
pperiyasamy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if err != nil { | ||
| return nil, types.CommandResult{}, err | ||
| } | ||
| return nil, result, nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.