list_commits MCP tool
Note
Before picking up this work: this issue adds an MCP tool. Please follow the Adding a new tool guidance first. That includes creating an MCP Tool Proposal, using verb_object naming (get_ / list_ / save_ / delete_), and the shared resource-identification input base classes.
Problem Statement / Use Case
Agents need to browse a project's commit history, filtered by ref, author, path, or date range, when investigating changes or building context. list_commits is the collection reader for the commit domain, paired with the single-object get_commit facet reader.
Scope and Non-Goals
- In scope: list commits with filters (ref, author, path, since/until) and pagination.
- Non-goals: single-commit detail/diff/comments (
get_commit), creating commits (add_commit), commit-message search (unifiedsearch, scope=commits). - Follow-ups: none.
Data Shape and Context Engineering
Commit lists grow unbounded, so pagination is the guard. Entries are commit metadata (sha, title, author, date); full diffs are never inlined — the model drills into get_commit for those. with_stats optionally adds per-commit line counts without pulling patches.
- Input schema (example):
{
"tool": "list_commits",
"description": "List commits in a GitLab project, filtered by ref, author, path, or date.",
"parameters": {
"url": "string (optional)",
"project_id": "int | string (optional)",
"ref_name": "string (optional; branch/tag)",
"author": "string (optional)",
"path": "string (optional; only commits touching this path)",
"since": "string (optional; ISO 8601)",
"until": "string (optional; ISO 8601)",
"order": "string (optional)",
"first_parent": "boolean (optional)",
"with_stats": "boolean (optional)",
"first": "integer (optional, 1-100, default: 20)",
"after": "string (optional; cursor from the previous response's page_info.end_cursor)"
}
}- Output schema (JSON example):
{
"commits": [
{ "sha": "9a1b2c", "title": "Fix panic in cache", "author_name": "Jane Doe", "created_at": "2026-07-01T10:00:00Z", "stats": { "additions": 12, "deletions": 3 } }
],
"page_info": { "has_next_page": true, "end_cursor": "eyJpZCI6..." }
}Resources (already implemented similar tools etc.)
- Existing GraphQL field:
Query.project(fullPath:) { repository { commits(ref:, path:, author:, committedBefore:, committedAfter:, first:, after:) } }—Resolvers::Repositories::CommitsResolver(app/graphql/resolvers/repositories/commits_resolver.rb) →Types::Repositories::CommitTypeconnection, cursor pagination via GitalyListCommits. - Field mapping:
ref_name→ref,author→author,path→path,since→committedAfter,until→committedBefore. Output fields onCommitType:sha/shortId→sha,title,authoredDate→created_at,authorName→author_name,webUrl(a real field here — no client-side construction needed). - Depends on #609537 (closed) (blocks this issue):
with_stats,first_parent, andorderare not on the resolver/type today. That issue adds astatsfield toCommitType(fromCommit#diff_stats) andfirst_parent/orderarguments toCommitsResolver(the model'slist_commitsalready accepts the latter two). Build this tool against those once they land. - Resource resolution precedent:
Mcp::Tools::Concerns::ResourceFinder#find_parent_by_id_or_path!— resolveproject_id/urlto a project, pass itsfull_pathasfullPath. - Reference shape:
list_merge_requests(app/services/mcp/tools/merge_requests/list_merge_requests_{tool,service}.rb). - MCP dev guidelines:
doc/development/duo_agent_platform/mcp/_index.md;gitlab-mcp-tool-builderskill's build recipe — verify a GraphQL field exists before writing a custom one.
Implementation Plan
- Two classes:
Mcp::Tools::Commits::ListCommitsTool < Mcp::Tools::Base::GraphqlToolandListCommitsService < Base::GraphqlService. - Operation file:
app/graphql/queries/mcp/commits/list_commits.query.graphql, callingproject(fullPath:) { repository { commits(ref:, path:, author:, committedBefore:, committedAfter:, firstParent:, order:, first:, after:) { pageInfo { hasNextPage endCursor } nodes { sha title authoredDate authorName webUrl stats { additions deletions } } } } }(thefirstParent/orderargs andstatsfield come from #609537 (closed)). - Resolve
url/project_id→fullPathviafind_parent_by_id_or_path!(:project, identifier). Mapref_name/since/untiltoref/committedAfter/committedBefore. with_stats: only select thestatsfield in the query whenwith_statsis true (it triggers a per-commit Gitaly call, so don't request it otherwise).- Pagination:
first/after, returningpage_info.has_next_page/end_cursor. - Register in
GRAPHQL_TOOLSinapp/services/mcp/tools/manager.rb. - Specs:
spec/graphql/all_queries_spec.rbcoverage comes free from the committed.graphqlfile; add Tool/Service specs for ref/author/path/date filters,with_stats,first_parent, pagination, empty result, missing/inaccessible project.