Skip to main content
Glama
Derrbal

TestRail MCP Server

by Derrbal

Add TestRail Case

add_case

Create new test cases in TestRail by specifying title, section, type, priority, and custom fields to organize testing requirements systematically.

Instructions

Create a new TestRail test case in a specific section. IMPORTANT: Before creating a case, gather required information using get_projects, get_suites, get_sections, and get_case_fields tools to ensure proper section_id, type_id, and custom field values. Or ask the user to provide the information if not provided.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
titleYesTest case title - should be descriptive and unique within the section
section_idYesSection ID where the case will be created. REQUIRED: Use get_sections tool first to find valid section IDs for your project/suite. Different projects have different section structures.
type_idNoTest case type ID (e.g., 1=Acceptance, 2=Accessibility, 3=Automated, 4=Compatibility, 5=Destructive, 6=Functional, 7=Other, 8=Performance, 9=Regression, 10=Security, 11=Smoke & Sanity, 12=Usability). RECOMMENDED: Use get_cases tool to see what type_id values are used in existing cases in your target section.
priority_idNoPriority ID (1=Low, 2=Medium, 3=High, 4=Critical). RECOMMENDED: Use get_cases tool to see what priority_id values are used in existing cases.
refsNoReferences (e.g., requirement IDs, JIRA tickets, user story numbers). Can be comma-separated for multiple references.
customNoCustom fields (key-value pairs). REQUIRED: Use get_case_fields tool first to discover available custom fields and their valid values. Common fields include: custom_automation_type (1=None, 2=Playwright, 3=ChatGPT, 4=Non-Automated, 5=Partial), custom_environment (1=UAT Only, 2=UAT/Prod, 3=Demo UAT, 4=Live UAT), custom_preconds (preconditions text), custom_steps (test steps text), custom_expected (expected results text). Some custom fields are required by the project configuration.

Implementation Reference

  • Inline handler function for the 'add_case' MCP tool. Validates inputs via Zod schema, constructs CaseCreatePayload, calls addCase service, formats success/error responses.
    server.registerTool(
      'add_case',
      {
        title: 'Add TestRail Case',
        description: 'Create a new TestRail test case in a specific section. IMPORTANT: Before creating a case, gather required information using get_projects, get_suites, get_sections, and get_case_fields tools to ensure proper section_id, type_id, and custom field values. Or ask the user to provide the information if not provided.',
        inputSchema: {
          title: z.string().min(1).describe('Test case title - should be descriptive and unique within the section'),
          section_id: z.number().int().positive().describe('Section ID where the case will be created. REQUIRED: Use get_sections tool first to find valid section IDs for your project/suite. Different projects have different section structures.'),
          type_id: z.number().int().positive().optional().describe('Test case type ID (e.g., 1=Acceptance, 2=Accessibility, 3=Automated, 4=Compatibility, 5=Destructive, 6=Functional, 7=Other, 8=Performance, 9=Regression, 10=Security, 11=Smoke & Sanity, 12=Usability). RECOMMENDED: Use get_cases tool to see what type_id values are used in existing cases in your target section.'),
          priority_id: z.number().int().positive().optional().describe('Priority ID (1=Low, 2=Medium, 3=High, 4=Critical). RECOMMENDED: Use get_cases tool to see what priority_id values are used in existing cases.'),
          refs: z.string().nullable().optional().describe('References (e.g., requirement IDs, JIRA tickets, user story numbers). Can be comma-separated for multiple references.'),
          custom: z.record(z.string(), z.unknown()).optional().describe('Custom fields (key-value pairs). REQUIRED: Use get_case_fields tool first to discover available custom fields and their valid values. Common fields include: custom_automation_type (1=None, 2=Playwright, 3=ChatGPT, 4=Non-Automated, 5=Partial), custom_environment (1=UAT Only, 2=UAT/Prod, 3=Demo UAT, 4=Live UAT), custom_preconds (preconditions text), custom_steps (test steps text), custom_expected (expected results text). Some custom fields are required by the project configuration.'),
        },
      },
      async ({ title, section_id, type_id, priority_id, refs, custom }) => {
        logger.debug(`Add case tool called with section_id: ${section_id}, title: ${title}`);
        try {
          const payload: CaseCreatePayload = {
            title,
            section_id,
            type_id,
            priority_id,
            refs,
            custom,
          };
          
          const result = await addCase(payload);
          logger.debug(`Add case tool completed successfully. Case ID: ${result.id}`);
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(result, null, 2),
              },
            ],
          };
        } catch (err) {
          logger.error({ err }, `Add case tool failed for section_id: ${section_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 = `Section ${section_id} not found. Use get_sections tool to find valid section IDs for your project.`;
          else if (e?.type === 'validation_error') message = `Validation error: ${e.message}. Check custom field values using get_case_fields tool and ensure required fields are provided.`;
          else if (e?.type === 'permission_denied') message = `Permission denied for section ${section_id}. Try a different project or section using get_projects and get_sections tools.`;
          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,
          };
        }
      },
    );
  • Zod input schema for 'add_case' tool defining required/optional parameters with descriptions and validation rules.
    inputSchema: {
      title: z.string().min(1).describe('Test case title - should be descriptive and unique within the section'),
      section_id: z.number().int().positive().describe('Section ID where the case will be created. REQUIRED: Use get_sections tool first to find valid section IDs for your project/suite. Different projects have different section structures.'),
      type_id: z.number().int().positive().optional().describe('Test case type ID (e.g., 1=Acceptance, 2=Accessibility, 3=Automated, 4=Compatibility, 5=Destructive, 6=Functional, 7=Other, 8=Performance, 9=Regression, 10=Security, 11=Smoke & Sanity, 12=Usability). RECOMMENDED: Use get_cases tool to see what type_id values are used in existing cases in your target section.'),
      priority_id: z.number().int().positive().optional().describe('Priority ID (1=Low, 2=Medium, 3=High, 4=Critical). RECOMMENDED: Use get_cases tool to see what priority_id values are used in existing cases.'),
      refs: z.string().nullable().optional().describe('References (e.g., requirement IDs, JIRA tickets, user story numbers). Can be comma-separated for multiple references.'),
      custom: z.record(z.string(), z.unknown()).optional().describe('Custom fields (key-value pairs). REQUIRED: Use get_case_fields tool first to discover available custom fields and their valid values. Common fields include: custom_automation_type (1=None, 2=Playwright, 3=ChatGPT, 4=Non-Automated, 5=Partial), custom_environment (1=UAT Only, 2=UAT/Prod, 3=Demo UAT, 4=Live UAT), custom_preconds (preconditions text), custom_steps (test steps text), custom_expected (expected results text). Some custom fields are required by the project configuration.'),
    },
  • Service layer function addCase that transforms high-level payload to TestRailCaseCreateDto (adds custom_ prefix), calls TestRail client, normalizes response to CaseSummary.
    export async function addCase(payload: CaseCreatePayload): Promise<CaseSummary> {
      // Transform the payload to match TestRail API format
      const createPayload: TestRailCaseCreateDto = {
        title: payload.title,
        section_id: payload.section_id,
        type_id: payload.type_id,
        priority_id: payload.priority_id,
        refs: payload.refs,
      };
    
      // Add custom fields with proper naming convention
      if (payload.custom) {
        for (const [key, value] of Object.entries(payload.custom)) {
          // Ensure custom field keys have the 'custom_' prefix
          const fieldKey = key.startsWith('custom_') ? key : `custom_${key}`;
          createPayload[fieldKey] = value;
        }
      }
    
      const data: TestRailCaseDto = await testRailClient.addCase(payload.section_id, createPayload);
      
      // Normalize the response using the same logic as getCase
      const {
        id,
        title,
        section_id,
        type_id,
        priority_id,
        refs,
        created_on,
        updated_on,
        ...rest
      } = data;
    
      const custom: Record<string, unknown> = {};
      for (const [key, value] of Object.entries(rest)) {
        if (key.startsWith('custom_')) custom[key] = value;
      }
    
      return {
        id,
        title,
        section_id,
        type_id,
        priority_id,
        refs: refs ?? null,
        created_on,
        updated_on,
        custom: Object.keys(custom).length ? custom : undefined,
      };
    }
  • TypeScript interface CaseCreatePayload defining the structure expected by the addCase service function.
    export interface CaseCreatePayload {
      title: string;
      section_id: number;
      type_id?: number;
      priority_id?: number;
      refs?: string | null;
      custom?: Record<string, unknown>;
    }
  • HTTP client method that performs POST request to TestRail API endpoint /add_case/{sectionId} to create the case.
    async addCase(sectionId: number, caseData: TestRailCaseCreateDto): Promise<TestRailCaseDto> {
      try {
        const res = await this.http.post(`/add_case/${sectionId}`, caseData);
        if (res.status >= 200 && res.status < 300) {
          logger.info({
            message: 'Successfully created test case',
            sectionId,
            caseId: res.data.id,
            responseSize: JSON.stringify(res.data).length,
          });
          return res.data as TestRailCaseDto;
        }
        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, sectionId }, 'TestRail addCase failed');
        throw normalized;
      }
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden. It clearly indicates this is a creation/mutation operation ('Create a new TestRail test case'), mentions important prerequisites (gathering data from other tools), and hints at complexity (custom fields may be required by project configuration). However, it doesn't explicitly mention authentication needs, rate limits, or what happens on failure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized (3 sentences) and front-loaded with the core purpose. Every sentence adds value: first states what the tool does, second provides crucial prerequisite guidance, third offers alternative approach. Could be slightly more concise by combining the last two sentences.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a creation tool with 6 parameters (including nested objects) and no annotations/output schema, the description does well by emphasizing prerequisites and complexity. However, it doesn't describe what the tool returns (no output schema exists) or potential error conditions, leaving some gaps for a mutation operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 6 parameters thoroughly. The description doesn't add significant parameter semantics beyond what's in the schema - it references the same tools (get_sections, get_case_fields) that the schema descriptions mention. Baseline 3 is appropriate when schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states 'Create a new TestRail test case in a specific section' - a clear verb ('Create') + resource ('TestRail test case') + location constraint ('in a specific section'). This distinguishes it from sibling tools like 'update_case' (modification) and 'get_case' (retrieval).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool: 'Before creating a case, gather required information using get_projects, get_suites, get_sections, and get_case_fields tools' and 'Or ask the user to provide the information if not provided.' It names specific prerequisite tools and offers an alternative approach when data is missing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Derrbal/testrail-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server