Other git hosts
The dashboard and the REST API take github.com only. Claude can also add a public repository from GitLab, Bitbucket or a self-hosted git server by calling the add_repository tool. This page covers the URLs it accepts, how the backend reads the repository, the links in search results and every refusal message.
Ask Claude something like:
Add https://gitlab.com/group/project to CodeSage.
Claude calls add_repository, which returns repository_id, name, status and Indexing started; check list_repositories for progress. Indexing runs in the background. Ask Claude to check list_repositories, and search once the status is completed. Then try “Search it for where requests are retried.”
Accepted URL forms
Section titled “Accepted URL forms”The URL must be an https URL on a public host. The tool also accepts http:// (rewritten to https://) and git@host:path (rewritten to https://host/path).
| Host | URL | Result |
|---|---|---|
| GitLab | https://gitlab.com/group/project |
Name gitlab.com/group/project, default branch |
| GitLab, nested groups | https://gitlab.com/group/subgroup/project |
Name gitlab.com/group/subgroup/project |
| GitLab, branch | https://gitlab.com/group/project/-/tree/dev |
Branch dev |
| GitLab, branch with slash | https://gitlab.com/group/project/-/tree/feature/login |
Branch feature/login |
| Bitbucket | https://bitbucket.org/team/repo |
Name bitbucket.org/team/repo |
| Bitbucket, branch | https://bitbucket.org/team/repo/src/main |
Branch main |
| Self-hosted | https://git.example.com/team/repo |
Name git.example.com/team/repo |
| Self-hosted, nested path | https://git.example.com/a/b/c |
Name git.example.com/a/b/c |
| GitHub | https://github.com/owner/repo/tree/dev |
Name owner/repo, branch dev |
Any host, .git suffix |
https://gitlab.com/group/project.git |
.git and a trailing slash are removed |
| Any host, SSH form | git@gitlab.com:group/project.git |
Rewritten to https://gitlab.com/group/project |
Rules behind the table:
- Branch markers. On GitHub the branch follows
/tree/. On other hosts it follows/-/tree/. Onbitbucket.orgit follows/src/. Everything after the marker is joined into the branch name, so a URL that ends in a file path becomes a very odd branch. Pass a plain repository URL plus thebranchargument when in doubt. - Other hosts. Without one of those markers, extra path segments are read as nested groups. A forge with a different URL style (Gitea’s
/src/branch/main, for example) isn’t understood; the segments become part of the repository path and the clone later fails. - Argument wins. If you pass
branchit overrides the branch in the URL. An emptybranchmeans the default branch, even with a/tree/URL. - Segment characters. Each path segment may contain only letters, digits,
_,.and-, and may not be.or... www.is removed only fromwww.github.com.- Length. The URL is at most 500 characters and the derived name at most 255.
- One row per URL. The stored URL has no branch, so you can’t index two branches of the same repository. A second add, even with another branch or another letter case, is refused as a duplicate.
A GitHub repository is named owner/repo. Every other repository is named <host>/<path>, such as gitlab.com/group/project, so names from different hosts can’t collide. Pass that name, its numeric ID, or the same name in any letter case as the repository argument of the other tools.
Public repositories only
Section titled “Public repositories only”Git can’t ask for credentials here, so a repository that needs a login is treated as missing. Private repositories are refused on every host except github.com, where the server’s GITHUB_TOKEN can read them. The message is:
gitlab.com/group/project was not found or is private; only public repositories are supported on gitlab.com.A repository that answers 401, 403 or 404 to git, or prints “authentication failed” or “terminal prompts disabled”, is reported this way.
Rejected URLs
Section titled “Rejected URLs”A URL the backend rejects comes back as an error that starts with url: and says why. Nothing is created.
| Rejected | Message after url: |
|---|---|
ssh://..., ftp://..., git://... |
Invalid repository URL (only https is supported): <url> |
| No host | Invalid repository URL (missing host): <url> |
https://user:pass@host/... or https://user@host/... |
Invalid repository URL (credentials are not allowed): <url> |
Any port, even :443 |
Invalid repository URL (ports are not supported): <url> |
?query or #fragment |
Invalid repository URL (query or fragment not allowed): <url> |
localhost or *.localhost |
Invalid repository URL (localhost is not allowed): <url> |
127.0.0.1, 10.0.0.5, 192.168.1.5, [::1] (any non-public IP literal) |
Invalid repository URL (private or loopback address): <url> |
A /-/ not followed by tree; a GitHub URL with extra segments other than /tree/<branch>; a Bitbucket URL with extra segments other than /src/<branch> |
Invalid repository URL (unsupported path): <url> |
| Fewer than two path segments | Invalid repository URL (missing owner or repository): <url> |
A segment with other characters, or . and .. |
Invalid repository URL (bad owner or repository name): <url> |
A branch starting with - or containing a .. segment |
Invalid repository URL (bad branch): <url> |
| A branch argument with the same faults | branch: Invalid branch name |
| An empty or whitespace-only URL | Invalid repository URL: empty |
| Over 500 characters | url: String should have at most 500 characters |
The IP rule means a repository on a private network (an internal GitLab at 10.x.x.x) can’t be added by IP. A DNS name is not checked by the URL rules, but it is resolved before git runs: a name that resolves to a private, loopback or other non-public address (such as 10.0.0.1.nip.io), or that doesn’t resolve, is refused. See Refusal messages.
How the backend reads the repository
Section titled “How the backend reads the repository”github.com goes through the GitHub REST API. Any other host is read with the git command, so the backend machine needs git (the prod Docker image includes it).
- Check. Before anything is created, the backend resolves the host name and refuses it unless every address is public. It then runs
git ls-remote --heads --symref -- <url>(withrefs/heads/<branch>appended when a branch is set), pinned to the checked address. It times out after 10 seconds. - Create. If the check passes, a repository row is created with status
pendingand a background job starts. - Clone. The job resolves and checks the host again, then runs
git clone --depth 1 --single-branch --no-tags(with--branch <b>when set) into a temporarycodesage-clone-*folder. It times out after 120 seconds.git rev-parse HEADrecords the commit that was cloned, and source links point at it. The folder is always removed afterwards. - Select. The same rules as for GitHub apply: skip
node_modules,dist,build,vendor,third_party,.git,__pycache__,.nextandcoverage; skip minified files, source maps and lockfiles; keep known extensions only; skip files of 1 MB or more; keep the first 1000 files or 100 MB, taking source files first, then tests, then Markdown, then files that yield no entities, each group by path. Anything left out makes the index partial. - Parse, store, index. The same pipeline as for GitHub. See Indexing pipeline.
The git sandbox
Section titled “The git sandbox”Git runs without a shell (create_subprocess_exec), with no stdin, and is killed on timeout. Every call carries:
-c credential.helper=, so your keychain is never consulted;-c protocol.allow=never -c protocol.https.allow=always, so only https works whatever your git config says;- a clean environment:
PATH,HOMEset to the null device,GIT_TERMINAL_PROMPT=0,GIT_CONFIG_GLOBALset to the null device,GIT_CONFIG_NOSYSTEM=1andLC_ALL=C. OnlySSL_CERT_FILE,SSL_CERT_DIR,GIT_SSL_CAINFO,HTTPS_PROXY,https_proxyandNO_PROXYare passed through from the backend’s own environment.
The ls-remote and clone calls also carry -c http.curloptResolve=<host>:443:<ip>, with the address the backend just checked (IPv4 when there is one). git never resolves the name itself, so a DNS answer that changes between the check and the request (DNS rebinding) can’t send it elsewhere.
Redirects are still followed, because gitlab.com answers /group/repo with a 301 to /group/repo.git. git follows that redirect without the address check, so a hostile public host could point it at an internal address. If that matters, route the backend’s traffic through an egress proxy that refuses private addresses.
Symlinks: a clone keeps symlinks as real links. The tree walk doesn’t follow or list them, so they are never indexed. (Older notes that mention core.symlinks=false are out of date.)
For a host with a private certificate authority, set SSL_CERT_FILE (or GIT_SSL_CAINFO) in the backend’s environment.
Source links in search results
Section titled “Source links in search results”Search hits carry a link to the exact lines. The format depends on the host and uses the indexed commit as <ref>:
| Host | Link |
|---|---|
| github.com | https://github.com/<owner>/<repo>/blob/<ref>/<file>#L<start>-L<end> |
gitlab.com, or any host whose name starts with gitlab. |
https://<host>/<path>/-/blob/<ref>/<file>#L<start>-<end> |
bitbucket.org |
https://bitbucket.org/<path>/src/<ref>/<file>#lines-<start>:<end> |
| Any other host | No link |
The link points at the commit that was indexed, so its line numbers stay right after the branch moves on. A repository indexed before commits were recorded links to its branch (or HEAD when none was set) until you reprocess it. On the repository page the source link reads View source for hosts other than github.com.
Refusal messages
Section titled “Refusal messages”For a non-GitHub URL, add_repository returns one of these, and nothing is created. (For github.com URLs, the tool uses GitHub’s check and the messages in the dashboard guide.)
| Message | Cause |
|---|---|
<host>/<path> was not found or is private; only public repositories are supported on <host>. |
The repository doesn’t exist, or it needs a login |
Can't index <host>/<path>: <host> resolves to a private or loopback address. |
The host name resolves to a non-public address (private, loopback, link-local and so on). Status 422 |
Can't index <host>/<path>: <host> could not be resolved. |
The host name doesn’t resolve. Status 422 |
Branch '<b>' does not exist in <host>/<path>. |
The branch is missing |
<host>/<path> is empty; there is nothing to index. |
The repository has no branches (checked only when no branch was given) |
<host> did not respond in time; try again. |
The 10 s check timed out |
Could not reach <host>; try again. |
The host failed in another way, or git couldn’t be started |
<host>/<path> is already added as repository <id>. |
The URL already exists (case-insensitive) |
Search indexing is not configured on the server (Algolia credentials are missing). |
ALGOLIA_APP_ID or ALGOLIA_ADMIN_API_KEY is unset |
url: ... |
The URL was rejected, see the table above |
The check makes one attempt, with no retries.
A repository that passes the check can still fail while it runs. The reason is stored in error_message and shows in list_repositories, the dashboard card and the repository page:
error_message |
Cause |
|---|---|
<host> did not respond in time while cloning <host>/<path> |
The clone passed 120 s |
Can't clone <host>/<path>: <host> resolves to a private or loopback address |
The host name now resolves to a non-public address, or (...could not be resolved) no longer resolves |
Could not run git to clone <host>/<path> |
git isn’t installed or couldn’t start |
Branch '<b>' not found in <host>/<path> |
The branch disappeared between the check and the clone |
<host>/<path> was not found or is private; only public repositories are supported on <host> |
The repository became private or was removed |
Could not clone <host>/<path> from <host> |
Any other clone failure |
No supported code files found in repository |
Nothing in the clone matched the file rules |
Fix the cause and reprocess from the repository page.
Limits
Section titled “Limits”- Public only on every host except github.com.
- Not in the dashboard. The web form and
POST /api/v1/repositories/refuse other hosts with422and a message that the host must be github.com. - Tool limits.
add_repositoryneeds the API key like every tool call and counts toward the 30 per minute limit. - Clone size. A shallow clone of a huge repository must finish in 120 seconds and fit the 1000-file and 100 MB selection.
See also MCP tools, Claude Desktop and Troubleshooting.