MCP tools reference
CodeSage serves seven tools and two resources to any MCP client. Claude Desktop reaches them through a stdio bridge that forwards each call to the backend over HTTP. This page lists every argument, every result field and every error message the tools produce. For the HTTP side of the same calls, see MCP HTTP endpoints. For setup, see Claude Desktop.
Argument schemas live in backend/codesage_mcp/schemas.py. The bridge answers tools/list from that file, so the list works even while the backend is down. Only tool calls and resource reads need the backend.
Overview
Section titled “Overview”| Tool | Reads from | Needs a completed repository |
Required arguments | What it returns |
|---|---|---|---|---|
list_repositories |
SQLite | No | none | Every repository with status and size |
search_code |
Algolia | Only if repository is given |
query |
Ranked hits with file locations, snippets and source links |
get_file |
SQLite | Yes | repository, path |
A file with line numbers, at most 400 lines per call |
list_definitions |
SQLite | Yes | repository |
Entities ordered by file and line |
analyze_repository |
SQLite | Yes | repository |
Counts, languages, top directories, largest files |
find_patterns |
SQLite | Yes | repository, pattern |
Lines matching a literal string or regular expression |
add_repository |
Git host, SQLite | No | url |
The new repository’s ID and status; indexing continues in the background |
Every tool call counts toward the rate limit of 30 per minute per client address. See Configuration.
Shared rules
Section titled “Shared rules”- Unknown arguments are rejected. Every argument model sets
additionalProperties: false. The bridge checks arguments first, and the backend checks them again. - Failures are tool errors. A failure the caller can act on (an unknown repository, a repository still indexing) comes back as a tool result with
is_error: trueand a one-line message. Transport and HTTP failures also come back as tool errors; see How failures reach Claude. - Results are JSON text. The bridge returns the
resultobject as pretty-printed JSON in one text block.
Resolving the repository argument
Section titled “Resolving the repository argument”Five tools take a repository argument. It accepts three forms, and the backend resolves it the same way for all of them.
| You pass | Matches |
|---|---|
12 |
The repository whose numeric ID is 12 |
octo/demo |
A repository whose name, or whose GitHub owner/name taken from its URL, equals the text, ignoring case |
gitlab.com/group/project |
A repository named <host>/<path>, which is how repositories on other hosts are named. The stored URL (https://gitlab.com/group/project) also matches |
The rules, in order:
- The text is trimmed.
- If it is made only of ASCII digits, it is looked up as an ID and never as a name. A repository whose name is a number can only be reached by its ID. An all-digit value longer than 19 digits is treated as not found, because no ID can be that long.
- Otherwise the text is lowercased and compared with each repository’s name and URL path. One match wins.
- Several matches are an error that lists the candidates. Use the numeric ID.
- No match is an error that lists the repository names that exist.
After resolution, every tool except list_repositories and add_repository checks that the repository is ready.
| Situation | Error message |
|---|---|
| No match | Repository 'nope' not found. Available repositories: gitlab.com/gitlab-org/gitlab-test, pmndrs/zustand |
| No match and nothing indexed | Repository 'nope' not found. Available repositories: none indexed yet |
| More than one match | Repository 'demo' is ambiguous: demo (id 3), demo (id 7). Use the numeric ID. |
Status failed |
Repository 'octo/demo' failed to index: <error_message>. Reprocess it from the dashboard. The : <error_message> part is left out when there is none |
Status pending or processing |
Repository 'octo/demo' is still indexing (120/400 files); try again when it completes. The (n/m files) part appears only when the total file count is known |
list_repositories
Section titled “list_repositories”Lists every repository CodeSage knows about, ordered by ID. It does not require a ready repository, so it also shows ones that are indexing or failed. Use it to see IDs, names and progress.
Arguments
Section titled “Arguments”None. The schema is {"type": "object", "properties": {}, "additionalProperties": false}.
Result
Section titled “Result”| Field | Type | Meaning |
|---|---|---|
repositories |
array | One object per repository |
repositories[].id |
integer | Numeric ID |
repositories[].name |
string | owner/name for GitHub, <host>/<path> for other hosts |
repositories[].url |
string or null | Normalised https:// URL |
repositories[].branch |
string or null | null means the default branch |
repositories[].commit_sha |
string or null | The commit the index was built from. Source links point at it. null until a run has listed the files, or for a repository indexed before commits were recorded |
repositories[].status |
string | pending, processing, completed or failed |
repositories[].total_files |
integer | Files selected for the last run |
repositories[].total_entities |
integer | Entities stored in SQLite |
repositories[].error_message |
string or null | Why the last run failed, or what a completed run left out |
repositories[].processed_at |
string or null | ISO 8601 time the last run finished, in UTC with a +00:00 offset |
The real result, from a backend holding one finished and one failed repository. Both were indexed before commits were recorded, so commit_sha is null:
{ "repositories": [ { "id": 1, "name": "gitlab.com/gitlab-org/gitlab-test", "url": "https://gitlab.com/gitlab-org/gitlab-test", "branch": null, "commit_sha": null, "status": "failed", "total_files": 0, "total_entities": 0, "error_message": "Processing was interrupted by a server restart; reprocess the repository", "processed_at": null }, { "id": 2, "name": "pmndrs/zustand", "url": "https://github.com/pmndrs/zustand", "branch": null, "commit_sha": null, "status": "completed", "total_files": 120, "total_entities": 1025, "error_message": null, "processed_at": "2026-10-26T23:32:50.170474+00:00" } ]}Limits and errors
Section titled “Limits and errors”The list is not paged or truncated. This tool has no tool-level errors beyond the transport failures in How failures reach Claude.
Example prompt
Section titled “Example prompt”Which repositories does CodeSage have indexed, and are they all finished?
search_code
Section titled “search_code”Full-text search over the Algolia index of functions, classes (including interfaces, type aliases and enums), Markdown sections and Terraform blocks. Imports are searched only when entity_type is import. Without repository, it searches every indexed repository.
Arguments
Section titled “Arguments”| Argument | Type | Required | Default | Constraints |
|---|---|---|---|---|
query |
string | Yes | none | minLength 1, and at most 512 bytes of UTF-8 |
repository |
string or null | No | all repositories | ID, name or owner/name; the repository must be completed |
language |
string or null | No | none | Exact language label such as python, typescript, cpp, csharp. See Languages |
entity_type |
string or null | No | none | One of function, class, import, doc_section, terraform_block |
limit |
integer | No | 10 | 1 to 50. Results per page |
page |
integer | No | 0 | 0 or more. Zero-based |
How the search runs
Section titled “How the search runs”- Each query asks Algolia to drop words that miss when nothing matches every word (
removeWordsIfNoResults: allOptional), ignore stop words and plurals, and use English (queryLanguages: ["en"]). A question in plain words returns the best partial matches instead of nothing. repository,languageandentity_typebecome filters joined withAND. Values are quoted and escaped, so input cannot add filter clauses.- Without
entity_type, the filterNOT entity_type:"import"is added, so import lines don’t crowd out the definitions. Passentity_type: "import"to search imports. - Part of a camelCase name matches:
WithStatefindsTestShallowWithState. - On a relevance tie, source files rank before test files, then
rank_weightdecides the order: classes, then functions and Terraform blocks, then doc sections, then imports. See Algolia record fields. - With no
repository, hits can come from repositories that are being reprocessed.
Result
Section titled “Result”| Field | Type | Meaning |
|---|---|---|
query |
string | The query as sent |
repository |
string or null | Name of the repository you filtered by, otherwise null |
total_hits |
integer | Total matches reported by Algolia (nbHits) |
page |
integer | The page returned |
pages |
integer | Total pages (nbPages) |
results |
array | Hits for this page |
results[].entity_name |
string | Bare name, for example parse_repository_url |
results[].entity_type |
string | One of the five entity types |
results[].file_path |
string | Path inside the repository |
results[].line_start, line_end |
integer | 1-based line range |
results[].language |
string | Language label of the file |
results[].signature |
string or null | The definition line, cut at 300 characters; null when empty |
results[].snippet |
string | Signature plus body, cut at 600 characters |
results[].description |
string or null | For example Python function: GitSource._read_tree, cut at 300 characters; null when empty |
results[].url |
string or null | Source link at the exact lines, or null when the host has no known link format |
Source link formats:
| Host | Link |
|---|---|
| github.com | https://github.com/<owner>/<repo>/blob/<ref>/<path>#L<start>-L<end> |
gitlab.com and hosts starting 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 | null |
<ref> is the commit the index was built from (commit_sha in list_repositories), so the line numbers stay right after the branch moves on. A repository indexed before commits were recorded uses its branch, or HEAD when it has none, until it is reprocessed. Path segments are percent-encoded.
Real output for query: "createStoreImpl", repository: "pmndrs/zustand" and limit: 2, from a repository indexed before commits were recorded (hence HEAD in the links). The snippet strings are trimmed here with …:
{ "query": "createStoreImpl", "repository": "pmndrs/zustand", "total_hits": 5, "page": 0, "pages": 3, "results": [ { "entity_name": "createStoreImpl", "entity_type": "function", "file_path": "src/vanilla.ts", "line_start": 60, "line_end": 97, "language": "typescript", "signature": "const createStoreImpl: CreateStoreImpl = (createState) => {", "snippet": "const createStoreImpl: CreateStoreImpl = (createState) => {\n type TState = ReturnType<typeof createState>\n type Listener = (state: TState, prevState: TState) => void\n let state: TState\n const listeners: Set<Listener> = new Set…", "description": "TypeScript function: createStoreImpl", "url": "https://github.com/pmndrs/zustand/blob/HEAD/src/vanilla.ts#L60-L97" }, { "entity_name": "setState", "entity_type": "function", "file_path": "src/vanilla.ts", "line_start": 66, "line_end": 81, "language": "typescript", "signature": "const setState: StoreApi<TState>['setState'] = (partial, replace) => {", "snippet": " const setState: StoreApi<TState>['setState'] = (partial, replace) => {\n // TODO: Remove type assertion once https://github.com/microsoft/TypeScript/issues/37663 is resolved\n // https://github.com/microsoft/TypeScript/issue…", "description": "TypeScript function: createStoreImpl.setState", "url": "https://github.com/pmndrs/zustand/blob/HEAD/src/vanilla.ts#L66-L81" } ]}After a reprocess, each url has the indexed commit’s SHA where this one has HEAD.
Limits and truncation
Section titled “Limits and truncation”queryis at most 512 bytes of UTF-8, because Algolia rejects longer queries.limitis capped at 50 per page. Usepagefor more.- Snippets are cut at 600 characters, and signatures and descriptions at 300.
- The index stores at most 2,000 characters of each entity’s body, so a snippet never shows more than that.
Errors
Section titled “Errors”| Message | Cause |
|---|---|
| Any message from Resolving the repository argument | The repository is unknown, ambiguous, failed or still indexing |
Invalid arguments for 'search_code': ... ending in query is too long; the limit is 512 bytes |
query is over 512 bytes of UTF-8 |
Search is not configured on the server (ALGOLIA_APP_ID / ALGOLIA_ADMIN_API_KEY are missing). |
The backend has no Algolia credentials |
Search is unavailable; try again later. |
Algolia rejected the query or could not be reached. The detail is logged on the backend only, because it can name the Algolia host or app ID |
Example prompt
Section titled “Example prompt”Search the octo/demo repository for where the Algolia index settings are applied.
get_file
Section titled “get_file”Reads one stored file with line numbers. The text comes from SQLite, so it matches what was indexed, not the live branch.
Arguments
Section titled “Arguments”| Argument | Type | Required | Default | Constraints |
|---|---|---|---|---|
repository |
string | Yes | none | ID, name or owner/name; must be completed |
path |
string | Yes | none | Exact path, case-sensitive, relative to the repository root |
start_line |
integer or null | No | 1 | 1 or more |
end_line |
integer or null | No | end of file | 1 or more |
Result
Section titled “Result”| Field | Type | Meaning |
|---|---|---|
repository |
string | Repository name |
path |
string | The stored path |
language |
string | Language label |
total_lines |
integer | Lines in the whole file |
start_line |
integer | First line returned |
end_line |
integer | Last line returned |
truncated |
boolean | true if the 400-line or 100,000-character cap cut the output short |
content |
string | Lines formatted as <number>| <text>, with the number right-aligned to at least 4 characters |
Real output for repository: "pmndrs/zustand", path: "src/vanilla.ts", start_line: 1 and end_line: 6:
{ "repository": "pmndrs/zustand", "path": "src/vanilla.ts", "language": "typescript", "total_lines": 100, "start_line": 1, "end_line": 6, "truncated": false, "content": " 1| type SetStateInternal<T> = {\n 2| _(\n 3| partial: T | Partial<T> | { _(state: T): T | Partial<T> }['_'],\n 4| replace?: false,\n 5| ): void\n 6| _(state: T | { _(state: T): T }['_'], replace: true): void"}Limits and truncation
Section titled “Limits and truncation”- At most 400 lines per call (
MAX_FILE_LINES). The returnedend_lineis the smaller of yourend_line, the last line andstart_line + 399. - At most 100,000 characters of formatted
content. - When
truncatedistrue, call again withstart_lineset to the returnedend_line + 1.
Errors
Section titled “Errors”| Message | Cause |
|---|---|
File 'src/mian.py' not found in octo/demo. Close paths: src/main.py, src/main_test.py |
No stored file has that exact path. Up to 10 similar paths (similarity cutoff 0.4) are suggested. The Close paths: part is left out when none are close |
start_line 500 is past the end of the file (104 lines) |
start_line is greater than the line count |
end_line must not be before start_line |
end_line is smaller than start_line |
| Repository errors | See Resolving the repository argument |
Example prompt
Section titled “Example prompt”Show me lines 1 to 120 of backend/app/main.py in octo/demo.
list_definitions
Section titled “list_definitions”Lists stored entities for one repository, ordered by file path, then start line, then ID. It reads SQLite, so it is exact and unranked.
Arguments
Section titled “Arguments”| Argument | Type | Required | Default | Constraints |
|---|---|---|---|---|
repository |
string | Yes | none | ID, name or owner/name; must be completed |
path_prefix |
string or null | No | none | Only files whose path starts with this text. Literal match, no wildcards |
entity_type |
string or null | No | none | function, class, import, doc_section or terraform_block |
name |
string or null | No | none | Case-insensitive substring of the entity name. Literal match |
limit |
integer | No | 100 | 1 to 500 |
Result
Section titled “Result”| Field | Type | Meaning |
|---|---|---|
repository |
string | Repository name |
total |
integer | All entities that match the filters, not only those returned |
definitions |
array | Up to limit entities |
definitions[].entity_type |
string | One of the five entity types |
definitions[].entity_name |
string | Bare name; methods are not prefixed with their class |
definitions[].file_path |
string | Path inside the repository |
definitions[].line_start, line_end |
integer | 1-based range |
definitions[].signature |
string or null | Cut at 300 characters |
definitions[].description |
string or null | The stored description, cut at 300 characters |
Real output for repository: "pmndrs/zustand", path_prefix: "src/vanilla" and limit: 3. total is 14, so the response holds the first three of 14 matches:
{ "repository": "pmndrs/zustand", "total": 14, "definitions": [ { "entity_type": "class", "entity_name": "StoreApi", "file_path": "src/vanilla.ts", "line_start": 9, "line_end": 14, "signature": "export interface StoreApi<T> {", "description": "TypeScript class: StoreApi" }, { "entity_type": "class", "entity_name": "StoreMutators", "file_path": "src/vanilla.ts", "line_start": 40, "line_end": 40, "signature": "export interface StoreMutators<S, A> {}", "description": "TypeScript class: StoreMutators" }, { "entity_type": "function", "entity_name": "createStoreImpl", "file_path": "src/vanilla.ts", "line_start": 60, "line_end": 97, "signature": "const createStoreImpl: CreateStoreImpl = (createState) => {", "description": "TypeScript function: createStoreImpl" } ]}This output comes from an index built before TypeScript type aliases and enums were extracted. After a reprocess, StoreApi and StoreMutators are described as TypeScript interface: ..., type aliases (TypeScript type: ...) and enums (TypeScript enum: ...) are listed as class entities too, and total grows accordingly.
Limits and errors
Section titled “Limits and errors”limit caps the page at 500 and there is no paging, so narrow the filters when total is larger than the number returned. Errors are the repository errors from Resolving the repository argument.
Example prompt
Section titled “Example prompt”List every class in octo/demo under backend/app/services.
analyze_repository
Section titled “analyze_repository”Returns measured counts and layout, with no interpretation. Use it first to learn what a repository contains.
Arguments
Section titled “Arguments”| Argument | Type | Required | Constraints |
|---|---|---|---|
repository |
string | Yes | ID, name or owner/name; must be completed |
Result
Section titled “Result”| Field | Type | Meaning |
|---|---|---|
repository |
string | Repository name |
repository_id |
integer | Numeric ID |
status |
string | Always completed here |
total_files |
integer | Files stored |
processed_files |
integer | Files processed in the last run |
total_lines |
integer | Lines across stored files |
total_entities |
integer | Entities stored |
entities_by_type |
object | Entity count per type, for example {"function": 910} |
files_by_language |
object | File count per language label |
language |
string or null | Primary programming language chosen by the last run |
framework |
string or null | Framework guessed from package.json or Python imports |
top_directories |
array | Up to 10 {directory, files} pairs by file count. The directory is the first path segment, or (root) for files at the top |
largest_files |
array | The 10 files with the most lines, as {file_path, line_count} |
definitions_by_language |
object | Entity count per file language. A file with an unknown language is grouped under unknown |
Real output for repository: "pmndrs/zustand". The top_directories and largest_files lists are trimmed to three entries (the tool returns up to 10):
{ "repository": "pmndrs/zustand", "repository_id": 2, "status": "completed", "total_files": 120, "processed_files": 120, "total_lines": 25117, "total_entities": 1025, "entities_by_type": { "class": 22, "doc_section": 398, "function": 474, "import": 131 }, "files_by_language": { "css": 3, "html": 2, "javascript": 14, "json": 7, "markdown": 47, "typescript": 34, "yaml": 13 }, "language": "typescript", "framework": "react", "top_directories": [ { "directory": "docs", "files": 42 }, { "directory": "examples", "files": 27 }, { "directory": "src", "files": 16 } ], "largest_files": [ { "file_path": "tests/devtools.test.tsx", "line_count": 2926 }, { "file_path": "docs/learn/guides/tutorial-tic-tac-toe.md", "line_count": 1429 }, { "file_path": "tests/persistAsync.test.tsx", "line_count": 1244 } ], "definitions_by_language": { "javascript": 64, "markdown": 398, "typescript": 563 }}Limits and errors
Section titled “Limits and errors”The lists are capped at 10 entries. Errors are the repository errors from Resolving the repository argument.
Example prompt
Section titled “Example prompt”Analyze octo/demo: how big is it, which languages does it use and where is the code?
find_patterns
Section titled “find_patterns”Finds lines in the stored files that match a literal string or a regular expression. Use it when search ranking is not what you want, for example to find every call to a function. Files are scanned in path order.
Arguments
Section titled “Arguments”| Argument | Type | Required | Default | Constraints |
|---|---|---|---|---|
repository |
string | Yes | none | ID, name or owner/name; must be completed |
pattern |
string | Yes | none | 1 to 200 characters |
regex |
boolean | No | false |
true treats pattern as a regular expression |
path_glob |
string or null | No | none | Matched against the whole path, case-sensitive |
max_results |
integer | No | 100 | 1 to 500 |
Matching rules:
- A literal pattern is a case-sensitive substring test on each line.
- A regular expression is compiled with the Python
regexpackage and searched in each line. It is not anchored, so use^and$if you need that. Add(?i)for a case-insensitive match. path_globusesfnmatchsemantics on the full path, and*also matches/.src/*.pytherefore matchessrc/a/b.py.
Result
Section titled “Result”| Field | Type | Meaning |
|---|---|---|
repository |
string | Repository name |
pattern |
string | The pattern as sent |
regex |
boolean | Whether it was treated as a regular expression |
total_matches |
integer | Number of matches returned (never more than max_results) |
truncated |
boolean | true if more matches exist, or if a regex time budget ran out |
matches |
array | {file_path, line, text} objects. text is the line with surrounding whitespace removed, cut at 300 characters |
skipped_files |
array | Present only when files were skipped. The first 50 paths |
skipped_count |
integer | Present only when files were skipped. The full count |
Real output for repository: "pmndrs/zustand", pattern: "createStoreImpl" and max_results: 3:
{ "repository": "pmndrs/zustand", "pattern": "createStoreImpl", "regex": false, "total_matches": 2, "truncated": false, "matches": [ { "file_path": "src/vanilla.ts", "line": 60, "text": "const createStoreImpl: CreateStoreImpl = (createState) => {" }, { "file_path": "src/vanilla.ts", "line": 100, "text": "createState ? createStoreImpl(createState) : createStoreImpl) as CreateStore" } ]}Limits and truncation
Section titled “Limits and truncation”| Limit | Value | Effect |
|---|---|---|
| Matches | max_results (1 to 500) |
The scan stops at the first extra match and sets truncated: true |
| Regex time per file | 1 second | The file is added to skipped_files; the scan goes on |
| Regex time per call | 10 seconds | The remaining files are added to skipped_files and truncated is true |
| Regex file size | Files over 1,000,000 characters | Skipped and listed in skipped_files |
| Literal search | No time limit | Not subject to the regex limits |
Errors
Section titled “Errors”| Message | Cause |
|---|---|
Invalid regular expression: <detail> |
regex is true and pattern does not compile |
| Repository errors | See Resolving the repository argument |
Example prompt
Section titled “Example prompt”In octo/demo, find every place that matches
TODO|FIXMEin Python files under backend/, as a regular expression.
add_repository
Section titled “add_repository”Adds a repository by https URL and starts indexing it in the background. It works with GitHub, GitLab (nested groups too), Bitbucket and self-hosted git servers. It runs the same pre-flight checks as the REST endpoint, and nothing is created if a check fails.
Arguments
Section titled “Arguments”| Argument | Type | Required | Default | Constraints |
|---|---|---|---|---|
url |
string | Yes | none | minLength 1. An https repository URL. http:// and git@host:path forms are rewritten to https://. A branch URL such as .../tree/dev, .../-/tree/dev or Bitbucket’s .../src/dev is understood |
branch |
string or null | No | default branch | At most 100 characters. Must not contain a .. path segment or a NUL, and must not start with -. An explicit value wins over a branch in the URL |
description |
string or null | No | none | Free text note |
The URL is refused if it has a port, credentials, a query or fragment, the host localhost (or *.localhost), or a private or loopback IP address. A path that is not owner/repo (plus a recognised branch suffix) is refused as well. On hosts other than github.com, the host name is also resolved before git runs: if it doesn’t resolve, or any address it resolves to is not public, the repository is refused, and git then connects only to the address that was checked.
Private repositories work only on github.com, and only when the server’s GITHUB_TOKEN can read them. On every other host the repository must be public.
Result
Section titled “Result”| Field | Type | Meaning |
|---|---|---|
repository_id |
integer | ID to pass to the other tools |
name |
string | owner/name for GitHub, <host>/<path> otherwise |
status |
string | pending at creation |
message |
string | Always Indexing started; check list_repositories for progress. |
The values below are illustrative. This call writes, so it was not run against a live backend; the shape follows the response schema in the code.
{ "repository_id": 5, "name": "gitlab.com/group/project", "status": "pending", "message": "Indexing started; check list_repositories for progress."}Indexing runs after the tool returns. Poll list_repositories until the status is completed, then search it.
Limits
Section titled “Limits”The pre-flight check takes up to 10 seconds. A later clone on a non-GitHub host takes up to 120 seconds. Indexing keeps at most 1,000 files and 100 MB, and skips files of 1 MB or more. See Indexing pipeline.
Errors
Section titled “Errors”| Message | Cause |
|---|---|
url: <reason> |
The URL failed validation, for example Invalid repository URL (only https is supported): ... |
branch: Invalid branch name |
The branch contains .., a NUL, or starts with - |
octo/demo is already added as repository 1. |
The URL (case-insensitive) already exists |
Search indexing is not configured on the server (Algolia credentials are missing). |
ALGOLIA_APP_ID or ALGOLIA_ADMIN_API_KEY is unset |
<host>/<path> was not found or is private; only public repositories are supported on <host>. |
Non-GitHub host: the repository is missing or needs credentials |
Can't index <host>/<path>: <host> resolves to a private or loopback address. or ...: <host> could not be resolved. |
Non-GitHub host: the name points at a non-public address, or doesn’t resolve |
GitHub repository octo/demo was not found. Check the URL; if it's private, the server's GITHUB_TOKEN needs access. |
github.com: missing, or private without token access |
GitHub denied access to octo/demo. |
github.com answered 403 |
Branch 'x' does not exist in octo/demo. |
The branch is missing |
octo/demo is empty; there is nothing to index. |
No commits |
GitHub's API rate limit was reached; try again in about 2 minutes. |
GitHub is rate limiting the server’s token |
GitHub rejected the server's GITHUB_TOKEN; check the token configured on the CodeSage server. |
The token is invalid |
Could not reach GitHub; try again. or Could not reach <host>; try again. |
The host or git failed in another way |
GitHub did not respond in time; try again. or <host> did not respond in time; try again. |
The 10-second check timed out |
Example prompt
Section titled “Example prompt”Add https://gitlab.com/group/project to CodeSage, on the dev branch, then tell me when it is ready.
Resources
Section titled “Resources”The bridge also answers resources/list and resources/read. Reading needs the API key, because no public endpoint exposes the file list.
| URI | MIME type | Listed by resources/list |
Content |
|---|---|---|---|
codesage://repositories |
application/json |
Yes (name Repositories) |
The same object as list_repositories |
codesage://repositories/{id}/files |
application/json |
No | The files of one repository |
{id} is the numeric ID, 1 to 19 digits. Names are not accepted here. The files resource is readable even when the repository is not completed.
Real output for codesage://repositories/2/files, with files trimmed to the first two entries:
{ "repository": "pmndrs/zustand", "total_files": 120, "truncated": false, "files": [ { "file_path": ".github/DISCUSSION_TEMPLATE/bug-report.yml", "language": "yaml", "line_count": 17 }, { "file_path": ".github/FUNDING.yml", "language": "yaml", "line_count": 16 } ]}| Field | Meaning |
|---|---|
total_files |
Every stored file for the repository |
truncated |
true when total_files is above the 5,000-file cap, so only the first 5,000 by path are listed |
files[] |
file_path, language and line_count, ordered by path |
Resource errors
Section titled “Resource errors”Resource reads fail as MCP protocol errors, not tool errors. The backend answers 404 and the bridge words it as Resource or repository not found: <detail>.
| Detail | Cause |
|---|---|
Unknown resource '<uri>' |
The URI is neither of the two forms above, or the ID has more than 19 digits |
Repository 99 not found |
No repository has that ID. IDs at or above 2^63 are treated the same way |
Related pages
Section titled “Related pages”- MCP HTTP endpoints for the request and response envelope.
- Search for how ranking and filters work.
- Troubleshooting for symptoms and fixes.