#!/usr/bin/env python3 """ CI Tool Name Parity Check Validates that MCP server tool names match the capability registry. Fails CI if: - A tool exists but isn't registered - A registered tool no longer exists """ import json import subprocess import sys import os import re from pathlib import Path def get_registry_tools(): """Load tool names from capability registry.""" with open("capability_registry_v2.json", "r") as f: registry = json.load(f) registry_tools = {} for server_name, server_info in registry["mcp_servers"].items(): # Extract base tool names (remove operation type) tools = [] for tool_desc in server_info["tools"]: tool_name = tool_desc.split(" (")[0] # Remove "(operation_type)" tools.append(tool_name) registry_tools[server_name] = set(tools) return registry_tools def get_actual_tools(): """Get actual tool names from MCP servers by examining source code.""" actual_tools = {} # Extract tools from Cloudflare Safe server source try: with open("mcp/cloudflare_safe/server.py", "r") as f: content = f.read() # Look for tool function definitions import re tool_pattern = r"def (cf_\w+)\(" tools_found = set(re.findall(tool_pattern, content)) # Filter out internal functions valid_tools = {tool for tool in tools_found if not tool.startswith("_")} actual_tools["cloudflare_safe"] = valid_tools except Exception as e: print(f"⚠️ Could not extract tools from cloudflare_safe: {e}") # Extract tools from WAF Intelligence server source try: with open("mcp/waf_intelligence/mcp_server.py", "r") as f: content = f.read() tool_pattern = r"def (waf_\w+)\(" tools_found = set(re.findall(tool_pattern, content)) valid_tools = {tool for tool in tools_found if not tool.startswith("_")} actual_tools["waf_intelligence"] = valid_tools except Exception as e: print(f"⚠️ Could not extract tools from waf_intelligence: {e}") # Extract tools from Oracle Answer server source try: with open("mcp/oracle_answer/server.py", "r") as f: content = f.read() tool_pattern = r"def (\w+)\(" tools_found = set(re.findall(tool_pattern, content)) # Look for oracle_answer specifically oracle_tools = {tool for tool in tools_found if "oracle" in tool.lower()} actual_tools["oracle_answer"] = oracle_tools except Exception as e: print(f"⚠️ Could not extract tools from oracle_answer: {e}") return actual_tools def check_tool_parity(): """Compare registry tools with actual tools.""" registry_tools = get_registry_tools() actual_tools = get_actual_tools() errors = [] for server_name in set(registry_tools.keys()) | set(actual_tools.keys()): reg_tools = registry_tools.get(server_name, set()) act_tools = actual_tools.get(server_name, set()) # Check for tools in registry but not in actual missing_in_actual = reg_tools - act_tools if missing_in_actual: errors.append( f"❌ {server_name}: Tools registered but not found: {missing_in_actual}" ) # Check for tools in actual but not in registry missing_in_registry = act_tools - reg_tools if missing_in_registry: errors.append( f"❌ {server_name}: Tools found but not registered: {missing_in_registry}" ) # Report parity if not missing_in_actual and not missing_in_registry: print(f"✅ {server_name}: Tool parity verified ({len(reg_tools)} tools)") return errors def main(): """Main CI check function.""" print("🔍 CI Tool Name Parity Check") print("=" * 50) errors = check_tool_parity() if errors: print("\n❌ Registry drift detected:") for error in errors: print(error) print( "\n💡 Fix: Update capability_registry_v2.json to match actual MCP server tools" ) sys.exit(1) else: print("\n✅ All MCP server tools match capability registry") sys.exit(0) if __name__ == "__main__": main()