diff --git a/mcp.json b/mcp.json index e3b0b02b..569f5b70 100644 --- a/mcp.json +++ b/mcp.json @@ -3,14 +3,20 @@ { "id": "ado_org", "type": "promptString", - "description": "Azure DevOps organization name (e.g. 'contoso')" + "description": "Azure DevOps organization name (e.g. 'contoso')" + }, + { + "id": "domains", + "type": "promptString", + "description": "Enter one or more Azure DevOps domains separated by commas. Available options: advanced-security, builds, core, releases, repositories, search, test-plans, wiki, work, work-items, all. (e.g. 'core,work-items,builds' or 'all')", + "default": "all" } ], "servers": { "ado": { "type": "stdio", "command": "mcp-server-azuredevops", - "args": ["${input:ado_org}"] + "args": ["${input:ado_org}", "${input:domains}"] } } } diff --git a/package-lock.json b/package-lock.json index d2c362fe..b0c30965 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@azure-devops/mcp", - "version": "1.3.1", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@azure-devops/mcp", - "version": "1.3.1", + "version": "2.0.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 1466f255..21bd53ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@azure-devops/mcp", - "version": "1.3.1", + "version": "2.0.0", "description": "MCP server for interacting with Azure DevOps", "license": "MIT", "author": "Microsoft Corporation", diff --git a/src/domains.ts b/src/domains.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/index.ts b/src/index.ts index 79cc9a32..a835224e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,18 +14,27 @@ import { configurePrompts } from "./prompts.js"; import { configureAllTools } from "./tools.js"; import { UserAgentComposer } from "./useragent.js"; import { packageVersion } from "./version.js"; +import { DomainsManager } from "./shared/domains.js"; // Parse command line arguments using yargs const argv = yargs(hideBin(process.argv)) .scriptName("mcp-server-azuredevops") - .usage("Usage: $0 [options]") + .usage("Usage: $0 [domains...] [options]") .version(packageVersion) - .command("$0 ", "Azure DevOps MCP Server", (yargs) => { + .command("$0 [domains...] [options]", "Azure DevOps MCP Server", (yargs) => { yargs.positional("organization", { describe: "Azure DevOps organization name", type: "string", + demandOption: true, }); }) + .option("domains", { + alias: "d", + describe: "Domain(s) to enable: 'all' for everything, or specific domains like 'repositories builds work'. Defaults to 'all'.", + type: "string", + array: true, + default: "all", + }) .option("tenant", { alias: "t", describe: "Azure tenant ID (optional, required for multi-tenant scenarios)", @@ -34,10 +43,14 @@ const argv = yargs(hideBin(process.argv)) .help() .parseSync(); -export const orgName = argv.organization as string; const tenantId = argv.tenant; + +export const orgName = argv.organization as string; const orgUrl = "https://dev.azure.com/" + orgName; +const domainsManager = new DomainsManager(argv.domains); +export const enabledDomains = domainsManager.getEnabledDomains(); + async function getAzureDevOpsToken(): Promise { if (process.env.ADO_MCP_AZURE_TOKEN_CREDENTIALS) { process.env.AZURE_TOKEN_CREDENTIALS = process.env.ADO_MCP_AZURE_TOKEN_CREDENTIALS; @@ -84,7 +97,7 @@ async function main() { configurePrompts(server); - configureAllTools(server, getAzureDevOpsToken, getAzureDevOpsClient(userAgentComposer), () => userAgentComposer.userAgent); + configureAllTools(server, getAzureDevOpsToken, getAzureDevOpsClient(userAgentComposer), () => userAgentComposer.userAgent, enabledDomains); const transport = new StdioServerTransport(); await server.connect(transport); diff --git a/src/prompts.ts b/src/prompts.ts index e8f5f3b3..bbd7960f 100644 --- a/src/prompts.ts +++ b/src/prompts.ts @@ -4,7 +4,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { CORE_TOOLS } from "./tools/core.js"; -import { WORKITEM_TOOLS } from "./tools/workitems.js"; +import { WORKITEM_TOOLS } from "./tools/work-items.js"; function configurePrompts(server: McpServer) { server.prompt("Projects", "Lists all projects in the Azure DevOps organization.", {}, () => ({ diff --git a/src/shared/domains.ts b/src/shared/domains.ts new file mode 100644 index 00000000..51ee4fa8 --- /dev/null +++ b/src/shared/domains.ts @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * Available Azure DevOps MCP domains + */ +export enum Domain { + ADVANCED_SECURITY = "advanced-security", + BUILDS = "builds", + CORE = "core", + RELEASES = "releases", + REPOSITORIES = "repositories", + SEARCH = "search", + TEST_PLANS = "test-plans", + WIKI = "wiki", + WORK = "work", + WORK_ITEMS = "work-items", +} + +/** + * Manages domain parsing and validation for Azure DevOps MCP server tools + */ +export class DomainsManager { + private static readonly AVAILABLE_DOMAINS = Object.values(Domain); + + private readonly enabledDomains: Set; + + constructor(domainsInput?: string | string[]) { + this.enabledDomains = new Set(); + const normalizedInput = DomainsManager.parseDomainsInput(domainsInput); + this.parseDomains(normalizedInput); + } + + /** + * Parse and validate domains from input + * @param domainsInput - Either "all", single domain name, array of domain names, or undefined (defaults to "all") + */ + private parseDomains(domainsInput?: string | string[]): void { + if (!domainsInput) { + console.log("No domains specified, enabling all domains for backward compatibility"); + this.enableAllDomains(); + return; + } + + if (Array.isArray(domainsInput)) { + this.handleArrayInput(domainsInput); + return; + } + + this.handleStringInput(domainsInput); + } + + private handleArrayInput(domainsInput: string[]): void { + if (domainsInput.length === 0) { + console.log("No valid domains specified, enabling all domains by default"); + this.enableAllDomains(); + return; + } + + if (domainsInput.length === 1 && domainsInput[0] === "all") { + this.enableAllDomains(); + return; + } + + const domains = domainsInput.map((d) => d.trim().toLowerCase()); + this.validateAndAddDomains(domains); + } + + private handleStringInput(domainsInput: string): void { + if (domainsInput === "all") { + this.enableAllDomains(); + return; + } + + const domains = [domainsInput.trim().toLowerCase()]; + this.validateAndAddDomains(domains); + } + + private validateAndAddDomains(domains: string[]): void { + domains.forEach((domain) => { + if ((Object.values(Domain) as string[]).includes(domain)) { + this.enabledDomains.add(domain); + } else { + console.warn(`Warning: Unknown domain '${domain}'. Available domains: ${Object.values(Domain).join(", ")}`); + } + }); + + if (this.enabledDomains.size === 0) { + console.log("No valid domains specified, enabling all domains by default"); + this.enableAllDomains(); + } + } + + private enableAllDomains(): void { + Object.values(Domain).forEach((domain) => this.enabledDomains.add(domain)); + } + + /** + * Check if a specific domain is enabled + * @param domain - Domain name to check + * @returns true if domain is enabled + */ + public isDomainEnabled(domain: string): boolean { + return this.enabledDomains.has(domain); + } + + /** + * Get all enabled domains + * @returns Set of enabled domain names + */ + public getEnabledDomains(): Set { + return new Set(this.enabledDomains); + } + + /** + * Get list of all available domains + * @returns Array of available domain names + */ + public static getAvailableDomains(): string[] { + return Object.values(Domain); + } + + /** + * Parse domains input from string or array to a normalized array of strings + * @param domainsInput - Domains input to parse + * @returns Normalized array of domain strings + */ + public static parseDomainsInput(domainsInput?: string | string[]): string[] { + if (!domainsInput) { + return []; + } + + if (typeof domainsInput === "string") { + return domainsInput.split(",").map((d) => d.trim().toLowerCase()); + } + + return domainsInput.map((d) => d.trim().toLowerCase()); + } +} diff --git a/src/tools.ts b/src/tools.ts index 31824655..34d75210 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -5,28 +5,35 @@ import { AccessToken } from "@azure/identity"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { WebApi } from "azure-devops-node-api"; -import { configureAdvSecTools } from "./tools/advsec.js"; +import { Domain } from "./shared/domains.js"; +import { configureAdvSecTools } from "./tools/advanced-security.js"; import { configureBuildTools } from "./tools/builds.js"; import { configureCoreTools } from "./tools/core.js"; import { configureReleaseTools } from "./tools/releases.js"; -import { configureRepoTools } from "./tools/repos.js"; +import { configureRepoTools } from "./tools/repositories.js"; import { configureSearchTools } from "./tools/search.js"; -import { configureTestPlanTools } from "./tools/testplans.js"; +import { configureTestPlanTools } from "./tools/test-plans.js"; import { configureWikiTools } from "./tools/wiki.js"; import { configureWorkTools } from "./tools/work.js"; -import { configureWorkItemTools } from "./tools/workitems.js"; +import { configureWorkItemTools } from "./tools/work-items.js"; -function configureAllTools(server: McpServer, tokenProvider: () => Promise, connectionProvider: () => Promise, userAgentProvider: () => string) { - configureCoreTools(server, tokenProvider, connectionProvider, userAgentProvider); - configureWorkTools(server, tokenProvider, connectionProvider); - configureBuildTools(server, tokenProvider, connectionProvider, userAgentProvider); - configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider); - configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider); - configureReleaseTools(server, tokenProvider, connectionProvider); - configureWikiTools(server, tokenProvider, connectionProvider); - configureTestPlanTools(server, tokenProvider, connectionProvider); - configureSearchTools(server, tokenProvider, connectionProvider, userAgentProvider); - configureAdvSecTools(server, tokenProvider, connectionProvider); +function configureAllTools(server: McpServer, tokenProvider: () => Promise, connectionProvider: () => Promise, userAgentProvider: () => string, enabledDomains: Set) { + const configureIfDomainEnabled = (domain: string, configureFn: () => void) => { + if (enabledDomains.has(domain)) { + configureFn(); + } + }; + + configureIfDomainEnabled(Domain.CORE, () => configureCoreTools(server, tokenProvider, connectionProvider, userAgentProvider)); + configureIfDomainEnabled(Domain.WORK, () => configureWorkTools(server, tokenProvider, connectionProvider)); + configureIfDomainEnabled(Domain.BUILDS, () => configureBuildTools(server, tokenProvider, connectionProvider, userAgentProvider)); + configureIfDomainEnabled(Domain.REPOSITORIES, () => configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider)); + configureIfDomainEnabled(Domain.WORK_ITEMS, () => configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider)); + configureIfDomainEnabled(Domain.RELEASES, () => configureReleaseTools(server, tokenProvider, connectionProvider)); + configureIfDomainEnabled(Domain.WIKI, () => configureWikiTools(server, tokenProvider, connectionProvider)); + configureIfDomainEnabled(Domain.TEST_PLANS, () => configureTestPlanTools(server, tokenProvider, connectionProvider)); + configureIfDomainEnabled(Domain.SEARCH, () => configureSearchTools(server, tokenProvider, connectionProvider, userAgentProvider)); + configureIfDomainEnabled(Domain.ADVANCED_SECURITY, () => configureAdvSecTools(server, tokenProvider, connectionProvider)); } export { configureAllTools }; diff --git a/src/tools/advsec.ts b/src/tools/advanced-security.ts similarity index 100% rename from src/tools/advsec.ts rename to src/tools/advanced-security.ts diff --git a/src/tools/repos.ts b/src/tools/repositories.ts similarity index 100% rename from src/tools/repos.ts rename to src/tools/repositories.ts diff --git a/src/tools/testplans.ts b/src/tools/test-plans.ts similarity index 100% rename from src/tools/testplans.ts rename to src/tools/test-plans.ts diff --git a/src/tools/workitems.ts b/src/tools/work-items.ts similarity index 100% rename from src/tools/workitems.ts rename to src/tools/work-items.ts diff --git a/src/version.ts b/src/version.ts index 8b32b968..856af813 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const packageVersion = "1.3.1"; +export const packageVersion = "2.0.0"; diff --git a/test/src/domains.test.ts b/test/src/domains.test.ts new file mode 100644 index 00000000..361eaeef --- /dev/null +++ b/test/src/domains.test.ts @@ -0,0 +1,222 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { DomainsManager } from "../../src/shared/domains"; + +describe("DomainsManager backward compatibility", () => { + // Mock console.log and console.warn to avoid test output noise + let consoleSpy: jest.SpyInstance; + let warnSpy: jest.SpyInstance; + + beforeEach(() => { + consoleSpy = jest.spyOn(console, "log").mockImplementation(); + warnSpy = jest.spyOn(console, "warn").mockImplementation(); + }); + + afterEach(() => { + consoleSpy.mockRestore(); + warnSpy.mockRestore(); + }); + + describe("constructor", () => { + it("should enable all domains when no input is provided (backward compatibility)", () => { + const manager = new DomainsManager(); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(10); + expect(enabledDomains.has("advanced-security")).toBe(true); + expect(enabledDomains.has("builds")).toBe(true); + expect(enabledDomains.has("core")).toBe(true); + expect(enabledDomains.has("releases")).toBe(true); + expect(enabledDomains.has("repositories")).toBe(true); + expect(enabledDomains.has("search")).toBe(true); + expect(enabledDomains.has("test-plans")).toBe(true); + expect(enabledDomains.has("wiki")).toBe(true); + expect(enabledDomains.has("work")).toBe(true); + expect(enabledDomains.has("work-items")).toBe(true); + + expect(consoleSpy).toHaveBeenCalledWith("No valid domains specified, enabling all domains by default"); + }); + + it("should enable all domains when undefined is passed", () => { + const manager = new DomainsManager(undefined); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(10); + expect(Array.from(enabledDomains).sort()).toEqual(["advanced-security", "builds", "core", "releases", "repositories", "search", "test-plans", "wiki", "work", "work-items"]); + }); + + it("should enable all domains when null is passed", () => { + // @ts-ignore - Testing null input for backward compatibility + const manager = new DomainsManager(null); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(10); + }); + }); + + describe("string input handling", () => { + it("should enable all domains when 'all' string is passed", () => { + const manager = new DomainsManager("all"); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(10); + expect(enabledDomains.has("repositories")).toBe(true); + expect(enabledDomains.has("builds")).toBe(true); + }); + + it("should enable single domain when valid domain name is passed", () => { + const manager = new DomainsManager("repositories"); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(1); + expect(enabledDomains.has("repositories")).toBe(true); + }); + + it("should handle case insensitive domain names", () => { + const manager = new DomainsManager("REPOSITORIES"); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(1); + expect(enabledDomains.has("repositories")).toBe(true); + }); + + it("should warn and enable all domains when invalid domain is passed", () => { + const manager = new DomainsManager("invalid-domain"); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(10); + expect(warnSpy).toHaveBeenCalledWith( + "Warning: Unknown domain 'invalid-domain'. Available domains: advanced-security, builds, core, releases, repositories, search, test-plans, wiki, work, work-items" + ); + expect(consoleSpy).toHaveBeenCalledWith("No valid domains specified, enabling all domains by default"); + }); + }); + + describe("array input handling", () => { + it("should enable all domains when ['all'] array is passed", () => { + const manager = new DomainsManager(["all"]); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(10); + expect(enabledDomains.has("repositories")).toBe(true); + expect(enabledDomains.has("builds")).toBe(true); + }); + + it("should enable multiple specific domains when array is passed", () => { + const manager = new DomainsManager(["repositories", "builds", "work"]); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(3); + expect(enabledDomains.has("repositories")).toBe(true); + expect(enabledDomains.has("builds")).toBe(true); + expect(enabledDomains.has("work")).toBe(true); + expect(enabledDomains.has("wiki")).toBe(false); + }); + + it("should handle empty array by enabling all domains", () => { + const manager = new DomainsManager([]); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(10); + expect(consoleSpy).toHaveBeenCalledWith("No valid domains specified, enabling all domains by default"); + }); + + it("should filter out invalid domains and keep valid ones", () => { + const manager = new DomainsManager(["repositories", "invalid-domain", "builds"]); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(2); + expect(enabledDomains.has("repositories")).toBe(true); + expect(enabledDomains.has("builds")).toBe(true); + expect(warnSpy).toHaveBeenCalledWith( + "Warning: Unknown domain 'invalid-domain'. Available domains: advanced-security, builds, core, releases, repositories, search, test-plans, wiki, work, work-items" + ); + }); + + it("should handle case insensitive domain names in arrays", () => { + const manager = new DomainsManager(["REPOSITORIES", "Builds", "work"]); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(3); + expect(enabledDomains.has("repositories")).toBe(true); + expect(enabledDomains.has("builds")).toBe(true); + expect(enabledDomains.has("work")).toBe(true); + }); + }); + + describe("isDomainEnabled method", () => { + it("should return true for enabled domains", () => { + const manager = new DomainsManager(["repositories", "builds"]); + + expect(manager.isDomainEnabled("repositories")).toBe(true); + expect(manager.isDomainEnabled("builds")).toBe(true); + expect(manager.isDomainEnabled("wiki")).toBe(false); + }); + + it("should return false for non-enabled domains", () => { + const manager = new DomainsManager(["repositories"]); + + expect(manager.isDomainEnabled("builds")).toBe(false); + expect(manager.isDomainEnabled("wiki")).toBe(false); + }); + }); + + describe("static methods", () => { + it("should return all available domains", () => { + const availableDomains = DomainsManager.getAvailableDomains(); + + expect(availableDomains).toEqual(["advanced-security", "builds", "core", "releases", "repositories", "search", "test-plans", "wiki", "work", "work-items"]); + expect(availableDomains.length).toBe(10); + }); + }); + + describe("getEnabledDomains method", () => { + it("should return a new Set instance (not reference to internal set)", () => { + const manager = new DomainsManager(["repositories"]); + const enabledDomains1 = manager.getEnabledDomains(); + const enabledDomains2 = manager.getEnabledDomains(); + + expect(enabledDomains1).not.toBe(enabledDomains2); + expect(enabledDomains1).toEqual(enabledDomains2); + }); + + it("should not allow external modification of enabled domains", () => { + const manager = new DomainsManager(["repositories"]); + const enabledDomains = manager.getEnabledDomains(); + + enabledDomains.add("builds"); + + // Original manager should not be affected + expect(manager.isDomainEnabled("builds")).toBe(false); + expect(manager.getEnabledDomains().has("builds")).toBe(false); + }); + }); + + describe("edge cases", () => { + it("should handle whitespace in domain names", () => { + const manager = new DomainsManager([" repositories ", " builds "]); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(2); + expect(enabledDomains.has("repositories")).toBe(true); + expect(enabledDomains.has("builds")).toBe(true); + }); + + it("should handle duplicate domain names", () => { + const manager = new DomainsManager(["repositories", "repositories", "builds"]); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(2); + expect(enabledDomains.has("repositories")).toBe(true); + expect(enabledDomains.has("builds")).toBe(true); + }); + + it("should handle mixed case 'ALL' string", () => { + const manager = new DomainsManager("ALL"); + const enabledDomains = manager.getEnabledDomains(); + + expect(enabledDomains.size).toBe(10); + }); + }); +}); diff --git a/test/src/tools/advsec.test.ts b/test/src/tools/advanced-security.test.ts similarity index 99% rename from test/src/tools/advsec.test.ts rename to test/src/tools/advanced-security.test.ts index ce105f9e..af84b9b5 100644 --- a/test/src/tools/advsec.test.ts +++ b/test/src/tools/advanced-security.test.ts @@ -4,7 +4,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { WebApi } from "azure-devops-node-api"; import { Alert, AlertType, AlertValidityStatus, Confidence, Severity, State } from "azure-devops-node-api/interfaces/AlertInterfaces"; import { PagedList } from "azure-devops-node-api/interfaces/common/VSSInterfaces"; -import { configureAdvSecTools } from "../../../src/tools/advsec"; +import { configureAdvSecTools } from "../../../src/tools/advanced-security"; type TokenProviderMock = () => Promise; type ConnectionProviderMock = () => Promise; diff --git a/test/src/tools/repos.test.ts b/test/src/tools/repositories.test.ts similarity index 99% rename from test/src/tools/repos.test.ts rename to test/src/tools/repositories.test.ts index acc3e41c..038b91ee 100644 --- a/test/src/tools/repos.test.ts +++ b/test/src/tools/repositories.test.ts @@ -4,8 +4,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { AccessToken } from "@azure/identity"; import { WebApi } from "azure-devops-node-api"; +import { configureRepoTools, REPO_TOOLS } from "../../../src/tools/repositories"; import { PullRequestStatus, GitVersionType, GitPullRequestQueryType, CommentThreadStatus } from "azure-devops-node-api/interfaces/GitInterfaces.js"; -import { configureRepoTools, REPO_TOOLS } from "../../../src/tools/repos"; import { getCurrentUserDetails } from "../../../src/tools/auth"; // Mock the auth module diff --git a/test/src/tools/testplan.test.ts b/test/src/tools/test-plan.test.ts similarity index 99% rename from test/src/tools/testplan.test.ts rename to test/src/tools/test-plan.test.ts index 8d199167..0c1c59d4 100644 --- a/test/src/tools/testplan.test.ts +++ b/test/src/tools/test-plan.test.ts @@ -2,7 +2,7 @@ import { AccessToken } from "@azure/identity"; import { describe, expect, it } from "@jest/globals"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { WebApi } from "azure-devops-node-api"; -import { configureTestPlanTools } from "../../../src/tools/testplans"; +import { configureTestPlanTools } from "../../../src/tools/test-plans"; import { ITestPlanApi } from "azure-devops-node-api/TestPlanApi"; import { ITestResultsApi } from "azure-devops-node-api/TestResultsApi"; import { IWorkItemTrackingApi } from "azure-devops-node-api/WorkItemTrackingApi"; diff --git a/test/src/tools/workitems.test.ts b/test/src/tools/work-items.test.ts similarity index 99% rename from test/src/tools/workitems.test.ts rename to test/src/tools/work-items.test.ts index b8ed3cea..2ad3986d 100644 --- a/test/src/tools/workitems.test.ts +++ b/test/src/tools/work-items.test.ts @@ -1,7 +1,7 @@ import { AccessToken } from "@azure/identity"; import { describe, expect, it } from "@jest/globals"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { configureWorkItemTools } from "../../../src/tools/workitems"; +import { configureWorkItemTools } from "../../../src/tools/work-items"; import { WebApi } from "azure-devops-node-api"; import { QueryExpand } from "azure-devops-node-api/interfaces/WorkItemTrackingInterfaces.js"; import {