Skip to main content
Glama
Jameswlepage

WordPress Trac MCP Server

by Jameswlepage

getTimeline

Retrieve recent WordPress Trac activity including tickets, commits, and events to track development progress and changes.

Instructions

Get recent activity from WordPress Trac timeline including recent tickets, commits, and other events.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
daysNoNumber of days to look back (default: 7, max: 30)
limitNoMaximum number of events to return (default: 20, max: 100)

Implementation Reference

  • src/index.ts:120-138 (registration)
    Registration of the getTimeline tool in the tools/list response, including its input schema.
    { name: "getTimeline", description: "Get recent activity from WordPress Trac timeline including recent tickets, commits, and other events.", inputSchema: { type: "object", properties: { days: { type: "number", description: "Number of days to look back (default: 7, max: 30)", default: 7, }, limit: { type: "number", description: "Maximum number of events to return (default: 20, max: 100)", default: 20, }, }, }, },
  • Main handler implementation for the getTimeline tool within the tools/call switch statement. Fetches RSS timeline from Trac, parses events, and formats results.
    case "getTimeline": { const { days = 7, limit = 20 } = args; try { const timelineUrl = `https://core.trac.wordpress.org/timeline?from=${days}%2Bdays+ago&max=${Math.min(limit, 100)}&format=rss`; const response = await fetch(timelineUrl, { headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } }); if (!response.ok) { throw new Error(`Failed to fetch timeline: ${response.statusText}`); } const rssText = await response.text(); // Better RSS parsing with multiple pattern attempts const itemMatches = rssText.match(/<item>([\s\S]*?)<\/item>/g); const events = []; if (itemMatches) { for (const itemMatch of itemMatches) { // Try CDATA patterns first let titleMatch = itemMatch.match(/<title><!\[CDATA\[(.*?)\]\]><\/title>/s); let linkMatch = itemMatch.match(/<link>(.*?)<\/link>/s); let descMatch = itemMatch.match(/<description><!\[CDATA\[(.*?)\]\]><\/description>/s); let dateMatch = itemMatch.match(/<pubDate>(.*?)<\/pubDate>/s); let creatorMatch = itemMatch.match(/<dc:creator>(.*?)<\/dc:creator>/s); // Fallback to non-CDATA patterns if (!titleMatch) { titleMatch = itemMatch.match(/<title>(.*?)<\/title>/s); } if (!descMatch) { descMatch = itemMatch.match(/<description>(.*?)<\/description>/s); } if (titleMatch && linkMatch) { const title = titleMatch[1]?.trim() || 'Unknown Event'; const link = linkMatch[1]?.trim() || ''; const description = descMatch ? descMatch[1]?.replace(/<[^>]*>/g, '').trim() : ''; const date = dateMatch ? dateMatch[1]?.trim() : ''; const creator = creatorMatch ? creatorMatch[1]?.trim() : ''; events.push({ id: link || `event-${events.length}`, title, text: `${title}\n\nAuthor: ${creator || 'Unknown'}\nDate: ${date || 'Unknown'}\n\n${description || 'No description available'}`, url: link, metadata: { date, author: creator, description, }, }); } } } result = { results: events, totalEvents: events.length, daysBack: days, timelineUrl: 'https://core.trac.wordpress.org/timeline', }; } catch (error) { result = { results: [], error: error instanceof Error ? error.message : 'Unknown error', daysBack: days, timelineUrl: 'https://core.trac.wordpress.org/timeline', }; } break;
  • Helper function getTimelineForChatGPT used in ChatGPT-specific MCP handler for timeline queries.
    async function getTimelineForChatGPT(days: number, limit: number) { try { const timelineUrl = `https://core.trac.wordpress.org/timeline?from=${days}%2Bdays+ago&max=${Math.min(limit, 100)}&format=rss`; const response = await fetch(timelineUrl, { headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } }); if (!response.ok) { throw new Error(`Failed to fetch timeline: ${response.statusText}`); } const rssText = await response.text(); const itemMatches = rssText.match(/<item>([\s\S]*?)<\/item>/g); const results = []; if (itemMatches) { for (const itemMatch of itemMatches) { let titleMatch = itemMatch.match(/<title><!\[CDATA\[(.*?)\]\]><\/title>/s); let linkMatch = itemMatch.match(/<link>(.*?)<\/link>/s); let descMatch = itemMatch.match(/<description><!\[CDATA\[(.*?)\]\]><\/description>/s); let dateMatch = itemMatch.match(/<pubDate>(.*?)<\/pubDate>/s); let creatorMatch = itemMatch.match(/<dc:creator>(.*?)<\/dc:creator>/s); if (!titleMatch) titleMatch = itemMatch.match(/<title>(.*?)<\/title>/s); if (!descMatch) descMatch = itemMatch.match(/<description>(.*?)<\/description>/s); if (titleMatch && linkMatch) { const title = titleMatch[1]?.trim() || 'Unknown Event'; const link = linkMatch[1]?.trim() || ''; const description = descMatch ? descMatch[1]?.replace(/<[^>]*>/g, '').trim() : ''; const date = dateMatch ? dateMatch[1]?.trim() : ''; const creator = creatorMatch ? creatorMatch[1]?.trim() : ''; results.push({ id: link || `event-${results.length}`, title, text: `${title}\n\nAuthor: ${creator || 'Unknown'}\nDate: ${date || 'Unknown'}\n\n${description || 'No description available'}`, url: link, metadata: { date, author: creator, description }, }); } } } return { results }; } catch (error) { return { results: [], error: error instanceof Error ? error.message : 'Unknown error' }; } }

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