@mcp-apps/azure-devops-mcp-server
Version:
A Model Context Protocol (MCP) server for Azure DevOps integration
141 lines (138 loc) • 6.82 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.replyPullRequestCommentTool = void 0;
const zod_1 = require("zod");
const azure_devops_client_1 = require("../utils/azure-devops-client");
const GitInterfaces = __importStar(require("azure-devops-node-api/interfaces/GitInterfaces"));
const STATUS_MAP = {
active: GitInterfaces.CommentThreadStatus.Active,
fixed: GitInterfaces.CommentThreadStatus.Fixed,
"won't-fix": GitInterfaces.CommentThreadStatus.WontFix,
closed: GitInterfaces.CommentThreadStatus.Closed,
"by-design": GitInterfaces.CommentThreadStatus.ByDesign,
pending: GitInterfaces.CommentThreadStatus.Pending,
};
exports.replyPullRequestCommentTool = {
name: "git-reply-pr-comment",
description: `
Replies to an existing comment thread on a pull request, optionally updating the
thread's status (e.g. resolve to "fixed" or "closed").
Always call "git-list-pr-threads" first to discover the threadId. Note that Azure
DevOps renders thread comments as a flat list, so parentCommentId is largely
cosmetic — leaving it at 0 (thread root) is correct in almost all cases.
updateStatus values (use to resolve or change a thread's state in the same call):
- "active": Open / unresolved
- "fixed": Resolved as fixed (typical "resolve" after the author addressed feedback)
- "closed": Discussion ended without explicit resolution
- "won't-fix": Acknowledged but won't be addressed
- "by-design": Behavior is intentional
- "pending": Draft / not yet published
Parameters:
- organizationUrl: Azure DevOps organization URL
- project: Project name
- repositoryName: Repository name
- pullRequestId: Pull request ID
- threadId: ID of the existing comment thread to reply to (from git-list-pr-threads)
- content: Reply text (Markdown supported)
- parentCommentId: Optional parent commentId within the thread (default: 0 = thread root).
Leave at 0 unless you have a specific reason; comments render flat regardless.
- updateStatus: Optional new thread status to set after replying.
`,
parameters: {
organizationUrl: zod_1.z.string().describe("Azure DevOps organization URL"),
project: zod_1.z.string().describe("Project name"),
repositoryName: zod_1.z.string().describe("Repository name"),
pullRequestId: zod_1.z.number().int().describe("Pull request ID"),
threadId: zod_1.z.number().int().describe("Existing thread ID"),
content: zod_1.z.string().describe("Reply content (Markdown supported)"),
parentCommentId: zod_1.z.number().int().default(0).describe("Parent comment id within the thread (0 = root)"),
updateStatus: zod_1.z
.enum(["active", "fixed", "won't-fix", "closed", "by-design", "pending"])
.optional()
.describe("Optional new status for the thread after replying"),
},
handler: async ({ organizationUrl, project, repositoryName, pullRequestId, threadId, content, parentCommentId, updateStatus, }) => {
try {
const gitApi = await (0, azure_devops_client_1.getGitApi)(organizationUrl);
const repositories = await gitApi.getRepositories(project);
const repository = repositories.find((r) => r.name && r.name.toLowerCase() === repositoryName.toLowerCase());
if (!repository?.id) {
return {
content: [{
type: "text",
text: `Repository "${repositoryName}" not found in project "${project}".`,
}],
};
}
const createdComment = await gitApi.createComment({
parentCommentId,
content,
commentType: GitInterfaces.CommentType.Text,
}, repository.id, pullRequestId, threadId, project);
let updatedStatusValue;
if (updateStatus) {
const updated = await gitApi.updateThread({ status: STATUS_MAP[updateStatus] }, repository.id, pullRequestId, threadId, project);
updatedStatusValue = updated?.status;
}
return {
content: [{
type: "text",
text: JSON.stringify({
pullRequestId,
threadId,
commentId: createdComment?.id,
parentCommentId: createdComment?.parentCommentId,
author: createdComment?.author?.displayName,
publishedDate: createdComment?.publishedDate,
updatedThreadStatus: updateStatus
? { name: updateStatus, value: updatedStatusValue }
: null,
}, null, 2),
}],
};
}
catch (error) {
const msg = error instanceof Error ? error.message : String(error);
return {
content: [{
type: "text",
text: `Error replying to pull request comment: ${msg}`,
}],
};
}
},
};
//# sourceMappingURL=git-reply-pr-comment.js.map