can you make it clear about it .js file in tools/
Absolutely! Let’s clarify the structure and responsibility of each .js file inside the src/tools/ folder in the context of your Azure DevOps MCP Server.## ✅ Overview of src/tools/ FolderEach .js file in src/tools/ implements a single MCP tool, such as retrieving a work item, queuing a build, or listing build definitions. These files all follow a similar pattern:- Define a unique tool name- Specify its input schema using zod (for validation)- Provide a description- Implement the handler() function that uses the Azure DevOps API to get the work done Each tool module exports a function like this:jsexport default function register(server) { server.registerTool({ name: 'tool_name', description: 'Tool description here...', inputSchema: z.object({ // Input parameters here... }), handler: async (args) => { // Logic using Azure DevOps API } });}## 📁 File-by-File Breakdown### 📄 getWorkItem.jsTool to fetch a single work item by ID.jsimport { z } from 'zod';import getConnection from '../utils/azureConnection.js';export default function register(server) { server.registerTool({ name: 'get_work_item', description: 'Retrieve details of a specific Azure DevOps work item by its ID.', inputSchema: z.object({ id: z.number().int().positive() }), handler: async ({ id }) => { const connection = await getConnection(); const witApi = await connection.getWorkItemTrackingApi(); const workItem = await witApi.getWorkItem(id); return { success: true, data: { id: workItem.id, title: workItem.fields['System.Title'], state: workItem.fields['System.State'], assignedTo: workItem.fields['System.AssignedTo']?.displayName || 'Unassigned', url: workItem._links?.html?.href } }; } });}### 📄 createWorkItem.jsTool to create a new task or bug in Azure Boards.jsimport { z } from 'zod';import getConnection from '../utils/azureConnection.js';const PROJECT = process.env.AZURE_DEVOPS_PROJECT;export default function register(server) { server.registerTool({ name: 'create_work_item', description: 'Create a new work item (Task, Bug) in Azure DevOps.', inputSchema: z.object({ title: z.string(), workItemType: z.string().default('Task'), description: z.string().optional() }), handler: async ({ title, workItemType, description }) => { const connection = await getConnection(); const witApi = await connection.getWorkItemTrackingApi(); const patchDocument = [ { op: 'add', path: '/fields/System.Title', value: title }, ]; if (description) { patchDocument.push({ op: 'add', path: '/fields/System.Description', value: description, }); } const item = await witApi.createWorkItem(null, patchDocument, PROJECT, workItemType); return { success: true, data: item }; } });}### 📄 getBuildDefinitions.jsLists all pipeline definitions in the project.jsimport getConnection from '../utils/azureConnection.js';const PROJECT = process.env.AZURE_DEVOPS_PROJECT;export default function register(server) { server.registerTool({ name: 'get_build_definitions', description: 'Fetch all build definitions (pipelines) in Azure DevOps.', handler: async () => { const connection = await getConnection(); const buildApi = await connection.getBuildApi(); const definitions = await buildApi.getDefinitions(PROJECT); return { success: true, data: definitions.map((def) => ({ id: def.id, name: def.name, path: def.path, queueStatus: def.queueStatus })) }; } });}### 📄 queueBuild.jsTool to queue a build by pipeline/definition ID.jsimport { z } from 'zod';import getConnection from '../utils/azureConnection.js';const PROJECT = process.env.AZURE_DEVOPS_PROJECT;export default function register(server) { server.registerTool({ name: 'queue_build', description: 'Queue a build by build definition ID.', inputSchema: z.object({ definitionId: z.number().int().positive() }), handler: async ({ definitionId }) => { const connection = await getConnection(); const buildApi = await connection.getBuildApi(); const build = await buildApi.queueBuild({ definition: { id: definitionId } }, PROJECT); return { success: true, data: { id: build.id, buildNumber: build.buildNumber, status: build.status, result: build.result } }; } });}### 📄 getRecentBuilds.js *(optional)*Lists the most recent builds.jsimport getConnection from '../utils/azureConnection.js';const PROJECT = process.env.AZURE_DEVOPS_PROJECT;export default function register(server) { server.registerTool({ name: 'get_recent_builds', description: 'Get the most recent builds in Azure DevOps.', handler: async () => { const connection = await getConnection(); const buildApi = await connection.getBuildApi(); const builds = await buildApi.getBuilds(PROJECT, null, null, null, null, null, null, null, null, null, 5); // last 5 builds return { success: true, data: builds.map((build) => ({ id: build.id, buildNumber: build.buildNumber, status: build.status, result: build.result, queueTime: build.queueTime })) }; } });}### 📄 getBoards.js *(optional)*Tool that lists available Azure Boards for the current team.jsimport getConnection from '../utils/azureConnection.js';const PROJECT = process.env.AZURE_DEVOPS_PROJECT;const TEAM = process.env.AZURE_DEVOPS_TEAM; // Set this if neededexport default function register(server) { server.registerTool({ name: 'get_boards', description: 'List all boards (e.g., Agile, Scrum) in the current project.', handler: async () => { const connection = await getConnection(); const workApi = await connection.getWorkApi(); const boards = await workApi.getBoards(PROJECT, TEAM); return { success: true, data: boards.map(board => ({ id: board.id, name: board.name, isDefault: board.isDefault })) }; } });}## 📄 azureConnection.js (Utility Function)jsimport * as azdev from 'azure-devops-node-api';export default async function getConnection() { const orgUrl = process.env.AZURE_DEVOPS_ORG_URL; const pat = process.env.AZURE_DEVOPS_PAT; if (!orgUrl || !pat) { throw new Error('AZURE_DEVOPS_ORG_URL and AZURE_DEVOPS_PAT must be defined.'); } const authHandler = azdev.getPersonalAccessTokenHandler(pat); return new azdev.WebApi(orgUrl, authHandler);}## ✅ How These Fit Together (index.js)Finally, in your index.js, you can dynamically load and register all tools like this:jsimport { McpServer } from '@modelcontextprotocol/sdk/mcp/server/index.js';import { StdioServerTransport } from '@modelcontextprotocol/sdk/mcp/server/stdio.js';import registerGetWorkItem from './src/tools/getWorkItem.js';import registerCreateWorkItem from './src/tools/createWorkItem.js';import registerGetBuildDefs from './src/tools/getBuildDefinitions.js';import registerQueueBuild from './src/tools/queueBuild.js';import registerRecentBuilds from './src/tools/getRecentBuilds.js';import registerGetBoards from './src/tools/getBoards.js';const server = new McpServer({ name: 'azure-devops-mcp', version: '1.0.0'});registerGetWorkItem(server);registerCreateWorkItem(server);registerGetBuildDefs(server);registerQueueBuild(server);registerRecentBuilds(server);registerGetBoards(server);await server.listen(new StdioServerTransport());## ✅ ResultThis structure is:- Clean: Each tool has only one responsibility.- Modular: Easily add/edit/remove tool files.- Scalable: Future features (e.g., sprint insights, PR linking) just need a new tool *.js file.Let me know if you’d like me to generate the full files / boilerplate content.
so if I run the MCP then i could see the tools
Yes, once you run your MCP (Model Context Protocol) server and have it properly registered in your configuration (such as a mcp.json in your workspace), the tools you’ve defined will be discoverable and usable by supporting clients like GitHub Copilot in Agent Mode.## How It Works- When your MCP server is running, it exposes all registered tools (each with its name, description, input schema, and handler) to any MCP-capable client.- In VS Code or another MCP-enabled environment, the client will automatically discover your tools either through autoconfiguration or by reading your mcp.json (or similar) config file.- Tools appear as available actions for Copilot/Agent Mode to invoke in response to your natural language prompts: for example, "get work item," "create work item," or "queue build".## What You’ll See- In Copilot Chat (Agent Mode), you can type prompts like “Get details for work item 123” or “List build definitions,” and Copilot will show, suggest, and call these tools where appropriate.- Many clients (such as VS Code) provide a UI to view available tools, their parameters, and descriptions by inspecting the currently connected MCP servers.- If you run your server and open the Copilot Chat, the tools defined in your server’s code will automatically become available for use in that environment.## Useful Tips- You don’t need to manually register tools in the UI—they are published dynamically by the MCP server and discovered by the client on startup or reload.- If you modify your tools or add new ones, restarting the MCP server and client will make them available.- Some platforms let you see a list of tools explicitly (for example, a “Tools” panel or command palette) and let you trigger specific tools directly.In summary: As soon as your MCP server is running and correctly configured, the tools you define will show up as available functions for GitHub Copilot (Agent Mode) and other MCP-aware environments—with no extra steps needed beyond initial setup.---: https://code.visualstudio.com/docs/copilot/chat/mcp-servers : https://docs.github.com/en/copilot/how-tos/context/model-context-protocol/extending-copilot-chat-with-mcp : https://learn.microsoft.com/en-us/visualstudio/ide/copilot-agent-mode?view=vs-2022 : https://code.visualstudio.com/api/extension-guides/ai/mcp : https://github.blog/ai-and-ml/github-copilot/agent-mode-101-all-about-github-copilots-powerful-mode