repo-pulse-mcp
repo-pulse-mcp is a read-only MCP server that turns local git history into aggregated, ready-to-use insights for code review, ownership, releases, and activity.
Review & risk:
review_context(one-call PR/branch briefing with risky files, likely forgotten companions, suggested reviewers),file_hotspots(most-changed files and fix counts, e.g.types: ["fix"]for files that keep breaking),change_coupling(file pairs that always change together, revealing hidden dependencies).Ownership & team:
knowledge_map(who knows a file/dir, recency-weighted, plus bus factor and single-owner files),code_age(oldest files/dirs and files whose last author has left),contributor_summary(commits per author in a window, optional path).Releases & branches:
release_notes_draft(commits between refs grouped by conventional-commit type, as Markdown or structured sections),list_tags(tags newest first, latest on HEAD, unreleased commit count),compare_refs(ahead/behind counts and commit lists between refs),stale_branches(old branches and whether they are merged).History & activity:
recent_activity(commits in the last N days, filterable by author/path),commit_search(literal case-insensitive message search, optional window/author/path),file_history(one file's commits with line stats, following renames).Extras: resource
repo://{name}/overview(branch, last commit, latest tag, counts) and promptsreview_branchandweekly_engineering_update.Operating traits: strictly read-only (git subcommand allowlist), no shell, no network, repository allowlist, bounded/truncation-reported results, author names only (no emails), and works with any local repo including remote-less ones.
Provides read-only analysis of local Git repositories, turning git history into insights: code review risk, file hotspots, change coupling, knowledge ownership, release notes drafting, branch comparison, stale branches, commit search, file history, and recent activity.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@repo-pulse-mcpDraft release notes for v0.3.0 from recent commits"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
repo-pulse-mcp
Turn git history into answers your AI assistant can act on: what is risky in this branch, who should review it, which files keep breaking and what changed since the last release.
repo-pulse is a read-only Model Context Protocol server for local git repositories. It runs over stdio, needs nothing but Node.js and git, never modifies a repository and never touches the network. Ask your assistant things like:
🔍 "I'm about to open a PR. What's risky, what did I forget, and who should review it?"
🐛 "Which files keep breaking? Where is the churn?"
👥 "Who knows the billing module best? Are we depending on a single person?"
🔗 "Which files always change together with this one?"
🚀 "What changed since the last release? Draft the release notes."
🧹 "Which branches are stale, and which are already merged?"
It works with any local git repository, whether it was cloned from GitHub, GitLab, Bitbucket or anywhere else, or has no remote at all. Platform data such as pull requests, issues or CI status is out of scope.

A real Claude Code session reviewing a branch of vitejs/vite with no shell, only repo-pulse. See how it was recorded.
Quick start
Requires Node.js 22.12+ and git. There is nothing to clone or build: the server runs from npm.
# Claude Code: run inside the repository you want to ask about
claude mcp add repo-pulse -- npx -y repo-pulse-mcp --repo "$PWD"Then ask "What changed since the last release?", or review your branch with the built-in prompt:
/mcp__repo-pulse__review_branch main. For Claude Desktop and other MCP clients, see
Installation.
Related MCP server: mcp-git-tools
Features
13 tools, grouped by the question they answer. 🆕 marks the newest ones.
🔍 Code review and risk
Tool | What it answers |
| One-call briefing for a branch or PR: changed files ranked by past churn and fixes, files that usually change with them but were left out, reviewers |
| Files ranked by how often they changed, with how many changes were bug fixes. |
| File pairs that keep changing in the same commits: hidden dependencies that imports do not show |
👥 Ownership and team
Tool | What it answers |
| Who knows a file or directory, weighted toward recent work, plus the bus factor and files that depend on one person |
| How long ago each file last changed, the oldest directories and files whose last author has left |
| Commits per author in a time window, optionally for one path |
🚀 Releases and branches
Tool | What it answers |
| Commits between two refs grouped by conventional-commit type, plus Markdown |
| Tags newest first, the latest release on HEAD and how much is unreleased |
| Ahead/behind counts and the commits between two branches, tags or commits |
| Branches with no commits for N days, and whether each is already merged |
🕒 History and activity
Tool | What it answers |
| Commits from the last N days, filterable by author and path |
| Commits whose message mentions a ticket id, feature name or any text |
| One file's commits with line stats, following it across renames |
📦 Also included
Kind | Name | What it does |
Resource |
| Current branch, last commit, latest tag, commit/branch/tag counts |
Prompt |
| Review briefing for a branch: risky files, forgotten files, reviewers, a checklist |
Prompt |
| Polished release notes since the last tag, with a suggested next version |
Prompt |
| Owners, bus factor and knowledge at risk for a directory, with next steps |
Prompt |
| Stale branches split into safe to delete and needs a decision, with the commands |
Prompt |
| Guides the model to gather data with the tools and write an update |
All tools are annotated readOnlyHint: true, declare an outputSchema, and return both
structuredContent and a JSON text block for older clients. Full inputs and outputs are in the
Tool reference.
Example conversations
You: I'm about to open a PR for
feature/search. Anything I should know?Assistant (calls
review_contextwithbase: "main",head: "feature/search"): 4 commits touching 6 files.src/api.tsis the risky one: it changed 14 times onmainin the last six months and 6 of those were bug fixes. You may have forgottentest/api.test.ts: it changed together withsrc/api.tsin 9 of 10 commits but is not in this branch. Suggested reviewer: Alice, who holds most of the recent knowledge of these files.
You: Who knows
src/billing/best? Are we depending on one person?Assistant (calls
knowledge_mapwithpath: "src/billing"): The bus factor is 1. Carol holds 72% of the recent knowledge and is the main owner of 11 of 14 files; 9 of them depend on her alone. Bob is a distant second at 18%. Pairing Bob with Carol on the next billing change would spread the knowledge…
You: Which parts of the code has nobody touched in a long time?
Assistant (calls
code_agewithminAgeDays: 365): Half of the files haven't changed in 18 months.src/legacy/is the oldest directory (median 3.2 years), and 41 of its files were last changed by people who no longer commit here. That's where knowledge is being lost…
You: Where is the churn this quarter, and what keeps breaking?
Assistant (calls
file_hotspotswithdays: 90):src/core/insights.tschanged in 23 commits by 3 people, far more than anything else, and 9 of those commits were fixes. That's a good candidate for splitting up or adding tests…
You: What changed since the last release?
Assistant (calls
release_notes_draftwith defaults: latest tag → HEAD): Since v0.2.0 there are 14 commits. One breaking change: the config file moved torepo-pulse.json. New features: an author filter for activity and branch merge detection. Three bug fixes, mostly around empty repositories. Here's a Markdown draft you can paste into the GitHub release: …
You: Can we clean up some branches?
Assistant (calls
stale_brancheswithdays: 30): 6 branches haven't seen a commit in 30+ days. 4 of them are already merged intomainand are safe to delete: …
You: (selects the
weekly_engineering_updateprompt)Assistant (calls
recent_activity,contributor_summary,file_hotspots,stale_branches): Highlights — …
The conversations are illustrative: names and numbers are made up.
And a real session on vite, asking about unreleased changes and stale branches:

Why repo-pulse?
There are already MCP servers for GitHub, GitLab and git itself, and coding agents with a shell
can run git log directly. repo-pulse does not try to replace them. It does a different job: it
turns local git history into answers and is safe to auto-approve.
repo-pulse | GitHub / GitLab MCP | Generic git MCP / shell | |
Returns | Aggregated insights | Platform API objects | Raw git output |
Can modify anything | No, enforced by an allowlist | Usually yes (token scopes) | Yes |
Needs a token or network | No | Yes | No |
Sees local and unpushed branches | Yes | No | Yes |
Works without a hosting platform | Yes | No | Yes |
Sees pull requests, issues, CI | No | Yes | No |
What that means in practice:
Fewer steps, correct numbers. Questions like "where is the churn?", "what goes into the release notes?" or "which branches are merged and stale?" otherwise take several shell commands and hand-written counting pipelines, which can be silently wrong. repo-pulse does the counting in tested code and returns deterministic JSON that says which filters it applied. Some tools go further than any single git command:
review_contextcombines churn, fix history, change coupling and ownership for a branch in one call.Read-only you can verify. Every tool is annotated
readOnlyHintand backed by a git subcommand allowlist (see Security model), so a client can run it without asking for approval each time. You would not give the same permission to a shell or to a git server that can commit and push.Useful where there is no shell. Chat clients such as Claude Desktop cannot run
git. With repo-pulse, an engineering manager or tech lead can ask about a repository directly.Works with anything git can open. It works offline and in air-gapped environments, on self-hosted or remote-less repositories, and it never returns author email addresses.
Use it alongside a platform server, not instead of one. Pair it with the GitHub or GitLab MCP
when you also need pull requests, issues or CI status. If you work in a coding agent that already
has a shell and you only need the occasional git log, you probably do not need repo-pulse.
Benchmark
Claude Code (Sonnet 5.5) answered the same four questions about vite and express twice: once with only a read-only shell and once with only repo-pulse. Full method, answers and caveats are in bench/README.md.
Question | Shell: turns · time · result | repo-pulse: turns · time · result |
Top 10 hotspots, last 90 days | 2 · 6.6 s · ✅ | 2 · 6.5 s · ✅ |
Files coupled to | 2 · 7.4 s · ❌ undercounted | 2 · 8.6 s · ✅ |
Release notes, latest vs previous | 3 · 14.0 s · ✅ | 3 · 12.5 s · ✅ |
Merged branches stale for 90 days | 5 · 15.9 s · ✅ | 2 · 4.3 s · ✅ |
The benchmark was run with v0.3.0 (10 tools). The honest summary: repo-pulse is not cheaper in tokens for simple one-off questions, because a good model writes compact shell pipelines and repo-pulse's tool definitions cost tokens in every session (about 4k for the current 13 tools). It wins on multi-step questions, on getting the numbers right, and in clients that have no shell at all.
Requirements
Node.js 22.12+
git 2.24+ (for
--end-of-options)
Installation
The package is published on npm, so there is nothing to clone or build. The server only serves
repositories you explicitly allow. Pass them as CLI arguments or an environment variable
(separated by : on macOS/Linux, ; on Windows):
npx -y repo-pulse-mcp --repo ~/code/my-app --repo ~/code/my-lib
# or, as plain arguments
npx -y repo-pulse-mcp ~/code/my-app ~/code/my-lib
# or
REPO_PULSE_REPOS=~/code/my-app:~/code/my-lib npx -y repo-pulse-mcpWithout any configured repository the server refuses to start. It is also listed in the
MCP Registry as io.github.hasanyelaldi/repo-pulse-mcp.
Choosing tools
Every enabled tool's definition costs tokens in every session, about 4k for all 13. If you only
need some of them, enable just those with --tools (or REPO_PULSE_TOOLS), using the groups
from Features, single tool names, or both:
Toolset | Tools | Definitions |
|
| ~1.3k tokens |
|
| ~950 tokens |
|
| ~950 tokens |
|
| ~800 tokens |
| Everything (the default) | ~4k tokens |
npx -y repo-pulse-mcp --repo ~/code/my-app --tools review,list_tags
# or
REPO_PULSE_TOOLS=review,ownership npx -y repo-pulse-mcp ~/code/my-appAn unknown name stops the server at startup with the list of valid names. Prompts follow the
tools: review_branch needs review_context, release_notes needs release_notes_draft,
ownership_report needs knowledge_map, cleanup_branches needs stale_branches, and
weekly_engineering_update needs the four tools it uses.
Claude Code
Run this inside the repository you want to ask about:
claude mcp add repo-pulse -- npx -y repo-pulse-mcp --repo "$PWD"Claude Desktop
Edit claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/,
Windows: %APPDATA%\Claude\) and restart Claude Desktop. Use absolute paths:
{
"mcpServers": {
"repo-pulse": {
"command": "npx",
"args": ["-y", "repo-pulse-mcp", "--repo", "/absolute/path/to/your/repository"]
}
}
}From source
git clone https://github.com/hasanyelaldi/repo-pulse-mcp.git
cd repo-pulse-mcp
npm ci
npm run build
node build/index.js --repo /absolute/path/to/your/repositoryPodman / Docker
podman build -t repo-pulse-mcp .
podman run -i --rm \
-v "$PWD:/repos/project:ro" \
-e REPO_PULSE_REPOS=/repos/project \
repo-pulse-mcpThe repository is mounted read-only. On SELinux hosts (Fedora, RHEL) add ,Z to the volume
options. Replace podman with docker if you prefer; the image uses no Docker-specific features.
For an MCP client, use podman as the command and the rest as args.
Tool reference
Every tool accepts an optional repo argument: a repository name (the directory name, shown
in the overview resource) or its absolute path. It defaults to the first configured repository.
Results are compact JSON, and commit lists carry shortHash (git's abbreviated hash, long enough
to be unambiguous in the repository) rather than the full hash; it works anywhere a ref is
accepted.
review_context
Input | Type | Default | Notes |
| string | – | Required. Target ref the changes would merge into |
| string |
| Ref with the changes |
| integer | 180 | How far back to read the history of |
| integer | 90 | Recency weighting for reviewers, as in |
| string[] | – | Up to 20 globs to skip, same syntax as |
| integer | 2 | Minimum shared commits for a missing companion |
| integer | 50 | Minimum coupling degree (%) for a missing companion |
| integer | 30 | History commits touching more files are skipped (see below) |
| integer | 20 | 1–100, applies to each list separately |
Output: { repo, base, head, baseCommit, headCommit, windowDays, commits, truncated, changeAuthors, filesChanged, additions, deletions, historyScanned, scanTruncated, files: [{ path, additions, deletions, changes, fixes, lastChanged, owner, ownerShare }], missingCompanions: [{ path, coupledWith, sharedCommits, degree }], reviewers: [{ author, share, filesKnown, lastCommit }] }
One call that combines file_hotspots, change_coupling and knowledge_map for the files
changed in base..head, read against the history of base:
filesare the changed files, riskiest first: most fixes onbase, then most changes. New files have no history, sochangesis 0 andownerisnull.missingCompanionsare files that were not changed but usually change together with a changed file (samedegreeaschange_coupling), such as a test or config that may have been forgotten.reviewersare ranked by recent-weighted knowledge of the changed files and leave out the authors of the changes themselves.shareis relative to everyone's knowledge, change authors included.
The changed files come from the non-merge commits in base..head, so a file that was changed and
later changed back still counts. Commits touching more than maxFilesPerCommit files still count
toward changes and fixes, but not toward ownership or coupling. Renames are not followed.
file_hotspots
Input | Type | Default | Notes |
| integer | 90 | 1–3650 |
| string | – | Restrict to a subtree |
| string[] | – | Up to 20 globs to skip, e.g. |
| string[] | – | Only count these conventional-commit types, e.g. |
| integer | 10 | 1–100 |
Output: { repo, windowDays, types, commitsScanned, scanTruncated, commitsMatched, fixesFromKeywords, filesChanged, hotspots: [{ path, changes, fixes, additions, deletions, authors, lastChanged }] }
Merge commits are excluded and renames are not followed, so a renamed file starts fresh. Use
file_history to trace a single file across renames.
fixes counts the commits among changes that were bug fixes: a file that changes often and
mostly through fixes is a better refactoring candidate than one that just grows. With
types: ["fix"] only fixes are counted, so the ranking shows the files that keep breaking.
Commit types come from conventional-commit headers (fix(api): …). A commit without one counts
as a fix when its subject mentions "fix", "bug" or "hotfix"; fixesFromKeywords says how many
fixes were found that way, so a non-zero value means the counts are an estimate. A header always
wins, so docs: fix typo is not a fix.
change_coupling
Input | Type | Default | Notes |
| integer | 90 | 1–3650 |
| string | – | Only pairs that include this file |
| string | – | Restrict to a subtree |
| string[] | – | Up to 20 globs to skip, same syntax as |
| integer | 2 | Minimum shared commits for a pair to be listed |
| integer | 30 | Commits touching more files are skipped |
| integer | 10 | 1–100 |
Output: { repo, windowDays, commitsScanned, scanTruncated, commitsSkipped, pairCount, pairs: [{ fileA, fileB, sharedCommits, revisionsA, revisionsB, degree }] }
degree is the number of shared commits as a percentage of the average number of commits that
touched each file: 100 means the two files always change together. A high degree between files
that do not import each other usually points to a hidden dependency (duplicated logic, a config
or fixture that must be kept in sync). Large commits such as bulk renames or formatting runs are
skipped because they would couple every file they touch. Merge commits are excluded and renames
are not followed.
knowledge_map
Input | Type | Default | Notes |
| string | – | A file or subtree; omit for the whole repository |
| integer | 365 | 1–3650 |
| integer | 90 | A commit counts half as much for every N days of age |
| string[] | – | Up to 20 globs to skip, same syntax as |
| integer | 30 | Commits touching more files are skipped |
| integer | 10 | 1–100, applies to |
Output: { repo, path, windowDays, halfLifeDays, commitsScanned, scanTruncated, commitsSkipped, filesAnalyzed, busFactor, concentratedFiles, authors: [{ author, share, filesOwned, commits, lastCommit }], files: [{ path, owner, ownerShare, authors, commits, lastChanged }] }
Every commit that touches a file gives its author knowledge of that file, halved every
halfLifeDays, so someone who reworked a module last month outranks someone who wrote it three
years ago. Line counts are ignored so generated or reformatted diffs do not dominate. share and
ownerShare are percentages. busFactor is the fewest authors who together hold more than half
of the knowledge in the area; concentratedFiles counts files where one author holds at least
80%. files lists the most frequently changed files first with their main owner, which makes it
a quick reviewer suggestion for a path. Merge commits are excluded and renames are not followed.
code_age
Input | Type | Default | Notes |
| string | – | A file or subtree; omit for the whole repository |
| integer | 0 | Only list files at least this old; the summary covers all |
| integer | 180 | An author with no commit for this long counts as inactive |
| string[] | – | Up to 20 globs to skip, same syntax as |
| integer | 20 | 1–100, applies to |
Output: { repo, path, inactiveDays, commitsScanned, scanTruncated, filesOnHead, medianAgeDays, ageBuckets: [{ label, files }], inactiveAuthorFiles, directories: [{ path, files, medianAgeDays, newestAgeDays }], files: [{ path, lastChanged, ageDays, lastAuthor, lastAuthorActive, commits }] }
Every file on HEAD gets the date and author of the last non-merge commit that changed it.
ageBuckets splits the files into under 30 days, 30–90 days, 90 days–1 year, 1–2 years and over
2 years. directories groups files by the first directory level below path (. for files
directly in it), oldest median first, which shows the parts of the code nobody works on any more.
lastAuthorActive is false when that person has not committed anywhere in the repository for
inactiveDays, merges included; inactiveAuthorFiles counts those files, a rough measure of
knowledge that has left. People who commit under several names with one email are counted once.
Which files are on HEAD comes from additions and deletions along the first-parent history, so
files that a merge added or deleted by itself are handled correctly. History is read up to 20,000
commits; beyond that scanTruncated is true and files last changed before the cut-off are
missing.
contributor_summary
Input | Type | Default | Notes |
| integer | 30 | 1–3650 |
| boolean | false | Count merges too |
| string | – | Restrict to a subtree |
| integer | 20 | 1–100 |
Output: { repo, windowDays, totalCommits, scanTruncated, contributorCount, contributors: [{ author, commits, firstCommit, lastCommit }] }
release_notes_draft
Input | Type | Default | Notes |
| string | latest tag before | Exclusive. Tag, branch or commit hash |
| string |
| Inclusive |
| string |
|
|
Output: { repo, from, to, commitCount, truncated, markdown?, sections?: [{ key, title, entries: [{ shortHash, scope, description, author }] }] }
markdown is a ready-to-paste draft; sections is the same content as structured data. Only the
requested format is returned, because both together double the size of the result.
Sections, in order: Breaking Changes, Features, Bug Fixes, Performance, Refactoring,
Documentation, Tests, Build System, Continuous Integration, Chores, Reverts, Other Changes.
A commit is breaking if its header has ! (feat!: …) or its body has a BREAKING CHANGE:
footer; breaking commits also appear in their type section. Merge commits are skipped.
list_tags
Input | Type | Default | Notes |
| integer | 20 | 1–200 |
Output: { repo, totalTags, latestOnHead, commitsSinceLatest, tags: [{ name, date, shortHash, subject }] }
Tags are sorted by tag date, newest first. latestOnHead is the latest tag reachable from HEAD,
which can differ from the newest tag when maintenance releases are tagged on other branches.
commitsSinceLatest counts the commits after it, i.e. unreleased work.
compare_refs
Input | Type | Default | Notes |
| string | – | Required. Ref to compare against (e.g. |
| string |
| Ref to compare |
| integer | 20 | 1–100, applies to each commit list separately |
Output: { repo, base, head, baseCommit, headCommit, ahead, behind, status, truncated, aheadCommits: [{ shortHash, author, date, subject }], behindCommits: [...] }
ahead counts commits reachable from head but not base, behind the reverse. status is
identical, ahead, behind or diverged. Both refs go through the same validation as
release_notes_draft, and the counts include merge commits.
stale_branches
Input | Type | Default | Notes |
| integer | 30 | Minimum age of the latest commit |
| boolean | false | Also list remote-tracking branches |
| boolean | false | Only branches already merged into |
| integer | 50 | 1–200 |
Output: { repo, thresholdDays, totalBranches, staleCount, branches: [{ name, lastCommitDate, lastAuthor, daysSinceLastCommit, mergedIntoHead }] }
recent_activity
Input | Type | Default | Notes |
| integer | 7 | 1–3650 |
| string | – | Substring of the author name, matched literally |
| string | – | Repository-relative file or directory |
| integer | 50 | 1–200 |
Output: { repo, windowDays, totalCommits, truncated, commits: [{ shortHash, author, date, subject }] }
commit_search
Input | Type | Default | Notes |
| string | – | Required. Literal, case-insensitive; matches subject + body |
| integer | – | 1–3650. Omit to search the whole history |
| string | – | Substring of the author name, matched literally |
| string | – | Repository-relative file or directory |
| integer | 20 | 1–100 |
Output: { repo, query, windowDays, totalMatches, truncated, commits: [{ shortHash, author, date, subject }] }
file_history
Input | Type | Default | Notes |
| string | – | Required. Repository-relative file, as at HEAD |
| integer | 20 | 1–100 |
Output: { repo, path, commitCount, truncated, renamed, commits: [{ shortHash, author, date, subject, path, additions, deletions }] }
Renames are followed, so older commits report the path the file had at the time and renamed is
true when the listed commits span more than one path. Merge commits are skipped; additions and
deletions are null for binary files.
Resource: repo://{name}/overview
One resource per configured repository (listed via resources/list, name completion supported):
{
"name": "my-app",
"path": "/home/me/code/my-app",
"currentBranch": "main",
"detachedHead": false,
"lastCommit": { "hash": "…", "shortHash": "1a2b3c4", "author": "…", "date": "…", "subject": "…" },
"latestTag": "v0.2.0",
"commitCount": 412,
"branchCount": 7,
"tagCount": 9
}Prompt: review_branch
Arguments (strings): base (required, e.g. main), head (default HEAD), repo. In Claude Code
it is a slash command:
/mcp__repo-pulse__review_branch main feature/searchIt tells the model to call review_context (and compare_refs, when that tool is enabled) and
write a review briefing: summary, risky files, possibly missing files, suggested reviewers and a
checklist of things to verify. It also tells the model that repo-pulse returns history, not the
diff, so the briefing does not claim to have read the code. Available whenever review_context is
enabled.
Prompt: release_notes
Arguments (strings, all optional): from (default: the latest tag before to), to (default
HEAD), version, repo. In Claude Code:
/mcp__repo-pulse__release_notesIt tells the model to call release_notes_draft and rewrite the grouped commits as user-facing
release notes: breaking changes first, then features and fixes, with internal chores left out.
Without version, it also calls list_tags (when enabled) and suggests the next semver version.
Available whenever release_notes_draft is enabled.
Prompt: ownership_report
Arguments (strings, both optional): path (default: the whole repository), repo. In Claude Code:
/mcp__repo-pulse__ownership_report src/billingIt tells the model to call knowledge_map and write a report on the owners, the bus factor and
the files that depend on a single person, ending with concrete recommendations. When code_age
and file_hotspots are enabled it also calls them, to flag files last changed by people who have
left and hotspots owned by one person. Available whenever knowledge_map is enabled.
Prompt: cleanup_branches
Arguments (strings, both optional): days (default 30), repo. In Claude Code:
/mcp__repo-pulse__cleanup_branchesIt tells the model to call stale_branches and split the result into merged branches that are safe
to delete, with one git branch -d command each, and unmerged branches that need a decision, with
their last author. The model is told not to run the commands itself. Available whenever
stale_branches is enabled.
Prompt: weekly_engineering_update
Arguments (strings, both optional): repo, days (default 7). Produces a user message that
tells the model which tools to call and how to structure a short stakeholder update.
Security model
The server is designed so that a confused or manipulated model cannot do damage:
Strictly read-only. git runs through a single function that only allows
log,rev-list,rev-parse,describeandfor-each-ref. Anything else is rejected before a process starts.No shell, ever. git is started with
child_process.execFileand an argument array, so arguments are never interpreted by a shell.Repository allowlist. Only repositories passed via
--repo/REPO_PULSE_REPOSare served. Paths are canonicalized withrealpath(symlinks resolved) at startup and compared exactly; unknown repositories get the same error whether or not they exist on disk. If nothing is configured, the server does not start (fail closed).Input validation. All inputs go through zod schemas with bounds. Refs must look like a branch, tag, hash or
~N/^Nsuffix: no leading-, no..ranges, no@{}or:syntax, and they are passed after--end-of-options, then resolved withrev-parse --verify. Paths must be relative without..segments and are passed after--with--literal-pathspecs(no globbing). Author filters use--fixed-strings, so they are not regexes.Bounded work. Result counts are capped per tool, scans stop at 5,000 commits, git output is capped at 16 MB, and every git call times out after 15 s. Truncation is reported in the output.
Hardened git environment. Inherited
GIT_*variables (e.g.GIT_DIR) are dropped, prompts are disabled, optional locks are off,core.fsmonitoris forced off (a repository's own config could otherwise make git run a program), and external diff/textconv drivers are disabled where diffs are computed.Less personal data. Only author names are returned, never email addresses.
Out of scope: the server trusts the git binary on PATH and the repositories you allowlist.
It is a local developer tool; it has no network transport and no authentication.
Architecture
src/
├── index.ts # stdio entry point: config → registry → server → transport
├── server.ts # MCP layer: tools, resource and prompt with zod schemas
├── config.ts # --repo / REPO_PULSE_REPOS parsing, repo allowlist (RepoRegistry)
├── errors.ts # InputError (safe to show) and GitError
├── core/
│ ├── insights.ts # one method per feature: builds git args, aggregates results
│ ├── glob.ts # exclude globs, matched in-process on paths git returned
│ └── release-notes.ts # pure conventional-commit parsing, grouping and Markdown
└── git/
├── runner.ts # the only place that spawns processes (allowlist, limits, env)
├── parsers.ts # pure parsers for git output (no I/O)
└── validation.ts # ref / path / author validationDesign notes:
Process I/O is isolated from parsing.
runner.tsis the only module that spawns processes;parsers.tsandrelease-notes.tsare pure functions tested with string fixtures.Unambiguous output format. git output uses ASCII unit (
0x1f) and record (0x1e) separators instead of tabs or newlines, which can appear in commit subjects and file names.Testable composition.
createServer()takes its dependencies (allowlist, git runner, clock) as arguments, so tests connect it to an in-memory transport with a fixed "now".Errors become tool results. Validation and git failures surface as
isError: truetool results with an actionable message, so the model can correct itself instead of the session failing.stdout is sacred. It carries JSON-RPC, so logging goes to stderr and ESLint forbids
console.log.
Built on the official MCP TypeScript SDK v2 (@modelcontextprotocol/server).
Development
npm ci
npm run dev -- --repo . # run from source with tsx
npm test # unit + integration tests (vitest)
npm run lint # ESLint
npm run format # Prettier
npm run typecheck
npm run build # compile to build/
npm run check # everything CI runs
npm run inspect # build first; opens MCP Inspector against this repositoryReleasing. Bump the version in package.json, server.json (both version fields) and
SERVER_VERSION in src/server.ts (a unit test fails if they differ), merge, then push a tag:
git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z. The release workflow runs the full check, publishes
to npm with provenance and registers the new version in the MCP Registry. Both use GitHub OIDC
(npm Trusted Publishing and the registry's github-oidc login), so no publishing token is
stored. If only the registry step fails, run the workflow manually (gh workflow run release.yml): it skips npm and retries the registry.
Tests. Unit tests cover the parsers, validation, config parsing and release-notes grouping. Integration tests create a temporary git repository with a scripted history (fictional authors, fixed dates) and exercise every feature, both directly and end to end through a real MCP client over an in-memory transport, including the security checks.
MCP Inspector. npm run build && npm run inspect opens the Inspector web UI connected to this
repository. For scripted checks, the CLI mode works too:
npx @modelcontextprotocol/inspector --cli node build/index.js -e REPO_PULSE_REPOS="$PWD" \
--method tools/call --tool-name release_notes_draftLimitations and ideas
Activity, hotspot and contributor tools look at commits reachable from
HEAD; work on unmerged branches is not counted.Renames are not followed in
file_hotspots,change_coupling,knowledge_map,review_contextandcode_age: a renamed file counts as new from the rename on.Each session pays about 4k tokens for the tool definitions, so on simple one-off questions a shell can be cheaper (see Benchmark). Choosing tools cuts this to the groups you use.
Possible next steps: an HTTP transport for team use.
License
Available Tools
13 toolschange_couplingChange couplingARead-onlyIdempotent
Find pairs of files that are often changed in the same commits in the last N days (merge commits excluded), most shared commits first. Reveals hidden dependencies that imports do not show, e.g. code and the config or test it always drags along. Pass "file" to see what usually changes together with one file.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Time window in days. | |
| file | No | Only pairs that include this repository-relative file. | |
| path | No | Restrict to this file or directory. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| exclude | No | Globs to leave out, as in file_hotspots. | |
| minShared | No | Minimum number of shared commits for a pair to be listed. | |
| maxFilesPerCommit | No | Skip commits touching more files than this (bulk renames, formatting). |
Output Schema
| Name | Required | Description |
|---|---|---|
| repo | Yes | Name of the repository that was queried. |
| pairs | Yes | |
| pairCount | Yes | All pairs with at least minShared shared commits. |
| windowDays | Yes | |
| scanTruncated | Yes | |
| commitsScanned | Yes | |
| commitsSkipped | Yes | Commits ignored for touching more than maxFilesPerCommit. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so safety is covered. The description adds real behavioral detail beyond that: merge commits are excluded from the count, results are ranked by shared-commit count, and commits touching many files are skipped (the maxFilesPerCommit behavior). It does not mention cost/performance or result shape, but the output schema covers the latter.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the core operation and scope, then the value proposition, then the key optional usage pattern. No filler and no repetition of schema content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no explanation, and all 8 parameters carry full schema descriptions. The description covers the non-obvious analytic semantics (merge exclusion, bulk-commit skipping, ranking) that an agent would otherwise have to guess from the schema alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description adds interpretive value by explaining what the 'file' filter actually does ('see what usually changes together with one file') and by tying 'often changed' to the minShared threshold and the merge-commit/bulk-commit exclusions. This helps disambiguate 'file' from 'path', which the schema alone leaves subtle.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Find pairs of files that are often changed in the same commits'), with scope (last N days, merge commits excluded) and ordering (most shared commits first). This is clearly distinguishable from siblings like file_hotspots or file_history, which concern individual file churn rather than co-change pairs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear usage context: it 'reveals hidden dependencies that imports do not show, e.g. code and the config or test it always drags along,' and instructs 'Pass "file" to see what usually changes together with one file.' That tells an agent when this tool is the right one, but no explicit when-not condition or named alternative among the 12 siblings is offered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
code_ageCode ageARead-onlyIdempotent
How long ago each file on HEAD last changed, oldest first, with its last author and whether that author still commits to the repository. Also returns an age histogram, the median age per directory and how many files were last touched by someone who has left. Useful for "which parts of the code nobody touches any more?" and "where are we losing knowledge?".
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Restrict to this file or directory. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| exclude | No | Globs to leave out, as in file_hotspots. | |
| minAgeDays | No | Only list files at least this many days old (the summary covers all files). | |
| inactiveDays | No | An author with no commit in this many days counts as inactive. |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | The requested file or directory, or null for the whole repo. |
| repo | Yes | Name of the repository that was queried. |
| files | Yes | Oldest first, at least minAgeDays old. |
| ageBuckets | Yes | |
| directories | Yes | Oldest (highest median age) first. |
| filesOnHead | Yes | Files on HEAD in the area, after exclude. |
| inactiveDays | Yes | |
| medianAgeDays | Yes | |
| scanTruncated | Yes | True if history was cut off; files last changed before the cut-off are missing. |
| commitsScanned | Yes | |
| inactiveAuthorFiles | Yes | Files whose last author has not committed anywhere in the last inactiveDays. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the full safety profile (readOnly, idempotent, non-destructive, closed-world), so the description carries a lighter burden. It adds genuinely useful behavior beyond annotations only in the result-ordering clause ("oldest first") and the interpretation of last-author activity; the rest restates return content that the output schema already covers. No permissions, cost, or failure-mode context is offered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core behavior and the oldest-first ordering are front-loaded in the first sentence, and the use-case framing comes last. It is dense but every clause conveys real information; the enumeration of return aggregates is somewhat lengthy given an output schema exists, keeping it from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a full output schema, complete parameter documentation, and annotations covering the safety profile, the description is sufficient for an agent to call the tool correctly. The main remaining gap is routing guidance against the many sibling analysis tools, which the description never addresses.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all six parameters (path, repo, limit, exclude, minAgeDays, inactiveDays) are already documented in the schema. The description adds no parameter-level meaning beyond what is in the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific resource and computed result: file age on HEAD, oldest first, with last author and author activity status, plus histogram/median/per-directory aggregates. An agent understands exactly what the tool produces. It does not, however, explicitly differentiate itself from adjacent siblings like file_hotspots or knowledge_map, which is the only thing separating this from a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear situational context via two quoted questions ("which parts of the code nobody touches any more?", "where are we losing knowledge?"), which tells an agent when this tool is the right pick. It stops short of naming alternatives or exclusions (e.g. when to prefer file_hotspots or knowledge_map instead), so it does not reach 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
commit_searchCommit searchARead-onlyIdempotent
Search commit messages (subject and body) on HEAD for a literal, case-insensitive text, newest first. Useful for finding when and why something changed, e.g. a ticket id, a feature name or "revert". Optionally limited to a time window, author and/or path.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Only search the last N days. Omit to search the whole history. | |
| path | No | Only commits touching this file or directory. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| query | Yes | Text to look for in commit messages. | |
| author | No | Only commits whose author name contains this text. |
Output Schema
| Name | Required | Description |
|---|---|---|
| repo | Yes | Name of the repository that was queried. |
| query | Yes | |
| commits | Yes | |
| truncated | Yes | |
| windowDays | Yes | Null when the whole history was searched. |
| totalMatches | Yes | All matching commits, before applying the limit. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so safety is covered; the description adds genuinely useful behavior: search is limited to HEAD (not all refs), matching is literal and case-insensitive, and results come back newest first. It does not describe result volume/paging, though limit is documented in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero padding, front-loaded with the core capability and semantics before the use-case examples. Every clause carries information the agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter search tool with an output schema and full annotation coverage, the description supplies scope, match semantics, ordering and use cases. Only minor gaps remain (e.g. how filters combine, ordering guarantees with filters), which the schema largely handles.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all six parameters are already documented in-schema (days, path, repo, limit, query, author). The description restates the optional narrowing filters ("time window, author and/or path") without adding format or interaction detail, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Search commit messages") plus the precise search surface ("subject and body on HEAD") and match semantics ("literal, case-insensitive"). This level of scoping clearly separates it from siblings like file_history, recent_activity and compare_refs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete usage context ("finding when and why something changed, e.g. a ticket id, a feature name or 'revert'" with example queries), which makes the intended scenarios obvious. It stops short of naming alternatives or stating when NOT to use it, so it does not reach the explicit routing level of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
compare_refsCompare refsARead-onlyIdempotent
Compare two refs (branches, tags or commits): how many commits "head" is ahead of and behind "base", and which commits those are, newest first. Useful for "how far behind main is my branch?" or "what is on this branch that is not released yet?".
| Name | Required | Description | Default |
|---|---|---|---|
| base | Yes | Ref to compare against, e.g. "main" or a tag. | |
| head | No | Ref to compare, e.g. a branch. | HEAD |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. |
Output Schema
| Name | Required | Description |
|---|---|---|
| base | Yes | |
| head | Yes | |
| repo | Yes | Name of the repository that was queried. |
| ahead | Yes | Commits reachable from head but not from base. |
| behind | Yes | Commits reachable from base but not from head. |
| status | Yes | |
| truncated | Yes | True if either commit list was cut off by the limit. |
| baseCommit | Yes | Full hash that "base" resolved to. |
| headCommit | Yes | Full hash that "head" resolved to. |
| aheadCommits | Yes | Commits only in head, newest first. |
| behindCommits | Yes | Commits only in base, newest first. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/destructiveHint=false, so the safety profile is covered. The description adds behavior beyond that: the result is a directional divergence computation (head ahead of / behind base) with the actual commit list in newest-first order. It stops short of noting the effect of the limit default or how large divergences are truncated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler: the first defines the operation and its output shape front-loaded, the second supplies motivating examples. Nothing is repeated from the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present the description need not enumerate return fields, yet it still summarizes the key result (ahead/behind counts and commit identities). Safety is covered by annotations and the remaining four parameters are fully documented in the schema, so nothing an agent needs to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description still adds real meaning by disambiguating the direction of the comparison — head is measured ahead of and behind base — which the schema's terse "Ref to compare against" / "Ref to compare" phrasings do not make explicit.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (compare) and resource (two refs: branches, tags, commits) and specifies exactly what is produced: ahead/behind counts plus the specific commits, newest first. This is functionally distinct from every sibling (list_tags, stale_branches, commit_search), so an agent can route to it without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete triggering scenarios ("how far behind main is my branch?", "what is on this branch that is not released yet?"), which tells the agent when this tool is the right choice. It does not, however, name alternatives or state when-not to use it versus siblings like stale_branches or list_tags.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
contributor_summaryContributor summaryBRead-onlyIdempotent
Count commits per author on HEAD in the last N days, most active first. Optionally restricted to a path.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Time window in days. | |
| path | No | Restrict to this file or directory. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| includeMerges | No | Count merge commits too. |
Output Schema
| Name | Required | Description |
|---|---|---|
| repo | Yes | Name of the repository that was queried. |
| windowDays | Yes | |
| contributors | Yes | |
| totalCommits | Yes | |
| scanTruncated | Yes | |
| contributorCount | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and closed-world, so safety is covered. The description does add real context beyond that: counts are computed on HEAD only, not all refs, and output is ordered by activity. It omits that merge commits are excluded by default, a behavior that materially changes the counts.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, zero filler, with the core scope constraint (HEAD, last N days) and ordering front-loaded before the optional path restriction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, and the annotations cover the safety profile, so the description's scope details are largely sufficient. The one gap is silent merge-commit handling, which a caller counting commits would want flagged.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so days, path, repo, limit and includeMerges are all documented in the schema. The description only echoes the days window and path restriction, adding no syntax or semantics beyond what the schema already carries. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb (count) plus resource (commits per author), with scope (HEAD, last N days) and ordering (most active first) stated up front. An agent can distinguish this aggregation tool from commit_search or recent_activity, though the description never names or contrasts a sibling explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance and no mention of alternatives among the many commit-history siblings (recent_activity, commit_search, file_hotspots). The per-author framing implies its use case but leaves the agent to infer when to pick it over neighbors.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
file_historyFile historyARead-onlyIdempotent
List the commits on HEAD that changed a single file, newest first, following the file across renames. Each commit includes the path it had then and lines added/removed. Useful for "how did this file evolve?" and "who reworked it, and when?".
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Repository-relative path of a single file, as named at HEAD. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | The requested path, as it is named at HEAD. |
| repo | Yes | Name of the repository that was queried. |
| commits | Yes | |
| renamed | Yes | True if the file had a different path in a listed commit. |
| truncated | Yes | |
| commitCount | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive/openWorld=false, so the safety profile is covered. The description adds real behavioral value beyond that: newest-first ordering, rename-following semantics, and per-commit payload (historical path, lines added/removed).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly written sentences: the core behavior and ordering are front-loaded, and the second sentence adds usage framing without redundancy. Nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be fully explained, yet the description still previews the per-commit fields. With annotations covering safety and the schema covering parameters, nothing an agent needs to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so path, repo, and limit are already documented in the schema. The description adds no parameter syntax or format detail beyond what the schema provides; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource+scope: 'List the commits on HEAD that changed a single file, newest first, following the file across renames.' An agent can distinguish it from commit_search or recent_activity by the single-file and rename-following scope, though no sibling is named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear motivating contexts ('how did this file evolve?' and 'who reworked it, and when?') that map to real agent intents. It stops short of naming alternatives or stating when not to use it (e.g., vs commit_search for content-based lookup).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
file_hotspotsFile hotspotsARead-onlyIdempotent
Rank files by how many commits changed them in the last N days (merge commits excluded), with how many of those commits were fixes. Pass types: ["fix"] to rank by bug fixes only and find the files that keep breaking. Useful for spotting churn, risky areas and refactoring candidates.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Time window in days. | |
| path | No | Restrict to this file or directory. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| types | No | Only count commits of these conventional-commit types, e.g. ["fix"]. Commits without a type count as "fix" when their subject mentions fix, bug or hotfix. | |
| exclude | No | Glob patterns for files to leave out, e.g. ["*.lock", "dist/", "docs/**/*.md"]. A pattern without "/" matches at any depth; a trailing "/" excludes a directory. |
Output Schema
| Name | Required | Description |
|---|---|---|
| repo | Yes | Name of the repository that was queried. |
| types | Yes | Commit types counted; null means all commits. |
| hotspots | Yes | |
| windowDays | Yes | |
| filesChanged | Yes | |
| scanTruncated | Yes | |
| commitsMatched | Yes | Scanned commits that passed the types filter. |
| commitsScanned | Yes | |
| fixesFromKeywords | Yes | Counted fixes that have no conventional-commit header and were recognized by words like "fix" or "bug" in the subject. Non-zero means fix counts are an estimate. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/non-destructive, so the safety bar is low; the description adds real behavioral context beyond them by disclosing that merge commits are excluded and how the 'fix' classification is derived. It does not mention cost, ordering ties or result shape, but the output schema covers returns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: the metric, an example invocation, and the analytical use case. The core ranking rule is front-loaded with the qualifying details appended.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a rich schema, full annotations and an output schema, the description only needs to convey what the tool computes, which it does clearly. Minor omissions — default 90-day window relevance and that filtering is scoped by repo/path — are adequately covered by the schema itself.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, and the description adds genuine meaning on top: it demonstrates the purpose of the `types` parameter with a concrete example (["fix"]) and explains what that filter is for. The remaining parameters (days, path, repo, limit, exclude) get no additional description-side semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a precise verb+resource+metric: 'Rank files by how many commits changed them in the last N days (merge commits excluded), with how many of those commits were fixes.' That is far more specific than the bare title 'File hotspots'. It does not explicitly contrast itself with likely-confusable siblings such as change_coupling, file_history or code_age, so it falls short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states concrete use contexts ('spotting churn, risky areas and refactoring candidates') and gives a motivating configuration ('Pass types: ["fix"] ... find the files that keep breaking'). There is no explicit when-not-to-use or named alternative tool, which keeps it below 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
knowledge_mapKnowledge mapARead-onlyIdempotent
Who knows a file or directory: each author's share of the knowledge, from the commits they made in the last N days (merge commits excluded), with recent commits weighted more. Also returns the bus factor and how many files depend on a single person. Useful for "who should review this?" and "who knows the auth module best?".
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Time window in days. | |
| path | No | Restrict to this file or directory. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| exclude | No | Globs to leave out, as in file_hotspots. | |
| halfLifeDays | No | A commit counts half as much for every this many days of age. | |
| maxFilesPerCommit | No | Skip commits touching more files than this (bulk renames, formatting). |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | The requested file or directory, or null for the whole repo. |
| repo | Yes | Name of the repository that was queried. |
| files | Yes | Most frequently changed files first. |
| authors | Yes | |
| busFactor | Yes | Fewest authors who together hold more than half of the knowledge. |
| windowDays | Yes | |
| halfLifeDays | Yes | |
| filesAnalyzed | Yes | |
| scanTruncated | Yes | |
| commitsScanned | Yes | |
| commitsSkipped | Yes | Commits ignored for touching more than maxFilesPerCommit. |
| concentratedFiles | Yes | Files where a single author holds at least 80% of the knowledge. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), but the description adds real methodology: merge commits excluded, recency weighting, bus factor, and files depending on one person. It doesn't disclose cost/performance characteristics of a potentially long commit scan, which is the only notable gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly packed sentences with zero filler: the first defines the computation and its exclusions/weighting, the second names the outputs and use cases. Front-loaded with the core answer rather than preamble.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, and all seven parameters are schema-documented. What's missing is only marginal: no note on result limits/pagination behavior or the default-repo fallback, both of which an agent relies on the schema for.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter including days, halfLifeDays, and maxFilesPerCommit is already documented in the schema. The description restates the time-window and weighting concepts but adds no syntax or format detail beyond it, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource with scope spelled out: per-author share of knowledge for a file/directory, plus bus factor and single-person dependency count. It is clearly distinguishable from siblings like contributor_summary and file_hotspots, which the description implicitly disambiguates through its per-path focus.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It names two concrete decision contexts ('who should review this?' and 'who knows the auth module best?'), which tells the agent when this tool is the right call. It stops short of stating when-not to use it or naming an alternative sibling explicitly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tagsList tagsARead-onlyIdempotent
List tags (releases) newest first with their date and tagged commit, plus the latest tag reachable from HEAD and how many commits were made since it (unreleased work). Use it to find tag names for release_notes_draft or compare_refs.
| Name | Required | Description | Default |
|---|---|---|---|
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. |
Output Schema
| Name | Required | Description |
|---|---|---|
| repo | Yes | Name of the repository that was queried. |
| tags | Yes | |
| totalTags | Yes | |
| latestOnHead | Yes | Latest tag reachable from HEAD (the current release line), or null. |
| commitsSinceLatest | Yes | Commits on HEAD after latestOnHead, i.e. not released yet. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds genuinely non-derivable behavior: results are sorted newest first, a second derived value (latest tag reachable from HEAD) is returned, and 'unreleased work' is quantified as a commit count.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences, with the core purpose and scoping detail front-loaded before the routing hint at the end. No filler, though the parenthetical '(releases)' and stacked output clauses make it slightly loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value documentation is not required, yet the description still conveys the shape of the result. With 100% schema coverage and a read-only annotation profile, essentially everything an agent needs to select and call this tool is present; only explicit negative routing is absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters ('repo' with its default-repo fallback and 'limit' with range/default) are fully documented structurally. The description adds no filter or pagination semantics beyond that, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource ('List tags (releases)'), states the ordering ('newest first'), and enumerates the payload ('date and tagged commit', 'latest tag reachable from HEAD', 'commits since it'). It also disambiguates from nearby tools by naming release_notes_draft and compare_refs as separate concerns.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit usage condition ('Use it to find tag names for release_notes_draft or compare_refs'), routing the agent toward downstream tools rather than leaving intent to inference. It stops short of stating when *not* to use it (e.g. versus commit_search for commit-level queries), so it is clear context without exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recent_activityRecent activityARead-onlyIdempotent
List commits on HEAD from the last N days, newest first. Optionally filter by author (case-sensitive substring of the author name) and/or a repository-relative path.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Time window in days. | |
| path | No | Only commits touching this file or directory. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| author | No | Only commits whose author name contains this text. |
Output Schema
| Name | Required | Description |
|---|---|---|
| repo | Yes | Name of the repository that was queried. |
| commits | Yes | |
| truncated | Yes | |
| windowDays | Yes | |
| totalCommits | Yes | All matching commits in the window, before applying the limit. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, non-destructive and closed-world, so the safety profile is covered. The description adds behavioral detail beyond them: results are ordered newest first, the author match is a case-sensitive substring, and scope is limited to HEAD. It stops short of noting pagination or truncation behavior against the limit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler, and the core scope (HEAD, last N days, newest first) is front-loaded before the optional filters. Every clause carries information an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need not be described, and all five parameters are documented in the schema at full coverage. The description supplies the operational context (window, ordering, filter semantics, scope) needed to invoke the tool correctly, so nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description genuinely adds meaning: it specifies the author filter is a case-sensitive substring and that the path filter is repository-relative, neither of which the schema states. It does not restate defaults or bounds already in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource ('List commits on HEAD from the last N days'), states ordering ('newest first'), and scopes the operation to a recency window plus optional author/path filters. It distinguishes itself functionally from broader siblings like commit_search by pinning the window to HEAD and N days, though it never names an alternative tool explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: an agent can infer 'use this for recent activity in a time window' from the description, but there is no explicit when-to-use/when-not guidance and no reference to the sibling commit_search, which is the obvious overlap. Adequate but leaves routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
release_notes_draftRelease notes draftARead-onlyIdempotent
Group commits between two refs by conventional-commit type (breaking changes, features, fixes, ...) as a Markdown draft, or as structured sections. "from" defaults to the latest tag before "to"; without any tag, all commits up to "to" are used. To draft notes for an existing release, pass its tag as "to" (see list_tags).
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | End ref, inclusive. | HEAD |
| from | No | Start ref, exclusive (tag, branch or commit). | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| format | No | "markdown" for a ready-to-paste draft, "sections" for structured data. | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| to | Yes | |
| from | Yes | Start ref (exclusive); null means from the first commit. |
| repo | Yes | Name of the repository that was queried. |
| markdown | No | Present when format is "markdown" or "both". |
| sections | No | Present when format is "sections" or "both". |
| truncated | Yes | |
| commitCount | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), so the description is free to add the behavior that actually matters: how the ref range resolves by default and what happens when no tag exists. It does not mention pagination or size limits on potentially large commit ranges, which is the remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, tightly front-loaded with the core action and output modes, then the defaulting rule, then the concrete use case. Every sentence earns its place with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return structure needs no explanation, and all four parameters are schema-documented. The description covers defaults, modes, and the release-drafting workflow; only edge cases like empty ranges or oversized histories are unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds real semantics the schema lacks: the default resolution rule for 'from' (latest tag before 'to', else all commits up to 'to'), which is not expressed in the schema's parameter descriptions. It also clarifies the practical role of 'to' for existing releases.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (group commits between two refs into a release-notes draft) with the grouping axis (conventional-commit type) and both output modes. This is clearly separable from siblings like commit_search, compare_refs, and list_tags at a glance.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives the key usage path: defaulting behavior of 'from', the no-tag fallback, and the specific case of drafting notes for an existing release by passing its tag as 'to', with a pointer to list_tags. It does not explicitly say when to prefer compare_refs or commit_search over this tool, so it stops short of full alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
review_contextReview contextARead-onlyIdempotent
Review briefing for the changes in base..head (e.g. a branch or pull request): the changed files ranked by how often they changed and broke on base, files that usually change together with them but were left out (a forgotten test, config or caller), and who knows the changed files best as suggested reviewers. Merge commits are excluded.
| Name | Required | Description | Default |
|---|---|---|---|
| base | Yes | Target ref the changes would merge into, e.g. "main". | |
| days | No | Time window in days. | |
| head | No | Ref with the changes, e.g. a branch. | HEAD |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| exclude | No | Globs to leave out, as in file_hotspots. | |
| minDegree | No | Minimum coupling degree (percentage) for a missing companion. | |
| minShared | No | Minimum shared commits for a missing companion. | |
| halfLifeDays | No | For reviewers: a commit counts half as much for every this many days of age. | |
| maxFilesPerCommit | No | Skip history commits touching more files than this (bulk renames, formatting). |
Output Schema
| Name | Required | Description |
|---|---|---|
| base | Yes | |
| head | Yes | |
| repo | Yes | Name of the repository that was queried. |
| files | Yes | Riskiest first: most fixes, then most changes on base. |
| commits | Yes | Non-merge commits in base..head. |
| additions | Yes | |
| deletions | Yes | |
| reviewers | Yes | Who knows the changed files best, excluding the change authors. |
| truncated | Yes | True if base..head has more commits than were read. |
| baseCommit | Yes | Full hash that "base" resolved to. |
| headCommit | Yes | Full hash that "head" resolved to. |
| windowDays | Yes | How far back the history of base was read. |
| filesChanged | Yes | |
| changeAuthors | Yes | Authors of base..head, left out of reviewers. |
| scanTruncated | Yes | |
| historyScanned | Yes | Commits on base read for history and ownership. |
| missingCompanions | Yes | Files that usually change with the changed files but were left out. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so safety is covered. The description adds genuine behavioral context beyond that: merge commits are excluded, base is the ranking reference ('changed and broke on base'), and the companion signal means forgotten tests/config/callers. It stops short of describing pagination or output ordering.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence that compacts three analytical facets plus an exclusion rule. It is dense but every clause earns its place; the length is justified by the composite nature of the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a composite analysis tool with an output schema, the description covers what the briefing contains and the one notable filtering rule (merge commits excluded). An agent has everything needed to decide to call it and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with rich per-parameter descriptions, so the schema carries the load. The description only loosely alludes to base/head semantics via 'base..head' and reviewer half-life concept, adding little beyond the structured fields. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific artifact ('Review briefing for the changes in base..head') and then enumerates its three concrete outputs: ranked changed files, missing co-change companions, and suggested reviewers. This is precise enough to distinguish it from the narrower point tools like file_hotspots or change_coupling even though it doesn't name them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly frames the scenario ('e.g. a branch or pull request'), so an agent knows this is for pre-merge review of a diff range. It gives no explicit when-not or named alternative to pick instead, which keeps it below a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stale_branchesStale branchesARead-onlyIdempotent
List branches whose latest commit is at least N days old, oldest first, and whether each is already merged into HEAD.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | Time window in days. | |
| repo | No | Repo name or path; defaults to the first configured repo. | |
| limit | No | Maximum number of results. | |
| mergedOnly | No | Only list branches already merged into HEAD, i.e. safe cleanup candidates. | |
| includeRemote | No | Also include remote-tracking branches. |
Output Schema
| Name | Required | Description |
|---|---|---|
| repo | Yes | Name of the repository that was queried. |
| branches | Yes | |
| staleCount | Yes | |
| thresholdDays | Yes | |
| totalBranches | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=false, so safety is covered. The description still adds behavior not captured there: result ordering (oldest first) and the inclusion of per-branch merge status against HEAD.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence with no filler; the stale-threshold filter is front-loaded, followed by ordering and output content. Nothing is repeated or padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and full annotation coverage, the description need not explain return values, and it correctly focuses on filter and ordering semantics. Minor gaps remain around default-repo behavior and remote-branch handling, but those are covered in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and every parameter (days, repo, limit, mergedOnly, includeRemote) is documented in-schema with defaults and bounds. The description restates the days threshold and the merged-into-HEAD condition, which loosely maps to days and mergedOnly, but adds no syntax or format detail beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (list), resource (branches), and a precise filter (latest commit at least N days old), plus the ordering (oldest first) and an output dimension (merged into HEAD). It is clearly distinguishable in practice from siblings like list_tags or recent_activity, though it never names an alternative to anchor that distinction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use statement, no prerequisites, and no named alternative among the many siblings. The only usage signal is indirect, via mergedOnly's schema note about 'safe cleanup candidates,' which implies branch-pruning intent but leaves the agent to infer when this beats compare_refs or recent_activity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
13 tool updates
v0.5.0- Changed
change_coupling4 fields changed- changed
Input schema / properties / days / descriptionPrevious value: -"Size of the time window in days."New value: +"Time window in days." - changed
Input schema / properties / exclude / descriptionPrevious value: -"Glob patterns for files to leave out, same syntax as in file_hotspots."New value: +"Globs to leave out, as in file_hotspots." - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-100)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo."
- Added
code_age - Changed
commit_search5 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-100)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo." - removed
Output schema / properties / commits / items / properties / hashRemoved value: -{ - "type": "string" -} - added
Output schema / properties / commits / items / properties / shortHash / descriptionAdded value: +"Abbreviated hash; usable wherever a ref is accepted." - changed
Output schema / properties / commits / items / requiredPrevious value: -[ - "hash", - "shortHash", - "author", - "date", - "subject" -]New value: +[ + "shortHash", + "author", + "date", + "subject" +]
- Changed
compare_refs8 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-100)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo." - removed
Output schema / properties / aheadCommits / items / properties / hashRemoved value: -{ - "type": "string" -} - added
Output schema / properties / aheadCommits / items / properties / shortHash / descriptionAdded value: +"Abbreviated hash; usable wherever a ref is accepted." - changed
Output schema / properties / aheadCommits / items / requiredPrevious value: -[ - "hash", - "shortHash", - "author", - "date", - "subject" -]New value: +[ + "shortHash", + "author", + "date", + "subject" +] - removed
Output schema / properties / behindCommits / items / properties / hashRemoved value: -{ - "type": "string" -} - added
Output schema / properties / behindCommits / items / properties / shortHash / descriptionAdded value: +"Abbreviated hash; usable wherever a ref is accepted." - changed
Output schema / properties / behindCommits / items / requiredPrevious value: -[ - "hash", - "shortHash", - "author", - "date", - "subject" -]New value: +[ + "shortHash", + "author", + "date", + "subject" +]
- Changed
contributor_summary3 fields changed- changed
Input schema / properties / days / descriptionPrevious value: -"Size of the time window in days."New value: +"Time window in days." - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-100)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo."
- Changed
file_history5 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-100)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo." - removed
Output schema / properties / commits / items / properties / hashRemoved value: -{ - "type": "string" -} - added
Output schema / properties / commits / items / properties / shortHash / descriptionAdded value: +"Abbreviated hash; usable wherever a ref is accepted." - changed
Output schema / properties / commits / items / requiredPrevious value: -[ - "hash", - "shortHash", - "author", - "date", - "subject", - "path", - "additions", - "deletions" -]New value: +[ + "shortHash", + "author", + "date", + "subject", + "path", + "additions", + "deletions" +]
- Changed
file_hotspots10 fields changed- changed
Input schema / properties / days / descriptionPrevious value: -"Size of the time window in days."New value: +"Time window in days." - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-100)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo." - added
Input schema / properties / typesAdded value: +{ + "description": "Only count commits of these conventional-commit types, e.g. [\"fix\"]. Commits without a type count as \"fix\" when their subject mentions fix, bug or hotfix.", + "items": { + "pattern": "^[a-zA-Z]{1,20}$", + "type": "string" + }, + "maxItems": 12, + "minItems": 1, + "type": "array" +} - added
Output schema / properties / commitsMatchedAdded value: +{ + "description": "Scanned commits that passed the types filter.", + "type": "number" +} - added
Output schema / properties / fixesFromKeywordsAdded value: +{ + "description": "Counted fixes that have no conventional-commit header and were recognized by words like \"fix\" or \"bug\" in the subject. Non-zero means fix counts are an estimate.", + "type": "number" +} - added
Output schema / properties / hotspots / items / properties / fixesAdded value: +{ + "description": "How many of those commits were fixes.", + "type": "number" +} - changed
Output schema / properties / hotspots / items / requiredPrevious value: -[ - "path", - "changes", - "additions", - "deletions", - "authors", - "lastChanged" -]New value: +[ + "path", + "changes", + "fixes", + "additions", + "deletions", + "authors", + "lastChanged" +] - added
Output schema / properties / typesAdded value: +{ + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "description": "Commit types counted; null means all commits." +} - changed
Output schema / requiredPrevious value: -[ - "repo", - "windowDays", - "commitsScanned", - "scanTruncated", - "filesChanged", - "hotspots" -]New value: +[ + "repo", + "windowDays", + "types", + "commitsScanned", + "scanTruncated", + "commitsMatched", + "fixesFromKeywords", + "filesChanged", + "hotspots" +]
- Added
knowledge_map - Changed
list_tags2 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-200)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo."
- Changed
recent_activity6 fields changed- changed
Input schema / properties / days / descriptionPrevious value: -"Size of the time window in days."New value: +"Time window in days." - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-200)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo." - removed
Output schema / properties / commits / items / properties / hashRemoved value: -{ - "type": "string" -} - added
Output schema / properties / commits / items / properties / shortHash / descriptionAdded value: +"Abbreviated hash; usable wherever a ref is accepted." - changed
Output schema / properties / commits / items / requiredPrevious value: -[ - "hash", - "shortHash", - "author", - "date", - "subject" -]New value: +[ + "shortHash", + "author", + "date", + "subject" +]
- Changed
release_notes_draft5 fields changed- added
Input schema / properties / formatAdded value: +{ + "default": "markdown", + "description": "\"markdown\" for a ready-to-paste draft, \"sections\" for structured data.", + "enum": [ + "markdown", + "sections", + "both" + ], + "type": "string" +} - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo." - added
Output schema / properties / markdown / descriptionAdded value: +"Present when format is \"markdown\" or \"both\"." - added
Output schema / properties / sections / descriptionAdded value: +"Present when format is \"sections\" or \"both\"." - changed
Output schema / requiredPrevious value: -[ - "repo", - "from", - "to", - "commitCount", - "truncated", - "sections", - "markdown" -]New value: +[ + "repo", + "from", + "to", + "commitCount", + "truncated" +]
- Added
review_context - Changed
stale_branches3 fields changed- changed
Input schema / properties / days / descriptionPrevious value: -"Size of the time window in days."New value: +"Time window in days." - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (1-200)."New value: +"Maximum number of results." - changed
Input schema / properties / repo / descriptionPrevious value: -"Repository name or absolute path from the allowlist. Defaults to the first configured repo."New value: +"Repo name or path; defaults to the first configured repo."
10 tool updates
v0.2.0- First observed
change_coupling - First observed
commit_search - First observed
compare_refs - First observed
contributor_summary - First observed
file_history - First observed
file_hotspots - First observed
list_tags - First observed
recent_activity - First observed
release_notes_draft - First observed
stale_branches
TDQS
Scored across 13 tools
Each tool targets a distinct git-analytics question: activity, commit search, file history, hotspots, coupling, ownership, releases, branch staleness, review context, code age, and contributor counts. The only mild overlap is commit-listing tools like recent_activity and commit_search, but their descriptions clearly distinguish time-based listing from literal message search.
All names use snake_case and are descriptive, giving a predictable style overall. However, the set mixes noun-phrase names (recent_activity, file_history, code_age) with two verb-first names (list_tags, compare_refs), so it is not a single consistent verb_noun pattern.
13 tools fit the repo-analytics scope well; each covers a different analysis and there is no obvious redundancy. The count is within the ideal 3-15 range and earns its place.
The surface covers commit history, file churn, ownership, releases, branch staleness, and review context, which is strong for a read-only analytics server. Minor gaps remain: no general branch listing or single-commit diff inspection, which could force agents to work around missing operations.
Maintenance
Related MCP Connectors
Code intelligence for LLMs. Analyze, search, and retrieve code from any public git repository.
Plain-English git for agents: 22 tools to branch, commit, push and tag. Free tier.
Operate GitHub from your AI assistant: repos, PRs, issues, Actions. Previews and read-only mode.
Git-backed platform for skills, tools, and context for AI agents
Related MCP Servers
- AlicenseBqualityAmaintenanceGive your AI coding agent superpowers over git history — auto-generate changelogs, analyze commits, attribute blame, and draft release notes via MCP. Zero runtime dependencies.61MIT
- AlicenseNot gradedqualityFmaintenanceEnables AI agents to analyze local Git repositories with commit logs, diffs, blame, statistics, and branch analysis using native git commands without requiring a GitHub API.80 npmMIT
- AlicenseAqualityAmaintenanceEnables AI assistants to perform Git workflows with semantic commit messages, pre-PR secret audits, conflict resolution, and release changelog generation.5MIT
- AlicenseAqualityBmaintenanceEnables AI clients to interrogate a local Git repository's history, surfacing change hotspots, per-author activity, files that change together, single-file commit history, and draft changelogs between refs. It is strictly read-only, requires no API keys, and returns structured data for the client to interpret.6MIT