Skip to main content
Glama
Dsazz

JIRA MCP Server

search_jira_issues

Search JIRA issues using JQL queries or simplified filters to find tickets by project, status, assignee, or text content.

Instructions

Search JIRA issues using JQL queries or helper parameters. Supports both expert JQL and beginner-friendly filters.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
jqlNo
assignedToMeNo
projectNo
statusNo
textNo
maxResultsNo
fieldsNo

Implementation Reference

  • SearchIssuesHandler class: core tool handler that validates params, executes search via use-case, formats results, and handles errors.
    export class SearchIssuesHandler extends BaseToolHandler<
      SearchJiraIssuesParams,
      string
    > {
      private readonly formatter: IssueListFormatter;
    
      /**
       * Create a new SearchIssuesHandler with use case
       *
       * @param searchIssuesUseCase - Use case for searching issues with validation
       */
      constructor(private readonly searchIssuesUseCase: SearchIssuesUseCase) {
        super("JIRA", "Search Issues");
        this.formatter = new IssueListFormatter();
      }
    
      /**
       * Execute the handler logic
       * Searches for JIRA issues using JQL query and formats results
       * Delegates business logic to the use case
       *
       * @param params - Search parameters with JQL or helper parameters
       */
      protected async execute(params: SearchJiraIssuesParams): Promise<string> {
        try {
          // Step 1: Validate parameters
          const validatedParams = this.validateParameters(params);
          this.logger.info("Searching JIRA issues");
    
          // Step 2: Map parameters to use case request
          const useCaseRequest: SearchIssuesUseCaseRequest =
            this.mapToUseCaseRequest(validatedParams);
    
          // Step 3: Execute the use case
          this.logger.debug("Delegating to SearchIssuesUseCase", {
            jql: validatedParams.jql,
            hasFilters:
              !validatedParams.jql &&
              (!!validatedParams.text ||
                !!validatedParams.project ||
                !!validatedParams.status ||
                !!validatedParams.assignedToMe),
          });
    
          const issues = await this.searchIssuesUseCase.execute(useCaseRequest);
    
          // Step 4: Format and return success response
          this.logger.info(`Successfully found ${issues.length} issues`);
          return this.formatter.format(issues);
        } catch (error) {
          this.logger.error(`Failed to search JIRA issues: ${error}`);
          throw this.enhanceError(error);
        }
      }
    
      /**
       * Validate parameters using Zod schema
       */
      private validateParameters(
        params: SearchJiraIssuesParams,
      ): SearchJiraIssuesParams {
        const result = searchJiraIssuesBaseSchema.safeParse(params);
    
        if (!result.success) {
          const errorMessage = `Invalid search parameters: ${formatZodError(
            result.error,
          )}`;
          throw JiraApiError.withStatusCode(errorMessage, 400);
        }
    
        return result.data;
      }
    
      /**
       * Map handler parameters to use case request
       * Maps search parameters from handler format to use case format
       */
      private mapToUseCaseRequest(
        params: SearchJiraIssuesParams,
      ): SearchIssuesUseCaseRequest {
        return {
          jql: params.jql,
          text: params.text,
          project: params.project,
          status: params.status,
          assignedToMe: params.assignedToMe,
          maxResults: params.maxResults,
          fields: params.fields,
        };
      }
    
      /**
       * Enhance error messages for better user guidance
       */
      private enhanceError(error: unknown): Error {
        if (error instanceof JiraNotFoundError) {
          return new Error(
            `❌ **No Issues Found**\n\nNo issues found matching your search criteria.\n\n**Solutions:**\n- Try broadening your search terms\n- Check your JQL syntax if using custom queries\n- Verify you have permission to view the projects\n\n**Example:** \`search_jira_issues text="bug" project="PROJ"\``,
          );
        }
    
        if (error instanceof JiraPermissionError) {
          return new Error(
            `❌ **Permission Denied**\n\nYou don't have permission to search issues.\n\n**Solutions:**\n- Check your JIRA permissions\n- Contact your JIRA administrator\n- Verify you're logged in with the correct account\n\n**Required Permissions:** Browse Projects`,
          );
        }
    
        if (error instanceof JiraApiError) {
          return new Error(
            `❌ **JIRA API Error**\n\n${error.message}\n\n**Solutions:**\n- Check your JQL syntax if using custom queries\n- Verify project keys are correct\n- Try simpler search criteria\n\n**Example:** \`search_jira_issues jql="project = PROJ AND status = Open"\``,
          );
        }
    
        if (error instanceof Error) {
          return new Error(
            `❌ **Search Failed**\n\n${error.message}\n\n**Solutions:**\n- Check your search parameters are valid\n- Try a simpler query first\n- Verify your JIRA connection\n\n**Example:** \`search_jira_issues text="bug"\``,
          );
        }
    
        return new Error(
          "❌ **Unknown Error**\n\nAn unknown error occurred during issue search.\n\nPlease check your parameters and try again.",
        );
      }
    }
  • Zod schemas for tool input validation: base schema, refined schema requiring at least one search param, and inferred TypeScript type.
    export const searchJiraIssuesBaseSchema = z.object({
      // Advanced JQL option
      jql: z.string().min(1).optional(),
    
      // Helper parameters (ignored if jql provided)
      assignedToMe: z.boolean().optional(),
      project: z.string().optional(),
      status: z.union([z.string(), z.array(z.string())]).optional(),
      text: z.string().optional(),
    
      // Common options
      maxResults: z.number().min(1).max(50).default(25),
      fields: z.array(z.string()).optional(),
    });
    
    /**
     * Schema for search JIRA issues with hybrid JQL + helper parameters
     */
    export const searchJiraIssuesSchema = searchJiraIssuesBaseSchema.refine(
      (data) =>
        data.jql || data.assignedToMe || data.project || data.status || data.text,
      {
        message:
          "Either 'jql' parameter or at least one helper parameter must be provided",
      },
    );
    
    /**
     * Type for search parameters
     */
    export type SearchJiraIssuesParams = z.infer<typeof searchJiraIssuesSchema>;
  • Tool registration configuration: defines tool name, description, input params schema, and binds the handler.
    {
      name: "search_jira_issues",
      description: "Search JIRA issues using JQL queries or helper parameters. Supports both expert JQL and beginner-friendly filters.",
      params: searchJiraIssuesBaseSchema.shape,
      handler: tools.jira_search_issues.handle.bind(tools.jira_search_issues),
    },
  • Factory instantiation of SearchIssuesHandler and creation of the jira_search_issues handler wrapper for tool registry.
    const searchIssuesHandler = new SearchIssuesHandler(
      dependencies.searchIssuesUseCase,
    );
    
    return {
      jira_get_issue: {
        handle: async (args: unknown) => getIssueHandler.handle(args),
      },
      jira_get_issue_comments: {
        handle: async (args: unknown) => getIssueCommentsHandler.handle(args),
      },
      jira_get_assigned_issues: {
        handle: async (args: unknown) => getAssignedIssuesHandler.handle(args),
      },
      jira_create_issue: {
        handle: async (args: unknown) => createIssueHandler.handle(args),
      },
      jira_update_issue: {
        handle: async (args: unknown) => updateIssueHandler.handle(args),
      },
      jira_search_issues: {
        handle: async (args: unknown) => searchIssuesHandler.handle(args),
      },
  • SearchIssuesUseCaseImpl: business logic for building JQL queries from params and executing issue search via repository.
    export class SearchIssuesUseCaseImpl implements SearchIssuesUseCase {
      /**
       * Create a new SearchIssuesUseCase implementation
       *
       * @param issueSearchRepository - Repository for searching issues
       */
      constructor(private readonly issueSearchRepository: IssueSearchRepository) {}
    
      /**
       * Execute the search issues use case
       *
       * @param request - Search parameters
       * @returns List of JIRA issues matching the search criteria
       */
      public async execute(request: SearchIssuesUseCaseRequest): Promise<Issue[]> {
        try {
          logger.debug("Executing search issues use case", {
            prefix: "JIRA:SearchIssuesUseCase",
            request,
          });
    
          // Build JQL query from search parameters
          const jqlQuery = this.buildJQLQuery(request);
    
          // Prepare search options
          const searchOptions: SearchIssuesOptions = {
            jql: jqlQuery,
            fields: request.fields || [
              "summary",
              "status",
              "priority",
              "assignee",
              "created",
              "updated",
            ],
            maxResults: request.maxResults || 25,
            startAt: 0,
          };
    
          // Search for issues using repository
          const searchResult =
            await this.issueSearchRepository.searchIssues(searchOptions);
    
          logger.debug(`Found ${searchResult.issues.length} issues`, {
            prefix: "JIRA:SearchIssuesUseCase",
            total: searchResult.total,
          });
    
          return searchResult.issues;
        } catch (error) {
          logger.error("Failed to search issues", {
            prefix: "JIRA:SearchIssuesUseCase",
            error: error instanceof Error ? error.message : String(error),
          });
    
          // Rethrow with better context if needed
          if (error instanceof Error) {
            throw JiraApiError.withStatusCode(
              `Failed to search issues: ${error.message}`,
              400,
            );
          }
          throw error;
        }
      }
    
      /**
       * Build JQL query from search parameters
       *
       * @param params - Search parameters
       * @returns JQL query string
       */
      private buildJQLQuery(params: SearchIssuesUseCaseRequest): string {
        // If JQL is provided directly, use it
        if (params.jql) {
          return params.jql;
        }
    
        // Build JQL from helper parameters
        const conditions: string[] = [];
    
        if (params.assignedToMe) {
          conditions.push("assignee = currentUser()");
        }
    
        if (params.project) {
          conditions.push(`project = "${params.project}"`);
        }
    
        if (params.status) {
          if (Array.isArray(params.status)) {
            const statusList = params.status.map((s) => `"${s}"`).join(", ");
            conditions.push(`status IN (${statusList})`);
          } else {
            conditions.push(`status = "${params.status}"`);
          }
        }
    
        if (params.text) {
          conditions.push(`text ~ "${params.text}"`);
        }
    
        // If no conditions, search for all issues
        if (conditions.length === 0) {
          return "ORDER BY updated DESC";
        }
    
        return `${conditions.join(" AND ")} ORDER BY updated DESC`;
      }
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions the tool 'supports both expert JQL and beginner-friendly filters,' which adds some context about flexibility. However, it lacks critical behavioral details such as whether this is a read-only operation, potential rate limits, authentication requirements, pagination behavior (implied by 'maxResults' but not explained), or what the output format looks like.

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

Conciseness5/5

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

The description is appropriately sized and front-loaded with the core purpose in the first clause. Both sentences earn their place by explaining the search methods and user expertise levels, with zero wasted words or redundancy.

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

Completeness2/5

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

Given the tool's complexity (7 parameters, no annotations, no output schema), the description is incomplete. It fails to explain parameter meanings, output structure, behavioral constraints, or differentiation from siblings. For a search tool with multiple input options and no structured guidance, this leaves significant gaps for an AI agent to understand and use it correctly.

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

Parameters2/5

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

Schema description coverage is 0%, so the description must compensate for all 7 parameters. The description only generically mentions 'JQL queries or helper parameters' without explaining what specific parameters are available (e.g., 'assignedToMe', 'project', 'status', 'text', 'maxResults', 'fields') or their semantics. This leaves most parameters undocumented and unclear in purpose.

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

Purpose4/5

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

The description clearly states the tool's purpose: 'Search JIRA issues using JQL queries or helper parameters.' It specifies the verb ('search'), resource ('JIRA issues'), and two search methods. However, it doesn't explicitly differentiate from sibling tools like 'jira_get_assigned_issues' or 'jira_get_issue', which are more specific retrieval tools.

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

Usage Guidelines3/5

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

The description implies usage context by mentioning 'expert JQL and beginner-friendly filters,' suggesting this tool is versatile for different user expertise levels. However, it provides no explicit guidance on when to use this versus alternatives like 'jira_get_assigned_issues' or 'jira_get_issue', nor does it mention prerequisites or exclusions.

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/Dsazz/mcp-jira'

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