create_test_plan
Create structured test plans in TestCollab with optional test cases, configurations, and team assignments to organize testing workflows.
Instructions
Create a test plan in TestCollab using a single MCP tool call.
Before calling this tool:
Ask follow-up questions for missing required information.
Do not infer or auto-generate required values like project_id.
Execution flow:
POST /testplans
POST /testplantestcases/bulkAdd (optional)
POST /testplanconfigurations (optional)
POST /testplans/assign (optional)
Optional:
project_id
title (defaults to "Test Plan DD Month YYYY HH:mm:ss" if omitted)
description
priority (0=Low, 1=Normal, 2=High)
test_plan_folder (ID or title)
release (ID or title)
start_date, end_date
custom_fields
test_cases (test_case_ids/selector/assignee; assignee supports user ID/"me"/name)
configurations
assignment (supports user IDs/"me"/names; if user says "assign to me", use "me")
Example: { "project_id": 16, "title": "Release 2.9 Regression", "priority": 1, "test_cases": { "test_case_ids": [101, 102, 103] }, "configurations": [ [{ "field": "Browser", "value": "Chrome" }, { "field": "OS", "value": "Windows" }] ], "assignment": { "executor": "team", "assignment_criteria": "testCase", "assignment_method": "automatic", "user_ids": [27, 31] } }
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | No | Project ID (optional if TC_DEFAULT_PROJECT is set) | |
| title | No | Test plan title (optional; defaults to "Test Plan DD Month YYYY HH:mm:ss") | |
| description | No | Test plan description (HTML supported) | |
| priority | No | Priority: 0=Low, 1=Normal, 2=High | |
| test_plan_folder | No | Test plan folder ID or title (null to place at root) | |
| release | No | Release ID or title | |
| start_date | No | Planned start date (YYYY-MM-DD) | |
| end_date | No | Planned end date (YYYY-MM-DD) | |
| custom_fields | No | Array of test plan custom field values | |
| test_cases | No | Test cases to bulk-add immediately after plan creation | |
| configurations | No | Configuration matrix to attach to the test plan | |
| assignment | No | Assignment payload to execute after creation |
Implementation Reference
- src/tools/test-plans/create.ts:1005-1811 (handler)The handler function that executes the "create_test_plan" tool logic.
export async function handleCreateTestPlan(args: unknown): Promise<ToolResponse> { const parsed = createTestPlanSchema.safeParse(args); if (!parsed.success) { const missingFields = extractMissingRequiredFields(parsed.error.errors); if (missingFields.length > 0) { return toMissingInfoError( "MISSING_REQUIRED_INFO", "Missing required information to create a test plan.", missingFields ); } return toError("VALIDATION_ERROR", "Invalid input parameters", parsed.error.errors); } const { project_id, title, description, priority, test_plan_folder, release, start_date, end_date, custom_fields, test_cases, configurations, assignment, } = parsed.data; const normalizedTitle = normalizeString(title) ?? buildDefaultTestPlanTitle(); const requestContext = getRequestContext(); const envConfig = requestContext ? null : getConfig(); const resolvedProjectId = project_id ?? requestContext?.defaultProjectId ?? envConfig?.defaultProjectId; if (!resolvedProjectId) { return toMissingInfoError( "MISSING_PROJECT_ID", "project_id is required. Either provide it in the request or set TC_DEFAULT_PROJECT.", ["project_id"] ); } const assignmentUsers = normalizeAssignmentUsers(assignment?.user_ids); if (assignmentUsers.invalidValues.length > 0) { return toError( "INVALID_ASSIGNMENT_USERS", "assignment.user_ids contains invalid values.", { invalid_values: assignmentUsers.invalidValues, } ); } if ( assignmentUsers.hasMe && (assignmentUsers.userIds.length > 0 || assignmentUsers.userLookups.length > 0) ) { return toError( "INVALID_ASSIGNMENT_USERS", 'assignment.user_ids cannot mix "me" with user IDs or user names.' ); } const assignmentUserIds = assignmentUsers.userIds; const assignmentUserLookups = assignmentUsers.userLookups; const assignmentTargetsMe = assignmentUsers.hasMe || assignment?.executor === "me"; const resolvedAssignmentExecutor: "me" | "team" = assignmentTargetsMe ? "me" : (assignment?.executor ?? "team"); const assignmentTestCaseIds = normalizeNumberIds(assignment?.test_case_ids); const assignmentConfigurationIds = normalizeNumberIds( assignment?.configuration_ids ); const assignmentSelector = assignment?.selector ?? []; if ( assignment?.assignment_method === "manual" && !assignmentTargetsMe && assignmentUserIds.length === 0 && assignmentUserLookups.length === 0 ) { return toMissingInfoError( "MISSING_ASSIGNMENT_USERS", "Manual assignment requires at least one user_id in assignment.user_ids.", ["assignment.user_ids"] ); } if ( assignment?.assignment_method === "manual" && assignment.assignment_criteria === "testCase" && assignmentTestCaseIds.length === 0 && assignmentSelector.length === 0 ) { return toMissingInfoError( "MISSING_ASSIGNMENT_TEST_CASES", "Manual testCase assignment requires assignment.test_case_ids or assignment.selector.", ["assignment.test_case_ids", "assignment.selector"] ); } if ( assignment?.assignment_method === "manual" && assignment.assignment_criteria === "configuration" && assignmentConfigurationIds.length === 0 && (!configurations || configurations.length === 0) ) { return toMissingInfoError( "MISSING_ASSIGNMENT_CONFIGURATIONS", "Manual configuration assignment requires assignment.configuration_ids or configurations.", ["assignment.configuration_ids", "configurations"] ); } const testCaseIds = normalizeNumberIds(test_cases?.test_case_ids); const testCaseSelector = test_cases?.selector; const hasTestCaseAssignee = test_cases?.assignee !== undefined; const testCaseAssigneeValue = normalizeAssignee(test_cases?.assignee); if (hasTestCaseAssignee && testCaseAssigneeValue === undefined) { return toError( "INVALID_TEST_CASE_ASSIGNEE", 'test_cases.assignee must be a user ID, "me", name, username, or email.' ); } const shouldAddTestCases = test_cases !== undefined && (testCaseIds.length > 0 || (testCaseSelector?.length ?? 0) > 0); const shouldCreateConfigurations = configurations !== undefined && configurations.length > 0; const shouldAssignFromTestCaseAssignee = assignment === undefined && hasTestCaseAssignee && shouldAddTestCases; const shouldAssign = assignment !== undefined || shouldAssignFromTestCaseAssignee; const steps: Record<string, { endpoint: string; status: string; detail?: string }> = { create_test_plan: { endpoint: "/testplans", status: "pending" }, add_test_cases: { endpoint: "/testplantestcases/bulkAdd", status: shouldAddTestCases ? "pending" : "skipped", }, add_configurations: { endpoint: "/testplanconfigurations", status: shouldCreateConfigurations ? "pending" : "skipped", }, assign_test_plan: { endpoint: "/testplans/assign", status: shouldAssign ? "pending" : "skipped", }, }; let createdPlanId: number | undefined; let createdPlanTitle: string | undefined; try { const client = getApiClient(); // Resolve folder (supports ID or title) let resolvedFolderId: number | null | undefined; if (test_plan_folder !== undefined) { if (test_plan_folder === null) { resolvedFolderId = null; } else { const numericFolderId = toNumberId(test_plan_folder); if (numericFolderId !== undefined) { resolvedFolderId = numericFolderId; } else { const folderTitle = normalizeString(test_plan_folder); if (!folderTitle) { return toError( "INVALID_TEST_PLAN_FOLDER", "test_plan_folder must be a numeric ID, non-empty title, or null." ); } const cachedContext = getCachedProjectContext(resolvedProjectId); const cachedFolders = mapTestPlanFoldersForLookup( Array.isArray(cachedContext?.test_plan_folders) ? cachedContext.test_plan_folders : [] ); let matchedFolders = findFoldersByTitle(cachedFolders, folderTitle); if (matchedFolders.length !== 1) { const folders = await client.listTestPlanFolders(resolvedProjectId); const liveFolders = mapTestPlanFoldersForLookup( Array.isArray(folders) ? folders : [] ); matchedFolders = findFoldersByTitle(liveFolders, folderTitle); } if (matchedFolders.length === 0) { return toError( "TEST_PLAN_FOLDER_NOT_FOUND", `Test plan folder not found with title "${folderTitle}" in that project.` ); } if (matchedFolders.length > 1) { const matchingIds = matchedFolders.map((folder) => folder.id); return toError( "AMBIGUOUS_TEST_PLAN_FOLDER", `Multiple folders matched "${folderTitle}". Provide folder ID instead.`, { matching_ids: matchingIds } ); } resolvedFolderId = matchedFolders[0].id; } } } // Resolve release (supports ID or title) let resolvedReleaseId: number | undefined; if (release !== undefined) { const numericReleaseId = toNumberId(release); if (numericReleaseId !== undefined) { resolvedReleaseId = numericReleaseId; } else { const releaseTitle = normalizeString(release); if (!releaseTitle) { return toError( "INVALID_RELEASE", "release must be a numeric ID or non-empty title." ); } const cachedContext = getCachedProjectContext(resolvedProjectId); const cachedReleases = mapReleasesForLookup( Array.isArray(cachedContext?.releases) ? cachedContext.releases : [] ); let matchedReleases = findReleasesByTitle(cachedReleases, releaseTitle); if (matchedReleases.length !== 1) { const releases = await client.listReleases(resolvedProjectId); const liveReleases = mapReleasesForLookup( Array.isArray(releases) ? releases : [] ); matchedReleases = findReleasesByTitle(liveReleases, releaseTitle); } if (matchedReleases.length === 0) { return toError( "RELEASE_NOT_FOUND", `Release not found with title "${releaseTitle}" in that project.` ); } if (matchedReleases.length > 1) { const matchingIds = matchedReleases.map((item) => item.id); return toError( "AMBIGUOUS_RELEASE", `Multiple releases matched "${releaseTitle}". Provide release ID instead.`, { matching_ids: matchingIds } ); } resolvedReleaseId = matchedReleases[0].id; } } let resolvedAssignmentUserIds = assignmentUserIds; let resolvedTestCaseAssignee: ResolvedAssigneeValue | undefined = typeof testCaseAssigneeValue === "number" || testCaseAssigneeValue === "me" ? testCaseAssigneeValue : undefined; const testCaseAssigneeLookup = typeof testCaseAssigneeValue === "string" && testCaseAssigneeValue !== "me" ? testCaseAssigneeValue : undefined; if (assignmentUserLookups.length > 0 || testCaseAssigneeLookup !== undefined) { const projectUsersRaw = await client.listProjectUsers(resolvedProjectId); const projectUsers = mapProjectUsersForLookup( Array.isArray(projectUsersRaw) ? projectUsersRaw : [] ); const toUserMatchPayload = (user: ProjectUserLookup) => ({ id: user.id, name: user.name, ...(user.username ? { username: user.username } : {}), ...(user.email ? { email: user.email } : {}), }); if (assignmentUserLookups.length > 0) { const resolvedLookupIds: number[] = []; for (const lookup of assignmentUserLookups) { const matches = findProjectUserMatches(projectUsers, lookup); if (matches.length === 0) { return toError( "ASSIGNEE_NOT_FOUND", `No project user matched "${lookup}" for assignment.user_ids.`, { field: "assignment.user_ids", lookup, } ); } if (matches.length > 1) { return toError( "AMBIGUOUS_ASSIGNEE", `Multiple project users matched "${lookup}" for assignment.user_ids. Use a numeric user ID.`, { field: "assignment.user_ids", lookup, matches: matches.map(toUserMatchPayload), } ); } resolvedLookupIds.push(matches[0].id); } resolvedAssignmentUserIds = dedupeNumbers([ ...assignmentUserIds, ...resolvedLookupIds, ]); } if (testCaseAssigneeLookup) { const matches = findProjectUserMatches(projectUsers, testCaseAssigneeLookup); if (matches.length === 0) { return toError( "ASSIGNEE_NOT_FOUND", `No project user matched "${testCaseAssigneeLookup}" for test_cases.assignee.`, { field: "test_cases.assignee", lookup: testCaseAssigneeLookup, } ); } if (matches.length > 1) { return toError( "AMBIGUOUS_ASSIGNEE", `Multiple project users matched "${testCaseAssigneeLookup}" for test_cases.assignee. Use a numeric user ID.`, { field: "test_cases.assignee", lookup: testCaseAssigneeLookup, matches: matches.map(toUserMatchPayload), } ); } resolvedTestCaseAssignee = matches[0].id; } } // Resolve custom fields for TestPlan entity let resolvedCustomFields: | Array<{ id: number; name: string; label?: string; value: CustomFieldResolvedValue; valueLabel?: string | string[]; color?: string; }> | undefined; if (custom_fields && custom_fields.length > 0) { const project = await client.getProject(resolvedProjectId); const companyId = getCompanyIdFromProject(project); const customFieldList = await client.listProjectCustomFields( resolvedProjectId, companyId, "TestPlan" ); const definitionsByName = new Map<string, CustomFieldDefinition>(); const definitionsById = new Map<number, CustomFieldDefinition>(); customFieldList.forEach((field) => { const id = toNumberId(getField(field, "id")); const name = normalizeString(getField<string>(field, "name")); if (id === undefined || !name) { return; } const fieldType = getField<string>(field, "field_type") ?? getField<string>(field, "type"); const directOptions = getField<unknown[]>(field, "options"); const extra = getField<Record<string, unknown>>(field, "extra"); const extraOptions = extra ? getField<unknown[]>(extra, "options") : undefined; const options = buildOptionLookup(directOptions ?? extraOptions ?? []); const definition: CustomFieldDefinition = { id, name, label: normalizeString(getField<string>(field, "label")), fieldType: normalizeString(fieldType), options, }; definitionsByName.set(name, definition); definitionsById.set(id, definition); }); const missingFields: string[] = []; resolvedCustomFields = custom_fields .map((field) => { const fieldId = toNumberId(field.id); const byId = fieldId !== undefined ? definitionsById.get(fieldId) : undefined; const byName = definitionsByName.get(field.name); const definition = byId ?? byName; if (!definition && fieldId === undefined) { missingFields.push(field.name); return undefined; } const resolvedId = definition?.id ?? fieldId; if (resolvedId === undefined) { missingFields.push(field.name); return undefined; } let resolvedValue: CustomFieldResolvedValue = field.value; let resolvedValueLabel: string | string[] | undefined = field.valueLabel; if (definition && isDropdownFieldType(definition.fieldType)) { if (Array.isArray(field.value)) { throw new Error( `Custom field "${definition.name}" expects a single value, not an array.` ); } if (isNonNumericString(field.value)) { const matchedOption = findOptionByLabel(definition.options, field.value); if (!matchedOption) { throw new Error( `Custom field option "${field.value}" not found for "${definition.name}".` ); } resolvedValue = matchedOption.id; resolvedValueLabel = typeof field.valueLabel === "string" && field.valueLabel.trim().length > 0 ? field.valueLabel : matchedOption.label; } } if (definition && isMultiSelectFieldType(definition.fieldType)) { const inputValues = Array.isArray(field.value) ? field.value : [field.value]; const outputValues: Array<string | number> = []; const outputLabels: string[] = []; inputValues.forEach((inputValue) => { const numeric = toNumberId(inputValue); if (numeric !== undefined) { outputValues.push(numeric); return; } if (isNonNumericString(inputValue)) { const matchedOption = findOptionByLabel( definition.options, inputValue ); if (!matchedOption) { throw new Error( `Custom field option "${inputValue}" not found for "${definition.name}".` ); } outputValues.push(matchedOption.id); outputLabels.push(matchedOption.label); } }); resolvedValue = outputValues; if (Array.isArray(field.valueLabel)) { resolvedValueLabel = field.valueLabel; } else if (outputLabels.length > 0) { resolvedValueLabel = outputLabels; } } return { id: resolvedId, name: definition?.name ?? field.name, ...(field.label !== undefined ? { label: field.label } : definition?.label ? { label: definition.label } : {}), value: resolvedValue, ...(resolvedValueLabel !== undefined ? { valueLabel: resolvedValueLabel } : {}), ...(field.color !== undefined ? { color: field.color } : {}), }; }) .filter( ( field ): field is { id: number; name: string; label?: string; value: CustomFieldResolvedValue; valueLabel?: string | string[]; color?: string; } => field !== undefined ); if (missingFields.length > 0) { return toError( "CUSTOM_FIELD_NOT_FOUND", `Custom field(s) not found: ${Array.from(new Set(missingFields)).join(", ")}` ); } } // Step 1: Create test plan steps.create_test_plan.status = "in_progress"; const createResult = await client.createTestPlan({ projectId: resolvedProjectId, title: normalizedTitle, description, priority, testPlanFolderId: resolvedFolderId, release: resolvedReleaseId, startDate: start_date, endDate: end_date, customFields: resolvedCustomFields, }); const createFailure = apiFailureMessage(createResult); if (createFailure) { steps.create_test_plan.status = "failed"; steps.create_test_plan.detail = createFailure; return toToolResponse( { error: { code: "CREATE_TEST_PLAN_FAILED", message: createFailure, step: "create_test_plan", }, steps, }, true ); } createdPlanId = extractId(createResult); createdPlanTitle = normalizeString(getField<string>(createResult, "title")) ?? normalizedTitle; if (createdPlanId === undefined) { steps.create_test_plan.status = "failed"; steps.create_test_plan.detail = "Create response did not include test plan ID."; return toToolResponse( { error: { code: "INVALID_CREATE_TEST_PLAN_RESPONSE", message: "Create response did not include test plan ID.", step: "create_test_plan", }, steps, }, true ); } steps.create_test_plan.status = "completed"; // Step 2: Bulk add test cases let bulkAddResult: Record<string, unknown> | undefined; if (shouldAddTestCases) { steps.add_test_cases.status = "in_progress"; bulkAddResult = await client.bulkAddTestPlanTestCases({ testplan: createdPlanId, testCaseCollection: toSelectorCollection(testCaseIds, testCaseSelector), ...(resolvedTestCaseAssignee !== undefined ? { assignee: resolvedTestCaseAssignee } : {}), }); const bulkAddFailure = apiFailureMessage(bulkAddResult); if (bulkAddFailure) { steps.add_test_cases.status = "failed"; steps.add_test_cases.detail = bulkAddFailure; return toToolResponse( { error: { code: "ADD_TEST_CASES_FAILED", message: bulkAddFailure, step: "add_test_cases", }, testPlan: { id: createdPlanId, title: createdPlanTitle, project_id: resolvedProjectId, }, steps, }, true ); } steps.add_test_cases.status = "completed"; } // Step 3: Create configurations let createConfigurationsResult: | Array<Record<string, unknown>> | Record<string, unknown> | undefined; let createdConfigurationIds: number[] = []; if (shouldCreateConfigurations) { steps.add_configurations.status = "in_progress"; createConfigurationsResult = await client.createTestPlanConfigurations({ projectId: resolvedProjectId, testplan: createdPlanId, parameters: (configurations ?? []).map((row) => row.map((entry) => ({ ...(entry.id !== undefined ? { id: entry.id } : {}), field: entry.field, value: entry.value, })) ), }); const createConfigurationsFailure = apiFailureMessage(createConfigurationsResult); if (createConfigurationsFailure) { steps.add_configurations.status = "failed"; steps.add_configurations.detail = createConfigurationsFailure; return toToolResponse( { error: { code: "ADD_CONFIGURATIONS_FAILED", message: createConfigurationsFailure, step: "add_configurations", }, testPlan: { id: createdPlanId, title: createdPlanTitle, project_id: resolvedProjectId, }, steps, }, true ); } createdConfigurationIds = extractIds(createConfigurationsResult); steps.add_configurations.status = "completed"; } // Step 4: Assignment let assignResult: Record<string, unknown> | undefined; if (shouldAssign) { steps.assign_test_plan.status = "in_progress"; const assignFromTestCaseAssignee = assignment === undefined && resolvedTestCaseAssignee !== undefined && shouldAddTestCases; const assignmentExecutorForPayload: "me" | "team" = assignFromTestCaseAssignee ? resolvedTestCaseAssignee === "me" ? "me" : "team" : resolvedAssignmentExecutor; const assignmentCriteriaForPayload: "testCase" | "configuration" = assignment?.assignment_criteria ?? "testCase"; const assignmentMethodForPayload: "automatic" | "manual" = assignment?.assignment_method ?? "automatic"; const resolvedAssignmentConfigIds = assignmentCriteriaForPayload === "configuration" ? assignmentConfigurationIds.length > 0 ? assignmentConfigurationIds : createdConfigurationIds : []; if ( assignmentMethodForPayload === "manual" && assignmentCriteriaForPayload === "configuration" && resolvedAssignmentConfigIds.length === 0 ) { steps.assign_test_plan.status = "failed"; steps.assign_test_plan.detail = "No configuration IDs available for manual configuration assignment."; return toToolResponse( { error: { code: "MISSING_ASSIGNMENT_CONFIGURATIONS", message: "No configuration IDs available for manual configuration assignment.", step: "assign_test_plan", details: { missing_fields: ["assignment.configuration_ids", "configurations"], follow_up_questions: [ missingInfoQuestionByField["assignment.configuration_ids"], missingInfoQuestionByField["configurations"], ], }, }, testPlan: { id: createdPlanId, title: createdPlanTitle, project_id: resolvedProjectId, }, steps, }, true ); } const assignmentUsersForPayload: Array<number | "me"> = assignFromTestCaseAssignee && resolvedTestCaseAssignee !== undefined ? [resolvedTestCaseAssignee] : assignmentExecutorForPayload === "me" ? ["me"] : resolvedAssignmentUserIds; assignResult = await client.assignTestPlan({ projectId: resolvedProjectId, testplan: createdPlanId, executor: assignmentExecutorForPayload, assignmentCriteria: assignmentCriteriaForPayload, assignmentMethod: assignmentMethodForPayload, assignment: { user: assignmentUsersForPayload, testCases: assignFromTestCaseAssignee ? toSelectorCollection(testCaseIds, testCaseSelector) : toSelectorCollection(assignmentTestCaseIds, assignmentSelector), configuration: assignmentCriteriaForPayload === "configuration" ? resolvedAssignmentConfigIds : null, }, }); const assignFailure = apiFailureMessage(assignResult); if (assignFailure) { steps.assign_test_plan.status = "failed"; steps.assign_test_plan.detail = assignFailure; return toToolResponse( { error: { code: "ASSIGN_TEST_PLAN_FAILED", message: assignFailure, step: "assign_test_plan", }, testPlan: { id: createdPlanId, title: createdPlanTitle, project_id: resolvedProjectId, }, steps, }, true ); } steps.assign_test_plan.status = "completed"; } return toToolResponse( { success: true, message: "Test plan created successfully", testPlan: { id: createdPlanId, title: createdPlanTitle, project_id: resolvedProjectId, }, steps, results: { ...(bulkAddResult !== undefined ? { add_test_cases: bulkAddResult } : {}), ...(createConfigurationsResult !== undefined ? { add_configurations: createConfigurationsResult } : {}), ...(assignResult !== undefined ? { assign_test_plan: assignResult } : {}), }, }, true ); } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; return toToolResponse( { error: { code: "API_ERROR", message, }, ...(createdPlanId !== undefined ? { testPlan: { id: createdPlanId, title: createdPlanTitle ?? normalizedTitle, project_id: resolvedProjectId, }, } : {}), steps, }, true ); } } - The Zod schema definition for input validation of the "create_test_plan" tool.
export const createTestPlanSchema = z.object({ project_id: z .number() .optional() .describe("Project ID (optional if TC_DEFAULT_PROJECT is set)"), title: z .string() .optional() .describe( 'Test plan title (optional; defaults to "Test Plan DD Month YYYY HH:mm:ss")' ), description: z.string().optional().describe("Test plan description (HTML supported)"), priority: z .number() .min(0) .max(2) .optional() .describe("Priority: 0=Low, 1=Normal, 2=High"), test_plan_folder: z .union([z.number(), z.string(), z.null()]) .optional() .describe("Test plan folder ID or title (null to place at root)"), release: z .union([z.number(), z.string()]) .optional() .describe("Release ID or title"), start_date: z .string() .optional() .describe("Planned start date (YYYY-MM-DD)"), end_date: z .string() .optional() .describe("Planned end date (YYYY-MM-DD)"), custom_fields: z .array(customFieldSchema) .optional() .describe("Array of test plan custom field values"), test_cases: testCasesSchema .optional() .describe("Test cases to bulk-add immediately after plan creation"), configurations: z .array(z.array(configurationPairSchema)) .optional() .describe("Configuration matrix to attach to the test plan"), assignment: assignmentSchema .optional() .describe("Assignment payload to execute after creation"), }); - src/tools/index.ts:326-330 (registration)Tool registration where "create_test_plan" is linked to its handler.
createTestPlanTool.name, createTestPlanTool.description, createTestPlanSchema.shape, async (args) => { return handleCreateTestPlan(args);