Overview: Five MCP Servers for Agile Project Management
Agile project management is one of the most complex domains in software development: it requires coordinating sprints, tasks, performance metrics, time tracking, budgets, and retrospectives. In the Tech-MCP suite, these aspects are managed by five specialized MCP servers that collaborate through the EventBus, forming an integrated project management ecosystem.
In this article, we will analyze each server in detail: its tools with Zod schemas, published and subscribed events, and the collaboration flow that binds them into a cohesive system.
The Five PM Servers of the MCP Suite
| Server | Role | Tools | Storage |
|---|---|---|---|
| scrum-board | Central Scrum hub | 7 | SQLite (3 tables) |
| agile-metrics | Analytics and forecasts | 4 | Stateless |
| time-tracking | Time tracking | 4 | SQLite (2 tables) |
| project-economics | Budget and costs | 4 | SQLite (2 tables) |
| retrospective-manager | Agile retrospectives | 5 | SQLite (3 tables) |
1. Scrum Board Server: The Central Hub
The scrum-board server is the operational heart of the entire MCP Suite for project management. It manages sprints, user stories, and tasks following the Scrum methodology, serving as the hub through which most collaborative workflows pass. Virtually every other PM server interacts with scrum-board, either directly or through the events it publishes.
+---------------------------+
| SCRUM-BOARD SERVER |
| CENTRAL HUB |
+---------------------------+
/ | | | \
/ | | | \
v v v v v
+-------+ +-------+ +------+ +-------+ +-------+
|agile | |time | |retro | |standup| |project|
|metrics| |track. | |mgr | |notes | |econ. |
+-------+ +-------+ +------+ +-------+ +-------+
Scrum-board Server Tools
The scrum-board exposes 7 tools covering the entire Scrum lifecycle:
Tool Table: scrum-board
| Tool | Description | Key Parameters |
|---|---|---|
create-sprint |
Creates a new sprint | name (string), startDate (string), endDate (string), goals (string[]) |
get-sprint |
Retrieves sprint details | sprintId (number) |
create-story |
Creates a new user story | title, description, acceptanceCriteria (string[]), storyPoints (number), priority, sprintId (opt.) |
create-task |
Creates a task for a story | title, description, storyId (number), assignee (opt.) |
update-task-status |
Updates a task's status | taskId (number), status (enum: todo | in_progress | in_review | done | blocked) |
sprint-board |
Kanban view of the sprint | sprintId (number, optional; default: active sprint) |
get-backlog |
Stories not assigned to a sprint | None |
Zod Schema: create-sprint
Every tool defines its parameters through Zod schemas for automatic input validation:
// Tool: create-sprint
const CreateSprintSchema = z.object({
name: z.string().describe("Sprint name"),
startDate: z.string().describe("Start date ISO (YYYY-MM-DD)"),
endDate: z.string().describe("End date ISO (YYYY-MM-DD)"),
goals: z.array(z.string()).describe("Sprint goals")
});
Zod Schema: update-task-status
// Tool: update-task-status
const UpdateTaskStatusSchema = z.object({
taskId: z.number().describe("ID of the task to update"),
status: z.enum(["todo", "in_progress", "in_review", "done", "blocked"])
.describe("New task status in the Kanban flow")
});
Task Kanban Flow
Every task status change follows the standard Kanban flow. From any state, it is possible
to transition to blocked:
+-------+ +-------------+ +-----------+ +------+
| todo | --> | in_progress | --> | in_review | --> | done |
+-------+ +-------------+ +-----------+ +------+
| | |
+-------+-------+-------------------+
|
v
+--------+
| blocked|
+--------+
ScrumStore Database: Three SQLite Tables
The ScrumStore manages three interconnected tables via foreign keys:
+------------------+ +-------------------+ +------------------+
| sprints | | stories | | tasks |
+------------------+ +-------------------+ +------------------+
| id (PK) |<------| sprintId (FK) | | id (PK) |
| name | | id (PK) |<------| storyId (FK, NN) |
| startDate | | title | | sprintId (FK) |
| endDate | | description | | title |
| goals (JSON) | | acceptanceCriteria| | description |
| status | | storyPoints | | status |
| createdAt | | priority | | assignee |
+------------------+ | status | | createdAt |
| createdAt | | updatedAt |
+-------------------+ +------------------+
Sprint Board: Kanban View
The sprint-board tool generates a complete Kanban view, organizing tasks into columns.
If no sprintId is specified, it automatically retrieves the sprint with active status:
+--------------------------------------------------------------------+
| Sprint Board: Sprint 14 |
+--------------------------------------------------------------------+
| TODO | IN PROGRESS | IN REVIEW | DONE | BLOCKED |
|---------------|---------------|---------------|---------|----------|
| Task: Setup | Task: Login | Task: Test | Task: | Task: |
| OAuth | form UI | OAuth flow | DB | Deploy |
| | | | schema | (needs |
| Task: 2FA | Task: Token | | | infra) |
| research | refresh | | | |
+--------------------------------------------------------------------+
Events Published by scrum-board
The scrum-board is the primary event producer of the PM suite. Every critical operation generates an event that triggers chain reactions in other servers:
Scrum-board Events
| Event | Payload | Emitted by |
|---|---|---|
scrum:sprint-started |
{ sprintId, name, startDate, endDate } |
create-sprint |
scrum:task-updated |
{ taskId, status, previousStatus, storyId, sprintId } |
update-task-status |
scrum:sprint-completed |
{ sprintId, name, completedPoints } |
Sprint closure |
scrum:story-completed |
{ storyId, storyPoints, sprintId } |
Story completion |
Events Subscribed by scrum-board
The scrum-board is also one of the few servers that receives events from other servers, creating a bidirectional feedback loop:
Subscription: retro:action-item-created
When the retrospective-manager generates action items from a retrospective, it publishes
retro:action-item-created. The scrum-board receives it and automatically creates
a task in the backlog, closing the continuous improvement loop.
Example: Complete Sprint Workflow
// 1. Create the sprint
{ "tool": "create-sprint",
"arguments": {
"name": "Sprint 14 - Authentication",
"startDate": "2025-02-03",
"endDate": "2025-02-14",
"goals": ["Implement OAuth login", "Add 2FA"]
} }
// 2. Create a user story
{ "tool": "create-story",
"arguments": {
"title": "Google OAuth Login",
"description": "As a user I want to authenticate with Google",
"acceptanceCriteria": ["Redirect to Google", "Token saved", "Session active"],
"storyPoints": 8,
"priority": "high",
"sprintId": 1
} }
// 3. Create tasks for the story
{ "tool": "create-task",
"arguments": {
"title": "Configure OAuth credentials",
"description": "Setup on Google Cloud Console",
"storyId": 1,
"assignee": "mario"
} }
// 4. Update task status
{ "tool": "update-task-status",
"arguments": { "taskId": 1, "status": "in_progress" } }
// 5. View the board
{ "tool": "sprint-board", "arguments": { "sprintId": 1 } }
2. Agile Metrics Server: Analytics and Forecasts
The agile-metrics server provides analytical tools for measuring and predicting team performance. It calculates velocity, generates burndown data, analyzes cycle time, and produces completion forecasts based on Monte Carlo simulations.
A fundamental characteristic is that the server is completely stateless: it has no database or internal store. It receives data as input (typically from the scrum-board) and returns pure calculations.
Tool Table: agile-metrics
| Tool | Description | Key Parameters |
|---|---|---|
calculate-velocity |
Average velocity with trend analysis | sprints (array of { name, completedPoints, totalPoints }) |
generate-burndown |
Burndown chart data | totalPoints, sprintDays, dailyCompleted (number[]) |
calculate-cycle-time |
Cycle time statistics | tasks (array of { taskId, startDate, endDate }) |
forecast-completion |
Monte Carlo forecast | remainingPoints, velocityHistory (number[]), sprintLengthDays |
Zod Schema: calculate-velocity
const CalculateVelocitySchema = z.object({
sprints: z.array(z.object({
name: z.string(),
completedPoints: z.number(),
totalPoints: z.number()
})).describe("Sprint list with completed and total points")
});
Zod Schema: forecast-completion
const ForecastCompletionSchema = z.object({
remainingPoints: z.number().describe("Remaining points to complete"),
velocityHistory: z.array(z.number())
.describe("Velocity history from recent sprints"),
sprintLengthDays: z.number()
.describe("Sprint duration in days")
});
Monte Carlo Simulation
The forecast-completion tool runs 1000 simulations to predict
when the remaining work will be completed, accounting for historical velocity variability:
Monte Carlo Algorithm:
+-------------------------------------------------------+
| For each simulation (1..1000): |
| remainingWork = remainingPoints |
| sprints = 0 |
| while remainingWork > 0: |
| velocity = random(velocityHistory) |
| remainingWork -= velocity |
| sprints++ |
| record(sprints * sprintLengthDays) |
+-------------------------------------------------------+
| Sort results |
| p50 = result at the 50th percentile |
| p85 = result at the 85th percentile |
| p95 = result at the 95th percentile |
+-------------------------------------------------------+
The output provides three confidence levels:
{
"remainingPoints": 50,
"simulations": 1000,
"forecast": {
"p50": { "sprints": 2, "days": 28, "date": "2025-03-14" },
"p85": { "sprints": 3, "days": 42, "date": "2025-03-28" },
"p95": { "sprints": 3, "days": 42, "date": "2025-03-28" }
},
"confidence": "85% probability of completion within 42 days"
}
Burndown Chart: Ideal vs Actual
The generate-burndown tool compares the ideal line with actual progress,
indicating for each day whether progress is on-track, behind, or ahead:
Points
40 |*
| * .
30 | * .
| * . * <-- actual (behind schedule)
20 | * .
| * . *
10 | * . *
| * . *
0 |--------*------------------.--*-----> Days
1 2 3 4 5 6 7 8 9 10
* = ideal line . = actual progress
Cycle Time: Detailed Statistics
The calculate-cycle-time tool analyzes task completion times:
Cycle Time Metrics
| Metric | Description |
|---|---|
average |
Arithmetic mean of cycle time |
median |
Median value (p50) |
p95 |
95th percentile |
min |
Minimum completion time |
max |
Maximum completion time |
Events Subscribed by agile-metrics
Agile-metrics is a pure data consumer. It publishes no events, but subscribes to scrum-board events to react to changes in the current sprint:
scrum:sprint-completed→ trigger for velocity recalculation and end-of-sprint reportscrum:task-updated→ real-time burndown data updatescrum:story-completed→ completed points update for velocity tracking
3. Time Tracking Server: Time Management
The time-tracking server manages tracking of time worked on tasks, with support for live timers and manual logging. It maintains persistent state via SQLite with two tables: one for time entries and one for active timers.
The server implements double-start protection: it is not possible to have more than one active timer simultaneously for the same user.
Tool Table: time-tracking
| Tool | Description | Key Parameters |
|---|---|---|
start-timer |
Starts a timer for a task | taskId (string), description (opt.), userId (opt.) |
stop-timer |
Stops the active timer | userId (opt.) |
log-time |
Manual time logging | taskId, durationMinutes (number), description (opt.), date (opt.) |
get-timesheet |
Timesheet filtered by dates | userId (opt.), startDate (opt.), endDate (opt.) |
Zod Schema: start-timer and log-time
const StartTimerSchema = z.object({
taskId: z.string().describe("ID of the task to track"),
description: z.string().optional()
.describe("Activity description"),
userId: z.string().optional()
.describe("User ID (default: 'default')")
});
const LogTimeSchema = z.object({
taskId: z.string().describe("Task ID"),
durationMinutes: z.number()
.describe("Duration in minutes"),
description: z.string().optional(),
date: z.string().optional()
.describe("ISO date (YYYY-MM-DD)"),
userId: z.string().optional()
});
Timer Flow
The start-timer flow includes double-start protection.
The stop-timer automatically calculates the duration and creates a time entry:
start-timer(taskId: "TASK-42")
|
v
Check: does an active timer exist for userId?
|
+--[YES]--> Error: "Active timer for TASK-15. Stop it first."
|
+--[NO]--> Create record in active_timers
startTime = new Date().toISOString()
stop-timer()
|
v
Find active timer for userId
|
+--[NOT FOUND]--> Error: "No active timer found"
|
+--[FOUND]
|
v
endTime = now()
durationMinutes = Math.round((endMs - startMs) / 60000)
|
v
INSERT INTO time_entries --> DELETE FROM active_timers
|
v
Publish time:entry-logged
TimeStore Database
The TimeStore manages two SQLite tables:
- active_timers: currently running timers (
id,taskId,userId,startTime,description) - time_entries: completed entries (
id,taskId,userId,startTime,endTime,durationMinutes,description,date)
Time-tracking Events
Published Event: time:entry-logged
Every time a timer is stopped or time is manually logged, the server publishes
the time:entry-logged event with payload:
{
"taskId": "TASK-42",
"userId": "mario",
"durationMinutes": 45,
"date": "2025-02-07"
}
This event is intercepted by project-economics to convert time into cost and update the project budget.
4. Project Economics Server: Budget and Costs
The project-economics server manages project budgets and costs, providing visibility on spending, category breakdown, and budget exhaustion forecasts. It includes an automatic alert system that publishes an event when budget usage reaches or exceeds 80%.
Tool Table: project-economics
| Tool | Description | Key Parameters |
|---|---|---|
set-budget |
Defines a project budget | projectName, totalBudget (number), currency (default: 'EUR') |
log-cost |
Records a cost | projectName, category, amount (number), description, taskId (opt.) |
get-budget-status |
Complete budget status | projectName |
forecast-budget |
Budget exhaustion forecast | projectName |
Zod Schema: log-cost
const LogCostSchema = z.object({
projectName: z.string().describe("Project name"),
category: z.string().describe("Spending category (e.g. development, infra, testing)"),
amount: z.number().describe("Amount in budget currency"),
description: z.string().describe("Cost description"),
date: z.string().optional().describe("ISO date of the cost"),
taskId: z.string().optional().describe("Associated task ID")
});
Budget Forecast Algorithm
The forecast-budget tool calculates the daily burn rate and estimates
when the budget will be exhausted:
Forecast algorithm:
+---------------------------------------------------+
| 1. Find first and last cost date |
| 2. daysTracked = (lastDate - firstDate) + 1 |
| 3. dailyBurnRate = totalSpent / daysTracked |
| 4. estimatedDaysRemaining = remaining / burnRate |
| 5. runOutDate = today + daysRemaining |
+---------------------------------------------------+
Budget Status with Breakdown
The get-budget-status tool returns a complete budget view with a breakdown
by spending category:
{
"projectName": "MCP Suite v2",
"totalBudget": 50000,
"currency": "EUR",
"totalSpent": 38500,
"remaining": 11500,
"percentageUsed": 77.0,
"breakdown": [
{ "category": "development", "total": 25000 },
{ "category": "infrastructure", "total": 8500 },
{ "category": "testing", "total": 3000 },
{ "category": "design", "total": 2000 }
]
}
Project-economics Events
Automatic Alert System
| Event | Payload | Condition |
|---|---|---|
economics:cost-updated |
{ projectName, category, amount, totalSpent } |
Always (on every log-cost) |
economics:budget-alert |
{ projectName, percentageUsed, remaining, totalBudget } |
When percentageUsed >= 80% |
Budget Alert Flow
log-cost("MCP Suite v2", "development", 5000, ...)
|
v
Calculate percentageUsed = totalSpent / totalBudget * 100
|
v
percentageUsed = 82% --> >= 80%?
|
+--[YES]--> Publish economics:budget-alert
| { projectName: "MCP Suite v2",
| percentageUsed: 82,
| remaining: 9000,
| totalBudget: 50000 }
|
+--[NO]--> Only economics:cost-updated
Events Subscribed by project-economics
time:entry-logged(from time-tracking) → converts logged time into cost and adds it to the projectscrum:sprint-completed(from scrum-board) → trigger for end-of-sprint report with cost analysis
5. Retrospective Manager Server: Agile Retrospectives
The retrospective-manager server manages the complete agile retrospective cycle: from creating a session with a specific format, to collecting feedback by category, to voting, and finally to the automatic generation of action items from the most voted themes.
It supports three standard formats of retrospective:
Supported Retrospective Formats
| Format | Categories |
|---|---|
| mad-sad-glad | mad, sad, glad |
| 4Ls | liked, learned, lacked, longed-for |
| start-stop-continue | start, stop, continue |
Tool Table: retrospective-manager
| Tool | Description | Key Parameters |
|---|---|---|
create-retro |
Creates a retrospective | format (enum), sprintId (opt.) |
add-retro-item |
Adds feedback (with category validation) | retroId, category, content, authorId (opt.) |
vote-retro-item |
Adds a vote to an item | itemId (number) |
generate-action-items |
Generates action items from top voted | retroId, topN (default: 3) |
get-retro |
Complete retrospective | retroId (number) |
Zod Schema: create-retro and add-retro-item
const CreateRetroSchema = z.object({
format: z.enum(["mad-sad-glad", "4ls", "start-stop-continue"])
.describe("Retrospective format"),
sprintId: z.string().optional()
.describe("Associated sprint ID")
});
const AddRetroItemSchema = z.object({
retroId: z.number().describe("Retrospective ID"),
category: z.string()
.describe("Category (validated against retro format)"),
content: z.string().describe("Feedback content"),
authorId: z.string().optional()
.describe("Feedback author")
});
Category Validation
The server validates the category by checking that it is consistent with the retrospective format. If the category is invalid, it returns an error:
add-retro-item(retroId: 1, category: "happy", content: "...")
|
v
Check retro #1 format = "mad-sad-glad"
Valid categories: ["mad", "sad", "glad"]
"happy" is NOT valid
|
v
Error: 'Invalid category "happy" for format "mad-sad-glad".
Valid categories: mad, sad, glad'
Action Item Generation
The generate-action-items tool selects the top-N most voted items and generates
an action item for each, publishing the retro:action-item-created event:
Top 3 voted --> Action Items:
1. [mad] Deploy too slow (8 votes)
2. [sad] Missing documentation (6 votes)
3. [glad] Code review improves quality (5 votes)
|
v
For each action item:
Publish retro:action-item-created
{ retroId, actionItemId, description, assignee }
|
v
scrum-board receives --> creates task in backlog
RetroStore Database: Three SQLite Tables
- retros: retrospective sessions (
id,sprintId,format,status) - retro_items: team feedback (
id,retroId,category,content,votes,authorId) - action_items: concrete actions (
id,retroId,description,assignee,dueDate,status)
Bidirectional Flow with scrum-board
Feedback Loop: Sprint → Retro → Backlog
- Sprint completes → scrum-board publishes
scrum:sprint-completed - The retrospective-manager receives the event and automatically creates a retrospective
- The team adds items and votes
- Generated action items are published as
retro:action-item-created - The scrum-board receives the event and creates tasks in the backlog
This cycle closes the continuous improvement loop: every retrospective generates concrete actions that enter the Scrum workflow as backlog tasks.
Collaboration Flow: How the 5 PM Servers Interact
The real value of these five servers emerges from their EventBus-driven collaboration. No server operates in isolation: every published event triggers chain reactions that create an integrated and automated project management flow.
Complete PM Event Map
| Event | Producer | Consumers |
|---|---|---|
scrum:sprint-started |
scrum-board | agile-metrics, retrospective-manager |
scrum:task-updated |
scrum-board | agile-metrics, time-tracking, project-economics |
scrum:sprint-completed |
scrum-board | agile-metrics, retrospective-manager, project-economics |
scrum:story-completed |
scrum-board | agile-metrics |
time:entry-logged |
time-tracking | project-economics |
economics:budget-alert |
project-economics | (future: standup-notes) |
economics:cost-updated |
project-economics | (logging and audit) |
retro:action-item-created |
retrospective-manager | scrum-board |
Collaboration Diagram
+------------------+
| SCRUM-BOARD | (CENTRAL HUB)
| 7 tools |
+--------+---------+
|
| scrum:sprint-started
| scrum:task-updated
| scrum:sprint-completed
| scrum:story-completed
|
+-----+-----+-----+------------------+
| | | |
v v v v
+----------+ +------+ +---------------+ +----+
|agile- | |time- | |retrospective- | |... |
|metrics | |track.| |manager | | |
|4 tools | |4 tool| |5 tools | | |
|stateless | | | | | | |
+----------+ +--+---+ +------+--------+ +----+
| |
| time: | retro:action-
| entry- | item-created
| logged |
v v
+----------+ +------------------+
|project- | | SCRUM-BOARD |
|economics | | (receives and |
|4 tools | | creates tasks) |
+----+-----+ +------------------+
|
| economics:budget-alert
| economics:cost-updated
v
(alerts and audit)
Integrated Scenario: Complete Sprint with All Servers
Here is an example of how all five servers collaborate during a sprint lifecycle:
-
Planning: the AI invokes
create-sprinton the scrum-board. Thescrum:sprint-startedevent notifies agile-metrics (new velocity) and retrospective-manager (context for future retro). -
Execution: developers update task statuses with
update-task-status. Thescrum:task-updatedevent triggers burndown recalculation in agile-metrics and time-tracking can automatically start/stop timers. -
Time tracking: on
stop-timer, time-tracking publishestime:entry-logged. Project-economics receives the event and converts time to cost, updating the budget. -
Budget alert: if spending exceeds 80% of the budget, project-economics publishes
economics:budget-alertto notify the team. - Sprint closure: on sprint completion, agile-metrics recalculates velocity and forecasts, while retrospective-manager automatically creates a retrospective.
-
Retrospective: the team adds feedback, votes, and generates action items.
The
retro:action-item-createdevent creates tasks in the scrum-board backlog, ready for the next sprint.
Internal Architecture of PM Servers
All five PM servers follow the same standardized architectural structure,
sharing patterns and conventions from the @mcp-suite/core package:
server-name/
src/
index.ts --> entry point
server.ts --> createXxxServer(), registers tools and collaboration
services/
xxx-store.ts --> SQLite Store (persistent data management)
tools/
tool-name.ts --> individual tool handler with Zod schema
collaboration.ts --> setupCollaborationHandlers(eventBus, store)
The collaboration.ts pattern is particularly important: it is the file that manages
event subscription and publication on the EventBus, making each server's collaboration contract
with the rest of the suite explicit.
Shared Package Dependencies
The PM servers use three shared packages from the MCP Suite:
@mcp-suite/core: base for MCP server creation, tool registration, lifecycle management@mcp-suite/event-bus: typed event system for inter-server communication@mcp-suite/database: SQLite wrapper with automatic migrations (used by 4 of 5 servers; agile-metrics is stateless)
Conclusions
The five project management servers of the MCP Suite demonstrate how the MCP protocol can support complex and truly collaborative workflows. The scrum-board as the central hub, agile-metrics for predictive analytics with Monte Carlo, time-tracking for time management, project-economics for cost control, and retrospective-manager for continuous improvement form a complete agile management ecosystem.
The key pattern is event-driven collaboration: each server publishes events that trigger actions in others, creating automation chains that reduce manual work and keep the entire system synchronized. The retrospective-backlog cycle is a perfect example of how MCP servers can close feedback loops in a fully automated way.
In the next article, we will explore the advanced servers of the suite: incident-manager, decision-log, access-policy, quality-gate, workflow-orchestrator, insight-engine, and more, discovering how to handle even more complex scenarios with evolved architectural patterns.
The complete code for all project management servers is available in the Tech-MCP GitHub repository.







