fix: execute tool param mapping + docker-compose port 9801

- execute: map each operation to correct Komodo API param key
  (stack ops use 'stack', build ops use 'build', etc.)
- execute: container ops pass server alongside container name
- docker-compose: use host port 9801 to avoid conflict with
  existing bare node process on 9800
This commit is contained in:
2026-09-08 15:32:00 +00:00
parent c362aa1783
commit b4301285e5
3 changed files with 93 additions and 22 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ services:
build: . build: .
container_name: komodo-mcp-server container_name: komodo-mcp-server
ports: ports:
- "9800:9800" - "9801:9800"
environment: environment:
- KOMODO_BASE_URL=${KOMODO_BASE_URL:-http://10.10.2.114:9120} - KOMODO_BASE_URL=${KOMODO_BASE_URL:-http://10.10.2.114:9120}
- KOMODO_API_KEY=${KOMODO_API_KEY} - KOMODO_API_KEY=${KOMODO_API_KEY}
Binary file not shown.
+92 -21
View File
@@ -2,24 +2,95 @@ import { z } from "zod";
import { KomodoClient } from "../komodo-client.js"; import { KomodoClient } from "../komodo-client.js";
import { ExecuteOperation, EXECUTE_REQUEST_MAP } from "../types.js"; import { ExecuteOperation, EXECUTE_REQUEST_MAP } from "../types.js";
// Operations that need {server: <value>} instead of {id: <value>} // Maps each operation to the Komodo API param key for the id field.
const SERVER_SCOPED_OPS = new Set([ // Most operations use their entity type as the key (e.g. "stack", "build").
"run_server_prune_images", const OPERATION_PARAM_KEY: Record<string, string> = {
"run_server_prune_containers", // Build
"run_server_prune_networks", run_build: "build",
"rotate_all_server_keys", cancel_build: "build",
"prune_buildx", // Stack
"prune_docker_builders", deploy_stack: "stack",
"prune_system", deploy_stack_service: "stack",
"prune_volumes", deploy_stack_if_changed: "stack",
"delete_image", destroy_stack: "stack",
"delete_network", pull_stack: "stack",
"delete_volume", 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: <value>} 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 = { export const executeInputSchema = {
operation: ExecuteOperation.describe( 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)"), id: z.string().optional().describe("Resource ID, name, or server name (depends on operation)"),
params: z params: z
@@ -44,13 +115,13 @@ export async function handleExecute(
} }
const requestParams: Record<string, unknown> = { ...params }; const requestParams: Record<string, unknown> = { ...params };
if (id) { const paramKey = OPERATION_PARAM_KEY[operation];
if (SERVER_SCOPED_OPS.has(operation)) {
requestParams.server = id; if (id && paramKey && paramKey !== "_none" && paramKey !== "ids") {
} else if (operation === "run_sync") { requestParams[paramKey] = id;
requestParams.sync = id; // Container ops also need server in params
} else { if (NEEDS_SERVER_PARAM.has(operation) && params?.server) {
requestParams.id = id; requestParams.server = params.server;
} }
} }