search_tickets
Search for Jira tickets in specified projects using text queries to find relevant issues quickly.
Instructions
Search for tickets in specific projects using text search
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| searchText | Yes | The text to search for in tickets | |
| projectKeys | Yes | Comma-separated list of project keys | |
| maxResults | No | Maximum number of results to return |
Implementation Reference
- src/server.ts:385-452 (handler)The handler function for the 'search_tickets' tool. It validates Jira configuration, processes project keys, escapes the search text for JQL, performs a text search across specified projects, fetches issue details including descriptions, and formats the results.async ({ searchText, projectKeys, maxResults = 50 }: { searchText: string; projectKeys: string; maxResults?: number }) => { const configError = validateJiraConfig(); if (configError) { return { content: [{ type: "text", text: `Configuration error: ${configError}` }], }; } try { // Validate and format project keys const projects = validateAndFormatProjectKeys(projectKeys); if (projects.length === 0) { return { content: [{ type: "text", text: "No valid project keys provided. Please provide at least one project key." }], }; } // Escape the search text for JQL const escapedText = escapeJQLText(searchText); // Construct the JQL query const jql = `text ~ "${escapedText}" AND project IN (${projects.join(',')}) ORDER BY updated DESC`; // Execute the search with description field included const searchResults = await jira.issueSearch.searchForIssuesUsingJql({ jql, maxResults, fields: ['summary', 'status', 'updated', 'project', 'description'], }); if (!searchResults.issues || searchResults.issues.length === 0) { return { content: [{ type: "text", text: `No tickets found matching "${searchText}" in projects: ${projects.join(', ')}` }], }; } // Format the results with descriptions const formattedResults = searchResults.issues.map(issue => { const summary = issue.fields?.summary || 'No summary'; const status = issue.fields?.status?.name || 'Unknown status'; const project = issue.fields?.project?.key || 'Unknown project'; const updated = issue.fields?.updated ? new Date(issue.fields.updated).toLocaleString() : 'Unknown date'; const description = issue.fields?.description ? extractTextFromADF(issue.fields.description) : 'No description'; return `[${project}] ${issue.key}: ${summary} Status: ${status} (Updated: ${updated}) Description: ${description.trim()} ----------------------------------------\n`; }).join('\n'); const totalResults = searchResults.total || 0; const headerText = `Found ${totalResults} ticket${totalResults !== 1 ? 's' : ''} matching "${searchText}"\n\n`; return { content: [{ type: "text", text: headerText + formattedResults }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: "text", text: `Failed to search tickets: ${errorMessage}` }], }; } }
- src/server.ts:380-384 (schema)Input schema defined using Zod for the search_tickets tool parameters: searchText (required string), projectKeys (required string), maxResults (optional number).{ searchText: z.string().describe("The text to search for in tickets"), projectKeys: z.string().describe("Comma-separated list of project keys"), maxResults: z.number().optional().describe("Maximum number of results to return"), },
- src/server.ts:377-453 (registration)Registration of the 'search_tickets' MCP tool via server.tool(), specifying name, description, input schema, and inline handler function.server.tool( "search_tickets", "Search for tickets in specific projects using text search", { searchText: z.string().describe("The text to search for in tickets"), projectKeys: z.string().describe("Comma-separated list of project keys"), maxResults: z.number().optional().describe("Maximum number of results to return"), }, async ({ searchText, projectKeys, maxResults = 50 }: { searchText: string; projectKeys: string; maxResults?: number }) => { const configError = validateJiraConfig(); if (configError) { return { content: [{ type: "text", text: `Configuration error: ${configError}` }], }; } try { // Validate and format project keys const projects = validateAndFormatProjectKeys(projectKeys); if (projects.length === 0) { return { content: [{ type: "text", text: "No valid project keys provided. Please provide at least one project key." }], }; } // Escape the search text for JQL const escapedText = escapeJQLText(searchText); // Construct the JQL query const jql = `text ~ "${escapedText}" AND project IN (${projects.join(',')}) ORDER BY updated DESC`; // Execute the search with description field included const searchResults = await jira.issueSearch.searchForIssuesUsingJql({ jql, maxResults, fields: ['summary', 'status', 'updated', 'project', 'description'], }); if (!searchResults.issues || searchResults.issues.length === 0) { return { content: [{ type: "text", text: `No tickets found matching "${searchText}" in projects: ${projects.join(', ')}` }], }; } // Format the results with descriptions const formattedResults = searchResults.issues.map(issue => { const summary = issue.fields?.summary || 'No summary'; const status = issue.fields?.status?.name || 'Unknown status'; const project = issue.fields?.project?.key || 'Unknown project'; const updated = issue.fields?.updated ? new Date(issue.fields.updated).toLocaleString() : 'Unknown date'; const description = issue.fields?.description ? extractTextFromADF(issue.fields.description) : 'No description'; return `[${project}] ${issue.key}: ${summary} Status: ${status} (Updated: ${updated}) Description: ${description.trim()} ----------------------------------------\n`; }).join('\n'); const totalResults = searchResults.total || 0; const headerText = `Found ${totalResults} ticket${totalResults !== 1 ? 's' : ''} matching "${searchText}"\n\n`; return { content: [{ type: "text", text: headerText + formattedResults }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: "text", text: `Failed to search tickets: ${errorMessage}` }], }; } } );
- src/server.ts:106-109 (helper)Helper function to escape special characters in the search text for safe inclusion in JQL queries.function escapeJQLText(text: string): string { // Escape special characters: + - & | ! ( ) { } [ ] ^ ~ * ? \ / return text.replace(/[+\-&|!(){}[\]^~*?\\\/]/g, '\\$&'); }
- src/server.ts:98-103 (helper)Helper function to parse and validate comma-separated project keys, trimming and uppercasing them.function validateAndFormatProjectKeys(projectKeys: string): string[] { return projectKeys .split(',') .map(key => key.trim().toUpperCase()) .filter(key => key.length > 0); }