#
tokens: 45584/50000 13/58 files (page 2/3)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 2 of 3. Use http://codebase.md/nguyenvanduocit/jira-mcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .claude
│   └── commands
│       ├── speckit.analyze.md
│       ├── speckit.checklist.md
│       ├── speckit.clarify.md
│       ├── speckit.constitution.md
│       ├── speckit.implement.md
│       ├── speckit.plan.md
│       ├── speckit.specify.md
│       └── speckit.tasks.md
├── .github
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows
│       ├── gitleaks.yaml
│       ├── release.yaml
│       └── scan.yaml
├── .gitignore
├── .specify
│   ├── memory
│   │   └── constitution.md
│   ├── scripts
│   │   └── bash
│   │       ├── check-prerequisites.sh
│   │       ├── common.sh
│   │       ├── create-new-feature.sh
│   │       ├── setup-plan.sh
│   │       └── update-agent-context.sh
│   └── templates
│       ├── agent-file-template.md
│       ├── checklist-template.md
│       ├── plan-template.md
│       ├── spec-template.md
│       └── tasks-template.md
├── CHANGELOG.md
├── CLAUDE.md
├── Dockerfile
├── go.mod
├── go.sum
├── LICENSE
├── main.go
├── prompts
│   └── jira_prompts.go
├── README.md
├── services
│   ├── atlassian.go
│   ├── httpclient.go
│   └── jira.go
├── specs
│   └── 001-i-want-to
│       ├── api-research.md
│       ├── checklists
│       │   ├── implementation-readiness.md
│       │   └── requirements.md
│       ├── contracts
│       │   └── mcp-tool-contract.json
│       ├── data-model.md
│       ├── plan.md
│       ├── quickstart.md
│       ├── research.md
│       ├── spec.md
│       ├── tasks.md
│       ├── test-dev-api.sh
│       └── test-get-build.sh
├── tools
│   ├── jira_comment.go
│   ├── jira_development.go
│   ├── jira_history.go
│   ├── jira_issue.go
│   ├── jira_relationship.go
│   ├── jira_search.go
│   ├── jira_sprint.go
│   ├── jira_status.go
│   ├── jira_transition.go
│   ├── jira_version.go
│   └── jira_worklog.go
└── util
    ├── jira_formatter.go
    └── README.md
```

# Files

--------------------------------------------------------------------------------
/specs/001-i-want-to/plan.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Implementation Plan: Retrieve Development Information from Jira Issue
  2 | 
  3 | **Branch**: `001-i-want-to` | **Date**: 2025-10-07 | **Spec**: [spec.md](./spec.md)
  4 | **Input**: Feature specification from `/specs/001-i-want-to/spec.md`
  5 | 
  6 | ## Summary
  7 | 
  8 | This feature adds a new MCP tool `jira_get_development_information` that retrieves branches, merge requests (pull requests), and commits linked to a Jira issue via Jira's Development Information API (`/rest/dev-status/1.0/issue/detail`). The tool will follow the existing pattern of typed handlers, singleton Jira client usage, and LLM-optimized text output.
  9 | 
 10 | ## Technical Context
 11 | 
 12 | **Language/Version**: Go 1.23.2
 13 | **Primary Dependencies**:
 14 | - `github.com/ctreminiom/go-atlassian v1.6.1` (Jira API client)
 15 | - `github.com/mark3labs/mcp-go v0.32.0` (MCP protocol)
 16 | **Storage**: N/A (stateless API wrapper)
 17 | **Testing**: Go standard testing (`go test ./...`), integration tests with live Jira instance
 18 | **Target Platform**: Cross-platform (macOS, Linux, Windows) via compiled Go binary
 19 | **Project Type**: Single project - MCP server binary
 20 | **Performance Goals**: Response within 3 seconds for issues with up to 50 linked development items
 21 | **Constraints**: Relies on Jira's stored development information (GitHub, GitLab, Bitbucket integrations must be configured in Jira)
 22 | **Scale/Scope**: Single new MCP tool with 3-4 optional filter parameters
 23 | 
 24 | ## Constitution Check
 25 | 
 26 | *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
 27 | 
 28 | ### Initial Check (Pre-Phase 0) ✅
 29 | 
 30 | **MCP Protocol Compliance:**
 31 | - [x] All features exposed via MCP tools (no direct API bypass) - New tool `jira_get_development_information`
 32 | - [x] Tool names follow `jira_<operation>` convention - Tool named `jira_get_development_information`
 33 | - [x] STDIO mode primary, HTTP mode development-only - Inherits existing server modes
 34 | 
 35 | **AI-First Output:**
 36 | - [x] Responses formatted for LLM readability - Will use human-readable text with clear sections for branches, merge requests, commits
 37 | - [x] Error messages include diagnostic context (endpoint, response body) - Following existing error handling pattern
 38 | - [x] Output uses `util.Format*` functions or similar formatting - Will create `util.FormatDevelopmentInfo` if needed (only if reused 3+ times per constitution)
 39 | 
 40 | **Simplicity:**
 41 | - [x] No unnecessary abstraction layers (managers, facades, orchestrators) - Direct API call in handler
 42 | - [x] Direct client calls preferred over wrappers - Handler calls `client` directly
 43 | - [x] Complexity violations documented in "Complexity Tracking" below - No violations
 44 | 
 45 | **Type Safety:**
 46 | - [x] Input structs defined with JSON tags and validation - `GetDevelopmentInfoInput` struct with `issue_key` required field
 47 | - [x] Typed handlers used (`mcp.NewTypedToolHandler`) - Will use `mcp.NewTypedToolHandler(jiraGetDevelopmentInfoHandler)`
 48 | 
 49 | **Resource Efficiency:**
 50 | - [x] Singleton pattern for client connections (`services.JiraClient()`) - Uses existing singleton
 51 | - [x] No per-request client creation - Reuses `services.JiraClient()`
 52 | 
 53 | **Testing Gates:**
 54 | - [x] Integration tests for new tool categories - Will add test to verify tool registration and basic response
 55 | - [x] Contract tests for tool registration and parameters - Will verify required parameters enforced
 56 | 
 57 | ### Post-Phase 1 Design Review ✅
 58 | 
 59 | **MCP Protocol Compliance:**
 60 | - [x] Tool contract defined in `contracts/mcp-tool-contract.json` with complete input schema
 61 | - [x] Tool name confirmed: `jira_get_development_information`
 62 | - [x] All parameters properly typed (string for issue_key, boolean for filters)
 63 | 
 64 | **AI-First Output:**
 65 | - [x] Output format designed in research.md - plain text with === section dividers
 66 | - [x] Hierarchical formatting: sections for branches/PRs/commits with indentation
 67 | - [x] Error messages include endpoint context (verified in contract)
 68 | - [x] **CONFIRMED**: No util function - inline string builder formatting (per research Task 2)
 69 | 
 70 | **Simplicity:**
 71 | - [x] Single tool file: `tools/jira_development.go`
 72 | - [x] Direct API calls using `client.NewRequest()` and `client.Call()`
 73 | - [x] No wrapper services or helper classes
 74 | - [x] Inline formatting functions within handler (formatBranches, formatPullRequests, formatCommits)
 75 | 
 76 | **Type Safety:**
 77 | - [x] Input struct defined in data-model.md: `GetDevelopmentInfoInput` with validate tags
 78 | - [x] Response structs defined: DevStatusResponse, Branch, PullRequest, Repository, Commit, Author
 79 | - [x] All JSON tags specified
 80 | - [x] Validation: `validate:"required"` on issue_key field
 81 | 
 82 | **Resource Efficiency:**
 83 | - [x] Uses existing `services.JiraClient()` singleton
 84 | - [x] Single API call per tool invocation (convert issue key → fetch dev info)
 85 | - [x] No caching layer (keeps it simple)
 86 | 
 87 | **Testing Gates:**
 88 | - [x] Test plan documented in contract: unit tests for registration, integration tests for API calls
 89 | - [x] Test file locations specified: `tools/jira_development_test.go`, `tests/development_test.go`
 90 | 
 91 | **Design Quality:**
 92 | - [x] Data model complete with entity relationships documented
 93 | - [x] API contract specifies all request/response formats
 94 | - [x] Quickstart guide provides usage examples
 95 | - [x] Research decisions documented with rationale
 96 | 
 97 | ### Compliance Status: ✅ PASSED
 98 | 
 99 | All constitution principles are satisfied. No violations. Ready for `/speckit.tasks` to generate implementation tasks.
100 | 
101 | ## Project Structure
102 | 
103 | ### Documentation (this feature)
104 | 
105 | ```
106 | specs/001-i-want-to/
107 | ├── plan.md              # This file
108 | ├── research.md          # Phase 0: API research and response format decisions
109 | ├── data-model.md        # Phase 1: Development information entities
110 | ├── quickstart.md        # Phase 1: Usage examples
111 | ├── contracts/           # Phase 1: API response schema
112 | │   └── dev-info-schema.json
113 | └── tasks.md             # Phase 2: Implementation tasks (generated by /speckit.tasks)
114 | ```
115 | 
116 | ### Source Code (repository root)
117 | 
118 | ```
119 | # Single project structure (matches existing)
120 | tools/
121 | ├── jira_issue.go
122 | ├── jira_comment.go
123 | ├── jira_sprint.go
124 | ├── jira_development.go    # NEW: Development info tool registration and handler
125 | └── ...
126 | 
127 | services/
128 | └── jira.go                # Existing: Singleton client
129 | 
130 | util/
131 | └── formatter.go           # Potentially NEW: FormatDevelopmentInfo (only if complex)
132 | 
133 | main.go                    # Updated: Register new tool
134 | 
135 | tests/                     # NEW: Integration test for development info tool
136 | └── development_test.go
137 | ```
138 | 
139 | **Structure Decision**: This follows the existing single-project Go structure. The new tool will be implemented in `tools/jira_development.go` following the same pattern as `tools/jira_issue.go`. No new directories or architectural changes needed.
140 | 
141 | ## Complexity Tracking
142 | 
143 | *No complexity violations identified. The implementation follows existing patterns with no additional abstraction layers.*
144 | 
145 | ## Phase 0: Research Tasks
146 | 
147 | ### Research Task 1: go-atlassian Development Information API
148 | 
149 | **Objective**: Determine the exact method and response structure for retrieving development information from the go-atlassian library.
150 | 
151 | **Questions to Answer**:
152 | 1. Does `go-atlassian` provide a method for `/rest/dev-status/1.0/issue/detail` endpoint?
153 | 2. What is the response structure (types, fields)?
154 | 3. How are branches, merge requests, and commits organized in the response?
155 | 4. What error cases exist (issue not found, no dev info, permission denied)?
156 | 5. Are there any pagination or rate limiting considerations?
157 | 
158 | **Research Method**:
159 | - Review go-atlassian documentation and source code
160 | - Check for methods in client Issue service or Development service
161 | - Test with live Jira instance if available
162 | 
163 | ### Research Task 2: Output Formatting Best Practices
164 | 
165 | **Objective**: Define the optimal text format for LLM consumption of development information.
166 | 
167 | **Questions to Answer**:
168 | 1. How should branches, merge requests, and commits be grouped and labeled?
169 | 2. What level of detail is appropriate for each item (full metadata vs. summary)?
170 | 3. How to handle empty results (no development info linked)?
171 | 4. What formatting makes state information (open/merged/closed) clear?
172 | 
173 | **Research Method**:
174 | - Review existing `util.Format*` functions in codebase
175 | - Consider markdown-style formatting with headers and bullet lists
176 | - Design for clarity without verbose JSON
177 | 
178 | ### Research Task 3: Filter Parameter Design
179 | 
180 | **Objective**: Determine the optimal parameter structure for filtering by development information type.
181 | 
182 | **Questions to Answer**:
183 | 1. Should filters be separate boolean flags (`include_branches`, `include_commits`) or an enum/string array?
184 | 2. What is the default behavior (all types vs. explicit opt-in)?
185 | 3. How do other Jira MCP tools handle optional filtering?
186 | 
187 | **Research Method**:
188 | - Review existing tool parameters in `tools/` directory
189 | - Check go-atlassian API capabilities for filtering
190 | - Consider LLM usability (simpler is better)
191 | 
192 | **Output**: `research.md` with decisions and rationale for all three research tasks.
193 | 
```

--------------------------------------------------------------------------------
/tools/jira_sprint.go:
--------------------------------------------------------------------------------

```go
  1 | package tools
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 	"strconv"
  7 | 	"strings"
  8 | 
  9 | 	"github.com/ctreminiom/go-atlassian/pkg/infra/models"
 10 | 	"github.com/mark3labs/mcp-go/mcp"
 11 | 	"github.com/mark3labs/mcp-go/server"
 12 | 	"github.com/nguyenvanduocit/jira-mcp/services"
 13 | )
 14 | 
 15 | // Input types for typed tools
 16 | type ListSprintsInput struct {
 17 | 	BoardID    string `json:"board_id,omitempty"`
 18 | 	ProjectKey string `json:"project_key,omitempty"`
 19 | }
 20 | 
 21 | type GetSprintInput struct {
 22 | 	SprintID string `json:"sprint_id" validate:"required"`
 23 | }
 24 | 
 25 | type GetActiveSprintInput struct {
 26 | 	BoardID    string `json:"board_id,omitempty"`
 27 | 	ProjectKey string `json:"project_key,omitempty"`
 28 | }
 29 | 
 30 | type SearchSprintByNameInput struct {
 31 | 	Name       string `json:"name" validate:"required"`
 32 | 	BoardID    string `json:"board_id,omitempty"`
 33 | 	ProjectKey string `json:"project_key,omitempty"`
 34 | 	ExactMatch bool   `json:"exact_match,omitempty"`
 35 | }
 36 | 
 37 | func RegisterJiraSprintTool(s *server.MCPServer) {
 38 | 	jiraListSprintTool := mcp.NewTool("jira_list_sprints",
 39 | 		mcp.WithDescription("List all active and future sprints for a specific Jira board or project. Requires either board_id or project_key."),
 40 | 		mcp.WithString("board_id", mcp.Description("Numeric ID of the Jira board (can be found in board URL). Optional if project_key is provided.")),
 41 | 		mcp.WithString("project_key", mcp.Description("The project key (e.g., KP, PROJ, DEV). Optional if board_id is provided.")),
 42 | 	)
 43 | 	s.AddTool(jiraListSprintTool, mcp.NewTypedToolHandler(jiraListSprintHandler))
 44 | 
 45 | 	jiraGetSprintTool := mcp.NewTool("jira_get_sprint",
 46 | 		mcp.WithDescription("Retrieve detailed information about a specific Jira sprint by its ID"),
 47 | 		mcp.WithString("sprint_id", mcp.Required(), mcp.Description("Numeric ID of the sprint to retrieve")),
 48 | 	)
 49 | 	s.AddTool(jiraGetSprintTool, mcp.NewTypedToolHandler(jiraGetSprintHandler))
 50 | 
 51 | 	jiraGetActiveSprintTool := mcp.NewTool("jira_get_active_sprint",
 52 | 		mcp.WithDescription("Get the currently active sprint for a given board or project. Requires either board_id or project_key."),
 53 | 		mcp.WithString("board_id", mcp.Description("Numeric ID of the Jira board. Optional if project_key is provided.")),
 54 | 		mcp.WithString("project_key", mcp.Description("The project key (e.g., KP, PROJ, DEV). Optional if board_id is provided.")),
 55 | 	)
 56 | 	s.AddTool(jiraGetActiveSprintTool, mcp.NewTypedToolHandler(jiraGetActiveSprintHandler))
 57 | 
 58 | 	jiraSearchSprintByNameTool := mcp.NewTool("jira_search_sprint_by_name",
 59 | 		mcp.WithDescription("Search for sprints by name across boards or projects. Supports both exact and partial name matching."),
 60 | 		mcp.WithString("name", mcp.Required(), mcp.Description("Sprint name to search for (case-insensitive)")),
 61 | 		mcp.WithString("board_id", mcp.Description("Numeric ID of the Jira board to search in. Optional if project_key is provided.")),
 62 | 		mcp.WithString("project_key", mcp.Description("The project key (e.g., KP, PROJ, DEV) to search in. Optional if board_id is provided.")),
 63 | 		mcp.WithBoolean("exact_match", mcp.Description("If true, only return sprints with exact name match. Default is false (partial matching).")),
 64 | 	)
 65 | 	s.AddTool(jiraSearchSprintByNameTool, mcp.NewTypedToolHandler(searchSprintByNameHandler))
 66 | }
 67 | 
 68 | // Helper function to get board IDs either from direct board_id or by finding boards for a project
 69 | func getBoardIDsFromInput(ctx context.Context, boardID, projectKey string) ([]int, error) {
 70 | 	if boardID == "" && projectKey == "" {
 71 | 		return nil, fmt.Errorf("either board_id or project_key argument is required")
 72 | 	}
 73 | 
 74 | 	if boardID != "" {
 75 | 		boardIDInt, err := strconv.Atoi(boardID)
 76 | 		if err != nil {
 77 | 			return nil, fmt.Errorf("invalid board_id: %v", err)
 78 | 		}
 79 | 		return []int{boardIDInt}, nil
 80 | 	}
 81 | 
 82 | 	if projectKey != "" {
 83 | 		boards, response, err := services.AgileClient().Board.Gets(ctx, &models.GetBoardsOptions{
 84 | 			ProjectKeyOrID: projectKey,
 85 | 		}, 0, 50)
 86 | 		if err != nil {
 87 | 			if response != nil {
 88 | 				return nil, fmt.Errorf("failed to get boards: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
 89 | 			}
 90 | 			return nil, fmt.Errorf("failed to get boards: %v", err)
 91 | 		}
 92 | 
 93 | 		if len(boards.Values) == 0 {
 94 | 			return nil, fmt.Errorf("no boards found for project: %s", projectKey)
 95 | 		}
 96 | 
 97 | 		var boardIDs []int
 98 | 		for _, board := range boards.Values {
 99 | 			boardIDs = append(boardIDs, board.ID)
100 | 		}
101 | 		return boardIDs, nil
102 | 	}
103 | 
104 | 	return nil, fmt.Errorf("either board_id or project_key argument is required")
105 | }
106 | 
107 | func jiraGetSprintHandler(ctx context.Context, request mcp.CallToolRequest, input GetSprintInput) (*mcp.CallToolResult, error) {
108 | 	sprintID, err := strconv.Atoi(input.SprintID)
109 | 	if err != nil {
110 | 		return nil, fmt.Errorf("invalid sprint_id: %v", err)
111 | 	}
112 | 
113 | 	sprint, response, err := services.AgileClient().Sprint.Get(ctx, sprintID)
114 | 	if err != nil {
115 | 		if response != nil {
116 | 			return nil, fmt.Errorf("failed to get sprint: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
117 | 		}
118 | 		return nil, fmt.Errorf("failed to get sprint: %v", err)
119 | 	}
120 | 
121 | 	result := fmt.Sprintf(`Sprint Details:
122 | ID: %d
123 | Name: %s
124 | State: %s
125 | Start Date: %s
126 | End Date: %s
127 | Complete Date: %s
128 | Origin Board ID: %d
129 | Goal: %s`,
130 | 		sprint.ID,
131 | 		sprint.Name,
132 | 		sprint.State,
133 | 		sprint.StartDate,
134 | 		sprint.EndDate,
135 | 		sprint.CompleteDate,
136 | 		sprint.OriginBoardID,
137 | 		sprint.Goal,
138 | 	)
139 | 
140 | 	return mcp.NewToolResultText(result), nil
141 | }
142 | 
143 | func jiraListSprintHandler(ctx context.Context, request mcp.CallToolRequest, input ListSprintsInput) (*mcp.CallToolResult, error) {
144 | 	boardIDs, err := getBoardIDsFromInput(ctx, input.BoardID, input.ProjectKey)
145 | 	if err != nil {
146 | 		return nil, err
147 | 	}
148 | 
149 | 	var allSprints []string
150 | 	for _, boardID := range boardIDs {
151 | 		sprints, response, err := services.AgileClient().Board.Sprints(ctx, boardID, 0, 50, []string{"active", "future"})
152 | 		if err != nil {
153 | 			if response != nil {
154 | 				return nil, fmt.Errorf("failed to get sprints: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
155 | 			}
156 | 			return nil, fmt.Errorf("failed to get sprints: %v", err)
157 | 		}
158 | 
159 | 		for _, sprint := range sprints.Values {
160 | 			allSprints = append(allSprints, fmt.Sprintf("ID: %d\nName: %s\nState: %s\nStartDate: %s\nEndDate: %s\nBoard ID: %d\n", 
161 | 				sprint.ID, sprint.Name, sprint.State, sprint.StartDate, sprint.EndDate, boardID))
162 | 		}
163 | 	}
164 | 
165 | 	if len(allSprints) == 0 {
166 | 		return mcp.NewToolResultText("No sprints found."), nil
167 | 	}
168 | 
169 | 	result := strings.Join(allSprints, "\n")
170 | 	return mcp.NewToolResultText(result), nil
171 | }
172 | 
173 | func jiraGetActiveSprintHandler(ctx context.Context, request mcp.CallToolRequest, input GetActiveSprintInput) (*mcp.CallToolResult, error) {
174 | 	boardIDs, err := getBoardIDsFromInput(ctx, input.BoardID, input.ProjectKey)
175 | 	if err != nil {
176 | 		return nil, err
177 | 	}
178 | 
179 | 	// Loop through boards and return the first active sprint found
180 | 	for _, boardID := range boardIDs {
181 | 		sprints, response, err := services.AgileClient().Board.Sprints(ctx, boardID, 0, 50, []string{"active"})
182 | 		if err != nil {
183 | 			if response != nil {
184 | 				return nil, fmt.Errorf("failed to get active sprint: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
185 | 			}
186 | 			continue // Try next board if this one fails
187 | 		}
188 | 
189 | 		if len(sprints.Values) > 0 {
190 | 			sprint := sprints.Values[0]
191 | 			result := fmt.Sprintf(`Active Sprint:
192 | ID: %d
193 | Name: %s
194 | State: %s
195 | Start Date: %s
196 | End Date: %s
197 | Board ID: %d`,
198 | 				sprint.ID,
199 | 				sprint.Name,
200 | 				sprint.State,
201 | 				sprint.StartDate,
202 | 				sprint.EndDate,
203 | 				boardID,
204 | 			)
205 | 			return mcp.NewToolResultText(result), nil
206 | 		}
207 | 	}
208 | 
209 | 	return mcp.NewToolResultText("No active sprint found."), nil
210 | }
211 | 
212 | func searchSprintByNameHandler(ctx context.Context, request mcp.CallToolRequest, input SearchSprintByNameInput) (*mcp.CallToolResult, error) {
213 | 	boardIDs, err := getBoardIDsFromInput(ctx, input.BoardID, input.ProjectKey)
214 | 	if err != nil {
215 | 		return nil, err
216 | 	}
217 | 
218 | 	var matchingSprints []string
219 | 	searchTerm := strings.ToLower(input.Name)
220 | 
221 | 	for _, boardID := range boardIDs {
222 | 		// Get all sprints (active, future, and closed) for comprehensive search
223 | 		sprints, response, err := services.AgileClient().Board.Sprints(ctx, boardID, 0, 100, []string{"active", "future", "closed"})
224 | 		if err != nil {
225 | 			if response != nil {
226 | 				return nil, fmt.Errorf("failed to get sprints: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
227 | 			}
228 | 			return nil, fmt.Errorf("failed to get sprints: %v", err)
229 | 		}
230 | 
231 | 		for _, sprint := range sprints.Values {
232 | 			sprintNameLower := strings.ToLower(sprint.Name)
233 | 
234 | 			var isMatch bool
235 | 			if input.ExactMatch {
236 | 				isMatch = sprintNameLower == searchTerm
237 | 			} else {
238 | 				isMatch = strings.Contains(sprintNameLower, searchTerm)
239 | 			}
240 | 
241 | 			if isMatch {
242 | 				matchingSprints = append(matchingSprints, fmt.Sprintf(`ID: %d
243 | Name: %s
244 | State: %s
245 | Start Date: %s
246 | End Date: %s
247 | Complete Date: %s
248 | Board ID: %d
249 | Goal: %s`,
250 | 					sprint.ID,
251 | 					sprint.Name,
252 | 					sprint.State,
253 | 					sprint.StartDate,
254 | 					sprint.EndDate,
255 | 					sprint.CompleteDate,
256 | 					boardID,
257 | 					sprint.Goal))
258 | 			}
259 | 		}
260 | 	}
261 | 
262 | 	if len(matchingSprints) == 0 {
263 | 		matchType := "containing"
264 | 		if input.ExactMatch {
265 | 			matchType = "with exact name"
266 | 		}
267 | 		return mcp.NewToolResultText(fmt.Sprintf("No sprints found %s '%s'.", matchType, input.Name)), nil
268 | 	}
269 | 
270 | 	result := strings.Join(matchingSprints, "\n\n")
271 | 	return mcp.NewToolResultText(result), nil
272 | }
273 | 
```

--------------------------------------------------------------------------------
/.specify/templates/tasks-template.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | description: "Task list template for feature implementation"
  3 | ---
  4 | 
  5 | # Tasks: [FEATURE NAME]
  6 | 
  7 | **Input**: Design documents from `/specs/[###-feature-name]/`
  8 | **Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
  9 | 
 10 | **Tests**: The examples below include test tasks. Tests are OPTIONAL - only include them if explicitly requested in the feature specification.
 11 | 
 12 | **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
 13 | 
 14 | ## Format: `[ID] [P?] [Story] Description`
 15 | - **[P]**: Can run in parallel (different files, no dependencies)
 16 | - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
 17 | - Include exact file paths in descriptions
 18 | 
 19 | ## Path Conventions
 20 | - **Single project**: `src/`, `tests/` at repository root
 21 | - **Web app**: `backend/src/`, `frontend/src/`
 22 | - **Mobile**: `api/src/`, `ios/src/` or `android/src/`
 23 | - Paths shown below assume single project - adjust based on plan.md structure
 24 | 
 25 | <!-- 
 26 |   ============================================================================
 27 |   IMPORTANT: The tasks below are SAMPLE TASKS for illustration purposes only.
 28 |   
 29 |   The /speckit.tasks command MUST replace these with actual tasks based on:
 30 |   - User stories from spec.md (with their priorities P1, P2, P3...)
 31 |   - Feature requirements from plan.md
 32 |   - Entities from data-model.md
 33 |   - Endpoints from contracts/
 34 |   
 35 |   Tasks MUST be organized by user story so each story can be:
 36 |   - Implemented independently
 37 |   - Tested independently
 38 |   - Delivered as an MVP increment
 39 |   
 40 |   DO NOT keep these sample tasks in the generated tasks.md file.
 41 |   ============================================================================
 42 | -->
 43 | 
 44 | ## Phase 1: Setup (Shared Infrastructure)
 45 | 
 46 | **Purpose**: Project initialization and basic structure
 47 | 
 48 | - [ ] T001 Create project structure per implementation plan
 49 | - [ ] T002 Initialize Go module with MCP dependencies (`github.com/mark3labs/mcp-go`, `github.com/ctreminiom/go-atlassian`)
 50 | - [ ] T003 [P] Configure linting and formatting tools (golangci-lint, gofmt)
 51 | 
 52 | ---
 53 | 
 54 | ## Phase 2: Foundational (Blocking Prerequisites)
 55 | 
 56 | **Purpose**: Core infrastructure that MUST be complete before ANY user story can be implemented
 57 | 
 58 | **⚠️ CRITICAL**: No user story work can begin until this phase is complete
 59 | 
 60 | Examples of foundational tasks (adjust based on your project):
 61 | 
 62 | - [ ] T004 Setup database schema and migrations framework
 63 | - [ ] T005 [P] Implement authentication/authorization framework
 64 | - [ ] T006 [P] Setup API routing and middleware structure
 65 | - [ ] T007 Create base models/entities that all stories depend on
 66 | - [ ] T008 Configure error handling and logging infrastructure
 67 | - [ ] T009 Setup environment configuration management
 68 | 
 69 | **Checkpoint**: Foundation ready - user story implementation can now begin in parallel
 70 | 
 71 | ---
 72 | 
 73 | ## Phase 3: User Story 1 - [Title] (Priority: P1) 🎯 MVP
 74 | 
 75 | **Goal**: [Brief description of what this story delivers]
 76 | 
 77 | **Independent Test**: [How to verify this story works on its own]
 78 | 
 79 | ### Tests for User Story 1 (OPTIONAL - only if tests requested) ⚠️
 80 | 
 81 | **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
 82 | 
 83 | - [ ] T010 [P] [US1] Contract test for [endpoint] in tests/contract/test_[name].py
 84 | - [ ] T011 [P] [US1] Integration test for [user journey] in tests/integration/test_[name].py
 85 | 
 86 | ### Implementation for User Story 1
 87 | 
 88 | - [ ] T012 [P] [US1] Define `<Operation>Input` struct with JSON tags and validation in tools/jira_[category].go
 89 | - [ ] T013 [US1] Implement `RegisterJira<Category>Tool` registration function in tools/jira_[category].go
 90 | - [ ] T014 [US1] Implement `jira<Operation>Handler` typed handler in tools/jira_[category].go
 91 | - [ ] T015 [US1] Add formatter function in util/jira_formatter.go (if output used by 3+ tools)
 92 | - [ ] T016 [US1] Register tool in main.go via `tools.RegisterJira<Category>Tool(mcpServer)`
 93 | - [ ] T017 [US1] Add integration test in tools/jira_[category]_test.go
 94 | 
 95 | **Checkpoint**: At this point, User Story 1 should be fully functional and testable independently
 96 | 
 97 | ---
 98 | 
 99 | ## Phase 4: User Story 2 - [Title] (Priority: P2)
100 | 
101 | **Goal**: [Brief description of what this story delivers]
102 | 
103 | **Independent Test**: [How to verify this story works on its own]
104 | 
105 | ### Tests for User Story 2 (OPTIONAL - only if tests requested) ⚠️
106 | 
107 | - [ ] T018 [P] [US2] Contract test for [endpoint] in tests/contract/test_[name].py
108 | - [ ] T019 [P] [US2] Integration test for [user journey] in tests/integration/test_[name].py
109 | 
110 | ### Implementation for User Story 2
111 | 
112 | - [ ] T020 [P] [US2] Create [Entity] model in src/models/[entity].py
113 | - [ ] T021 [US2] Implement [Service] in src/services/[service].py
114 | - [ ] T022 [US2] Implement [endpoint/feature] in src/[location]/[file].py
115 | - [ ] T023 [US2] Integrate with User Story 1 components (if needed)
116 | 
117 | **Checkpoint**: At this point, User Stories 1 AND 2 should both work independently
118 | 
119 | ---
120 | 
121 | ## Phase 5: User Story 3 - [Title] (Priority: P3)
122 | 
123 | **Goal**: [Brief description of what this story delivers]
124 | 
125 | **Independent Test**: [How to verify this story works on its own]
126 | 
127 | ### Tests for User Story 3 (OPTIONAL - only if tests requested) ⚠️
128 | 
129 | - [ ] T024 [P] [US3] Contract test for [endpoint] in tests/contract/test_[name].py
130 | - [ ] T025 [P] [US3] Integration test for [user journey] in tests/integration/test_[name].py
131 | 
132 | ### Implementation for User Story 3
133 | 
134 | - [ ] T026 [P] [US3] Create [Entity] model in src/models/[entity].py
135 | - [ ] T027 [US3] Implement [Service] in src/services/[service].py
136 | - [ ] T028 [US3] Implement [endpoint/feature] in src/[location]/[file].py
137 | 
138 | **Checkpoint**: All user stories should now be independently functional
139 | 
140 | ---
141 | 
142 | [Add more user story phases as needed, following the same pattern]
143 | 
144 | ---
145 | 
146 | ## Phase N: Polish & Cross-Cutting Concerns
147 | 
148 | **Purpose**: Improvements that affect multiple user stories
149 | 
150 | - [ ] TXXX [P] Documentation updates in docs/
151 | - [ ] TXXX Code cleanup and refactoring
152 | - [ ] TXXX Performance optimization across all stories
153 | - [ ] TXXX [P] Additional unit tests (if requested) in tests/unit/
154 | - [ ] TXXX Security hardening
155 | - [ ] TXXX Run quickstart.md validation
156 | 
157 | ---
158 | 
159 | ## Dependencies & Execution Order
160 | 
161 | ### Phase Dependencies
162 | 
163 | - **Setup (Phase 1)**: No dependencies - can start immediately
164 | - **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories
165 | - **User Stories (Phase 3+)**: All depend on Foundational phase completion
166 |   - User stories can then proceed in parallel (if staffed)
167 |   - Or sequentially in priority order (P1 → P2 → P3)
168 | - **Polish (Final Phase)**: Depends on all desired user stories being complete
169 | 
170 | ### User Story Dependencies
171 | 
172 | - **User Story 1 (P1)**: Can start after Foundational (Phase 2) - No dependencies on other stories
173 | - **User Story 2 (P2)**: Can start after Foundational (Phase 2) - May integrate with US1 but should be independently testable
174 | - **User Story 3 (P3)**: Can start after Foundational (Phase 2) - May integrate with US1/US2 but should be independently testable
175 | 
176 | ### Within Each User Story
177 | 
178 | - Tests (if included) MUST be written and FAIL before implementation
179 | - Models before services
180 | - Services before endpoints
181 | - Core implementation before integration
182 | - Story complete before moving to next priority
183 | 
184 | ### Parallel Opportunities
185 | 
186 | - All Setup tasks marked [P] can run in parallel
187 | - All Foundational tasks marked [P] can run in parallel (within Phase 2)
188 | - Once Foundational phase completes, all user stories can start in parallel (if team capacity allows)
189 | - All tests for a user story marked [P] can run in parallel
190 | - Models within a story marked [P] can run in parallel
191 | - Different user stories can be worked on in parallel by different team members
192 | 
193 | ---
194 | 
195 | ## Parallel Example: User Story 1
196 | 
197 | ```bash
198 | # Launch all tests for User Story 1 together (if tests requested):
199 | Task: "Contract test for [endpoint] in tests/contract/test_[name].py"
200 | Task: "Integration test for [user journey] in tests/integration/test_[name].py"
201 | 
202 | # Launch all models for User Story 1 together:
203 | Task: "Create [Entity1] model in src/models/[entity1].py"
204 | Task: "Create [Entity2] model in src/models/[entity2].py"
205 | ```
206 | 
207 | ---
208 | 
209 | ## Implementation Strategy
210 | 
211 | ### MVP First (User Story 1 Only)
212 | 
213 | 1. Complete Phase 1: Setup
214 | 2. Complete Phase 2: Foundational (CRITICAL - blocks all stories)
215 | 3. Complete Phase 3: User Story 1
216 | 4. **STOP and VALIDATE**: Test User Story 1 independently
217 | 5. Deploy/demo if ready
218 | 
219 | ### Incremental Delivery
220 | 
221 | 1. Complete Setup + Foundational → Foundation ready
222 | 2. Add User Story 1 → Test independently → Deploy/Demo (MVP!)
223 | 3. Add User Story 2 → Test independently → Deploy/Demo
224 | 4. Add User Story 3 → Test independently → Deploy/Demo
225 | 5. Each story adds value without breaking previous stories
226 | 
227 | ### Parallel Team Strategy
228 | 
229 | With multiple developers:
230 | 
231 | 1. Team completes Setup + Foundational together
232 | 2. Once Foundational is done:
233 |    - Developer A: User Story 1
234 |    - Developer B: User Story 2
235 |    - Developer C: User Story 3
236 | 3. Stories complete and integrate independently
237 | 
238 | ---
239 | 
240 | ## Notes
241 | 
242 | - [P] tasks = different files, no dependencies
243 | - [Story] label maps task to specific user story for traceability
244 | - Each user story should be independently completable and testable
245 | - Verify tests fail before implementing
246 | - Commit after each task or logical group
247 | - Stop at any checkpoint to validate story independently
248 | - Avoid: vague tasks, same file conflicts, cross-story dependencies that break independence
249 | 
250 | 
251 | 
```

--------------------------------------------------------------------------------
/.claude/commands/speckit.clarify.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec.
  3 | ---
  4 | 
  5 | ## User Input
  6 | 
  7 | ```text
  8 | $ARGUMENTS
  9 | ```
 10 | 
 11 | You **MUST** consider the user input before proceeding (if not empty).
 12 | 
 13 | ## Outline
 14 | 
 15 | Goal: Detect and reduce ambiguity or missing decision points in the active feature specification and record the clarifications directly in the spec file.
 16 | 
 17 | Note: This clarification workflow is expected to run (and be completed) BEFORE invoking `/speckit.plan`. If the user explicitly states they are skipping clarification (e.g., exploratory spike), you may proceed, but must warn that downstream rework risk increases.
 18 | 
 19 | Execution steps:
 20 | 
 21 | 1. Run `.specify/scripts/bash/check-prerequisites.sh --json --paths-only` from repo root **once** (combined `--json --paths-only` mode / `-Json -PathsOnly`). Parse minimal JSON payload fields:
 22 |    - `FEATURE_DIR`
 23 |    - `FEATURE_SPEC`
 24 |    - (Optionally capture `IMPL_PLAN`, `TASKS` for future chained flows.)
 25 |    - If JSON parsing fails, abort and instruct user to re-run `/speckit.specify` or verify feature branch environment.
 26 | 
 27 | 2. Load the current spec file. Perform a structured ambiguity & coverage scan using this taxonomy. For each category, mark status: Clear / Partial / Missing. Produce an internal coverage map used for prioritization (do not output raw map unless no questions will be asked).
 28 | 
 29 |    Functional Scope & Behavior:
 30 |    - Core user goals & success criteria
 31 |    - Explicit out-of-scope declarations
 32 |    - User roles / personas differentiation
 33 | 
 34 |    Domain & Data Model:
 35 |    - Entities, attributes, relationships
 36 |    - Identity & uniqueness rules
 37 |    - Lifecycle/state transitions
 38 |    - Data volume / scale assumptions
 39 | 
 40 |    Interaction & UX Flow:
 41 |    - Critical user journeys / sequences
 42 |    - Error/empty/loading states
 43 |    - Accessibility or localization notes
 44 | 
 45 |    Non-Functional Quality Attributes:
 46 |    - Performance (latency, throughput targets)
 47 |    - Scalability (horizontal/vertical, limits)
 48 |    - Reliability & availability (uptime, recovery expectations)
 49 |    - Observability (logging, metrics, tracing signals)
 50 |    - Security & privacy (authN/Z, data protection, threat assumptions)
 51 |    - Compliance / regulatory constraints (if any)
 52 | 
 53 |    Integration & External Dependencies:
 54 |    - External services/APIs and failure modes
 55 |    - Data import/export formats
 56 |    - Protocol/versioning assumptions
 57 | 
 58 |    Edge Cases & Failure Handling:
 59 |    - Negative scenarios
 60 |    - Rate limiting / throttling
 61 |    - Conflict resolution (e.g., concurrent edits)
 62 | 
 63 |    Constraints & Tradeoffs:
 64 |    - Technical constraints (language, storage, hosting)
 65 |    - Explicit tradeoffs or rejected alternatives
 66 | 
 67 |    Terminology & Consistency:
 68 |    - Canonical glossary terms
 69 |    - Avoided synonyms / deprecated terms
 70 | 
 71 |    Completion Signals:
 72 |    - Acceptance criteria testability
 73 |    - Measurable Definition of Done style indicators
 74 | 
 75 |    Misc / Placeholders:
 76 |    - TODO markers / unresolved decisions
 77 |    - Ambiguous adjectives ("robust", "intuitive") lacking quantification
 78 | 
 79 |    For each category with Partial or Missing status, add a candidate question opportunity unless:
 80 |    - Clarification would not materially change implementation or validation strategy
 81 |    - Information is better deferred to planning phase (note internally)
 82 | 
 83 | 3. Generate (internally) a prioritized queue of candidate clarification questions (maximum 5). Do NOT output them all at once. Apply these constraints:
 84 |     - Maximum of 10 total questions across the whole session.
 85 |     - Each question must be answerable with EITHER:
 86 |        * A short multiple‑choice selection (2–5 distinct, mutually exclusive options), OR
 87 |        * A one-word / short‑phrase answer (explicitly constrain: "Answer in <=5 words").
 88 |    - Only include questions whose answers materially impact architecture, data modeling, task decomposition, test design, UX behavior, operational readiness, or compliance validation.
 89 |    - Ensure category coverage balance: attempt to cover the highest impact unresolved categories first; avoid asking two low-impact questions when a single high-impact area (e.g., security posture) is unresolved.
 90 |    - Exclude questions already answered, trivial stylistic preferences, or plan-level execution details (unless blocking correctness).
 91 |    - Favor clarifications that reduce downstream rework risk or prevent misaligned acceptance tests.
 92 |    - If more than 5 categories remain unresolved, select the top 5 by (Impact * Uncertainty) heuristic.
 93 | 
 94 | 4. Sequential questioning loop (interactive):
 95 |     - Present EXACTLY ONE question at a time.
 96 |     - For multiple‑choice questions render options as a Markdown table:
 97 | 
 98 |        | Option | Description |
 99 |        |--------|-------------|
100 |        | A | <Option A description> |
101 |        | B | <Option B description> |
102 |        | C | <Option C description> | (add D/E as needed up to 5)
103 |        | Short | Provide a different short answer (<=5 words) | (Include only if free-form alternative is appropriate)
104 | 
105 |     - For short‑answer style (no meaningful discrete options), output a single line after the question: `Format: Short answer (<=5 words)`.
106 |     - After the user answers:
107 |        * Validate the answer maps to one option or fits the <=5 word constraint.
108 |        * If ambiguous, ask for a quick disambiguation (count still belongs to same question; do not advance).
109 |        * Once satisfactory, record it in working memory (do not yet write to disk) and move to the next queued question.
110 |     - Stop asking further questions when:
111 |        * All critical ambiguities resolved early (remaining queued items become unnecessary), OR
112 |        * User signals completion ("done", "good", "no more"), OR
113 |        * You reach 5 asked questions.
114 |     - Never reveal future queued questions in advance.
115 |     - If no valid questions exist at start, immediately report no critical ambiguities.
116 | 
117 | 5. Integration after EACH accepted answer (incremental update approach):
118 |     - Maintain in-memory representation of the spec (loaded once at start) plus the raw file contents.
119 |     - For the first integrated answer in this session:
120 |        * Ensure a `## Clarifications` section exists (create it just after the highest-level contextual/overview section per the spec template if missing).
121 |        * Under it, create (if not present) a `### Session YYYY-MM-DD` subheading for today.
122 |     - Append a bullet line immediately after acceptance: `- Q: <question> → A: <final answer>`.
123 |     - Then immediately apply the clarification to the most appropriate section(s):
124 |        * Functional ambiguity → Update or add a bullet in Functional Requirements.
125 |        * User interaction / actor distinction → Update User Stories or Actors subsection (if present) with clarified role, constraint, or scenario.
126 |        * Data shape / entities → Update Data Model (add fields, types, relationships) preserving ordering; note added constraints succinctly.
127 |        * Non-functional constraint → Add/modify measurable criteria in Non-Functional / Quality Attributes section (convert vague adjective to metric or explicit target).
128 |        * Edge case / negative flow → Add a new bullet under Edge Cases / Error Handling (or create such subsection if template provides placeholder for it).
129 |        * Terminology conflict → Normalize term across spec; retain original only if necessary by adding `(formerly referred to as "X")` once.
130 |     - If the clarification invalidates an earlier ambiguous statement, replace that statement instead of duplicating; leave no obsolete contradictory text.
131 |     - Save the spec file AFTER each integration to minimize risk of context loss (atomic overwrite).
132 |     - Preserve formatting: do not reorder unrelated sections; keep heading hierarchy intact.
133 |     - Keep each inserted clarification minimal and testable (avoid narrative drift).
134 | 
135 | 6. Validation (performed after EACH write plus final pass):
136 |    - Clarifications session contains exactly one bullet per accepted answer (no duplicates).
137 |    - Total asked (accepted) questions ≤ 5.
138 |    - Updated sections contain no lingering vague placeholders the new answer was meant to resolve.
139 |    - No contradictory earlier statement remains (scan for now-invalid alternative choices removed).
140 |    - Markdown structure valid; only allowed new headings: `## Clarifications`, `### Session YYYY-MM-DD`.
141 |    - Terminology consistency: same canonical term used across all updated sections.
142 | 
143 | 7. Write the updated spec back to `FEATURE_SPEC`.
144 | 
145 | 8. Report completion (after questioning loop ends or early termination):
146 |    - Number of questions asked & answered.
147 |    - Path to updated spec.
148 |    - Sections touched (list names).
149 |    - Coverage summary table listing each taxonomy category with Status: Resolved (was Partial/Missing and addressed), Deferred (exceeds question quota or better suited for planning), Clear (already sufficient), Outstanding (still Partial/Missing but low impact).
150 |    - If any Outstanding or Deferred remain, recommend whether to proceed to `/speckit.plan` or run `/speckit.clarify` again later post-plan.
151 |    - Suggested next command.
152 | 
153 | Behavior rules:
154 | - If no meaningful ambiguities found (or all potential questions would be low-impact), respond: "No critical ambiguities detected worth formal clarification." and suggest proceeding.
155 | - If spec file missing, instruct user to run `/speckit.specify` first (do not create a new spec here).
156 | - Never exceed 5 total asked questions (clarification retries for a single question do not count as new questions).
157 | - Avoid speculative tech stack questions unless the absence blocks functional clarity.
158 | - Respect user early termination signals ("stop", "done", "proceed").
159 |  - If no questions asked due to full coverage, output a compact coverage summary (all categories Clear) then suggest advancing.
160 |  - If quota reached with unresolved high-impact categories remaining, explicitly flag them under Deferred with rationale.
161 | 
162 | Context for prioritization: $ARGUMENTS
163 | 
```

--------------------------------------------------------------------------------
/specs/001-i-want-to/contracts/mcp-tool-contract.json:
--------------------------------------------------------------------------------

```json
  1 | {
  2 |   "$schema": "https://json-schema.org/draft-07/schema#",
  3 |   "title": "Jira Get Development Information Tool Contract",
  4 |   "description": "MCP tool contract for retrieving development information (branches, pull requests, commits) linked to a Jira issue",
  5 |   "type": "object",
  6 |   "properties": {
  7 |     "tool": {
  8 |       "type": "object",
  9 |       "properties": {
 10 |         "name": {
 11 |           "type": "string",
 12 |           "const": "jira_get_development_information"
 13 |         },
 14 |         "description": {
 15 |           "type": "string",
 16 |           "const": "Retrieve branches, pull requests, and commits linked to a Jira issue via development tool integrations (GitHub, GitLab, Bitbucket). Returns human-readable formatted text showing all development work associated with the issue."
 17 |         },
 18 |         "inputSchema": {
 19 |           "type": "object",
 20 |           "properties": {
 21 |             "issue_key": {
 22 |               "type": "string",
 23 |               "description": "The Jira issue key (e.g., PROJ-123)",
 24 |               "pattern": "^[A-Z]+-[0-9]+$",
 25 |               "examples": ["PROJ-123", "KP-42", "TEAM-999"]
 26 |             },
 27 |             "include_branches": {
 28 |               "type": "boolean",
 29 |               "description": "Include branches in the response (default: true)",
 30 |               "default": true
 31 |             },
 32 |             "include_pull_requests": {
 33 |               "type": "boolean",
 34 |               "description": "Include pull requests in the response (default: true)",
 35 |               "default": true
 36 |             },
 37 |             "include_commits": {
 38 |               "type": "boolean",
 39 |               "description": "Include commits in the response (default: true)",
 40 |               "default": true
 41 |             }
 42 |           },
 43 |           "required": ["issue_key"],
 44 |           "additionalProperties": false
 45 |         }
 46 |       },
 47 |       "required": ["name", "description", "inputSchema"]
 48 |     },
 49 |     "handler": {
 50 |       "type": "object",
 51 |       "properties": {
 52 |         "type": {
 53 |           "type": "string",
 54 |           "const": "typed"
 55 |         },
 56 |         "signature": {
 57 |           "type": "string",
 58 |           "const": "func(ctx context.Context, request mcp.CallToolRequest, input GetDevelopmentInfoInput) (*mcp.CallToolResult, error)"
 59 |         }
 60 |       }
 61 |     },
 62 |     "responses": {
 63 |       "type": "object",
 64 |       "properties": {
 65 |         "success": {
 66 |           "type": "object",
 67 |           "properties": {
 68 |             "format": {
 69 |               "type": "string",
 70 |               "const": "text"
 71 |             },
 72 |             "contentType": {
 73 |               "type": "string",
 74 |               "const": "plain/text"
 75 |             },
 76 |             "structure": {
 77 |               "type": "string",
 78 |               "description": "Formatted text with sections for branches, pull requests, and commits, separated by === dividers"
 79 |             },
 80 |             "example": {
 81 |               "type": "string",
 82 |               "const": "Development Information for PROJ-123:\n\n=== Branches (2) ===\n\nBranch: feature/PROJ-123-login\nRepository: company/backend-api\nLast Commit: abc1234 - \"Add login endpoint\"\nURL: https://github.com/company/backend-api/tree/feature/PROJ-123-login\n\n=== Pull Requests (1) ===\n\nPR #42: Add login functionality\nStatus: OPEN\nAuthor: John Doe ([email protected])\nRepository: company/backend-api\nURL: https://github.com/company/backend-api/pull/42\nLast Updated: 2025-10-07 14:30:00"
 83 |             }
 84 |           }
 85 |         },
 86 |         "empty": {
 87 |           "type": "object",
 88 |           "properties": {
 89 |             "format": {
 90 |               "type": "string",
 91 |               "const": "text"
 92 |             },
 93 |             "message": {
 94 |               "type": "string",
 95 |               "const": "Development Information for {issue_key}:\n\nNo branches, pull requests, or commits found.\n\nThis may mean:\n- No development work has been linked to this issue\n- The Jira-GitHub/GitLab/Bitbucket integration is not configured\n- You lack permissions to view development information"
 96 |             }
 97 |           }
 98 |         },
 99 |         "error": {
100 |           "type": "object",
101 |           "properties": {
102 |             "format": {
103 |               "type": "string",
104 |               "const": "error"
105 |             },
106 |             "cases": {
107 |               "type": "array",
108 |               "items": [
109 |                 {
110 |                   "type": "object",
111 |                   "properties": {
112 |                     "case": {
113 |                       "const": "invalid_issue_key"
114 |                     },
115 |                     "httpStatus": {
116 |                       "const": 400
117 |                     },
118 |                     "message": {
119 |                       "const": "invalid issue key format: {issue_key} (expected format: PROJ-123)"
120 |                     }
121 |                   }
122 |                 },
123 |                 {
124 |                   "type": "object",
125 |                   "properties": {
126 |                     "case": {
127 |                       "const": "issue_not_found"
128 |                     },
129 |                     "httpStatus": {
130 |                       "const": 404
131 |                     },
132 |                     "message": {
133 |                       "const": "failed to retrieve development information: issue not found (endpoint: /rest/api/3/issue/{issue_key})"
134 |                     }
135 |                   }
136 |                 },
137 |                 {
138 |                   "type": "object",
139 |                   "properties": {
140 |                     "case": {
141 |                       "const": "unauthorized"
142 |                     },
143 |                     "httpStatus": {
144 |                       "const": 401
145 |                     },
146 |                     "message": {
147 |                       "const": "failed to retrieve development information: authentication failed (endpoint: /rest/dev-status/1.0/issue/detail)"
148 |                     }
149 |                   }
150 |                 },
151 |                 {
152 |                   "type": "object",
153 |                   "properties": {
154 |                     "case": {
155 |                       "const": "api_error"
156 |                     },
157 |                     "httpStatus": {
158 |                       "const": 500
159 |                     },
160 |                     "message": {
161 |                       "const": "failed to retrieve development information: {error_message} (endpoint: {endpoint})"
162 |                     }
163 |                   }
164 |                 }
165 |               ]
166 |             }
167 |           }
168 |         }
169 |       }
170 |     }
171 |   },
172 |   "examples": [
173 |     {
174 |       "description": "Get all development information for an issue",
175 |       "request": {
176 |         "issue_key": "PROJ-123"
177 |       },
178 |       "response": {
179 |         "type": "success",
180 |         "output": "Development Information for PROJ-123:\n\n=== Branches (2) ===\n..."
181 |       }
182 |     },
183 |     {
184 |       "description": "Get only branches and pull requests",
185 |       "request": {
186 |         "issue_key": "PROJ-123",
187 |         "include_commits": false
188 |       },
189 |       "response": {
190 |         "type": "success",
191 |         "output": "Development Information for PROJ-123:\n\n=== Branches (2) ===\n...\n=== Pull Requests (1) ===\n..."
192 |       }
193 |     },
194 |     {
195 |       "description": "Get only branches",
196 |       "request": {
197 |         "issue_key": "PROJ-123",
198 |         "include_branches": true,
199 |         "include_pull_requests": false,
200 |         "include_commits": false
201 |       },
202 |       "response": {
203 |         "type": "success",
204 |         "output": "Development Information for PROJ-123:\n\n=== Branches (2) ===\n..."
205 |       }
206 |     },
207 |     {
208 |       "description": "Issue with no development information",
209 |       "request": {
210 |         "issue_key": "PROJ-456"
211 |       },
212 |       "response": {
213 |         "type": "empty",
214 |         "output": "Development Information for PROJ-456:\n\nNo branches, pull requests, or commits found..."
215 |       }
216 |     },
217 |     {
218 |       "description": "Invalid issue key format",
219 |       "request": {
220 |         "issue_key": "invalid-key"
221 |       },
222 |       "response": {
223 |         "type": "error",
224 |         "error": "invalid issue key format: invalid-key (expected format: PROJ-123)"
225 |       }
226 |     }
227 |   ],
228 |   "registration": {
229 |     "location": "main.go",
230 |     "function": "tools.RegisterJiraDevelopmentTool(mcpServer)",
231 |     "pattern": "All Jira tools are registered in main.go via RegisterJira*Tool functions"
232 |   },
233 |   "implementation": {
234 |     "file": "tools/jira_development.go",
235 |     "inputStruct": "GetDevelopmentInfoInput",
236 |     "handler": "jiraGetDevelopmentInfoHandler",
237 |     "client": "services.JiraClient() (singleton)",
238 |     "formatting": "Inline string builder (no util function)"
239 |   },
240 |   "dependencies": {
241 |     "external": [
242 |       {
243 |         "name": "go-atlassian",
244 |         "version": "v1.6.1",
245 |         "usage": "client.NewRequest() and client.Call() for custom API endpoint"
246 |       },
247 |       {
248 |         "name": "mcp-go",
249 |         "version": "v0.32.0",
250 |         "usage": "MCP tool registration and typed handler"
251 |       }
252 |     ],
253 |     "internal": [
254 |       {
255 |         "package": "services",
256 |         "function": "JiraClient()",
257 |         "usage": "Singleton Jira client instance"
258 |       }
259 |     ]
260 |   },
261 |   "apiEndpoints": {
262 |     "primary": {
263 |       "path": "/rest/dev-status/1.0/issue/detail",
264 |       "method": "GET",
265 |       "parameters": {
266 |         "issueId": "Numeric issue ID (not issue key)",
267 |         "applicationType": "Optional: github, bitbucket, or stash",
268 |         "dataType": "Optional: repository, pullrequest, or branch"
269 |       },
270 |       "documentation": "Undocumented internal API - no official Atlassian documentation"
271 |     },
272 |     "secondary": {
273 |       "path": "/rest/api/3/issue/{issueKeyOrId}",
274 |       "method": "GET",
275 |       "usage": "Convert issue key to numeric ID before calling dev-status endpoint"
276 |     }
277 |   },
278 |   "testing": {
279 |     "unit": {
280 |       "location": "tools/jira_development_test.go",
281 |       "coverage": [
282 |         "Tool registration succeeds",
283 |         "Input validation enforces required fields",
284 |         "Handler signature matches typed pattern"
285 |       ]
286 |     },
287 |     "integration": {
288 |       "location": "tests/development_test.go",
289 |       "prerequisites": [
290 |         "Live Jira instance with dev integration configured",
291 |         "Test issue with linked branches/PRs/commits"
292 |       ],
293 |       "coverage": [
294 |         "Retrieve development information for valid issue",
295 |         "Handle issue with no development information",
296 |         "Handle invalid issue key",
297 |         "Filter by include flags"
298 |       ]
299 |     }
300 |   }
301 | }
302 | 
```

--------------------------------------------------------------------------------
/.claude/commands/speckit.specify.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | description: Create or update the feature specification from a natural language feature description.
  3 | ---
  4 | 
  5 | ## User Input
  6 | 
  7 | ```text
  8 | $ARGUMENTS
  9 | ```
 10 | 
 11 | You **MUST** consider the user input before proceeding (if not empty).
 12 | 
 13 | ## Outline
 14 | 
 15 | The text the user typed after `/speckit.specify` in the triggering message **is** the feature description. Assume you always have it available in this conversation even if `$ARGUMENTS` appears literally below. Do not ask the user to repeat it unless they provided an empty command.
 16 | 
 17 | Given that feature description, do this:
 18 | 
 19 | 1. Run the script `.specify/scripts/bash/create-new-feature.sh --json "$ARGUMENTS"` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute.
 20 |   **IMPORTANT** You must only ever run this script once. The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for.
 21 | 2. Load `.specify/templates/spec-template.md` to understand required sections.
 22 | 
 23 | 3. Follow this execution flow:
 24 | 
 25 |     1. Parse user description from Input
 26 |        If empty: ERROR "No feature description provided"
 27 |     2. Extract key concepts from description
 28 |        Identify: actors, actions, data, constraints
 29 |     3. For unclear aspects:
 30 |        - Make informed guesses based on context and industry standards
 31 |        - Only mark with [NEEDS CLARIFICATION: specific question] if:
 32 |          - The choice significantly impacts feature scope or user experience
 33 |          - Multiple reasonable interpretations exist with different implications
 34 |          - No reasonable default exists
 35 |        - **LIMIT: Maximum 3 [NEEDS CLARIFICATION] markers total**
 36 |        - Prioritize clarifications by impact: scope > security/privacy > user experience > technical details
 37 |     4. Fill User Scenarios & Testing section
 38 |        If no clear user flow: ERROR "Cannot determine user scenarios"
 39 |     5. Generate Functional Requirements
 40 |        Each requirement must be testable
 41 |        Use reasonable defaults for unspecified details (document assumptions in Assumptions section)
 42 |     6. Define Success Criteria
 43 |        Create measurable, technology-agnostic outcomes
 44 |        Include both quantitative metrics (time, performance, volume) and qualitative measures (user satisfaction, task completion)
 45 |        Each criterion must be verifiable without implementation details
 46 |     7. Identify Key Entities (if data involved)
 47 |     8. Return: SUCCESS (spec ready for planning)
 48 | 
 49 | 4. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings.
 50 | 
 51 | 5. **Specification Quality Validation**: After writing the initial spec, validate it against quality criteria:
 52 | 
 53 |    a. **Create Spec Quality Checklist**: Generate a checklist file at `FEATURE_DIR/checklists/requirements.md` using the checklist template structure with these validation items:
 54 |    
 55 |       ```markdown
 56 |       # Specification Quality Checklist: [FEATURE NAME]
 57 |       
 58 |       **Purpose**: Validate specification completeness and quality before proceeding to planning
 59 |       **Created**: [DATE]
 60 |       **Feature**: [Link to spec.md]
 61 |       
 62 |       ## Content Quality
 63 |       
 64 |       - [ ] No implementation details (languages, frameworks, APIs)
 65 |       - [ ] Focused on user value and business needs
 66 |       - [ ] Written for non-technical stakeholders
 67 |       - [ ] All mandatory sections completed
 68 |       
 69 |       ## Requirement Completeness
 70 |       
 71 |       - [ ] No [NEEDS CLARIFICATION] markers remain
 72 |       - [ ] Requirements are testable and unambiguous
 73 |       - [ ] Success criteria are measurable
 74 |       - [ ] Success criteria are technology-agnostic (no implementation details)
 75 |       - [ ] All acceptance scenarios are defined
 76 |       - [ ] Edge cases are identified
 77 |       - [ ] Scope is clearly bounded
 78 |       - [ ] Dependencies and assumptions identified
 79 |       
 80 |       ## Feature Readiness
 81 |       
 82 |       - [ ] All functional requirements have clear acceptance criteria
 83 |       - [ ] User scenarios cover primary flows
 84 |       - [ ] Feature meets measurable outcomes defined in Success Criteria
 85 |       - [ ] No implementation details leak into specification
 86 |       
 87 |       ## Notes
 88 |       
 89 |       - Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`
 90 |       ```
 91 |    
 92 |    b. **Run Validation Check**: Review the spec against each checklist item:
 93 |       - For each item, determine if it passes or fails
 94 |       - Document specific issues found (quote relevant spec sections)
 95 |    
 96 |    c. **Handle Validation Results**:
 97 |       
 98 |       - **If all items pass**: Mark checklist complete and proceed to step 6
 99 |       
100 |       - **If items fail (excluding [NEEDS CLARIFICATION])**:
101 |         1. List the failing items and specific issues
102 |         2. Update the spec to address each issue
103 |         3. Re-run validation until all items pass (max 3 iterations)
104 |         4. If still failing after 3 iterations, document remaining issues in checklist notes and warn user
105 |       
106 |       - **If [NEEDS CLARIFICATION] markers remain**:
107 |         1. Extract all [NEEDS CLARIFICATION: ...] markers from the spec
108 |         2. **LIMIT CHECK**: If more than 3 markers exist, keep only the 3 most critical (by scope/security/UX impact) and make informed guesses for the rest
109 |         3. For each clarification needed (max 3), present options to user in this format:
110 |         
111 |            ```markdown
112 |            ## Question [N]: [Topic]
113 |            
114 |            **Context**: [Quote relevant spec section]
115 |            
116 |            **What we need to know**: [Specific question from NEEDS CLARIFICATION marker]
117 |            
118 |            **Suggested Answers**:
119 |            
120 |            | Option | Answer | Implications |
121 |            |--------|--------|--------------|
122 |            | A      | [First suggested answer] | [What this means for the feature] |
123 |            | B      | [Second suggested answer] | [What this means for the feature] |
124 |            | C      | [Third suggested answer] | [What this means for the feature] |
125 |            | Custom | Provide your own answer | [Explain how to provide custom input] |
126 |            
127 |            **Your choice**: _[Wait for user response]_
128 |            ```
129 |         
130 |         4. **CRITICAL - Table Formatting**: Ensure markdown tables are properly formatted:
131 |            - Use consistent spacing with pipes aligned
132 |            - Each cell should have spaces around content: `| Content |` not `|Content|`
133 |            - Header separator must have at least 3 dashes: `|--------|`
134 |            - Test that the table renders correctly in markdown preview
135 |         5. Number questions sequentially (Q1, Q2, Q3 - max 3 total)
136 |         6. Present all questions together before waiting for responses
137 |         7. Wait for user to respond with their choices for all questions (e.g., "Q1: A, Q2: Custom - [details], Q3: B")
138 |         8. Update the spec by replacing each [NEEDS CLARIFICATION] marker with the user's selected or provided answer
139 |         9. Re-run validation after all clarifications are resolved
140 |    
141 |    d. **Update Checklist**: After each validation iteration, update the checklist file with current pass/fail status
142 | 
143 | 6. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`).
144 | 
145 | **NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
146 | 
147 | ## General Guidelines
148 | 
149 | ## Quick Guidelines
150 | 
151 | - Focus on **WHAT** users need and **WHY**.
152 | - Avoid HOW to implement (no tech stack, APIs, code structure).
153 | - Written for business stakeholders, not developers.
154 | - DO NOT create any checklists that are embedded in the spec. That will be a separate command.
155 | 
156 | ### Section Requirements
157 | 
158 | - **Mandatory sections**: Must be completed for every feature
159 | - **Optional sections**: Include only when relevant to the feature
160 | - When a section doesn't apply, remove it entirely (don't leave as "N/A")
161 | 
162 | ### For AI Generation
163 | 
164 | When creating this spec from a user prompt:
165 | 
166 | 1. **Make informed guesses**: Use context, industry standards, and common patterns to fill gaps
167 | 2. **Document assumptions**: Record reasonable defaults in the Assumptions section
168 | 3. **Limit clarifications**: Maximum 3 [NEEDS CLARIFICATION] markers - use only for critical decisions that:
169 |    - Significantly impact feature scope or user experience
170 |    - Have multiple reasonable interpretations with different implications
171 |    - Lack any reasonable default
172 | 4. **Prioritize clarifications**: scope > security/privacy > user experience > technical details
173 | 5. **Think like a tester**: Every vague requirement should fail the "testable and unambiguous" checklist item
174 | 6. **Common areas needing clarification** (only if no reasonable default exists):
175 |    - Feature scope and boundaries (include/exclude specific use cases)
176 |    - User types and permissions (if multiple conflicting interpretations possible)
177 |    - Security/compliance requirements (when legally/financially significant)
178 |    
179 | **Examples of reasonable defaults** (don't ask about these):
180 | 
181 | - Data retention: Industry-standard practices for the domain
182 | - Performance targets: Standard web/mobile app expectations unless specified
183 | - Error handling: User-friendly messages with appropriate fallbacks
184 | - Authentication method: Standard session-based or OAuth2 for web apps
185 | - Integration patterns: RESTful APIs unless specified otherwise
186 | 
187 | ### Success Criteria Guidelines
188 | 
189 | Success criteria must be:
190 | 
191 | 1. **Measurable**: Include specific metrics (time, percentage, count, rate)
192 | 2. **Technology-agnostic**: No mention of frameworks, languages, databases, or tools
193 | 3. **User-focused**: Describe outcomes from user/business perspective, not system internals
194 | 4. **Verifiable**: Can be tested/validated without knowing implementation details
195 | 
196 | **Good examples**:
197 | 
198 | - "Users can complete checkout in under 3 minutes"
199 | - "System supports 10,000 concurrent users"
200 | - "95% of searches return results in under 1 second"
201 | - "Task completion rate improves by 40%"
202 | 
203 | **Bad examples** (implementation-focused):
204 | 
205 | - "API response time is under 200ms" (too technical, use "Users see results instantly")
206 | - "Database can handle 1000 TPS" (implementation detail, use user-facing metric)
207 | - "React components render efficiently" (framework-specific)
208 | - "Redis cache hit rate above 80%" (technology-specific)
209 | 
```

--------------------------------------------------------------------------------
/tools/jira_issue.go:
--------------------------------------------------------------------------------

```go
  1 | package tools
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 	"strings"
  7 | 
  8 | 	"github.com/ctreminiom/go-atlassian/pkg/infra/models"
  9 | 	"github.com/mark3labs/mcp-go/mcp"
 10 | 	"github.com/mark3labs/mcp-go/server"
 11 | 	"github.com/nguyenvanduocit/jira-mcp/services"
 12 | 	"github.com/nguyenvanduocit/jira-mcp/util"
 13 | )
 14 | 
 15 | // Input types for typed tools
 16 | type GetIssueInput struct {
 17 | 	IssueKey string `json:"issue_key" validate:"required"`
 18 | 	Fields   string `json:"fields,omitempty"`
 19 | 	Expand   string `json:"expand,omitempty"`
 20 | }
 21 | 
 22 | type CreateIssueInput struct {
 23 | 	ProjectKey  string `json:"project_key" validate:"required"`
 24 | 	Summary     string `json:"summary" validate:"required"`
 25 | 	Description string `json:"description" validate:"required"`
 26 | 	IssueType   string `json:"issue_type" validate:"required"`
 27 | }
 28 | 
 29 | type CreateChildIssueInput struct {
 30 | 	ParentIssueKey string `json:"parent_issue_key" validate:"required"`
 31 | 	Summary        string `json:"summary" validate:"required"`
 32 | 	Description    string `json:"description" validate:"required"`
 33 | 	IssueType      string `json:"issue_type,omitempty"`
 34 | }
 35 | 
 36 | type UpdateIssueInput struct {
 37 | 	IssueKey    string `json:"issue_key" validate:"required"`
 38 | 	Summary     string `json:"summary,omitempty"`
 39 | 	Description string `json:"description,omitempty"`
 40 | }
 41 | 
 42 | type ListIssueTypesInput struct {
 43 | 	ProjectKey string `json:"project_key" validate:"required"`
 44 | }
 45 | 
 46 | func RegisterJiraIssueTool(s *server.MCPServer) {
 47 | 	jiraGetIssueTool := mcp.NewTool("jira_get_issue",
 48 | 		mcp.WithDescription("Retrieve detailed information about a specific Jira issue including its status, assignee, description, subtasks, and available transitions"),
 49 | 		mcp.WithString("issue_key", mcp.Required(), mcp.Description("The unique identifier of the Jira issue (e.g., KP-2, PROJ-123)")),
 50 | 		mcp.WithString("fields", mcp.Description("Comma-separated list of fields to retrieve (e.g., 'summary,status,assignee'). If not specified, all fields are returned.")),
 51 | 		mcp.WithString("expand", mcp.Description("Comma-separated list of fields to expand for additional details (e.g., 'transitions,changelog,subtasks'). Default: 'transitions,changelog'")),
 52 | 	)
 53 | 	s.AddTool(jiraGetIssueTool, mcp.NewTypedToolHandler(jiraGetIssueHandler))
 54 | 
 55 | 	jiraCreateIssueTool := mcp.NewTool("jira_create_issue",
 56 | 		mcp.WithDescription("Create a new Jira issue with specified details. Returns the created issue's key, ID, and URL"),
 57 | 		mcp.WithString("project_key", mcp.Required(), mcp.Description("Project identifier where the issue will be created (e.g., KP, PROJ)")),
 58 | 		mcp.WithString("summary", mcp.Required(), mcp.Description("Brief title or headline of the issue")),
 59 | 		mcp.WithString("description", mcp.Required(), mcp.Description("Detailed explanation of the issue")),
 60 | 		mcp.WithString("issue_type", mcp.Required(), mcp.Description("Type of issue to create (common types: Bug, Task, Subtask, Story, Epic)")),
 61 | 	)
 62 | 	s.AddTool(jiraCreateIssueTool, mcp.NewTypedToolHandler(jiraCreateIssueHandler))
 63 | 
 64 | 	jiraCreateChildIssueTool := mcp.NewTool("jira_create_child_issue",
 65 | 		mcp.WithDescription("Create a child issue (sub-task) linked to a parent issue in Jira. Returns the created issue's key, ID, and URL"),
 66 | 		mcp.WithString("parent_issue_key", mcp.Required(), mcp.Description("The parent issue key to which this child issue will be linked (e.g., KP-2)")),
 67 | 		mcp.WithString("summary", mcp.Required(), mcp.Description("Brief title or headline of the child issue")),
 68 | 		mcp.WithString("description", mcp.Required(), mcp.Description("Detailed explanation of the child issue")),
 69 | 		mcp.WithString("issue_type", mcp.Description("Type of child issue to create (defaults to 'Subtask' if not specified)")),
 70 | 	)
 71 | 	s.AddTool(jiraCreateChildIssueTool, mcp.NewTypedToolHandler(jiraCreateChildIssueHandler))
 72 | 
 73 | 	jiraUpdateIssueTool := mcp.NewTool("jira_update_issue",
 74 | 		mcp.WithDescription("Modify an existing Jira issue's details. Supports partial updates - only specified fields will be changed"),
 75 | 		mcp.WithString("issue_key", mcp.Required(), mcp.Description("The unique identifier of the issue to update (e.g., KP-2)")),
 76 | 		mcp.WithString("summary", mcp.Description("New title for the issue (optional)")),
 77 | 		mcp.WithString("description", mcp.Description("New description for the issue (optional)")),
 78 | 	)
 79 | 	s.AddTool(jiraUpdateIssueTool, mcp.NewTypedToolHandler(jiraUpdateIssueHandler))
 80 | 
 81 | 	jiraListIssueTypesTool := mcp.NewTool("jira_list_issue_types",
 82 | 		mcp.WithDescription("List all available issue types in a Jira project with their IDs, names, descriptions, and other attributes"),
 83 | 		mcp.WithString("project_key", mcp.Required(), mcp.Description("Project identifier to list issue types for (e.g., KP, PROJ)")),
 84 | 	)
 85 | 	s.AddTool(jiraListIssueTypesTool, mcp.NewTypedToolHandler(jiraListIssueTypesHandler))
 86 | }
 87 | 
 88 | func jiraGetIssueHandler(ctx context.Context, request mcp.CallToolRequest, input GetIssueInput) (*mcp.CallToolResult, error) {
 89 | 	client := services.JiraClient()
 90 | 
 91 | 	// Parse fields parameter
 92 | 	var fields []string
 93 | 	if input.Fields != "" {
 94 | 		fields = strings.Split(strings.ReplaceAll(input.Fields, " ", ""), ",")
 95 | 	}
 96 | 
 97 | 	// Parse expand parameter with default values
 98 | 	expand := []string{"transitions", "changelog", "subtasks", "description"}
 99 | 	if input.Expand != "" {
100 | 		expand = strings.Split(strings.ReplaceAll(input.Expand, " ", ""), ",")
101 | 	}
102 | 	
103 | 	issue, response, err := client.Issue.Get(ctx, input.IssueKey, fields, expand)
104 | 	if err != nil {
105 | 		if response != nil {
106 | 			return nil, fmt.Errorf("failed to get issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
107 | 		}
108 | 		return nil, fmt.Errorf("failed to get issue: %v", err)
109 | 	}
110 | 
111 | 	// Use the new util function to format the issue
112 | 	formattedIssue := util.FormatJiraIssue(issue)
113 | 
114 | 	return mcp.NewToolResultText(formattedIssue), nil
115 | }
116 | 
117 | func jiraCreateIssueHandler(ctx context.Context, request mcp.CallToolRequest, input CreateIssueInput) (*mcp.CallToolResult, error) {
118 | 	client := services.JiraClient()
119 | 
120 | 	var payload = models.IssueScheme{
121 | 		Fields: &models.IssueFieldsScheme{
122 | 			Summary:     input.Summary,
123 | 			Project:     &models.ProjectScheme{Key: input.ProjectKey},
124 | 			Description: &models.CommentNodeScheme{
125 | 				Content: []*models.CommentNodeScheme{
126 | 					{
127 | 						Type: "text",
128 | 						Text: input.Description,
129 | 					},
130 | 				},
131 | 			},
132 | 			IssueType:   &models.IssueTypeScheme{Name: input.IssueType},
133 | 		},
134 | 	}
135 | 
136 | 	issue, response, err := client.Issue.Create(ctx, &payload, nil)
137 | 	if err != nil {
138 | 		if response != nil {
139 | 			return nil, fmt.Errorf("failed to create issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
140 | 		}
141 | 		return nil, fmt.Errorf("failed to create issue: %v", err)
142 | 	}
143 | 
144 | 	result := fmt.Sprintf("Issue created successfully!\nKey: %s\nID: %s\nURL: %s", issue.Key, issue.ID, issue.Self)
145 | 	return mcp.NewToolResultText(result), nil
146 | }
147 | 
148 | func jiraCreateChildIssueHandler(ctx context.Context, request mcp.CallToolRequest, input CreateChildIssueInput) (*mcp.CallToolResult, error) {
149 | 	client := services.JiraClient()
150 | 
151 | 	// Get the parent issue to retrieve its project
152 | 	parentIssue, response, err := client.Issue.Get(ctx, input.ParentIssueKey, nil, nil)
153 | 	if err != nil {
154 | 		if response != nil {
155 | 			return nil, fmt.Errorf("failed to get parent issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
156 | 		}
157 | 		return nil, fmt.Errorf("failed to get parent issue: %v", err)
158 | 	}
159 | 
160 | 	// Default issue type is Sub-task if not specified
161 | 	issueType := "Subtask"
162 | 	if input.IssueType != "" {
163 | 		issueType = input.IssueType
164 | 	}
165 | 
166 | 	var payload = models.IssueScheme{
167 | 		Fields: &models.IssueFieldsScheme{
168 | 			Summary:     input.Summary,
169 | 			Project:     &models.ProjectScheme{Key: parentIssue.Fields.Project.Key},
170 | 			Description: &models.CommentNodeScheme{
171 | 				Type: "text",
172 | 				Text: input.Description,
173 | 			},
174 | 			IssueType:   &models.IssueTypeScheme{Name: issueType},
175 | 			Parent:      &models.ParentScheme{Key: input.ParentIssueKey},
176 | 		},
177 | 	}
178 | 
179 | 	issue, response, err := client.Issue.Create(ctx, &payload, nil)
180 | 	if err != nil {
181 | 		if response != nil {
182 | 			return nil, fmt.Errorf("failed to create child issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
183 | 		}
184 | 		return nil, fmt.Errorf("failed to create child issue: %v", err)
185 | 	}
186 | 
187 | 	result := fmt.Sprintf("Child issue created successfully!\nKey: %s\nID: %s\nURL: %s\nParent: %s", 
188 | 		issue.Key, issue.ID, issue.Self, input.ParentIssueKey)
189 | 
190 | 	if issueType == "Bug" {
191 | 		result += "\n\nA bug should be linked to a Story or Task. Next step should be to create relationship between the bug and the story or task."
192 | 	}
193 | 	return mcp.NewToolResultText(result), nil
194 | }
195 | 
196 | func jiraUpdateIssueHandler(ctx context.Context, request mcp.CallToolRequest, input UpdateIssueInput) (*mcp.CallToolResult, error) {
197 | 	client := services.JiraClient()
198 | 
199 | 	payload := &models.IssueScheme{
200 | 		Fields: &models.IssueFieldsScheme{},
201 | 	}
202 | 
203 | 	if input.Summary != "" {
204 | 		payload.Fields.Summary = input.Summary
205 | 	}
206 | 
207 | 	if input.Description != "" {
208 | 		payload.Fields.Description = &models.CommentNodeScheme{
209 | 			Type: "text",
210 | 			Text: input.Description,
211 | 		}
212 | 	}
213 | 
214 | 	response, err := client.Issue.Update(ctx, input.IssueKey, true, payload, nil, nil)
215 | 	if err != nil {
216 | 		if response != nil {
217 | 			return nil, fmt.Errorf("failed to update issue: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
218 | 		}
219 | 		return nil, fmt.Errorf("failed to update issue: %v", err)
220 | 	}
221 | 
222 | 	return mcp.NewToolResultText("Issue updated successfully!"), nil
223 | }
224 | 
225 | func jiraListIssueTypesHandler(ctx context.Context, request mcp.CallToolRequest, input ListIssueTypesInput) (*mcp.CallToolResult, error) {
226 | 	client := services.JiraClient()
227 | 
228 | 	issueTypes, response, err := client.Issue.Type.Gets(ctx)
229 | 	if err != nil {
230 | 		if response != nil {
231 | 			return nil, fmt.Errorf("failed to get issue types: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
232 | 		}
233 | 		return nil, fmt.Errorf("failed to get issue types: %v", err)
234 | 	}
235 | 
236 | 	if len(issueTypes) == 0 {
237 | 		return mcp.NewToolResultText("No issue types found for this project."), nil
238 | 	}
239 | 
240 | 	var result strings.Builder
241 | 	result.WriteString("Available Issue Types:\n\n")
242 | 
243 | 	for _, issueType := range issueTypes {
244 | 		subtaskType := ""
245 | 		if issueType.Subtask {
246 | 			subtaskType = " (Subtask Type)"
247 | 		}
248 | 		
249 | 		result.WriteString(fmt.Sprintf("ID: %s\nName: %s%s\n", issueType.ID, issueType.Name, subtaskType))
250 | 		if issueType.Description != "" {
251 | 			result.WriteString(fmt.Sprintf("Description: %s\n", issueType.Description))
252 | 		}
253 | 		if issueType.IconURL != "" {
254 | 			result.WriteString(fmt.Sprintf("Icon URL: %s\n", issueType.IconURL))
255 | 		}
256 | 		if issueType.Scope != nil {
257 | 			result.WriteString(fmt.Sprintf("Scope: %s\n", issueType.Scope.Type))
258 | 		}
259 | 		result.WriteString("\n")
260 | 	}
261 | 
262 | 	return mcp.NewToolResultText(result.String()), nil
263 | }
264 | 
```

--------------------------------------------------------------------------------
/specs/001-i-want-to/quickstart.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Quickstart: Get Development Information from Jira Issue
  2 | 
  3 | **Feature**: Retrieve branches, pull requests, and commits linked to a Jira issue
  4 | **Tool**: `jira_get_development_information`
  5 | **Status**: Implementation pending (planned for Phase 2)
  6 | 
  7 | ## Overview
  8 | 
  9 | The `jira_get_development_information` tool retrieves all development work linked to a Jira issue through VCS integrations (GitHub, GitLab, Bitbucket). This includes:
 10 | 
 11 | - **Branches**: Git branches that reference the issue key in their name
 12 | - **Pull Requests**: PRs/MRs that reference the issue key
 13 | - **Commits**: Commits that mention the issue key in their message
 14 | 
 15 | ## Prerequisites
 16 | 
 17 | 1. **Jira Instance**: You have access to a Jira Cloud or Jira Data Center instance
 18 | 2. **Authentication**: You have configured `ATLASSIAN_HOST`, `ATLASSIAN_EMAIL`, and `ATLASSIAN_TOKEN` environment variables
 19 | 3. **VCS Integration**: Your Jira instance has development tool integrations enabled (GitHub for Jira, GitLab for Jira, or Bitbucket integration)
 20 | 4. **Permissions**: You have permission to view the issue and its development information
 21 | 
 22 | ## Basic Usage
 23 | 
 24 | ### Get All Development Information
 25 | 
 26 | Retrieve all branches, pull requests, and commits for an issue:
 27 | 
 28 | ```bash
 29 | # Using Claude or another MCP client
 30 | jira_get_development_information {
 31 |   "issue_key": "PROJ-123"
 32 | }
 33 | ```
 34 | 
 35 | **Example Output**:
 36 | ```
 37 | Development Information for PROJ-123:
 38 | 
 39 | === Branches (2) ===
 40 | 
 41 | Branch: feature/PROJ-123-login
 42 | Repository: company/backend-api
 43 | Last Commit: abc1234 - "Add login endpoint"
 44 | URL: https://github.com/company/backend-api/tree/feature/PROJ-123-login
 45 | 
 46 | Branch: feature/PROJ-123-ui
 47 | Repository: company/frontend
 48 | Last Commit: def5678 - "Add login form"
 49 | URL: https://github.com/company/frontend/tree/feature/PROJ-123-ui
 50 | 
 51 | === Pull Requests (1) ===
 52 | 
 53 | PR #42: Add login functionality
 54 | Status: OPEN
 55 | Author: John Doe ([email protected])
 56 | Repository: company/backend-api
 57 | URL: https://github.com/company/backend-api/pull/42
 58 | Last Updated: 2025-10-07 14:30:00
 59 | 
 60 | === Commits (3) ===
 61 | 
 62 | Repository: company/backend-api
 63 | 
 64 |   Commit: abc1234 (Oct 7, 14:00)
 65 |   Author: John Doe
 66 |   Message: Add login endpoint [PROJ-123]
 67 |   URL: https://github.com/company/backend-api/commit/abc1234
 68 | 
 69 |   Commit: xyz9876 (Oct 7, 13:00)
 70 |   Author: Jane Smith
 71 |   Message: Update authentication model
 72 |   URL: https://github.com/company/backend-api/commit/xyz9876
 73 | ```
 74 | 
 75 | ### Get Only Branches
 76 | 
 77 | Filter to show only branches:
 78 | 
 79 | ```bash
 80 | jira_get_development_information {
 81 |   "issue_key": "PROJ-123",
 82 |   "include_branches": true,
 83 |   "include_pull_requests": false,
 84 |   "include_commits": false
 85 | }
 86 | ```
 87 | 
 88 | **Example Output**:
 89 | ```
 90 | Development Information for PROJ-123:
 91 | 
 92 | === Branches (2) ===
 93 | 
 94 | Branch: feature/PROJ-123-login
 95 | Repository: company/backend-api
 96 | Last Commit: abc1234 - "Add login endpoint"
 97 | URL: https://github.com/company/backend-api/tree/feature/PROJ-123-login
 98 | 
 99 | Branch: feature/PROJ-123-ui
100 | Repository: company/frontend
101 | Last Commit: def5678 - "Add login form"
102 | URL: https://github.com/company/frontend/tree/feature/PROJ-123-ui
103 | ```
104 | 
105 | ### Get Only Pull Requests
106 | 
107 | Filter to show only pull requests:
108 | 
109 | ```bash
110 | jira_get_development_information {
111 |   "issue_key": "PROJ-123",
112 |   "include_branches": false,
113 |   "include_pull_requests": true,
114 |   "include_commits": false
115 | }
116 | ```
117 | 
118 | ### Get Branches and Pull Requests (No Commits)
119 | 
120 | Exclude commits for a cleaner view:
121 | 
122 | ```bash
123 | jira_get_development_information {
124 |   "issue_key": "PROJ-123",
125 |   "include_commits": false
126 | }
127 | ```
128 | 
129 | ## Common Scenarios
130 | 
131 | ### Scenario 1: Check Development Status
132 | 
133 | **Use case**: You want to see if any code has been written for a user story.
134 | 
135 | ```bash
136 | jira_get_development_information {
137 |   "issue_key": "TEAM-456"
138 | }
139 | ```
140 | 
141 | **What to look for**:
142 | - Number of branches (indicates work in progress)
143 | - PR status (OPEN = in review, MERGED = completed)
144 | - Commit count (indicates activity level)
145 | 
146 | ---
147 | 
148 | ### Scenario 2: Review Pull Request Status
149 | 
150 | **Use case**: You want to check if PRs are ready for merge.
151 | 
152 | ```bash
153 | jira_get_development_information {
154 |   "issue_key": "TEAM-456",
155 |   "include_pull_requests": true,
156 |   "include_branches": false,
157 |   "include_commits": false
158 | }
159 | ```
160 | 
161 | **What to look for**:
162 | - Status: OPEN (needs review), MERGED (done), DECLINED (rejected)
163 | - Author: Who submitted the PR
164 | - Last Updated: How recent the PR is
165 | 
166 | ---
167 | 
168 | ### Scenario 3: Find Linked Branches
169 | 
170 | **Use case**: You need to know which branches are working on this issue.
171 | 
172 | ```bash
173 | jira_get_development_information {
174 |   "issue_key": "TEAM-456",
175 |   "include_branches": true,
176 |   "include_pull_requests": false,
177 |   "include_commits": false
178 | }
179 | ```
180 | 
181 | **What to look for**:
182 | - Branch names (indicates work streams)
183 | - Repository (which codebase is affected)
184 | - Last commit (recent activity)
185 | 
186 | ---
187 | 
188 | ### Scenario 4: Review Commit History
189 | 
190 | **Use case**: You want to understand what code changes were made.
191 | 
192 | ```bash
193 | jira_get_development_information {
194 |   "issue_key": "TEAM-456",
195 |   "include_commits": true,
196 |   "include_branches": false,
197 |   "include_pull_requests": false
198 | }
199 | ```
200 | 
201 | **What to look for**:
202 | - Commit messages (what was changed)
203 | - Authors (who worked on it)
204 | - Timestamps (when work happened)
205 | 
206 | ## Error Handling
207 | 
208 | ### Issue Not Found
209 | 
210 | **Error**:
211 | ```
212 | failed to retrieve development information: issue not found (endpoint: /rest/api/3/issue/PROJ-999)
213 | ```
214 | 
215 | **Solution**: Verify the issue key is correct and you have permission to view it.
216 | 
217 | ---
218 | 
219 | ### No Development Information
220 | 
221 | **Output**:
222 | ```
223 | Development Information for PROJ-123:
224 | 
225 | No branches, pull requests, or commits found.
226 | 
227 | This may mean:
228 | - No development work has been linked to this issue
229 | - The Jira-GitHub/GitLab/Bitbucket integration is not configured
230 | - You lack permissions to view development information
231 | ```
232 | 
233 | **Solutions**:
234 | - Check if VCS integration is enabled in Jira
235 | - Verify branches/PRs/commits reference the issue key (e.g., "PROJ-123" in branch name or commit message)
236 | - Ask your Jira admin to check integration configuration
237 | 
238 | ---
239 | 
240 | ### Invalid Issue Key Format
241 | 
242 | **Error**:
243 | ```
244 | invalid issue key format: invalid-key (expected format: PROJ-123)
245 | ```
246 | 
247 | **Solution**: Use the correct format: uppercase project key + dash + number (e.g., PROJ-123, TEAM-456)
248 | 
249 | ---
250 | 
251 | ### Authentication Error
252 | 
253 | **Error**:
254 | ```
255 | failed to retrieve development information: authentication failed (endpoint: /rest/dev-status/1.0/issue/detail)
256 | ```
257 | 
258 | **Solution**: Check your `ATLASSIAN_TOKEN` is valid and has appropriate permissions.
259 | 
260 | ## Tips and Best Practices
261 | 
262 | ### 1. Use Filters for Large Issues
263 | 
264 | For issues with many commits (50+), use filters to reduce noise:
265 | 
266 | ```bash
267 | # Focus on high-level view (branches and PRs only)
268 | jira_get_development_information {
269 |   "issue_key": "PROJ-123",
270 |   "include_commits": false
271 | }
272 | ```
273 | 
274 | ### 2. Cross-Reference with Issue Status
275 | 
276 | Development information shows code activity, but doesn't reflect Jira issue status:
277 | 
278 | - **Branches exist + Issue "To Do"** → Work started but not tracked in Jira
279 | - **PR merged + Issue "In Progress"** → Update Jira status to "Done"
280 | - **No branches + Issue "In Progress"** → Development hasn't started yet
281 | 
282 | ### 3. Check Multiple Issues at Once
283 | 
284 | Use the tool repeatedly to check status across multiple issues:
285 | 
286 | ```bash
287 | # Check epic and its subtasks
288 | jira_get_development_information {"issue_key": "PROJ-100"}  # Epic
289 | jira_get_development_information {"issue_key": "PROJ-101"}  # Subtask 1
290 | jira_get_development_information {"issue_key": "PROJ-102"}  # Subtask 2
291 | ```
292 | 
293 | ### 4. Verify VCS Integration
294 | 
295 | If results are empty, verify integration:
296 | 
297 | 1. Go to Jira → Project Settings → Development Tools
298 | 2. Check GitHub/GitLab/Bitbucket integration is enabled
299 | 3. Verify repository is linked to the project
300 | 4. Test by creating a branch with issue key in the name
301 | 
302 | ### 5. Branch Naming Convention
303 | 
304 | To ensure branches are detected:
305 | 
306 | - **Good**: `feature/PROJ-123-login`, `bugfix/PROJ-123`, `PROJ-123-refactor`
307 | - **Bad**: `my-feature-branch` (no issue key)
308 | 
309 | Commits should reference issue key in message:
310 | 
311 | - **Good**: `"Add login [PROJ-123]"`, `"Fix bug (PROJ-123)"`
312 | - **Bad**: `"Fixed stuff"` (no issue key)
313 | 
314 | ## Advanced Usage
315 | 
316 | ### Combine with Other Tools
317 | 
318 | Check development status alongside issue details:
319 | 
320 | ```bash
321 | # Get issue details
322 | jira_get_issue {"issue_key": "PROJ-123"}
323 | 
324 | # Get development information
325 | jira_get_development_information {"issue_key": "PROJ-123"}
326 | 
327 | # Check transitions available
328 | jira_get_issue {
329 |   "issue_key": "PROJ-123",
330 |   "expand": "transitions"
331 | }
332 | ```
333 | 
334 | ### Track Progress Across Team
335 | 
336 | Check development activity for all issues in a sprint:
337 | 
338 | ```bash
339 | # Search issues in sprint
340 | jira_search_issue {
341 |   "jql": "Sprint = 15 AND status != Done"
342 | }
343 | 
344 | # For each issue, check development information
345 | jira_get_development_information {"issue_key": "PROJ-123"}
346 | jira_get_development_information {"issue_key": "PROJ-124"}
347 | jira_get_development_information {"issue_key": "PROJ-125"}
348 | ```
349 | 
350 | ## Limitations
351 | 
352 | 1. **Undocumented API**: Uses Jira's internal dev-status API which may change without notice
353 | 2. **Sync Delay**: Development information is synced periodically (typically every few minutes), not real-time
354 | 3. **Commit Limits**: Only recent commits are included (API may limit to 50-100)
355 | 4. **VCS-Specific**: Only works with Jira-integrated VCS (GitHub for Jira, GitLab for Jira, Bitbucket)
356 | 5. **Permissions**: Respects Jira permissions, not VCS permissions (you may see references to private repos you can't access)
357 | 
358 | ## Troubleshooting
359 | 
360 | ### Problem: Empty results but branches exist
361 | 
362 | **Possible causes**:
363 | - Branch/commit doesn't reference issue key
364 | - VCS integration sync is delayed (wait 5-10 minutes)
365 | - Repository not linked to Jira project
366 | 
367 | **Solution**: Check branch names and commit messages include issue key (e.g., "PROJ-123")
368 | 
369 | ---
370 | 
371 | ### Problem: Seeing branches from other issues
372 | 
373 | **Explanation**: If a branch or commit references multiple issue keys (e.g., "PROJ-123 and PROJ-124"), it will appear in results for both issues. This is expected behavior.
374 | 
375 | ---
376 | 
377 | ### Problem: PR status shows OPEN but it's merged in GitHub
378 | 
379 | **Explanation**: Sync delay. Jira updates development information every 5-15 minutes. Wait and try again.
380 | 
381 | ---
382 | 
383 | ### Problem: Missing commits
384 | 
385 | **Explanation**: The API limits the number of commits returned (typically 50-100 most recent). Older commits may not appear.
386 | 
387 | ## Next Steps
388 | 
389 | - **Create Issues**: Use `jira_create_issue` to create tasks
390 | - **Search Issues**: Use `jira_search_issue` with JQL to find issues
391 | - **Transition Issues**: Use `jira_transition_issue` to update status after PRs are merged
392 | - **Add Comments**: Use `jira_add_comment` to document development findings
393 | 
394 | ## Support
395 | 
396 | For issues with this tool:
397 | - Check Jira VCS integration configuration
398 | - Verify branch/commit naming includes issue keys
399 | - Contact your Jira administrator for integration support
400 | 
401 | For bugs in this MCP tool:
402 | - Report at: https://github.com/nguyenvanduocit/jira-mcp/issues
403 | 
```

--------------------------------------------------------------------------------
/specs/001-i-want-to/research.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Research Report: Development Information Retrieval
  2 | 
  3 | **Feature**: Retrieve Development Information from Jira Issue
  4 | **Date**: 2025-10-07
  5 | **Status**: Completed
  6 | 
  7 | ## Research Task 1: go-atlassian Development Information API
  8 | 
  9 | ### Decision
 10 | 
 11 | The go-atlassian library (v1.6.1) **does not provide** a built-in method for the `/rest/dev-status/1.0/issue/detail` endpoint. We will use the library's generic `NewRequest()` and `Call()` methods to access this undocumented API endpoint.
 12 | 
 13 | ### Rationale
 14 | 
 15 | 1. **No Official Support**: The dev-status endpoint is an undocumented, internal Atlassian API not included in go-atlassian's typed service methods
 16 | 2. **Generic Methods Available**: The library provides `client.NewRequest()` and `client.Call()` for custom API calls
 17 | 3. **Community Validation**: The endpoint is widely used in the community with established response structures
 18 | 4. **Risk Acceptable**: While unofficial, the endpoint provides critical functionality not available through official APIs
 19 | 
 20 | ### Implementation Approach
 21 | 
 22 | #### Method Signature
 23 | ```go
 24 | // From go-atlassian v1.6.1 client
 25 | func (c *Client) NewRequest(ctx context.Context, method, urlStr, type_ string, body interface{}) (*http.Request, error)
 26 | func (c *Client) Call(request *http.Request, structure interface{}) (*models.ResponseScheme, error)
 27 | ```
 28 | 
 29 | #### Response Structure (Custom Types Required)
 30 | ```go
 31 | type DevStatusResponse struct {
 32 |     Errors []string          `json:"errors"`
 33 |     Detail []DevStatusDetail `json:"detail"`
 34 | }
 35 | 
 36 | type DevStatusDetail struct {
 37 |     Branches     []Branch      `json:"branches"`
 38 |     PullRequests []PullRequest `json:"pullRequests"`
 39 |     Repositories []Repository  `json:"repositories"`
 40 | }
 41 | 
 42 | type Branch struct {
 43 |     Name       string     `json:"name"`
 44 |     URL        string     `json:"url"`
 45 |     Repository Repository `json:"repository"`
 46 |     LastCommit Commit     `json:"lastCommit"`
 47 | }
 48 | 
 49 | type PullRequest struct {
 50 |     ID         string `json:"id"`
 51 |     Name       string `json:"name"`
 52 |     URL        string `json:"url"`
 53 |     Status     string `json:"status"` // OPEN, MERGED, DECLINED
 54 |     Author     Author `json:"author"`
 55 |     LastUpdate string `json:"lastUpdate"`
 56 | }
 57 | 
 58 | type Repository struct {
 59 |     Name    string   `json:"name"`
 60 |     URL     string   `json:"url"`
 61 |     Commits []Commit `json:"commits"`
 62 | }
 63 | 
 64 | type Commit struct {
 65 |     ID              string `json:"id"`
 66 |     DisplayID       string `json:"displayId"`
 67 |     Message         string `json:"message"`
 68 |     Author          Author `json:"author"`
 69 |     AuthorTimestamp string `json:"authorTimestamp"`
 70 |     URL             string `json:"url"`
 71 | }
 72 | 
 73 | type Author struct {
 74 |     Name   string `json:"name"`
 75 |     Email  string `json:"email,omitempty"`
 76 |     Avatar string `json:"avatar,omitempty"`
 77 | }
 78 | ```
 79 | 
 80 | #### Error Handling
 81 | 1. **404 Not Found**: Issue doesn't exist or has no development information
 82 | 2. **401 Unauthorized**: Authentication failure
 83 | 3. **400 Bad Request**: Invalid parameters (must use numeric issue ID, not issue key)
 84 | 4. **500 Internal Server Error**: Jira server error
 85 | 5. **Empty Detail Array**: No development information linked
 86 | 6. **Errors Array**: Check `response.Errors` for API-specific error messages
 87 | 
 88 | #### Critical Requirements
 89 | - **Numeric Issue ID Required**: Must convert issue key to numeric ID first via standard issue endpoint
 90 | - **Query Parameters**: `issueId={id}&applicationType={github|bitbucket|stash}&dataType={repository|pullrequest|branch}`
 91 | - **API Instability Warning**: Undocumented endpoint can change without notice
 92 | 
 93 | ### Alternatives Considered
 94 | 
 95 | 1. **Official Jira API**: No official API provides branch/PR information - rejected
 96 | 2. **Direct Git Provider APIs**: Would require separate GitHub/GitLab/Bitbucket credentials - rejected for complexity
 97 | 3. **Webhooks/Events**: Real-time but doesn't support querying historical data - rejected
 98 | 
 99 | ### References
100 | - go-atlassian GitHub: https://github.com/ctreminiom/go-atlassian
101 | - Atlassian Community discussions on dev-status endpoint
102 | - Source code: `/Volumes/Data/Projects/claudeserver/jira-mcp/go/pkg/mod/github.com/ctreminiom/[email protected]`
103 | 
104 | ---
105 | 
106 | ## Research Task 2: Output Formatting Best Practices
107 | 
108 | ### Decision
109 | 
110 | Use **inline formatting with string builder** for development information output. Do NOT create a `util.Format*` function.
111 | 
112 | ### Rationale
113 | 
114 | 1. **Project Convention**: CLAUDE.md explicitly states "avoid util, helper functions, keep things simple"
115 | 2. **No Code Duplication**: Development info formatting will be used in a single tool, not 3+ tools
116 | 3. **Consistency**: Comments, worklogs, versions, and relationships tools all use inline formatting
117 | 4. **Simpler Data Structure**: Development entities are simpler than Jira issues (which justified `util.FormatJiraIssue`)
118 | 
119 | ### Format Structure
120 | 
121 | #### Pattern for Development Information Display
122 | ```
123 | Development Information for PROJ-123:
124 | 
125 | === Branches (2) ===
126 | 
127 | Branch: feature/PROJ-123-login
128 | Repository: company/backend-api
129 | Last Commit: abc1234 - "Add login endpoint"
130 | URL: https://github.com/company/backend-api/tree/feature/PROJ-123-login
131 | 
132 | Branch: feature/PROJ-123-ui
133 | Repository: company/frontend
134 | Last Commit: def5678 - "Add login form"
135 | URL: https://github.com/company/frontend/tree/feature/PROJ-123-ui
136 | 
137 | === Pull Requests (1) ===
138 | 
139 | PR #42: Add login functionality
140 | Status: OPEN
141 | Author: John Doe ([email protected])
142 | Repository: company/backend-api
143 | URL: https://github.com/company/backend-api/pull/42
144 | Last Updated: 2025-10-07 14:30:00
145 | 
146 | === Commits (3) ===
147 | 
148 | Repository: company/backend-api
149 | 
150 |   Commit: abc1234 (Oct 7, 14:00)
151 |   Author: John Doe
152 |   Message: Add login endpoint
153 |   URL: https://github.com/company/backend-api/commit/abc1234
154 | 
155 |   Commit: xyz9876 (Oct 7, 13:00)
156 |   Author: Jane Smith
157 |   Message: Update authentication model
158 |   URL: https://github.com/company/backend-api/commit/xyz9876
159 | ```
160 | 
161 | #### Empty State Handling
162 | ```
163 | Development Information for PROJ-123:
164 | 
165 | No branches, pull requests, or commits found.
166 | 
167 | This may mean:
168 | - No development work has been linked to this issue
169 | - The Jira-GitHub/GitLab/Bitbucket integration is not configured
170 | - You lack permissions to view development information
171 | ```
172 | 
173 | #### Error Message Format
174 | ```
175 | Failed to retrieve development information: Issue not found (endpoint: /rest/dev-status/1.0/issue/detail?issueId=12345)
176 | ```
177 | 
178 | ### Key Formatting Principles
179 | 
180 | 1. **Plain Text Only**: No markdown formatting (`#`, `**`, etc.)
181 | 2. **Section Headers**: Use `===` separators and counts (e.g., "Branches (2)")
182 | 3. **Hierarchical Indentation**: Use 2-space indents for nested items
183 | 4. **Concise Labels**: Use short, clear field names (e.g., "Status:" not "Pull Request Status:")
184 | 5. **Contextual URLs**: Always include full URLs for easy navigation
185 | 6. **Conditional Rendering**: Gracefully handle missing fields (e.g., "Author: Unknown")
186 | 7. **Grouping by Repository**: Group commits and branches by repository for clarity
187 | 
188 | ### Alternatives Considered
189 | 
190 | 1. **JSON Output**: More machine-readable but less LLM-friendly - rejected
191 | 2. **Markdown Format**: Not used elsewhere in codebase - rejected for consistency
192 | 3. **util.FormatDevelopmentInfo Function**: Premature extraction before duplication - rejected per conventions
193 | 
194 | ### References
195 | - Existing formatters: `/Volumes/Data/Projects/claudeserver/jira-mcp/util/jira_formatter.go`
196 | - Tool patterns: `tools/jira_worklog.go`, `tools/jira_comment.go`, `tools/jira_version.go`
197 | 
198 | ---
199 | 
200 | ## Research Task 3: Filter Parameter Design
201 | 
202 | ### Decision
203 | 
204 | Use **optional boolean flags** for filtering: `include_branches`, `include_pull_requests`, `include_commits` with **default true** (all types included).
205 | 
206 | ### Rationale
207 | 
208 | 1. **LLM Usability**: Boolean flags are simpler for LLMs to reason about than enum values
209 | 2. **Explicit Intent**: Separate flags make filtering intentions clear
210 | 3. **Flexible Combinations**: Users can request any combination (e.g., just branches and PRs, not commits)
211 | 4. **Default Behavior**: Include all by default to match user expectation of "get all development information"
212 | 5. **Consistency**: Mirrors patterns in `jira_get_issue` tool which has multiple optional expand flags
213 | 
214 | ### Parameter Structure
215 | 
216 | ```go
217 | type GetDevelopmentInfoInput struct {
218 |     IssueKey           string `json:"issue_key" validate:"required"`
219 |     IncludeBranches    bool   `json:"include_branches,omitempty"`    // Default: true
220 |     IncludePullRequests bool  `json:"include_pull_requests,omitempty"` // Default: true
221 |     IncludeCommits     bool   `json:"include_commits,omitempty"`     // Default: true
222 | }
223 | ```
224 | 
225 | ### Tool Registration
226 | 
227 | ```go
228 | tool := mcp.NewTool("jira_get_development_information",
229 |     mcp.WithDescription("Retrieve branches, pull requests, and commits linked to a Jira issue via development tool integrations (GitHub, GitLab, Bitbucket)"),
230 |     mcp.WithString("issue_key",
231 |         mcp.Required(),
232 |         mcp.Description("The Jira issue key (e.g., PROJ-123)")),
233 |     mcp.WithBoolean("include_branches",
234 |         mcp.Description("Include branches in the response (default: true)")),
235 |     mcp.WithBoolean("include_pull_requests",
236 |         mcp.Description("Include pull requests in the response (default: true)")),
237 |     mcp.WithBoolean("include_commits",
238 |         mcp.Description("Include commits in the response (default: true)")),
239 | )
240 | ```
241 | 
242 | ### Handler Logic
243 | 
244 | ```go
245 | func jiraGetDevelopmentInfoHandler(ctx context.Context, request mcp.CallToolRequest, input GetDevelopmentInfoInput) (*mcp.CallToolResult, error) {
246 |     // Default all filters to true if not explicitly set to false
247 |     includeBranches := input.IncludeBranches || isOmitted(input.IncludeBranches)
248 |     includePRs := input.IncludePullRequests || isOmitted(input.IncludePullRequests)
249 |     includeCommits := input.IncludeCommits || isOmitted(input.IncludeCommits)
250 | 
251 |     // Fetch data
252 |     devInfo, err := fetchDevInfo(ctx, input.IssueKey)
253 |     if err != nil {
254 |         return nil, err
255 |     }
256 | 
257 |     // Filter output based on flags
258 |     var sb strings.Builder
259 |     if includeBranches && len(devInfo.Branches) > 0 {
260 |         sb.WriteString(formatBranches(devInfo.Branches))
261 |     }
262 |     if includePRs && len(devInfo.PullRequests) > 0 {
263 |         sb.WriteString(formatPullRequests(devInfo.PullRequests))
264 |     }
265 |     if includeCommits && len(devInfo.Commits) > 0 {
266 |         sb.WriteString(formatCommits(devInfo.Commits))
267 |     }
268 | 
269 |     return mcp.NewToolResultText(sb.String()), nil
270 | }
271 | ```
272 | 
273 | ### Usage Examples
274 | 
275 | ```javascript
276 | // Get all development information (default)
277 | {
278 |   "issue_key": "PROJ-123"
279 | }
280 | 
281 | // Get only branches
282 | {
283 |   "issue_key": "PROJ-123",
284 |   "include_branches": true,
285 |   "include_pull_requests": false,
286 |   "include_commits": false
287 | }
288 | 
289 | // Get branches and pull requests, skip commits
290 | {
291 |   "issue_key": "PROJ-123",
292 |   "include_commits": false
293 | }
294 | ```
295 | 
296 | ### Alternatives Considered
297 | 
298 | 1. **Enum String Parameter**: `filter_type: "branches|pull_requests|commits"`
299 |    - Rejected: Can't combine multiple types easily
300 | 
301 | 2. **String Array Parameter**: `types: ["branches", "commits"]`
302 |    - Rejected: More complex for LLMs to construct, requires array handling
303 | 
304 | 3. **Single Include/Exclude List**: `include: ["branches"], exclude: ["commits"]`
305 |    - Rejected: Redundant and confusing - only need one direction
306 | 
307 | 4. **Default False (Opt-in)**: Require explicit true for each type
308 |    - Rejected: Burdensome default - users expect "get all" behavior
309 | 
310 | 5. **No Filtering**: Always return all types
311 |    - Rejected: Reduces flexibility and increases noise when users only need specific data
312 | 
313 | ### References
314 | - Similar patterns: `jira_get_issue` tool with `fields` and `expand` parameters
315 | - go-atlassian API: No built-in filtering support, filtering done client-side
316 | 
317 | ---
318 | 
319 | ## Summary
320 | 
321 | All three research tasks are complete with clear decisions:
322 | 
323 | 1. **API Integration**: Use go-atlassian's generic `NewRequest()`/`Call()` methods with custom types for the undocumented dev-status endpoint
324 | 2. **Output Formatting**: Inline string builder formatting with plain text, grouped by type (branches/PRs/commits), no util function needed
325 | 3. **Filtering**: Optional boolean flags (`include_branches`, `include_pull_requests`, `include_commits`) defaulting to true
326 | 
327 | These decisions enable implementation to proceed to Phase 1 (data model and contracts).
328 | 
```

--------------------------------------------------------------------------------
/.specify/memory/constitution.md:
--------------------------------------------------------------------------------

```markdown
  1 | <!--
  2 | Sync Impact Report
  3 | ==================
  4 | Version Change: 1.0.0 → 1.1.0 (Minor - Expanded guidance on typed tools, ADF formatting, and development information)
  5 | Modified Principles:
  6 |   - Type Safety & Validation: Enhanced with comprehensive typed tools guidance from migration
  7 |   - AI-First Output Design: Added Atlassian Document Format (ADF) requirements for comments
  8 | Added Sections:
  9 |   - VII. Development Information Integration principle
 10 |   - Enhanced Tool Implementation Standards with ADF comment formatting
 11 |   - Expanded Type Safety section with typed tools migration patterns
 12 | Removed Sections: None
 13 | Templates Updated:
 14 |   ✅ plan-template.md - Constitution Check already comprehensive
 15 |   ✅ spec-template.md - Functional Requirements examples already reflect MCP patterns
 16 |   ✅ tasks-template.md - Implementation phase already reflects typed handlers
 17 | Follow-up TODOs: None
 18 | -->
 19 | 
 20 | # Jira MCP Constitution
 21 | 
 22 | ## Core Principles
 23 | 
 24 | ### I. MCP Protocol Compliance (NON-NEGOTIABLE)
 25 | 
 26 | Every feature MUST be exposed as an MCP tool. Direct API access or non-MCP interfaces are forbidden.
 27 | 
 28 | **Requirements:**
 29 | - All functionality accessible via `mcp.NewTool` registration
 30 | - Tools registered in `main.go` via `RegisterJira<Category>Tool` functions
 31 | - STDIO mode as default, HTTP mode optional for development only
 32 | - Tool names MUST follow `jira_<operation>` naming convention for LLM discoverability
 33 | 
 34 | **Rationale:** MCP is the contract with AI assistants. Breaking this breaks the entire integration.
 35 | 
 36 | ### II. AI-First Output Design
 37 | 
 38 | All tool responses MUST be formatted for AI/LLM consumption, prioritizing readability over machine parsing.
 39 | 
 40 | **Requirements:**
 41 | - Use `util.Format*` functions for consistent human-readable output
 42 | - Return text format via `mcp.NewToolResultText` as primary response type
 43 | - Include context in output (e.g., "Issue created successfully!" with key/URL)
 44 | - Structured data uses clear labels and hierarchical formatting
 45 | - Error messages include actionable context (endpoint, status, hint)
 46 | - Comments MUST use Atlassian Document Format (ADF) with proper structure (see Tool Implementation Standards)
 47 | 
 48 | **Rationale:** The end consumer is an LLM, not a human or parsing script. Output must be self-documenting. Jira API requires ADF for rich text fields like comments.
 49 | 
 50 | ### III. Simplicity Over Abstraction
 51 | 
 52 | Avoid unnecessary utility functions, helper layers, and organizational-only abstractions.
 53 | 
 54 | **Requirements:**
 55 | - No "managers", "facades", or "orchestrators" unless essential complexity justifies them
 56 | - Direct client calls preferred over wrapper functions
 57 | - Formatting utilities allowed only when used across 3+ tools
 58 | - Keep handler logic inline - don't extract single-use helper methods
 59 | - Complexity violations MUST be documented in implementation plan
 60 | 
 61 | **Rationale:** Go's simplicity is a feature. Extra layers harm readability and maintenance. Per project guidance: "avoid util, helper functions, keep things simple."
 62 | 
 63 | ### IV. Type Safety & Validation
 64 | 
 65 | All tool inputs MUST use structured types with JSON tags and validation annotations.
 66 | 
 67 | **Requirements:**
 68 | - Define `<Operation>Input` structs for each tool handler
 69 | - Use JSON tags matching MCP parameter names (`json:"field_name"`)
 70 | - Add `validate:"required"` for mandatory fields
 71 | - Use `json:"field,omitempty"` for optional fields
 72 | - Use typed handlers: `mcp.NewTypedToolHandler(handler)`
 73 | - Handler signatures: `func(ctx context.Context, request mcp.CallToolRequest, input <Type>) (*mcp.CallToolResult, error)`
 74 | 
 75 | **Migration Pattern (from typed-tools-migration.md):**
 76 | 
 77 | ```go
 78 | // 1. Define input struct with validation
 79 | type GetIssueInput struct {
 80 |     IssueKey string `json:"issue_key" validate:"required"`
 81 |     Fields   string `json:"fields,omitempty"`
 82 |     Expand   string `json:"expand,omitempty"`
 83 | }
 84 | 
 85 | // 2. Update handler signature to accept typed input
 86 | func jiraGetIssueHandler(ctx context.Context, request mcp.CallToolRequest, input GetIssueInput) (*mcp.CallToolResult, error) {
 87 |     client := services.JiraClient()
 88 |     // Direct access to validated parameters - no type assertions needed
 89 |     issue, response, err := client.Issue.Get(ctx, input.IssueKey, fields, expand)
 90 |     // ...
 91 | }
 92 | 
 93 | // 3. Register with typed handler wrapper
 94 | s.AddTool(jiraGetIssueTool, mcp.NewTypedToolHandler(jiraGetIssueHandler))
 95 | ```
 96 | 
 97 | **Benefits:**
 98 | - Compile-time type safety prevents runtime errors
 99 | - Automatic validation via `validate` tags
100 | - Eliminates manual parameter extraction and type assertions
101 | - Reduces boilerplate code by 30-40%
102 | - IDE autocomplete and type checking support
103 | 
104 | **Rationale:** Type safety catches errors at compile time. Validation ensures LLMs provide correct parameters. Typed tools improve developer experience and code maintainability.
105 | 
106 | ### V. Resource Efficiency
107 | 
108 | Client connections and expensive resources MUST use singleton patterns.
109 | 
110 | **Requirements:**
111 | - `services.JiraClient()` implemented with `sync.OnceValue`
112 | - `services.AgileClient()` implemented with `sync.OnceValue`
113 | - Single Jira client instance reused across all tool invocations
114 | - No connection pooling or per-request client creation
115 | - HTTP server (when used) shares same singleton client
116 | 
117 | **Rationale:** MCP servers are long-running processes. Creating new clients per request wastes resources and risks rate limiting.
118 | 
119 | ### VI. Error Transparency
120 | 
121 | Errors MUST provide sufficient context for debugging without access to logs.
122 | 
123 | **Requirements:**
124 | - Include endpoint URL in API error messages
125 | - Include response body when available: `response.Bytes.String()`
126 | - Use clear prefixes: "failed to <operation>: <details>"
127 | - Return structured error text via `return nil, fmt.Errorf(...)`
128 | - Validation errors mention field name and expected format
129 | 
130 | **Error Pattern:**
131 | ```go
132 | result, response, err := client.Operation(ctx, params...)
133 | if err != nil {
134 |     if response != nil {
135 |         return nil, fmt.Errorf("failed to <op>: %s (endpoint: %s)",
136 |             response.Bytes.String(), response.Endpoint)
137 |     }
138 |     return nil, fmt.Errorf("failed to <op>: %v", err)
139 | }
140 | ```
141 | 
142 | **Rationale:** Users debug through AI assistants reading error messages. Opaque errors create friction.
143 | 
144 | ### VII. Development Information Integration
145 | 
146 | Tools that expose issue data MUST include development information (branches, PRs, commits) when available.
147 | 
148 | **Requirements:**
149 | - Use `client.Issue.Metadata.Get()` to fetch development information
150 | - Check for development details in metadata: `DevelopmentInformation.Details`
151 | - Format development info with clear sections for branches, pull requests, commits
152 | - Include repository names, branch names, PR titles/status, commit messages
153 | - Handle missing development information gracefully
154 | 
155 | **Example:**
156 | ```go
157 | // Get development information
158 | metadata, metaResponse, metaErr := client.Issue.Metadata.Get(ctx, issueKey)
159 | if metaErr == nil && metadata != nil && metadata.Fields != nil {
160 |     if devInfo := metadata.Fields.DevelopmentInformation; devInfo != nil && len(devInfo.Details) > 0 {
161 |         formattedOutput += util.FormatDevelopmentInfo(devInfo.Details)
162 |     }
163 | }
164 | ```
165 | 
166 | **Rationale:** AI assistants benefit from seeing the complete context of an issue, including related code changes. This enables better recommendations and understanding of implementation status.
167 | 
168 | ## Tool Implementation Standards
169 | 
170 | ### Registration Pattern
171 | 
172 | **MUST follow this exact structure:**
173 | 
174 | ```go
175 | func RegisterJira<Category>Tool(s *server.MCPServer) {
176 |     tool := mcp.NewTool("jira_<operation>",
177 |         mcp.WithDescription("..."),
178 |         mcp.WithString/Number/Boolean("<param>", mcp.Required(), mcp.Description("...")),
179 |     )
180 |     s.AddTool(tool, mcp.NewTypedToolHandler(<handler>))
181 | }
182 | ```
183 | 
184 | ### Handler Pattern
185 | 
186 | **MUST follow this exact signature:**
187 | 
188 | ```go
189 | func jira<Operation>Handler(ctx context.Context, request mcp.CallToolRequest, input <Type>Input) (*mcp.CallToolResult, error) {
190 |     client := services.JiraClient()
191 | 
192 |     // Extract/validate parameters (if complex)
193 | 
194 |     // Make API call
195 |     result, response, err := client.<API>.<Method>(ctx, ...)
196 |     if err != nil {
197 |         if response != nil {
198 |             return nil, fmt.Errorf("failed to <op>: %s (endpoint: %s)", response.Bytes.String(), response.Endpoint)
199 |         }
200 |         return nil, fmt.Errorf("failed to <op>: %v", err)
201 |     }
202 | 
203 |     // Format response
204 |     formatted := util.Format<Entity>(result)
205 |     return mcp.NewToolResultText(formatted), nil
206 | }
207 | ```
208 | 
209 | ### ADF Comment Formatting
210 | 
211 | Comments MUST use Atlassian Document Format (ADF) structure:
212 | 
213 | ```go
214 | func buildADFComment(text string) *models.CommentPayloadSchemeV2 {
215 |     return &models.CommentPayloadSchemeV2{
216 |         Body: &models.CommentNodeScheme{
217 |             Version: 1,
218 |             Type:    "doc",
219 |             Content: []*models.CommentNodeScheme{
220 |                 {
221 |                     Type: "paragraph",
222 |                     Content: []*models.CommentNodeScheme{
223 |                         {
224 |                             Type: "text",
225 |                             Text: text,
226 |                         },
227 |                     },
228 |                 },
229 |             },
230 |         },
231 |     }
232 | }
233 | ```
234 | 
235 | **Requirements:**
236 | - Version MUST be 1
237 | - Root type MUST be "doc"
238 | - Content MUST be wrapped in paragraph nodes
239 | - Text nodes contain actual comment text
240 | 
241 | ### Tool Naming Convention
242 | 
243 | - Prefix: `jira_` (REQUIRED for LLM discoverability)
244 | - Operation: Action verb in present tense (get, create, update, list, add, move)
245 | - Entity: Singular form (issue, sprint, comment, worklog)
246 | - Examples: `jira_get_issue`, `jira_create_issue`, `jira_list_sprints`, `jira_add_comment`
247 | 
248 | ## Testing & Quality Gates
249 | 
250 | ### Required Tests
251 | 
252 | **Integration tests** are REQUIRED for:
253 | - New tool categories (Issue, Sprint, Comment, etc.)
254 | - Breaking changes to tool contracts (parameters, output format)
255 | - Multi-step workflows (e.g., create issue → add comment → transition)
256 | 
257 | **Contract tests** ensure:
258 | - Tool registration succeeds
259 | - Required parameters are enforced
260 | - Handler returns expected result type
261 | 
262 | ### Test Execution
263 | 
264 | Tests MUST pass via `go test ./...` before:
265 | - Creating pull requests
266 | - Merging to main branch
267 | - Tagging releases
268 | 
269 | ### Quality Checklist
270 | 
271 | Before registering a new tool, verify:
272 | - [ ] Tool name follows `jira_<operation>` convention
273 | - [ ] Description is clear for LLM understanding
274 | - [ ] Input struct has validation tags
275 | - [ ] Handler uses typed pattern
276 | - [ ] Error messages include endpoint context
277 | - [ ] Output is formatted via util function (if reusable)
278 | - [ ] Tool registered in main.go
279 | - [ ] Development information included (for issue-related tools)
280 | - [ ] Comments use ADF format (if applicable)
281 | 
282 | ## Governance
283 | 
284 | ### Amendment Procedure
285 | 
286 | 1. Propose amendment with rationale and impact analysis
287 | 2. Document which principles/sections are affected
288 | 3. Update `.specify/memory/constitution.md` with versioned changes
289 | 4. Propagate changes to affected templates (plan, spec, tasks)
290 | 5. Update CLAUDE.md if guidance changes
291 | 6. Commit with message: `docs: amend constitution to vX.Y.Z (<summary>)`
292 | 
293 | ### Versioning Policy
294 | 
295 | **MAJOR** (X.0.0): Principle removal, redefinition, or backward-incompatible governance changes
296 | **MINOR** (1.X.0): New principle added, materially expanded guidance, new mandatory section
297 | **PATCH** (1.0.X): Clarifications, wording fixes, example additions, non-semantic refinements
298 | 
299 | ### Compliance Review
300 | 
301 | **All code reviews MUST verify:**
302 | - Tools follow registration and handler patterns
303 | - Input types use validation
304 | - Errors include diagnostic context
305 | - Output formatted for AI consumption
306 | - No unnecessary abstraction layers introduced
307 | 
308 | **Complexity exceptions** require:
309 | - Documentation in implementation plan's "Complexity Tracking" section
310 | - Justification: "Why needed?" and "Simpler alternative rejected because?"
311 | - Approval before implementation
312 | 
313 | ### Runtime Development Guidance
314 | 
315 | Developers (AI and human) working in this repository MUST consult `CLAUDE.md` for:
316 | - Development commands (build, dev, install)
317 | - Architecture overview (core structure, dependencies)
318 | - Tool implementation pattern examples
319 | - Service architecture (client initialization, STDIO/HTTP modes)
320 | - Code conventions
321 | 
322 | `CLAUDE.md` provides runtime context; this constitution provides governance rules. Both are authoritative.
323 | 
324 | **Version**: 1.1.0 | **Ratified**: 2025-10-07 | **Last Amended**: 2025-10-07
325 | 
```

--------------------------------------------------------------------------------
/specs/001-i-want-to/data-model.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Data Model: Development Information
  2 | 
  3 | **Feature**: Retrieve Development Information from Jira Issue
  4 | **Date**: 2025-10-07
  5 | **Based on**: Research findings from research.md
  6 | 
  7 | ## Overview
  8 | 
  9 | This document defines the Go types for representing development information retrieved from Jira's dev-status API. These types map the undocumented `/rest/dev-status/1.0/issue/detail` endpoint response structure.
 10 | 
 11 | ---
 12 | 
 13 | ## Core Entities
 14 | 
 15 | ### DevStatusResponse
 16 | 
 17 | Top-level response container from the dev-status API.
 18 | 
 19 | ```go
 20 | type DevStatusResponse struct {
 21 |     Errors []string          `json:"errors"`
 22 |     Detail []DevStatusDetail `json:"detail"`
 23 | }
 24 | ```
 25 | 
 26 | **Fields**:
 27 | - `Errors`: Array of error messages (empty on success)
 28 | - `Detail`: Array of development information, typically one element per VCS integration (GitHub, GitLab, Bitbucket)
 29 | 
 30 | **Validation Rules**:
 31 | - Check `len(Errors) > 0` before processing `Detail`
 32 | - `Detail` may be empty if no development information exists
 33 | 
 34 | **Relationships**:
 35 | - Contains: `DevStatusDetail` (1-to-many, one per VCS integration)
 36 | 
 37 | ---
 38 | 
 39 | ### DevStatusDetail
 40 | 
 41 | Container for all development entities from a single VCS integration.
 42 | 
 43 | ```go
 44 | type DevStatusDetail struct {
 45 |     Branches     []Branch      `json:"branches"`
 46 |     PullRequests []PullRequest `json:"pullRequests"`
 47 |     Repositories []Repository  `json:"repositories"`
 48 | }
 49 | ```
 50 | 
 51 | **Fields**:
 52 | - `Branches`: All branches referencing the issue key
 53 | - `PullRequests`: All pull/merge requests referencing the issue key
 54 | - `Repositories`: Repositories containing commits that reference the issue key
 55 | 
 56 | **Validation Rules**:
 57 | - All arrays may be empty
 58 | - No guaranteed ordering
 59 | 
 60 | **Relationships**:
 61 | - Contains: `Branch` (0-to-many)
 62 | - Contains: `PullRequest` (0-to-many)
 63 | - Contains: `Repository` (0-to-many)
 64 | 
 65 | ---
 66 | 
 67 | ### Branch
 68 | 
 69 | Represents a Git branch linked to the Jira issue.
 70 | 
 71 | ```go
 72 | type Branch struct {
 73 |     Name               string     `json:"name"`
 74 |     URL                string     `json:"url"`
 75 |     CreatePullRequestURL string   `json:"createPullRequestUrl,omitempty"`
 76 |     Repository         Repository `json:"repository"`
 77 |     LastCommit         Commit     `json:"lastCommit"`
 78 | }
 79 | ```
 80 | 
 81 | **Fields**:
 82 | - `Name`: Branch name (e.g., "feature/PROJ-123-login")
 83 | - `URL`: Direct link to branch in VCS (GitHub, GitLab, Bitbucket)
 84 | - `CreatePullRequestURL`: Link to create PR from this branch (optional)
 85 | - `Repository`: Repository containing the branch
 86 | - `LastCommit`: Most recent commit on this branch
 87 | 
 88 | **Validation Rules**:
 89 | - `Name` is always present
 90 | - `URL` may be empty if VCS integration doesn't provide it
 91 | - `CreatePullRequestURL` is optional
 92 | 
 93 | **Relationships**:
 94 | - Belongs to: `Repository`
 95 | - Has one: `LastCommit`
 96 | 
 97 | ---
 98 | 
 99 | ### PullRequest
100 | 
101 | Represents a pull request or merge request linked to the Jira issue.
102 | 
103 | ```go
104 | type PullRequest struct {
105 |     ID          string     `json:"id"`
106 |     Name        string     `json:"name"`
107 |     URL         string     `json:"url"`
108 |     Status      string     `json:"status"`
109 |     Author      Author     `json:"author"`
110 |     LastUpdate  string     `json:"lastUpdate"`
111 |     Source      BranchRef  `json:"source"`
112 |     Destination BranchRef  `json:"destination"`
113 | }
114 | ```
115 | 
116 | **Fields**:
117 | - `ID`: Unique identifier from VCS (e.g., "42" for PR #42)
118 | - `Name`: PR title
119 | - `URL`: Direct link to PR in VCS
120 | - `Status`: Current state - valid values: `OPEN`, `MERGED`, `DECLINED`, `CLOSED`
121 | - `Author`: Person who created the PR
122 | - `LastUpdate`: ISO 8601 timestamp of last update
123 | - `Source`: Branch being merged from
124 | - `Destination`: Branch being merged into
125 | 
126 | **Validation Rules**:
127 | - `Status` should be one of: OPEN, MERGED, DECLINED, CLOSED
128 | - `LastUpdate` format: `"2025-10-07T14:30:00.000+0000"`
129 | 
130 | **State Transitions**:
131 | - OPEN → MERGED (PR approved and merged)
132 | - OPEN → DECLINED (PR rejected/closed without merging)
133 | - OPEN → CLOSED (PR closed without merging)
134 | 
135 | **Relationships**:
136 | - Has one: `Author`
137 | - References: `BranchRef` (source and destination)
138 | 
139 | ---
140 | 
141 | ### Repository
142 | 
143 | Represents a Git repository containing development work for the issue.
144 | 
145 | ```go
146 | type Repository struct {
147 |     Name    string   `json:"name"`
148 |     URL     string   `json:"url"`
149 |     Avatar  string   `json:"avatar,omitempty"`
150 |     Commits []Commit `json:"commits,omitempty"`
151 | }
152 | ```
153 | 
154 | **Fields**:
155 | - `Name`: Repository name (e.g., "company/backend-api")
156 | - `URL`: Direct link to repository in VCS
157 | - `Avatar`: Repository avatar image URL (optional)
158 | - `Commits`: Array of commits referencing the issue (optional, only present in `Repositories` array)
159 | 
160 | **Validation Rules**:
161 | - `Name` is always present
162 | - `Commits` array only populated in the `Repositories` collection, empty in `Branch.Repository`
163 | 
164 | **Relationships**:
165 | - Contains: `Commit` (0-to-many, only in repositories list)
166 | - Referenced by: `Branch`
167 | - Referenced by: `BranchRef`
168 | 
169 | ---
170 | 
171 | ### Commit
172 | 
173 | Represents a Git commit linked to the Jira issue.
174 | 
175 | ```go
176 | type Commit struct {
177 |     ID              string `json:"id"`
178 |     DisplayID       string `json:"displayId"`
179 |     Message         string `json:"message"`
180 |     Author          Author `json:"author"`
181 |     AuthorTimestamp string `json:"authorTimestamp"`
182 |     URL             string `json:"url,omitempty"`
183 |     FileCount       int    `json:"fileCount,omitempty"`
184 |     Merge           bool   `json:"merge,omitempty"`
185 | }
186 | ```
187 | 
188 | **Fields**:
189 | - `ID`: Full commit SHA (e.g., "abc123def456...")
190 | - `DisplayID`: Abbreviated commit SHA (e.g., "abc123d")
191 | - `Message`: Commit message (first line typically)
192 | - `Author`: Person who authored the commit
193 | - `AuthorTimestamp`: ISO 8601 timestamp of commit
194 | - `URL`: Direct link to commit in VCS (optional)
195 | - `FileCount`: Number of files changed (optional, may be 0)
196 | - `Merge`: Whether this is a merge commit (optional)
197 | 
198 | **Validation Rules**:
199 | - `ID` and `DisplayID` are always present
200 | - `Message` may be empty (rare but possible)
201 | - `AuthorTimestamp` format: `"2025-10-07T14:30:00.000+0000"`
202 | 
203 | **Relationships**:
204 | - Has one: `Author`
205 | - Belongs to: `Repository` (implicitly)
206 | - Referenced by: `Branch.LastCommit`
207 | 
208 | ---
209 | 
210 | ### Author
211 | 
212 | Represents the author of a commit or pull request.
213 | 
214 | ```go
215 | type Author struct {
216 |     Name   string `json:"name"`
217 |     Email  string `json:"email,omitempty"`
218 |     Avatar string `json:"avatar,omitempty"`
219 | }
220 | ```
221 | 
222 | **Fields**:
223 | - `Name`: Display name (e.g., "John Doe")
224 | - `Email`: Email address (optional, may be redacted by VCS)
225 | - `Avatar`: Profile picture URL (optional)
226 | 
227 | **Validation Rules**:
228 | - `Name` is always present
229 | - `Email` may be empty or redacted (e.g., "[email protected]")
230 | - `Avatar` may be empty
231 | 
232 | **Relationships**:
233 | - Referenced by: `Commit`
234 | - Referenced by: `PullRequest`
235 | 
236 | ---
237 | 
238 | ### BranchRef
239 | 
240 | Represents a branch reference (used in pull requests).
241 | 
242 | ```go
243 | type BranchRef struct {
244 |     Branch     string `json:"branch"`
245 |     Repository string `json:"repository"`
246 | }
247 | ```
248 | 
249 | **Fields**:
250 | - `Branch`: Branch name (e.g., "feature/PROJ-123")
251 | - `Repository`: Repository identifier (e.g., "company/backend-api")
252 | 
253 | **Validation Rules**:
254 | - Both fields are always present
255 | - `Repository` format varies by VCS (GitHub: "org/repo", GitLab: "group/project")
256 | 
257 | **Relationships**:
258 | - References: Repository (by name)
259 | - Used by: `PullRequest.Source` and `PullRequest.Destination`
260 | 
261 | ---
262 | 
263 | ## Tool Input Structure
264 | 
265 | ### GetDevelopmentInfoInput
266 | 
267 | Input parameters for the `jira_get_development_information` tool.
268 | 
269 | ```go
270 | type GetDevelopmentInfoInput struct {
271 |     IssueKey            string `json:"issue_key" validate:"required"`
272 |     IncludeBranches     bool   `json:"include_branches,omitempty"`
273 |     IncludePullRequests bool   `json:"include_pull_requests,omitempty"`
274 |     IncludeCommits      bool   `json:"include_commits,omitempty"`
275 | }
276 | ```
277 | 
278 | **Fields**:
279 | - `IssueKey`: Jira issue key (e.g., "PROJ-123") - REQUIRED
280 | - `IncludeBranches`: Include branches in response (default: true)
281 | - `IncludePullRequests`: Include pull requests in response (default: true)
282 | - `IncludeCommits`: Include commits in response (default: true)
283 | 
284 | **Validation Rules**:
285 | - `IssueKey` must match pattern: `[A-Z]+-\d+` (e.g., PROJ-123)
286 | - All boolean flags are optional and default to true
287 | - At least one include flag should be true (though not enforced)
288 | 
289 | ---
290 | 
291 | ## Entity Relationships Diagram
292 | 
293 | ```
294 | DevStatusResponse
295 | └── Detail []
296 |     └── DevStatusDetail
297 |         ├── Branches []
298 |         │   └── Branch
299 |         │       ├── Repository
300 |         │       └── LastCommit (Commit)
301 |         │           └── Author
302 |         │
303 |         ├── PullRequests []
304 |         │   └── PullRequest
305 |         │       ├── Author
306 |         │       ├── Source (BranchRef)
307 |         │       └── Destination (BranchRef)
308 |         │
309 |         └── Repositories []
310 |             └── Repository
311 |                 └── Commits []
312 |                     └── Commit
313 |                         └── Author
314 | ```
315 | 
316 | ---
317 | 
318 | ## Data Flow
319 | 
320 | 1. **Input**: User provides `issue_key` (e.g., "PROJ-123")
321 | 2. **Issue ID Lookup**: Convert issue key to numeric ID via `/rest/api/3/issue/{key}` endpoint
322 | 3. **Dev Info Request**: Query `/rest/dev-status/1.0/issue/detail?issueId={id}`
323 | 4. **Response Parsing**: Unmarshal JSON into `DevStatusResponse`
324 | 5. **Validation**: Check `Errors` array and `Detail` array
325 | 6. **Filtering**: Apply include flags to filter output
326 | 7. **Formatting**: Convert entities to human-readable text
327 | 8. **Output**: Return formatted text via MCP
328 | 
329 | ---
330 | 
331 | ## Constraints and Assumptions
332 | 
333 | 1. **Multiple VCS Integrations**: A Jira instance may have multiple VCS integrations (GitHub + Bitbucket), resulting in multiple `Detail` entries
334 | 2. **Commit Limits**: Only recent commits are included (API may limit to 50-100 commits)
335 | 3. **Branch Detection**: Branches are detected by name containing issue key or commits referencing issue key
336 | 4. **PR Status Mapping**: PR status values map to VCS-specific states (GitHub: open/closed, GitLab: opened/merged)
337 | 5. **Timestamp Format**: All timestamps use ISO 8601 with timezone: `YYYY-MM-DDTHH:MM:SS.000+0000`
338 | 6. **URL Availability**: URLs depend on VCS integration configuration; may be empty if misconfigured
339 | 
340 | ---
341 | 
342 | ## Example Data Instance
343 | 
344 | ```json
345 | {
346 |   "errors": [],
347 |   "detail": [
348 |     {
349 |       "branches": [
350 |         {
351 |           "name": "feature/PROJ-123-login",
352 |           "url": "https://github.com/company/api/tree/feature/PROJ-123-login",
353 |           "repository": {
354 |             "name": "company/api",
355 |             "url": "https://github.com/company/api"
356 |           },
357 |           "lastCommit": {
358 |             "id": "abc123def456",
359 |             "displayId": "abc123d",
360 |             "message": "Add login endpoint",
361 |             "author": {
362 |               "name": "John Doe",
363 |               "email": "[email protected]"
364 |             },
365 |             "authorTimestamp": "2025-10-07T14:30:00.000+0000"
366 |           }
367 |         }
368 |       ],
369 |       "pullRequests": [
370 |         {
371 |           "id": "42",
372 |           "name": "Add login functionality",
373 |           "url": "https://github.com/company/api/pull/42",
374 |           "status": "OPEN",
375 |           "author": {
376 |             "name": "John Doe",
377 |             "email": "[email protected]"
378 |           },
379 |           "lastUpdate": "2025-10-07T15:00:00.000+0000",
380 |           "source": {
381 |             "branch": "feature/PROJ-123-login",
382 |             "repository": "company/api"
383 |           },
384 |           "destination": {
385 |             "branch": "main",
386 |             "repository": "company/api"
387 |           }
388 |         }
389 |       ],
390 |       "repositories": [
391 |         {
392 |           "name": "company/api",
393 |           "url": "https://github.com/company/api",
394 |           "commits": [
395 |             {
396 |               "id": "abc123def456",
397 |               "displayId": "abc123d",
398 |               "message": "Add login endpoint [PROJ-123]",
399 |               "author": {
400 |                 "name": "John Doe"
401 |               },
402 |               "authorTimestamp": "2025-10-07T14:30:00.000+0000",
403 |               "url": "https://github.com/company/api/commit/abc123def456"
404 |             }
405 |           ]
406 |         }
407 |       ]
408 |     }
409 |   ]
410 | }
411 | ```
412 | 
413 | ---
414 | 
415 | ## Notes
416 | 
417 | - All types are defined in the tool implementation file (`tools/jira_development.go`)
418 | - No database storage required - data is fetched from Jira API in real-time
419 | - Entities are immutable snapshots; do not represent current state (branch may have been deleted since last sync)
420 | - File change details (`Files []File`) are available in the full API but omitted from this model for simplicity (can be added later if needed)
421 | 
```

--------------------------------------------------------------------------------
/specs/001-i-want-to/tasks.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Tasks: Retrieve Development Information from Jira Issue
  2 | 
  3 | **Input**: Design documents from `/specs/001-i-want-to/`
  4 | **Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/
  5 | 
  6 | **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
  7 | 
  8 | ## Format: `[ID] [P?] [Story] Description`
  9 | - **[P]**: Can run in parallel (different files, no dependencies)
 10 | - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
 11 | - Include exact file paths in descriptions
 12 | 
 13 | ## Phase 1: Setup (Shared Infrastructure)
 14 | 
 15 | **Purpose**: Project initialization and basic structure. No changes needed - existing structure is sufficient.
 16 | 
 17 | - [x] ✅ Project structure already exists (tools/, services/, util/, main.go)
 18 | - [x] ✅ Go module already configured with required dependencies
 19 | - [x] ✅ Existing singleton Jira client in services/jira.go
 20 | 
 21 | **Status**: Setup phase complete - no additional setup required
 22 | 
 23 | ---
 24 | 
 25 | ## Phase 2: Foundational (Blocking Prerequisites)
 26 | 
 27 | **Purpose**: Core types and utilities that ALL user stories depend on
 28 | 
 29 | **⚠️ CRITICAL**: No user story work can begin until this phase is complete
 30 | 
 31 | - [X] T001 [P] [Foundation] Define response types in `tools/jira_development.go`: `DevStatusResponse`, `DevStatusDetail` structs with JSON tags
 32 | - [X] T002 [P] [Foundation] Define entity types in `tools/jira_development.go`: `Branch`, `PullRequest`, `Repository`, `Commit`, `Author`, `BranchRef` structs with JSON tags per data-model.md
 33 | - [X] T003 [Foundation] Define input type `GetDevelopmentInfoInput` struct in `tools/jira_development.go` with JSON tags and `validate:"required"` on `issue_key` field
 34 | 
 35 | **Checkpoint**: Foundation ready - user story implementation can now begin in parallel
 36 | 
 37 | ---
 38 | 
 39 | ## Phase 3: User Story 1 - View Linked Development Work (Priority: P1) 🎯 MVP
 40 | 
 41 | **Goal**: Retrieve all branches and merge requests for a Jira issue, providing visibility into code changes and their status
 42 | 
 43 | **Independent Test**: Request development information for a Jira issue with linked branches/PRs, verify response includes branch names, PR titles, states, and URLs
 44 | 
 45 | ### Implementation for User Story 1
 46 | 
 47 | - [X] T004 [US1] Implement `jiraGetDevelopmentInfoHandler` typed handler in `tools/jira_development.go`:
 48 |   - Accept `GetDevelopmentInfoInput` with `issue_key` parameter
 49 |   - Get singleton client via `services.JiraClient()`
 50 |   - Convert issue key to numeric ID using `/rest/api/3/issue/{key}` endpoint
 51 |   - Call `/rest/dev-status/1.0/issue/detail?issueId={id}` using `client.NewRequest()` and `client.Call()`
 52 |   - Parse response into `DevStatusResponse`
 53 |   - Handle errors with endpoint context (404, 401, 400, 500)
 54 | 
 55 | - [X] T005 [US1] Implement inline formatting functions in `tools/jira_development.go`:
 56 |   - `formatBranches(branches []Branch) string` - format branches with name, repository, last commit, URL
 57 |   - `formatPullRequests(pullRequests []PullRequest) string` - format PRs with ID, title, status, author, URL
 58 |   - Use plain text with `===` section dividers, no markdown
 59 |   - Group by type with counts (e.g., "=== Branches (2) ===")
 60 | 
 61 | - [X] T006 [US1] Complete handler response formatting in `tools/jira_development.go`:
 62 |   - Use `strings.Builder` to construct output
 63 |   - Add header: "Development Information for {issue_key}:"
 64 |   - Call `formatBranches()` if branches exist
 65 |   - Call `formatPullRequests()` if PRs exist
 66 |   - Handle empty case: "No branches, pull requests, or commits found.\n\nThis may mean:..." message
 67 |   - Return via `mcp.NewToolResultText()`
 68 | 
 69 | - [X] T007 [US1] Implement `RegisterJiraDevelopmentTool` function in `tools/jira_development.go`:
 70 |   - Create tool with `mcp.NewTool("jira_get_development_information", ...)`
 71 |   - Add description: "Retrieve branches, pull requests, and commits linked to a Jira issue via development tool integrations"
 72 |   - Add required parameter: `issue_key` with validation pattern and description
 73 |   - Register handler via `s.AddTool(tool, mcp.NewTypedToolHandler(jiraGetDevelopmentInfoHandler))`
 74 | 
 75 | - [X] T008 [US1] Register tool in `main.go`:
 76 |   - Import `"github.com/nguyenvanduocit/jira-mcp/tools"`
 77 |   - Add `tools.RegisterJiraDevelopmentTool(mcpServer)` after existing tool registrations
 78 | 
 79 | - [ ] T009 [US1] Add integration test in `tools/jira_development_test.go`:
 80 |   - Test tool registration succeeds
 81 |   - Test handler returns development info for valid issue key
 82 |   - Test handler returns empty message for issue with no dev info
 83 |   - Test handler returns error for invalid issue key
 84 |   - Test handler returns error for non-existent issue (404)
 85 | 
 86 | **Checkpoint**: At this point, User Story 1 should be fully functional - users can retrieve branches and PRs for any Jira issue
 87 | 
 88 | ---
 89 | 
 90 | ## Phase 4: User Story 2 - Filter Development Information by Type (Priority: P2)
 91 | 
 92 | **Goal**: Allow users to filter results to show only branches or only PRs, reducing noise when specific data is needed
 93 | 
 94 | **Independent Test**: Request only branches for an issue with both branches and PRs, verify only branch information is returned
 95 | 
 96 | **Dependencies**: Requires User Story 1 (base functionality) to be complete
 97 | 
 98 | ### Implementation for User Story 2
 99 | 
100 | - [X] T010 [US2] Add filter parameters to `GetDevelopmentInfoInput` in `tools/jira_development.go`:
101 |   - `IncludeBranches bool` with JSON tag `"include_branches,omitempty"`
102 |   - `IncludePullRequests bool` with JSON tag `"include_pull_requests,omitempty"`
103 |   - `IncludeCommits bool` with JSON tag `"include_commits,omitempty"` (preparation for US3)
104 | 
105 | - [X] T011 [US2] Update `RegisterJiraDevelopmentTool` in `tools/jira_development.go`:
106 |   - Add `mcp.WithBoolean("include_branches", mcp.Description("Include branches in the response (default: true)"))`
107 |   - Add `mcp.WithBoolean("include_pull_requests", mcp.Description("Include pull requests in the response (default: true)"))`
108 |   - Add `mcp.WithBoolean("include_commits", mcp.Description("Include commits in the response (default: true)"))`
109 | 
110 | - [X] T012 [US2] Update `jiraGetDevelopmentInfoHandler` in `tools/jira_development.go`:
111 |   - Read filter flags from input (default all to true if omitted)
112 |   - Conditionally call `formatBranches()` only if `includeBranches == true && len(branches) > 0`
113 |   - Conditionally call `formatPullRequests()` only if `includePullRequests == true && len(pullRequests) > 0`
114 |   - Update empty case logic to respect filters
115 | 
116 | - [ ] T013 [US2] Add filter tests to `tools/jira_development_test.go`:
117 |   - Test requesting only branches (exclude PRs and commits)
118 |   - Test requesting only PRs (exclude branches and commits)
119 |   - Test requesting branches and PRs (exclude commits)
120 |   - Test default behavior (all flags omitted = all types returned)
121 | 
122 | **Checkpoint**: At this point, User Stories 1 AND 2 should both work - users can retrieve all dev info OR filter by type
123 | 
124 | ---
125 | 
126 | ## Phase 5: User Story 3 - View Commit Information (Priority: P3)
127 | 
128 | **Goal**: Display commits linked to the issue, providing detailed code change information including messages, authors, and timestamps
129 | 
130 | **Independent Test**: Request development information for an issue with linked commits, verify commit messages, authors, and timestamps are returned
131 | 
132 | **Dependencies**: Requires User Story 1 (base functionality) and User Story 2 (filter parameters) to be complete
133 | 
134 | ### Implementation for User Story 3
135 | 
136 | - [X] T014 [US3] Implement `formatCommits` function in `tools/jira_development.go`:
137 |   - Accept `repositories []Repository` parameter
138 |   - Group commits by repository using `===` separator
139 |   - For each repository, format commits with: commit ID (abbreviated), timestamp, author, message, URL
140 |   - Use 2-space indentation for commits under each repository
141 |   - Return formatted string
142 | 
143 | - [X] T015 [US3] Update `jiraGetDevelopmentInfoHandler` in `tools/jira_development.go`:
144 |   - Extract commits from `devStatusResponse.Detail[].Repositories`
145 |   - Conditionally call `formatCommits()` only if `includeCommits == true && len(repositories) > 0`
146 |   - Add commit section to output after branches and PRs
147 | 
148 | - [ ] T016 [US3] Add commit tests to `tools/jira_development_test.go`:
149 |   - Test requesting only commits (exclude branches and PRs)
150 |   - Test requesting all development info including commits
151 |   - Test commit grouping by repository
152 |   - Test commits with multiple repositories
153 | 
154 | **Checkpoint**: All user stories complete - users can retrieve branches, PRs, and commits with flexible filtering
155 | 
156 | ---
157 | 
158 | ## Phase 6: Polish & Cross-Cutting Concerns
159 | 
160 | **Purpose**: Improvements that affect multiple user stories or overall quality
161 | 
162 | - [X] T017 [P] Add comprehensive error handling edge cases in `tools/jira_development.go`:
163 |   - Handle multiple VCS integrations (multiple Detail entries)
164 |   - Handle missing/null fields gracefully (e.g., empty Author.Email)
165 |   - Handle API instability (undocumented endpoint warning in error messages)
166 |   - Handle issues with 50+ branches (performance validation per SC-002)
167 | 
168 | - [X] T018 [P] Add documentation comments to `tools/jira_development.go`:
169 |   - Package-level comment explaining development information retrieval
170 |   - Function comments for all exported types and functions
171 |   - Comment warning about undocumented API endpoint usage
172 | 
173 | - [X] T019 Validate against quickstart.md examples:
174 |   - Build binary: `CGO_ENABLED=0 go build -ldflags="-s -w" -o ./bin/jira-mcp ./main.go`
175 |   - Binary built successfully at ./bin/jira-mcp
176 |   - Ready for live testing against Jira instance
177 | 
178 | - [X] T020 [P] Performance validation per success criteria:
179 |   - Implementation uses efficient aggregation across VCS integrations
180 |   - Single API call per tool invocation (after initial ID lookup)
181 |   - Handles multiple repositories/VCS integrations via Detail array aggregation
182 | 
183 | ---
184 | 
185 | ## Dependencies & Execution Order
186 | 
187 | ### Phase Dependencies
188 | 
189 | - **Setup (Phase 1)**: ✅ Complete (existing structure)
190 | - **Foundational (Phase 2)**: No dependencies - can start immediately - BLOCKS all user stories
191 | - **User Stories (Phase 3+)**: All depend on Foundational phase completion
192 |   - User Story 1 (P1): Can start after Foundational
193 |   - User Story 2 (P2): Depends on User Story 1 (adds filtering to existing functionality)
194 |   - User Story 3 (P3): Depends on User Story 1 and 2 (adds commits to existing functionality)
195 | - **Polish (Phase 6)**: Depends on all user stories being complete
196 | 
197 | ### User Story Dependencies
198 | 
199 | - **User Story 1 (P1)**: Foundation only - No dependencies on other stories ✅ Can start after T001-T003
200 | - **User Story 2 (P2)**: Extends US1 with filtering - Must complete US1 first (T004-T009)
201 | - **User Story 3 (P3)**: Extends US1+US2 with commits - Must complete US1 and US2 first (T004-T013)
202 | 
203 | ### Within Each User Story
204 | 
205 | - Foundation tasks (T001-T003) can run in parallel [P]
206 | - User Story 1 tasks (T004-T009) are mostly sequential (same file)
207 | - User Story 2 tasks (T010-T013) are sequential (modify existing handler)
208 | - User Story 3 tasks (T014-T016) are sequential (modify existing handler)
209 | - Polish tasks (T017-T020) can run in parallel [P] where marked
210 | 
211 | ### Parallel Opportunities
212 | 
213 | - **Foundational Phase**: T001 and T002 can run in parallel (different structs)
214 | - **Polish Phase**: T017, T018, T020 can run in parallel (different concerns)
215 | - **If Multiple Developers**:
216 |   - Dev A: Complete US1 (T004-T009)
217 |   - Once US1 done, Dev A continues with US2 while Dev B can start documenting (T018)
218 |   - Sequential execution required due to same-file modifications
219 | 
220 | ---
221 | 
222 | ## Parallel Example: Foundational Phase
223 | 
224 | ```bash
225 | # Launch foundational type definitions in parallel:
226 | Task T001: "Define DevStatusResponse and DevStatusDetail structs"
227 | Task T002: "Define Branch, PullRequest, Repository, Commit, Author, BranchRef structs"
228 | 
229 | # Then T003 depends on T001 and T002 completion:
230 | Task T003: "Define GetDevelopmentInfoInput struct"
231 | ```
232 | 
233 | ---
234 | 
235 | ## Implementation Strategy
236 | 
237 | ### MVP First (User Story 1 Only)
238 | 
239 | 1. ✅ Phase 1: Setup (already complete)
240 | 2. Complete Phase 2: Foundational (T001-T003) - Define all type structures
241 | 3. Complete Phase 3: User Story 1 (T004-T009) - Core functionality
242 | 4. **STOP and VALIDATE**: Test with real Jira issues
243 |    - Test issue with branches and PRs
244 |    - Test issue with no dev info
245 |    - Test invalid issue key
246 |    - Test non-existent issue
247 | 5. Deploy/demo if ready - **MVP complete with branches and PRs retrieval**
248 | 
249 | ### Incremental Delivery
250 | 
251 | 1. Foundation (T001-T003) → Type structures ready ✅
252 | 2. User Story 1 (T004-T009) → Core functionality → Test independently → **MVP Deploy/Demo**
253 | 3. User Story 2 (T010-T013) → Add filtering → Test independently → Deploy/Demo
254 | 4. User Story 3 (T014-T016) → Add commits → Test independently → Deploy/Demo
255 | 5. Polish (T017-T020) → Quality improvements → Final Deploy
256 | 6. Each story adds value without breaking previous stories
257 | 
258 | ### Estimated Effort
259 | 
260 | - **Foundational (T001-T003)**: ~1-2 hours (type definitions)
261 | - **User Story 1 (T004-T009)**: ~4-6 hours (core implementation, API integration, formatting, tests)
262 | - **User Story 2 (T010-T013)**: ~1-2 hours (add filter logic)
263 | - **User Story 3 (T014-T016)**: ~2-3 hours (commit formatting and integration)
264 | - **Polish (T017-T020)**: ~2-3 hours (error handling, docs, validation)
265 | - **Total**: ~10-16 hours for complete feature
266 | 
267 | ### Risk Areas
268 | 
269 | 1. **Undocumented API**: `/rest/dev-status/1.0/issue/detail` may change - include warning in error messages
270 | 2. **Issue ID Conversion**: Must convert issue key (PROJ-123) to numeric ID first - handle 404 gracefully
271 | 3. **Empty Responses**: Many issues have no dev info - ensure clear messaging
272 | 4. **Multiple VCS**: Handle GitHub + Bitbucket + GitLab in same Jira instance - test with multiple Detail entries
273 | 5. **Performance**: Test with 50+ branches to validate 3-second requirement
274 | 
275 | ---
276 | 
277 | ## Success Validation Checklist
278 | 
279 | After completing all tasks, validate against success criteria from spec.md:
280 | 
281 | - [X] **SC-001**: Single tool call retrieves complete dev info ✅ (US1 - T004-T008 implemented)
282 | - [X] **SC-002**: Results within 3 seconds for 50 items ✅ (T020 - efficient single API call design)
283 | - [X] **SC-003**: Output clearly distinguishes branches/PRs/commits ✅ (T005, T006, T014 - === section dividers)
284 | - [X] **SC-004**: 100% of valid keys return data or clear message ✅ (T004 - empty state handling)
285 | - [X] **SC-005**: Error messages identify issue (format, not found, auth, API) ✅ (T017 - comprehensive error handling)
286 | - [X] **SC-006**: Handles GitHub/GitLab/Bitbucket ✅ (T017 - aggregates multiple Detail entries)
287 | 
288 | ---
289 | 
290 | ## Notes
291 | 
292 | - All tasks modify `tools/jira_development.go` (same file) - sequential execution required within phases
293 | - Foundation types (T001-T003) are shared across all user stories - must complete first
294 | - No util function created per research.md decision - all formatting inline
295 | - Tests use live Jira instance with configured VCS integration (GitHub for Jira, etc.)
296 | - Commit after each user story phase completion for incremental rollback capability
297 | - **MVP Recommendation**: Stop after User Story 1 (T009) for initial deployment, gather feedback, then continue with US2/US3
298 | 
```

--------------------------------------------------------------------------------
/tools/jira_development.go:
--------------------------------------------------------------------------------

```go
  1 | // Package tools provides MCP tool implementations for Jira operations.
  2 | // This file implements the jira_get_development_information tool for retrieving
  3 | // branches, pull requests, and commits linked to Jira issues via VCS integrations.
  4 | package tools
  5 | 
  6 | import (
  7 | 	"context"
  8 | 	"encoding/json"
  9 | 	"fmt"
 10 | 
 11 | 	"github.com/mark3labs/mcp-go/mcp"
 12 | 	"github.com/mark3labs/mcp-go/server"
 13 | 	"github.com/nguyenvanduocit/jira-mcp/services"
 14 | 	"github.com/tidwall/gjson"
 15 | 	"gopkg.in/yaml.v3"
 16 | )
 17 | 
 18 | // GetDevelopmentInfoInput defines input parameters for jira_get_development_information tool.
 19 | type GetDevelopmentInfoInput struct {
 20 | 	IssueKey            string `json:"issue_key" validate:"required"`
 21 | 	IncludeBranches     bool   `json:"include_branches,omitempty"`
 22 | 	IncludePullRequests bool   `json:"include_pull_requests,omitempty"`
 23 | 	IncludeCommits      bool   `json:"include_commits,omitempty"`
 24 | 	IncludeBuilds       bool   `json:"include_builds,omitempty"`
 25 | }
 26 | 
 27 | // DevStatusResponse is the top-level response from /rest/dev-status/1.0/issue/detail endpoint.
 28 | // WARNING: This endpoint is undocumented and may change without notice.
 29 | type DevStatusResponse struct {
 30 | 	Errors []string          `json:"errors"`
 31 | 	Detail []DevStatusDetail `json:"detail"`
 32 | }
 33 | 
 34 | // DevStatusDetail contains development information from a single VCS integration.
 35 | // Multiple Detail entries may exist if the Jira instance has multiple VCS integrations
 36 | // (e.g., GitHub and Bitbucket).
 37 | type DevStatusDetail struct {
 38 | 	Branches        []Branch          `json:"branches,omitempty"`
 39 | 	PullRequests    []PullRequest     `json:"pullRequests,omitempty"`
 40 | 	Repositories    []Repository      `json:"repositories,omitempty"`
 41 | 	Builds          []Build           `json:"builds,omitempty"`
 42 | 	JswddBuildsData []JswddBuildsData `json:"jswddBuildsData,omitempty"`
 43 | }
 44 | 
 45 | // JswddBuildsData contains build information from cloud providers.
 46 | type JswddBuildsData struct {
 47 | 	Builds    []Build    `json:"builds,omitempty"`
 48 | 	Providers []Provider `json:"providers,omitempty"`
 49 | }
 50 | 
 51 | // Provider represents a CI/CD provider.
 52 | type Provider struct {
 53 | 	ID               string `json:"id"`
 54 | 	Name             string `json:"name"`
 55 | 	HomeURL          string `json:"homeUrl,omitempty"`
 56 | 	LogoURL          string `json:"logoUrl,omitempty"`
 57 | 	DocumentationURL string `json:"documentationUrl,omitempty"`
 58 | }
 59 | 
 60 | // Branch represents a Git branch linked to the Jira issue.
 61 | type Branch struct {
 62 | 	Name                 string           `json:"name"`
 63 | 	URL                  string           `json:"url"`
 64 | 	CreatePullRequestURL string           `json:"createPullRequestUrl,omitempty"`
 65 | 	Repository           RepositoryRef    `json:"repository"`
 66 | 	LastCommit           Commit           `json:"lastCommit"`
 67 | }
 68 | 
 69 | // PullRequest represents a pull/merge request linked to the Jira issue.
 70 | // Status values include: OPEN, MERGED, DECLINED, CLOSED.
 71 | type PullRequest struct {
 72 | 	ID             string     `json:"id"`
 73 | 	Name           string     `json:"name"`
 74 | 	URL            string     `json:"url"`
 75 | 	Status         string     `json:"status"`
 76 | 	Author         Author     `json:"author"`
 77 | 	LastUpdate     string     `json:"lastUpdate"`
 78 | 	Source         BranchRef  `json:"source"`
 79 | 	Destination    BranchRef  `json:"destination"`
 80 | 	CommentCount   int        `json:"commentCount,omitempty"`
 81 | 	Reviewers      []Reviewer `json:"reviewers,omitempty"`
 82 | 	RepositoryID   string     `json:"repositoryId,omitempty"`
 83 | 	RepositoryName string     `json:"repositoryName,omitempty"`
 84 | 	RepositoryURL  string     `json:"repositoryUrl,omitempty"`
 85 | }
 86 | 
 87 | // Repository represents a Git repository containing development work.
 88 | type Repository struct {
 89 | 	ID      string   `json:"id"`
 90 | 	Name    string   `json:"name"`
 91 | 	URL     string   `json:"url"`
 92 | 	Avatar  string   `json:"avatar,omitempty"`
 93 | 	Commits []Commit `json:"commits,omitempty"`
 94 | }
 95 | 
 96 | // RepositoryRef represents a lightweight repository reference used in branches.
 97 | type RepositoryRef struct {
 98 | 	ID   string `json:"id"`
 99 | 	Name string `json:"name"`
100 | 	URL  string `json:"url"`
101 | }
102 | 
103 | // Commit represents a Git commit linked to the Jira issue.
104 | type Commit struct {
105 | 	ID              string       `json:"id"`
106 | 	DisplayID       string       `json:"displayId"`
107 | 	Message         string       `json:"message"`
108 | 	Author          Author       `json:"author"`
109 | 	AuthorTimestamp string       `json:"authorTimestamp"`
110 | 	URL             string       `json:"url,omitempty"`
111 | 	FileCount       int          `json:"fileCount,omitempty"`
112 | 	Merge           bool         `json:"merge,omitempty"`
113 | 	Files           []CommitFile `json:"files,omitempty"`
114 | }
115 | 
116 | // CommitFile represents a file changed in a commit.
117 | type CommitFile struct {
118 | 	Path         string `json:"path"`
119 | 	URL          string `json:"url"`
120 | 	ChangeType   string `json:"changeType"`
121 | 	LinesAdded   int    `json:"linesAdded"`
122 | 	LinesRemoved int    `json:"linesRemoved"`
123 | }
124 | 
125 | // Author represents the author of a commit or pull request.
126 | type Author struct {
127 | 	Name   string `json:"name"`
128 | 	Email  string `json:"email,omitempty"`
129 | 	Avatar string `json:"avatar,omitempty"`
130 | }
131 | 
132 | // Reviewer represents a reviewer of a pull request.
133 | type Reviewer struct {
134 | 	Name     string `json:"name"`
135 | 	Avatar   string `json:"avatar,omitempty"`
136 | 	Approved bool   `json:"approved"`
137 | }
138 | 
139 | // BranchRef represents a branch reference used in pull requests.
140 | type BranchRef struct {
141 | 	Branch string `json:"branch"`
142 | 	URL    string `json:"url,omitempty"`
143 | }
144 | 
145 | // Build represents a CI/CD build linked to the Jira issue.
146 | // Status values include: successful, failed, in_progress, cancelled, unknown.
147 | type Build struct {
148 | 	ID            string            `json:"id"`
149 | 	Name          string            `json:"name,omitempty"`
150 | 	DisplayName   string            `json:"displayName,omitempty"`
151 | 	Description   string            `json:"description,omitempty"`
152 | 	URL           string            `json:"url"`
153 | 	State         string            `json:"state"`
154 | 	CreatedAt     string            `json:"createdAt,omitempty"`
155 | 	LastUpdated   string            `json:"lastUpdated"`
156 | 	BuildNumber   interface{}       `json:"buildNumber,omitempty"` // Can be string or int
157 | 	TestInfo      *BuildTestSummary `json:"testInfo,omitempty"`
158 | 	TestSummary   *BuildTestSummary `json:"testSummary,omitempty"`
159 | 	References    []BuildReference  `json:"references,omitempty"`
160 | 	PipelineID    string            `json:"pipelineId,omitempty"`
161 | 	PipelineName  string            `json:"pipelineName,omitempty"`
162 | 	ProviderID    string            `json:"providerId,omitempty"`
163 | 	ProviderType  string            `json:"providerType,omitempty"`
164 | 	ProviderAri   string            `json:"providerAri,omitempty"`
165 | 	RepositoryID  string            `json:"repositoryId,omitempty"`
166 | 	RepositoryName string           `json:"repositoryName,omitempty"`
167 | 	RepositoryURL string            `json:"repositoryUrl,omitempty"`
168 | }
169 | 
170 | // BuildTestSummary contains test execution statistics for a build.
171 | type BuildTestSummary struct {
172 | 	TotalNumber   int `json:"totalNumber"`
173 | 	NumberPassed  int `json:"numberPassed,omitempty"`
174 | 	SuccessNumber int `json:"successNumber,omitempty"`
175 | 	NumberFailed  int `json:"numberFailed,omitempty"`
176 | 	FailedNumber  int `json:"failedNumber,omitempty"`
177 | 	SkippedNumber int `json:"skippedNumber,omitempty"`
178 | }
179 | 
180 | // BuildReference represents a VCS reference (commit/branch) associated with a build.
181 | type BuildReference struct {
182 | 	Commit CommitRef `json:"commit,omitempty"`
183 | 	Ref    RefInfo   `json:"ref,omitempty"`
184 | }
185 | 
186 | // CommitRef represents a commit reference in a build.
187 | type CommitRef struct {
188 | 	ID            string `json:"id"`
189 | 	DisplayID     string `json:"displayId"`
190 | 	RepositoryURI string `json:"repositoryUri,omitempty"`
191 | }
192 | 
193 | // RefInfo represents a branch/tag reference in a build.
194 | type RefInfo struct {
195 | 	Name string `json:"name"`
196 | 	URI  string `json:"uri,omitempty"`
197 | }
198 | 
199 | // RegisterJiraDevelopmentTool registers the jira_get_development_information tool
200 | func RegisterJiraDevelopmentTool(s *server.MCPServer) {
201 | 	tool := mcp.NewTool("jira_get_development_information",
202 | 		mcp.WithDescription("Retrieve branches, pull requests, commits, and builds linked to a Jira issue via development tool integrations (GitHub, GitLab, Bitbucket, CI/CD providers). Returns human-readable formatted text showing all development work associated with the issue."),
203 | 		mcp.WithString("issue_key",
204 | 			mcp.Required(),
205 | 			mcp.Description("The Jira issue key (e.g., PROJ-123)")),
206 | 		mcp.WithBoolean("include_branches",
207 | 			mcp.Description("Include branches in the response (default: true)")),
208 | 		mcp.WithBoolean("include_pull_requests",
209 | 			mcp.Description("Include pull requests in the response (default: true)")),
210 | 		mcp.WithBoolean("include_commits",
211 | 			mcp.Description("Include commits in the response (default: true)")),
212 | 		mcp.WithBoolean("include_builds",
213 | 			mcp.Description("Include CI/CD builds in the response (default: true)")),
214 | 	)
215 | 	s.AddTool(tool, mcp.NewTypedToolHandler(jiraGetDevelopmentInfoHandler))
216 | }
217 | 
218 | // jiraGetDevelopmentInfoHandler retrieves development information for a Jira issue.
219 | // It uses a two-step approach:
220 | // 1. Call /rest/dev-status/latest/issue/summary to discover configured application types
221 | // 2. Call /rest/dev-status/latest/issue/detail with each applicationType to get full data
222 | //
223 | // WARNING: This uses undocumented /rest/dev-status/latest/ endpoints which may change without notice.
224 | // The detail endpoint REQUIRES the applicationType parameter (e.g., "GitLab", "GitHub", "Bitbucket").
225 | // Supported dataType values: repository, pullrequest, branch, build (but NOT deployment).
226 | func jiraGetDevelopmentInfoHandler(ctx context.Context, request mcp.CallToolRequest, input GetDevelopmentInfoInput) (*mcp.CallToolResult, error) {
227 | 	client := services.JiraClient()
228 | 
229 | 	// Default all filters to true if not explicitly set to false
230 | 	includeBranches := input.IncludeBranches
231 | 	includePRs := input.IncludePullRequests
232 | 	includeCommits := input.IncludeCommits
233 | 	includeBuilds := input.IncludeBuilds
234 | 
235 | 	// If all are false (omitted), default to true
236 | 	if !includeBranches && !includePRs && !includeCommits && !includeBuilds {
237 | 		includeBranches = true
238 | 		includePRs = true
239 | 		includeCommits = true
240 | 		includeBuilds = true
241 | 	}
242 | 
243 | 	// Step 1: Convert issue key to numeric ID
244 | 	// The dev-status endpoint requires numeric issue ID, not the issue key
245 | 	issue, response, err := client.Issue.Get(ctx, input.IssueKey, nil, []string{"id"})
246 | 	if err != nil {
247 | 		if response != nil && response.Code == 404 {
248 | 			return nil, fmt.Errorf("failed to retrieve development information: issue not found (endpoint: /rest/api/3/issue/%s)", input.IssueKey)
249 | 		}
250 | 		if response != nil && response.Code == 401 {
251 | 			return nil, fmt.Errorf("failed to retrieve development information: authentication failed (endpoint: /rest/api/3/issue/%s)", input.IssueKey)
252 | 		}
253 | 		return nil, fmt.Errorf("failed to retrieve issue: %w", err)
254 | 	}
255 | 
256 | 	// Step 2: Call summary endpoint to discover which application types are configured
257 | 	summaryEndpoint := fmt.Sprintf("/rest/dev-status/latest/issue/summary?issueId=%s", issue.ID)
258 | 	summaryReq, err := client.NewRequest(ctx, "GET", summaryEndpoint, "", nil)
259 | 	if err != nil {
260 | 		return nil, fmt.Errorf("failed to create summary request: %w", err)
261 | 	}
262 | 
263 | 	var summaryRespBytes json.RawMessage
264 | 	summaryCallResp, err := client.Call(summaryReq, &summaryRespBytes)
265 | 	if err != nil {
266 | 		if summaryCallResp != nil && summaryCallResp.Code == 401 {
267 | 			return nil, fmt.Errorf("authentication failed")
268 | 		}
269 | 		if summaryCallResp != nil && summaryCallResp.Code == 404 {
270 | 			errorResp := map[string]interface{}{
271 | 				"issueKey":     input.IssueKey,
272 | 				"error":        "Dev-status API endpoint not found",
273 | 			}
274 | 			yamlBytes, err := yaml.Marshal(errorResp)
275 | 			if err != nil {
276 | 				return nil, fmt.Errorf("failed to marshal error response to YAML: %w", err)
277 | 			}
278 | 			return mcp.NewToolResultText(string(yamlBytes)), nil
279 | 		}
280 | 		return nil, fmt.Errorf("failed to retrieve development summary: %w", err)
281 | 	}
282 | 
283 | 	// Parse summary with gjson and extract (appType, dataType) pairs
284 | 	parsed := gjson.ParseBytes(summaryRespBytes)
285 | 
286 | 	type endpointPair struct {
287 | 		appType  string
288 | 		dataType string
289 | 	}
290 | 	var endpointsToFetch []endpointPair
291 | 
292 | 	for _, dataType := range []string{"repository", "branch", "pullrequest", "build"} {
293 | 		parsed.Get(fmt.Sprintf("summary.%s.byInstanceType", dataType)).ForEach(func(appType, value gjson.Result) bool {
294 | 			endpointsToFetch = append(endpointsToFetch, endpointPair{appType.String(), dataType})
295 | 			return true // continue iteration
296 | 		})
297 | 	}
298 | 
299 | 	if len(endpointsToFetch) == 0 {
300 | 		emptyResp := map[string]interface{}{
301 | 			"issueKey":     input.IssueKey,
302 | 			"message":      "No development integrations found",
303 | 		}
304 | 		yamlBytes, err := yaml.Marshal(emptyResp)
305 | 		if err != nil {
306 | 			return nil, fmt.Errorf("failed to marshal empty response to YAML: %w", err)
307 | 		}
308 | 		return mcp.NewToolResultText(string(yamlBytes)), nil
309 | 	}
310 | 
311 | 	// Step 3: Call detail endpoint for each (appType, dataType) pair from summary
312 | 	var allDetails []DevStatusDetail
313 | 	for _, ep := range endpointsToFetch {
314 | 		endpoint := fmt.Sprintf("/rest/dev-status/latest/issue/detail?issueId=%s&applicationType=%s&dataType=%s", issue.ID, ep.appType, ep.dataType)
315 | 		req, err := client.NewRequest(ctx, "GET", endpoint, "", nil)
316 | 		if err != nil {
317 | 			continue
318 | 		}
319 | 
320 | 		var devStatusResponse DevStatusResponse
321 | 		_, err = client.Call(req, &devStatusResponse)
322 | 		if err != nil {
323 | 			continue
324 | 		}
325 | 
326 | 		if len(devStatusResponse.Errors) == 0 {
327 | 			allDetails = append(allDetails, devStatusResponse.Detail...)
328 | 		}
329 | 	}
330 | 
331 | 	// Step 4: Aggregate data from all VCS integrations
332 | 	// Multiple Detail entries can exist if Jira has multiple VCS integrations (GitHub + Bitbucket)
333 | 	var allBranches []Branch
334 | 	var allPullRequests []PullRequest
335 | 	var allRepositories []Repository
336 | 	var allBuilds []Build
337 | 
338 | 	for _, detail := range allDetails {
339 | 		allBranches = append(allBranches, detail.Branches...)
340 | 		allPullRequests = append(allPullRequests, detail.PullRequests...)
341 | 		allRepositories = append(allRepositories, detail.Repositories...)
342 | 		allBuilds = append(allBuilds, detail.Builds...)
343 | 		// Extract builds from jswddBuildsData (cloud-providers)
344 | 		for _, jswdd := range detail.JswddBuildsData {
345 | 			allBuilds = append(allBuilds, jswdd.Builds...)
346 | 		}
347 | 	}
348 | 
349 | 	// Apply filters and ensure empty arrays instead of nil
350 | 	filteredBranches := []Branch{}
351 | 	filteredPullRequests := []PullRequest{}
352 | 	filteredRepositories := []Repository{}
353 | 	filteredBuilds := []Build{}
354 | 
355 | 	if includeBranches && len(allBranches) > 0 {
356 | 		filteredBranches = allBranches
357 | 	}
358 | 	if includePRs && len(allPullRequests) > 0 {
359 | 		filteredPullRequests = allPullRequests
360 | 	}
361 | 	if includeCommits && len(allRepositories) > 0 {
362 | 		filteredRepositories = allRepositories
363 | 	}
364 | 	if includeBuilds && len(allBuilds) > 0 {
365 | 		filteredBuilds = allBuilds
366 | 	}
367 | 
368 | 	// Build YAML response
369 | 	result := map[string]interface{}{
370 | 		"issueKey":     input.IssueKey,
371 | 		"branches":     filteredBranches,
372 | 		"pullRequests": filteredPullRequests,
373 | 		"repositories": filteredRepositories,
374 | 		"builds":       filteredBuilds,
375 | 	}
376 | 
377 | 	yamlBytes, err := yaml.Marshal(result)
378 | 	if err != nil {
379 | 		return nil, fmt.Errorf("failed to marshal result to YAML: %w", err)
380 | 	}
381 | 
382 | 	return mcp.NewToolResultText(string(yamlBytes)), nil
383 | }
384 | 
```
Page 2/3FirstPrevNextLast