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`; } }

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