UNPKG

@gorizond/mcp-rancher-multi

Version:

MCP server for multiple Rancher Manager backends with Fleet GitOps support

417 lines (368 loc) 13.3 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; // Mock MCP SDK vi.mock("@modelcontextprotocol/sdk/server/mcp.js", () => ({ McpServer: vi.fn().mockImplementation(() => ({ registerTool: vi.fn(), run: vi.fn() })) })); // Mock utils vi.mock('../src/utils.js', () => ({ loadEnvFiles: vi.fn(), loadConfigFromEnv: vi.fn(() => ({ testServer: { id: 'testServer', baseUrl: 'https://test.rancher.com', token: 'test-token', name: 'Test Server' } })), resolveToken: vi.fn((token) => token), obfuscateConfig: vi.fn((config) => config) })); // Mock fetch globally global.fetch = vi.fn(); describe('MCP Server', () => { let mockServer: any; beforeEach(() => { vi.clearAllMocks(); mockServer = { registerTool: vi.fn(), run: vi.fn() }; (McpServer as any).mockImplementation(() => mockServer); }); afterEach(() => { vi.restoreAllMocks(); }); it('should create MCP server instance with correct name and version', () => { // Test the server creation logic const server = new McpServer({ name: "mcp-rancher-multi", version: "0.3.0" }); expect(McpServer).toHaveBeenCalledWith({ name: "mcp-rancher-multi", version: "0.3.0" }); expect(server).toBeDefined(); }); it('should register rancher_servers_list tool', () => { // Test tool registration mockServer.registerTool('rancher_servers_list', { title: "List registered Rancher servers", description: "Returns known servers from local store", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_servers_list', { title: "List registered Rancher servers", description: "Returns known servers from local store", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register rancher_servers_add tool', () => { mockServer.registerTool('rancher_servers_add', { title: "Add/Update Rancher server (runtime only)", description: "Register a Rancher Manager for current session (not persisted)", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_servers_add', { title: "Add/Update Rancher server (runtime only)", description: "Register a Rancher Manager for current session (not persisted)", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register rancher_servers_remove tool', () => { mockServer.registerTool('rancher_servers_remove', { title: "Remove Rancher server (runtime only)", description: "Deletes a server from current session (not persisted)", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_servers_remove', { title: "Remove Rancher server (runtime only)", description: "Deletes a server from current session (not persisted)", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register rancher_clusters_list tool', () => { mockServer.registerTool('rancher_clusters_list', { title: "List clusters", description: "Return clusters from selected Rancher server", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_clusters_list', { title: "List clusters", description: "Return clusters from selected Rancher server", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register rancher_clusters_kubeconfig tool', () => { mockServer.registerTool('rancher_clusters_kubeconfig', { title: "Generate kubeconfig for a cluster", description: "POST /v3/clusters/{id}?action=generateKubeconfig", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_clusters_kubeconfig', { title: "Generate kubeconfig for a cluster", description: "POST /v3/clusters/{id}?action=generateKubeconfig", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register rancher_nodes_list tool', () => { mockServer.registerTool('rancher_nodes_list', { title: "List nodes", description: "Return nodes (v3/nodes)", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_nodes_list', { title: "List nodes", description: "Return nodes (v3/nodes)", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register rancher_projects_list tool', () => { mockServer.registerTool('rancher_projects_list', { title: "List projects", description: "Return projects in cluster (v3/projects)", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_projects_list', { title: "List projects", description: "Return projects in cluster (v3/projects)", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register k8s_namespaces_list tool', () => { mockServer.registerTool('k8s_namespaces_list', { title: "List namespaces", description: "GET /api/v1/namespaces via Rancher proxy", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'k8s_namespaces_list', { title: "List namespaces", description: "GET /api/v1/namespaces via Rancher proxy", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register k8s_raw tool', () => { mockServer.registerTool('k8s_raw', { title: "Raw Kubernetes API request", description: "Arbitrary request to /api or /apis (DANGEROUS) — use carefully", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'k8s_raw', { title: "Raw Kubernetes API request", description: "Arbitrary request to /api or /apis (DANGEROUS) — use carefully", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register rancher_health tool', () => { mockServer.registerTool('rancher_health', { title: "Check Rancher server health", description: "Check /v3 endpoint", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_health', { title: "Check Rancher server health", description: "Check /v3 endpoint", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register rancher_kubeconfigs_merge tool', () => { mockServer.registerTool('rancher_kubeconfigs_merge', { title: "Merge multiple kubeconfigs", description: "Concatenate generated kubeconfigs for a list of clusterIds", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'rancher_kubeconfigs_merge', { title: "Merge multiple kubeconfigs", description: "Concatenate generated kubeconfigs for a list of clusterIds", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register fleet_gitrepos_list tool', () => { mockServer.registerTool('fleet_gitrepos_list', { title: "List Fleet GitRepos", description: "GET /apis/fleet.cattle.io/v1alpha1/namespaces/{ns}/gitrepos", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'fleet_gitrepos_list', { title: "List Fleet GitRepos", description: "GET /apis/fleet.cattle.io/v1alpha1/namespaces/{ns}/gitrepos", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register fleet_gitrepos_get tool', () => { mockServer.registerTool('fleet_gitrepos_get', { title: "Get Fleet GitRepo", description: "GET /apis/fleet.cattle.io/v1alpha1/namespaces/{ns}/gitrepos/{name}", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'fleet_gitrepos_get', { title: "Get Fleet GitRepo", description: "GET /apis/fleet.cattle.io/v1alpha1/namespaces/{ns}/gitrepos/{name}", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register fleet_gitrepos_create tool', () => { mockServer.registerTool('fleet_gitrepos_create', { title: "Create Fleet GitRepo", description: "POST a GitRepo manifest (JSON)", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'fleet_gitrepos_create', { title: "Create Fleet GitRepo", description: "POST a GitRepo manifest (JSON)", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register fleet_gitrepos_apply tool', () => { mockServer.registerTool('fleet_gitrepos_apply', { title: "Apply Fleet GitRepo", description: "PATCH application/apply-patch+yaml to GitRepo (idempotent)", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'fleet_gitrepos_apply', { title: "Apply Fleet GitRepo", description: "PATCH application/apply-patch+yaml to GitRepo (idempotent)", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register fleet_gitrepos_redeploy tool', () => { mockServer.registerTool('fleet_gitrepos_redeploy', { title: "Redeploy Fleet GitRepo", description: "PATCH merge-patch: set metadata.annotations['fleet.cattle.io/redeployHash']", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'fleet_gitrepos_redeploy', { title: "Redeploy Fleet GitRepo", description: "PATCH merge-patch: set metadata.annotations['fleet.cattle.io/redeployHash']", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register fleet_bdeploys_list tool', () => { mockServer.registerTool('fleet_bdeploys_list', { title: "List Fleet BundleDeployments", description: "GET /apis/fleet.cattle.io/v1alpha1/bundledeployments (optional labelSelector)", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'fleet_bdeploys_list', { title: "List Fleet BundleDeployments", description: "GET /apis/fleet.cattle.io/v1alpha1/bundledeployments (optional labelSelector)", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register fleet_status_summary tool', () => { mockServer.registerTool('fleet_status_summary', { title: "Fleet status summary", description: "Aggregate Ready/NonReady from BundleDeployments and link to GitRepos", inputSchema: expect.any(Object) }, expect.any(Function)); expect(mockServer.registerTool).toHaveBeenCalledWith( 'fleet_status_summary', { title: "Fleet status summary", description: "Aggregate Ready/NonReady from BundleDeployments and link to GitRepos", inputSchema: expect.any(Object) }, expect.any(Function) ); }); it('should register all expected tools', () => { const expectedTools = [ 'rancher_servers_list', 'rancher_servers_add', 'rancher_servers_remove', 'rancher_clusters_list', 'rancher_clusters_kubeconfig', 'rancher_nodes_list', 'rancher_projects_list', 'k8s_namespaces_list', 'k8s_raw', 'rancher_health', 'rancher_kubeconfigs_merge', 'fleet_gitrepos_list', 'fleet_gitrepos_get', 'fleet_gitrepos_create', 'fleet_gitrepos_apply', 'fleet_gitrepos_redeploy', 'fleet_bdeploys_list', 'fleet_status_summary' ]; // Register all tools expectedTools.forEach(tool => { mockServer.registerTool(tool, expect.any(Object), expect.any(Function)); }); expect(mockServer.registerTool).toHaveBeenCalledTimes(expectedTools.length); const registeredTools = mockServer.registerTool.mock.calls.map((call: any) => call[0]); expectedTools.forEach(tool => { expect(registeredTools).toContain(tool); }); }); });