Skip to main content
Glama
Tiberriver256

Azure DevOps MCP Server

list_commits

Retrieve recent commits from an Azure DevOps branch, with file-level diff content per commit. Specify repository and branch; use top to limit results and skip for pagination.

Instructions

List recent commits on a branch including file-level diff content for each commit

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectIdNoThe ID or name of the project (Default: MyProject)
organizationIdNoThe ID or name of the organization (Default: mycompany)
repositoryIdYesThe ID or name of the repository
branchNameYesBranch name to list commits from
topNoMaximum number of commits to return (Default: 10)
skipNoNumber of commits to skip from the newest

Implementation Reference

  • The main handler function that lists commits on a branch including file-level diffs. Uses Azure DevOps Git API to fetch commits, retrieves file changes for each commit, generates unified diffs, and returns commits with content.
    export async function listCommits(
      connection: WebApi,
      options: ListCommitsOptions,
    ): Promise<ListCommitsResponse> {
      try {
        const gitApi = await connection.getGitApi();
        const commits = await gitApi.getCommits(
          options.repositoryId,
          {
            itemVersion: {
              version: options.branchName,
              versionType: GitVersionType.Branch,
            },
            $top: options.top ?? 10,
            $skip: options.skip,
          },
          options.projectId,
        );
    
        if (!commits || commits.length === 0) {
          return { commits: [] };
        }
    
        const getBlobText = async (objId?: string): Promise<string> => {
          if (!objId) {
            return '';
          }
          const stream = await gitApi.getBlobContent(
            options.repositoryId,
            objId,
            options.projectId,
          );
          return stream ? await streamToString(stream) : '';
        };
    
        const commitsWithContent: CommitWithContent[] = [];
    
        for (const commit of commits) {
          const commitId = commit.commitId;
          if (!commitId) {
            continue;
          }
    
          const commitChanges = await gitApi.getChanges(
            commitId,
            options.repositoryId,
            options.projectId,
          );
          const changeEntries = commitChanges?.changes ?? [];
    
          const files = await Promise.all(
            changeEntries.map(async (entry: GitChange) => {
              const path = entry.item?.path || entry.originalPath || '';
              const [oldContent, newContent] = await Promise.all([
                getBlobText(entry.item?.originalObjectId),
                getBlobText(entry.item?.objectId),
              ]);
              const patch = createTwoFilesPatch(
                entry.originalPath || path,
                path,
                oldContent,
                newContent,
              );
              return { path, patch };
            }),
          );
    
          commitsWithContent.push({
            commitId,
            comment: commit.comment,
            author: commit.author,
            committer: commit.committer,
            url: commit.url,
            parents: commit.parents,
            files,
          });
        }
    
        return { commits: commitsWithContent };
      } catch (error) {
        if (error instanceof AzureDevOpsError) {
          throw error;
        }
        throw new Error(
          `Failed to list commits: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }
  • Zod schema for validating list_commits input: projectId, organizationId, repositoryId, branchName, top (1-100, default 10), skip.
    export const ListCommitsSchema = z.object({
      projectId: z
        .string()
        .optional()
        .describe(`The ID or name of the project (Default: ${defaultProject})`),
      organizationId: z
        .string()
        .optional()
        .describe(`The ID or name of the organization (Default: ${defaultOrg})`),
      repositoryId: z.string().describe('The ID or name of the repository'),
      branchName: z.string().describe('Branch name to list commits from'),
      top: z
        .number()
        .int()
        .min(1)
        .max(100)
        .optional()
        .describe('Maximum number of commits to return (Default: 10)'),
      skip: z
        .number()
        .int()
        .min(0)
        .optional()
        .describe('Number of commits to skip from the newest'),
    });
  • TypeScript interfaces: ListCommitsOptions (input), CommitWithContent (per-commit structure with files containing path/patch), and ListCommitsResponse (output).
    export interface ListCommitsOptions {
      projectId: string;
      repositoryId: string;
      branchName: string;
      top?: number;
      skip?: number;
    }
    
    /**
     * Representation of a commit along with the file diffs it touches
     */
    export interface CommitWithContent {
      commitId: string;
      comment?: string;
      author?: {
        name?: string;
        email?: string;
        date?: Date;
      };
      committer?: {
        name?: string;
        email?: string;
        date?: Date;
      };
      url?: string;
      parents?: string[];
      files: Array<{ path: string; patch: string }>;
    }
    
    /**
     * Response for listing commits with their associated content
     */
    export interface ListCommitsResponse {
      commits: CommitWithContent[];
    }
  • Tool definition registration with name 'list_commits', description, and inputSchema derived from ListCommitsSchema.
      {
        name: 'list_commits',
        description:
          'List recent commits on a branch including file-level diff content for each commit',
        inputSchema: zodToJsonSchema(ListCommitsSchema),
      },
    ];
  • Helper function streamToString used to convert a Node.js ReadableStream to a string (for reading blob content).
    async function streamToString(stream: NodeJS.ReadableStream): Promise<string> {
      const chunks: Buffer[] = [];
      return await new Promise<string>((resolve, reject) => {
        stream.on('data', (c) => chunks.push(Buffer.from(c)));
        stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
        stream.on('error', (err) => reject(err));
      });
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It discloses that diffs are included, but does not mention pagination, performance considerations, or authentication requirements. Adequate but leaves some gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single, well-structured sentence that immediately states the core functionality. No wasted words, and the detail about file-level diffs is efficiently included.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a list tool with 6 parameters, no output schema, and no annotations, the description provides sufficient context about the main purpose and return content (diffs). Could mention output format or pagination, but still fairly complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already explains all 6 parameters. The description adds no additional parameter context beyond what's in the schema, so baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly specifies the action ('List'), the resource ('commits on a branch'), and an important additional detail ('including file-level diff content for each commit'). It effectively distinguishes from sibling tools like 'create_commit' or 'list_pull_requests'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool vs alternatives. The description implies it's for listing commits with diffs, but lacks context on when not to use it or how it compares to similar tools like 'get_repository' or 'get_pull_request_changes'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Tiberriver256/mcp-server-azure-devops'

If you have feedback or need assistance with the MCP directory API, please join our Discord server