Skip to main content
Glama

FogBugz MCP Server

by akari2600
api-explorer.js39.9 kB
#!/usr/bin/env node /** * FogBugz JSON API Explorer * * This script demonstrates and tests the FogBugz JSON API equivalents * of the XML API functionality. */ const axios = require('axios'); require('dotenv').config(); // Configuration from environment variables const API_KEY = process.env.FOGBUGZ_API_KEY; const FOGBUGZ_URL = process.env.FOGBUGZ_URL; const API_ENDPOINT = `${FOGBUGZ_URL}/f/api/0/jsonapi`; // Helper function to make API requests async function makeApiRequest(cmd, params = {}) { try { const payload = { cmd, token: API_KEY, ...params }; console.log(`\n🚀 Making request: ${cmd}`); console.log('Request payload:', JSON.stringify(payload, null, 2)); const response = await axios.post(API_ENDPOINT, payload, { headers: { 'Content-Type': 'application/json' } }); console.log('✅ Success!'); return response.data; } catch (error) { console.log('❌ Error:'); if (error.response) { console.error(JSON.stringify(error.response.data, null, 2)); } else { console.error(error.message); } return null; } } // Function to print JSON response nicely function printResponse(data) { console.log('\nResponse:'); console.log(JSON.stringify(data, null, 2)); console.log('\n-----------------------------------'); } // Ensure we're logged in before running any demo async function ensureLoggedIn() { console.log('\n🔑 Ensuring we are logged in...'); const result = await makeApiRequest('logon'); if (result && result.data && result.data.token) { console.log('✅ Successfully logged in with token.'); return true; } console.log('❌ Failed to log in. Continuing without authentication...'); console.log('Note: Some API calls may fail due to authentication issues.'); return false; } // Demonstrate Views functionality async function demoViews() { if (!(await ensureLoggedIn())) return; console.log('\n==== Views ===='); console.log('\nThe FogBugz JSON API provides commands to view specific entities:'); // First, list some projects to get an ID to view console.log('\n1. List projects to get an ID to view:'); const projectsResult = await makeApiRequest('listProjects'); printResponse(projectsResult); // View a specific project let projectId = null; if (projectsResult && projectsResult.data && projectsResult.data.projects && projectsResult.data.projects.length > 0) { projectId = projectsResult.data.projects[0].ixProject; console.log(`\n2. View specific project with ID ${projectId} (cmd=viewProject):`); const viewProjectResult = await makeApiRequest('viewProject', { ixProject: projectId }); printResponse(viewProjectResult); // List areas to get an ID to view console.log(`\n3. List areas in project ${projectId} to get an ID to view:`); const areasResult = await makeApiRequest('listAreas', { ixProject: projectId }); printResponse(areasResult); // View a specific area if we have any if (areasResult && areasResult.data && areasResult.data.areas && areasResult.data.areas.length > 0) { const areaId = areasResult.data.areas[0].ixArea; console.log(`\n4. View specific area with ID ${areaId} (cmd=viewArea):`); const viewAreaResult = await makeApiRequest('viewArea', { ixArea: areaId }); printResponse(viewAreaResult); } else { console.log('\n4. No areas found to view.'); } } else { console.log('\n2. No projects found to view.'); } // View a specific person (current user) console.log('\n5. View current user (cmd=viewPerson):'); const viewPersonResult = await makeApiRequest('viewPerson'); printResponse(viewPersonResult); // List priorities to get an ID to view console.log('\n6. List priorities to get an ID to view:'); const prioritiesResult = await makeApiRequest('listPriorities'); printResponse(prioritiesResult); // View a specific priority if we have any if (prioritiesResult && prioritiesResult.data && prioritiesResult.data.priorities && prioritiesResult.data.priorities.length > 0) { const priorityId = prioritiesResult.data.priorities[0].ixPriority; console.log(`\n7. View specific priority with ID ${priorityId} (cmd=viewPriority):`); const viewPriorityResult = await makeApiRequest('viewPriority', { ixPriority: priorityId }); printResponse(viewPriorityResult); } else { console.log('\n7. No priorities found to view.'); } console.log('\nNote: The JSON API provides additional view commands for other entity types.'); } // Main function to run the demos async function main() { console.log('============================================'); console.log('FogBugz JSON API Explorer'); console.log('============================================'); if (!API_KEY || !FOGBUGZ_URL) { console.error('Error: Missing API_KEY or FOGBUGZ_URL in .env file'); process.exit(1); } console.log(`Using FogBugz URL: ${FOGBUGZ_URL}`); // Run the demo functions // Comment out any demo you don't want to run // await demoApiVersionAndLocation(); // await demoLoggingOn(); // await demoGeneralRules(); // await demoLoggingOff(); // await demoFilters(); // await demoListingViewingCases(); // await demoEditingCases(); // await demoLists(); // await demoCreating(); // await demoEditingPerson(); // await demoViews(); // await demoMilestoneDependencies(); // await demoWorkingSchedule(); // await demoTimeTracking(); // await demoSourceControl(); // await demoTags(); // await demoReleaseNotes(); // await demoMilestones(); // await demoWikis(); // await demoDiscussionGroups(); await demoBugzScout(); console.log('\n✨ Completed API Explorer demonstration'); } // Demonstrate Milestone Dependencies functionality async function demoMilestoneDependencies() { if (!(await ensureLoggedIn())) return; console.log('\n==== Milestone Dependencies ===='); console.log('\nThe FogBugz JSON API provides commands to create and remove milestone dependencies:'); // First, list milestones to get IDs to work with console.log('\n1. List milestones (cmd=listFixFors):'); const milestonesResult = await makeApiRequest('listFixFors'); printResponse(milestonesResult); // Check if we have at least 2 milestones to create a dependency between if (milestonesResult && milestonesResult.data && milestonesResult.data.fixfors && milestonesResult.data.fixfors.length >= 2) { const milestone1 = milestonesResult.data.fixfors[0].ixFixFor; const milestone2 = milestonesResult.data.fixfors[1].ixFixFor; // Create a dependency console.log(`\n2. Create a dependency: Milestone ${milestone1} depends on Milestone ${milestone2} (cmd=addFixForDependency):`); console.log(` Note: This is a demonstration. The actual request will be commented out to prevent creating unwanted dependencies.`); // Commented out to prevent creating unwanted dependencies /* const addDependencyResult = await makeApiRequest('addFixForDependency', { ixFixFor: milestone1, ixFixForDependsOn: milestone2 }); printResponse(addDependencyResult); */ // Instead, show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'addFixForDependency', token: 'API_TOKEN', ixFixFor: milestone1, ixFixForDependsOn: milestone2 }, null, 2)); // Remove a dependency console.log(`\n3. Remove a dependency (cmd=deleteFixForDependency):`); console.log(` Note: This is a demonstration. The actual request will be commented out to prevent removing existing dependencies.`); // Commented out to prevent removing existing dependencies /* const deleteDependencyResult = await makeApiRequest('deleteFixForDependency', { ixFixFor: milestone1, ixFixForDependsOn: milestone2 }); printResponse(deleteDependencyResult); */ // Instead, show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'deleteFixForDependency', token: 'API_TOKEN', ixFixFor: milestone1, ixFixForDependsOn: milestone2 }, null, 2)); } else { console.log('\n2. Not enough milestones found to demonstrate dependencies (need at least 2).'); } console.log('\nNote: Milestone dependencies allow you to specify that one milestone must be completed before another can begin.'); } // Demonstrate Working Schedule functionality async function demoWorkingSchedule() { if (!(await ensureLoggedIn())) return; console.log('\n==== Working Schedule ===='); console.log('\nThe FogBugz JSON API provides commands to manage working schedules and project time allocations:'); // List working schedule for current user console.log('\n1. List working schedule for current user (cmd=listWorkingSchedule):'); const workingScheduleResult = await makeApiRequest('listWorkingSchedule'); printResponse(workingScheduleResult); // Calculate a future date based on working hours console.log('\n2. Calculate a date by adding working hours (cmd=wsDateFromHours):'); const currentDate = new Date().toISOString(); const hoursToAdd = 16; // Two standard workdays const wsDateResult = await makeApiRequest('wsDateFromHours', { dt: currentDate, hrs: hoursToAdd }); printResponse(wsDateResult); // List per-project time allocations console.log('\n3. List per-project time allocations (cmd=listProjectPercentTime):'); const percentTimeResult = await makeApiRequest('listProjectPercentTime'); printResponse(percentTimeResult); // Project protection examples console.log('\n4. Project protection operations:'); // Find a project to use as an example const projectsResult = await makeApiRequest('listProjects'); if (projectsResult && projectsResult.data && projectsResult.data.projects && projectsResult.data.projects.length > 0) { const projectId = projectsResult.data.projects[0].ixProject; console.log(`\n 4.1. Add project protection (cmd=addProjectPercentTime):`); console.log(` Note: This is a demonstration. The actual request will be commented out to prevent modifying allocations.`); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'addProjectPercentTime', token: 'API_TOKEN', ixProject: projectId, nPercent: 25 // Allocate 25% of time to this project }, null, 2)); console.log(`\n 4.2. Edit project protection (cmd=editProjectPercentTime):`); console.log(` Note: This is a demonstration. The actual request would need an ixProjectPercentTime value.`); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'editProjectPercentTime', token: 'API_TOKEN', ixProjectPercentTime: 123, // This would be an actual ID from listProjectPercentTime nPercent: 30 // Change allocation to 30% }, null, 2)); console.log(`\n 4.3. Delete project protection (cmd=deleteProjectPercentTime):`); console.log(` Note: This is a demonstration. The actual request would need an ixProjectPercentTime value.`); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'deleteProjectPercentTime', token: 'API_TOKEN', ixProjectPercentTime: 123 // This would be an actual ID from listProjectPercentTime }, null, 2)); } else { console.log('\n No projects found to demonstrate project protection operations.'); } console.log('\nNote: Working schedule operations allow you to manage work hours, calculate dates based on working time, and protect projects by specifying time allocations.'); } // Demonstrate Time Tracking functionality async function demoTimeTracking() { if (!(await ensureLoggedIn())) return; console.log('\n==== Time Tracking ===='); console.log('\nThe FogBugz JSON API provides commands to manage time tracking:'); // Search for a case we can use for time tracking examples console.log('\n1. Search for a case to use for time tracking examples:'); const casesResult = await makeApiRequest('search', { q: '', // Empty search to get all visible cases cols: 'ixBug,sTitle', max: 1 // Just get one case }); printResponse(casesResult); // Check if we found a case let caseId = null; if (casesResult && casesResult.data && casesResult.data.cases && casesResult.data.cases.length > 0) { caseId = casesResult.data.cases[0].ixBug; // Start working on a case console.log(`\n2. Start working on case #${caseId} (cmd=startWork):`); console.log(` Note: This is a demonstration. The actual request will be commented out to avoid affecting time tracking records.`); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'startWork', token: 'API_TOKEN', ixBug: caseId }, null, 2)); // Stop working on everything console.log('\n3. Stop working on all cases (cmd=stopWork):'); console.log(` Note: This is a demonstration. The actual request will be commented out to avoid affecting time tracking records.`); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'stopWork', token: 'API_TOKEN' }, null, 2)); // Import a time interval console.log('\n4. Import a time interval (cmd=newInterval):'); console.log(` Note: This is a demonstration. The actual request will be commented out to avoid creating new time intervals.`); // Calculate start and end dates for the example const now = new Date(); const startDate = new Date(now.getTime() - (60 * 60 * 1000)); // 1 hour ago const endDate = now; // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'newInterval', token: 'API_TOKEN', ixBug: caseId, dtStart: startDate.toISOString(), dtEnd: endDate.toISOString() }, null, 2)); // List time intervals console.log('\n5. List time intervals (cmd=listIntervals):'); const intervalsResult = await makeApiRequest('listIntervals'); printResponse(intervalsResult); } else { console.log('\n2. No cases found to demonstrate time tracking operations.'); } console.log('\nNote: Time tracking operations allow you to manage work intervals, start and stop the stopwatch, and track time spent on cases.'); } // Demonstrate Source Control functionality async function demoSourceControl() { if (!(await ensureLoggedIn())) return; console.log('\n==== Source Control ===='); console.log('\nThe FogBugz JSON API provides commands to associate source control check-ins with cases:'); // Search for a case to associate with check-ins console.log('\n1. Search for a case to associate with source control check-ins:'); const casesResult = await makeApiRequest('search', { q: '', // Empty search to get all visible cases cols: 'ixBug,sTitle', max: 1 // Just get one case }); printResponse(casesResult); // Check if we found a case let caseId = null; if (casesResult && casesResult.data && casesResult.data.cases && casesResult.data.cases.length > 0) { caseId = casesResult.data.cases[0].ixBug; // Associate a new check-in with a case console.log(`\n2. Associate a new check-in with case #${caseId} (cmd=newCheckin):`); console.log(` Note: This is a demonstration. The actual request will be commented out to avoid creating check-in records.`); // Example check-in details const sampleFile = 'src/api/jsonapi.js'; const prevRevision = '12345'; const newRevision = '12346'; const repoId = 2; // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'newCheckin', token: 'API_TOKEN', ixBug: caseId, sFile: sampleFile, sPrev: prevRevision, sNew: newRevision, ixRepository: repoId }, null, 2)); // List check-ins associated with a case console.log(`\n3. List check-ins associated with case #${caseId} (cmd=listCheckins):`); const checkinsResult = await makeApiRequest('listCheckins', { ixBug: caseId }); printResponse(checkinsResult); } else { console.log('\n2. No cases found to demonstrate source control operations.'); } console.log('\nNote: Source control commands allow you to associate code check-ins with cases, providing traceability between code changes and issue tracking.'); } // Demonstrate Tags functionality async function demoTags() { if (!(await ensureLoggedIn())) return; console.log('\n==== Tags ===='); console.log('\nThe FogBugz JSON API provides commands to list and manage tags (only available with FogBugz 8.2+):'); // List all tags console.log('\n1. List all tags visible to the current user (cmd=listTags):'); const tagsResult = await makeApiRequest('listTags'); printResponse(tagsResult); // Search for cases with a specific tag if (tagsResult && tagsResult.data && tagsResult.data.tags && tagsResult.data.tags.length > 0) { // Extract a tag name from the response to search for const tagName = tagsResult.data.tags[0].sTag; console.log(`\n2. Search for cases with tag '${tagName}':`); const tagSearchResult = await makeApiRequest('search', { q: `tag:"${tagName}"`, cols: 'ixBug,sTitle,tags' }); printResponse(tagSearchResult); // Demonstrate adding and removing tags using edit case console.log('\n3. Adding and removing tags:'); console.log(' Tags can be added or removed using the edit case command with the sTags parameter.'); console.log(' Example: To add tag1 and tag2 to a case while removing all other tags:'); // Show what the request would look like console.log('\n Example request payload for adding tags:'); console.log(JSON.stringify({ cmd: 'edit', token: 'API_TOKEN', ixBug: 123, sTags: 'tag1,tag2' }, null, 2)); } else { console.log('\n2. No tags found in the system to demonstrate tag search.'); } console.log('\nNote: Tags provide a flexible way to categorize and filter cases in FogBugz beyond the standard project/area/milestone fields.'); } // Demonstrate BugzScout functionality async function demoBugzScout() { if (!(await ensureLoggedIn())) return; console.log('\n==== BugzScout ===='); console.log('\nThe FogBugz JSON API provides commands to manage BugzScout cases:'); // 1. List BugzScout cases console.log('\n1. List BugzScout cases (cmd=listScoutCase):'); const scoutCasesResult = await makeApiRequest('listScoutCase', { sScoutDescription: 'crash in CMyClass.GetData() at line 125 - version 2.5.44' // Example scout description }); printResponse(scoutCasesResult); // 2. Creating a new BugzScout case (demonstration) console.log('\n2. Creating a new BugzScout case:'); console.log(' Note: This is a demonstration. The actual request uses cmd=new with BugzScout-specific parameters.'); console.log('\n Example request payload for creating a BugzScout case:'); console.log(JSON.stringify({ cmd: 'new', token: 'API_TOKEN', sTitle: 'Application crash in data processing module', sScoutDescription: 'crash in DataProcessor.processRecord() at line 217 - version 3.1.2', sScoutMessage: 'The development team has been notified of this issue and is working on a fix.', sEvent: 'User was processing a large data file when the application crashed.\ Stack trace: DataProcessor.processRecord() at line 217', ixProject: 5, // Project ID (replace with actual value) ixArea: 8, // Area ID (replace with actual value) fScoutStopReporting: 0 // Continue to record occurrences (0=continue, 1=stop) }, null, 2)); // 3. Search for BugzScout cases console.log('\n3. Search for cases and check for BugzScout fields:'); const scoutSearchResult = await makeApiRequest('search', { q: '', // Empty search to get all visible cases cols: 'ixBug,sTitle,sScoutDescription,sScoutMessage,fScoutStopReporting,dtLastOccurrence', max: 20 // Limit to 20 cases }); printResponse(scoutSearchResult); // 4. Updating a BugzScout case console.log('\n4. Updating a BugzScout case:'); console.log(' Note: This is a demonstration. The actual request uses cmd=edit with BugzScout-specific parameters.'); console.log('\n Example request payload for updating a BugzScout case:'); console.log(JSON.stringify({ cmd: 'edit', token: 'API_TOKEN', ixBug: 5184, // Case ID (replace with actual value) sScoutMessage: 'This issue has been resolved in version 3.1.3. Please update your application.', fScoutStopReporting: 1 // Stop recording occurrences (0=continue, 1=stop) }, null, 2)); console.log('\nNote: BugzScout is used for automatic crash reporting in applications.'); console.log(' The sScoutDescription field is used to identify unique crash signatures.'); console.log(' When a new crash with the same signature occurs, it increases the occurrence count'); console.log(' rather than creating duplicate cases.'); } // Demonstrate Discussion Groups functionality async function demoDiscussionGroups() { if (!(await ensureLoggedIn())) return; console.log('\n==== Discussion Groups ===='); console.log('\nThe FogBugz JSON API provides commands to view discussion groups and topics:'); // 1. List all discussion groups console.log('\n1. List all discussion groups (cmd=listDiscussGroups):'); const discussGroupsResult = await makeApiRequest('listDiscussGroups'); printResponse(discussGroupsResult); // Check if we have any discussion groups to work with if (discussGroupsResult && discussGroupsResult.data && discussGroupsResult.data.discussions && discussGroupsResult.data.discussions.length > 0) { const groupId = discussGroupsResult.data.discussions[0].ixDiscussGroup; // 2. List all topics in a discussion group console.log(`\n2. List all topics in discussion group ${groupId} (cmd=listDiscussion):`); const discussionResult = await makeApiRequest('listDiscussion', { ixDiscussGroup: groupId, fFull: 1 // Get all posts including replies }); printResponse(discussionResult); // 3. Check if there are any topics to view if (discussionResult && discussionResult.data && discussionResult.data.discussion && discussionResult.data.discussion.topics && discussionResult.data.discussion.topics.length > 0) { // Find a topic ID to use let topicId = null; for (const topic of discussionResult.data.discussion.topics) { if (topic.post && topic.post.ixDiscussTopic) { topicId = topic.post.ixDiscussTopic; break; } } if (topicId) { // 3. View a specific discussion topic console.log(`\n3. View specific discussion topic ${topicId} (cmd=listDiscussTopic):`); const topicResult = await makeApiRequest('listDiscussTopic', { ixDiscussTopic: topicId }); printResponse(topicResult); } else { console.log('\n3. Unable to find a valid topic ID in the discussion.'); } } else { console.log('\n3. No topics found in this discussion group.'); } // 4. Example of filtering topics by month and year console.log('\n4. Filter discussion by month and year (cmd=listDiscussion with m and y parameters):'); console.log(' Note: This example shows how to filter discussions by month (3 = March) and year (2025).'); // Example request console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'listDiscussion', token: 'API_TOKEN', ixDiscussGroup: groupId, m: 3, // March y: 2025 }, null, 2)); } else { console.log('\n2. No discussion groups found to demonstrate discussion-specific operations.'); } console.log('\nNote: Discussion groups provide a way to organize team communications about general topics'); console.log(' that may not be tied to specific cases. The API provides read-only access to discussions.'); console.log(' Creating new discussion groups or posts must be done through the FogBugz UI.'); } // Demonstrate Wikis functionality async function demoWikis() { if (!(await ensureLoggedIn())) return; console.log('\n==== Wikis ===='); console.log('\nThe FogBugz JSON API provides commands to manage wikis, wiki articles, and templates:'); // 1. List all wikis console.log('\n1. List all wikis (cmd=listWikis):'); const wikisResult = await makeApiRequest('listWikis'); printResponse(wikisResult); let wikiId = null; let wikiFound = false; // Check if we have any wikis to work with if (wikisResult && wikisResult.data && wikisResult.data.wikis && wikisResult.data.wikis.length > 0) { wikiId = wikisResult.data.wikis[0].ixWiki; wikiFound = true; // 2. List all articles in a wiki console.log(`\n2. List all articles in wiki ${wikiId} (cmd=listArticles):`); const articlesResult = await makeApiRequest('listArticles', { ixWiki: wikiId }); printResponse(articlesResult); // 3. View a specific article if available if (articlesResult && articlesResult.data && articlesResult.data.articles && articlesResult.data.articles.length > 0) { const articleId = articlesResult.data.articles[0].ixWikiPage; console.log(`\n3. View article ${articleId} (cmd=viewArticle):`); const viewArticleResult = await makeApiRequest('viewArticle', { ixWikiPage: articleId }); printResponse(viewArticleResult); // 3.1 List article revisions console.log(`\n3.1. List revisions for article ${articleId} (cmd=listRevisions):`); const revisionsResult = await makeApiRequest('listRevisions', { ixWikiPage: articleId }); printResponse(revisionsResult); // 3.2 Example of editing an article (demonstration only) console.log('\n3.2. Edit an article (cmd=editArticle):'); console.log(' Note: This is a demonstration. The actual request will be commented out to prevent modifying content.'); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'editArticle', token: 'API_TOKEN', ixWikiPage: articleId, sHeadline: articlesResult.data.articles[0].sHeadline + ' (Updated)', sBody: '<p>Updated content for the article.</p>', sComment: 'Updated via API demo' }, null, 2)); } else { console.log('\n3. No articles found in this wiki to demonstrate article-specific operations.'); } } else { console.log('\n2. No wikis found to demonstrate wiki-specific operations.'); wikiFound = false; } // 4. List templates console.log('\n4. List wiki templates (cmd=listTemplates):'); const templatesResult = await makeApiRequest('listTemplates'); printResponse(templatesResult); // Check if we have any templates to work with if (templatesResult && templatesResult.data && templatesResult.data.templates && templatesResult.data.templates.length > 0) { const templateId = templatesResult.data.templates[0].ixTemplate; // 4.1 View a specific template console.log(`\n4.1. View template ${templateId} (cmd=viewTemplate):`); const viewTemplateResult = await makeApiRequest('viewTemplate', { ixTemplate: templateId }); printResponse(viewTemplateResult); // 4.2 List template revisions console.log(`\n4.2. List revisions for template ${templateId} (cmd=listTemplateRevisions):`); const templateRevisionsResult = await makeApiRequest('listTemplateRevisions', { ixTemplate: templateId }); printResponse(templateRevisionsResult); } else { console.log('\n4.1. No templates found to demonstrate template-specific operations.'); } // 5. Create a new wiki (demonstration only) console.log('\n5. Create a new wiki (cmd=newWiki):'); console.log(' Note: This is a demonstration. The actual request will be commented out to prevent creating test wikis.'); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'newWiki', token: 'API_TOKEN', s: 'Development Guide', sTagLineHTML: '<p>Internal documentation for developers</p>' }, null, 2)); // 6. Create a new article (demonstration only) if (wikiFound) { console.log('\n6. Create a new article (cmd=newArticle):'); console.log(' Note: This is a demonstration. The actual request will be commented out to prevent creating test articles.'); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'newArticle', token: 'API_TOKEN', ixWiki: wikiId, sHeadline: 'API Integration Guide', sBody: '<h1>API Integration Guide</h1><p>This guide explains how to integrate with our APIs.</p>', sTags: 'api,development,guide' }, null, 2)); } else { console.log('\n6. No wiki found to demonstrate article creation.'); } // 7. Create a new template (demonstration only) console.log('\n7. Create a new template (cmd=newTemplate):'); console.log(' Note: This is a demonstration. The actual request will be commented out to prevent creating test templates.'); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'newTemplate', token: 'API_TOKEN', sTemplate: 'Technical Documentation Template', sBodyHTML: '<div class="tech-doc">{{content}}</div>', sBodyCSS: '.tech-doc { font-family: monospace; }', sComment: 'Template for technical documentation', fDefault: 0 }, null, 2)); // 8. File upload (demonstration only) if (wikiFound) { console.log('\n8. Upload a file to a wiki (cmd=wikiFileUpload):'); console.log(' Note: This is a demonstration. File uploads require multipart/form-data encoding.'); console.log(' This cannot be demonstrated in this script but would involve a form upload:'); console.log(` HTML Form Example: <form method="post" action="${API_ENDPOINT}" enctype="multipart/form-data"> <input type="hidden" name="cmd" value="wikiFileUpload" /> <input type="hidden" name="token" value="your_api_token" /> <input type="hidden" name="ixWiki" value="${wikiId}" /> <input type="file" name="File1" /> <input type="submit" value="Upload" /> </form> `); } else { console.log('\n8. No wiki found to demonstrate file upload.'); } console.log('\nNote: The wiki system in FogBugz provides a collaborative documentation platform.'); console.log(' Wikis can contain multiple articles, and each article maintains a complete revision history.'); console.log(' Templates control the appearance and layout of wiki articles.'); } // Demonstrate Milestones functionality async function demoMilestones() { if (!(await ensureLoggedIn())) return; console.log('\n==== Milestones ===='); console.log('\nThe FogBugz JSON API provides commands to manage milestones (also known as FixFors):'); // 1. List all milestones console.log('\n1. List all milestones (cmd=listFixFors):'); const milestonesResult = await makeApiRequest('listFixFors'); printResponse(milestonesResult); // 2. View a specific milestone if available if (milestonesResult && milestonesResult.data && milestonesResult.data.fixfors && milestonesResult.data.fixfors.length > 0) { const milestoneId = milestonesResult.data.fixfors[0].ixFixFor; console.log(`\n2. View a specific milestone with ID ${milestoneId} (cmd=viewFixFor):`); const viewMilestoneResult = await makeApiRequest('viewFixFor', { ixFixFor: milestoneId }); printResponse(viewMilestoneResult); // 3. List milestones for a specific project console.log('\n3. List projects to get a project ID for milestone filtering:'); const projectsResult = await makeApiRequest('listProjects'); printResponse(projectsResult); if (projectsResult && projectsResult.data && projectsResult.data.projects && projectsResult.data.projects.length > 0) { const projectId = projectsResult.data.projects[0].ixProject; console.log(`\n3.1 List milestones for project ${projectId} (cmd=listFixFors with ixProject):`); const projectMilestonesResult = await makeApiRequest('listFixFors', { ixProject: projectId }); printResponse(projectMilestonesResult); } else { console.log('\n3.1 No projects found to demonstrate project-specific milestone listing.'); } // 4. Create a new milestone (demonstration only) console.log('\n4. Create a new milestone (cmd=newFixFor):'); console.log(' Note: This is a demonstration. The actual request will be commented out to prevent creating test milestones.'); // Get a project ID to use for the example let projectId = null; if (projectsResult && projectsResult.data && projectsResult.data.projects && projectsResult.data.projects.length > 0) { projectId = projectsResult.data.projects[0].ixProject; } else { projectId = 1; // Default for example } // Show what the request would look like console.log('\n Example request payload for creating a milestone:'); console.log(JSON.stringify({ cmd: 'newFixFor', token: 'API_TOKEN', ixProject: projectId, sFixFor: 'Version 2.1', dtRelease: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(), // 90 days from now dtStart: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), // 30 days from now sStartNote: 'Development begins after previous version release', fAssignable: 1 }, null, 2)); // 5. Edit a milestone (demonstration only) console.log('\n5. Edit a milestone (cmd=editFixFor):'); console.log(' Note: This is a demonstration. The actual request will be commented out to prevent modifying existing milestones.'); // Show what the request would look like console.log('\n Example request payload for editing a milestone:'); console.log(JSON.stringify({ cmd: 'editFixFor', token: 'API_TOKEN', ixFixFor: milestoneId, sFixFor: milestonesResult.data.fixfors[0].sFixFor + ' (Updated)', dtRelease: new Date(Date.now() + 120 * 24 * 60 * 60 * 1000).toISOString() // 120 days from now }, null, 2)); // 6. Milestone dependencies (demonstration only) console.log('\n6. Milestone dependencies:'); // Check if we have at least 2 milestones to demonstrate dependencies if (milestonesResult.data.fixfors.length >= 2) { const milestone1 = milestonesResult.data.fixfors[0].ixFixFor; const milestone2 = milestonesResult.data.fixfors[1].ixFixFor; console.log('\n6.1. Add a milestone dependency (cmd=addFixForDependency):'); console.log(' Note: This is a demonstration. The actual request will be commented out to prevent creating unwanted dependencies.'); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'addFixForDependency', token: 'API_TOKEN', ixFixFor: milestone1, ixFixForDependsOn: milestone2 }, null, 2)); console.log('\n6.2. Remove a milestone dependency (cmd=deleteFixForDependency):'); console.log(' Note: This is a demonstration. The actual request will be commented out to prevent removing existing dependencies.'); // Show what the request would look like console.log('\n Example request payload:'); console.log(JSON.stringify({ cmd: 'deleteFixForDependency', token: 'API_TOKEN', ixFixFor: milestone1, ixFixForDependsOn: milestone2 }, null, 2)); } else { console.log('\n6.1 Not enough milestones found to demonstrate dependencies (need at least 2).'); } } else { console.log('\n2. No milestones found to demonstrate milestone-specific operations.'); } console.log('\nNote: Milestones (FixFors) are used to group cases for release planning and scheduling.'); console.log(' Milestone dependencies help establish a sequence for work, ensuring that dependent milestones'); console.log(' are not started until their prerequisites are completed.'); } // Demonstrate Release Notes functionality async function demoReleaseNotes() { if (!(await ensureLoggedIn())) return; console.log('\n==== Release Notes ===='); console.log('\nThe FogBugz JSON API provides commands to retrieve release notes for cases:'); // Get release notes for cases marked with release notes console.log('\n1. Get cases with release notes using search query:'); const releaseNotesResult = await makeApiRequest('search', { q: 'releaseNotes:"*"', // Search for cases that have release notes cols: 'ixBug,sTitle,sReleaseNotes,ixFixFor,sFixFor' }); printResponse(releaseNotesResult); // Get release notes for a specific milestone console.log('\n2. Get release notes for a specific milestone:'); // First, list milestones to get an ID to use console.log('\n2.1 First, list milestones to get an ID:'); const milestonesResult = await makeApiRequest('listFixFors'); printResponse(milestonesResult); // Now get release notes for a specific milestone if available if (milestonesResult && milestonesResult.data && milestonesResult.data.fixfors && milestonesResult.data.fixfors.length > 0) { const milestoneId = milestonesResult.data.fixfors[0].ixFixFor; const milestoneName = milestonesResult.data.fixfors[0].sFixFor; console.log(`\n2.2 Get release notes for milestone: ${milestoneName} (ID: ${milestoneId})`); const milestoneReleaseNotesResult = await makeApiRequest('search', { q: `fixfor:"${milestoneName}" AND releaseNotes:"*"`, cols: 'ixBug,sTitle,sReleaseNotes,ixFixFor,sFixFor' }); printResponse(milestoneReleaseNotesResult); } else { console.log('\n2.2 No milestones found to demonstrate milestone-specific release notes.'); } // Adding release notes to a case console.log('\n3. Adding release notes to a case:'); console.log('Note: This is a demonstration. The actual request would be using cmd=edit with sReleaseNotes parameter.'); console.log('\nExample request to add release notes to a case:'); console.log(JSON.stringify({ cmd: 'edit', token: 'API_TOKEN', ixBug: 123, // Replace with actual case ID sReleaseNotes: 'Fixed an issue where the application would crash when processing large files.' }, null, 2)); console.log('\nNote: Release notes are text associated with cases that describe changes made for a version release.'); console.log('They can be retrieved using search queries that filter for cases with release notes content.'); } // Run the main function main().catch(error => { console.error('Unhandled error:', error); process.exit(1); });

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/akari2600/fogbugz-mcp'

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