Skip to main content
Glama
Jameswlepage

WordPress Trac MCP Server

by Jameswlepage

getTicket

Retrieve detailed WordPress Trac ticket information including descriptions, comments, and metadata to track development issues and discussions.

Instructions

Get detailed information about a specific WordPress Trac ticket including description, comments, and metadata.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
idYesTrac ticket ID number
includeCommentsNoInclude ticket comments and discussion (default: true)
commentLimitNoMaximum number of comments to return (default: 10, max: 50)

Implementation Reference

  • src/index.ts:72-96 (registration)
    Registration of the 'getTicket' tool in the tools/list MCP method response, including its input schema definition.
    { name: "getTicket", description: "Get detailed information about a specific WordPress Trac ticket including description, comments, and metadata.", inputSchema: { type: "object", properties: { id: { type: "number", description: "Trac ticket ID number", }, includeComments: { type: "boolean", description: "Include ticket comments and discussion (default: true)", default: true, }, commentLimit: { type: "number", description: "Maximum number of comments to return (default: 10, max: 50)", default: 10, }, }, required: ["id"], }, }, {
  • Primary handler implementation for the 'getTicket' tool within the handleMcpRequest function's tools/call switch statement. Fetches ticket data using Trac's CSV query API, parses the response, extracts metadata, handles comments placeholder, and formats the output.
    case "getTicket": { const { id, includeComments = true } = args; try { // Use search approach since CSV parsing is problematic const searchUrl = new URL('https://core.trac.wordpress.org/query'); searchUrl.searchParams.set('format', 'csv'); searchUrl.searchParams.set('id', id.toString()); searchUrl.searchParams.set('max', '1'); const response = await fetch(searchUrl.toString(), { headers: { 'User-Agent': 'Mozilla/5.0 (compatible; WordPress-Trac-MCP-Server/1.0)', 'Accept': 'text/csv,text/plain,*/*', } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const csvData = await response.text(); // Parse CSV data similar to searchTickets const lines = csvData.replace(/^\uFEFF/, '').trim().split(/\r?\n/); if (lines.length < 2) { throw new Error(`Ticket ${id} not found`); } // Parse each line like in searchTickets for (let i = 1; i < lines.length; i++) { const line = lines[i]?.trim(); if (!line) continue; // Better CSV parsing - handle quoted fields properly const values = []; let currentField = ''; let inQuotes = false; let escapeNext = false; for (let j = 0; j < line.length; j++) { const char = line[j]; if (escapeNext) { currentField += char; escapeNext = false; continue; } if (char === '\\') { escapeNext = true; continue; } if (char === '"') { if (inQuotes) { // Check if this is an escaped quote if (j + 1 < line.length && line[j + 1] === '"') { currentField += '"'; j++; // Skip the next quote } else { inQuotes = false; } } else { inQuotes = true; } } else if (char === ',' && !inQuotes) { values.push(currentField.trim()); currentField = ''; } else { currentField += char; } } values.push(currentField.trim()); if (values.length >= 2 && values[0] && !isNaN(parseInt(values[0]))) { const ticketId = parseInt(values[0]); if (ticketId === id) { // Map fields based on actual headers from search query // Headers: id,Summary,Owner,Type,Status,Priority,Milestone const ticket = { id: parseInt(values[0]), summary: values[1] || '', owner: values[2] || '', type: values[3] || '', status: values[4] || '', priority: values[5] || '', milestone: values[6] || '', reporter: '', // Not available in search query description: 'Full description not available in search query. Visit the ticket URL for complete details.', component: '', // Not available in search query version: '', severity: '', resolution: '', keywords: '', cc: '', focuses: '', }; // Note: Comments are not available through the CSV API let comments: any[] = []; if (includeComments) { comments = [{ author: 'system', timestamp: new Date().toISOString(), comment: 'Comment history not available through CSV API. Visit the ticket URL for full discussion.', }]; } result = { id: id, title: `#${id}: ${ticket.summary}`, text: `Ticket #${id}: ${ticket.summary}\n\nStatus: ${ticket.status}\nComponent: ${ticket.component}\nPriority: ${ticket.priority}\nType: ${ticket.type}\nReporter: ${ticket.reporter}\nOwner: ${ticket.owner}\nMilestone: ${ticket.milestone}\nVersion: ${ticket.version}\nKeywords: ${ticket.keywords}\n\nDescription:\n${ticket.description}\n\nFor full discussion and comments, visit: https://core.trac.wordpress.org/ticket/${id}`, url: `https://core.trac.wordpress.org/ticket/${id}`, metadata: { ticket, comments, totalComments: comments.length, }, }; break; // Found the ticket, exit the loop } } } // If we didn't find the ticket, result will be undefined if (!result) { throw new Error(`Ticket ${id} not found`); } } catch (error) { result = { id: id, title: `Error loading ticket ${id}`, text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, url: `https://core.trac.wordpress.org/ticket/${id}`, metadata: { error: true }, }; } break; }
  • Helper function getTicketForChatGPT used in the ChatGPT-specific MCP handler for fetching and formatting ticket data, similar logic to main handler but simplified for ChatGPT's search/fetch tools.
    async function getTicketForChatGPT(ticketId: number, includeComments: boolean) { try { const searchUrl = new URL('https://core.trac.wordpress.org/query'); searchUrl.searchParams.set('format', 'csv'); searchUrl.searchParams.set('id', ticketId.toString()); searchUrl.searchParams.set('max', '1'); const response = await fetch(searchUrl.toString(), { headers: { 'User-Agent': 'Mozilla/5.0 (compatible; WordPress-Trac-MCP-Server/1.0)', 'Accept': 'text/csv,text/plain,*/*', } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const csvData = await response.text(); const lines = csvData.replace(/^\uFEFF/, '').trim().split(/\r?\n/); if (lines.length < 2) { throw new Error(`Ticket ${ticketId} not found`); } const values = parseCSVLine(lines[1] || ''); if (values.length >= 2 && values[0] && parseInt(values[0]) === ticketId) { const ticket = { id: parseInt(values[0]), summary: values[1] || '', owner: values[2] || '', type: values[3] || '', status: values[4] || '', priority: values[5] || '', milestone: values[6] || '', }; // Cache the ticket chatgptCache.set(ticketId.toString(), ticket); const commentNote = includeComments ? "\n\nNote: Full comments and description available on the ticket page." : ""; return { id: ticketId.toString(), title: `#${ticketId}: ${ticket.summary}`, text: `Ticket #${ticketId}: ${ticket.summary}\n\nStatus: ${ticket.status}\nType: ${ticket.type}\nPriority: ${ticket.priority}\nOwner: ${ticket.owner}\nMilestone: ${ticket.milestone}${commentNote}`, url: `https://core.trac.wordpress.org/ticket/${ticketId}`, metadata: { ticket }, }; } else { throw new Error(`Ticket ${ticketId} not found`); } } catch (error) { return { id: ticketId.toString(), title: `Error loading ticket ${ticketId}`, text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, url: `https://core.trac.wordpress.org/ticket/${ticketId}`, metadata: { error: true }, }; } }

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/Jameswlepage/trac-mcp'

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