Skip to main content
Glama
jake-mok-nelson

GitHub Support Assistant

find-similar-issues

Identify similar GitHub issues by comparing new issue descriptions to existing ones, helping prioritize and resolve problems efficiently within a repository.

Instructions

Find GitHub issues similar to a new issue description

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
issueDescriptionYesDescription of the issue to find similar ones for
maxResultsNoMaximum number of similar issues to return
ownerYesGitHub repository owner/organization
repoYesGitHub repository name

Implementation Reference

  • The main handler function that implements the 'find-similar-issues' tool logic, searching GitHub for similar issues using keyword extraction, API search, similarity scoring, and formatting results.
    async ({ owner, repo, issueDescription, maxResults }) => {
        // Combine title and description for better search results
        const searchText = `${issueDescription}`;
        
        // Extract important keywords for search (simple approach)
        const keywords = searchText
            .toLowerCase()
            .replace(/[^\w\s]/g, '')
            .split(/\s+/)
            .filter(word => word.length > 3)  // Filter out short words
            .filter(word => !['the', 'and', 'that', 'this', 'with'].includes(word))  // Filter common words
            .slice(0, 10)  // Limit number of keywords
            .join(' ');
            
        // Search for issues in the repository
        const searchParams = {
            q: `repo:${owner}/${repo} ${keywords}`,
            sort: 'updated',
            order: 'desc',
            per_page: '30'  // Get more results to filter by similarity
        };
        
        const searchResponse = await makeGithubRequest<GithubSearchResponse>(
            '/search/issues', 
            searchParams
        );
        
        if (!searchResponse) {
            return {
                content: [
                    {
                        type: "text",
                        text: "Failed to retrieve issues from GitHub"
                    }
                ]
            };
        }
        
        if (searchResponse.total_count === 0) {
            return {
                content: [
                    {
                        type: "text",
                        text: `No similar issues found in ${owner}/${repo}`
                    }
                ]
            };
        }
        
        // Calculate similarity score for each issue
        const issuesWithScores = searchResponse.items
            .map(issue => ({
                issue,
                score: calculateSimilarity(searchText, `${issue.title} ${issue.body || ''}`)
            }))
            .sort((a, b) => b.score - a.score)  // Sort by similarity score (highest first)
            .slice(0, maxResults);  // Take top N results
        
        // Format the response
        const formattedIssues = issuesWithScores.map(({ issue, score }) => 
            formatIssue(issue, score)
        );
        
        const responseText = `Found ${issuesWithScores.length} similar issues in ${owner}/${repo}:\n\n${formattedIssues.join("\n")}`;
        
        return {
            content: [
                {
                    type: "text",
                    text: responseText
                }
            ]
        };
    }
  • Zod input schema defining parameters: owner, repo, issueDescription, and maxResults for the tool.
    {
        owner: z.string().describe("GitHub repository owner/organization"),
        repo: z.string().describe("GitHub repository name"),
        issueDescription: z.string().describe("Description of the issue to find similar ones for"),
        maxResults: z.number().int().min(1).max(20).default(5).describe("Maximum number of similar issues to return")
    },
  • src/index.ts:105-188 (registration)
    Registration of the 'find-similar-issues' tool using McpServer.tool() with name, description, input schema, and inline handler.
    server.tool(
        "find-similar-issues",
        "Find GitHub issues similar to a new issue description",
        {
            owner: z.string().describe("GitHub repository owner/organization"),
            repo: z.string().describe("GitHub repository name"),
            issueDescription: z.string().describe("Description of the issue to find similar ones for"),
            maxResults: z.number().int().min(1).max(20).default(5).describe("Maximum number of similar issues to return")
        },
        async ({ owner, repo, issueDescription, maxResults }) => {
            // Combine title and description for better search results
            const searchText = `${issueDescription}`;
            
            // Extract important keywords for search (simple approach)
            const keywords = searchText
                .toLowerCase()
                .replace(/[^\w\s]/g, '')
                .split(/\s+/)
                .filter(word => word.length > 3)  // Filter out short words
                .filter(word => !['the', 'and', 'that', 'this', 'with'].includes(word))  // Filter common words
                .slice(0, 10)  // Limit number of keywords
                .join(' ');
                
            // Search for issues in the repository
            const searchParams = {
                q: `repo:${owner}/${repo} ${keywords}`,
                sort: 'updated',
                order: 'desc',
                per_page: '30'  // Get more results to filter by similarity
            };
            
            const searchResponse = await makeGithubRequest<GithubSearchResponse>(
                '/search/issues', 
                searchParams
            );
            
            if (!searchResponse) {
                return {
                    content: [
                        {
                            type: "text",
                            text: "Failed to retrieve issues from GitHub"
                        }
                    ]
                };
            }
            
            if (searchResponse.total_count === 0) {
                return {
                    content: [
                        {
                            type: "text",
                            text: `No similar issues found in ${owner}/${repo}`
                        }
                    ]
                };
            }
            
            // Calculate similarity score for each issue
            const issuesWithScores = searchResponse.items
                .map(issue => ({
                    issue,
                    score: calculateSimilarity(searchText, `${issue.title} ${issue.body || ''}`)
                }))
                .sort((a, b) => b.score - a.score)  // Sort by similarity score (highest first)
                .slice(0, maxResults);  // Take top N results
            
            // Format the response
            const formattedIssues = issuesWithScores.map(({ issue, score }) => 
                formatIssue(issue, score)
            );
            
            const responseText = `Found ${issuesWithScores.length} similar issues in ${owner}/${repo}:\n\n${formattedIssues.join("\n")}`;
            
            return {
                content: [
                    {
                        type: "text",
                        text: responseText
                    }
                ]
            };
        }
    );
  • Helper function to calculate Jaccard similarity score between issue description and candidate issues for ranking.
    function calculateSimilarity(text1: string, text2: string): number {
        // Convert to lowercase and remove special characters for comparison
        const normalize = (text: string) => text.toLowerCase().replace(/[^\w\s]/g, '');
        
        const words1 = new Set(normalize(text1).split(/\s+/));
        const words2 = new Set(normalize(text2).split(/\s+/));
        
        // Find intersection of words
        const intersection = new Set([...words1].filter(word => words2.has(word)));
        
        // Calculate Jaccard similarity coefficient
        const union = new Set([...words1, ...words2]);
        
        return intersection.size / union.size;
    }
  • Helper function for making authenticated GitHub API requests, used by the tool for searching issues.
    async function makeGithubRequest<T>(path: string, params: Record<string, string> = {}): Promise<T | null> {
        const url = new URL(path, GITHUB_API_BASE);
        
        // Add query parameters
        Object.entries(params).forEach(([key, value]) => {
            if (value) url.searchParams.append(key, value);
        });
        
        const headers: {
            "User-Agent": string;
            "Accept": string;
            "X-GitHub-Api-Version": string;
            Authorization?: string;
        } = {
            "User-Agent": USER_AGENT,
            "Accept": "application/vnd.github+json",
            "X-GitHub-Api-Version": "2022-11-28"
        };
        
        // Add authorization if token is available
        if (GITHUB_TOKEN) {
            headers["Authorization"] = `Bearer ${GITHUB_TOKEN}`;
        }
    
        try {
            const response = await fetch(url.toString(), { headers });
            if (!response.ok) {
                throw new Error(`GitHub API error! status: ${response.status}`);
            }
            return (await response.json()) as T;
        } catch (error) {
            console.error("Error making GitHub request:", error);
            return null;
        }
    }

Tool Definition Quality

Score is being calculated. Check back soon.

Install Server

Other Tools

Related 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/jake-mok-nelson/mcp-find-similar-github-issues'

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