
Azure Devops
- 45 installs
- 6 repo stars
- Updated July 22, 2026
- julianobarbosa/claude-code-skills
Helps with devops & ci/cd tasks.
About
azure-devops is a Claude Code skill for devops & ci/cd. It helps solo builders move faster with AI-assisted development.
- azure-devops
- DevOps & CI/CD
- AI-coding skill
Azure Devops by the numbers
- 45 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #772 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianobarbosa/claude-code-skills --skill azure-devopsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 45 |
|---|---|
| repo stars | ★ 6 |
| Last updated | July 22, 2026 |
| Repository | julianobarbosa/claude-code-skills ↗ |
What it does
Helps with devops & ci/cd tasks.
Files
Azure DevOps REST API Skill
Comprehensive guide for Azure DevOps REST API v7.2 operations including work items, pipelines, repositories, test plans, wikis, and search functionality.
Quick Reference
| Area | Base URL | MCP Tool Prefix |
|---|---|---|
| Core | dev.azure.com/{org}/_apis/ | mcp__ado__core_* |
| Work Items | dev.azure.com/{org}/{project}/_apis/wit/ | mcp__ado__wit_* |
| Pipelines | dev.azure.com/{org}/{project}/_apis/pipelines/ | mcp__ado__pipelines_* |
| Git/Repos | dev.azure.com/{org}/{project}/_apis/git/ | mcp__ado__repo_* |
| Test Plans | dev.azure.com/{org}/{project}/_apis/testplan/ | mcp__ado__testplan_* |
| Wiki | dev.azure.com/{org}/{project}/_apis/wiki/ | mcp__ado__wiki_* |
| Search | almsearch.dev.azure.com/{org}/_apis/search/ | mcp__ado__search_* |
Authentication Methods
Personal Access Token (PAT)
# Base64 encode empty username with PAT
AUTH=$(echo -n ":${PAT}" | base64)
curl -H "Authorization: Basic ${AUTH}" https://dev.azure.com/{org}/_apis/projectsOAuth 2.0 Scopes
| Scope | Access Level |
|---|---|
vso.work | Read work items |
vso.work_write | Create/update work items |
vso.code | Read source code |
vso.code_write | Create branches, PRs |
vso.build_execute | Run pipelines |
vso.test | Read test plans |
vso.wiki | Read wikis |
API Versioning
Format: {major}.{minor}[-{stage}[.{resource-version}]]
- Current:
7.2-preview.3 - Example:
api-version=7.2-preview.3
---
1. Work Item Operations
Available MCP Tools
mcp__ado__wit_get_work_item - Get single work item
mcp__ado__wit_get_work_items_batch_by_ids - Get multiple work items
mcp__ado__wit_my_work_items - Get items assigned to me
mcp__ado__wit_create_work_item - Create new work item
mcp__ado__wit_update_work_item - Update work item fields
mcp__ado__wit_update_work_items_batch - Bulk update work items
mcp__ado__wit_add_work_item_comment - Add comment to work item
mcp__ado__wit_list_work_item_comments - List work item comments
mcp__ado__wit_add_child_work_items - Create child work items
mcp__ado__wit_work_items_link - Link work items together
mcp__ado__wit_work_item_unlink - Remove work item links
mcp__ado__wit_link_work_item_to_pull_request - Link to PR
mcp__ado__wit_add_artifact_link - Add artifact links (branch, commit, build)
mcp__ado__wit_get_work_item_type - Get work item type definition
mcp__ado__wit_list_backlogs - List team backlogs
mcp__ado__wit_list_backlog_work_items - Get backlog items
mcp__ado__wit_get_work_items_for_iteration - Get sprint items
mcp__ado__wit_get_query - Get saved query
mcp__ado__wit_get_query_results_by_id - Execute saved queryWIQL Query Syntax
Basic Structure
SELECT [Fields]
FROM workitems
WHERE [Conditions]
ORDER BY [Fields]
ASOF [DateTime]Common Macros
| Macro | Description |
|---|---|
@Me | Current user |
@project | Current project |
@Today | Today's date |
@Today - N | N days ago |
@CurrentIteration | Current sprint |
@StartOfMonth | First of month |
Example Queries
-- Active tasks assigned to me
SELECT [System.Id], [System.Title], [System.State]
FROM workitems
WHERE [System.TeamProject] = @project
AND [System.WorkItemType] = 'Task'
AND [System.State] = 'Active'
AND [System.AssignedTo] = @Me
ORDER BY [System.Priority] ASC
-- Bugs created in last 30 days
SELECT [System.Id], [System.Title], [System.CreatedDate]
FROM workitems
WHERE [System.TeamProject] = @project
AND [System.WorkItemType] = 'Bug'
AND [System.CreatedDate] >= @Today - 30
-- Parent-child hierarchy
SELECT [Source].[System.Id], [Target].[System.Id]
FROM workitemLinks
WHERE [Source].[System.TeamProject] = @project
AND [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward'
MODE (Recursive)JSON Patch Operations
[
{"op": "add", "path": "/fields/System.Title", "value": "New Title"},
{"op": "replace", "path": "/fields/System.State", "value": "Active"},
{"op": "add", "path": "/relations/-", "value": {
"rel": "System.LinkTypes.Related",
"url": "https://dev.azure.com/{org}/_apis/wit/workItems/{id}"
}}
]Link Types Reference
| Type | Rel Name |
|---|---|
| Parent | System.LinkTypes.Hierarchy-Reverse |
| Child | System.LinkTypes.Hierarchy-Forward |
| Related | System.LinkTypes.Related |
| Predecessor | System.LinkTypes.Dependency-Reverse |
| Successor | System.LinkTypes.Dependency-Forward |
---
2. Pipeline Operations
Available MCP Tools
mcp__ado__pipelines_get_build_definitions - List pipeline definitions
mcp__ado__pipelines_get_build_definition_revisions - Get definition history
mcp__ado__pipelines_get_builds - List builds
mcp__ado__pipelines_get_build_status - Get build status
mcp__ado__pipelines_get_build_log - Get build logs
mcp__ado__pipelines_get_build_log_by_id - Get specific log
mcp__ado__pipelines_get_build_changes - Get commits in build
mcp__ado__pipelines_run_pipeline - Trigger pipeline run
mcp__ado__pipelines_get_run - Get pipeline run details
mcp__ado__pipelines_list_runs - List pipeline runs
mcp__ado__pipelines_update_build_stage - Retry/cancel stagePipeline Trigger Example
{
"resources": {
"repositories": {
"self": {"refName": "refs/heads/feature-branch"}
}
},
"templateParameters": {
"environment": "staging"
},
"variables": {
"customVar": {"value": "custom-value", "isSecret": false}
}
}Build Status Values
| Status | Description |
|---|---|
none | Not started |
inProgress | Running |
completed | Finished |
cancelling | Being cancelled |
postponed | Delayed |
notStarted | Queued |
Build Result Values
| Result | Description |
|---|---|
succeeded | All tasks passed |
partiallySucceeded | Some tasks failed |
failed | Build failed |
canceled | User cancelled |
---
3. Repository Operations
Available MCP Tools
mcp__ado__repo_list_repos_by_project - List repositories
mcp__ado__repo_get_repo_by_name_or_id - Get repository details
mcp__ado__repo_list_branches_by_repo - List branches
mcp__ado__repo_list_my_branches_by_repo - List my branches
mcp__ado__repo_get_branch_by_name - Get branch details
mcp__ado__repo_create_branch - Create new branch
mcp__ado__repo_search_commits - Search commit history
mcp__ado__repo_list_pull_requests_by_repo_or_project - List PRs
mcp__ado__repo_get_pull_request_by_id - Get PR details
mcp__ado__repo_create_pull_request - Create PR
mcp__ado__repo_update_pull_request - Update PR (autocomplete, status)
mcp__ado__repo_update_pull_request_reviewers - Add/remove reviewers
mcp__ado__repo_list_pull_request_threads - List PR comments
mcp__ado__repo_list_pull_request_thread_comments - Get thread comments
mcp__ado__repo_create_pull_request_thread - Create comment thread
mcp__ado__repo_reply_to_comment - Reply to comment
mcp__ado__repo_resolve_comment - Resolve thread
mcp__ado__repo_list_pull_requests_by_commits - Find PR by commitPR Status Values
| Status | Description |
|---|---|
active | Open for review |
abandoned | Closed without merge |
completed | Merged |
Merge Strategies
| Strategy | Description |
|---|---|
noFastForward | Merge commit (default) |
squash | Squash commits |
rebase | Rebase and fast-forward |
rebaseMerge | Rebase with merge commit |
---
4. Test Plan Operations
Available MCP Tools
mcp__ado__testplan_list_test_plans - List test plans
mcp__ado__testplan_create_test_plan - Create test plan
mcp__ado__testplan_create_test_suite - Create test suite
mcp__ado__testplan_list_test_cases - List test cases in suite
mcp__ado__testplan_create_test_case - Create test case
mcp__ado__testplan_update_test_case_steps - Update test steps
mcp__ado__testplan_add_test_cases_to_suite - Add cases to suite
mcp__ado__testplan_show_test_results_from_build_id - Get test resultsTest Case Steps Format
1. Navigate to login page|Login page displayed
2. Enter username|Username field populated
3. Enter password|Password field populated
4. Click login button|User is logged in successfullyTest Suite Types
| Type | Description |
|---|---|
staticTestSuite | Manual hierarchy |
dynamicTestSuite | Query-based |
requirementTestSuite | Linked to requirement |
Test Outcome Values
| Outcome | Description |
|---|---|
Passed | Test passed |
Failed | Test failed |
Blocked | Cannot execute |
NotExecuted | Not run |
Inconclusive | No clear result |
---
5. Wiki Operations
Available MCP Tools
mcp__ado__wiki_list_wikis - List wikis
mcp__ado__wiki_get_wiki - Get wiki details
mcp__ado__wiki_list_pages - List wiki pages
mcp__ado__wiki_get_page - Get page metadata
mcp__ado__wiki_get_page_content - Get page content
mcp__ado__wiki_create_or_update_page - Create/update pageWiki Types
| Type | Description |
|---|---|
projectWiki | Project-scoped wiki |
codeWiki | Git-backed wiki |
Page Path Format
- Root:
/ - Subpage:
/Parent/Child - Spaces:
/My%20Page
---
6. Search Operations
Available MCP Tools
mcp__ado__search_code - Search source code
mcp__ado__search_workitem - Search work items
mcp__ado__search_wiki - Search wiki pagesCode Search Filters
{
"Project": ["project-name"],
"Repository": ["repo-name"],
"Path": ["/src"],
"Branch": ["main"],
"CodeElement": ["class", "def", "function"]
}Work Item Search Filters
{
"System.TeamProject": ["project-name"],
"System.WorkItemType": ["Bug", "Task"],
"System.State": ["Active", "New"],
"System.AssignedTo": ["user@domain.com"]
}---
7. Core Operations
Available MCP Tools
mcp__ado__core_list_projects - List projects
mcp__ado__core_list_project_teams - List teams
mcp__ado__core_get_identity_ids - Get user identity
mcp__ado__work_list_team_iterations - List iterations
mcp__ado__work_list_iterations - List all iterations
mcp__ado__work_create_iterations - Create iterations
mcp__ado__work_assign_iterations - Assign to team
mcp__ado__work_get_team_capacity - Get team capacity
mcp__ado__work_update_team_capacity - Update capacity
mcp__ado__work_get_iteration_capacities - Get iteration capacity---
8. Advanced Security Operations
Available MCP Tools
mcp__ado__advsec_get_alerts - Get security alerts
mcp__ado__advsec_get_alert_details - Get alert detailsAlert Types
| Type | Description |
|---|---|
Dependency | Vulnerable dependencies |
Secret | Exposed secrets |
Code | Code vulnerabilities |
---
Rate Limiting
TSTU (Throughput Unit) Limits
- Anonymous: 200 TSTUs/minute
- Authenticated: 1200 TSTUs/minute
Response Headers
X-RateLimit-Resource: core
X-RateLimit-Delay: 500
X-RateLimit-Limit: 1200
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1609459200
Retry-After: 30Handling Rate Limits
import time
import requests
def api_call_with_retry(url, headers, max_retries=3):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 30))
time.sleep(retry_after)
continue
return response
raise Exception("Rate limit exceeded after retries")---
Error Handling
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 204 | No Content (DELETE) |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 409 | Conflict (version) |
| 429 | Rate Limited |
| 500 | Server Error |
Error Response Format
{
"$id": "1",
"innerException": null,
"message": "Error description",
"typeName": "Microsoft.VisualStudio.Services.WebApi.VssServiceException",
"typeKey": "VssServiceException",
"errorCode": 0
}---
Common Field Reference
System Fields
| Field | Reference Name |
|---|---|
| ID | System.Id |
| Title | System.Title |
| State | System.State |
| Assigned To | System.AssignedTo |
| Area Path | System.AreaPath |
| Iteration Path | System.IterationPath |
| Work Item Type | System.WorkItemType |
| Created Date | System.CreatedDate |
| Changed Date | System.ChangedDate |
| Tags | System.Tags |
| Description | System.Description |
Scheduling Fields
| Field | Reference Name |
|---|---|
| Story Points | Microsoft.VSTS.Scheduling.Effort |
| Remaining Work | Microsoft.VSTS.Scheduling.RemainingWork |
| Original Estimate | Microsoft.VSTS.Scheduling.OriginalEstimate |
| Completed Work | Microsoft.VSTS.Scheduling.CompletedWork |
---
Multi-Repo Work Item Segregation
When a single Azure DevOps project contains multiple repositories, use Area Paths for primary segregation and hierarchical Iteration Paths for repo-scoped sprint/epic tracking.
Target Structure
Area Paths: Iteration Paths:
project\ project\Iteration\
├── repo-a (per-repo) ├── Sprint 1 (shared across repos)
├── repo-b (per-repo) ├── Sprint 2 (shared across repos)
└── repo-c (per-repo) ├── repo-a\ (repo-scoped container)
│ ├── epic-1-... (auto-created by sync)
│ └── epic-2-...
└── repo-b\ (repo-scoped container)Path Format Differences (Critical)
Azure DevOps uses two different path formats for iterations:
| Operation | Format | Example |
|---|---|---|
az boards iteration project create --path | \Project\Iteration\Parent (literal "Iteration" segment required) | \devops-team\Iteration\azure-quota-automation |
az boards work-item update --iteration | Project\Parent\Child (no "Iteration" segment, no leading \) | devops-team\azure-quota-automation\epic-1-slug |
Setup Commands for a New Repo
# 1. Create area path (if not exists)
az boards area project create --name "<repo-name>" --path "\<project>"
# 2. Create iteration container
az boards iteration project create --name "<repo-name>" --path "\<project>\Iteration"
# 3. Configure BMAD sync (devops-sync-config.yaml)
# areaPath: "<project>\\<repo-name>"
# iterationRootPath: "<repo-name>"Moving Iterations (No Native Move)
Azure DevOps CLI has no move command for iterations. To restructure: 1. Verify no work items assigned: az boards query --wiql "... WHERE [System.IterationPath] = '...'" 2. Delete: az boards iteration project delete --path "\project\Iteration\old-path" --yes 3. Recreate under new parent: az boards iteration project create --name "name" --path "\project\Iteration\new-parent" 4. Update state files with new iteration IDs
Common Error: TF401347
TF401347: The iteration path does not exist — caused by using the wrong path format. Check:
--iterationon work items usesProject\Parent\Child(no "Iteration" segment)--pathon iteration create uses\Project\Iteration\Parent(with "Iteration" segment)
---
Best Practices
1. Use Batch Operations
- Use
mcp__ado__wit_get_work_items_batch_by_idsinstead of multiple single calls - Max 200 items per batch request
2. Minimize Field Selection
- Only request fields you need:
fields=System.Id,System.Title,System.State - Reduces response size and API load
3. Handle Pagination
- Use
$topand$skipfor large result sets - Follow continuation tokens when provided
4. Version Control
- Use
testoperation in JSON Patch for optimistic concurrency - Always check
revfield before updates
5. Error Recovery
- Implement exponential backoff for rate limits
- Log correlation IDs from response headers
---
References
---
Gotchas
- REST API rate limits are per-org per-PAT — concurrent batch jobs share the budget. HTTP 429 returns retry-after but most clients ignore it.
- WIQL `[System.AssignedTo]` returns identity ref objects, not names — equality comparison needs
EVERoperator or a UPN-shaped string match. - Work-item rev-based optimistic concurrency: concurrent updates with stale
revfail silently (the update is dropped, not errored). - Pipeline YAML stage-less mode: a YAML with only
jobs:runs as one implicit stage, but APIs filtering by stage name return empty. - PAT scopes are union, not intersection — a "Read & Write" scope on Code doesn't grant Build access; missing scopes get 401 with vague "TF400813" error.
- Date macros (`@Today`, `@StartOfWeek`) work in some queries and not others — saved query in UI may not match WIQL via API.
Git Repositories API Reference
Comprehensive reference for Azure DevOps Git REST API operations.
Table of Contents
---
Repository Concepts
Repository Properties
| Property | Type | Description |
|---|---|---|
id | GUID | Unique repository identifier |
name | String | Repository name |
url | String | API URL |
project | Object | Parent project |
defaultBranch | String | Default branch (refs/heads/main) |
size | Long | Repository size in bytes |
remoteUrl | String | Clone URL |
sshUrl | String | SSH clone URL |
webUrl | String | Web portal URL |
isDisabled | Boolean | Whether repo is disabled |
isFork | Boolean | Whether repo is a fork |
Ref Format
refs/heads/main # Branch
refs/heads/feature/test # Feature branch
refs/tags/v1.0.0 # Tag
refs/pull/123/merge # PR merge ref---
Pull Request Operations
PR Status Values
| Status | Description |
|---|---|
active | Open for review |
abandoned | Closed without merge |
completed | Merged |
all | All statuses |
Merge Strategies
| Strategy | Description |
|---|---|
noFastForward | Create merge commit (default) |
squash | Squash all commits into one |
rebase | Rebase source onto target |
rebaseMerge | Rebase with merge commit |
PR Vote Values
| Vote | Value | Description |
|---|---|---|
| Approved | 10 | Approved |
| Approved with suggestions | 5 | Approved with comments |
| No vote | 0 | No vote (reset) |
| Waiting for author | -5 | Needs work |
| Rejected | -10 | Rejected |
PR Completion Options
{
"autoCompleteSetBy": {
"id": "user-guid"
},
"completionOptions": {
"deleteSourceBranch": true,
"mergeCommitMessage": "Merged PR #123: Feature description",
"mergeStrategy": "squash",
"bypassPolicy": false,
"bypassReason": "",
"transitionWorkItems": true,
"squashMerge": true
}
}---
Branch Policies
Policy Types
| Type ID | Name | Description |
|---|---|---|
fa4e907d-c16b-4a4c-9dfa-4916e5d171ab | Minimum reviewers | Required approvals |
c6a1889d-b943-4856-b76f-9e46bb6b0df2 | Work item linking | Require linked work items |
0609b952-1397-4640-95ec-e00a01b2c241 | Comment requirements | Resolve all comments |
40e92b44-2fe1-4dd6-b3d8-74a9c21d0c6e | Merge strategy | Allowed merge types |
7ed39669-655c-494e-b4a0-a08b4da0fcce | Build validation | Required build to pass |
cbdc66da-9728-4af8-aada-9a5a32e4a226 | Status | Required external status |
ca93de9d-e26b-4dc5-9e97-4f76a0ff1ae5 | Required reviewers | Auto-add reviewers |
fd2167ab-b0be-447a-8ec8-39368250530e | File size | Max file size restriction |
001bf6b8-c251-4a78-b09e-c9b6b3f8b75a | File path | Path-based restrictions |
Policy Configuration Example
{
"isEnabled": true,
"isBlocking": true,
"type": {
"id": "fa4e907d-c16b-4a4c-9dfa-4916e5d171ab"
},
"settings": {
"minimumApproverCount": 2,
"creatorVoteCounts": false,
"allowDownvotes": false,
"resetOnSourcePush": true,
"scope": [
{
"refName": "refs/heads/main",
"matchKind": "exact",
"repositoryId": "repo-guid"
}
]
}
}---
Git Refs and Commits
Ref Update Operation
| Operation | Old Object ID | New Object ID |
|---|---|---|
| Create | 0000000...000 | New commit SHA |
| Update | Current SHA | New SHA |
| Delete | Current SHA | 0000000...000 |
Commit Properties
| Property | Type | Description |
|---|---|---|
commitId | String | Full SHA |
author | GitUserDate | Author info |
committer | GitUserDate | Committer info |
comment | String | Commit message |
commentTruncated | Boolean | Message truncated |
changeCounts | Object | Add/Edit/Delete counts |
url | String | API URL |
remoteUrl | String | Web URL |
parents | Array | Parent commit SHAs |
push | Object | Push details |
statuses | Array | Commit statuses |
Change Types
| Type | Description |
|---|---|
add | New file added |
edit | Existing file modified |
delete | File deleted |
rename | File renamed |
sourceRename | Source of rename |
targetRename | Target of rename |
---
Code Review
Thread Status
| Status | Description |
|---|---|
Unknown | Unknown status |
Active | Open thread |
Fixed | Marked as fixed |
WontFix | Won't fix |
Closed | Closed |
ByDesign | By design |
Pending | Pending review |
Comment Types
| Type | Description |
|---|---|
Unknown | Unknown type |
Text | Regular comment |
CodeChange | Code suggestion |
System | System-generated |
Thread Context
{
"filePath": "/src/main.cs",
"rightFileStart": {
"line": 10,
"offset": 1
},
"rightFileEnd": {
"line": 15,
"offset": 50
},
"leftFileStart": null,
"leftFileEnd": null
}---
MCP Tool Usage Examples
List Repositories
# Using mcp__ado__repo_list_repos_by_project
params = {
"project": "MyProject",
"repoNameFilter": "api", # Optional filter
"top": 100
}Get Repository
# Using mcp__ado__repo_get_repo_by_name_or_id
params = {
"project": "MyProject",
"repositoryNameOrId": "my-repo"
}Create Branch
# Using mcp__ado__repo_create_branch
params = {
"repositoryId": "repo-guid",
"branchName": "feature/new-feature",
"sourceBranchName": "main"
}List Branches
# Using mcp__ado__repo_list_branches_by_repo
params = {
"repositoryId": "repo-guid",
"filterContains": "feature",
"top": 50
}Search Commits
# Using mcp__ado__repo_search_commits
params = {
"project": "MyProject",
"repository": "my-repo",
"searchText": "fix bug",
"author": "developer@company.com",
"fromDate": "2024-01-01T00:00:00Z",
"toDate": "2024-12-31T23:59:59Z",
"includeWorkItems": True,
"top": 20
}Create Pull Request
# Using mcp__ado__repo_create_pull_request
params = {
"repositoryId": "repo-guid",
"sourceRefName": "refs/heads/feature/new-feature",
"targetRefName": "refs/heads/main",
"title": "Add new feature",
"description": "## Summary\nThis PR adds...\n\n## Testing\n- [ ] Unit tests\n- [ ] Integration tests",
"isDraft": False,
"workItems": "12345 12346" # Space-separated IDs
}Update Pull Request
# Using mcp__ado__repo_update_pull_request
params = {
"repositoryId": "repo-guid",
"pullRequestId": 123,
"title": "Updated title",
"description": "Updated description",
"autoComplete": True,
"mergeStrategy": "Squash",
"deleteSourceBranch": True,
"transitionWorkItems": True
}Add/Remove Reviewers
# Using mcp__ado__repo_update_pull_request_reviewers
params = {
"repositoryId": "repo-guid",
"pullRequestId": 123,
"reviewerIds": ["user-guid-1", "user-guid-2"],
"action": "add" # or "remove"
}List PR Threads
# Using mcp__ado__repo_list_pull_request_threads
params = {
"repositoryId": "repo-guid",
"pullRequestId": 123,
"top": 100
}Create Comment Thread
# Using mcp__ado__repo_create_pull_request_thread
params = {
"repositoryId": "repo-guid",
"pullRequestId": 123,
"content": "Please review this logic",
"filePath": "/src/main.cs",
"rightFileStartLine": 10,
"rightFileEndLine": 15,
"status": "Active"
}Reply to Comment
# Using mcp__ado__repo_reply_to_comment
params = {
"repositoryId": "repo-guid",
"pullRequestId": 123,
"threadId": 456,
"content": "Good catch, I'll fix this."
}Resolve Thread
# Using mcp__ado__repo_resolve_comment
params = {
"repositoryId": "repo-guid",
"pullRequestId": 123,
"threadId": 456
}Find PRs by Commit
# Using mcp__ado__repo_list_pull_requests_by_commits
params = {
"project": "MyProject",
"repository": "my-repo",
"commits": ["abc123def456", "789ghi012jkl"],
"queryType": "LastMergeCommit"
}---
Git URL Patterns
Clone URLs
# HTTPS
https://dev.azure.com/{org}/{project}/_git/{repo}
# SSH
git@ssh.dev.azure.com:v3/{org}/{project}/{repo}
# With PAT
https://{pat}@dev.azure.com/{org}/{project}/_git/{repo}Web URLs
# Repository home
https://dev.azure.com/{org}/{project}/_git/{repo}
# Specific branch
https://dev.azure.com/{org}/{project}/_git/{repo}?version=GB{branch}
# Specific file
https://dev.azure.com/{org}/{project}/_git/{repo}?path=/path/to/file&version=GB{branch}
# Commit
https://dev.azure.com/{org}/{project}/_git/{repo}/commit/{commitId}
# Pull request
https://dev.azure.com/{org}/{project}/_git/{repo}/pullrequest/{prId}
# Compare
https://dev.azure.com/{org}/{project}/_git/{repo}/branchCompare?baseVersion=GB{base}&targetVersion=GB{target}Pipelines and Build API Reference
Comprehensive reference for Azure DevOps Pipelines REST API operations.
Table of Contents
- Pipeline Concepts
- Build Status and Results
- YAML Pipeline Reference
- Pipeline Variables
- Triggers and Resources
---
Pipeline Concepts
Pipeline Types
| Type | Description | Definition Location |
|---|---|---|
| YAML Pipeline | Code-as-config pipeline | Repository (azure-pipelines.yml) |
| Classic Build | UI-defined build pipeline | Azure DevOps |
| Classic Release | UI-defined release pipeline | Azure DevOps |
Pipeline Hierarchy
Pipeline Definition
└── Stages
└── Jobs
└── Steps (Tasks/Scripts)Run vs Build
- Pipeline Run: Modern YAML pipeline execution (Pipelines API)
- Build: Classic build or YAML execution (Build API)
---
Build Status and Results
Build Status Values
| Status | Value | Description |
|---|---|---|
| None | 0 | No status |
| In Progress | 1 | Currently running |
| Completed | 2 | Finished execution |
| Cancelling | 4 | Being cancelled |
| Postponed | 8 | Execution postponed |
| Not Started | 32 | Queued but not started |
| All | 47 | All statuses |
Build Result Values
| Result | Value | Description |
|---|---|---|
| None | 0 | No result yet |
| Succeeded | 2 | All tasks passed |
| Partially Succeeded | 4 | Some tasks failed (continue on error) |
| Failed | 8 | Build failed |
| Canceled | 32 | User or system cancelled |
Build Reason Values
| Reason | Value | Description |
|---|---|---|
| None | 0 | No reason |
| Manual | 1 | Manual trigger |
| Individual CI | 2 | Continuous integration |
| Batch CI | 4 | Batched CI trigger |
| Schedule | 8 | Scheduled trigger |
| User Created | 32 | Created by user |
| Validate Shelveset | 64 | Shelveset validation |
| Check In Shelveset | 128 | Shelveset check-in |
| Pull Request | 256 | PR trigger |
| Build Completion | 512 | Build completion trigger |
| Resource Trigger | 1024 | Resource trigger |
---
YAML Pipeline Reference
Basic Structure
# azure-pipelines.yml
trigger:
branches:
include:
- main
- release/*
exclude:
- feature/experimental/*
paths:
include:
- src/*
exclude:
- docs/*
pr:
branches:
include:
- main
autoCancel: true
pool:
vmImage: 'ubuntu-latest'
variables:
- name: buildConfiguration
value: 'Release'
- group: 'production-secrets'
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildJob
displayName: 'Build Application'
steps:
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: 'build'
projects: '**/*.csproj'Stage Template
stages:
- stage: stageName
displayName: 'Stage Display Name'
dependsOn: [previousStage]
condition: succeeded()
variables:
stageVar: 'value'
jobs:
- job: jobName
# Job definitionJob Template
jobs:
- job: JobName
displayName: 'Job Display Name'
pool:
vmImage: 'ubuntu-latest'
timeoutInMinutes: 60
cancelTimeoutInMinutes: 5
dependsOn: [PreviousJob]
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
continueOnError: false
strategy:
matrix:
linux:
imageName: 'ubuntu-latest'
windows:
imageName: 'windows-latest'
maxParallel: 2
steps:
- script: echo HelloDeployment Job
jobs:
- deployment: DeploymentName
displayName: 'Deploy to Production'
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: drop
- script: ./deploy.sh---
Pipeline Variables
Predefined Variables
| Variable | Description |
|---|---|
Build.BuildId | Unique build ID |
Build.BuildNumber | Build number/name |
Build.DefinitionName | Pipeline name |
Build.SourceBranch | Source branch (refs/heads/main) |
Build.SourceBranchName | Branch name only (main) |
Build.SourceVersion | Commit SHA |
Build.Repository.Name | Repository name |
Build.Repository.Uri | Repository URI |
Build.RequestedFor | User who triggered |
Build.Reason | Trigger reason |
System.TeamProject | Project name |
System.DefaultWorkingDirectory | Working directory |
Pipeline.Workspace | Pipeline workspace path |
Agent.Name | Agent name |
Agent.MachineName | Machine name |
Agent.OS | Operating system |
Agent.TempDirectory | Temp directory |
Agent.ToolsDirectory | Tools directory |
Variable Syntax
# Macro syntax (compile-time)
variables:
myVar: 'value'
steps:
- script: echo $(myVar)
# Template expression (compile-time)
steps:
- script: echo ${{ variables.myVar }}
# Runtime expression
steps:
- script: echo $[variables.myVar]
# Environment variable (Bash)
steps:
- script: echo $MYVAR
env:
MYVAR: $(myVar)
# PowerShell
steps:
- powershell: Write-Host $env:MYVAR
env:
MYVAR: $(myVar)Variable Groups
variables:
- group: 'my-variable-group'
- name: localVar
value: 'local value'Secret Variables
variables:
- name: mySecret
value: $(secretFromLibrary) # From variable group
steps:
- script: |
echo "Using secret (masked in logs)"
curl -H "Authorization: Bearer $(mySecret)" https://api.example.com
env:
MY_SECRET: $(mySecret)---
Triggers and Resources
CI Triggers
# Branch triggers
trigger:
batch: true # Batch changes
branches:
include:
- main
- release/*
exclude:
- feature/experimental/*
paths:
include:
- src/*
exclude:
- '**/*.md'
tags:
include:
- v*
exclude:
- v0.*
# Disable CI trigger
trigger: nonePR Triggers
pr:
autoCancel: true
drafts: false
branches:
include:
- main
- release/*
paths:
include:
- src/*
exclude:
- docs/*Scheduled Triggers
schedules:
- cron: '0 0 * * *' # Daily at midnight UTC
displayName: 'Daily Build'
branches:
include:
- main
always: false # Only if changes
- cron: '0 12 * * 0' # Weekly Sunday noon
displayName: 'Weekly Build'
branches:
include:
- main
always: true # Always runPipeline Resources
resources:
repositories:
- repository: templates
type: git
name: ProjectName/TemplateRepo
ref: refs/heads/main
- repository: external
type: github
name: org/repo
endpoint: 'GitHub Connection'
pipelines:
- pipeline: upstream
source: 'Upstream-Pipeline'
trigger:
branches:
include:
- main
containers:
- container: build-container
image: mcr.microsoft.com/dotnet/sdk:6.0
options: --privileged
packages:
- package: myPackage
type: npm
connection: 'npm-connection'
name: '@scope/package'
version: 1.0.0---
MCP Tool Usage Examples
List Build Definitions
# Using mcp__ado__pipelines_get_build_definitions
params = {
"project": "MyProject",
"includeLatestBuilds": True,
"queryOrder": "LastModifiedDescending",
"top": 50
}Get Builds
# Using mcp__ado__pipelines_get_builds
params = {
"project": "MyProject",
"definitions": [123, 456], # Definition IDs
"statusFilter": 2, # Completed
"resultFilter": 8, # Failed
"queryOrder": "FinishTimeDescending",
"top": 20
}Run Pipeline
# Using mcp__ado__pipelines_run_pipeline
params = {
"project": "MyProject",
"pipelineId": 123,
"resources": {
"repositories": {
"self": {
"refName": "refs/heads/feature-branch"
}
}
},
"templateParameters": {
"environment": "staging",
"runTests": "true"
},
"variables": {
"customVar": {
"value": "custom-value",
"isSecret": False
}
},
"stagesToSkip": ["Deploy-Production"]
}Get Build Logs
# Using mcp__ado__pipelines_get_build_log
params = {
"project": "MyProject",
"buildId": 12345
}
# Get specific log
# Using mcp__ado__pipelines_get_build_log_by_id
params = {
"project": "MyProject",
"buildId": 12345,
"logId": 5,
"startLine": 100,
"endLine": 200
}Retry Failed Stage
# Using mcp__ado__pipelines_update_build_stage
params = {
"project": "MyProject",
"buildId": 12345,
"stageName": "Deploy",
"status": "Retry",
"forceRetryAllJobs": True
}Get Pipeline Run
# Using mcp__ado__pipelines_get_run
params = {
"project": "MyProject",
"pipelineId": 123,
"runId": 456
}---
Common Pipeline Tasks
Checkout
steps:
- checkout: self
clean: true
fetchDepth: 0
lfs: true
submodules: recursive
persistCredentials: trueDownload Artifacts
steps:
- download: current
artifact: 'drop'
patterns: '**/*.dll'
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'specific'
project: 'ProjectGuid'
definition: '123'
buildVersionToDownload: 'latest'
artifactName: 'drop'
targetPath: '$(Pipeline.Workspace)/artifacts'Publish Artifacts
steps:
- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)'
artifact: 'drop'
publishLocation: 'pipeline'Cache
steps:
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
restoreKeys: |
npm | "$(Agent.OS)"
path: $(npm_config_cache)
displayName: 'Cache npm packages'Test Plans API Reference
Comprehensive reference for Azure DevOps Test Plans REST API operations.
Table of Contents
---
Test Plan Hierarchy
Structure
Test Plan
└── Test Suite (Root)
├── Test Suite (Static)
│ └── Test Cases
├── Test Suite (Requirements-based)
│ └── Test Cases (auto-linked)
└── Test Suite (Query-based)
└── Test Cases (dynamic)Test Suite Types
| Type | ID | Description |
|---|---|---|
| Static | staticTestSuite | Manual hierarchy of test cases |
| Requirements-based | requirementTestSuite | Linked to a requirement work item |
| Query-based | dynamicTestSuite | Test cases from WIQL query |
Test Plan States
| State | Description |
|---|---|
active | Currently active plan |
inactive | Archived/completed plan |
---
Test Case Management
Test Case Fields
| Field | Reference Name | Description |
|---|---|---|
| ID | System.Id | Unique identifier |
| Title | System.Title | Test case name |
| State | System.State | Design/Ready/Closed |
| Priority | Microsoft.VSTS.Common.Priority | Priority (1-4) |
| Steps | Microsoft.VSTS.TCM.Steps | Test steps XML |
| Expected Result | (in steps) | Expected outcomes |
| Automated | Microsoft.VSTS.TCM.AutomationStatus | Automation status |
| Automation Test | Microsoft.VSTS.TCM.AutomatedTestName | Automated test name |
| Test Suite | - | Parent suite(s) |
| Area Path | System.AreaPath | Area classification |
| Iteration Path | System.IterationPath | Iteration |
| Parameters | - | Shared parameters |
| Local Data Source | Microsoft.VSTS.TCM.LocalDataSource | Data-driven source |
Test Steps Format
1. Step action|Expected result
2. Another step|Another expected result
3. Validate data|Data is correctTest Steps XML Structure
<steps id="0" last="3">
<step id="1" type="ActionStep">
<parameterizedString isFormatted="true">
<P>Navigate to login page</P>
</parameterizedString>
<parameterizedString isFormatted="true">
<P>Login page is displayed</P>
</parameterizedString>
</step>
<step id="2" type="ActionStep">
<parameterizedString isFormatted="true">
<P>Enter username @username</P>
</parameterizedString>
<parameterizedString isFormatted="true">
<P>Username field populated</P>
</parameterizedString>
</step>
</steps>Test Case States
| State | Description |
|---|---|
Design | Being designed |
Ready | Ready for execution |
Closed | Completed/obsolete |
Automation Status
| Status | Description |
|---|---|
Not Automated | Manual test |
Planned | Automation planned |
Automated | Has associated automated test |
---
Test Runs and Results
Test Run States
| State | Description |
|---|---|
Unspecified | Not specified |
NotStarted | Not yet started |
InProgress | Currently running |
Completed | Finished |
Aborted | Was aborted |
Waiting | Waiting for resources |
NeedsInvestigation | Requires investigation |
Test Run Types
| Type | Description |
|---|---|
Manual | Manual test execution |
Automated | Automated test run |
NoConfigRun | Run without configuration |
Test Outcome Values
| Outcome | ID | Description |
|---|---|---|
| None | 0 | No outcome |
| Passed | 2 | Test passed |
| Failed | 3 | Test failed |
| Inconclusive | 4 | No clear result |
| Timeout | 5 | Timed out |
| Aborted | 6 | Was aborted |
| Blocked | 7 | Could not execute |
| NotExecuted | 8 | Not run |
| Warning | 9 | Passed with warnings |
| Error | 10 | Error during execution |
| NotApplicable | 11 | Not applicable |
| Paused | 12 | Execution paused |
| InProgress | 13 | Currently running |
| NotImpacted | 14 | Not impacted by changes |
Test Result Fields
| Field | Type | Description |
|---|---|---|
id | Int | Result ID |
testCaseTitle | String | Test case name |
outcome | String | Pass/Fail/etc. |
state | String | Result state |
durationInMs | Long | Execution time |
errorMessage | String | Error if failed |
stackTrace | String | Stack trace |
comment | String | Tester comments |
runBy | Identity | Who ran the test |
completedDate | DateTime | Completion time |
configuration | Object | Test configuration |
build | Object | Associated build |
release | Object | Associated release |
---
Test Configurations
Configuration Properties
| Property | Type | Description |
|---|---|---|
id | Int | Configuration ID |
name | String | Configuration name |
description | String | Description |
isDefault | Boolean | Default configuration |
state | String | Active/Inactive |
values | Array | Variable values |
Configuration Variables
{
"name": "Windows 11 Chrome",
"description": "Test on Windows 11 with Chrome browser",
"values": [
{"name": "Operating System", "value": "Windows 11"},
{"name": "Browser", "value": "Chrome"},
{"name": "Browser Version", "value": "Latest"}
]
}---
Test Points
Test Point Concept
A Test Point is the combination of:
- Test Case
- Test Configuration
- Test Suite
Test Point Properties
| Property | Type | Description |
|---|---|---|
id | Int | Point ID |
testCase | Object | Test case reference |
configuration | Object | Configuration |
suite | Object | Test suite |
assignedTo | Identity | Assigned tester |
outcome | String | Last outcome |
state | String | Point state |
lastRunBuildNumber | String | Last run build |
lastResultState | String | Last result state |
lastTestRun | Object | Last run reference |
lastResultDetails | Object | Last result |
Test Point Assignment
{
"id": 123,
"testCase": {"id": 456},
"configuration": {"id": 1},
"assignedTo": {
"displayName": "Test User",
"uniqueName": "user@domain.com"
}
}---
MCP Tool Usage Examples
List Test Plans
# Using mcp__ado__testplan_list_test_plans
params = {
"project": "MyProject",
"filterActivePlans": True,
"includePlanDetails": True
}Create Test Plan
# Using mcp__ado__testplan_create_test_plan
params = {
"project": "MyProject",
"name": "Q1 2024 Release Testing",
"iteration": "MyProject\\Release 1.0",
"areaPath": "MyProject\\Team A",
"description": "Test plan for Q1 2024 release",
"startDate": "2024-01-01",
"endDate": "2024-03-31"
}Create Test Suite
# Using mcp__ado__testplan_create_test_suite
params = {
"project": "MyProject",
"planId": 123,
"parentSuiteId": 456, # Root suite ID if top-level
"name": "Login Feature Tests"
}Create Test Case
# Using mcp__ado__testplan_create_test_case
params = {
"project": "MyProject",
"title": "Verify successful login with valid credentials",
"priority": 1,
"areaPath": "MyProject\\Team A",
"iterationPath": "MyProject\\Sprint 1",
"steps": """1. Navigate to login page|Login page is displayed
2. Enter valid username in the username field|Username is entered
3. Enter valid password in the password field|Password is masked and entered
4. Click the Login button|User is redirected to dashboard
5. Verify user name is displayed|User's name appears in header""",
"testsWorkItemId": 789 # Optional: link to user story/requirement
}Update Test Case Steps
# Using mcp__ado__testplan_update_test_case_steps
params = {
"id": 12345,
"steps": """1. Navigate to login page|Login page is displayed
2. Enter valid username|Username field populated
3. Enter valid password|Password field shows masked characters
4. Click Login button|System processes credentials
5. Verify dashboard loads|Dashboard is displayed with user info"""
}Add Test Cases to Suite
# Using mcp__ado__testplan_add_test_cases_to_suite
params = {
"project": "MyProject",
"planId": 123,
"suiteId": 456,
"testCaseIds": ["12345", "12346", "12347"]
}List Test Cases in Suite
# Using mcp__ado__testplan_list_test_cases
params = {
"project": "MyProject",
"planid": 123,
"suiteid": 456
}Get Test Results from Build
# Using mcp__ado__testplan_show_test_results_from_build_id
params = {
"project": "MyProject",
"buildid": 78901
}---
Test Plan Best Practices
1. Structure Test Plans by Release/Sprint
Release 2.0 Test Plan
├── Sprint 1 Tests
│ ├── Feature A Tests
│ └── Feature B Tests
├── Sprint 2 Tests
│ └── Feature C Tests
├── Regression Tests
└── Performance Tests2. Use Requirements-Based Suites
Link test suites to user stories for traceability:
User Story: As a user, I want to login...
└── Requirements Suite
├── TC: Valid login
├── TC: Invalid password
├── TC: Account locked
└── TC: Forgot password3. Parameterize Test Cases
Use shared parameters for data-driven testing:
@username = testuser1, testuser2, admin
@password = valid123, Valid456, Admin7894. Configure Multiple Configurations
Test across environments:
| Config | OS | Browser |
|---|---|---|
| Config 1 | Windows 11 | Chrome |
| Config 2 | Windows 11 | Firefox |
| Config 3 | macOS | Safari |
| Config 4 | Ubuntu | Chrome |
5. Link to Automation
{
"automationStatus": "Automated",
"automatedTestName": "MyTests.LoginTests.ValidLoginTest",
"automatedTestStorage": "MyTests.dll",
"automatedTestType": "Unit Test"
}Wiki and Search API Reference
Comprehensive reference for Azure DevOps Wiki and Search REST API operations.
Table of Contents
---
Wiki Operations
Wiki Types
| Type | Description | Storage |
|---|---|---|
projectWiki | Project-scoped wiki | Dedicated Git repo |
codeWiki | Repository-based wiki | Existing Git repo |
Wiki Properties
| Property | Type | Description |
|---|---|---|
id | GUID | Wiki identifier |
name | String | Wiki name |
projectId | GUID | Parent project |
repositoryId | GUID | Backing Git repo |
type | String | projectWiki or codeWiki |
mappedPath | String | Path in repo (codeWiki) |
version | Object | Branch/version info |
url | String | API URL |
remoteUrl | String | Web URL |
Page Path Format
| Path | Description |
|---|---|
/ | Root/home page |
/Getting-Started | Top-level page |
/Guides/Setup | Nested page |
/API/v1/Users | Deep nesting |
/My%20Page%20Name | URL-encoded spaces |
Page Properties
| Property | Type | Description |
|---|---|---|
id | Int | Page ID |
path | String | Page path |
content | String | Markdown content |
gitItemPath | String | Git file path |
subPages | Array | Child pages |
order | Int | Sort order |
url | String | API URL |
remoteUrl | String | Web URL |
isParentPage | Boolean | Has children |
Wiki Markdown Extensions
# Standard Markdown
**bold** *italic* `code`
# Azure DevOps Extensions
[[_TOC_]] # Table of contents
[[/Page/Path]] # Wiki page link
[[/Page/Path|Display Text]] # Named wiki link
# Work Item Links
#1234 # Work item link
AB#1234 # Cross-project work item
# Mentions
@<user-guid> # User mention
@<group-name> # Group mention
# Pull Request Links
!123 # PR link
# Code Snippets
:::code language="csharp" source="path/to/file.cs" range="1-10":::
# Mermaid Diagrams
:::mermaid
graph TD
A --> B
:::
# Math (LaTeX)
$$E = mc^2$$
$inline math$---
Code Search
Search Syntax
| Syntax | Description | Example |
|---|---|---|
keyword | Simple search | login |
"exact phrase" | Exact match | "public void Login" |
field:value | Field filter | repo:MyRepo |
NOT term | Exclusion | login NOT test |
term1 AND term2 | Both required | login AND password |
term1 OR term2 | Either term | config OR settings |
* | Wildcard | log* matches login, logout |
? | Single char | te?t matches test, text |
Code Search Filters
| Filter | Description | Example |
|---|---|---|
proj: | Project name | proj:MyProject |
repo: | Repository name | repo:Backend |
path: | File path | path:src/api |
file: | File name | file:config.json |
ext: | File extension | ext:cs |
branch: | Branch name | branch:main |
lang: | Language | lang:csharp |
Code Element Filters
| Filter | Description |
|---|---|
class: | Class definition |
struct: | Struct definition |
enum: | Enum definition |
interface: | Interface definition |
method: | Method/function |
property: | Property definition |
field: | Field definition |
comment: | In comments |
string: | In string literals |
Example Code Searches
# Find class definitions
class:UserService
# Find methods with specific name
method:ValidateCredentials
# Find in specific repo and path
repo:Backend path:src/api login
# Find specific file type
ext:cs "async Task" repo:MyRepo
# Complex query
repo:Backend path:src NOT path:test method:Login---
Work Item Search
Work Item Search Filters
| Filter | Description | Example |
|---|---|---|
t: | Work item type | t:Bug |
s: | State | s:Active |
a: | Assigned to | a:@Me |
c: | Created by | c:"John Smith" |
project: | Project | project:MyProject |
area: | Area path | area:"MyProject\Team" |
iteration: | Iteration | iteration:@CurrentIteration |
tags: | Tags | tags:urgent |
Date Filters
| Filter | Description |
|---|---|
created: | Created date |
changeddate: | Last changed |
resolveddate: | Resolved date |
closeddate: | Closed date |
Date Syntax
created:>=2024-01-01
changeddate:<=2024-06-30
created:2024-01-01..2024-03-31
created:@Today-30Example Work Item Searches
# Active bugs assigned to me
t:Bug s:Active a:@Me
# High priority items
t:Task priority:<=2 s:Active
# Recently created in current sprint
created:@Today-7 iteration:@CurrentIteration
# Specific area path
area:"MyProject\Backend Team" t:User Story
# With specific tag
tags:security t:Bug s:Active
# Created by specific user
c:"john.doe@company.com" created:>=2024-01-01---
Wiki Search
Wiki Search Filters
| Filter | Description | Example |
|---|---|---|
project: | Project | project:MyProject |
wiki: | Wiki name | wiki:"Team Wiki" |
path: | Page path | path:/Guides |
Example Wiki Searches
# Search in specific wiki
wiki:"Developer Wiki" API
# Search specific path
path:/Architecture microservices
# Multiple terms
"getting started" installation project:MyProject---
Search Filters
Filter Structure (API)
{
"searchText": "search query",
"filters": {
"Project": ["Project1", "Project2"],
"Repository": ["Repo1"],
"Path": ["/src"],
"Branch": ["main", "develop"],
"CodeElement": ["class", "method"]
},
"$skip": 0,
"$top": 25,
"$orderBy": [
{
"field": "filename",
"sortOrder": "ASC"
}
]
}Facets Response
{
"facets": {
"Project": [
{"name": "MyProject", "count": 150},
{"name": "AnotherProject", "count": 45}
],
"Repository": [
{"name": "Backend", "count": 100},
{"name": "Frontend", "count": 50}
],
"CodeElement": [
{"name": "class", "count": 30},
{"name": "method", "count": 120}
]
}
}---
MCP Tool Usage Examples
List Wikis
# Using mcp__ado__wiki_list_wikis
params = {
"project": "MyProject"
}Get Wiki
# Using mcp__ado__wiki_get_wiki
params = {
"wikiIdentifier": "MyProject.wiki",
"project": "MyProject"
}List Wiki Pages
# Using mcp__ado__wiki_list_pages
params = {
"wikiIdentifier": "MyProject.wiki",
"project": "MyProject",
"pageViewsForDays": 7, # Include view stats
"top": 50
}Get Wiki Page Content
# Using mcp__ado__wiki_get_page_content
params = {
"wikiIdentifier": "MyProject.wiki",
"project": "MyProject",
"path": "/Getting-Started/Setup"
}
# Or by URL
params = {
"url": "https://dev.azure.com/org/project/_wiki/wikis/wiki-name?pagePath=/My%20Page"
}Create/Update Wiki Page
# Using mcp__ado__wiki_create_or_update_page
params = {
"wikiIdentifier": "MyProject.wiki",
"project": "MyProject",
"path": "/Guides/New-Feature",
"content": """# New Feature Guide
## Overview
This guide covers the new feature implementation.
## Steps
1. Step one
2. Step two
## Related
- [[/API/Reference]]
- #12345
"""
}Search Code
# Using mcp__ado__search_code
params = {
"searchText": "async Task<User> GetUserById",
"project": ["MyProject"],
"repository": ["Backend"],
"path": ["/src"],
"branch": ["main"],
"top": 25,
"includeFacets": True
}Search Work Items
# Using mcp__ado__search_workitem
params = {
"searchText": "login authentication",
"project": ["MyProject"],
"workItemType": ["Bug", "User Story"],
"state": ["Active", "New"],
"assignedTo": ["@Me"],
"top": 50,
"includeFacets": True
}Search Wiki
# Using mcp__ado__search_wiki
params = {
"searchText": "deployment guide kubernetes",
"project": ["MyProject"],
"wiki": ["Team Wiki"],
"top": 20,
"includeFacets": True
}---
Search Response Structure
Code Search Result
{
"count": 150,
"results": [
{
"fileName": "UserService.cs",
"path": "/src/Services/UserService.cs",
"repository": {
"name": "Backend",
"id": "repo-guid"
},
"project": {
"name": "MyProject"
},
"versions": [
{
"branchName": "main",
"changeId": "commit-sha"
}
],
"matches": {
"content": [
{
"charOffset": 100,
"length": 15
}
]
},
"contentId": "blob-sha"
}
],
"facets": {
"Project": [...],
"Repository": [...],
"CodeElement": [...]
}
}Work Item Search Result
{
"count": 45,
"results": [
{
"project": {
"name": "MyProject"
},
"fields": {
"system.id": "12345",
"system.workitemtype": "Bug",
"system.title": "Login fails with SSO",
"system.state": "Active",
"system.assignedto": "user@company.com"
},
"hits": [
{
"fieldReferenceName": "system.title",
"highlights": ["Login fails with <highlighttext>SSO</highlighttext>"]
}
],
"url": "https://dev.azure.com/org/project/_workitems/edit/12345"
}
],
"facets": {
"System.WorkItemType": [...],
"System.State": [...],
"System.AssignedTo": [...]
}
}---
Best Practices
1. Use Specific Filters
# Bad: Too broad
login
# Good: Specific
repo:Backend path:src/auth class:LoginService method:Validate2. Leverage Facets
Use facets to narrow results progressively.
3. Quote Exact Phrases
"public async Task" vs public async Task4. Use Code Element Filters for Code
# Find all implementations of interface
class:IUserRepository NOT interface:IUserRepository5. Combine Filters
t:Bug s:Active a:@Me created:@Today-7 priority:<=2Work Item Tracking API Reference
Comprehensive reference for Azure DevOps Work Item Tracking REST API operations.
Table of Contents
---
Work Item Fields
System Fields
| Field | Reference Name | Type | Description |
|---|---|---|---|
| ID | System.Id | Integer | Unique work item identifier |
| Title | System.Title | String | Work item title (required) |
| State | System.State | String | Current state |
| Reason | System.Reason | String | Reason for state change |
| Assigned To | System.AssignedTo | Identity | Assigned user |
| Area Path | System.AreaPath | TreePath | Area classification |
| Iteration Path | System.IterationPath | TreePath | Sprint/iteration |
| Work Item Type | System.WorkItemType | String | Type (Bug, Task, etc.) |
| Created Date | System.CreatedDate | DateTime | Creation timestamp |
| Created By | System.CreatedBy | Identity | Creator |
| Changed Date | System.ChangedDate | DateTime | Last modified timestamp |
| Changed By | System.ChangedBy | Identity | Last modifier |
| Tags | System.Tags | String | Semicolon-separated tags |
| Description | System.Description | HTML | Detailed description |
| History | System.History | History | Discussion/comments |
| Rev | System.Rev | Integer | Revision number |
| Team Project | System.TeamProject | String | Project name |
| Board Column | System.BoardColumn | String | Kanban board column |
| Board Lane | System.BoardLane | String | Kanban board lane |
Scheduling Fields
| Field | Reference Name | Type | Description |
|---|---|---|---|
| Story Points | Microsoft.VSTS.Scheduling.StoryPoints | Double | Agile story points |
| Effort | Microsoft.VSTS.Scheduling.Effort | Double | Effort estimate |
| Remaining Work | Microsoft.VSTS.Scheduling.RemainingWork | Double | Hours remaining |
| Original Estimate | Microsoft.VSTS.Scheduling.OriginalEstimate | Double | Initial estimate |
| Completed Work | Microsoft.VSTS.Scheduling.CompletedWork | Double | Hours completed |
| Start Date | Microsoft.VSTS.Scheduling.StartDate | DateTime | Planned start |
| Finish Date | Microsoft.VSTS.Scheduling.FinishDate | DateTime | Planned finish |
| Target Date | Microsoft.VSTS.Scheduling.TargetDate | DateTime | Target completion |
Bug-Specific Fields
| Field | Reference Name | Type | Description |
|---|---|---|---|
| Repro Steps | Microsoft.VSTS.TCM.ReproSteps | HTML | Steps to reproduce |
| System Info | Microsoft.VSTS.TCM.SystemInfo | HTML | System information |
| Found In | Microsoft.VSTS.Build.FoundIn | String | Build where found |
| Integration Build | Microsoft.VSTS.Build.IntegrationBuild | String | Fix build |
| Severity | Microsoft.VSTS.Common.Severity | String | Bug severity |
| Priority | Microsoft.VSTS.Common.Priority | Integer | Priority (1-4) |
Custom Field Pattern
Custom fields follow the pattern: Custom.{FieldName}
---
Work Item Types
Agile Process Template
| Type | Purpose | Parent Type |
|---|---|---|
| Epic | Large initiative | - |
| Feature | Product capability | Epic |
| User Story | User requirement | Feature |
| Task | Implementation work | User Story |
| Bug | Defect | User Story/Feature |
| Issue | Impediment | - |
| Test Case | Test scenario | - |
Scrum Process Template
| Type | Purpose | Parent Type |
|---|---|---|
| Epic | Large initiative | - |
| Feature | Product capability | Epic |
| Product Backlog Item | Backlog item | Feature |
| Task | Sprint task | Product Backlog Item |
| Bug | Defect | Product Backlog Item |
| Impediment | Blocker | - |
| Test Case | Test scenario | - |
CMMI Process Template
| Type | Purpose | Parent Type |
|---|---|---|
| Epic | Large initiative | - |
| Feature | Product capability | Epic |
| Requirement | Formal requirement | Feature |
| Task | Implementation task | Requirement |
| Bug | Defect | Requirement |
| Change Request | Change proposal | - |
| Issue | Problem | - |
| Review | Review item | - |
| Risk | Risk item | - |
| Test Case | Test scenario | - |
---
WIQL Reference
Query Syntax
SELECT [Field1], [Field2], ...
FROM workitems | workitemLinks
WHERE [Conditions]
ORDER BY [Field] [ASC|DESC]
ASOF [DateTime]
MODE [Options]Operators
| Operator | Description | Example |
|---|---|---|
= | Equals | [System.State] = 'Active' |
<> | Not equals | [System.State] <> 'Closed' |
> | Greater than | [System.Priority] > 2 |
< | Less than | [Microsoft.VSTS.Scheduling.RemainingWork] < 8 |
>= | Greater or equal | [System.CreatedDate] >= @Today - 7 |
<= | Less or equal | [System.ChangedDate] <= @Today |
IN | In list | [System.State] IN ('Active', 'New') |
NOT IN | Not in list | [System.WorkItemType] NOT IN ('Task') |
CONTAINS | Contains text | [System.Title] CONTAINS 'API' |
NOT CONTAINS | Not contains | [System.Description] NOT CONTAINS 'deprecated' |
UNDER | Under tree path | [System.AreaPath] UNDER 'Project\Team' |
NOT UNDER | Not under path | [System.IterationPath] NOT UNDER 'Project\Archive' |
IN GROUP | In group | [System.AssignedTo] IN GROUP '[Team]' |
WAS EVER | Was ever value | [System.AssignedTo] WAS EVER @Me |
Macros
| Macro | Description |
|---|---|
@Me | Current authenticated user |
@Today | Today's date (midnight) |
@Today - N | N days before today |
@Today + N | N days after today |
@Project | Current project |
@CurrentIteration | Current team iteration |
@CurrentIteration + N | N iterations ahead |
@CurrentIteration - N | N iterations behind |
@TeamAreas | Team's area paths |
@StartOfDay | Start of today |
@StartOfWeek | Start of current week |
@StartOfMonth | Start of current month |
@StartOfYear | Start of current year |
@RecentProjectActivity | Recent activity filter |
@follows | Items user follows |
@MyRecentActivity | User's recent activity |
@RecentMentions | Recent @mentions |
Link Query Modes
| Mode | Description |
|---|---|
MustContain | Links must contain specified items |
MayContain | Links may contain specified items |
DoesNotContain | Links must not contain specified items |
Recursive | Follow links recursively |
ReturnMatchingChildren | Return only matching children |
Example Queries
-- Active bugs assigned to me, high priority
SELECT [System.Id], [System.Title], [System.State], [Microsoft.VSTS.Common.Severity]
FROM workitems
WHERE [System.TeamProject] = @Project
AND [System.WorkItemType] = 'Bug'
AND [System.State] = 'Active'
AND [System.AssignedTo] = @Me
AND [Microsoft.VSTS.Common.Priority] <= 2
ORDER BY [Microsoft.VSTS.Common.Priority] ASC, [System.CreatedDate] DESC
-- Items changed in last week
SELECT [System.Id], [System.Title], [System.ChangedDate], [System.ChangedBy]
FROM workitems
WHERE [System.TeamProject] = @Project
AND [System.ChangedDate] >= @Today - 7
ORDER BY [System.ChangedDate] DESC
-- Current sprint work items
SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo]
FROM workitems
WHERE [System.TeamProject] = @Project
AND [System.IterationPath] = @CurrentIteration
AND [System.WorkItemType] IN ('User Story', 'Task', 'Bug')
ORDER BY [System.State], [System.AssignedTo]
-- Parent-child hierarchy
SELECT [Source].[System.Id], [Source].[System.Title],
[Target].[System.Id], [Target].[System.Title]
FROM workitemLinks
WHERE [Source].[System.TeamProject] = @Project
AND [Source].[System.WorkItemType] = 'User Story'
AND [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward'
AND [Target].[System.WorkItemType] = 'Task'
MODE (MustContain)---
Link Types
Standard Link Types
| Display Name | Forward Ref | Reverse Ref |
|---|---|---|
| Parent/Child | System.LinkTypes.Hierarchy-Forward | System.LinkTypes.Hierarchy-Reverse |
| Related | System.LinkTypes.Related | System.LinkTypes.Related |
| Predecessor/Successor | System.LinkTypes.Dependency-Forward | System.LinkTypes.Dependency-Reverse |
| Duplicate/Duplicate Of | System.LinkTypes.Duplicate-Forward | System.LinkTypes.Duplicate-Reverse |
| Tested By/Tests | Microsoft.VSTS.Common.TestedBy-Forward | Microsoft.VSTS.Common.TestedBy-Reverse |
| Test Case/Shared Steps | Microsoft.VSTS.TestCase.SharedStepReferencedBy | Microsoft.VSTS.TestCase.SharedStepReferencedBy-Reverse |
| Affects/Affected By | Microsoft.VSTS.Common.Affects-Forward | Microsoft.VSTS.Common.Affects-Reverse |
External Link Types
| Link Type | Description |
|---|---|
ArtifactLink | Link to build, release, or other artifact |
Hyperlink | Link to external URL |
Storyboard | Link to storyboard |
Remote Work Item Link | Cross-organization link |
GitHub Commit | GitHub commit link |
GitHub Pull Request | GitHub PR link |
VSTFS Link URIs
# Branch
vstfs:///Git/Ref/{projectId}/{repositoryId}/GB{branchName}
# Commit
vstfs:///Git/Commit/{projectId}/{repositoryId}/{commitId}
# Pull Request
vstfs:///Git/PullRequestId/{projectId}/{pullRequestId}
# Build
vstfs:///Build/Build/{projectId}/{buildId}
# Release
vstfs:///ReleaseManagement/ReleaseEnvironment/{projectId}/{releaseId}:{environmentId}---
State Transitions
Agile Bug States
New → Active → Resolved → Closed
↓ ↓
Removed RemovedAgile User Story States
New → Active → Resolved → Closed
↓
RemovedAgile Task States
New → Active → Closed
↓
RemovedState Reasons
| From State | To State | Reason |
|---|---|---|
| New | Active | Approved, Investigation |
| Active | Resolved | Fixed, As Designed, Deferred |
| Resolved | Active | Not Fixed, Test Failed |
| Resolved | Closed | Verified |
| Active | Closed | Cut, Completed |
| Any | Removed | Removed from backlog |
---
MCP Tool Usage Examples
Get Work Item
# Using mcp__ado__wit_get_work_item
params = {
"project": "MyProject",
"id": 12345,
"expand": "relations", # Include links
"fields": ["System.Id", "System.Title", "System.State"]
}Create Work Item
# Using mcp__ado__wit_create_work_item
params = {
"project": "MyProject",
"workItemType": "Bug",
"fields": [
{"name": "System.Title", "value": "Login button not working"},
{"name": "System.Description", "value": "Users cannot click the login button"},
{"name": "Microsoft.VSTS.Common.Severity", "value": "2 - High"},
{"name": "System.AssignedTo", "value": "user@company.com"}
]
}Update Work Item
# Using mcp__ado__wit_update_work_item
params = {
"id": 12345,
"updates": [
{"op": "add", "path": "/fields/System.State", "value": "Active"},
{"op": "add", "path": "/fields/System.AssignedTo", "value": "dev@company.com"}
]
}Link Work Items
# Using mcp__ado__wit_work_items_link
params = {
"project": "MyProject",
"updates": [
{"id": 12345, "linkToId": 12346, "type": "child"},
{"id": 12345, "linkToId": 12347, "type": "related"}
]
}Add Artifact Link
# Using mcp__ado__wit_add_artifact_link
params = {
"workItemId": 12345,
"project": "MyProject",
"linkType": "Branch",
"projectId": "project-guid",
"repositoryId": "repo-guid",
"branchName": "feature/new-feature"
}#!/usr/bin/env bash
#
# Azure DevOps CLI Examples
#
# This script provides bash examples for common Azure DevOps operations
# using curl and the REST API.
#
# Prerequisites:
# - curl
# - jq (for JSON parsing)
# - base64
#
# Environment Variables:
# AZURE_DEVOPS_ORG: Organization name
# AZURE_DEVOPS_PAT: Personal Access Token
# AZURE_DEVOPS_PROJECT: Project name
#
set -euo pipefail
# Configuration
ORG="${AZURE_DEVOPS_ORG:-your-org}"
PAT="${AZURE_DEVOPS_PAT:-your-pat}"
PROJECT="${AZURE_DEVOPS_PROJECT:-your-project}"
API_VERSION="7.2-preview.3"
# Base URL
BASE_URL="https://dev.azure.com/${ORG}"
# Generate auth header
get_auth() {
echo -n ":${PAT}" | base64
}
AUTH_HEADER="Authorization: Basic $(get_auth)"
# ============================================================================
# Work Item Functions
# ============================================================================
# Get a single work item
get_work_item() {
local id="$1"
local expand="${2:-all}"
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/wit/workitems/${id}?api-version=${API_VERSION}&\$expand=${expand}"
}
# Create a work item
create_work_item() {
local type="$1"
local title="$2"
local description="${3:-}"
local body="["
body+="{\"op\":\"add\",\"path\":\"/fields/System.Title\",\"value\":\"${title}\"}"
if [[ -n "${description}" ]]; then
body+=",{\"op\":\"add\",\"path\":\"/fields/System.Description\",\"value\":\"${description}\"}"
fi
body+="]"
curl -s -X POST \
-H "${AUTH_HEADER}" \
-H "Content-Type: application/json-patch+json" \
-d "${body}" \
"${BASE_URL}/${PROJECT}/_apis/wit/workitems/\$${type}?api-version=${API_VERSION}"
}
# Update a work item
update_work_item() {
local id="$1"
local field="$2"
local value="$3"
local body="[{\"op\":\"add\",\"path\":\"/fields/${field}\",\"value\":\"${value}\"}]"
curl -s -X PATCH \
-H "${AUTH_HEADER}" \
-H "Content-Type: application/json-patch+json" \
-d "${body}" \
"${BASE_URL}/${PROJECT}/_apis/wit/workitems/${id}?api-version=${API_VERSION}"
}
# Run WIQL query
run_wiql() {
local query="$1"
local body="{\"query\":\"${query}\"}"
curl -s -X POST \
-H "${AUTH_HEADER}" \
-H "Content-Type: application/json" \
-d "${body}" \
"${BASE_URL}/${PROJECT}/_apis/wit/wiql?api-version=${API_VERSION}"
}
# ============================================================================
# Pipeline Functions
# ============================================================================
# List pipelines
list_pipelines() {
local top="${1:-50}"
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/pipelines?api-version=7.2-preview.1&\$top=${top}"
}
# Get pipeline
get_pipeline() {
local pipeline_id="$1"
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/pipelines/${pipeline_id}?api-version=7.2-preview.1"
}
# Run pipeline
run_pipeline() {
local pipeline_id="$1"
local branch="${2:-main}"
local body="{\"resources\":{\"repositories\":{\"self\":{\"refName\":\"refs/heads/${branch}\"}}}}"
curl -s -X POST \
-H "${AUTH_HEADER}" \
-H "Content-Type: application/json" \
-d "${body}" \
"${BASE_URL}/${PROJECT}/_apis/pipelines/${pipeline_id}/runs?api-version=7.2-preview.1"
}
# List builds
list_builds() {
local top="${1:-50}"
local status="${2:-}"
local url="${BASE_URL}/${PROJECT}/_apis/build/builds?api-version=7.2-preview.7&\$top=${top}"
if [[ -n "${status}" ]]; then
url+="&statusFilter=${status}"
fi
curl -s -H "${AUTH_HEADER}" "${url}"
}
# Get build
get_build() {
local build_id="$1"
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/build/builds/${build_id}?api-version=7.2-preview.7"
}
# Get build logs
get_build_logs() {
local build_id="$1"
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/build/builds/${build_id}/logs?api-version=7.2-preview.2"
}
# Get specific log content
get_log_content() {
local build_id="$1"
local log_id="$2"
curl -s -H "${AUTH_HEADER}" \
-H "Accept: text/plain" \
"${BASE_URL}/${PROJECT}/_apis/build/builds/${build_id}/logs/${log_id}?api-version=7.2-preview.2"
}
# ============================================================================
# Repository Functions
# ============================================================================
# List repositories
list_repos() {
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/git/repositories?api-version=7.2-preview.1"
}
# Get repository
get_repo() {
local repo_name="$1"
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/git/repositories/${repo_name}?api-version=7.2-preview.1"
}
# List branches
list_branches() {
local repo_id="$1"
local filter="${2:-}"
local url="${BASE_URL}/${PROJECT}/_apis/git/repositories/${repo_id}/refs?api-version=7.2-preview.1&filter=heads"
if [[ -n "${filter}" ]]; then
url+="/${filter}"
fi
curl -s -H "${AUTH_HEADER}" "${url}"
}
# ============================================================================
# Pull Request Functions
# ============================================================================
# List pull requests
list_prs() {
local repo_id="$1"
local status="${2:-active}"
local top="${3:-50}"
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/git/repositories/${repo_id}/pullrequests?api-version=7.2-preview.1&searchCriteria.status=${status}&\$top=${top}"
}
# Get pull request
get_pr() {
local repo_id="$1"
local pr_id="$2"
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/git/repositories/${repo_id}/pullrequests/${pr_id}?api-version=7.2-preview.1"
}
# Create pull request
create_pr() {
local repo_id="$1"
local source_branch="$2"
local target_branch="$3"
local title="$4"
local description="${5:-}"
local body=$(cat <<EOF
{
"sourceRefName": "refs/heads/${source_branch}",
"targetRefName": "refs/heads/${target_branch}",
"title": "${title}",
"description": "${description}"
}
EOF
)
curl -s -X POST \
-H "${AUTH_HEADER}" \
-H "Content-Type: application/json" \
-d "${body}" \
"${BASE_URL}/${PROJECT}/_apis/git/repositories/${repo_id}/pullrequests?api-version=7.2-preview.1"
}
# ============================================================================
# Search Functions
# ============================================================================
# Search code
search_code() {
local query="$1"
local top="${2:-25}"
local body=$(cat <<EOF
{
"searchText": "${query}",
"\$top": ${top}
}
EOF
)
curl -s -X POST \
-H "${AUTH_HEADER}" \
-H "Content-Type: application/json" \
-d "${body}" \
"https://almsearch.dev.azure.com/${ORG}/${PROJECT}/_apis/search/codesearchresults?api-version=7.2-preview.1"
}
# Search work items
search_work_items() {
local query="$1"
local top="${2:-25}"
local body=$(cat <<EOF
{
"searchText": "${query}",
"\$top": ${top}
}
EOF
)
curl -s -X POST \
-H "${AUTH_HEADER}" \
-H "Content-Type: application/json" \
-d "${body}" \
"https://almsearch.dev.azure.com/${ORG}/${PROJECT}/_apis/search/workitemsearchresults?api-version=7.2-preview.1"
}
# ============================================================================
# Wiki Functions
# ============================================================================
# List wikis
list_wikis() {
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/wiki/wikis?api-version=7.2-preview.2"
}
# Get wiki page content
get_wiki_page() {
local wiki_id="$1"
local page_path="$2"
# URL encode the path
local encoded_path=$(echo -n "${page_path}" | jq -sRr @uri)
curl -s -H "${AUTH_HEADER}" \
"${BASE_URL}/${PROJECT}/_apis/wiki/wikis/${wiki_id}/pages?path=${encoded_path}&api-version=7.2-preview.1&includeContent=true"
}
# ============================================================================
# Usage Examples
# ============================================================================
usage() {
cat <<EOF
Azure DevOps CLI Examples
Usage: $0 <command> [arguments]
Commands:
work-item get <id> Get work item by ID
work-item create <type> <title> Create work item
work-item update <id> <field> <value> Update work item field
pipeline list List pipelines
pipeline get <id> Get pipeline by ID
pipeline run <id> [branch] Run pipeline
build list [top] [status] List builds
build get <id> Get build by ID
build logs <id> Get build logs
repo list List repositories
repo get <name> Get repository
branch list <repo_id> List branches
pr list <repo_id> [status] List pull requests
pr get <repo_id> <pr_id> Get pull request
pr create <repo_id> <source> <target> <title> Create PR
search code <query> Search code
search work-items <query> Search work items
wiki list List wikis
wiki page <wiki_id> <path> Get wiki page
Environment Variables:
AZURE_DEVOPS_ORG Organization name (current: ${ORG})
AZURE_DEVOPS_PAT Personal Access Token
AZURE_DEVOPS_PROJECT Project name (current: ${PROJECT})
EOF
}
# Main command dispatcher
main() {
if [[ $# -lt 1 ]]; then
usage
exit 1
fi
local cmd="$1"
shift
case "${cmd}" in
work-item)
local subcmd="${1:-}"
shift || true
case "${subcmd}" in
get) get_work_item "$@" | jq . ;;
create) create_work_item "$@" | jq . ;;
update) update_work_item "$@" | jq . ;;
*) echo "Unknown work-item command: ${subcmd}" ;;
esac
;;
pipeline)
local subcmd="${1:-}"
shift || true
case "${subcmd}" in
list) list_pipelines "$@" | jq . ;;
get) get_pipeline "$@" | jq . ;;
run) run_pipeline "$@" | jq . ;;
*) echo "Unknown pipeline command: ${subcmd}" ;;
esac
;;
build)
local subcmd="${1:-}"
shift || true
case "${subcmd}" in
list) list_builds "$@" | jq . ;;
get) get_build "$@" | jq . ;;
logs) get_build_logs "$@" | jq . ;;
*) echo "Unknown build command: ${subcmd}" ;;
esac
;;
repo)
local subcmd="${1:-}"
shift || true
case "${subcmd}" in
list) list_repos | jq . ;;
get) get_repo "$@" | jq . ;;
*) echo "Unknown repo command: ${subcmd}" ;;
esac
;;
branch)
local subcmd="${1:-}"
shift || true
case "${subcmd}" in
list) list_branches "$@" | jq . ;;
*) echo "Unknown branch command: ${subcmd}" ;;
esac
;;
pr)
local subcmd="${1:-}"
shift || true
case "${subcmd}" in
list) list_prs "$@" | jq . ;;
get) get_pr "$@" | jq . ;;
create) create_pr "$@" | jq . ;;
*) echo "Unknown pr command: ${subcmd}" ;;
esac
;;
search)
local subcmd="${1:-}"
shift || true
case "${subcmd}" in
code) search_code "$@" | jq . ;;
work-items) search_work_items "$@" | jq . ;;
*) echo "Unknown search command: ${subcmd}" ;;
esac
;;
wiki)
local subcmd="${1:-}"
shift || true
case "${subcmd}" in
list) list_wikis | jq . ;;
page) get_wiki_page "$@" | jq . ;;
*) echo "Unknown wiki command: ${subcmd}" ;;
esac
;;
help|--help|-h)
usage
;;
*)
echo "Unknown command: ${cmd}"
usage
exit 1
;;
esac
}
# Run if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
#!/usr/bin/env python3
"""
Azure DevOps Pipelines - Python Examples
This script demonstrates common pipeline operations using the Azure DevOps REST API.
Prerequisites:
pip install requests
Environment Variables:
AZURE_DEVOPS_ORG: Organization name
AZURE_DEVOPS_PAT: Personal Access Token
AZURE_DEVOPS_PROJECT: Project name
"""
import os
import base64
import json
import time
import requests
from typing import Optional, List, Dict, Any
# Configuration
ORG = os.getenv("AZURE_DEVOPS_ORG", "your-org")
PAT = os.getenv("AZURE_DEVOPS_PAT", "your-pat")
PROJECT = os.getenv("AZURE_DEVOPS_PROJECT", "your-project")
API_VERSION = "7.2-preview.1"
# Base URL
BASE_URL = f"https://dev.azure.com/{ORG}"
def get_auth_header() -> Dict[str, str]:
"""Generate authorization header from PAT."""
auth_string = base64.b64encode(f":{PAT}".encode()).decode()
return {
"Authorization": f"Basic {auth_string}",
"Content-Type": "application/json"
}
def list_pipelines(
name_filter: Optional[str] = None,
top: int = 100
) -> List[Dict[str, Any]]:
"""
List pipeline definitions.
Args:
name_filter: Optional name filter
top: Maximum results
Returns:
List of pipeline definitions
"""
url = f"{BASE_URL}/{PROJECT}/_apis/pipelines"
params = {
"api-version": API_VERSION,
"$top": top
}
if name_filter:
params["name"] = name_filter
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json().get("value", [])
def get_pipeline(pipeline_id: int) -> Dict[str, Any]:
"""
Get pipeline definition details.
Args:
pipeline_id: Pipeline ID
Returns:
Pipeline definition
"""
url = f"{BASE_URL}/{PROJECT}/_apis/pipelines/{pipeline_id}"
params = {"api-version": API_VERSION}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json()
def run_pipeline(
pipeline_id: int,
branch: Optional[str] = None,
variables: Optional[Dict[str, str]] = None,
template_parameters: Optional[Dict[str, str]] = None,
stages_to_skip: Optional[List[str]] = None
) -> Dict[str, Any]:
"""
Trigger a pipeline run.
Args:
pipeline_id: Pipeline ID
branch: Branch to run (refs/heads/main)
variables: Pipeline variables
template_parameters: Template parameters
stages_to_skip: List of stage names to skip
Returns:
Pipeline run details
"""
url = f"{BASE_URL}/{PROJECT}/_apis/pipelines/{pipeline_id}/runs"
params = {"api-version": API_VERSION}
body: Dict[str, Any] = {}
if branch:
body["resources"] = {
"repositories": {
"self": {
"refName": branch if branch.startswith("refs/") else f"refs/heads/{branch}"
}
}
}
if variables:
body["variables"] = {
name: {"value": value, "isSecret": False}
for name, value in variables.items()
}
if template_parameters:
body["templateParameters"] = template_parameters
if stages_to_skip:
body["stagesToSkip"] = stages_to_skip
response = requests.post(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json()
def get_run(pipeline_id: int, run_id: int) -> Dict[str, Any]:
"""
Get pipeline run details.
Args:
pipeline_id: Pipeline ID
run_id: Run ID
Returns:
Run details
"""
url = f"{BASE_URL}/{PROJECT}/_apis/pipelines/{pipeline_id}/runs/{run_id}"
params = {"api-version": API_VERSION}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json()
def list_runs(
pipeline_id: int,
top: int = 50
) -> List[Dict[str, Any]]:
"""
List pipeline runs.
Args:
pipeline_id: Pipeline ID
top: Maximum results
Returns:
List of runs
"""
url = f"{BASE_URL}/{PROJECT}/_apis/pipelines/{pipeline_id}/runs"
params = {
"api-version": API_VERSION,
"$top": top
}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json().get("value", [])
def get_build(build_id: int) -> Dict[str, Any]:
"""
Get build details using Build API.
Args:
build_id: Build ID
Returns:
Build details
"""
url = f"{BASE_URL}/{PROJECT}/_apis/build/builds/{build_id}"
params = {"api-version": "7.2-preview.7"}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json()
def list_builds(
definition_ids: Optional[List[int]] = None,
branch_name: Optional[str] = None,
status_filter: Optional[str] = None,
result_filter: Optional[str] = None,
top: int = 50
) -> List[Dict[str, Any]]:
"""
List builds.
Args:
definition_ids: Filter by definition IDs
branch_name: Filter by branch
status_filter: Filter by status (inProgress, completed, etc.)
result_filter: Filter by result (succeeded, failed, etc.)
top: Maximum results
Returns:
List of builds
"""
url = f"{BASE_URL}/{PROJECT}/_apis/build/builds"
params: Dict[str, Any] = {
"api-version": "7.2-preview.7",
"$top": top,
"queryOrder": "queueTimeDescending"
}
if definition_ids:
params["definitions"] = ",".join(map(str, definition_ids))
if branch_name:
params["branchName"] = branch_name
if status_filter:
params["statusFilter"] = status_filter
if result_filter:
params["resultFilter"] = result_filter
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json().get("value", [])
def get_build_logs(build_id: int) -> List[Dict[str, Any]]:
"""
Get list of logs for a build.
Args:
build_id: Build ID
Returns:
List of log references
"""
url = f"{BASE_URL}/{PROJECT}/_apis/build/builds/{build_id}/logs"
params = {"api-version": "7.2-preview.2"}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json().get("value", [])
def get_build_log_content(
build_id: int,
log_id: int,
start_line: int = 0,
end_line: Optional[int] = None
) -> str:
"""
Get log content.
Args:
build_id: Build ID
log_id: Log ID
start_line: Start line (0-based)
end_line: End line
Returns:
Log content as text
"""
url = f"{BASE_URL}/{PROJECT}/_apis/build/builds/{build_id}/logs/{log_id}"
params: Dict[str, Any] = {
"api-version": "7.2-preview.2",
"startLine": start_line
}
if end_line:
params["endLine"] = end_line
headers = get_auth_header()
headers["Accept"] = "text/plain"
response = requests.get(url, headers=headers, params=params)
response.raise_for_status()
return response.text
def retry_build_stage(
build_id: int,
stage_name: str,
force_retry_all_jobs: bool = False
) -> Dict[str, Any]:
"""
Retry a failed stage.
Args:
build_id: Build ID
stage_name: Stage name to retry
force_retry_all_jobs: Retry all jobs in stage
Returns:
Updated build timeline
"""
url = f"{BASE_URL}/{PROJECT}/_apis/build/builds/{build_id}/stages/{stage_name}"
params = {"api-version": "7.2-preview.1"}
body = {
"forceRetryAllJobs": force_retry_all_jobs,
"state": "retry"
}
response = requests.patch(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json()
def wait_for_build(
build_id: int,
poll_interval: int = 30,
timeout: int = 3600
) -> Dict[str, Any]:
"""
Wait for a build to complete.
Args:
build_id: Build ID
poll_interval: Seconds between polls
timeout: Maximum wait time in seconds
Returns:
Final build state
Raises:
TimeoutError: If build doesn't complete within timeout
"""
start_time = time.time()
while True:
build = get_build(build_id)
status = build.get("status")
if status == "completed":
return build
elapsed = time.time() - start_time
if elapsed >= timeout:
raise TimeoutError(f"Build {build_id} did not complete within {timeout} seconds")
print(f"Build {build_id} status: {status}, elapsed: {int(elapsed)}s")
time.sleep(poll_interval)
def get_build_changes(build_id: int, top: int = 100) -> List[Dict[str, Any]]:
"""
Get commits associated with a build.
Args:
build_id: Build ID
top: Maximum results
Returns:
List of associated changes
"""
url = f"{BASE_URL}/{PROJECT}/_apis/build/builds/{build_id}/changes"
params = {
"api-version": "7.2-preview.2",
"$top": top
}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json().get("value", [])
# Example usage
if __name__ == "__main__":
# List pipelines
print("Listing pipelines...")
try:
pipelines = list_pipelines(top=10)
for p in pipelines:
print(f" {p['id']}: {p['name']}")
except Exception as e:
print(f"Error: {e}")
# Get recent builds
print("\nRecent builds...")
try:
builds = list_builds(top=5)
for b in builds:
print(f" #{b['id']}: {b['definition']['name']} - {b.get('status')} / {b.get('result', 'N/A')}")
except Exception as e:
print(f"Error: {e}")
# Run a pipeline
print("\nRunning pipeline...")
try:
pipeline_id = 1 # Replace with actual ID
run = run_pipeline(
pipeline_id=pipeline_id,
branch="main",
variables={"environment": "dev"},
template_parameters={"runTests": "true"}
)
print(f"Started run #{run['id']}, state: {run['state']}")
# Optionally wait for completion
# result = wait_for_build(run['id'], poll_interval=30, timeout=1800)
# print(f"Final result: {result.get('result')}")
except Exception as e:
print(f"Error: {e}")
# Get build logs
print("\nBuild logs...")
try:
build_id = 1 # Replace with actual ID
logs = get_build_logs(build_id)
print(f"Found {len(logs)} log files")
# Get content of first log
if logs:
content = get_build_log_content(build_id, logs[0]['id'], end_line=20)
print(f"First 20 lines of log {logs[0]['id']}:\n{content}")
except Exception as e:
print(f"Error: {e}")
#!/usr/bin/env python3
"""
Azure DevOps Pull Requests - Python Examples
This script demonstrates common PR operations using the Azure DevOps REST API.
Prerequisites:
pip install requests
Environment Variables:
AZURE_DEVOPS_ORG: Organization name
AZURE_DEVOPS_PAT: Personal Access Token
AZURE_DEVOPS_PROJECT: Project name
"""
import os
import base64
import json
import requests
from typing import Optional, List, Dict, Any
# Configuration
ORG = os.getenv("AZURE_DEVOPS_ORG", "your-org")
PAT = os.getenv("AZURE_DEVOPS_PAT", "your-pat")
PROJECT = os.getenv("AZURE_DEVOPS_PROJECT", "your-project")
API_VERSION = "7.2-preview.1"
# Base URL
BASE_URL = f"https://dev.azure.com/{ORG}"
def get_auth_header() -> Dict[str, str]:
"""Generate authorization header from PAT."""
auth_string = base64.b64encode(f":{PAT}".encode()).decode()
return {
"Authorization": f"Basic {auth_string}",
"Content-Type": "application/json"
}
def list_repositories(name_filter: Optional[str] = None) -> List[Dict[str, Any]]:
"""
List repositories in the project.
Args:
name_filter: Optional name filter
Returns:
List of repositories
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories"
params = {"api-version": "7.2-preview.1"}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
repos = response.json().get("value", [])
if name_filter:
repos = [r for r in repos if name_filter.lower() in r["name"].lower()]
return repos
def get_repository(repo_name_or_id: str) -> Dict[str, Any]:
"""
Get repository details.
Args:
repo_name_or_id: Repository name or ID
Returns:
Repository details
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repo_name_or_id}"
params = {"api-version": "7.2-preview.1"}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json()
def create_branch(
repository_id: str,
branch_name: str,
source_branch: str = "main"
) -> Dict[str, Any]:
"""
Create a new branch.
Args:
repository_id: Repository ID
branch_name: New branch name
source_branch: Source branch name
Returns:
Created ref
"""
# First get the source branch commit
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/refs"
params = {
"api-version": "7.2-preview.1",
"filter": f"heads/{source_branch}"
}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
refs = response.json().get("value", [])
if not refs:
raise ValueError(f"Source branch '{source_branch}' not found")
source_object_id = refs[0]["objectId"]
# Create the new branch
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/refs"
params = {"api-version": "7.2-preview.1"}
body = [
{
"name": f"refs/heads/{branch_name}",
"oldObjectId": "0000000000000000000000000000000000000000",
"newObjectId": source_object_id
}
]
response = requests.post(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json().get("value", [{}])[0]
def create_pull_request(
repository_id: str,
source_branch: str,
target_branch: str,
title: str,
description: Optional[str] = None,
reviewers: Optional[List[str]] = None,
work_item_ids: Optional[List[int]] = None,
is_draft: bool = False
) -> Dict[str, Any]:
"""
Create a pull request.
Args:
repository_id: Repository ID
source_branch: Source branch name
target_branch: Target branch name
title: PR title
description: PR description (markdown)
reviewers: List of reviewer IDs
work_item_ids: List of work item IDs to link
is_draft: Create as draft PR
Returns:
Created PR
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests"
params = {"api-version": "7.2-preview.1"}
# Format branch names
source_ref = source_branch if source_branch.startswith("refs/") else f"refs/heads/{source_branch}"
target_ref = target_branch if target_branch.startswith("refs/") else f"refs/heads/{target_branch}"
body: Dict[str, Any] = {
"sourceRefName": source_ref,
"targetRefName": target_ref,
"title": title,
"isDraft": is_draft
}
if description:
body["description"] = description
if reviewers:
body["reviewers"] = [{"id": r} for r in reviewers]
if work_item_ids:
body["workItemRefs"] = [{"id": str(wid)} for wid in work_item_ids]
response = requests.post(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json()
def list_pull_requests(
repository_id: str,
status: str = "active",
creator_id: Optional[str] = None,
reviewer_id: Optional[str] = None,
source_branch: Optional[str] = None,
target_branch: Optional[str] = None,
top: int = 50
) -> List[Dict[str, Any]]:
"""
List pull requests.
Args:
repository_id: Repository ID
status: Filter by status (active, abandoned, completed, all)
creator_id: Filter by creator
reviewer_id: Filter by reviewer
source_branch: Filter by source branch
target_branch: Filter by target branch
top: Maximum results
Returns:
List of PRs
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests"
params: Dict[str, Any] = {
"api-version": "7.2-preview.1",
"searchCriteria.status": status,
"$top": top
}
if creator_id:
params["searchCriteria.creatorId"] = creator_id
if reviewer_id:
params["searchCriteria.reviewerId"] = reviewer_id
if source_branch:
ref = source_branch if source_branch.startswith("refs/") else f"refs/heads/{source_branch}"
params["searchCriteria.sourceRefName"] = ref
if target_branch:
ref = target_branch if target_branch.startswith("refs/") else f"refs/heads/{target_branch}"
params["searchCriteria.targetRefName"] = ref
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json().get("value", [])
def get_pull_request(repository_id: str, pr_id: int) -> Dict[str, Any]:
"""
Get pull request details.
Args:
repository_id: Repository ID
pr_id: Pull request ID
Returns:
PR details
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests/{pr_id}"
params = {"api-version": "7.2-preview.1"}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json()
def update_pull_request(
repository_id: str,
pr_id: int,
title: Optional[str] = None,
description: Optional[str] = None,
status: Optional[str] = None,
target_branch: Optional[str] = None,
auto_complete: Optional[bool] = None,
merge_strategy: Optional[str] = None,
delete_source_branch: bool = True
) -> Dict[str, Any]:
"""
Update a pull request.
Args:
repository_id: Repository ID
pr_id: Pull request ID
title: New title
description: New description
status: New status (active, abandoned)
target_branch: New target branch
auto_complete: Enable auto-complete
merge_strategy: noFastForward, squash, rebase, rebaseMerge
delete_source_branch: Delete source branch on merge
Returns:
Updated PR
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests/{pr_id}"
params = {"api-version": "7.2-preview.1"}
body: Dict[str, Any] = {}
if title:
body["title"] = title
if description:
body["description"] = description
if status:
body["status"] = status
if target_branch:
ref = target_branch if target_branch.startswith("refs/") else f"refs/heads/{target_branch}"
body["targetRefName"] = ref
if auto_complete is not None:
if auto_complete:
# Get current user ID
me_url = f"https://dev.azure.com/{ORG}/_apis/connectionData"
me_response = requests.get(me_url, headers=get_auth_header())
me_response.raise_for_status()
user_id = me_response.json().get("authenticatedUser", {}).get("id")
body["autoCompleteSetBy"] = {"id": user_id}
body["completionOptions"] = {
"mergeStrategy": merge_strategy or "squash",
"deleteSourceBranch": delete_source_branch,
"transitionWorkItems": True
}
else:
body["autoCompleteSetBy"] = None
response = requests.patch(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json()
def add_reviewers(
repository_id: str,
pr_id: int,
reviewer_ids: List[str]
) -> List[Dict[str, Any]]:
"""
Add reviewers to a PR.
Args:
repository_id: Repository ID
pr_id: Pull request ID
reviewer_ids: List of reviewer identity IDs
Returns:
List of added reviewers
"""
results = []
for reviewer_id in reviewer_ids:
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests/{pr_id}/reviewers/{reviewer_id}"
params = {"api-version": "7.2-preview.1"}
body = {"vote": 0} # No vote initially
response = requests.put(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
results.append(response.json())
return results
def set_vote(
repository_id: str,
pr_id: int,
reviewer_id: str,
vote: int
) -> Dict[str, Any]:
"""
Set reviewer vote.
Args:
repository_id: Repository ID
pr_id: Pull request ID
reviewer_id: Reviewer identity ID
vote: Vote value
10: Approved
5: Approved with suggestions
0: No vote
-5: Waiting for author
-10: Rejected
Returns:
Updated reviewer
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests/{pr_id}/reviewers/{reviewer_id}"
params = {"api-version": "7.2-preview.1"}
body = {"vote": vote}
response = requests.put(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json()
def create_comment_thread(
repository_id: str,
pr_id: int,
content: str,
file_path: Optional[str] = None,
line_number: Optional[int] = None,
status: str = "active"
) -> Dict[str, Any]:
"""
Create a comment thread on a PR.
Args:
repository_id: Repository ID
pr_id: Pull request ID
content: Comment content
file_path: Optional file path for inline comment
line_number: Optional line number for inline comment
status: Thread status (active, fixed, wontFix, closed, pending)
Returns:
Created thread
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests/{pr_id}/threads"
params = {"api-version": "7.2-preview.1"}
body: Dict[str, Any] = {
"comments": [
{
"parentCommentId": 0,
"content": content,
"commentType": "text"
}
],
"status": status
}
if file_path and line_number:
body["threadContext"] = {
"filePath": file_path,
"rightFileStart": {
"line": line_number,
"offset": 1
},
"rightFileEnd": {
"line": line_number,
"offset": 1
}
}
response = requests.post(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json()
def reply_to_thread(
repository_id: str,
pr_id: int,
thread_id: int,
content: str
) -> Dict[str, Any]:
"""
Reply to a comment thread.
Args:
repository_id: Repository ID
pr_id: Pull request ID
thread_id: Thread ID
content: Reply content
Returns:
Created comment
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests/{pr_id}/threads/{thread_id}/comments"
params = {"api-version": "7.2-preview.1"}
body = {
"content": content,
"commentType": "text"
}
response = requests.post(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json()
def resolve_thread(
repository_id: str,
pr_id: int,
thread_id: int,
status: str = "fixed"
) -> Dict[str, Any]:
"""
Resolve a comment thread.
Args:
repository_id: Repository ID
pr_id: Pull request ID
thread_id: Thread ID
status: Resolution status (fixed, wontFix, closed)
Returns:
Updated thread
"""
url = f"{BASE_URL}/{PROJECT}/_apis/git/repositories/{repository_id}/pullrequests/{pr_id}/threads/{thread_id}"
params = {"api-version": "7.2-preview.1"}
body = {"status": status}
response = requests.patch(url, headers=get_auth_header(), params=params, json=body)
response.raise_for_status()
return response.json()
# Example usage
if __name__ == "__main__":
# List repositories
print("Listing repositories...")
try:
repos = list_repositories()
for repo in repos[:5]:
print(f" {repo['name']} ({repo['id']})")
except Exception as e:
print(f"Error: {e}")
# List active PRs
print("\nActive pull requests...")
try:
if repos:
prs = list_pull_requests(repos[0]["id"], status="active", top=5)
for pr in prs:
print(f" #{pr['pullRequestId']}: {pr['title']}")
print(f" {pr['sourceRefName']} -> {pr['targetRefName']}")
except Exception as e:
print(f"Error: {e}")
# Create a PR (example)
print("\nCreating PR...")
try:
repo_id = repos[0]["id"] if repos else "your-repo-id"
new_pr = create_pull_request(
repository_id=repo_id,
source_branch="feature/my-feature",
target_branch="main",
title="Add new feature",
description="""## Summary
This PR adds a new feature.
## Changes
- Added feature X
- Updated tests
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
""",
is_draft=True
)
print(f"Created PR #{new_pr['pullRequestId']}")
except Exception as e:
print(f"Error (expected if branch doesn't exist): {e}")
#!/usr/bin/env python3
"""
Azure DevOps Work Items - Python Examples
This script demonstrates common work item operations using the Azure DevOps REST API.
These examples can be used as templates for automation scripts.
Prerequisites:
pip install requests
Environment Variables:
AZURE_DEVOPS_ORG: Organization name
AZURE_DEVOPS_PAT: Personal Access Token
AZURE_DEVOPS_PROJECT: Project name
"""
import os
import base64
import json
import requests
from typing import Optional, List, Dict, Any
# Configuration
ORG = os.getenv("AZURE_DEVOPS_ORG", "your-org")
PAT = os.getenv("AZURE_DEVOPS_PAT", "your-pat")
PROJECT = os.getenv("AZURE_DEVOPS_PROJECT", "your-project")
API_VERSION = "7.2-preview.3"
# Base URL
BASE_URL = f"https://dev.azure.com/{ORG}"
def get_auth_header() -> Dict[str, str]:
"""Generate authorization header from PAT."""
auth_string = base64.b64encode(f":{PAT}".encode()).decode()
return {
"Authorization": f"Basic {auth_string}",
"Content-Type": "application/json-patch+json"
}
def get_work_item(work_item_id: int, expand: str = "all") -> Dict[str, Any]:
"""
Get a single work item by ID.
Args:
work_item_id: The work item ID
expand: Expansion options (all, fields, links, none, relations)
Returns:
Work item data
"""
url = f"{BASE_URL}/{PROJECT}/_apis/wit/workitems/{work_item_id}"
params = {
"api-version": API_VERSION,
"$expand": expand
}
response = requests.get(url, headers=get_auth_header(), params=params)
response.raise_for_status()
return response.json()
def get_work_items_batch(ids: List[int], fields: Optional[List[str]] = None) -> List[Dict[str, Any]]:
"""
Get multiple work items by IDs.
Args:
ids: List of work item IDs (max 200)
fields: Optional list of fields to return
Returns:
List of work items
"""
url = f"{BASE_URL}/{PROJECT}/_apis/wit/workitemsbatch"
params = {"api-version": API_VERSION}
body = {"ids": ids[:200]} # Max 200 per request
if fields:
body["fields"] = fields
headers = get_auth_header()
headers["Content-Type"] = "application/json"
response = requests.post(url, headers=headers, params=params, json=body)
response.raise_for_status()
return response.json().get("value", [])
def create_work_item(
work_item_type: str,
title: str,
description: Optional[str] = None,
assigned_to: Optional[str] = None,
area_path: Optional[str] = None,
iteration_path: Optional[str] = None,
additional_fields: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Create a new work item.
Args:
work_item_type: Type (Bug, Task, User Story, etc.)
title: Work item title
description: HTML description
assigned_to: Email or display name
area_path: Area path
iteration_path: Iteration path
additional_fields: Additional field values
Returns:
Created work item
"""
url = f"{BASE_URL}/{PROJECT}/_apis/wit/workitems/${work_item_type}"
params = {"api-version": API_VERSION}
# Build JSON Patch document
operations = [
{"op": "add", "path": "/fields/System.Title", "value": title}
]
if description:
operations.append({
"op": "add",
"path": "/fields/System.Description",
"value": description
})
if assigned_to:
operations.append({
"op": "add",
"path": "/fields/System.AssignedTo",
"value": assigned_to
})
if area_path:
operations.append({
"op": "add",
"path": "/fields/System.AreaPath",
"value": area_path
})
if iteration_path:
operations.append({
"op": "add",
"path": "/fields/System.IterationPath",
"value": iteration_path
})
if additional_fields:
for field, value in additional_fields.items():
operations.append({
"op": "add",
"path": f"/fields/{field}",
"value": value
})
response = requests.post(url, headers=get_auth_header(), params=params, json=operations)
response.raise_for_status()
return response.json()
def update_work_item(work_item_id: int, updates: Dict[str, Any]) -> Dict[str, Any]:
"""
Update a work item's fields.
Args:
work_item_id: Work item ID
updates: Dictionary of field reference names to values
Returns:
Updated work item
"""
url = f"{BASE_URL}/{PROJECT}/_apis/wit/workitems/{work_item_id}"
params = {"api-version": API_VERSION}
operations = [
{"op": "add", "path": f"/fields/{field}", "value": value}
for field, value in updates.items()
]
response = requests.patch(url, headers=get_auth_header(), params=params, json=operations)
response.raise_for_status()
return response.json()
def add_work_item_comment(work_item_id: int, comment: str) -> Dict[str, Any]:
"""
Add a comment to a work item.
Args:
work_item_id: Work item ID
comment: Comment text (supports HTML)
Returns:
Created comment
"""
url = f"{BASE_URL}/{PROJECT}/_apis/wit/workitems/{work_item_id}/comments"
params = {"api-version": "7.2-preview.4"}
headers = get_auth_header()
headers["Content-Type"] = "application/json"
body = {"text": comment}
response = requests.post(url, headers=headers, params=params, json=body)
response.raise_for_status()
return response.json()
def link_work_items(
source_id: int,
target_id: int,
link_type: str = "System.LinkTypes.Related"
) -> Dict[str, Any]:
"""
Link two work items together.
Args:
source_id: Source work item ID
target_id: Target work item ID
link_type: Link type reference name
- System.LinkTypes.Hierarchy-Forward (parent -> child)
- System.LinkTypes.Hierarchy-Reverse (child -> parent)
- System.LinkTypes.Related
- System.LinkTypes.Dependency-Forward (predecessor -> successor)
- System.LinkTypes.Dependency-Reverse (successor -> predecessor)
Returns:
Updated source work item
"""
url = f"{BASE_URL}/{PROJECT}/_apis/wit/workitems/{source_id}"
params = {"api-version": API_VERSION}
target_url = f"{BASE_URL}/{PROJECT}/_apis/wit/workitems/{target_id}"
operations = [
{
"op": "add",
"path": "/relations/-",
"value": {
"rel": link_type,
"url": target_url,
"attributes": {
"comment": "Linked via API"
}
}
}
]
response = requests.patch(url, headers=get_auth_header(), params=params, json=operations)
response.raise_for_status()
return response.json()
def run_wiql_query(wiql: str) -> List[Dict[str, Any]]:
"""
Execute a WIQL query and return work items.
Args:
wiql: WIQL query string
Returns:
List of work items matching the query
"""
# Execute query to get IDs
url = f"{BASE_URL}/{PROJECT}/_apis/wit/wiql"
params = {"api-version": API_VERSION}
headers = get_auth_header()
headers["Content-Type"] = "application/json"
body = {"query": wiql}
response = requests.post(url, headers=headers, params=params, json=body)
response.raise_for_status()
result = response.json()
# Extract IDs and fetch full work items
if "workItems" in result:
ids = [wi["id"] for wi in result["workItems"]]
if ids:
return get_work_items_batch(ids)
return []
def add_artifact_link(
work_item_id: int,
artifact_uri: str,
link_type: str = "ArtifactLink",
comment: Optional[str] = None
) -> Dict[str, Any]:
"""
Add an artifact link to a work item.
Args:
work_item_id: Work item ID
artifact_uri: VSTFS artifact URI
Examples:
- Branch: vstfs:///Git/Ref/{projectId}/{repoId}/GB{branchName}
- Commit: vstfs:///Git/Commit/{projectId}/{repoId}/{commitId}
- PR: vstfs:///Git/PullRequestId/{projectId}/{prId}
- Build: vstfs:///Build/Build/{projectId}/{buildId}
link_type: Link type name
comment: Optional comment
Returns:
Updated work item
"""
url = f"{BASE_URL}/{PROJECT}/_apis/wit/workitems/{work_item_id}"
params = {"api-version": API_VERSION}
link_value = {
"rel": link_type,
"url": artifact_uri
}
if comment:
link_value["attributes"] = {"comment": comment}
operations = [
{
"op": "add",
"path": "/relations/-",
"value": link_value
}
]
response = requests.patch(url, headers=get_auth_header(), params=params, json=operations)
response.raise_for_status()
return response.json()
# Example usage
if __name__ == "__main__":
# Example: Get a work item
print("Getting work item #1...")
try:
item = get_work_item(1)
print(f"Title: {item['fields'].get('System.Title')}")
print(f"State: {item['fields'].get('System.State')}")
except Exception as e:
print(f"Error: {e}")
# Example: Run WIQL query
print("\nQuerying active bugs...")
query = """
SELECT [System.Id], [System.Title], [System.State]
FROM workitems
WHERE [System.TeamProject] = @project
AND [System.WorkItemType] = 'Bug'
AND [System.State] = 'Active'
ORDER BY [System.CreatedDate] DESC
"""
try:
bugs = run_wiql_query(query)
for bug in bugs[:5]:
print(f" #{bug['id']}: {bug['fields'].get('System.Title')}")
except Exception as e:
print(f"Error: {e}")
# Example: Create work item
print("\nCreating new task...")
try:
new_task = create_work_item(
work_item_type="Task",
title="API Integration Task",
description="<p>Implement API integration for new feature</p>",
additional_fields={
"Microsoft.VSTS.Common.Priority": 2,
"Microsoft.VSTS.Scheduling.RemainingWork": 8
}
)
print(f"Created: #{new_task['id']}")
except Exception as e:
print(f"Error: {e}")