Skip to main content
Glama

Prisma MCP Server

Official
by prisma
Apache 2.0
4
44,192
  • Linux
  • Apple
auto-close-github-discussions.js9.16 kB
const { Octokit } = require('@octokit/rest') const { graphql } = require('@octokit/graphql') // Get current repository details from the GITHUB_REPOSITORY environment variable ("owner/repo") const repoInfo = process.env.GITHUB_REPOSITORY if (!repoInfo) { console.error('GITHUB_REPOSITORY environment variable not set') process.exit(1) } const [OWNER, REPO] = repoInfo.split('/') // ONE_WEEK_MS is set to one week (in milliseconds) const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000 async function run() { const token = process.env.GITHUB_TOKEN if (!token) { console.error('GITHUB_TOKEN not set') process.exit(1) } const closingMessage = process.env.CLOSING_MESSAGE || 'Closing discussion due to inactivity.' const octokit = new Octokit({ auth: token, userAgent: 'auto-close-discussions', }) // Set up GraphQL client const graphqlWithAuth = graphql.defaults({ headers: { authorization: `token ${token}`, }, }) try { // 1. Get the repository ID first (needed for GraphQL) const repoData = await octokit.repos.get({ owner: OWNER, repo: REPO, }) const repoId = repoData.data.node_id // NEW: Fetch collaborators with write (push) access const collabResponse = await octokit.repos.listCollaborators({ owner: OWNER, repo: REPO, }) const collaborators = collabResponse.data .filter((user) => user.permissions && user.permissions.push) .map((user) => user.login) console.log(`Found ${collaborators.length} collaborators with write access.`) const categoriesQuery = await graphqlWithAuth( `query getCategories($repoId: ID!) { node(id: $repoId) { ... on Repository { discussionCategories(first: 25) { nodes { id name } } } } }`, { repoId }, ) const categories = categoriesQuery.node.discussionCategories.nodes const qaCategory = categories.find((cat) => cat.name === 'Q&A') if (!qaCategory) { console.error('No Q&A category found.') process.exit(1) } console.log(`Found Q&A category with ID: ${qaCategory.id}`) // 2. Fetch top 50 open discussions in the Q&A category const discussionsQuery = await graphqlWithAuth( `query getDiscussions($repoId: ID!, $categoryId: ID!) { node(id: $repoId) { ... on Repository { discussions(first: 50, states: OPEN, categoryId: $categoryId) { nodes { id number title body createdAt updatedAt labels(first: 10) { nodes { id name } } comments(first: 50) { nodes { id createdAt author { login } replies(first: 50) { nodes { id createdAt author { login } } } } } } } } } }`, { repoId, categoryId: qaCategory.id }, ) // Extract discussions const discussions = discussionsQuery.node.discussions.nodes console.log(`Fetched ${discussions.length} discussions`) // 3. Filter open discussions that have at least one of the target labels const targetLabels = ['discussion/needs-information', 'discussion/needs-confirmation'] const filteredDiscussions = discussions.filter((discussion) => { if (!discussion.labels.nodes || discussion.labels.nodes.length === 0) return false const labels = discussion.labels.nodes.map((label) => label.name) return targetLabels.some((target) => labels.includes(target)) }) console.log(`Found ${filteredDiscussions.length} open Q&A discussions with at least one target label.`) // 4. Process each filtered discussion for (const discussion of filteredDiscussions) { const discussionNumber = discussion.number console.log(`Processing discussion #${discussionNumber}: ${discussion.title}`) // Find the most recent activity (comment or reply) let lastActivity = null let lastActivityDate = null let lastActivityAuthor = null // Check if there are comments if (!discussion.comments.nodes.length) { console.log('No comments found, skipping.') continue } // Find the most recent activity date from all comments and replies for (const comment of discussion.comments.nodes) { const commentDate = new Date(comment.createdAt) if (!lastActivityDate || commentDate > lastActivityDate) { lastActivity = 'comment' lastActivityDate = commentDate lastActivityAuthor = comment.author.login } if (comment.replies && comment.replies.nodes) { for (const reply of comment.replies.nodes) { const replyDate = new Date(reply.createdAt) if (!lastActivityDate || replyDate > lastActivityDate) { lastActivity = 'reply' lastActivityDate = replyDate lastActivityAuthor = reply.author.login } } } } console.log( `Last activity on discussion #${discussionNumber} was a ${lastActivity} from ${lastActivityAuthor} on ${lastActivityDate}`, ) // Check if the last activity is by a collaborator with write access and older than one week const now = new Date() if (collaborators.includes(lastActivityAuthor) && now.getTime() - lastActivityDate.getTime() > ONE_WEEK_MS) { console.log( `Discussion #${discussionNumber} qualifies for closure. Last activity was from collaborator ${lastActivityAuthor} more than a week ago.`, ) // 5. Add a closing comment using GraphQL try { await graphqlWithAuth( `mutation AddDiscussionComment($discussionId: ID!, $body: String!) { addDiscussionComment(input: {discussionId: $discussionId, body: $body}) { comment { id } } }`, { discussionId: discussion.id, body: closingMessage, }, ) console.log(`Posted closing comment on discussion #${discussionNumber}.`) } catch (commentError) { console.error(`Error posting comment: ${commentError.message}`) if (commentError.response) console.error(JSON.stringify(commentError.response, null, 2)) continue } // 6. Remove target labels using GraphQL removeLabelsFromLabelable mutation try { const labelsToRemove = discussion.labels.nodes .filter((label) => targetLabels.includes(label.name)) .map((label) => label.id) if (labelsToRemove.length > 0) { await graphqlWithAuth( `mutation RemoveLabels($labelableId: ID!, $labelIds: [ID!]!) { removeLabelsFromLabelable(input: { labelableId: $labelableId, labelIds: $labelIds }) { clientMutationId } }`, { labelableId: discussion.id, labelIds: labelsToRemove, }, ) console.log(`Removed target labels from discussion #${discussionNumber}.`) } else { console.log(`No target labels to remove from discussion #${discussionNumber}.`) } } catch (labelError) { console.error(`Error removing labels: ${labelError.message}`) if (labelError.errors) console.error(JSON.stringify(labelError.errors, null, 2)) } // 7. Close the discussion using GraphQL with the OUTDATED reason try { await graphqlWithAuth( `mutation CloseDiscussion($discussionId: ID!) { closeDiscussion(input: {discussionId: $discussionId, reason: OUTDATED}) { discussion { id } } }`, { discussionId: discussion.id, }, ) console.log(`Closed discussion #${discussionNumber} as OUTDATED.`) } catch (closeError) { console.error(`Error closing discussion: ${closeError.message}`) if (closeError.response) console.error(JSON.stringify(closeError.response, null, 2)) } } else { console.log(`Discussion #${discussionNumber} does not qualify for closure.`) } } } catch (error) { console.error('Error processing discussions:', error) if (error.response) console.error(JSON.stringify(error.response, null, 2)) } } run()

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/prisma/prisma'

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