Connecting Azure DevOps to Claude

Last updated: November 2, 2026

This guide connects Azure DevOps (ADO) to the Claude desktop app on a Mac or Windows PC. Once it's connected, Claude can read your repos, work items, pull requests and pipelines, and it can create branches, commit changes and open pull requests for you.

If your code is on GitHub instead, see Connecting GitHub to Claude.

Claude signs in with your own Microsoft account, so it can only see and change what your Azure DevOps account already has access to.

Before you start

You'll need:

  • The Claude desktop app on a Mac or Windows PC.

  • Azure DevOps access at the Basic level or higher. Stakeholder access can't see Repos.

  • Node.js version 20 or later. Step 1 checks this for you.

Step 1: Check Node.js

  1. Open Terminal on a Mac or PowerShell on Windows.

    • Mac: press Cmd + Space, type Terminal and press Enter.

    • Windows: press the Windows key, type PowerShell and press Enter.

  2. Type node -v and press Enter.

  3. Check the number. If it's v20 or higher, go to Step 2. If you see an error or a lower number, install the LTS version from nodejs.org, then reopen Terminal or PowerShell.

Step 2: Add Azure DevOps to Claude

Choose one option. Option A is the easiest.

Option A: Run a command (easiest)

  1. Open the command for your computer below and copy all of it.

Mac command (for Terminal)

node -e '
const fs = require(`fs`), path = require(`path`);
const say = (m) => console.log(`\n` + m + `\n`);
const isObj = (x) => Object.prototype.toString.call(x) === `[object Object]`;

if (parseInt(process.versions.node, 10) < 20) {
  say(`Your Node.js version is too old. Install the LTS version from nodejs.org, then run this again. Nothing was changed.`);
  process.exit(1);
}

const file = path.join(process.env.HOME, `Library`, `Application Support`, `Claude`, `claude_desktop_config.json`);
const rl = require(`readline`).createInterface({ input: process.stdin, output: process.stdout });

rl.question(`Paste your Azure DevOps link or organization name, then press Enter: `, (answer) => {
  rl.close();

  let org = String(answer || ``).trim().replace(/^[\x22\x27<\s]+|[\x22\x27>\/\s]+$/g, ``);
  const m = org.match(/dev\.azure\.com\/([^\/?#\s]+)/i) || org.match(/^(?:https?:\/\/)?([^.\/\s]+)\.visualstudio\.com/i);
  if (m) { org = decodeURIComponent(m[1]); }
  if (/^[A-Za-z0-9][A-Za-z0-9-]*$/.test(org) === false) {
    say(`That does not look like an Azure DevOps organization name. Nothing was changed.`);
    process.exit(1);
  }

  const real = fs.existsSync(file) ? fs.realpathSync(file) : file;
  let config = {};
  if (fs.existsSync(real)) {
    let text = fs.readFileSync(real, `utf8`);
    if (text.charCodeAt(0) === 65279) { text = text.slice(1); }
    if (text.trim()) {
      try { config = JSON.parse(text); } catch (e) {
        say(`Your Claude settings file has a formatting problem, so it was left as is. Nothing was changed.`);
        process.exit(1);
      }
    }
  }
  if (isObj(config) === false || (config.mcpServers === undefined ? false : isObj(config.mcpServers) === false)) {
    say(`Your Claude settings file is not in the expected format, so it was left as is. Nothing was changed.`);
    process.exit(1);
  }

  const servers = config.mcpServers || {};
  if (Object.keys(servers).some((k) => k === `ado` || String(JSON.stringify(servers[k])).includes(`@azure-devops/mcp`))) {
    say(`Azure DevOps is already set up in Claude, so nothing was changed.`);
    process.exit(0);
  }
  config.mcpServers = servers;
  servers.ado = { command: `npx`, args: [`-y`, `@azure-devops/mcp`, org] };

  let backup = ``;
  const tmp = real + `.tmp`;
  try {
    fs.mkdirSync(path.dirname(real), { recursive: true });
    if (fs.existsSync(real)) {
      backup = real.replace(/\.json$/, ``) + `.backup-` + new Date().toISOString().slice(0, 19).replace(/[:T]/g, `-`) + `.json`;
      fs.copyFileSync(real, backup);
    }
    fs.writeFileSync(tmp, JSON.stringify(config, null, 2) + `\n`);
    JSON.parse(fs.readFileSync(tmp, `utf8`));
    fs.renameSync(tmp, real);
  } catch (e) {
    try { fs.unlinkSync(tmp); } catch (x) {}
    say(`Could not save your Claude settings (${e.message}). Nothing was changed.`);
    process.exit(1);
  }

  say(`Done. Azure DevOps (${org}) is now added to Claude.` + (backup ? ` A backup of your old settings was saved as ${path.basename(backup)}.` : ``) + ` Now quit Claude with Cmd + Q and open it again.`);
});
'

Windows command (for PowerShell)

node -e '
const fs = require(`fs`), path = require(`path`);
const say = (m) => console.log(`\n` + m + `\n`);
const isObj = (x) => Object.prototype.toString.call(x) === `[object Object]`;
const ls = (d) => { try { return fs.readdirSync(d); } catch (e) { return []; } };

if (parseInt(process.versions.node, 10) < 20) {
  say(`Your Node.js version is too old. Install the LTS version from nodejs.org, then run this again. Nothing was changed.`);
  process.exit(1);
}
const local = process.env.LOCALAPPDATA || ``;
const roaming = process.env.APPDATA || ``;
if (roaming === ``) {
  say(`Could not find your user folder. Nothing was changed.`);
  process.exit(1);
}

const places = [
  ...ls(path.join(local, `Claude-Data`)).filter((n) => /^claude_/i.test(n)).map((n) => path.join(local, `Claude-Data`, n, `claude_desktop_config.json`)),
  ...ls(path.join(local, `Packages`)).filter((n) => /^Claude_pzs8sxrjxfjjc$/i.test(n)).map((n) => path.join(local, `Packages`, n, `LocalCache`, `Roaming`, `Claude`, `claude_desktop_config.json`)),
  path.join(roaming, `Claude`, `claude_desktop_config.json`)
];
let files = places.filter((f) => fs.existsSync(f));
if (files.length === 0) { files = [places.find((f) => fs.existsSync(path.dirname(f))) || places[places.length - 1]]; }

const rl = require(`readline`).createInterface({ input: process.stdin, output: process.stdout });

rl.question(`Paste your Azure DevOps link or organization name, then press Enter: `, (answer) => {
  rl.close();

  let org = String(answer || ``).trim().replace(/^[\x22\x27<\s]+|[\x22\x27>\/\s]+$/g, ``);
  const m = org.match(/dev\.azure\.com\/([^\/?#\s]+)/i) || org.match(/^(?:https?:\/\/)?([^.\/\s]+)\.visualstudio\.com/i);
  if (m) { org = decodeURIComponent(m[1]); }
  if (/^[A-Za-z0-9][A-Za-z0-9-]*$/.test(org) === false) {
    say(`That does not look like an Azure DevOps organization name. Nothing was changed.`);
    process.exit(1);
  }

  const jobs = [];
  for (const file of files) {
    let config = {};
    if (fs.existsSync(file)) {
      let text = fs.readFileSync(file, `utf8`);
      if (text.charCodeAt(0) === 65279) { text = text.slice(1); }
      if (text.trim()) {
        try { config = JSON.parse(text); } catch (e) {
          say(`Your Claude settings file has a formatting problem, so it was left as is. Nothing was changed. File: ${file}`);
          process.exit(1);
        }
      }
    }
    if (isObj(config) === false || (config.mcpServers === undefined ? false : isObj(config.mcpServers) === false)) {
      say(`Your Claude settings file is not in the expected format, so it was left as is. Nothing was changed. File: ${file}`);
      process.exit(1);
    }
    jobs.push({ file, config });
  }

  const todo = jobs.filter((j) => Object.keys(j.config.mcpServers || {}).some((k) => k === `ado` || String(JSON.stringify(j.config.mcpServers[k])).includes(`@azure-devops/mcp`)) === false);
  if (todo.length === 0) {
    say(`Azure DevOps is already set up in Claude, so nothing was changed.`);
    process.exit(0);
  }

  const backups = [];
  for (const j of todo) {
    j.config.mcpServers = j.config.mcpServers || {};
    j.config.mcpServers.ado = { command: `cmd`, args: [`/c`, `npx`, `-y`, `@azure-devops/mcp`, org] };
    const tmp = j.file + `.tmp`;
    try {
      fs.mkdirSync(path.dirname(j.file), { recursive: true });
      if (fs.existsSync(j.file)) {
        const backup = j.file.replace(/\.json$/, ``) + `.backup-` + new Date().toISOString().slice(0, 19).replace(/[:T]/g, `-`) + `.json`;
        fs.copyFileSync(j.file, backup);
        backups.push(path.basename(backup));
      }
      fs.writeFileSync(tmp, JSON.stringify(j.config, null, 2) + `\n`);
      JSON.parse(fs.readFileSync(tmp, `utf8`));
      fs.renameSync(tmp, j.file);
    } catch (e) {
      try { fs.unlinkSync(tmp); } catch (x) {}
      say(`Could not save your Claude settings (${e.message}). That file was not changed. File: ${j.file}`);
      process.exit(1);
    }
  }

  say(`Done. Azure DevOps (${org}) is now added to Claude.` + (backups.length ? ` A backup of your old settings was saved as ${backups.join(` and `)}.` : ``) + ` Now quit Claude from the system tray (right-click the Claude icon near the clock, then Quit) and open it again.`);
});
'
  1. Paste it into Terminal (Mac) or PowerShell (Windows) and press Enter. On Windows, use PowerShell, not Command Prompt.

  2. When asked, paste your Azure DevOps link, for example https://dev.azure.com/yourcompany, and press Enter.

  3. Wait for the message that starts with Done, then go to Step 3.

The command only adds Azure DevOps to Claude's settings, and it saves a backup first. If anything looks wrong, it stops and changes nothing.

Option B: Edit the settings file yourself

  1. Open Claude's settings file.

    • Mac: in the menu bar, click Claude → Settings… → Developer → Edit Config.

    • Windows: in Claude, click File → Settings → Developer → Edit Config.

  2. Open claude_desktop_config.json in a code editor such as VS Code. On a Mac, don't use TextEdit, because it can break the file.

  3. Copy the block for your computer below and paste it inside the file's outer { }. Replace yourcompany with your organization name.

Mac block

"mcpServers": {
  "ado": {
    "command": "npx",
    "args": ["-y", "@azure-devops/mcp", "yourcompany"]
  }
}

Windows block

"mcpServers": {
  "ado": {
    "command": "cmd",
    "args": ["/c", "npx", "-y", "@azure-devops/mcp", "yourcompany"]
  }
}
  1. Check the commas. If the file already has "mcpServers", add only the "ado" part inside it, and put a comma between it and the next entry.

  2. Save the file.

Important: If you installed Claude from the Microsoft Store, Edit Config can open a settings file that Claude doesn't read. Use Option A instead.

Step 3: Restart Claude and sign in

  1. Quit Claude completely. Closing the window isn't enough.

    • Mac: press Cmd + Q.

    • Windows: right-click the Claude icon near the clock and click Quit.

  2. Open Claude again and start a new chat.

  3. Send List my Azure DevOps projects. If Claude asks to use an Azure DevOps tool, allow it.

  4. Sign in with your Azure DevOps Microsoft account in the browser window that opens.

You're connected when Claude replies with a list of your projects.

What you can ask Claude

Replace the words in angle brackets with your own names.

  • Show my assigned work items

  • List the repos in <project>

  • Summarize the open pull requests in <repo>

  • In <repo>, create a branch called <branch>, make this change: <change>, commit it and open a pull request into main

Working on a new branch with a pull request, as in the last example, lets you review the change before it reaches your main branch. Claude can't delete anything in Azure DevOps through this connection.

Troubleshooting

Problem

Fix

command not found: node on a Mac, or node is not recognized on Windows

Install the LTS version of Node.js from nodejs.org, reopen Terminal or PowerShell, and run the command again.

Red errors on Windows after pasting the command

You're in Command Prompt. Open PowerShell and run it again. Your settings weren't changed.

The command says the settings file has a formatting problem

The file was already broken, so nothing was changed. Open it as in Option B. VS Code underlines the problem in red.

The command says Azure DevOps is already set up, but it isn't working

Open the file as in Option B and check the organization name in the existing entry.

Azure DevOps is missing in Claude, or Settings → Developer shows it as failed

Claude can't find Node.js. Install the LTS version, then quit Claude completely (Step 3) and reopen it.

Claude can't see any repos

Your access level is probably Stakeholder. Ask your Azure DevOps admin to change it to Basic in Organization settings → Users.

403 or "not authorized" error

You signed in with a different Microsoft account. Quit Claude completely, reopen it, and sign in with your Azure DevOps account.

"Failed to find api location for area"

The organization name is wrong. It's case-sensitive and should be just the name, not a full link. Fix it in the settings file (Option B).

If you're stuck, reach out to your Noibu contact.