@vunguyen/ado-mcp-server
Azure DevOps MCP Server
A Model Context Protocol (MCP) server for Azure DevOps Work Item Tracking, enabling AI Agents (Claude Code, Cursor, OpenCode, Antigravity) to interact with Azure DevOps directly.
Version: 1.0.0
Status: Stable
Last Updated: 2026-07-19
Features
- MCP Tool Interface - 19 tools for work item management, state transitions, and backlog queries
- Secure Credential Management - Encrypted storage of PAT and NTLM credentials
- Schema-Agnostic - Dynamically discovers and adapts to any Azure DevOps process template
- Intelligent State Transitions - Semantic actions (start/review/complete/block) map to actual states
- Markdown Content - AI Agent provides Markdown; server converts to HTML automatically
- Single-User Local - Runs as a local process per user, no multi-tenant overhead
- Smart Caching - 1-hour schema cache reduces API calls to Azure DevOps
- Rate Limiting - Throttling and retry logic for Azure DevOps API limits
Installation
Prerequisites
- Node.js 18+
- npm or yarn
- Access to Azure DevOps Server 2022 (on-prem) or Azure DevOps Services (cloud)
- Personal Access Token (PAT) or NTLM credentials
Quick Start
# Install globally via npm
npx @vunguyen/ado-mcp-server
# Or install locally
npm install @vunguyen/ado-mcp-server
npx ado-mcp-server
The server will:
- Start MCP server on stdio - Ready for AI Agent clients (Claude Code, Cursor, etc.)
- Start Config UI - Available at
http://localhost:7939for profile management
Configuration in AI Agent
Claude Code
Add to ~/.claude/mcp.json:
{
"ado-mcp": {
"command": "npx",
"args": ["@vunguyen/ado-mcp-server"]
}
}
Then configure MCP in Claude Code settings.
Cursor, OpenCode, Antigravity
Similar process - add MCP configuration pointing to:
npx @vunguyen/ado-mcp-server
Configuration
Step 1: Add Connection Profile
Open Config UI:
http://localhost:7939Fill in connection details:
- Profile Name: Name to reference this connection (e.g., "My Azure DevOps")
- Base URL:
- Cloud:
https://dev.azure.com/myorgname - On-prem:
https://tfs.mycompany.com
- Cloud:
- Collection (on-prem only): Usually
DefaultCollection - Default Project: Optional, used as fallback
- Auth Type: PAT or NTLM
- Credential:
- PAT: Your personal access token
- NTLM:
username:password
Test Connection to verify credentials are valid
Step 2: Use in AI Agent
When AI Agent calls MCP tools, include profile parameter:
get_work_item(profile="My Azure DevOps", id=123)
Tool Reference
Profile Management (REST API)
These are configured via the Config UI, not MCP tools.
GET /api/profiles- List all profilesPOST /api/profiles- Create new profilePOST /api/profiles/{id}/test- Test connectionDELETE /api/profiles/{id}- Delete profile
MCP Tools for AI Agent
Read Operations
get_work_item(profile, id, includeRelations?, includeComments?)- Get work item detailsquery_work_items(profile, project?, wiql)- Free-form WIQL query (fallback)get_backlog(profile, project, team, backlogLevel)- Get prioritized backloglist_work_items_by_type(profile, project, workItemTypes[], ...)- Predefined queryget_remaining_work(profile, project, team, iterationPath?)- Calculate sprint metricslist_my_work_items(profile, project, assignedTo, ...)- Personal work items
Create Operations
create_work_item(profile, project, workItemType, title, description, acceptanceCriteria, ...)descriptionandacceptanceCriteriaare required- Server auto-converts Markdown → HTML
- Validates required markdown headings
create_bug(profile, project, title, reproSteps, acceptanceCriteria, description, ...)- All 4 parameters required (CN-13)
- Separate fields for repro steps vs. acceptance criteria
- Description must include risk/mitigation heading
State Management (Semantic - Preferred)
start_work(profile, id, comment?)→ Moves toInProgress(CN-15)mark_in_review(profile, id, comment?)→ Moves toResolved(CN-15)complete_work(profile, id, comment?)→ Moves toCompleted(CN-15)reopen_work(profile, id, comment?)→ Moves back toInProgress(CN-15)block_work(profile, id, reason)→ Adds[BLOCKED]comment, no state change (CN-15)
State Management (Fallback)
update_work_item(profile, id, fields)- Direct field update (CN-08)report_progress(profile, id, state?, comment)- State + comment (CN-09)
Bug Resolution
resolve_bug(profile, id, resolution, comment?)- Set resolution reason (Fixed, Duplicate, As Designed, Won't Fix, Cannot Reproduce)close_bug(profile, id, comment?)- Close completed bug
Relationships
link_work_items(profile, sourceId, targetId, linkType, force?)- Parent-child or related links
Architecture
Layered Design
Transport Layer (stdio + REST)
↓
Tool Handler Layer (19 MCP tools + Config UI)
↓
Domain Layer (Business Logic - State Machine, Validation)
↓
Infrastructure Layer (Azure DevOps Client, Config Store, Caching)
Data Storage
- Config Store (
~/.ado-mcp-server/config.json): Encrypted connection profiles - Schema Cache (in-memory, 1-hour TTL): Work item types, fields, states, transitions
- Idempotency Cache (in-memory, 24-hour TTL): Prevents duplicate tool calls
- Logs (
~/.ado-mcp-server/logs/): Audit trail (no credentials logged)
Key Behaviors
| Behavior | Details |
|---|---|
| Rate Limiting | 5-second minimum between tool calls per profile; auto-sleep 60s on HTTP 429 |
| Schema Agnostic | No hardcoded field/type/state names - all discovered dynamically |
| Profile Required | Every tool call must specify profile explicitly (no "active" profile) |
| Markdown → HTML | AI Agent provides Markdown; server converts before sending to Azure DevOps |
| Retry Logic | Up to 5 retries for failed comment additions; automatic retry on rate limit |
| Error Messages | Azure DevOps errors passed through verbatim; server errors are sanitized |
Troubleshooting
Connection Failed (401/403)
401 Unauthorized:
- PAT: Verify token is valid and hasn't expired
- NTLM: Verify username/password is correct for your domain
403 Forbidden:
- Your user account lacks permissions for the specified project/work item
- Request appropriate permissions from your Azure DevOps administrator
Connection Failed (404)
- Cloud: Verify base URL format is
https://dev.azure.com/myorgname(not.com) - On-prem: Verify collection name (usually
DefaultCollection) - Verify project name is spelled correctly
SSL Certificate Error (on-prem)
If using a self-signed certificate:
- In Config UI, enable "Allow Self-Signed SSL Certificates"
- Understand the security implications: only enable for trusted internal servers
Rate Limit (HTTP 429)
The server automatically handles rate limiting:
- Sleeps 60 seconds when encountering 429
- Retries the failed request
- If still persistent, reduce frequency of tool calls
Credentials Not Saved
- Ensure Config UI can write to
~/.ado-mcp-server/ - Check file permissions:
ls -la ~/.ado-mcp-server/ - Review logs in
~/.ado-mcp-server/logs/
Logs
Logs are written to ~/.ado-mcp-server/logs/:
- ado-mcp-YYYY-MM-DD.log - Info and debug level
- error-YYYY-MM-DD.log - Errors only
Log Rotation: Automatically rotates when file exceeds 10MB; keeps 5 days of history.
Setting Log Level
LOG_LEVEL=debug npx @vunguyen/ado-mcp-server
Levels: error, warn, info, debug
Security Considerations
What We Do:
- Encrypt credentials at rest using AES-256-CBC with local encryption key
- Never log credentials, even in error cases
- Bind Config UI to localhost only (127.0.0.1)
- Use official Azure DevOps SDK for all API calls
- Validate all inputs before passing to Azure DevOps
What You Should Do:
- Don't share PAT tokens - treat them like passwords
- For NTLM, ensure your machine is on a secure network
- Regularly rotate PAT tokens (every 90 days recommended)
- Don't enable "Allow Self-Signed Cert" unless on trusted internal network
- Backup
~/.ado-mcp-server/if switching machines
What We Don't Do:
- Multi-tenant isolation (single-user per instance)
- Network-level authentication (beyond credential in profile)
- Audit logging of AI Agent actions (log is local only)
Development
Local Setup
# Install dependencies
npm install
# Type checking
npm run type-check
# Build
npm run build
# Run in development
npm run dev
# Run tests
npm run test
npm run test:coverage
# Lint
npm run lint
Project Structure
source/
├── src/
│ ├── bin/
│ │ └── index.ts # Entry point
│ ├── domain/
│ │ ├── stateMachine.ts # State transition logic (CN-15)
│ │ ├── workItemRules.ts # Validation rules (CN-06, CN-13)
│ │ └── profileResolver.ts # Profile resolution (CN-11)
│ ├── handlers/
│ │ ├── toolHandler.ts # Base class for all tools
│ │ ├── toolRegistry.ts # Tool definitions
│ │ ├── mcp/ # Individual tool implementations
│ │ └── rest/ # Config UI REST endpoints
│ ├── infra/
│ │ ├── adoClient.ts # Azure DevOps API wrapper
│ │ ├── configStore.ts # Profile storage & encryption
│ │ ├── schemaCache.ts # Schema caching
│ │ ├── markdownConverter.ts # Markdown → HTML
│ │ ├── rateLimiter.ts # Rate limiting
│ │ ├── logger.ts # Logging
│ │ └── exceptionHandler.ts # Error handling
│ ├── mcp/
│ │ └── server.ts # MCP server setup
│ ├── ui/
│ │ ├── server.ts # Config UI Express app
│ │ └── public/ # Static HTML/JS
│ └── types/
│ └── index.ts # Type definitions
├── package.json
├── tsconfig.json
├── vitest.config.ts
└── README.md
Adding a New Tool
- Create handler in
src/handlers/mcp/myTool.ts:
import { ToolHandler } from '../toolHandler';
import { ToolInput, ToolResult } from '@types/index';
export class MyToolHandler extends ToolHandler {
name = 'my_tool';
protected async handle(input: ToolInput, profile: any): Promise<ToolResult> {
// Implementation
return { success: true, data: {} };
}
}
- Add tool definition to
src/handlers/toolRegistry.ts - Register in
src/mcp/server.ts - Write tests in
src/handlers/mcp/__tests__/myTool.test.ts
API Versioning
This server targets:
- Azure DevOps REST API 7.0 (main endpoints)
- Azure DevOps Comments API 7.0-preview.3 (on-prem Server 2022)
- Azure DevOps Work Item Tracking API 7.0
Cloud instances may use different preview versions for Comments API - the SDK abstracts this difference.
Performance
| Operation | Target SLA | Typical |
|---|---|---|
| Read (get, query) | < 3s | 1-2s |
| Write (create, update) | < 5s | 2-4s |
| Batch (50 items) | < 15s | 8-12s |
| Schema cache hit | < 50ms | 10-20ms |
| First schema load (cache miss) | < 3s | 1-2s |
Support
For issues or questions:
- Check the logs:
~/.ado-mcp-server/logs/ - Enable debug logging:
LOG_LEVEL=debug npx @vunguyen/ado-mcp-server - Review documentation in
/docs/
License
MIT