Skip to main content
Glama

Targetprocess MCP Server

# Data Analysis with Targetprocess MCP This document provides guidance on using the Targetprocess MCP for data analysis, extraction, and integration with other systems. It focuses on scenarios where you need to extract and analyze data from your Targetprocess instance. ## Data Model Discovery Before performing any analysis, it's essential to understand the data model of your Targetprocess instance. This is particularly important in corporate settings where the schema, indices, custom fields, and data model can vary significantly between implementations. ### Discovering Entity Types ```json // List all available entity types { "action": "list_types" } ``` This will return a list of all entity types available in your Targetprocess instance. This is the foundation for understanding what data is available for analysis. ### Exploring Entity Properties ```json // Get properties for a specific entity type { "action": "get_properties", "entityType": "UserStory" } ``` This will return all properties available for the specified entity type, including standard and custom fields. This helps you understand what data points are available for analysis. ### Understanding Relationships ```json // Get entity with related data { "type": "UserStory", "id": 12345, "include": ["Project", "Team", "Feature", "Tasks", "Bugs"] } ``` By examining the relationships between entities, you can build a comprehensive understanding of the data model. This is crucial for complex analyses that span multiple entity types. ## Data Extraction Strategies ### Batch Extraction For large datasets, it's important to implement batch extraction to avoid overwhelming the API and to handle the data efficiently. ```json // Extract data in batches { "type": "UserStory", "take": 1000, "where": "CreateDate gt '2024-01-01'", "include": ["Project", "Team"] } ``` For datasets larger than the maximum batch size (1000), you'll need to implement pagination: ```javascript // Pseudocode for pagination let skip = 0; const take = 1000; let hasMore = true; while (hasMore) { const result = await searchEntities({ type: "UserStory", take: take, skip: skip, where: "CreateDate gt '2024-01-01'", include: ["Project", "Team"] }); // Process the batch processData(result); // Check if there's more data if (result.length < take) { hasMore = false; } else { skip += take; } } ``` ### Incremental Extraction For ongoing analysis, implement incremental extraction to only process new or changed data: ```json // Extract data modified since last extraction { "type": "UserStory", "where": "ModifyDate gt '2024-02-25T00:00:00'", "include": ["Project", "Team"] } ``` Store the timestamp of your last extraction and use it for subsequent extractions to only get new or updated data. ## Data Transformation Once you've extracted the data, you'll often need to transform it for analysis. Here are some common transformation patterns: ### Flattening Hierarchical Data Targetprocess data is often hierarchical. For analysis, you might need to flatten it: ```javascript // Pseudocode for flattening hierarchical data function flattenUserStory(userStory) { return { id: userStory.Id, name: userStory.Name, description: userStory.Description, state: userStory.EntityState.Name, projectId: userStory.Project?.Id, projectName: userStory.Project?.Name, teamId: userStory.Team?.Id, teamName: userStory.Team?.Name, // Add custom fields riskLevel: userStory.CustomFields?.RiskLevel, priority: userStory.CustomFields?.Priority }; } ``` ### Aggregating Data For analytics, you often need to aggregate data: ```javascript // Pseudocode for aggregating data function aggregateByTeam(userStories) { const teamAggregates = {}; for (const story of userStories) { const teamId = story.Team?.Id; if (!teamId) continue; if (!teamAggregates[teamId]) { teamAggregates[teamId] = { teamId, teamName: story.Team.Name, storyCount: 0, totalEffort: 0, storyPoints: 0 }; } teamAggregates[teamId].storyCount++; teamAggregates[teamId].totalEffort += story.TimeSpent || 0; teamAggregates[teamId].storyPoints += story.Effort || 0; } return Object.values(teamAggregates); } ``` ## Integration with Analytics Tools ### Exporting to CSV/JSON For integration with tools like Excel, Tableau, or Power BI: ```javascript // Pseudocode for exporting to CSV function exportToCSV(data, filename) { const fields = Object.keys(data[0]); const csv = [ fields.join(','), ...data.map(item => fields.map(field => JSON.stringify(item[field] || '')).join(',')) ].join('\n'); fs.writeFileSync(filename, csv); } ``` ### Pushing to a Database For more sophisticated analysis, you might want to store the data in a database: ```javascript // Pseudocode for pushing to a database async function storeInDatabase(data, tableName) { // Connect to your database const db = await connectToDatabase(); // Create a transaction const transaction = await db.beginTransaction(); try { // Insert or update each record for (const item of data) { await db.query(` INSERT INTO ${tableName} (id, name, description, state, project_id, team_id) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE name = VALUES(name), description = VALUES(description), state = VALUES(state), project_id = VALUES(project_id), team_id = VALUES(team_id) `, [item.id, item.name, item.description, item.state, item.projectId, item.teamId]); } // Commit the transaction await transaction.commit(); } catch (error) { // Rollback on error await transaction.rollback(); throw error; } } ``` ## Time Series Analysis Targetprocess data often has temporal aspects that are valuable for analysis: ### Extracting Historical Data ```json // Get historical data for trend analysis { "type": "UserStory", "where": "ModifyDate gt '2023-01-01' and ModifyDate lt '2024-01-01'", "include": ["EntityState", "Project", "Team"], "take": 1000 } ``` ### Analyzing Cycle Time ```javascript // Pseudocode for calculating cycle time function calculateCycleTime(userStories) { return userStories.map(story => { const startDate = new Date(story.StartDate); const endDate = story.EndDate ? new Date(story.EndDate) : null; return { id: story.Id, name: story.Name, startDate, endDate, cycleTime: endDate ? (endDate - startDate) / (1000 * 60 * 60 * 24) : null // in days }; }); } ``` ## Advanced Analytics ### Predictive Analysis Using historical data to predict future outcomes: ```javascript // Pseudocode for velocity prediction function predictVelocity(teamIterations) { // Calculate average velocity from past iterations const velocities = teamIterations.map(iteration => { return iteration.UserStories.reduce((sum, story) => sum + (story.Effort || 0), 0); }); // Calculate average and standard deviation const avgVelocity = velocities.reduce((sum, v) => sum + v, 0) / velocities.length; const stdDev = Math.sqrt( velocities.reduce((sum, v) => sum + Math.pow(v - avgVelocity, 2), 0) / velocities.length ); return { avgVelocity, stdDev, predictedRange: [avgVelocity - stdDev, avgVelocity + stdDev] }; } ``` ### Network Analysis Analyzing relationships between entities: ```javascript // Pseudocode for dependency network analysis function buildDependencyNetwork(userStories) { const nodes = {}; const edges = []; // Create nodes for (const story of userStories) { nodes[story.Id] = { id: story.Id, name: story.Name, type: 'UserStory' }; } // Create edges for (const story of userStories) { if (story.Relations) { for (const relation of story.Relations) { edges.push({ source: story.Id, target: relation.RelatedEntity.Id, type: relation.RelationType }); // Add related entity if not already in nodes if (!nodes[relation.RelatedEntity.Id]) { nodes[relation.RelatedEntity.Id] = { id: relation.RelatedEntity.Id, name: relation.RelatedEntity.Name, type: relation.RelatedEntity.ResourceType }; } } } } return { nodes: Object.values(nodes), edges }; } ``` ## Status Reporting Generate status reports for projects and teams: ### Project Status Report ```json // Get project status { "type": "Project", "id": 123, "include": ["UserStories", "Features", "Teams"] } ``` ```javascript // Pseudocode for generating project status report function generateProjectStatusReport(project) { const userStories = project.UserStories || []; const statusCounts = userStories.reduce((acc, story) => { const status = story.EntityState?.Name || 'Unknown'; acc[status] = (acc[status] || 0) + 1; return acc; }, {}); const totalStories = userStories.length; const completedStories = statusCounts['Done'] || 0; const progressPercentage = totalStories > 0 ? (completedStories / totalStories) * 100 : 0; return { projectName: project.Name, totalStories, statusBreakdown: statusCounts, progressPercentage: Math.round(progressPercentage), team: project.Team?.Name, features: (project.Features || []).map(f => f.Name) }; } ``` ### Team Velocity Report ```json // Get team iterations { "type": "TeamIteration", "where": "Team.Id eq 456 and EndDate lt @Today", "include": ["Team", "UserStories"], "orderBy": ["EndDate desc"], "take": 10 } ``` ```javascript // Pseudocode for calculating team velocity function calculateTeamVelocity(teamIterations) { return teamIterations.map(iteration => { const userStories = iteration.UserStories || []; const totalPoints = userStories.reduce((sum, story) => { return sum + (story.Effort || 0); }, 0); const completedStories = userStories.filter(story => story.EntityState?.Name === 'Done' ); const completedPoints = completedStories.reduce((sum, story) => { return sum + (story.Effort || 0); }, 0); return { iteration: iteration.Name, startDate: iteration.StartDate, endDate: iteration.EndDate, totalPoints, completedPoints, completionRate: totalPoints > 0 ? (completedPoints / totalPoints) * 100 : 0 }; }); } ``` ## Progress Tracking Track progress over time: ### Burndown Chart Data ```json // Get user stories for burndown { "type": "UserStory", "where": "TeamIteration.Id eq 789", "include": ["EntityState", "Effort", "ModifyDate"] } ``` ```javascript // Pseudocode for generating burndown data function generateBurndownData(userStories, startDate, endDate) { // Create array of dates between start and end const dates = []; const currentDate = new Date(startDate); const endDateTime = new Date(endDate).getTime(); while (currentDate.getTime() <= endDateTime) { dates.push(new Date(currentDate)); currentDate.setDate(currentDate.getDate() + 1); } // Calculate remaining points for each date return dates.map(date => { const dateTime = date.getTime(); // Filter stories that exist as of this date const storiesAsOfDate = userStories.filter(story => new Date(story.CreateDate).getTime() <= dateTime ); // Calculate remaining points const remainingPoints = storiesAsOfDate.reduce((sum, story) => { // If story was completed before this date, don't count its points const storyDone = story.EntityState?.Name === 'Done' && new Date(story.ModifyDate).getTime() <= dateTime; return sum + (storyDone ? 0 : (story.Effort || 0)); }, 0); return { date: date.toISOString().split('T')[0], remainingPoints }; }); } ``` ## Data Quality Monitoring Monitor data quality in your Targetprocess instance: ```javascript // Pseudocode for data quality check async function checkDataQuality() { const issues = []; // Check for missing required fields const entitiesWithMissingFields = await searchEntities({ type: "UserStory", where: "Description is null or Description eq ''", include: ["Project", "Team"], take: 1000 }); if (entitiesWithMissingFields.length > 0) { issues.push({ type: 'missing_required_field', field: 'Description', count: entitiesWithMissingFields.length, entities: entitiesWithMissingFields.map(e => e.Id) }); } // Check for orphaned entities const orphanedTasks = await searchEntities({ type: "Task", where: "UserStory is null", take: 1000 }); if (orphanedTasks.length > 0) { issues.push({ type: 'orphaned_entity', entityType: 'Task', count: orphanedTasks.length, entities: orphanedTasks.map(e => e.Id) }); } return issues; } ``` ## Discovery Using the Targetprocess MCP for data discovery: ### Entity Type Discovery ```json // Discover available entity types { "action": "list_types" } ``` ### Property Discovery ```json // Discover properties for an entity type { "action": "get_properties", "entityType": "UserStory" } ``` ### Relationship Discovery ```json // Discover relationships { "type": "UserStory", "id": 12345, "include": ["*"] } ``` ## Reporting Generate custom reports: ### Project Metrics Report ```javascript // Pseudocode for generating project metrics report async function generateProjectMetricsReport(projectId) { // Get project data const project = await getEntity({ type: "Project", id: projectId, include: ["Teams", "UserStories", "Features"] }); // Get team iterations const teamIterations = await searchEntities({ type: "TeamIteration", where: `Project.Id eq ${projectId}`, include: ["Team", "UserStories"], orderBy: ["EndDate desc"], take: 10 }); // Calculate metrics const userStories = project.UserStories || []; const totalStories = userStories.length; const completedStories = userStories.filter(story => story.EntityState?.Name === 'Done' ).length; const totalEffort = userStories.reduce((sum, story) => sum + (story.Effort || 0), 0); const completedEffort = userStories.filter(story => story.EntityState?.Name === 'Done' ).reduce((sum, story) => sum + (story.Effort || 0), 0); // Calculate velocity const velocities = teamIterations.map(iteration => { const iterationStories = iteration.UserStories || []; return iterationStories.filter(story => story.EntityState?.Name === 'Done' ).reduce((sum, story) => sum + (story.Effort || 0), 0); }); const avgVelocity = velocities.length > 0 ? velocities.reduce((sum, v) => sum + v, 0) / velocities.length : 0; return { projectName: project.Name, metrics: { totalStories, completedStories, progressPercentage: totalStories > 0 ? (completedStories / totalStories) * 100 : 0, totalEffort, completedEffort, effortProgressPercentage: totalEffort > 0 ? (completedEffort / totalEffort) * 100 : 0, avgVelocity }, projections: { estimatedRemainingIterations: avgVelocity > 0 ? Math.ceil((totalEffort - completedEffort) / avgVelocity) : null } }; } ``` ## Tips for Data Analysis 1. **Start with Exploration**: Begin by exploring the data model and available entities 2. **Limit Result Sets**: Use specific queries and pagination for large datasets 3. **Incremental Processing**: Process data in batches or incrementally for large analyses 4. **Cache Metadata**: Cache entity type and property information to reduce API calls 5. **Consider API Limits**: Be aware of API rate limits when performing large-scale analysis 6. **Data Consistency**: Account for potential inconsistencies in data across different entities 7. **Performance Optimization**: Optimize queries to reduce API load and processing time 8. **Documentation**: Document your analysis process and findings for future reference

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/aaronsb/apptio-target-process-mcp'

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