get_sections
Retrieve test sections for a specific project and suite with pagination controls to organize and access TestRail test management data efficiently.
Instructions
Get a list of sections for a project and test suite with optional pagination.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | Yes | TestRail project ID | |
| suite_id | No | TestRail suite ID (optional if project is in single suite mode) | |
| limit | No | The number of sections to return (max 250, default 250) | |
| offset | No | Where to start counting the sections from (pagination offset) |
Implementation Reference
- src/server.ts:527-564 (handler)The main MCP tool handler function for 'get_sections'. It processes input parameters, constructs filters, calls the service layer getSections, formats the response as JSON text content, and handles errors with specific messages.async ({ project_id, suite_id, limit, offset }) => { logger.debug(`Get sections tool called with project_id: ${project_id}, suite_id: ${suite_id}`); try { const filters = { project_id, ...(suite_id !== undefined && { suite_id }), ...(limit !== undefined && { limit }), ...(offset !== undefined && { offset }), }; const result = await getSections(filters); logger.debug(`Get sections tool completed successfully for project_id: ${project_id}. Found ${result.sections.length} sections (total: ${result.size})`); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (err) { logger.error({ err }, `Get sections tool failed for project_id: ${project_id}`); const e = err as { type?: string; status?: number; message?: string }; let message = 'Unexpected error'; if (e?.type === 'auth') message = 'Authentication failed: check TESTRAIL_USER/API_KEY'; else if (e?.type === 'not_found') message = `Project ${project_id} or suite ${suite_id} not found`; else if (e?.type === 'rate_limited') message = 'Rate limited by TestRail; try again later'; else if (e?.type === 'server') message = 'TestRail server error'; else if (e?.type === 'network') message = 'Network error contacting TestRail'; else if (e?.message) message = e.message; return { content: [ { type: 'text', text: message }, ], isError: true, }; }
- src/server.ts:520-525 (schema)Zod input schema validation for the get_sections tool parameters: project_id (required), suite_id/limit/offset (optional).inputSchema: { project_id: z.number().int().positive().describe('TestRail project ID'), suite_id: z.number().int().positive().optional().describe('TestRail suite ID (optional if project is in single suite mode)'), limit: z.number().int().positive().optional().describe('The number of sections to return (max 250, default 250)'), offset: z.number().int().min(0).optional().describe('Where to start counting the sections from (pagination offset)'), },
- src/server.ts:515-566 (registration)MCP server registration of the 'get_sections' tool, including title, description, input schema, and handler function reference.server.registerTool( 'get_sections', { title: 'Get TestRail Sections', description: 'Get a list of sections for a project and test suite with optional pagination.', inputSchema: { project_id: z.number().int().positive().describe('TestRail project ID'), suite_id: z.number().int().positive().optional().describe('TestRail suite ID (optional if project is in single suite mode)'), limit: z.number().int().positive().optional().describe('The number of sections to return (max 250, default 250)'), offset: z.number().int().min(0).optional().describe('Where to start counting the sections from (pagination offset)'), }, }, async ({ project_id, suite_id, limit, offset }) => { logger.debug(`Get sections tool called with project_id: ${project_id}, suite_id: ${suite_id}`); try { const filters = { project_id, ...(suite_id !== undefined && { suite_id }), ...(limit !== undefined && { limit }), ...(offset !== undefined && { offset }), }; const result = await getSections(filters); logger.debug(`Get sections tool completed successfully for project_id: ${project_id}. Found ${result.sections.length} sections (total: ${result.size})`); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (err) { logger.error({ err }, `Get sections tool failed for project_id: ${project_id}`); const e = err as { type?: string; status?: number; message?: string }; let message = 'Unexpected error'; if (e?.type === 'auth') message = 'Authentication failed: check TESTRAIL_USER/API_KEY'; else if (e?.type === 'not_found') message = `Project ${project_id} or suite ${suite_id} not found`; else if (e?.type === 'rate_limited') message = 'Rate limited by TestRail; try again later'; else if (e?.type === 'server') message = 'TestRail server error'; else if (e?.type === 'network') message = 'Network error contacting TestRail'; else if (e?.message) message = e.message; return { content: [ { type: 'text', text: message }, ], isError: true, }; } }, );
- Service layer adapter for get_sections: maps input filters to client params, calls TestRail client, normalizes response by extracting custom_ fields into a custom object for each section.export async function getSections(filters: GetSectionsFilters): Promise<SectionsResponse> { // Transform service filters to client parameters const clientParams: GetSectionsParams = { project_id: filters.project_id, suite_id: filters.suite_id, limit: filters.limit, offset: filters.offset, }; const response: TestRailSectionsResponse = await testRailClient.getSections(clientParams); // Transform each section using the same pattern as other entities const transformedSections: SectionSummary[] = response.sections.map((sectionData) => { const { depth, display_order, id, name, parent_id, suite_id, ...rest } = sectionData; const custom: Record<string, unknown> = {}; for (const [key, value] of Object.entries(rest)) { if (key.startsWith('custom_')) custom[key] = value; } return { depth, display_order, id, name, parent_id, suite_id, custom: Object.keys(custom).length ? custom : undefined, }; }); return { offset: response.offset, limit: response.limit, size: response.size, _links: response._links, sections: transformedSections, }; }
- Low-level TestRailClient getSections method: constructs API URL with query params for /get_sections/{project_id}, performs HTTP GET, validates paginated response format, handles retries and errors.async getSections(params: GetSectionsParams): Promise<TestRailSectionsResponse> { try { // Build query parameters const queryParams = new URLSearchParams(); // Handle suite_id parameter if (params.suite_id !== undefined) { queryParams.append('suite_id', params.suite_id.toString()); } // Handle pagination parameters if (params.limit !== undefined) { queryParams.append('limit', params.limit.toString()); } if (params.offset !== undefined) { queryParams.append('offset', params.offset.toString()); } const queryString = queryParams.toString(); const url = `/get_sections/${params.project_id}${queryString ? `&${queryString}` : ''}`; const res = await this.http.get(url); logger.info({ status: res.status, dataType: typeof res.data, hasSectionsProperty: res.data && typeof res.data === 'object' && 'sections' in res.data, projectId: params.project_id, suiteId: params.suite_id, queryParams: Object.fromEntries(queryParams) }, 'TestRail getSections response info'); if (res.status >= 200 && res.status < 300) { // TestRail get_sections always returns paginated format if (res.data && typeof res.data === 'object' && 'sections' in res.data) { const paginatedResponse = res.data as TestRailSectionsResponse; if (!Array.isArray(paginatedResponse.sections)) { throw Object.assign(new Error('API returned response with non-array sections field'), { response: { status: 200 } // Make it look like a server error }); } logger.info({ totalSections: paginatedResponse.size, returnedSections: paginatedResponse.sections.length, offset: paginatedResponse.offset, limit: paginatedResponse.limit, hasNext: paginatedResponse._links?.next !== null, hasPrev: paginatedResponse._links?.prev !== null }, 'TestRail getSections paginated response'); return paginatedResponse; } else { logger.error({ status: res.status, responseData: res.data, dataType: typeof res.data }, 'TestRail getSections returned unexpected response format'); throw Object.assign(new Error('API returned unexpected response format'), { response: { status: 200 } // Make it look like a server error }); } } throw Object.assign(new Error(`HTTP ${res.status}`), { response: res }); } catch (err) { const normalized = this.normalizeError(err); const safeDetails = this.getSafeErrorDetails(err); logger.error({ err: normalized, details: safeDetails, params }, 'TestRail getSections failed'); throw normalized; } }