# ADO MCP
This project uses [FastMCP 2.0](https://gofastmcp.com/getting-started/installation) python sdk to interact with Azure DevOps.
## Setup
1. **Install Dependencies**:
```bash
task install
```
2. **Set up Authentication**:
The MCP server supports multiple authentication methods (in order of precedence):
### Option 1: Personal Access Token (PAT)
**Environment Variable Method:**
```bash
task setup-env
```
This creates a `.env` file with your Personal Access Token (PAT) and other necessary variables.
**Direct Configuration:**
```bash
export AZURE_DEVOPS_EXT_PAT="your-personal-access-token"
export ADO_ORGANIZATION_URL="https://dev.azure.com/YourOrg"
```
### Option 2: Azure CLI Authentication (Recommended)
If you already use Azure CLI, you can authenticate using your existing session:
```bash
# Login to Azure (if not already logged in)
az login
# The MCP server will automatically use your Azure CLI credentials
task run
```
**Benefits of Azure CLI authentication:**
- No need to manage Personal Access Tokens
- Uses your existing Azure credentials
- More secure than storing PATs
- Automatically refreshes tokens
**Note:** Azure CLI authentication requires the user to be logged in with an account that has access to the Azure DevOps organization.
## Testing
- **Run Tests**:
```bash
task test
```
- **Test Coverage**:
```bash
task coverage
```
## Usage
- **Run the MCP Server**:
```bash
task run
```
- **Inspect the MCP Server**:
```bash
task inspect
```
## Working with Azure DevOps URLs
### Understanding Azure DevOps URL Structure
When working with Azure DevOps URLs from the web interface, it's important to understand the difference between **build/run IDs** and **pipeline definition IDs**:
**Example URL**: `https://dev.azure.com/RussellBoley/ado-mcp/_build/results?buildId=324&view=results`
- **Organization**: `RussellBoley` (from the URL path)
- **Project**: `ado-mcp` (from the URL path)
- **buildId=324**: This is a **run ID** (specific execution instance), NOT a pipeline definition ID
### Getting Pipeline Information from Build URLs
To work with a specific build/run from an Azure DevOps URL:
1. **Extract URL components**:
- Organization: `RussellBoley`
- Project: `ado-mcp`
- Build/Run ID: `324` (from `buildId` parameter)
2. **Use `get_build_by_id` to find the pipeline**:
```python
# Get build details to find the pipeline definition
build_details = await client.call_tool("get_build_by_id", {
"project_id": "49e895da-15c6-4211-97df-65c547a59c22", # ado-mcp project ID
"build_id": 324 # The buildId from the URL
})
# Extract pipeline information
pipeline_id = build_details.data["definition"]["id"] # e.g., 84
pipeline_name = build_details.data["definition"]["name"] # e.g., "log-test-complex"
```
3. **Then use pipeline-specific tools**:
```python
# Get detailed run information
run_details = await client.call_tool("get_pipeline_run", {
"project_id": "49e895da-15c6-4211-97df-65c547a59c22",
"pipeline_id": pipeline_id, # 84
"run_id": 324 # Same as buildId
})
# Get failure analysis if needed
failure_summary = await client.call_tool("get_pipeline_failure_summary", {
"project_id": "49e895da-15c6-4211-97df-65c547a59c22",
"pipeline_id": pipeline_id,
"run_id": 324
})
```
### Common Mistake to Avoid
❌ **Don't do this**:
```python
# This will fail - you can't guess the pipeline_id
await client.call_tool("get_pipeline_run", {
"project_id": "49e895da-15c6-4211-97df-65c547a59c22",
"pipeline_id": 15, # Wrong! This is just a guess
"run_id": 324
})
```
✅ **Do this instead**:
```python
# First, get the build details to find the correct pipeline_id
build_data = await client.call_tool("get_build_by_id", {
"project_id": "49e895da-15c6-4211-97df-65c547a59c22",
"build_id": 324 # buildId from URL
})
pipeline_id = build_data.data["definition"]["id"] # Now you have the correct pipeline_id
# Then use it for pipeline-specific operations
await client.call_tool("get_pipeline_run", {
"project_id": "49e895da-15c6-4211-97df-65c547a59c22",
"pipeline_id": pipeline_id, # Correct pipeline_id
"run_id": 324
})
```
### Available Tools for Build/Pipeline Analysis
- **`get_build_by_id`**: Get build details and extract pipeline information from a build/run ID
- **`get_pipeline_run`**: Get detailed run information (requires both pipeline_id and run_id)
- **`get_pipeline_failure_summary`**: Analyze failures with root cause analysis
- **`get_failed_step_logs`**: Get logs for failed steps
- **`get_pipeline_timeline`**: Get execution timeline
- **`list_pipeline_logs`**: List all available logs
- **`get_log_content_by_id`**: Get specific log content
- **`run_pipeline_and_get_outcome`**: Run a pipeline and wait for completion with analysis
Raw data
{
"_id": null,
"home_page": null,
"name": "ado-mcp-raboley",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "azure-devops, mcp, model-context-protocol, ci-cd, pipelines, automation, devops, azure, llm, ai-tools",
"author": null,
"author_email": "Russell Boley <countrypeaches@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/62/46/217e1bd6198e61612f4c6195202e23895a42eaeea1399c4f2a2b3308f32e/ado_mcp_raboley-0.0.1.tar.gz",
"platform": null,
"description": "# ADO MCP\n\nThis project uses [FastMCP 2.0](https://gofastmcp.com/getting-started/installation) python sdk to interact with Azure DevOps.\n\n## Setup\n\n1. **Install Dependencies**:\n ```bash\n task install\n ```\n\n2. **Set up Authentication**:\n The MCP server supports multiple authentication methods (in order of precedence):\n\n ### Option 1: Personal Access Token (PAT)\n \n **Environment Variable Method:**\n ```bash\n task setup-env\n ```\n This creates a `.env` file with your Personal Access Token (PAT) and other necessary variables.\n\n **Direct Configuration:**\n ```bash\n export AZURE_DEVOPS_EXT_PAT=\"your-personal-access-token\"\n export ADO_ORGANIZATION_URL=\"https://dev.azure.com/YourOrg\"\n ```\n\n ### Option 2: Azure CLI Authentication (Recommended)\n \n If you already use Azure CLI, you can authenticate using your existing session:\n \n ```bash\n # Login to Azure (if not already logged in)\n az login\n \n # The MCP server will automatically use your Azure CLI credentials\n task run\n ```\n \n **Benefits of Azure CLI authentication:**\n - No need to manage Personal Access Tokens\n - Uses your existing Azure credentials \n - More secure than storing PATs\n - Automatically refreshes tokens\n \n **Note:** Azure CLI authentication requires the user to be logged in with an account that has access to the Azure DevOps organization.\n\n## Testing\n\n- **Run Tests**:\n ```bash\n task test\n ```\n\n- **Test Coverage**:\n ```bash\n task coverage\n ```\n\n## Usage\n\n- **Run the MCP Server**:\n ```bash\n task run\n ```\n\n- **Inspect the MCP Server**:\n ```bash\n task inspect\n ```\n\n## Working with Azure DevOps URLs\n\n### Understanding Azure DevOps URL Structure\n\nWhen working with Azure DevOps URLs from the web interface, it's important to understand the difference between **build/run IDs** and **pipeline definition IDs**:\n\n**Example URL**: `https://dev.azure.com/RussellBoley/ado-mcp/_build/results?buildId=324&view=results`\n\n- **Organization**: `RussellBoley` (from the URL path)\n- **Project**: `ado-mcp` (from the URL path)\n- **buildId=324**: This is a **run ID** (specific execution instance), NOT a pipeline definition ID\n\n### Getting Pipeline Information from Build URLs\n\nTo work with a specific build/run from an Azure DevOps URL:\n\n1. **Extract URL components**:\n - Organization: `RussellBoley`\n - Project: `ado-mcp` \n - Build/Run ID: `324` (from `buildId` parameter)\n\n2. **Use `get_build_by_id` to find the pipeline**:\n ```python\n # Get build details to find the pipeline definition\n build_details = await client.call_tool(\"get_build_by_id\", {\n \"project_id\": \"49e895da-15c6-4211-97df-65c547a59c22\", # ado-mcp project ID\n \"build_id\": 324 # The buildId from the URL\n })\n \n # Extract pipeline information\n pipeline_id = build_details.data[\"definition\"][\"id\"] # e.g., 84\n pipeline_name = build_details.data[\"definition\"][\"name\"] # e.g., \"log-test-complex\"\n ```\n\n3. **Then use pipeline-specific tools**:\n ```python\n # Get detailed run information\n run_details = await client.call_tool(\"get_pipeline_run\", {\n \"project_id\": \"49e895da-15c6-4211-97df-65c547a59c22\",\n \"pipeline_id\": pipeline_id, # 84\n \"run_id\": 324 # Same as buildId\n })\n \n # Get failure analysis if needed\n failure_summary = await client.call_tool(\"get_pipeline_failure_summary\", {\n \"project_id\": \"49e895da-15c6-4211-97df-65c547a59c22\",\n \"pipeline_id\": pipeline_id,\n \"run_id\": 324\n })\n ```\n\n### Common Mistake to Avoid\n\n\u274c **Don't do this**: \n```python\n# This will fail - you can't guess the pipeline_id\nawait client.call_tool(\"get_pipeline_run\", {\n \"project_id\": \"49e895da-15c6-4211-97df-65c547a59c22\",\n \"pipeline_id\": 15, # Wrong! This is just a guess\n \"run_id\": 324\n})\n```\n\n\u2705 **Do this instead**:\n```python\n# First, get the build details to find the correct pipeline_id\nbuild_data = await client.call_tool(\"get_build_by_id\", {\n \"project_id\": \"49e895da-15c6-4211-97df-65c547a59c22\",\n \"build_id\": 324 # buildId from URL\n})\n\npipeline_id = build_data.data[\"definition\"][\"id\"] # Now you have the correct pipeline_id\n\n# Then use it for pipeline-specific operations\nawait client.call_tool(\"get_pipeline_run\", {\n \"project_id\": \"49e895da-15c6-4211-97df-65c547a59c22\", \n \"pipeline_id\": pipeline_id, # Correct pipeline_id\n \"run_id\": 324\n})\n```\n\n### Available Tools for Build/Pipeline Analysis\n\n- **`get_build_by_id`**: Get build details and extract pipeline information from a build/run ID\n- **`get_pipeline_run`**: Get detailed run information (requires both pipeline_id and run_id)\n- **`get_pipeline_failure_summary`**: Analyze failures with root cause analysis\n- **`get_failed_step_logs`**: Get logs for failed steps\n- **`get_pipeline_timeline`**: Get execution timeline\n- **`list_pipeline_logs`**: List all available logs\n- **`get_log_content_by_id`**: Get specific log content\n- **`run_pipeline_and_get_outcome`**: Run a pipeline and wait for completion with analysis \n",
"bugtrack_url": null,
"license": null,
"summary": "An MCP server for Azure DevOps pipelines.",
"version": "0.0.1",
"project_urls": {
"Documentation": "https://github.com/raboley/ado-mcp#readme",
"Homepage": "https://github.com/raboley/ado-mcp",
"Issues": "https://github.com/raboley/ado-mcp/issues",
"Repository": "https://github.com/raboley/ado-mcp"
},
"split_keywords": [
"azure-devops",
" mcp",
" model-context-protocol",
" ci-cd",
" pipelines",
" automation",
" devops",
" azure",
" llm",
" ai-tools"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "75de94e944b80c093c9a47d972f71332fe09df5999be30b579c552daf82eb502",
"md5": "6333140d71edc87e0e436a1f49ce09ec",
"sha256": "a5fb5eaaf617772e74a65a50685c80d8f300498622afcd457fb28db8c4b7e9a5"
},
"downloads": -1,
"filename": "ado_mcp_raboley-0.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6333140d71edc87e0e436a1f49ce09ec",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 29373,
"upload_time": "2025-07-14T15:04:31",
"upload_time_iso_8601": "2025-07-14T15:04:31.133131Z",
"url": "https://files.pythonhosted.org/packages/75/de/94e944b80c093c9a47d972f71332fe09df5999be30b579c552daf82eb502/ado_mcp_raboley-0.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "6246217e1bd6198e61612f4c6195202e23895a42eaeea1399c4f2a2b3308f32e",
"md5": "47c8e5dc2674e7ef497cf82c66dc1dfd",
"sha256": "aa4078647f9f607a4b65e68c9ac9cd7bce406d48e8f13b4c7d3385d1ad03d042"
},
"downloads": -1,
"filename": "ado_mcp_raboley-0.0.1.tar.gz",
"has_sig": false,
"md5_digest": "47c8e5dc2674e7ef497cf82c66dc1dfd",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 49275,
"upload_time": "2025-07-14T15:04:32",
"upload_time_iso_8601": "2025-07-14T15:04:32.101089Z",
"url": "https://files.pythonhosted.org/packages/62/46/217e1bd6198e61612f4c6195202e23895a42eaeea1399c4f2a2b3308f32e/ado_mcp_raboley-0.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-14 15:04:32",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "raboley",
"github_project": "ado-mcp#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "ado-mcp-raboley"
}