gitlab-readonly-mcp
Read-only access to a self-managed GitLab instance via a local bare mirror, letting agents read files from any branch, tag, or commit without touching the working tree. Provides tools for fetching files and directory trees at a ref, listing and resolving branches/tags/commits, grepping a ref's tree, pickaxe search across commit history, viewing logs and commit details, blame, comparing refs and diffing individual files, parsing GitLab links, and managing per-repository mirrors and fetch policies (list, sync, add, remove).
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., "@gitlab-readonly-mcpshow me the diff of src/app.js between main and develop"
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.
gitlab-readonly-mcp
A small, read-only, git-backed MCP server for a self-managed GitLab.
It lets an AI agent read files from any branch, tag, or commit — including branches that are not checked out locally — without ever touching your working tree. Content comes from a local bare mirror the server keeps in a cache directory, or from a clone of your own when you register one.
Why git instead of the GitLab REST API?
Reading a file at a ref, comparing branches, history, blame and code search are git's strengths and, on a self-managed free tier, the REST API's weaknesses. How it works has the longer answer.
Related MCP server: GitLab MCP Server
Requirements
Node.js 24+ (developed on Node 24.21)
gitonPATHA GitLab token with the
read_repositoryscope (a personal or project access token). Never useapiorwrite_repository.
Install
Install the tarball attached to the latest release. No checkout, no build, no npm account:
npm install -g https://github.com/igordev96/gitlab-readonly-mcp/releases/download/v0.10.1/gitlab-readonly-mcp-0.10.1.tgzEach release carries SHA256SUMS beside the tarball if you want to check the
download before installing it. The package is not on npm (private: true), and
the Source code archives GitHub attaches to a release are repository
snapshots without dist/ — they install nothing.
To work on the server instead, build the tarball from your checkout:
npm install
npm pack --pack-destination . # gitlab-readonly-mcp-0.10.1.tgz
npm install -g .\gitlab-readonly-mcp-0.10.1.tgzEither way, gitlab-readonly-mcp lands on PATH and speaks MCP on stdio from
any directory. To upgrade, install the newer release's tarball. To uninstall,
npm rm -g gitlab-readonly-mcp.
Under nvm, global packages live inside the active Node version, so switching
versions hides the command until you reinstall it.
Configuration
All configuration is via environment variables.
Variable | Required | Description |
| yes | Base URL of the instance, e.g. |
| yes | Read-only token ( |
| no | Project path used when a tool call omits |
| no | Cache root for mirrors. Defaults to |
| no | Path to a CA bundle, if the server uses a private CA. |
| no | Git executable. Defaults to |
| no | Default freshness policy for every project: |
| no | Age in seconds after which a mirror counts as stale. Default |
These are the defaults; a project can override its freshness policy in
repos.json (see Repositories and policy).
Wire into OpenCode
opencode mcp add gitlab --global `
--env GITLAB_HOST=https://gitlab.example.com `
--env 'GITLAB_TOKEN={env:GITLAB_TOKEN}' `
--env GITLAB_DEFAULT_PROJECT=group/subgroup/repo `
-- gitlab-readonly-mcpOr add it to configuration by hand (mcp.servers):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"gitlab": {
"type": "local",
"command": ["gitlab-readonly-mcp"],
"environment": {
"GITLAB_HOST": "https://gitlab.example.com",
"GITLAB_TOKEN": "{env:GITLAB_TOKEN}",
"GITLAB_DEFAULT_PROJECT": "group/subgroup/repo"
}
}
}
}
}The command is the installed binary, so the entry point no longer depends on
where the checkout lives — a global MCP server's working directory is the
workspace it is launched from, not this project. If the bare name does not
resolve on your machine, npm prefix -g gives the prefix;
<prefix>\gitlab-readonly-mcp.cmd is the same package by absolute path.
The tools appear as gitlab_<tool> — gitlab_get_file, gitlab_log, and so
on. All sixteen are listed below.
Wire into Cursor
Cursor reads MCP servers from a mcpServers object, in .cursor/mcp.json for
one project or ~/.cursor/mcp.json for every project
(%USERPROFILE%\.cursor\mcp.json on Windows). It starts the command from
Install, so that has to be on PATH first.
{
"mcpServers": {
"gitlab": {
"command": "cmd",
"args": ["/c", "gitlab-readonly-mcp"],
"env": {
"GITLAB_HOST": "https://gitlab.example.com",
"GITLAB_TOKEN": "${env:GITLAB_TOKEN}",
"GITLAB_DEFAULT_PROJECT": "group/subgroup/repo"
}
}
}
}On Windows the cmd /c wrapper is not optional. The installed package is a
.cmd shim, and Cursor starts the command itself rather than through a shell:
"command": "gitlab-readonly-mcp" fails with ENOENT and the server never
appears in the list. On macOS and Linux the bare name is correct:
"command": "gitlab-readonly-mcp",Quit Cursor completely and reopen it; a window reload is not always enough. The server then shows under Settings → MCP, and anything it printed on the way up is in Output → MCP Logs — the first place to look when the entry exists but its tools do not.
Two traps, both silent:
The interpolation syntax is not OpenCode's. Cursor resolves
${env:NAME}; OpenCode resolves{env:NAME}. Copy a config from one to the other unchanged and the literal string is passed as the token, which GitLab rejects.${env:GITLAB_TOKEN}reads Cursor's own environment, not your shell's history: a variable set only in the current terminal is invisible to an app started from the taskbar. Use the OS-level variable, or pointenvFileat a file:"envFile": "${workspaceFolder}/.env". A token pasted literally works too, but then.cursor/mcp.jsonmust stay out of git.
What it can do
Tool | What it answers |
Files and trees | |
| One file at a ref — the whole file or a line range. Accepts a blob, raw or blame link. |
| A directory at a ref, one level deep or recursive. Accepts a tree link. |
Refs | |
| Branches and tags, with the commit each points at. |
| What a branch, tag or sha resolves to, with close matches when the name is wrong. |
Search | |
| Grep a ref's tree, grouped by file — matched lines, file paths, or per-file counts. |
| The commits whose diff changed how often a text appears. |
History and diffs | |
| A ref's history, optionally for one path. Accepts a history link. |
| One commit: message, parents, author, files, line counts, and the patch on request. Accepts a commit link. |
| Who last touched each line, grouped by commit. Accepts a blame link. |
| What changed on one ref since it split from another, as a capped file summary. |
| One file's patch between two refs. |
Links | |
| What a GitLab link names — project, target, ref, path and line range — without reading anything. |
Repository policy | |
| Every project in use: its source, its policy and its true age. |
| Fetch now, ignoring the TTL. |
| Read a project from a clone of your own instead of a mirror. |
| Forget a project. Never deletes your clone. |
Quickstart
Examples use group/sub/repo; set GITLAB_DEFAULT_PROJECT to leave project=
out. Every result says which commit answered and how fresh it is — see
What the odd bits mean.
Read a file from a branch you have not checked out. No clone of your own, no checkout, no change to your working tree:
gitlab_get_file(project="group/sub/repo", ref="fix/PROJ-7608", path="src/index.tsx")<file project="group/sub/repo" ref="fix/PROJ-7608" commit="abc123…" path="src/index.tsx" synced_at="2026-10-18T09:12:44.318Z">
…file contents…
</file>Browse a tree before reading. Files carry their size, so a small config is
easy to tell from a bundle. recursive=true walks the whole subtree:
gitlab_list_tree(project="group/sub/repo", ref="fix/PROJ-7608", path="src")<tree project="group/sub/repo" ref="fix/PROJ-7608" commit="abc123…" path="src" count="3" synced_at="…">
blob 3047 src/App.tsx
tree - src/components
blob 412 src/theme.css
</tree>Find and confirm a branch. list_refs searches names; resolve_ref turns
one into a commit, and suggests close matches when the name is wrong:
gitlab_list_refs(project="group/sub/repo", kind="branches", search="PROJ")<refs project="group/sub/repo" kind="branches" default_branch="develop" count="1" synced_at="…">
branch abc123def456 fix/PROJ-7608 PROJ-7608: adjust the header
</refs>Find where something is used — no clone, no knowledge of the file:
gitlab_search(project="group/sub/repo", ref="fix/PROJ-7608", query="orderTotals")<search project="group/sub/repo" ref="fix/PROJ-7608" commit="abc123…" query="orderTotals" count="2" files="1" synced_at="…">
src/reports/OrderTotals.tsx
22: export const OrderTotalsReport = () => {
64: title={translations.reporting.seeOrderTotalsPage}
</search>output="count" answers "how many, in which files" without listing every match;
context=2 adds the lines around each hit. pickaxe(query=…) answers the
follow-up: when did this text appear?
Read a pasted link, including the lines it names. A #L118-126 anchor
becomes the range:
gitlab_get_file(url="https://gitlab.example.com/group/sub/repo/-/blob/fix/PROJ-7608/src/index.tsx#L118-126")<file project="group/sub/repo" ref="fix/PROJ-7608" commit="abc123…" path="src/index.tsx" lines="118-126" file_lines="373" synced_at="…">
118: …
</file>Each link kind has one reader: blob, raw and blame links go to get_file (or
blame), tree links to list_tree, /-/commit/ to get_commit, /-/commits/
to log. A link handed to another tool is refused by name and says which tool
wants it. parse_gitlab_url(url) reports what a link names without reading
anything, and resolve=false does it offline.
See the history, and who last touched a line. path is optional and is a git
pathspec, so globs work:
gitlab_log(project="group/sub/repo", ref="fix/PROJ-7608", path="src/index.tsx")<log project="group/sub/repo" ref="fix/PROJ-7608" commit="abc123…" path="src/index.tsx" count="2" synced_at="…">
abc123def4 2026-10-15T12:32:33+01:00 A. Author PROJ-7608: adjust the header
def456abc7 2026-10-14T09:15:02+01:00 B. Author Merge branch 'develop' into fix/PROJ-7608 (merge)
</log>gitlab_blame(project="group/sub/repo", ref="fix/PROJ-7608", path="src/index.tsx", lines="118-126")<blame project="group/sub/repo" ref="fix/PROJ-7608" commit="abc123…" path="src/index.tsx" lines="118-126" file_lines="373" groups="2" count="9" max_lines="500" synced_at="…">
def456abc789 B. Author 2026-04-23T10:11:50+02:00 118-121
118: …
abc123def456 A. Author 2026-10-15T12:32:33+01:00 122-126
122: …
</blame>get_commit(rev=…) reads one commit — message, files, line counts, and the
patch with patch=true.
Compare two branches. compare prints the merge base it used, so which diff
ran is never hidden:
gitlab_compare(project="group/sub/repo", from="develop", to="fix/PROJ-7608")<compare project="group/sub/repo" from="develop" to="fix/PROJ-7608" from_commit="…" to_commit="…" merge_base="def456abc789…" files="1" total_files="1" additions="32" deletions="11" max_files="100" synced_at="…">
M src/index.tsx +32 -11
</compare>diff_file(from=…, to=…, path=…) is one file's patch; pass the merge_base it
printed as from for the branch's own view of the file.
Gotchas
searchandpickaxematch literally and case-insensitively:foo|barlooks for that text. Passregex=truefor alternation,case_sensitive=truefor exact case.A plain pathspec's
**needs a directory level —src/**/*.tsxmissessrc/index.tsx. Use:(glob)src/**/*.tsxfor zero or more.Omit
refto read the default branch, which is what a fresh clone shows you.#L12and#L12-20anchors are 1-based and inclusive.compareis three-dot by default;merge_base=falseasks for the raw two-dot diff.Binary files are refused where the answer would be meaningless (a read, a blame); a diff gives git's
Binary files … differline instead of a patch.Caps are always reported:
truncated="true",total=…and[cut: …]say when an answer was shortened, rather than quietly returning less.
What the odd bits mean
You see | It means |
| When the source that answered last fetched successfully. |
| A refresh was due and failed, so the answer came from the cache; a |
| The answer was capped. Where the tool can count it, |
|
|
| The two refs share no history, so |
| Nothing matched. That is an answer, not an error. |
Repositories and policy
A project registers itself the first time it is used — its mirror is cloned on
demand — or explicitly with add_repo. The registry is a small repos.json in
the cache directory:
<repos count="2">
group/sub/repo source=mirror freshness=ttl ttl=600s present=true synced_at="2026-10-18T21:44:25.655Z"
group/sub/other source=local freshness=ttl ttl=600s present=true local_path=C:\work\other synced_at="unknown"
</repos>synced_at is when that source last fetched successfully, or unknown when
no fetch has ever stamped it; present=false means the project is registered but
its data is not on disk — a mirror never cloned, or a clone since moved. A
project's policy can be overridden by hand (absent fields mean the configured
default):
{
"version": 1,
"repos": {
"group/sub/repo": {
"source": "mirror", // "mirror" | "local"
"localPath": null, // set when source == "local"
"cloneStrategy": "full",
"freshnessMode": "manual", // "ttl" | "always" | "manual"
"freshnessTtlSeconds": 3600
}
}
}add_repo merges into what is already recorded, so re-pointing a project cannot
reset a freshnessMode you set by hand. sync_repo(project) fetches immediately
and follows the project's source (a mirror, or the clone's own remote) — that is
what makes manual usable, and what to call when you want a fresh answer now.
Unlike a read, sync_repo reports a failure as an error rather than answering
from the cache.
Reading from a clone of your own
gitlab_add_repo(project="group/sub/repo", local_path="C:\\work\\repo")The path is checked there and then — it must exist and be a git repository,
working clone or bare — so a typo fails at the call that records it. From then on
every read runs against that clone: nothing is cloned, and nothing is written to
your working tree, so uncommitted work is untouched. A branch that is not the
checked-out one is still found through its remote-tracking ref, because that is
where git clone keeps every other branch. A clone with no remote still reads
everything it holds; a miss says so plainly instead of pretending the ref does
not exist.
gitlab_add_repo(project="group/sub/repo") # back to a mirror
gitlab_remove_repo(project="group/sub/repo") # forget it entirely
gitlab_remove_repo(project="group/sub/repo", delete_mirror=True)remove_repo never deletes a clone: that repository is yours, and the server
only ever deletes inside its own mirrors/ directory. Forgetting a
mirror-backed project does not stick, because the next read registers it again;
forgetting a local registration does, and is how a project goes back to being
read from a mirror.
Troubleshooting
A failed read explains itself: the cause, a hint when the failure is one we
recognise, and git's own output as evidence (git said: …). No extra logging is
needed to find out what happened.
What you see | What it means |
GitLab rejected the credentials |
|
GitLab could not find this project | Check the project path ( |
TLS verification failed | The server uses a private CA; point |
Could not reach GitLab | Network or VPN. |
| The source could not be refreshed, so the answer came from the cache as of |
The registered clone at … is not a usable git repository | The path recorded by |
The registered clone at … has no remote to fetch from | The clone has no remote configured, so a ref it does not already hold cannot be fetched. Everything it does hold still reads. |
| Matching is literal and one argument: |
| It reports commits whose diff changed how many times the text appears ( |
A link is refused: | The link belongs to another GitLab instance. Use a link from the instance |
A link is refused: is a file link; use get_file for it (or directory, commit, history) | The link went to a tool that does not read it. Each kind has one reader: blob/raw/blame → |
A link is refused: is a merge request link | Merge requests, issues and other non-code links name no version-control object, so they are refused by name. Take the ref and path from the page and call the tool directly. |
A | It needs |
| A |
The credential case is worth one extra note: a running MCP server keeps the
token it started with. Editing GITLAB_TOKEN does nothing to a server that is
already running, which makes a valid token look broken. Restart the client (for
OpenCode, opencode service restart) after changing it.
The first read of a project also clones it, so it is slow exactly once.
Security
The token is only ever passed as
-c http.extraheader=…on the git command line, so it never lands in.git/config.The token is redacted from error output before it reaches a tool result.
credential.helper=is cleared andGIT_TERMINAL_PROMPT=0is set, so a bad token fails fast instead of prompting or hanging.Refs and paths beginning with
-are rejected to prevent argument injection.Git is invoked with an argument array and
shell: false.
Read more
How it works — where content comes from, freshness, and the read-only guarantee.
Development — build, test, releasing and the source layout.
Plan — the design decisions and what is next.
History — milestones, findings, and the evidence from live checks.
Available Tools
16 toolsadd_repoRegister a repositoryA
Register a project with this server. With local_path, reads use that existing clone instead of a server-side mirror; the path is checked to be a git repository here, so a wrong path is refused now rather than at read time. Without it, the project is registered as mirror-backed, which also makes it appear in list_repos before it has ever been read. Nothing is cloned or fetched: use sync_repo for that, or simply read the project. A project already registered is updated; its freshness policy is kept.
| Name | Required | Description | Default |
|---|---|---|---|
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| local_path | No | Path to an existing clone (or bare repository) to read from instead of a mirror. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only give readOnlyHint=false and openWorldHint=false; the description goes well beyond that, disclosing that the path is validated as a git repo at registration time, that nothing is cloned or fetched, that mirror-backed projects surface in list_repos before first read, and that re-registration preserves the existing freshness policy. These are exactly the behavioral traits an agent needs before calling.
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 action is front-loaded, followed by conditional behavior, side-effect exclusions, and re-registration semantics in four tight sentences. No filler and no repetition of schema or annotation 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?
For a zero-required-param, two-parameter registration tool with no output schema, the description covers validation timing, side effects (no clone/fetch), visibility effects, idempotence, and alternative tools. An agent has everything needed to invoke it correctly and to route elsewhere when cloning is actually wanted.
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 meaning beyond the schema: it explains what selecting local_path changes (reads use that clone rather than a mirror) and that the path is validated here, while the schema only says 'Path to an existing clone'. The project parameter's default behavior is left to the schema, which already covers it.
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?
Opens with a specific verb+resource ('Register a project with this server') and immediately differentiates the two registration modes: local_path clone-backed vs. mirror-backed. The distinction from siblings (sync_repo, list_repos) is made explicit in the body, so an agent can place this tool without opening any 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?
States precisely when to supply local_path (read from an existing clone instead of a server-side mirror) and what happens without it (mirror-backed registration). It names the alternative for fetching ('use sync_repo for that, or simply read the project') and the update semantics for an already-registered project, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
blameBlame a file at a refARead-only
Show who last touched each line of a file at a branch, tag, or commit — git blame --porcelain — without a working tree. Lines are grouped into runs from the same commit: a row is commit, author, date, line range, then the lines themselves, numbered. lines narrows the blame to a range ("118-126", or "118"), and a range past the end is clamped and reported rather than treated as an error. max_lines caps how many lines come back. The ref defaults to the repository's default branch, and the result states the commit it blamed and synced_at. A binary file is refused: git blames one, but its lines carry NUL bytes. Give either url — a GitLab /-/blame/<ref>/<path> link, whose #L12-20 anchor narrows the blame — or ref and path; the link's host must be GITLAB_HOST.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Branch, tag, or commit to blame at, e.g. 'fix/PROJ-7608'. Defaults to the default branch. | |
| url | No | A GitLab file link to blame, e.g. 'https://gitlab.example.com/group/repo/-/blame/develop/src/index.tsx#L12-20'. Replaces ref, path and lines; the host must be GITLAB_HOST. | |
| path | No | File path from the repository root, e.g. 'src/index.tsx'. Required unless `url` is given. | |
| lines | No | Restrict the blame to a line range: '118' or '118-126' (an `L` prefix is accepted, as in a `#L118-126` link). A `url` with an anchor wins over this. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| max_lines | No | Maximum blamed lines to return (default 500). A capped answer says so. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations only covering read-only/open-world hints, the description carries real behavioral weight: binary files are refused, out-of-range lines are clamped and reported rather than erroring, `max_lines` caps output, and the result reports the blamed `commit` and `synced_at`. This is exactly the extra context annotations don't provide.
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 purpose and output row shape are front-loaded, and each subsequent sentence (clamping, max_lines, binary refusal, input modes) earns its place. It is dense and runs long, but no sentence is 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?
With no output schema, the description steps up to describe the return shape (commit, author, date, line range, then numbered lines) plus the reported `commit`/`synced_at`. Combined with the documented input modes and edge-case behavior, an agent has everything needed to call it correctly.
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 genuine meaning beyond the schema: the clamping/reporting behavior for `lines`, the precedence of a URL anchor over `lines`, and the interaction where `url` replaces ref/path/lines. It stops short of 5 only because the schema already documents formats.
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 — showing per-line last-touch authorship of a file at a ref — and pins it to `git blame --porcelain`. It is clearly distinguishable from siblings like `log`, `diff_file`, and `get_file` by its line-level authorship semantics and 'without a working tree' framing.
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 invocation context: default ref is the default branch, and you supply either `url` (with `#L12-20` anchor narrowing) or `ref`+`path`. However, it never explicitly contrasts when to choose this over `log`, `pickaxe`, or `diff_file`, 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.
compareCompare two refsARead-only
Summarise what changed on to since it split from from — git diff <merge-base> <to> — as one row per file: git's status letter (A, M, D, R091 old -> new) and ± counts, or binary. The merge_base is printed, so which diff ran is never hidden; merge_base: false asks for the raw two-dot diff instead, and when the two refs share no history the two-dot diff runs with merge_base="none". This never returns a patch — a branch diff is unbounded — so the file list is capped by max_files and a capped answer says so. Use diff_file for one file's patch, passing a printed merge_base as from for the branch view of it. path is a git pathspec, and it narrows rename detection with it: a rename can then appear as a delete and an add. Both refs are resolved before the diff and both commits are stated in the result, with synced_at.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | The ref to compare to — whose changes are summarised, e.g. 'fix/PROJ-7608'. | |
| from | Yes | The ref to compare from — what the split is measured from, e.g. 'develop' or a commit sha. | |
| path | No | A git pathspec to narrow the comparison, e.g. 'src/'. Note that it narrows rename detection too. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| max_files | No | Maximum changed files to list (default 100). Totals still cover every file; a capped answer says so. | |
| merge_base | No | Diff from the merge base of the two refs (default true). Set false for the raw two-dot diff — 'what would applying this do'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so safety is covered; the description adds non-obvious behaviour instead: it never returns a patch because branch diffs are unbounded, the file list is capped by `max_files` and a capped answer is labelled as such, the `merge_base` used is printed, and both resolved commits plus `synced_at` appear in the result. These are real operational traits an agent cannot get from the annotations.
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?
Front-loaded with the core operation and output format before the caveats, and essentially every sentence carries information. It is a dense single paragraph with several interleaved concerns (capping, rename narrowing, no-history case), which costs some scannability but no obvious 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?
There is no output schema, so the description must carry the return contract — and it does: per-file status letters (`A`/`M`/`D`/`R091 old -> new`), ± counts, `binary`, printed `merge_base`, resolved commits and `synced_at`. Combined with the cap semantics and sibling routing, an agent has everything needed to call and interpret this tool.
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 earns an extra point by explaining consequences the schema only hints at — that narrowing with `path` can turn a rename into a delete plus an add, and that a `merge_base="none"` result signals unrelated histories. The `merge_base` toggle is largely restated from the schema, keeping this short of a 5.
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 precise verb and resource — summarise what changed on `to` since it split from `from` — and pins down the exact git operation (`git diff <merge-base> <to>`) and output shape (one row per file with status letter and ± counts). It also differentiates itself from the sibling `diff_file`, so an agent can pick between them without opening either 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?
Explicitly routes the agent: use `diff_file` for a single file's patch, and pass a printed `merge_base` as `from` to get the branch view of it. It also states the conditions that change behaviour (`merge_base: false` for the raw two-dot diff, no shared history ⇒ two-dot with `merge_base="none"`), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
diff_fileDiff one file between two refsARead-only
The unified diff of one file between two refs — git diff -M <from> <to> -- <path> — without a working tree. The caller names the file and the diff is scoped before git runs, so nothing is found by parsing a patch header. context sets the lines shown around each hunk (default 3, 0-10) and max_bytes caps the diff. Scoping by path narrows rename detection, so a renamed file is read as an added (or deleted) file. The two refs are a raw two-dot diff: pass a merge_base printed by compare as from for the branch view of one file. A file that is unchanged between the refs yields no diff, and one absent from both is reported as not found. A binary file is refused: git has only a "Binary files … differ" line for it, which is not a patch.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | The ref to diff to, e.g. 'fix/PROJ-7608'. | |
| from | Yes | The ref to diff from, e.g. 'develop' or a commit sha. | |
| path | Yes | File path from the repository root, e.g. 'src/index.tsx'. | |
| context | No | Lines of context on each side of a hunk (default 3, at most 10). Use 0 for the fewest lines. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| max_bytes | No | Byte cap for the patch (default 200000). A cut patch is marked in the result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations only covering readOnly/openWorld, the description adds substantial behavioral detail: path scoping is applied before git runs, rename detection is narrowed so renames read as add/delete, unchanged files yield no diff, absent files are reported not found, and binary files are refused because git emits no patch. These are exactly the edge cases an agent must anticipate.
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?
Front-loaded first sentence gives purpose and command form, and the remaining sentences each carry an edge case rather than filler. It is dense but slightly long, with some detail (e.g. restating context default) that the schema already supplies.
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?
No output schema exists, yet the description explains the meaningful result states (patch, empty diff, not-found, binary refusal) and the safety profile is covered by annotations. An agent has everything needed to call it correctly across the 6-param surface.
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 meaning to `from`/`to` (raw two-dot diff; merge_base from `compare`) and to how `context` and `max_bytes` shape the patch. The context default/range restates the schema, but the ref semantics elevate it above the baseline.
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 precise verb+resource ('The unified diff of one file between two refs') plus the exact underlying command, and distinguishes the scope from siblings like compare and get_file. An agent can tell it produces a patch for a single named path rather than a ref comparison or file read.
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 operative guidance: pass a `merge_base` printed by `compare` as `from` for a per-file branch view, and explains the consequences of path scoping (narrowed rename detection). It references the `compare` sibling but does not explicitly say when to prefer this over `compare`/`get_file` for other tasks.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_commitRead one commitARead-only
Read one commit — its sha, parents, author and committer with dates, and full message — then the files it changed with git's status letter (A, M, D, R091 old -> new) and their line counts, or binary. rev accepts a full or abbreviated sha, or a branch or tag. patch: true appends the unified diff, capped by max_bytes. A merge commit reports merge="true" and no file list, because git shows no diff for a merge by default: compare a parent with the commit to see what it brought in. The result states the commit it resolved rev to and synced_at. Give either url — a GitLab /-/commit/<sha> link — or rev; the link's host must be GITLAB_HOST.
| Name | Required | Description | Default |
|---|---|---|---|
| rev | No | The commit to read: a full or abbreviated sha, or a branch or tag name that resolves to one. Required unless `url` is given. | |
| url | No | A GitLab commit link, e.g. 'https://gitlab.example.com/group/repo/-/commit/8297d0da'. Replaces rev; the host must be GITLAB_HOST. | |
| patch | No | Append the commit's unified diff. Default false: metadata and the file list only. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| max_bytes | No | Byte cap for the patch when `patch` is set (default 200000). A cut patch is marked in the result. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint, so the description adds real behavioral context: merge commits report merge="true" with no file list, patches are capped and marked when cut, and the result states the resolved commit and synced_at. No contradictions with the read-only annotation.
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?
Dense but front-loaded, leading with what the commit read returns before the parameter guidance and merge caveat. Every sentence carries information, though the run-on style packs several distinct ideas into long clauses.
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 no output schema, the description correctly carries the burden of describing the return shape (commit metadata, file list with status letters, merge marker, synced_at). It is nearly complete for the tool's complexity, with only minor gaps such as pagination or error behavior.
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 still adds meaning beyond the schema: rev accepts full/abbreviated sha or branch/tag, url must have a GITLAB_HOST host, and patch appends a unified diff capped by max_bytes with cut patches flagged.
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 (read) and resource (one commit) and enumerates exactly what is returned: sha, parents, author/committer with dates, message, and changed files with status letters and line counts. This clearly differentiates it from list-oriented siblings like log and list_tree.
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?
Explains the url-vs-rev choice ("Give either `url` ... or `rev`") and points to the alternative for merges ("compare a parent with the commit to see what it brought in"). It names the sibling compare implicitly but does not spell out general when-not-to-use cases beyond merges.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_fileRead a file at a specific refARead-only
Return the contents of a file at a given branch, tag, or commit WITHOUT checking it out. Use this to inspect files on a branch other than the one currently checked out. Give either url — a GitLab web link, whose #L12-20 line anchor is honoured — or both ref and path. The repository is mirrored locally on first use and refreshed when it goes stale, or, for a project registered with add_repo, read from your own clone. The result reports the resolved commit and the last sync time. If the copy cannot be refreshed, the cached copy answers and the result says the refresh failed.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Branch, tag, or commit SHA, e.g. 'develop' or 'fix/PROJ-7608'. Required unless `url` is given. | |
| url | No | A GitLab web link, e.g. 'https://gitlab.example.com/group/repo/-/blob/develop/src/index.tsx#L12-20'. Replaces ref, path and (usually) project; the host must be GITLAB_HOST. | |
| path | No | File path inside the repository, forward slashes, e.g. 'src/index.tsx'. Required unless `url` is given. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT, or to the URL's project. | |
| max_bytes | No | Maximum bytes of content to return (default 200000). Ignored when a line range is asked for, which needs the whole file. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only carry readOnlyHint=true and openWorldHint=false; the description goes well beyond that by disclosing the mirror-on-first-use / refresh-when-stale model, the add_repo clone path, the stale-cache fallback when refresh fails, and that the result reports the resolved commit and sync time. This is exactly the extra behavioral context the annotations cannot supply, and it is consistent with openWorldHint=false.
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 scope constraint is front-loaded, and each subsequent sentence carries distinct information (addressing modes, the mirror lifecycle, the failure fallback). A few sentences are dense and could be split, but there is no filler or repetition.
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?
Given no output schema, the description usefully sketches the return payload (resolved commit, last sync time) and the degraded-refresh case, and it covers all five parameters' roles. A fully 5 definition might specify the content/encoding shape or the error behaviour when neither url nor ref+path is supplied, but the critical calling information is present.
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 baseline is 3, but the description adds genuine semantics: the `url` alternative to `ref`+`path`, that a `#L12-20` line anchor is honoured, that the URL usually overrides project, and that project falls back to GITLAB_DEFAULT_PROJECT or the URL's project. It does not explain max_bytes interaction beyond what the schema already says.
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 precise verb and resource ('Return the contents of a file at a given branch, tag, or commit') and adds the differentiator 'WITHOUT checking it out', which separates it from diff_file, list_tree, and get_commit. An agent can pick this tool out of the sibling list without opening a 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 a clear operative condition: 'Use this to inspect files on a branch other than the one currently checked out.' It also explains how to address the file (url vs ref+path) and when the local mirror versus your own registered clone is used. It does not name a sibling alternative or state when NOT to use it, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_refsList branches and tagsARead-only
List branch and/or tag names in a repository, without cloning it into a working tree. Each row is kind, short commit, name, and the tip commit's subject. The list reflects the local copy as of synced_at (by default at most 10 minutes old); a ref created since that moment may be missing, because a stale list cannot be detected. Use resolve_ref to check one specific ref, which does refresh on a miss. If the copy cannot be refreshed, the cached copy answers and the result says the refresh failed.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Which refs to list: 'branches' (default), 'tags', or 'all'. | |
| search | No | Case-insensitive substring filter on the ref name, e.g. 'PROJ' or 'fix/'. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| max_refs | No | Maximum refs to return (default 200). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint/openWorldHint; the description adds substantial non-obvious behavior: results reflect a local copy as of `synced_at` (default ≤10 min old), newly created refs may be missing, staleness is undetectable, and on refresh failure the cached copy answers and the result flags the failure. That is exactly the kind of operational context structured fields cannot carry.
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?
Front-loaded with purpose and output shape, then caveats in decreasing priority. Dense but almost every clause carries real information; the staleness/failure explanation is slightly verbose but justified given no output schema.
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 no output schema, the description compensates by specifying the returned row fields (kind, short commit, name, tip subject) and the freshness/failure semantics. An agent has everything needed to call it and interpret results.
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 four parameters (kind, search, project, max_refs) are already documented in the schema. The description adds no syntax or default 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?
States a specific verb and resource ('List branch and/or tag names in a repository') plus the defining scope qualifier 'without cloning it into a working tree', which distinguishes it from file/tree siblings like list_tree and get_file. It even describes the row shape, so an agent knows exactly what comes back.
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?
Explicitly routes the agent to the sibling alternative: 'Use `resolve_ref` to check one specific ref, which does refresh on a miss.' This gives a clear when-to-prefer-the-other-tool condition rather than leaving the choice to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_reposList known repositoriesARead-only
List the repositories this server knows about, with the freshness policy applied to each and when it last synced successfully. Each row is the project path followed by source (mirror or local), freshness (ttl, always or manual), ttl, clone_strategy=<value>(unsupported) when the registration names a strategy this server does not implement (only full is; such a project refuses to be read until it is re-cloned), present (whether the data is on disk), local_path for a local clone, and synced_at, or unknown when it has never synced. A mirror-backed project is registered the first time it is used; add_repo registers one before that, and is also how a project is pointed at an existing local clone. freshness=manual means nothing refreshes it by itself: use sync_repo.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations already declaring readOnlyHint and openWorldHint=false, the description adds substantial operational context: freshness semantics (ttl/always/manual), the fact that manual entries never self-refresh, that unsupported clone_strategy makes a project refuse to be read until re-cloned, and that synced_at may be 'unknown'. This is behavior an agent cannot infer from the annotations.
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 output-column enumeration is dense but front-loaded with purpose, and each column row earns its place given there is no output schema. Slightly long, but no clearly redundant sentences.
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?
There is no output schema, so the description compensates by documenting the full row layout (path, source, freshness, ttl, clone_strategy, present, local_path, synced_at). Combined with the freshness and re-clone caveats, it is complete enough for an agent to call and interpret the result correctly.
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?
The tool takes zero parameters, so the schema carries no semantic burden; the baseline for a 0-param tool is 4. The description instead spends its budget on return-shape semantics, which is appropriate but not a parameter concern.
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 ('List the repositories this server knows about') and immediately scopes it with the freshness policy and sync state, which no sibling does. It also contrasts with add_repo and sync_repo, letting an agent place it among the siblings without opening a 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 clear routing context: add_repo registers a mirror-backed project before first use or points at an existing clone, and freshness=manual cases should be refreshed via sync_repo. There is no explicit 'use list_repos when X vs Y' statement, so it stops short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_treeList a directory at a refARead-only
List the files and subdirectories at a given ref, one level deep by default; set recursive to walk the whole subtree. Files carry their size in bytes, so you can tell a small config from a bundle before reading it. Names are full paths relative to the repository root. Give either url — a GitLab tree link — or ref (and optionally path). Everything comes from git, so no file is fetched to answer this. If the copy cannot be refreshed, the cached copy answers and the result says the refresh failed.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Branch, tag, or commit SHA, e.g. 'develop' or 'fix/PROJ-7608'. Required unless `url` is given. | |
| url | No | A GitLab tree link, e.g. 'https://gitlab.example.com/group/repo/-/tree/develop/src'. Replaces ref, path and (usually) project; the host must be GITLAB_HOST. | |
| path | No | Directory inside the repository, forward slashes, e.g. 'src'. Defaults to the repository root. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT, or to the URL's project. | |
| recursive | No | List every file in the subtree instead of just the immediate children (files only). | |
| max_entries | No | Maximum entries to return (default 200). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and openWorldHint=false, so the safety profile is covered. The description adds real behavioral context beyond that: everything is answered from git with no file fetched, and a stale-cache fallback where the result flags a failed refresh. This is genuinely useful operational detail.
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?
Front-loaded with the core action and default depth, then parameter selection, then behavior. Every sentence carries information; the length is justified by the six parameters and caching nuance, though it is denser than strictly necessary.
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 no output schema, the description carries the return-value burden, and it does explain that results carry sizes and repo-root-relative full paths. Ordering and pagination behavior beyond the max_entries default are not covered, leaving a minor gap for a listing tool.
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 goes further by clarifying relationships the schema does not: url replaces ref/path/project, names are full paths relative to repo root, and size is in bytes. That adds meaning beyond the per-field descriptions.
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 uses a specific verb (list) on a specific resource (files and subdirectories at a ref) and states scope precisely: 'one level deep by default; set recursive to walk the whole subtree.' This clearly separates it from read-oriented siblings like get_file and log without needing to 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 gives concrete selection guidance: use url OR ref (plus optional path), and use recursive only when you want the whole subtree. The remark that sizes let you 'tell a small config from a bundle before reading it' implies using this before get_file. It stops short of explicit when-not or naming alternative siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
logList commits at a refARead-only
List commits on a branch, tag, or commit — git log — newest first, without cloning it into a working tree. Each row is commit, date, author, subject, and a merge commit carries (merge). The ref defaults to the repository's default branch, is resolved before the walk, and the result states the commit it walked and synced_at. path is a git pathspec, so globs work — and history stops at a rename, because git's --follow accepts only one path and walks renames backwards; pass the new name or use get_commit when you need to see the rename itself. limit bounds the answer; when it hides older commits the result says so rather than inventing a total. Use get_commit on a row's full sha to see what it changed. Give either url — a GitLab /-/commits/<ref>[/<path>] link, e.g. 'https://gitlab.example.com/group/repo/-/commits/fix/PROJ-7608/src/index.tsx' — or ref and path; the link's host must be GITLAB_HOST.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Branch, tag, or commit whose history to walk, e.g. 'fix/PROJ-7608'. Defaults to the default branch. | |
| url | No | A GitLab history link, e.g. 'https://gitlab.example.com/group/repo/-/commits/develop'. Replaces ref and path; the host must be GITLAB_HOST. | |
| path | No | A git pathspec to limit the walk, e.g. 'src/components/*.tsx'. History stops at a rename: a file's earlier names are not followed. | |
| limit | No | Maximum commits to return (default 20). A capped answer says so. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint/openWorldHint already covering the safety profile, the description adds real behavioral context: no working-tree clone, ref resolution before the walk, the result reporting the walked commit and `synced_at`, truncation being disclosed rather than a fabricated total, and the rename termination behavior of `path`.
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?
Front-loaded with the core action and return shape, and every sentence carries information; the caustic details (rename stop, limit honesty) earn their place. It is dense, with several clauses packed per sentence, which slightly taxes readability but adds 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?
There is no output schema, so the description supplies the return shape itself (commit, date, author, subject, `(merge)` marker) plus the `synced_at`/walked-commit metadata. Combined with 100% parameter coverage, an agent has everything needed to invoke and interpret this tool.
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 goes further: it gives the concrete `/-/commits/<ref>[/<path>]` link shape with a full example for `url`, notes `url` replaces `ref` and `path`, and explains the glob/rename semantics of `path` and the capping behavior of `limit`.
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 precise verb+resource ('List commits on a branch, tag, or commit'), names the git equivalent, and specifies ordering ('newest first') and the no-clone execution model. It is clearly distinguishable from siblings get_commit (what a single commit changed) and blame/list_tree.
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?
Explicitly routes to alternatives: 'Use `get_commit` on a row's full sha to see what it changed' and 'use `get_commit` when you need to see the rename itself.' It also states the two mutually exclusive input forms (either `url`, or `ref` and `path`) and their constraints.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
parse_gitlab_urlParse a GitLab web URLARead-only
Turn a GitLab web link into the project, ref, path and line range a read needs — including a ref that contains slashes, which is resolved against the project's own refs. Accepts /-/blob/, /-/raw/, /-/tree/ and /-/blame/ links, which name a file or a directory, and /-/commit/ and /-/commits/[/], which name a commit and a history; merge request and other links are refused by name rather than guessed at. The link's host must be GITLAB_HOST. Resolving may mirror the project on first use; set resolve: false to parse offline without touching it. get_file, list_tree, log, get_commit and blame accept a url directly, so this tool is for inspecting a link, not for reading.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The GitLab web link, e.g. 'https://gitlab.example.com/group/repo/-/blob/fix/PROJ-7608/src/index.tsx#L12-20'. | |
| resolve | No | Match the link's ref against the project's refs and resolve it to a commit (default true). Set false to split the ref at the first segment without touching the project. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered structurally. The description nonetheless adds real behavioral context: the host must be GITLAB_HOST, resolving may mirror the project on first use, and `resolve: false` avoids touching it. It does not describe the shape of the returned object (project/ref/path/line range are only implied), so it adds value but stops short of full behavioral disclosure.
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?
Front-loaded with the core transformation in the first clause, and every sentence carries non-redundant information (link types, refusals, host constraint, mirroring, plus the sibling hand-off). Sentences are long and clause-heavy, which costs some scannability but not correctness.
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?
No output schema exists, and the description compensates by naming what is extracted (project, ref, path, line range) and what happens on rejection. With the host/mirroring constraints and offline option covered, an agent has enough to call it correctly.
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 adds meaning beyond the schema by explaining that refs containing slashes are resolved against the project's own refs, which clarifies the hard case the two schema lines only gesture at. It does not add syntax detail for the base url parameter, so it falls short of 5.
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 ('Turn a GitLab web link into the project, ref, path and line range') and enumerates exactly which URL shapes it handles. It distinguishes itself from siblings by naming get_file, list_tree, log, get_commit and blame and clarifying they accept a url directly, so an agent can route without opening schemas.
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 explicit when-to-use ('for inspecting a link, not for reading') and when-not-to-use, plus an inbound filter: which GitLab link types are accepted versus refused ('merge request and other links are refused by name rather than guessed at'). It also states the host precondition and the offline escape hatch.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pickaxeFind commits that changed a stringARead-only
Find the commits whose diff changed how often a piece of text appears — git log -S — so you can answer "when was this introduced, and by whom?". Each row is commit, date, author, subject, most recent first. The text is matched literally and case-insensitively, as search matches it, so a pickaxe never reports "nothing changed" about text the caller can see. Unlike search this walks the history of the ref and is therefore slower; pair it with search to go from "where is it" to "when did it appear". The ref defaults to the repository's default branch, and path is a git pathspec.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Branch, tag, or commit whose history to walk. Defaults to the default branch. | |
| path | No | A git pathspec to limit the search, e.g. 'src/components'. | |
| limit | No | Maximum commits to return (default 20). | |
| query | Yes | Exact text to follow through history, e.g. 'orderTotals'. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| case_sensitive | No | Follow the text case-sensitively. Default false: history is followed by case too. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnlyHint=true, openWorldHint=false), and the description adds matching semantics (literal, case-insensitive, consistent with `search`), the ref default (repository default branch), result ordering (most recent first), and the relative cost versus `search`. These are concrete behavioral traits beyond the structured fields.
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?
Front-loads the core behavior in the first clause, then layers return shape, matching semantics, cost/comparison to sibling, and argument defaults. Every sentence carries distinct information despite the length.
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 no output schema, the description still specifies the row shape (`commit`, `date`, `author`, subject) and ordering, so an agent knows what comes back. Combined with the annotations and full schema coverage, nothing needed to invoke it 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 adds value by clarifying that matching is literal and case-insensitive consistent with `search`, which explains `case_sensitive`'s default behavior, and by restating ref/path semantics in workflow terms. It does not document `limit` or `project` beyond the schema, so it is not fully additive.
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 precise verb+resource: finding commits whose diff changed the occurrence count of a piece of text, with the git analogue (`git log -S`) and the user-facing question it answers. It explicitly distinguishes itself from the `search` sibling, so an agent can select between them without opening either 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 explicit when-to-use and the routing rule: use `search` to find where text is, use this to find when it appeared, and pair them. It also warns that this walks history and is slower, which is exactly the exclusion criterion an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_repoForget a repositoryADestructive
Forget a project's registration. With delete_mirror, the server-side mirror is deleted as well — but only a mirror: a registered local clone is never touched, and the request is reported as ignored. Forgetting a mirror-backed project is not permanent, since the next read registers it again and clones it again if the mirror was deleted. Forgetting a local registration is how a project goes back to being read from a mirror.
| Name | Required | Description | Default |
|---|---|---|---|
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| delete_mirror | No | Also delete the cached mirror for this project. Never deletes a local clone. Defaults to false, which forgets the project and leaves the data on disk. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations' destructiveHint=true, it discloses exactly what is destroyed (server-side mirror only; local clone never touched), that the request may be silently 'reported as ignored', and that mirror-backed forgetting is not permanent because the next read re-registers and re-clones. This is rich operational detail an agent cannot get from the annotations.
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 action is front-loaded in the first sentence, and every following clause carries a distinct behavioral fact (mirror scope, ignored request, re-registration, local→mirror revert). The dense em-dash constructions make it slightly heavier than necessary, but nothing is padding.
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 mutation tool with no output schema, the description covers the important semantics: what gets deleted, what is immune, idempotency/reversibility. It does not mention permission or authentication requirements, which is the only notable gap.
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 baseline is 3, but the description adds meaning beyond the schema: delete_mirror's effect is non-permanent (re-created on next read) and applies only to mirrors, while omitting it reverts a local registration to mirror-backed reading.
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 specific verb+resource ('Forget a project's registration') and immediately clarifies the scope of what is and isn't removed, which no sibling tool covers. It doesn't explicitly name add_repo/list_repos as counterparts, so it is clear but not sibling-routed.
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 explains clearly what delete_mirror does and when forgetting is reversible versus how a local registration is switched back to mirror-backed reading, giving real context for choosing the flag. It stops short of an explicit when-to-use-this-instead-of alternative (e.g. add_repo, sync_repo).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_refResolve a ref to a commitARead-only
Resolve a branch, tag, or revision to the full commit SHA it points at, and say what it is (branch, tag, or commit for anything else). Use this to confirm a ref exists before reading files from it. If the ref is missing, the error lists similar names, and it says so when the copy could not be refreshed, since then a missing ref proves nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | Yes | Branch, tag, or commit SHA, e.g. 'develop' or 'fix/PROJ-7608'. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds genuinely non-derivable behavior: missing refs produce an error that 'lists similar names,' and a stale working copy is flagged so that 'a missing ref proves nothing.' That stale-data caveat is exactly the kind of context that prevents a wrong conclusion, though nothing is said about auth or rate limits.
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, front-loaded with what the tool returns before the usage advice, and every clause carries information. The final sentence is somewhat dense ('and it says so when the copy could not be refreshed') but still earns its place by qualifying the error semantics.
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 two-parameter read tool with no output schema, the description covers purpose, when to call it, the shape of the return value, and error behavior. With annotations handling the safety profile and the schema handling parameter docs, it is close to complete; only the absence of any mention of alternative ref-listing tools is a minor gap.
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%, with the ref format and an example ('develop', 'fix/PROJ-7608') and the project path fallback to GITLAB_DEFAULT_PROJECT already documented in the schema. The description adds no syntax, format, or default information 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 ('resolve'), the resource ('a branch, tag, or revision'), and the exact output ('the full commit SHA it points at', plus a type label of branch/tag/commit). That scope clearly separates it from siblings like list_refs (enumerate) or get_commit (fetch metadata), so an agent can route correctly 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?
It gives an explicit use case: 'Use this to confirm a ref exists before reading files from it,' which tells the agent when this is the right call. It does not name an alternative or say when not to use it (e.g. vs. list_refs), so it stops short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchSearch code at a refARead-only
Search a whole file tree at a branch, tag, or commit — git grep — without cloning it into a working tree. Matches come back grouped by file: the path, then line: text for each hit. Set output to files or count when you want to know which files, or how many hits each holds, rather than read the lines: those two ask git not to emit a matched line at all, so they stay cheap and their totals stay exact on a broad query. Matching is literal (foo|bar matches that text, not foo or bar) and case-insensitive unless you say otherwise; the ref defaults to the repository's default branch. The ref is resolved before searching, so a branch created moments ago is fetched on the miss, and the result states the commit it searched and synced_at. path is a git pathspec, passed through as git defines it: src/components/*.tsx works, but a plain pathspec's ** needs at least one directory level — use :(glob)src/**/*.tsx for zero or more. A path that matches nothing simply returns no matches. Pass context to see the lines around each match: the hunk is numbered and matched lines are marked >, which git's own context flags cannot do here — under -z they lose which line matched. Use pickaxe to find when the text appeared.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | Branch, tag, or commit to search, e.g. 'fix/PROJ-7608'. Defaults to the default branch. | |
| path | No | A git pathspec to limit the search, e.g. 'src/components/*.tsx'. Use ':(glob)src/**/*.tsx' for `**` to match zero or more directories, which a plain pathspec does not do. | |
| query | Yes | Text to look for, e.g. 'vehicleCount'. Literal unless `regex` is set. | |
| regex | No | Treat `query` as a POSIX extended regex (not PCRE). Default false: match it literally. | |
| output | No | What to return. Default 'matches': the matching lines, grouped by file. 'files': just the paths that matched. 'count': the path and matching-line count per file, with an exact 'total'. The last two skip emitting matched lines, so they are cheaper and stay exact on a broad query. | |
| context | No | Lines to show on each side of every match (default 0, none), marked so a matched line is distinguishable from a nearby one. Built by reading each displayed file once — at most 20 files per call — because git's own context flags lose which line matched under -z. Needs output 'matches'. | |
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. | |
| max_matches | No | Maximum rows to return (default 100): matches, or files in the other modes. Tops them out; the totals in the header stay exact. | |
| case_sensitive | No | Match case exactly. Default false: search ignores case. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond readOnlyHint/openWorldHint it discloses non-obvious behavior: literal matching with case-insensitive default, ref defaults to the default branch, a cold ref is fetched on miss, results report `commit` and `synced_at`, context is built by re-reading each file with a cap of 20 files per call, and max_matches caps rows while totals stay exact.
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?
Front-loaded with the core capability and the output shape before edge cases. It is dense but nearly every clause carries actionable detail; the ref/pathspec/context sentences could be tightened into a list for faster scanning.
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?
Nine parameters and no output schema, yet the description effectively supplies one (grouping, line format, header fields, exact totals). Limits, defaults, and error-ish behavior (empty path match, ref fetch on miss) are all covered, so an agent can call it correctly without further information.
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 already 100%, so baseline is 3; the description earns an extra point by explaining consequences the schema only hints at — that `files`/`count` make git skip emitting matched lines so totals stay exact, and why `context` must be rendered internally. It repeats the pathspec `**` note verbatim from the schema, which slightly dilutes the added value.
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 a whole file tree at a branch, tag, or commit — git grep') and adds the key scoping fact ('without cloning it into a working tree'), which separates it from get_file, list_tree, and blame. The output shape (path then 'line: text' grouped by file) further pins down what it returns.
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?
Explicitly routes to `output: files|count` when counts matter and to `pickaxe` when the question is *when* text appeared, and warns that a nonexistent path silently returns nothing. It stops short of naming get_file or log as alternatives for the reverse cases, so it's clear but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sync_repoFetch a repository nowA
Fetch a project immediately, ignoring its freshness policy — the way to refresh a project whose policy is manual, and to check for new refs without waiting for the TTL. A mirror-backed project is cloned if its mirror is missing; a project registered as a local clone is fetched from that clone's own remote. Unlike a read, a failure is reported as an error rather than answered from the cached copy, because this call exists to fetch.
| Name | Required | Description | Default |
|---|---|---|---|
| project | No | GitLab project path, e.g. 'group/subgroup/repo'. Defaults to GITLAB_DEFAULT_PROJECT. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds rich behavioral detail beyond the annotations: a mirror-backed project is cloned if its mirror is missing, a local clone is fetched from its own remote, and failures surface as errors rather than cached answers. This meaningfully explains the readOnlyHint=false mutating behavior and is exactly the kind of context annotations cannot carry.
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?
Front-loads the core action and keeps to three sentences, each carrying distinct information. Slightly dense and the final clause is somewhat circular ('this call exists to fetch'), but there is no wasted padding.
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 mutation-flagged, open-world single-parameter tool with no output schema, the description covers trigger conditions, cache/failure behavior, and clone vs. mirror handling. It is close to complete; only return-value expectations remain implicit, which is acceptable given the tool's purpose.
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 the single `project` parameter (including its default) is fully documented in the schema. The description adds behavioral context but no syntax or format detail beyond what the schema already provides, 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 ('Fetch a project immediately, ignoring its freshness policy') and immediately contrasts the behavior with a cached read. An agent can distinguish this from read siblings like get_file or list_refs without opening either 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?
Explicitly names the conditions that select this tool: refreshing a project whose policy is `manual` and checking for new refs without waiting for the TTL. That is clear when-to-use guidance, though it routes by condition rather than naming a specific sibling alternative.
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.
16 tool updates
v0.10.1- First observed
add_repo - First observed
blame - First observed
compare - First observed
diff_file - First observed
get_commit - First observed
get_file - First observed
list_refs - First observed
list_repos - First observed
list_tree - First observed
log - First observed
parse_gitlab_url - First observed
pickaxe - First observed
remove_repo - First observed
resolve_ref - First observed
search - First observed
sync_repo
TDQS
Scored across 16 tools
Each tool maps to a distinct git or repository-management operation; overlaps like log/get_commit/pickaxe and compare/diff_file are explicitly separated in descriptions. No two tools appear interchangeable.
Mostly snake_case with a verb_noun pattern, but several tools are bare git terms (log, blame, compare, search, pickaxe) rather than verb_noun. Casing is consistent and names remain readable, so this is a minor deviation.
16 tools is at the upper end of the ideal 3-15 range, but each adds a distinct capability for git inspection plus mirror administration. Slightly heavy, though still reasonable for the domain.
Covers file/tree reading, ref resolution, commit history/detail, blame, diff/compare, search, pickaxe, URL parsing, and mirror registration/sync. Minor gaps: no project discovery on GitLab and no MR/issue support, but the core read-only git surface is complete.
Maintenance
Related MCP Connectors
Code intelligence for LLMs. Analyze, search, and retrieve code from any public git repository.
Git-backed platform for skills, tools, and context for AI agents
- OKForestOAuthai.okforest
Your organization's knowledge as OKF trees in Git, for AI assistants to search, read, and cite
Read and write KukGit repositories, files, issues and pull requests from an AI assistant.
Related MCP Servers
- AlicenseBqualityAmaintenanceEnables AI-powered exploration and interaction with GitLab instances through comprehensive search, code browsing, and repository management. Supports both self-hosted and GitLab.com with flexible authentication for read and write operations.6348 npm12MIT
- FlicenseNot gradedqualityNot gradedmaintenanceEnables AI agents to interact with GitLab repositories, monitor documentation changes, manage issues and merge requests, and search across projects with comprehensive file and commit operations.-
- 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.29 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to query and manage GitLab projects, issues, merge requests, and file contents via the GitLab REST API v4.8 npmMIT