diff --git a/docker-compose.yml b/docker-compose.yml index 1556f42..fc8da7e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ services: build: . container_name: komodo-mcp-server ports: - - "9800:9800" + - "9801:9800" environment: - KOMODO_BASE_URL=${KOMODO_BASE_URL:-http://10.10.2.114:9120} - KOMODO_API_KEY=${KOMODO_API_KEY} diff --git a/scripts/__pycache__/test_all_tools.cpython-314.pyc b/scripts/__pycache__/test_all_tools.cpython-314.pyc new file mode 100644 index 0000000..1610307 Binary files /dev/null and b/scripts/__pycache__/test_all_tools.cpython-314.pyc differ diff --git a/src/tools/execute.ts b/src/tools/execute.ts index 97338ad..4a98145 100644 --- a/src/tools/execute.ts +++ b/src/tools/execute.ts @@ -2,24 +2,95 @@ import { z } from "zod"; import { KomodoClient } from "../komodo-client.js"; import { ExecuteOperation, EXECUTE_REQUEST_MAP } from "../types.js"; -// Operations that need {server: } instead of {id: } -const SERVER_SCOPED_OPS = new Set([ - "run_server_prune_images", - "run_server_prune_containers", - "run_server_prune_networks", - "rotate_all_server_keys", - "prune_buildx", - "prune_docker_builders", - "prune_system", - "prune_volumes", - "delete_image", - "delete_network", - "delete_volume", +// Maps each operation to the Komodo API param key for the id field. +// Most operations use their entity type as the key (e.g. "stack", "build"). +const OPERATION_PARAM_KEY: Record = { + // Build + run_build: "build", + cancel_build: "build", + // Stack + deploy_stack: "stack", + deploy_stack_service: "stack", + deploy_stack_if_changed: "stack", + destroy_stack: "stack", + pull_stack: "stack", + start_stack: "stack", + stop_stack: "stack", + restart_stack: "stack", + pause_stack: "stack", + unpause_stack: "stack", + // Deployment + deploy: "deployment", + destroy_deployment: "deployment", + pull_deployment: "deployment", + start_deployment: "deployment", + stop_deployment: "deployment", + restart_deployment: "deployment", + pause_deployment: "deployment", + unpause_deployment: "deployment", + // Repo + build_repo: "repo", + clone_repo: "repo", + pull_repo: "repo", + cancel_repo_build: "repo", + clear_repo_cache: "repo", + // Procedure & Action + run_procedure: "procedure", + cancel_procedure: "procedure", + run_action: "action", + cancel_action: "action", + run_sync: "sync", + // Container (need both container + server) + start_container: "container", + stop_container: "container", + restart_container: "container", + pause_container: "container", + unpause_container: "container", + destroy_container: "container", + // Server-scoped (prune, delete, rotate, etc.) + run_server_prune_images: "server", + run_server_prune_containers: "server", + run_server_prune_networks: "server", + rotate_all_server_keys: "server", + prune_buildx: "server", + prune_docker_builders: "server", + prune_system: "server", + prune_volumes: "server", + delete_image: "server", + delete_network: "server", + delete_volume: "server", + // Batch ops — use "ids" array, not single id + batch_run_build: "ids", + batch_deploy_stack: "ids", + batch_deploy_stack_if_changed: "ids", + batch_deploy: "ids", + batch_pull_stack: "ids", + batch_pull_repo: "ids", + batch_build_repo: "ids", + batch_clone_repo: "ids", + batch_run_procedure: "ids", + batch_run_action: "ids", + batch_destroy_deployment: "ids", + batch_destroy_stack: "ids", + // Admin + backup_core_database: "_none", + rotate_core_keys: "_none", + global_auto_update: "_none", + send_alert: "alert", + test_alerter: "alerter", +}; + +// Operations that need {server: } alongside the entity param +const NEEDS_SERVER_PARAM = new Set([ + "start_container", "stop_container", "restart_container", + "pause_container", "unpause_container", "destroy_container", + "start_all_containers", "stop_all_containers", "restart_all_containers", + "pause_all_containers", "unpause_all_containers", ]); export const executeInputSchema = { operation: ExecuteOperation.describe( - "Execute operation. Server-scoped ops (prune_*, delete_*) require the server name in the id field. RunSync requires the sync name/id in the id field.", + "Execute operation. Pass the resource name/id in the id field. Server-scoped ops (prune_*, delete_*) require the server name. Batch ops ignore id.", ), id: z.string().optional().describe("Resource ID, name, or server name (depends on operation)"), params: z @@ -44,13 +115,13 @@ export async function handleExecute( } const requestParams: Record = { ...params }; - if (id) { - if (SERVER_SCOPED_OPS.has(operation)) { - requestParams.server = id; - } else if (operation === "run_sync") { - requestParams.sync = id; - } else { - requestParams.id = id; + const paramKey = OPERATION_PARAM_KEY[operation]; + + if (id && paramKey && paramKey !== "_none" && paramKey !== "ids") { + requestParams[paramKey] = id; + // Container ops also need server in params + if (NEEDS_SERVER_PARAM.has(operation) && params?.server) { + requestParams.server = params.server; } }