Skip to content

Commit 8f64961

Browse files
pperiyasamyclaude
andcommitted
Add MCP Tools for OVS layer
This commit introduces four new MCP tools for inspecting and debugging Open vSwitch (OVS) in ovn-kubernetes clusters: - ovs-list-br: List all OVS bridges on a pod - ovs-list-ports: List all ports attached to an OVS bridge - ovs-list-ifaces: List all interfaces attached to an OVS bridge - ovs-vsctl-show: Display comprehensive OVS configuration overview - ovs-ofctl-dump-flows: Dump and analyze OpenFlow rules from OVS bridges with optional filtering by pattern - ovs-appctl-dump-conntrack: Inspect connection tracking (conntrack) entries from the OVS datapath to debug stateful firewall rules and NAT - ovs-appctl-ofproto-trace: Simulate packet processing through the OpenFlow pipeline to troubleshoot flow rules and forwarding decisions These tools enable AI-assisted debugging of OVS networking issues without requiring direct cluster access. Signed-off-by: Periyasamy Palanisamy <pepalani@redhat.com> Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e63bf6c commit 8f64961

File tree

5 files changed

+879
-0
lines changed

5 files changed

+879
-0
lines changed

cmd/ovnk-mcp-server/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
mcp "github.com/modelcontextprotocol/go-sdk/mcp"
1414
kubernetesmcp "github.com/ovn-kubernetes/ovn-kubernetes-mcp/pkg/kubernetes/mcp"
15+
ovsmcp "github.com/ovn-kubernetes/ovn-kubernetes-mcp/pkg/ovs/mcp"
1516
)
1617

1718
type MCPServerConfig struct {
@@ -36,6 +37,12 @@ func main() {
3637
}
3738
log.Println("Adding tools to OVN-K MCP server")
3839
k8sMcpServer.AddTools(ovnkMcpServer)
40+
ovsServer, err := ovsmcp.NewMCPServer(k8sMcpServer)
41+
if err != nil {
42+
log.Fatalf("Failed to create OVS MCP server: %v", err)
43+
}
44+
log.Println("Adding OVS tools to OVN-K MCP server")
45+
ovsServer.AddTools(ovnkMcpServer)
3946
}
4047

4148
// Create a context that can be cancelled to shutdown the server.

pkg/ovs/mcp/commands.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package mcp
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"regexp"
7+
"strings"
8+
9+
"github.com/modelcontextprotocol/go-sdk/mcp"
10+
k8stypes "github.com/ovn-kubernetes/ovn-kubernetes-mcp/pkg/kubernetes/types"
11+
)
12+
13+
func (s *MCPServer) runCommand(ctx context.Context, req *mcp.CallToolRequest, namespacedName k8stypes.NamespacedNameParams,
14+
commands []string) ([]string, error) {
15+
_, result, err := s.k8sMcpServer.ExecPod(ctx, req, k8stypes.ExecPodParams{NamespacedNameParams: namespacedName, Command: commands})
16+
if err != nil {
17+
return nil, err
18+
}
19+
if result.Stderr != "" {
20+
return nil, fmt.Errorf("error occurred while running command %v on pod %s/%s: %s", commands, namespacedName.Namespace,
21+
namespacedName.Name, result.Stderr)
22+
}
23+
var output []string
24+
for _, line := range strings.Split(result.Stdout, "\n") {
25+
line = strings.TrimSpace(line)
26+
if line != "" {
27+
output = append(output, line)
28+
}
29+
}
30+
return output, nil
31+
}
32+
33+
// filterLines filters lines using a regex pattern.
34+
func filterLines(lines []string, pattern string) ([]string, error) {
35+
if pattern == "" {
36+
return lines, nil
37+
}
38+
39+
filterPattern, err := regexp.Compile(pattern)
40+
if err != nil {
41+
return nil, fmt.Errorf("invalid filter pattern %s: %w", pattern, err)
42+
}
43+
44+
var filtered []string
45+
for _, line := range lines {
46+
if filterPattern.MatchString(line) {
47+
filtered = append(filtered, line)
48+
}
49+
}
50+
return filtered, nil
51+
}
52+
53+
// limitLines limits the number of lines returned.
54+
func limitLines(lines []string, maxLines int) []string {
55+
if maxLines > 0 && len(lines) > maxLines {
56+
return lines[:maxLines]
57+
}
58+
return lines
59+
}
60+
61+
// validateBridgeName validates that a bridge name is safe and non-empty.
62+
// Bridge names should only contain alphanumeric characters, hyphens, and underscores.
63+
func validateBridgeName(bridge string) error {
64+
if bridge == "" {
65+
return fmt.Errorf("bridge name cannot be empty")
66+
}
67+
68+
// OVS bridge names typically follow naming conventions: alphanumeric, hyphens, underscores
69+
validBridgeName := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
70+
if !validBridgeName.MatchString(bridge) {
71+
return fmt.Errorf("invalid bridge name %q: must contain only alphanumeric characters, hyphens, and underscores", bridge)
72+
}
73+
74+
return nil
75+
}
76+
77+
// validateFlowSpec validates that a flow specification is safe and non-empty.
78+
func validateFlowSpec(flow string) error {
79+
if flow == "" {
80+
return fmt.Errorf("flow specification cannot be empty")
81+
}
82+
83+
// Check for potentially dangerous characters that shouldn't appear in flow specs
84+
// Flow specs should contain: alphanumeric, commas, equals, colons, periods, slashes, parentheses, brackets
85+
// We explicitly block: semicolons, pipes, backticks, dollar signs, and other shell metacharacters
86+
dangerousChars := regexp.MustCompile(`[;&|$` + "`" + `<>\\]`)
87+
if dangerousChars.MatchString(flow) {
88+
return fmt.Errorf("invalid flow specification: contains potentially dangerous characters")
89+
}
90+
91+
return nil
92+
}

0 commit comments

Comments
 (0)