Skip to main content
Glama
get_field_value.ts17.9 kB
import { GitHubConfig, ToolResponse } from '../../shared/types.js'; interface GetFieldValueArgs { project_id?: string; project_number?: number; item_id?: string; issue_number?: number; pr_number?: number; field_name?: string; field_id?: string; all_fields?: boolean; bulk_items?: Array<{ item_id?: string; issue_number?: number; pr_number?: number; }>; include_field_history?: boolean; format?: 'detailed' | 'simple' | 'json'; } /** * Get custom field values for GitHub Projects v2 items * Uses GraphQL query with field value resolution */ export async function getFieldValue(config: GitHubConfig, args: GetFieldValueArgs): Promise<ToolResponse> { const { graphqlWithAuth, owner } = config; if (!owner) { throw new Error('GITHUB_OWNER environment variable is required for project operations'); } try { let projectId = args.project_id; // If project_number is provided, get the project ID if (!projectId && args.project_number) { const projectQuery = ` query($owner: String!, $number: Int!) { user(login: $owner) { projectV2(number: $number) { id title number } } organization(login: $owner) { projectV2(number: $number) { id title number } } } `; const projectResult = await graphqlWithAuth(projectQuery, { owner, number: args.project_number }); const project = projectResult.user?.projectV2 || projectResult.organization?.projectV2; if (!project) { throw new Error(`Project #${args.project_number} not found`); } projectId = project.id; } if (!projectId) { throw new Error('Either project_id or project_number must be provided'); } // Get project fields and items const projectDataQuery = ` query($projectId: ID!) { node(id: $projectId) { ... on ProjectV2 { id title number fields(first: 50) { nodes { ... on ProjectV2Field { id name dataType } ... on ProjectV2IterationField { id name dataType configuration { iterations { id title startDate duration } } } ... on ProjectV2SingleSelectField { id name dataType options { id name color } } } } items(first: 100) { nodes { id type createdAt updatedAt content { ... on Issue { number title url } ... on PullRequest { number title url } ... on DraftIssue { title } } fieldValues(first: 50) { nodes { ... on ProjectV2ItemFieldTextValue { text createdAt updatedAt creator { login } field { ... on ProjectV2FieldCommon { id name } } } ... on ProjectV2ItemFieldNumberValue { number createdAt updatedAt creator { login } field { ... on ProjectV2FieldCommon { id name } } } ... on ProjectV2ItemFieldDateValue { date createdAt updatedAt creator { login } field { ... on ProjectV2FieldCommon { id name } } } ... on ProjectV2ItemFieldSingleSelectValue { name color createdAt updatedAt creator { login } field { ... on ProjectV2FieldCommon { id name } } } ... on ProjectV2ItemFieldIterationValue { title startDate duration createdAt updatedAt creator { login } field { ... on ProjectV2FieldCommon { id name } } } } } } } } } } `; const projectData = await graphqlWithAuth(projectDataQuery, { projectId }); const project = projectData.node; if (!project) { throw new Error('Project not found or access denied'); } const projectFields = project.fields?.nodes || []; const projectItems = project.items?.nodes || []; // Helper function to resolve field by name or ID const resolveField = (fieldName?: string, fieldId?: string): any | null => { if (fieldId) { return projectFields.find((f: any) => f.id === fieldId); } if (fieldName) { return projectFields.find((f: any) => f.name.toLowerCase() === fieldName.toLowerCase()); } return null; }; // Helper function to find item by issue/PR number const findItem = (issueNumber?: number, prNumber?: number): any | null => { return projectItems.find((item: any) => { if (item.content?.number) { if (issueNumber && item.type === 'ISSUE' && item.content.number === issueNumber) { return true; } if (prNumber && item.type === 'PULL_REQUEST' && item.content.number === prNumber) { return true; } } return false; }); }; // Helper function to format field value const formatFieldValue = (fieldValue: any, field: any): any => { const baseInfo = { fieldName: field?.name || 'Unknown Field', fieldType: field?.dataType || 'Unknown', value: null, displayValue: null, updatedAt: fieldValue.updatedAt, updatedBy: fieldValue.creator?.login }; if (fieldValue.text !== undefined) { baseInfo.value = fieldValue.text; baseInfo.displayValue = fieldValue.text; } else if (fieldValue.number !== undefined) { baseInfo.value = fieldValue.number; baseInfo.displayValue = fieldValue.number.toString(); } else if (fieldValue.date !== undefined) { baseInfo.value = fieldValue.date; baseInfo.displayValue = new Date(fieldValue.date).toLocaleDateString(); } else if (fieldValue.name !== undefined) { baseInfo.value = fieldValue.name; baseInfo.displayValue = `${fieldValue.name}${fieldValue.color ? ` (${fieldValue.color})` : ''}`; } else if (fieldValue.title !== undefined) { baseInfo.value = fieldValue.title; baseInfo.displayValue = `${fieldValue.title} (${fieldValue.startDate} - ${fieldValue.duration} days)`; } return baseInfo; }; const results: any[] = []; const format = args.format || 'detailed'; // Handle bulk items if (args.bulk_items && args.bulk_items.length > 0) { for (const bulkItem of args.bulk_items) { let item = null; if (bulkItem.item_id) { item = projectItems.find((i: any) => i.id === bulkItem.item_id); } else { item = findItem(bulkItem.issue_number, bulkItem.pr_number); } if (!item) { results.push({ success: false, error: `Item not found in project (issue: ${bulkItem.issue_number}, PR: ${bulkItem.pr_number})`, original: bulkItem }); continue; } const itemFieldValues = item.fieldValues?.nodes || []; const formattedValues = itemFieldValues.map((fv: any) => formatFieldValue(fv, fv.field)); results.push({ success: true, itemId: item.id, itemType: item.type, itemContent: item.content, fieldValues: formattedValues, totalFields: formattedValues.length, original: bulkItem }); } } else { // Handle single item let item = null; if (args.item_id) { item = projectItems.find((i: any) => i.id === args.item_id); } else { item = findItem(args.issue_number, args.pr_number); } if (!item) { throw new Error(`Item not found in project (issue: ${args.issue_number}, PR: ${args.pr_number})`); } const itemFieldValues = item.fieldValues?.nodes || []; if (args.all_fields) { // Return all field values const formattedValues = itemFieldValues.map((fv: any) => formatFieldValue(fv, fv.field)); results.push({ success: true, itemId: item.id, itemType: item.type, itemContent: item.content, fieldValues: formattedValues, totalFields: formattedValues.length }); } else { // Return specific field value const targetField = resolveField(args.field_name, args.field_id); if (!targetField) { throw new Error(`Field not found: ${args.field_name || args.field_id}`); } const fieldValue = itemFieldValues.find((fv: any) => fv.field?.id === targetField.id); if (!fieldValue) { results.push({ success: true, itemId: item.id, itemType: item.type, itemContent: item.content, fieldName: targetField.name, fieldType: targetField.dataType, value: null, message: 'Field has no value set' }); } else { const formattedValue = formatFieldValue(fieldValue, targetField); results.push({ success: true, itemId: item.id, itemType: item.type, itemContent: item.content, ...formattedValue }); } } } // Format response based on format preference if (format === 'json') { return { content: [{ type: "text", text: JSON.stringify({ projectId, projectTitle: project.title, projectNumber: project.number, results }, null, 2) }] }; } else if (format === 'simple') { let response = `📊 **Field Values** - ${project.title}\n\n`; results.forEach((result, index) => { if (result.success) { if (result.fieldValues) { // Multiple fields response += `${index + 1}. **Item:** ${result.itemContent?.title || result.itemContent?.number || 'Draft'}\n`; result.fieldValues.forEach((fv: any) => { response += ` • ${fv.fieldName}: ${fv.displayValue || 'No value'}\n`; }); } else { // Single field response += `${index + 1}. **${result.fieldName}:** ${result.displayValue || result.value || 'No value'}\n`; } } else { response += `${index + 1}. **Error:** ${result.error}\n`; } }); return { content: [{ type: "text", text: response }] }; } else { // Detailed format (default) let response = `📊 **Project Field Values** - ${project.title} (#${project.number})\n\n`; response += `**Project ID:** ${projectId}\n`; response += `**Available Fields:** ${projectFields.length}\n`; response += `**Query Results:** ${results.length}\n\n`; if (results.length === 0) { response += "No field values found matching the criteria.\n\n"; response += `💡 **Available actions:**\n`; response += `• Use 'set_field_value' to set field values\n`; response += `• Use 'list_project_items' to see all project items`; } else { response += `## Field Value Results\n\n`; results.forEach((result, index) => { if (result.success) { response += `### ${index + 1}. `; // Item identification if (result.itemContent?.number) { const type = result.itemType === 'ISSUE' ? 'Issue' : result.itemType === 'PULL_REQUEST' ? 'Pull Request' : 'Item'; response += `**${type} #${result.itemContent.number}: ${result.itemContent.title}**\n`; response += ` 🔗 ${result.itemContent.url}\n`; } else if (result.itemContent?.title) { response += `**Draft: ${result.itemContent.title}**\n`; } else { response += `**Item ID: ${result.itemId}**\n`; } response += ` 🆔 Item ID: ${result.itemId}\n`; if (result.fieldValues) { // Multiple fields response += ` 📊 **Field Values (${result.totalFields}):**\n`; if (result.fieldValues.length === 0) { response += ` No field values set\n`; } else { result.fieldValues.forEach((fv: any) => { response += ` • **${fv.fieldName}** (${fv.fieldType}): ${fv.displayValue || fv.value || 'No value'}\n`; if (args.include_field_history && fv.updatedAt && fv.updatedBy) { response += ` 📅 Updated: ${new Date(fv.updatedAt).toLocaleDateString()} by ${fv.updatedBy}\n`; } }); } } else { // Single field response += ` 📝 **Field:** ${result.fieldName} (${result.fieldType})\n`; response += ` 💎 **Value:** ${result.displayValue || result.value || 'No value'}\n`; if (result.message) { response += ` ℹ️ **Note:** ${result.message}\n`; } if (args.include_field_history && result.updatedAt && result.updatedBy) { response += ` 📅 **Last Updated:** ${new Date(result.updatedAt).toLocaleDateString()} by ${result.updatedBy}\n`; } } response += `\n`; } else { response += `### ${index + 1}. ❌ **Error**\n`; response += ` Error: ${result.error}\n`; if (result.original) { response += ` Original: ${JSON.stringify(result.original)}\n`; } response += `\n`; } }); // Show available fields information if (projectFields.length > 0) { response += `## Available Project Fields\n\n`; projectFields.forEach((field: any) => { response += `• **${field.name}** (${field.dataType})`; if (field.options && field.options.length > 0) { const options = field.options.map((opt: any) => opt.name).join(', '); response += ` - Options: ${options}`; } if (field.configuration?.iterations && field.configuration.iterations.length > 0) { const iterations = field.configuration.iterations.map((iter: any) => iter.title).join(', '); response += ` - Iterations: ${iterations}`; } response += `\n`; }); } response += `\n🛠️ **Available Actions:**\n`; response += `• Use 'set_field_value' to update field values\n`; response += `• Use 'list_project_items' to see all items with field values\n`; response += `• Use format: 'json' for machine-readable output`; } return { content: [{ type: "text", text: response }] }; } } catch (error: any) { if (error.message?.includes('insufficient permission')) { throw new Error('Insufficient permissions to get project field values. Ensure your GitHub token has "project" scope and read access to the project.'); } if (error.message?.includes('not found')) { throw new Error(`Project, item, or field not found: ${error.message}`); } throw new Error(`Failed to get field value: ${error.message}`); } }

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/Faresabdelghany/github-project-manager-mcp'

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