Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `/api/chat/blocking` endpoint that creates a blocking Ask thread, used by the MCP server. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `/api/models` endpoint to list configured language models. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)

### Fixed
- Fixed issue where files with a comma would not render correctly in file tree. [#831](https://github.com/sourcebot-dev/sourcebot/pull/831)

### Changed
- Changed `/api/source` api to support fetching source code for any revision, not just revisions that are indexed by zoekt. [#829](https://github.com/sourcebot-dev/sourcebot/pull/829)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)
- Adjusted prompts and tools for Ask agent. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [4.10.20] - 2026-01-28

Expand Down
84 changes: 68 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -147,32 +147,84 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)

### `search_code`

Fetches code that matches the provided regex pattern in `query`.
Searches for code that matches the provided search query as a substring by default, or as a regular expression if `useRegex` is true.

Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
| Name | Required | Description |
|:----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The search pattern to match against code contents. Do not escape quotes in your query. |
| `useRegex` | no | Whether to use regular expression matching. When false, substring matching is used (default: false). |
| `filterByRepos` | no | Scope the search to specific repositories. |
| `filterByLanguages` | no | Scope the search to specific languages. |
| `filterByFilepaths` | no | Scope the search to specific filepaths. |
| `caseSensitive` | no | Whether the search should be case sensitive (default: false). |
| `includeCodeSnippets` | no | Whether to include code snippets in the response (default: false). |
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
| `maxTokens` | no | The maximum number of tokens to return (default: 10000). |


### `list_repos`

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

### `get_file_source`
Parameters:
| Name | Required | Description |
|:------------|:---------|:--------------------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 30). |
| `sort` | no | Sort repositories by 'name' or 'pushed' (most recent commit). Default: 'name'. |
| `direction` | no | Sort direction: 'asc' or 'desc' (default: 'asc'). |


### `read_file`

Reads the source code for a given file.

Parameters:
| Name | Required | Description |
|:-------|:---------|:-------------------------------------------------------------------------------------------------------|
| `repo` | yes | The repository name. |
| `path` | yes | The path to the file. |
| `ref` | no | Commit SHA, branch or tag name to fetch the source code for. If not provided, uses the default branch. |


### `list_commits`

Get a list of commits for a given repository.

Parameters:
| Name | Required | Description |
|:----------|:---------|:-----------------------------------------------------------------------------------------------------------------------|
| `repo` | yes | The name of the repository to list commits for. |
| `query` | no | Search query to filter commits by message content (case-insensitive). |
| `since` | no | Show commits more recent than this date. Supports ISO 8601 (e.g., '2024-01-01') or relative formats (e.g., '30 days ago'). |
| `until` | no | Show commits older than this date. Supports ISO 8601 (e.g., '2024-12-31') or relative formats (e.g., 'yesterday'). |
| `author` | no | Filter commits by author name or email (case-insensitive). |
| `ref` | no | Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch. |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 50). |


### `list_language_models`

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

Parameters:

This tool takes no parameters.


### `ask_codebase`

Fetches the source code for a given file.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |
Comment thread
brendan-kellam marked this conversation as resolved.


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `ask_codebase` tool that can invoke the Ask subagent to explore a set of codebases and return a summarized answer. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `list_language_models` tool to discover available language models configured on the Sourcebot instance. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [1.0.14] - 2026-01-27

### Changed
Expand Down
26 changes: 26 additions & 0 deletions packages/mcp/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -234,6 +234,32 @@ Get a list of commits for a given repository.

</details>

### list_language_models

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

<details>
<summary>Parameters</summary>

This tool takes no parameters.

</details>

### ask_codebase

Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |

Comment thread
brendan-kellam marked this conversation as resolved.
</details>


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
45 changes: 42 additions & 3 deletions packages/mcp/src/client.ts
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { env } from './env.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema } from './schemas.js';
import { FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema, askCodebaseResponseSchema, listLanguageModelsResponseSchema } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema, ListLanguageModelsResponse } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand DownExpand Up@@ -106,4 +106,43 @@ export const listCommits = async (queryParams: ListCommitsQueryParamsSchema) =>
const commits = await parseResponse(response, listCommitsResponseSchema);
const totalCount = parseInt(response.headers.get('X-Total-Count') ?? '0', 10);
return { commits, totalCount };
}
}

/**
* Asks a natural language question about the codebase using the Sourcebot AI agent.
* This is a blocking call that runs the full agent loop and returns when complete.
*
* @param request - The question and optional repo filters
* @returns The agent's answer, chat URL, sources, and metadata
*/
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export const askCodebase = async (request: AskCodebaseRequest): Promise<AskCodebaseResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/chat/blocking`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
body: JSON.stringify(request),
});

return parseResponse(response, askCodebaseResponseSchema);
}

/**
* Lists the available language models configured on the Sourcebot instance.
*
* @returns Array of language model info objects
*/
export const listLanguageModels = async (): Promise<ListLanguageModelsResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
});

return parseResponse(response, listLanguageModelsResponseSchema);
}
56 changes: 53 additions & 3 deletions packages/mcp/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,10 +6,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import _dedent from "dedent";
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { getFileSource, listCommits, listRepos, search } from './client.js';
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, search } from './client.js';
import { env, numberSchema } from './env.js';
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';

const dedent = _dedent.withOptions({ alignValues: true });

Expand DownExpand Up@@ -238,7 +238,57 @@ server.tool(
}
);

server.tool(
"list_language_models",
dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
{},
async () => {
const models = await listLanguageModels();

return {
content: [{
type: "text",
text: JSON.stringify(models),
}],
};
}
);

server.tool(
"ask_codebase",
dedent`
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.

The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references

Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.

This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
`,
askCodebaseRequestSchema.shape,
async (request: AskCodebaseRequest) => {
const response = await askCodebase(request);

// Format the response with the answer and a link to the chat
const formattedResponse = dedent`
${response.answer}

---
**View full research session:** ${response.chatUrl}
**Model used:** ${response.languageModel.model}
`;

return {
content: [{
type: "text",
text: formattedResponse,
}],
};
}
);

const runServer = async () => {
const transport = new StdioServerTransport();
Expand Down
42 changes: 42 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,3 +272,45 @@ export const listCommitsResponseSchema = z.array(z.object({
author_name: z.string(),
author_email: z.string(),
}));

export const languageModelInfoSchema = z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
});

export const listLanguageModelsResponseSchema = z.array(languageModelInfoSchema);

export const askCodebaseRequestSchema = z.object({
query: z
.string()
.describe("The query to ask about the codebase."),
repos: z
.array(z.string())
.optional()
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema
.omit({ displayName: true })
.optional()
.describe("The language model to use for answering the question. If not provided, defaults to the first model in the config. Use list_language_models to see available options."),
});

export const sourceSchema = z.object({
type: z.literal('file'),
repo: z.string(),
path: z.string(),
name: z.string(),
language: z.string(),
revision: z.string(),
});

export const askCodebaseResponseSchema = z.object({
answer: z.string().describe("The agent's final answer in markdown format"),
chatId: z.string().describe("ID of the persisted chat session"),
chatUrl: z.string().describe("URL to view the chat in the web UI"),
languageModel: z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
}).describe("The language model used to generate the response"),
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `/api/chat/blocking` endpoint that creates a blocking Ask thread, used by the MCP server. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `/api/models` endpoint to list configured language models. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)

### Fixed
- Fixed issue where files with a comma would not render correctly in file tree. [#831](https://github.com/sourcebot-dev/sourcebot/pull/831)

### Changed
- Changed `/api/source` api to support fetching source code for any revision, not just revisions that are indexed by zoekt. [#829](https://github.com/sourcebot-dev/sourcebot/pull/829)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)
- Adjusted prompts and tools for Ask agent. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [4.10.20] - 2026-01-28

Expand Down
84 changes: 68 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -147,32 +147,84 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)

### `search_code`

Fetches code that matches the provided regex pattern in `query`.
Searches for code that matches the provided search query as a substring by default, or as a regular expression if `useRegex` is true.

Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
| Name | Required | Description |
|:----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The search pattern to match against code contents. Do not escape quotes in your query. |
| `useRegex` | no | Whether to use regular expression matching. When false, substring matching is used (default: false). |
| `filterByRepos` | no | Scope the search to specific repositories. |
| `filterByLanguages` | no | Scope the search to specific languages. |
| `filterByFilepaths` | no | Scope the search to specific filepaths. |
| `caseSensitive` | no | Whether the search should be case sensitive (default: false). |
| `includeCodeSnippets` | no | Whether to include code snippets in the response (default: false). |
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
| `maxTokens` | no | The maximum number of tokens to return (default: 10000). |


### `list_repos`

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

### `get_file_source`
Parameters:
| Name | Required | Description |
|:------------|:---------|:--------------------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 30). |
| `sort` | no | Sort repositories by 'name' or 'pushed' (most recent commit). Default: 'name'. |
| `direction` | no | Sort direction: 'asc' or 'desc' (default: 'asc'). |


### `read_file`

Reads the source code for a given file.

Parameters:
| Name | Required | Description |
|:-------|:---------|:-------------------------------------------------------------------------------------------------------|
| `repo` | yes | The repository name. |
| `path` | yes | The path to the file. |
| `ref` | no | Commit SHA, branch or tag name to fetch the source code for. If not provided, uses the default branch. |


### `list_commits`

Get a list of commits for a given repository.

Parameters:
| Name | Required | Description |
|:----------|:---------|:-----------------------------------------------------------------------------------------------------------------------|
| `repo` | yes | The name of the repository to list commits for. |
| `query` | no | Search query to filter commits by message content (case-insensitive). |
| `since` | no | Show commits more recent than this date. Supports ISO 8601 (e.g., '2024-01-01') or relative formats (e.g., '30 days ago'). |
| `until` | no | Show commits older than this date. Supports ISO 8601 (e.g., '2024-12-31') or relative formats (e.g., 'yesterday'). |
| `author` | no | Filter commits by author name or email (case-insensitive). |
| `ref` | no | Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch. |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 50). |


### `list_language_models`

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

Parameters:

This tool takes no parameters.


### `ask_codebase`

Fetches the source code for a given file.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |
Comment thread
brendan-kellam marked this conversation as resolved.


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `ask_codebase` tool that can invoke the Ask subagent to explore a set of codebases and return a summarized answer. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `list_language_models` tool to discover available language models configured on the Sourcebot instance. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [1.0.14] - 2026-01-27

### Changed
Expand Down
26 changes: 26 additions & 0 deletions packages/mcp/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -234,6 +234,32 @@ Get a list of commits for a given repository.

</details>

### list_language_models

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

<details>
<summary>Parameters</summary>

This tool takes no parameters.

</details>

### ask_codebase

Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |

Comment thread
brendan-kellam marked this conversation as resolved.
</details>


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
45 changes: 42 additions & 3 deletions packages/mcp/src/client.ts
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { env } from './env.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema } from './schemas.js';
import { FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema, askCodebaseResponseSchema, listLanguageModelsResponseSchema } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema, ListLanguageModelsResponse } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand DownExpand Up@@ -106,4 +106,43 @@ export const listCommits = async (queryParams: ListCommitsQueryParamsSchema) =>
const commits = await parseResponse(response, listCommitsResponseSchema);
const totalCount = parseInt(response.headers.get('X-Total-Count') ?? '0', 10);
return { commits, totalCount };
}
}

/**
* Asks a natural language question about the codebase using the Sourcebot AI agent.
* This is a blocking call that runs the full agent loop and returns when complete.
*
* @param request - The question and optional repo filters
* @returns The agent's answer, chat URL, sources, and metadata
*/
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export const askCodebase = async (request: AskCodebaseRequest): Promise<AskCodebaseResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/chat/blocking`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
body: JSON.stringify(request),
});

return parseResponse(response, askCodebaseResponseSchema);
}

/**
* Lists the available language models configured on the Sourcebot instance.
*
* @returns Array of language model info objects
*/
export const listLanguageModels = async (): Promise<ListLanguageModelsResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
});

return parseResponse(response, listLanguageModelsResponseSchema);
}
56 changes: 53 additions & 3 deletions packages/mcp/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,10 +6,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import _dedent from "dedent";
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { getFileSource, listCommits, listRepos, search } from './client.js';
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, search } from './client.js';
import { env, numberSchema } from './env.js';
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';

const dedent = _dedent.withOptions({ alignValues: true });

Expand DownExpand Up@@ -238,7 +238,57 @@ server.tool(
}
);

server.tool(
"list_language_models",
dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
{},
async () => {
const models = await listLanguageModels();

return {
content: [{
type: "text",
text: JSON.stringify(models),
}],
};
}
);

server.tool(
"ask_codebase",
dedent`
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.

The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references

Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.

This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
`,
askCodebaseRequestSchema.shape,
async (request: AskCodebaseRequest) => {
const response = await askCodebase(request);

// Format the response with the answer and a link to the chat
const formattedResponse = dedent`
${response.answer}

---
**View full research session:** ${response.chatUrl}
**Model used:** ${response.languageModel.model}
`;

return {
content: [{
type: "text",
text: formattedResponse,
}],
};
}
);

const runServer = async () => {
const transport = new StdioServerTransport();
Expand Down
42 changes: 42 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,3 +272,45 @@ export const listCommitsResponseSchema = z.array(z.object({
author_name: z.string(),
author_email: z.string(),
}));

export const languageModelInfoSchema = z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
});

export const listLanguageModelsResponseSchema = z.array(languageModelInfoSchema);

export const askCodebaseRequestSchema = z.object({
query: z
.string()
.describe("The query to ask about the codebase."),
repos: z
.array(z.string())
.optional()
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema
.omit({ displayName: true })
.optional()
.describe("The language model to use for answering the question. If not provided, defaults to the first model in the config. Use list_language_models to see available options."),
});

export const sourceSchema = z.object({
type: z.literal('file'),
repo: z.string(),
path: z.string(),
name: z.string(),
language: z.string(),
revision: z.string(),
});

export const askCodebaseResponseSchema = z.object({
answer: z.string().describe("The agent's final answer in markdown format"),
chatId: z.string().describe("ID of the persisted chat session"),
chatUrl: z.string().describe("URL to view the chat in the web UI"),
languageModel: z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
}).describe("The language model used to generate the response"),
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `/api/chat/blocking` endpoint that creates a blocking Ask thread, used by the MCP server. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `/api/models` endpoint to list configured language models. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)

### Fixed
- Fixed issue where files with a comma would not render correctly in file tree. [#831](https://github.com/sourcebot-dev/sourcebot/pull/831)

### Changed
- Changed `/api/source` api to support fetching source code for any revision, not just revisions that are indexed by zoekt. [#829](https://github.com/sourcebot-dev/sourcebot/pull/829)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)
- Adjusted prompts and tools for Ask agent. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [4.10.20] - 2026-01-28

Expand Down
84 changes: 68 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -147,32 +147,84 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)

### `search_code`

Fetches code that matches the provided regex pattern in `query`.
Searches for code that matches the provided search query as a substring by default, or as a regular expression if `useRegex` is true.

Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
| Name | Required | Description |
|:----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The search pattern to match against code contents. Do not escape quotes in your query. |
| `useRegex` | no | Whether to use regular expression matching. When false, substring matching is used (default: false). |
| `filterByRepos` | no | Scope the search to specific repositories. |
| `filterByLanguages` | no | Scope the search to specific languages. |
| `filterByFilepaths` | no | Scope the search to specific filepaths. |
| `caseSensitive` | no | Whether the search should be case sensitive (default: false). |
| `includeCodeSnippets` | no | Whether to include code snippets in the response (default: false). |
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
| `maxTokens` | no | The maximum number of tokens to return (default: 10000). |


### `list_repos`

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

### `get_file_source`
Parameters:
| Name | Required | Description |
|:------------|:---------|:--------------------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 30). |
| `sort` | no | Sort repositories by 'name' or 'pushed' (most recent commit). Default: 'name'. |
| `direction` | no | Sort direction: 'asc' or 'desc' (default: 'asc'). |


### `read_file`

Reads the source code for a given file.

Parameters:
| Name | Required | Description |
|:-------|:---------|:-------------------------------------------------------------------------------------------------------|
| `repo` | yes | The repository name. |
| `path` | yes | The path to the file. |
| `ref` | no | Commit SHA, branch or tag name to fetch the source code for. If not provided, uses the default branch. |


### `list_commits`

Get a list of commits for a given repository.

Parameters:
| Name | Required | Description |
|:----------|:---------|:-----------------------------------------------------------------------------------------------------------------------|
| `repo` | yes | The name of the repository to list commits for. |
| `query` | no | Search query to filter commits by message content (case-insensitive). |
| `since` | no | Show commits more recent than this date. Supports ISO 8601 (e.g., '2024-01-01') or relative formats (e.g., '30 days ago'). |
| `until` | no | Show commits older than this date. Supports ISO 8601 (e.g., '2024-12-31') or relative formats (e.g., 'yesterday'). |
| `author` | no | Filter commits by author name or email (case-insensitive). |
| `ref` | no | Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch. |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 50). |


### `list_language_models`

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

Parameters:

This tool takes no parameters.


### `ask_codebase`

Fetches the source code for a given file.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |
Comment thread
brendan-kellam marked this conversation as resolved.


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `ask_codebase` tool that can invoke the Ask subagent to explore a set of codebases and return a summarized answer. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `list_language_models` tool to discover available language models configured on the Sourcebot instance. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [1.0.14] - 2026-01-27

### Changed
Expand Down
26 changes: 26 additions & 0 deletions packages/mcp/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -234,6 +234,32 @@ Get a list of commits for a given repository.

</details>

### list_language_models

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

<details>
<summary>Parameters</summary>

This tool takes no parameters.

</details>

### ask_codebase

Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |

Comment thread
brendan-kellam marked this conversation as resolved.
</details>


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
45 changes: 42 additions & 3 deletions packages/mcp/src/client.ts
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { env } from './env.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema } from './schemas.js';
import { FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema, askCodebaseResponseSchema, listLanguageModelsResponseSchema } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema, ListLanguageModelsResponse } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand DownExpand Up@@ -106,4 +106,43 @@ export const listCommits = async (queryParams: ListCommitsQueryParamsSchema) =>
const commits = await parseResponse(response, listCommitsResponseSchema);
const totalCount = parseInt(response.headers.get('X-Total-Count') ?? '0', 10);
return { commits, totalCount };
}
}

/**
* Asks a natural language question about the codebase using the Sourcebot AI agent.
* This is a blocking call that runs the full agent loop and returns when complete.
*
* @param request - The question and optional repo filters
* @returns The agent's answer, chat URL, sources, and metadata
*/
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export const askCodebase = async (request: AskCodebaseRequest): Promise<AskCodebaseResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/chat/blocking`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
body: JSON.stringify(request),
});

return parseResponse(response, askCodebaseResponseSchema);
}

/**
* Lists the available language models configured on the Sourcebot instance.
*
* @returns Array of language model info objects
*/
export const listLanguageModels = async (): Promise<ListLanguageModelsResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
});

return parseResponse(response, listLanguageModelsResponseSchema);
}
56 changes: 53 additions & 3 deletions packages/mcp/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,10 +6,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import _dedent from "dedent";
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { getFileSource, listCommits, listRepos, search } from './client.js';
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, search } from './client.js';
import { env, numberSchema } from './env.js';
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';

const dedent = _dedent.withOptions({ alignValues: true });

Expand DownExpand Up@@ -238,7 +238,57 @@ server.tool(
}
);

server.tool(
"list_language_models",
dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
{},
async () => {
const models = await listLanguageModels();

return {
content: [{
type: "text",
text: JSON.stringify(models),
}],
};
}
);

server.tool(
"ask_codebase",
dedent`
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.

The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references

Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.

This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
`,
askCodebaseRequestSchema.shape,
async (request: AskCodebaseRequest) => {
const response = await askCodebase(request);

// Format the response with the answer and a link to the chat
const formattedResponse = dedent`
${response.answer}

---
**View full research session:** ${response.chatUrl}
**Model used:** ${response.languageModel.model}
`;

return {
content: [{
type: "text",
text: formattedResponse,
}],
};
}
);

const runServer = async () => {
const transport = new StdioServerTransport();
Expand Down
42 changes: 42 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,3 +272,45 @@ export const listCommitsResponseSchema = z.array(z.object({
author_name: z.string(),
author_email: z.string(),
}));

export const languageModelInfoSchema = z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
});

export const listLanguageModelsResponseSchema = z.array(languageModelInfoSchema);

export const askCodebaseRequestSchema = z.object({
query: z
.string()
.describe("The query to ask about the codebase."),
repos: z
.array(z.string())
.optional()
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema
.omit({ displayName: true })
.optional()
.describe("The language model to use for answering the question. If not provided, defaults to the first model in the config. Use list_language_models to see available options."),
});

export const sourceSchema = z.object({
type: z.literal('file'),
repo: z.string(),
path: z.string(),
name: z.string(),
language: z.string(),
revision: z.string(),
});

export const askCodebaseResponseSchema = z.object({
answer: z.string().describe("The agent's final answer in markdown format"),
chatId: z.string().describe("ID of the persisted chat session"),
chatUrl: z.string().describe("URL to view the chat in the web UI"),
languageModel: z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
}).describe("The language model used to generate the response"),
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `/api/chat/blocking` endpoint that creates a blocking Ask thread, used by the MCP server. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `/api/models` endpoint to list configured language models. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)

### Fixed
- Fixed issue where files with a comma would not render correctly in file tree. [#831](https://github.com/sourcebot-dev/sourcebot/pull/831)

### Changed
- Changed `/api/source` api to support fetching source code for any revision, not just revisions that are indexed by zoekt. [#829](https://github.com/sourcebot-dev/sourcebot/pull/829)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)
- Adjusted prompts and tools for Ask agent. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [4.10.20] - 2026-01-28

Expand Down
84 changes: 68 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -147,32 +147,84 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)

### `search_code`

Fetches code that matches the provided regex pattern in `query`.
Searches for code that matches the provided search query as a substring by default, or as a regular expression if `useRegex` is true.

Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
| Name | Required | Description |
|:----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The search pattern to match against code contents. Do not escape quotes in your query. |
| `useRegex` | no | Whether to use regular expression matching. When false, substring matching is used (default: false). |
| `filterByRepos` | no | Scope the search to specific repositories. |
| `filterByLanguages` | no | Scope the search to specific languages. |
| `filterByFilepaths` | no | Scope the search to specific filepaths. |
| `caseSensitive` | no | Whether the search should be case sensitive (default: false). |
| `includeCodeSnippets` | no | Whether to include code snippets in the response (default: false). |
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
| `maxTokens` | no | The maximum number of tokens to return (default: 10000). |


### `list_repos`

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

### `get_file_source`
Parameters:
| Name | Required | Description |
|:------------|:---------|:--------------------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 30). |
| `sort` | no | Sort repositories by 'name' or 'pushed' (most recent commit). Default: 'name'. |
| `direction` | no | Sort direction: 'asc' or 'desc' (default: 'asc'). |


### `read_file`

Reads the source code for a given file.

Parameters:
| Name | Required | Description |
|:-------|:---------|:-------------------------------------------------------------------------------------------------------|
| `repo` | yes | The repository name. |
| `path` | yes | The path to the file. |
| `ref` | no | Commit SHA, branch or tag name to fetch the source code for. If not provided, uses the default branch. |


### `list_commits`

Get a list of commits for a given repository.

Parameters:
| Name | Required | Description |
|:----------|:---------|:-----------------------------------------------------------------------------------------------------------------------|
| `repo` | yes | The name of the repository to list commits for. |
| `query` | no | Search query to filter commits by message content (case-insensitive). |
| `since` | no | Show commits more recent than this date. Supports ISO 8601 (e.g., '2024-01-01') or relative formats (e.g., '30 days ago'). |
| `until` | no | Show commits older than this date. Supports ISO 8601 (e.g., '2024-12-31') or relative formats (e.g., 'yesterday'). |
| `author` | no | Filter commits by author name or email (case-insensitive). |
| `ref` | no | Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch. |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 50). |


### `list_language_models`

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

Parameters:

This tool takes no parameters.


### `ask_codebase`

Fetches the source code for a given file.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |
Comment thread
brendan-kellam marked this conversation as resolved.


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `ask_codebase` tool that can invoke the Ask subagent to explore a set of codebases and return a summarized answer. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `list_language_models` tool to discover available language models configured on the Sourcebot instance. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [1.0.14] - 2026-01-27

### Changed
Expand Down
26 changes: 26 additions & 0 deletions packages/mcp/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -234,6 +234,32 @@ Get a list of commits for a given repository.

</details>

### list_language_models

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

<details>
<summary>Parameters</summary>

This tool takes no parameters.

</details>

### ask_codebase

Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |

Comment thread
brendan-kellam marked this conversation as resolved.
</details>


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
45 changes: 42 additions & 3 deletions packages/mcp/src/client.ts
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { env } from './env.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema } from './schemas.js';
import { FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema, askCodebaseResponseSchema, listLanguageModelsResponseSchema } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema, ListLanguageModelsResponse } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand DownExpand Up@@ -106,4 +106,43 @@ export const listCommits = async (queryParams: ListCommitsQueryParamsSchema) =>
const commits = await parseResponse(response, listCommitsResponseSchema);
const totalCount = parseInt(response.headers.get('X-Total-Count') ?? '0', 10);
return { commits, totalCount };
}
}

/**
* Asks a natural language question about the codebase using the Sourcebot AI agent.
* This is a blocking call that runs the full agent loop and returns when complete.
*
* @param request - The question and optional repo filters
* @returns The agent's answer, chat URL, sources, and metadata
*/
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export const askCodebase = async (request: AskCodebaseRequest): Promise<AskCodebaseResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/chat/blocking`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
body: JSON.stringify(request),
});

return parseResponse(response, askCodebaseResponseSchema);
}

/**
* Lists the available language models configured on the Sourcebot instance.
*
* @returns Array of language model info objects
*/
export const listLanguageModels = async (): Promise<ListLanguageModelsResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
});

return parseResponse(response, listLanguageModelsResponseSchema);
}
56 changes: 53 additions & 3 deletions packages/mcp/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,10 +6,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import _dedent from "dedent";
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { getFileSource, listCommits, listRepos, search } from './client.js';
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, search } from './client.js';
import { env, numberSchema } from './env.js';
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';

const dedent = _dedent.withOptions({ alignValues: true });

Expand DownExpand Up@@ -238,7 +238,57 @@ server.tool(
}
);

server.tool(
"list_language_models",
dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
{},
async () => {
const models = await listLanguageModels();

return {
content: [{
type: "text",
text: JSON.stringify(models),
}],
};
}
);

server.tool(
"ask_codebase",
dedent`
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.

The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references

Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.

This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
`,
askCodebaseRequestSchema.shape,
async (request: AskCodebaseRequest) => {
const response = await askCodebase(request);

// Format the response with the answer and a link to the chat
const formattedResponse = dedent`
${response.answer}

---
**View full research session:** ${response.chatUrl}
**Model used:** ${response.languageModel.model}
`;

return {
content: [{
type: "text",
text: formattedResponse,
}],
};
}
);

const runServer = async () => {
const transport = new StdioServerTransport();
Expand Down
42 changes: 42 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,3 +272,45 @@ export const listCommitsResponseSchema = z.array(z.object({
author_name: z.string(),
author_email: z.string(),
}));

export const languageModelInfoSchema = z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
});

export const listLanguageModelsResponseSchema = z.array(languageModelInfoSchema);

export const askCodebaseRequestSchema = z.object({
query: z
.string()
.describe("The query to ask about the codebase."),
repos: z
.array(z.string())
.optional()
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema
.omit({ displayName: true })
.optional()
.describe("The language model to use for answering the question. If not provided, defaults to the first model in the config. Use list_language_models to see available options."),
});

export const sourceSchema = z.object({
type: z.literal('file'),
repo: z.string(),
path: z.string(),
name: z.string(),
language: z.string(),
revision: z.string(),
});

export const askCodebaseResponseSchema = z.object({
answer: z.string().describe("The agent's final answer in markdown format"),
chatId: z.string().describe("ID of the persisted chat session"),
chatUrl: z.string().describe("URL to view the chat in the web UI"),
languageModel: z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
}).describe("The language model used to generate the response"),
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `/api/chat/blocking` endpoint that creates a blocking Ask thread, used by the MCP server. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `/api/models` endpoint to list configured language models. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)

### Fixed
- Fixed issue where files with a comma would not render correctly in file tree. [#831](https://github.com/sourcebot-dev/sourcebot/pull/831)

### Changed
- Changed `/api/source` api to support fetching source code for any revision, not just revisions that are indexed by zoekt. [#829](https://github.com/sourcebot-dev/sourcebot/pull/829)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)
- Adjusted prompts and tools for Ask agent. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [4.10.20] - 2026-01-28

Expand Down
84 changes: 68 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -147,32 +147,84 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)

### `search_code`

Fetches code that matches the provided regex pattern in `query`.
Searches for code that matches the provided search query as a substring by default, or as a regular expression if `useRegex` is true.

Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
| Name | Required | Description |
|:----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The search pattern to match against code contents. Do not escape quotes in your query. |
| `useRegex` | no | Whether to use regular expression matching. When false, substring matching is used (default: false). |
| `filterByRepos` | no | Scope the search to specific repositories. |
| `filterByLanguages` | no | Scope the search to specific languages. |
| `filterByFilepaths` | no | Scope the search to specific filepaths. |
| `caseSensitive` | no | Whether the search should be case sensitive (default: false). |
| `includeCodeSnippets` | no | Whether to include code snippets in the response (default: false). |
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
| `maxTokens` | no | The maximum number of tokens to return (default: 10000). |


### `list_repos`

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

### `get_file_source`
Parameters:
| Name | Required | Description |
|:------------|:---------|:--------------------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 30). |
| `sort` | no | Sort repositories by 'name' or 'pushed' (most recent commit). Default: 'name'. |
| `direction` | no | Sort direction: 'asc' or 'desc' (default: 'asc'). |


### `read_file`

Reads the source code for a given file.

Parameters:
| Name | Required | Description |
|:-------|:---------|:-------------------------------------------------------------------------------------------------------|
| `repo` | yes | The repository name. |
| `path` | yes | The path to the file. |
| `ref` | no | Commit SHA, branch or tag name to fetch the source code for. If not provided, uses the default branch. |


### `list_commits`

Get a list of commits for a given repository.

Parameters:
| Name | Required | Description |
|:----------|:---------|:-----------------------------------------------------------------------------------------------------------------------|
| `repo` | yes | The name of the repository to list commits for. |
| `query` | no | Search query to filter commits by message content (case-insensitive). |
| `since` | no | Show commits more recent than this date. Supports ISO 8601 (e.g., '2024-01-01') or relative formats (e.g., '30 days ago'). |
| `until` | no | Show commits older than this date. Supports ISO 8601 (e.g., '2024-12-31') or relative formats (e.g., 'yesterday'). |
| `author` | no | Filter commits by author name or email (case-insensitive). |
| `ref` | no | Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch. |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 50). |


### `list_language_models`

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

Parameters:

This tool takes no parameters.


### `ask_codebase`

Fetches the source code for a given file.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |
Comment thread
brendan-kellam marked this conversation as resolved.


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `ask_codebase` tool that can invoke the Ask subagent to explore a set of codebases and return a summarized answer. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `list_language_models` tool to discover available language models configured on the Sourcebot instance. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [1.0.14] - 2026-01-27

### Changed
Expand Down
26 changes: 26 additions & 0 deletions packages/mcp/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -234,6 +234,32 @@ Get a list of commits for a given repository.

</details>

### list_language_models

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

<details>
<summary>Parameters</summary>

This tool takes no parameters.

</details>

### ask_codebase

Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |

Comment thread
brendan-kellam marked this conversation as resolved.
</details>


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
45 changes: 42 additions & 3 deletions packages/mcp/src/client.ts
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { env } from './env.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema } from './schemas.js';
import { FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema, askCodebaseResponseSchema, listLanguageModelsResponseSchema } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema, ListLanguageModelsResponse } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand DownExpand Up@@ -106,4 +106,43 @@ export const listCommits = async (queryParams: ListCommitsQueryParamsSchema) =>
const commits = await parseResponse(response, listCommitsResponseSchema);
const totalCount = parseInt(response.headers.get('X-Total-Count') ?? '0', 10);
return { commits, totalCount };
}
}

/**
* Asks a natural language question about the codebase using the Sourcebot AI agent.
* This is a blocking call that runs the full agent loop and returns when complete.
*
* @param request - The question and optional repo filters
* @returns The agent's answer, chat URL, sources, and metadata
*/
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export const askCodebase = async (request: AskCodebaseRequest): Promise<AskCodebaseResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/chat/blocking`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
body: JSON.stringify(request),
});

return parseResponse(response, askCodebaseResponseSchema);
}

/**
* Lists the available language models configured on the Sourcebot instance.
*
* @returns Array of language model info objects
*/
export const listLanguageModels = async (): Promise<ListLanguageModelsResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
});

return parseResponse(response, listLanguageModelsResponseSchema);
}
56 changes: 53 additions & 3 deletions packages/mcp/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,10 +6,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import _dedent from "dedent";
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { getFileSource, listCommits, listRepos, search } from './client.js';
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, search } from './client.js';
import { env, numberSchema } from './env.js';
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';

const dedent = _dedent.withOptions({ alignValues: true });

Expand DownExpand Up@@ -238,7 +238,57 @@ server.tool(
}
);

server.tool(
"list_language_models",
dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
{},
async () => {
const models = await listLanguageModels();

return {
content: [{
type: "text",
text: JSON.stringify(models),
}],
};
}
);

server.tool(
"ask_codebase",
dedent`
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.

The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references

Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.

This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
`,
askCodebaseRequestSchema.shape,
async (request: AskCodebaseRequest) => {
const response = await askCodebase(request);

// Format the response with the answer and a link to the chat
const formattedResponse = dedent`
${response.answer}

---
**View full research session:** ${response.chatUrl}
**Model used:** ${response.languageModel.model}
`;

return {
content: [{
type: "text",
text: formattedResponse,
}],
};
}
);

const runServer = async () => {
const transport = new StdioServerTransport();
Expand Down
42 changes: 42 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,3 +272,45 @@ export const listCommitsResponseSchema = z.array(z.object({
author_name: z.string(),
author_email: z.string(),
}));

export const languageModelInfoSchema = z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
});

export const listLanguageModelsResponseSchema = z.array(languageModelInfoSchema);

export const askCodebaseRequestSchema = z.object({
query: z
.string()
.describe("The query to ask about the codebase."),
repos: z
.array(z.string())
.optional()
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema
.omit({ displayName: true })
.optional()
.describe("The language model to use for answering the question. If not provided, defaults to the first model in the config. Use list_language_models to see available options."),
});

export const sourceSchema = z.object({
type: z.literal('file'),
repo: z.string(),
path: z.string(),
name: z.string(),
language: z.string(),
revision: z.string(),
});

export const askCodebaseResponseSchema = z.object({
answer: z.string().describe("The agent's final answer in markdown format"),
chatId: z.string().describe("ID of the persisted chat session"),
chatUrl: z.string().describe("URL to view the chat in the web UI"),
languageModel: z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
}).describe("The language model used to generate the response"),
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `/api/chat/blocking` endpoint that creates a blocking Ask thread, used by the MCP server. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `/api/models` endpoint to list configured language models. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)

### Fixed
- Fixed issue where files with a comma would not render correctly in file tree. [#831](https://github.com/sourcebot-dev/sourcebot/pull/831)

### Changed
- Changed `/api/source` api to support fetching source code for any revision, not just revisions that are indexed by zoekt. [#829](https://github.com/sourcebot-dev/sourcebot/pull/829)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)
- Adjusted prompts and tools for Ask agent. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [4.10.20] - 2026-01-28

Expand Down
84 changes: 68 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -147,32 +147,84 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)

### `search_code`

Fetches code that matches the provided regex pattern in `query`.
Searches for code that matches the provided search query as a substring by default, or as a regular expression if `useRegex` is true.

Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
| Name | Required | Description |
|:----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The search pattern to match against code contents. Do not escape quotes in your query. |
| `useRegex` | no | Whether to use regular expression matching. When false, substring matching is used (default: false). |
| `filterByRepos` | no | Scope the search to specific repositories. |
| `filterByLanguages` | no | Scope the search to specific languages. |
| `filterByFilepaths` | no | Scope the search to specific filepaths. |
| `caseSensitive` | no | Whether the search should be case sensitive (default: false). |
| `includeCodeSnippets` | no | Whether to include code snippets in the response (default: false). |
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
| `maxTokens` | no | The maximum number of tokens to return (default: 10000). |


### `list_repos`

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

### `get_file_source`
Parameters:
| Name | Required | Description |
|:------------|:---------|:--------------------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 30). |
| `sort` | no | Sort repositories by 'name' or 'pushed' (most recent commit). Default: 'name'. |
| `direction` | no | Sort direction: 'asc' or 'desc' (default: 'asc'). |


### `read_file`

Reads the source code for a given file.

Parameters:
| Name | Required | Description |
|:-------|:---------|:-------------------------------------------------------------------------------------------------------|
| `repo` | yes | The repository name. |
| `path` | yes | The path to the file. |
| `ref` | no | Commit SHA, branch or tag name to fetch the source code for. If not provided, uses the default branch. |


### `list_commits`

Get a list of commits for a given repository.

Parameters:
| Name | Required | Description |
|:----------|:---------|:-----------------------------------------------------------------------------------------------------------------------|
| `repo` | yes | The name of the repository to list commits for. |
| `query` | no | Search query to filter commits by message content (case-insensitive). |
| `since` | no | Show commits more recent than this date. Supports ISO 8601 (e.g., '2024-01-01') or relative formats (e.g., '30 days ago'). |
| `until` | no | Show commits older than this date. Supports ISO 8601 (e.g., '2024-12-31') or relative formats (e.g., 'yesterday'). |
| `author` | no | Filter commits by author name or email (case-insensitive). |
| `ref` | no | Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch. |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 50). |


### `list_language_models`

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

Parameters:

This tool takes no parameters.


### `ask_codebase`

Fetches the source code for a given file.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |
Comment thread
brendan-kellam marked this conversation as resolved.


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `ask_codebase` tool that can invoke the Ask subagent to explore a set of codebases and return a summarized answer. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `list_language_models` tool to discover available language models configured on the Sourcebot instance. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [1.0.14] - 2026-01-27

### Changed
Expand Down
26 changes: 26 additions & 0 deletions packages/mcp/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -234,6 +234,32 @@ Get a list of commits for a given repository.

</details>

### list_language_models

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

<details>
<summary>Parameters</summary>

This tool takes no parameters.

</details>

### ask_codebase

Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |

Comment thread
brendan-kellam marked this conversation as resolved.
</details>


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
45 changes: 42 additions & 3 deletions packages/mcp/src/client.ts
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { env } from './env.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema } from './schemas.js';
import { FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema, askCodebaseResponseSchema, listLanguageModelsResponseSchema } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema, ListLanguageModelsResponse } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand DownExpand Up@@ -106,4 +106,43 @@ export const listCommits = async (queryParams: ListCommitsQueryParamsSchema) =>
const commits = await parseResponse(response, listCommitsResponseSchema);
const totalCount = parseInt(response.headers.get('X-Total-Count') ?? '0', 10);
return { commits, totalCount };
}
}

/**
* Asks a natural language question about the codebase using the Sourcebot AI agent.
* This is a blocking call that runs the full agent loop and returns when complete.
*
* @param request - The question and optional repo filters
* @returns The agent's answer, chat URL, sources, and metadata
*/
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export const askCodebase = async (request: AskCodebaseRequest): Promise<AskCodebaseResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/chat/blocking`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
body: JSON.stringify(request),
});

return parseResponse(response, askCodebaseResponseSchema);
}

/**
* Lists the available language models configured on the Sourcebot instance.
*
* @returns Array of language model info objects
*/
export const listLanguageModels = async (): Promise<ListLanguageModelsResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
});

return parseResponse(response, listLanguageModelsResponseSchema);
}
56 changes: 53 additions & 3 deletions packages/mcp/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,10 +6,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import _dedent from "dedent";
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { getFileSource, listCommits, listRepos, search } from './client.js';
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, search } from './client.js';
import { env, numberSchema } from './env.js';
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';

const dedent = _dedent.withOptions({ alignValues: true });

Expand DownExpand Up@@ -238,7 +238,57 @@ server.tool(
}
);

server.tool(
"list_language_models",
dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
{},
async () => {
const models = await listLanguageModels();

return {
content: [{
type: "text",
text: JSON.stringify(models),
}],
};
}
);

server.tool(
"ask_codebase",
dedent`
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.

The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references

Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.

This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
`,
askCodebaseRequestSchema.shape,
async (request: AskCodebaseRequest) => {
const response = await askCodebase(request);

// Format the response with the answer and a link to the chat
const formattedResponse = dedent`
${response.answer}

---
**View full research session:** ${response.chatUrl}
**Model used:** ${response.languageModel.model}
`;

return {
content: [{
type: "text",
text: formattedResponse,
}],
};
}
);

const runServer = async () => {
const transport = new StdioServerTransport();
Expand Down
42 changes: 42 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,3 +272,45 @@ export const listCommitsResponseSchema = z.array(z.object({
author_name: z.string(),
author_email: z.string(),
}));

export const languageModelInfoSchema = z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
});

export const listLanguageModelsResponseSchema = z.array(languageModelInfoSchema);

export const askCodebaseRequestSchema = z.object({
query: z
.string()
.describe("The query to ask about the codebase."),
repos: z
.array(z.string())
.optional()
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema
.omit({ displayName: true })
.optional()
.describe("The language model to use for answering the question. If not provided, defaults to the first model in the config. Use list_language_models to see available options."),
});

export const sourceSchema = z.object({
type: z.literal('file'),
repo: z.string(),
path: z.string(),
name: z.string(),
language: z.string(),
revision: z.string(),
});

export const askCodebaseResponseSchema = z.object({
answer: z.string().describe("The agent's final answer in markdown format"),
chatId: z.string().describe("ID of the persisted chat session"),
chatUrl: z.string().describe("URL to view the chat in the web UI"),
languageModel: z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
}).describe("The language model used to generate the response"),
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `/api/chat/blocking` endpoint that creates a blocking Ask thread, used by the MCP server. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `/api/models` endpoint to list configured language models. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)

### Fixed
- Fixed issue where files with a comma would not render correctly in file tree. [#831](https://github.com/sourcebot-dev/sourcebot/pull/831)

### Changed
- Changed `/api/source` api to support fetching source code for any revision, not just revisions that are indexed by zoekt. [#829](https://github.com/sourcebot-dev/sourcebot/pull/829)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)
- Adjusted prompts and tools for Ask agent. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [4.10.20] - 2026-01-28

Expand Down
84 changes: 68 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -147,32 +147,84 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)

### `search_code`

Fetches code that matches the provided regex pattern in `query`.
Searches for code that matches the provided search query as a substring by default, or as a regular expression if `useRegex` is true.

Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
| Name | Required | Description |
|:----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The search pattern to match against code contents. Do not escape quotes in your query. |
| `useRegex` | no | Whether to use regular expression matching. When false, substring matching is used (default: false). |
| `filterByRepos` | no | Scope the search to specific repositories. |
| `filterByLanguages` | no | Scope the search to specific languages. |
| `filterByFilepaths` | no | Scope the search to specific filepaths. |
| `caseSensitive` | no | Whether the search should be case sensitive (default: false). |
| `includeCodeSnippets` | no | Whether to include code snippets in the response (default: false). |
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
| `maxTokens` | no | The maximum number of tokens to return (default: 10000). |


### `list_repos`

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

### `get_file_source`
Parameters:
| Name | Required | Description |
|:------------|:---------|:--------------------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 30). |
| `sort` | no | Sort repositories by 'name' or 'pushed' (most recent commit). Default: 'name'. |
| `direction` | no | Sort direction: 'asc' or 'desc' (default: 'asc'). |


### `read_file`

Reads the source code for a given file.

Parameters:
| Name | Required | Description |
|:-------|:---------|:-------------------------------------------------------------------------------------------------------|
| `repo` | yes | The repository name. |
| `path` | yes | The path to the file. |
| `ref` | no | Commit SHA, branch or tag name to fetch the source code for. If not provided, uses the default branch. |


### `list_commits`

Get a list of commits for a given repository.

Parameters:
| Name | Required | Description |
|:----------|:---------|:-----------------------------------------------------------------------------------------------------------------------|
| `repo` | yes | The name of the repository to list commits for. |
| `query` | no | Search query to filter commits by message content (case-insensitive). |
| `since` | no | Show commits more recent than this date. Supports ISO 8601 (e.g., '2024-01-01') or relative formats (e.g., '30 days ago'). |
| `until` | no | Show commits older than this date. Supports ISO 8601 (e.g., '2024-12-31') or relative formats (e.g., 'yesterday'). |
| `author` | no | Filter commits by author name or email (case-insensitive). |
| `ref` | no | Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch. |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 50). |


### `list_language_models`

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

Parameters:

This tool takes no parameters.


### `ask_codebase`

Fetches the source code for a given file.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |
Comment thread
brendan-kellam marked this conversation as resolved.


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `ask_codebase` tool that can invoke the Ask subagent to explore a set of codebases and return a summarized answer. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `list_language_models` tool to discover available language models configured on the Sourcebot instance. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [1.0.14] - 2026-01-27

### Changed
Expand Down
26 changes: 26 additions & 0 deletions packages/mcp/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -234,6 +234,32 @@ Get a list of commits for a given repository.

</details>

### list_language_models

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

<details>
<summary>Parameters</summary>

This tool takes no parameters.

</details>

### ask_codebase

Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |

Comment thread
brendan-kellam marked this conversation as resolved.
</details>


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
45 changes: 42 additions & 3 deletions packages/mcp/src/client.ts
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { env } from './env.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema } from './schemas.js';
import { FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema, askCodebaseResponseSchema, listLanguageModelsResponseSchema } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema, ListLanguageModelsResponse } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand DownExpand Up@@ -106,4 +106,43 @@ export const listCommits = async (queryParams: ListCommitsQueryParamsSchema) =>
const commits = await parseResponse(response, listCommitsResponseSchema);
const totalCount = parseInt(response.headers.get('X-Total-Count') ?? '0', 10);
return { commits, totalCount };
}
}

/**
* Asks a natural language question about the codebase using the Sourcebot AI agent.
* This is a blocking call that runs the full agent loop and returns when complete.
*
* @param request - The question and optional repo filters
* @returns The agent's answer, chat URL, sources, and metadata
*/
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export const askCodebase = async (request: AskCodebaseRequest): Promise<AskCodebaseResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/chat/blocking`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
body: JSON.stringify(request),
});

return parseResponse(response, askCodebaseResponseSchema);
}

/**
* Lists the available language models configured on the Sourcebot instance.
*
* @returns Array of language model info objects
*/
export const listLanguageModels = async (): Promise<ListLanguageModelsResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
});

return parseResponse(response, listLanguageModelsResponseSchema);
}
56 changes: 53 additions & 3 deletions packages/mcp/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,10 +6,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import _dedent from "dedent";
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { getFileSource, listCommits, listRepos, search } from './client.js';
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, search } from './client.js';
import { env, numberSchema } from './env.js';
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';

const dedent = _dedent.withOptions({ alignValues: true });

Expand DownExpand Up@@ -238,7 +238,57 @@ server.tool(
}
);

server.tool(
"list_language_models",
dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
{},
async () => {
const models = await listLanguageModels();

return {
content: [{
type: "text",
text: JSON.stringify(models),
}],
};
}
);

server.tool(
"ask_codebase",
dedent`
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.

The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references

Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.

This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
`,
askCodebaseRequestSchema.shape,
async (request: AskCodebaseRequest) => {
const response = await askCodebase(request);

// Format the response with the answer and a link to the chat
const formattedResponse = dedent`
${response.answer}

---
**View full research session:** ${response.chatUrl}
**Model used:** ${response.languageModel.model}
`;

return {
content: [{
type: "text",
text: formattedResponse,
}],
};
}
);

const runServer = async () => {
const transport = new StdioServerTransport();
Expand Down
42 changes: 42 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,3 +272,45 @@ export const listCommitsResponseSchema = z.array(z.object({
author_name: z.string(),
author_email: z.string(),
}));

export const languageModelInfoSchema = z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
});

export const listLanguageModelsResponseSchema = z.array(languageModelInfoSchema);

export const askCodebaseRequestSchema = z.object({
query: z
.string()
.describe("The query to ask about the codebase."),
repos: z
.array(z.string())
.optional()
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema
.omit({ displayName: true })
.optional()
.describe("The language model to use for answering the question. If not provided, defaults to the first model in the config. Use list_language_models to see available options."),
});

export const sourceSchema = z.object({
type: z.literal('file'),
repo: z.string(),
path: z.string(),
name: z.string(),
language: z.string(),
revision: z.string(),
});

export const askCodebaseResponseSchema = z.object({
answer: z.string().describe("The agent's final answer in markdown format"),
chatId: z.string().describe("ID of the persisted chat session"),
chatUrl: z.string().describe("URL to view the chat in the web UI"),
languageModel: z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
}).describe("The language model used to generate the response"),
});
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `/api/chat/blocking` endpoint that creates a blocking Ask thread, used by the MCP server. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `/api/models` endpoint to list configured language models. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)

### Fixed
- Fixed issue where files with a comma would not render correctly in file tree. [#831](https://github.com/sourcebot-dev/sourcebot/pull/831)

### Changed
- Changed `/api/source` api to support fetching source code for any revision, not just revisions that are indexed by zoekt. [#829](https://github.com/sourcebot-dev/sourcebot/pull/829)
- Added additional telemetry for api requests. [#835](https://github.com/sourcebot-dev/sourcebot/pull/835)
- Adjusted prompts and tools for Ask agent. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [4.10.20] - 2026-01-28

Expand Down
84 changes: 68 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -147,32 +147,84 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP)

### `search_code`

Fetches code that matches the provided regex pattern in `query`.
Searches for code that matches the provided search query as a substring by default, or as a regular expression if `useRegex` is true.

Parameters:
| Name | Required | Description |
|:----------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | Regex pattern to search for. Escape special characters and spaces with a single backslash (e.g., 'console\.log', 'console\ log'). |
| `filterByRepoIds` | no | Restrict search to specific repository IDs (from 'list_repos'). Leave empty to search all. |
| `filterByLanguages` | no | Restrict search to specific languages (GitHub linguist format, e.g., Python, JavaScript). |
| `caseSensitive` | no | Case sensitive search (default: false). |
| `includeCodeSnippets` | no | Include code snippets in results (default: false). |
| `maxTokens` | no | Max tokens to return (default: env.DEFAULT_MINIMUM_TOKENS). |
| Name | Required | Description |
|:----------------------|:---------|:---------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The search pattern to match against code contents. Do not escape quotes in your query. |
| `useRegex` | no | Whether to use regular expression matching. When false, substring matching is used (default: false). |
| `filterByRepos` | no | Scope the search to specific repositories. |
| `filterByLanguages` | no | Scope the search to specific languages. |
| `filterByFilepaths` | no | Scope the search to specific filepaths. |
| `caseSensitive` | no | Whether the search should be case sensitive (default: false). |
| `includeCodeSnippets` | no | Whether to include code snippets in the response (default: false). |
| `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. |
| `maxTokens` | no | The maximum number of tokens to return (default: 10000). |


### `list_repos`

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

### `get_file_source`
Parameters:
| Name | Required | Description |
|:------------|:---------|:--------------------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 30). |
| `sort` | no | Sort repositories by 'name' or 'pushed' (most recent commit). Default: 'name'. |
| `direction` | no | Sort direction: 'asc' or 'desc' (default: 'asc'). |


### `read_file`

Reads the source code for a given file.

Parameters:
| Name | Required | Description |
|:-------|:---------|:-------------------------------------------------------------------------------------------------------|
| `repo` | yes | The repository name. |
| `path` | yes | The path to the file. |
| `ref` | no | Commit SHA, branch or tag name to fetch the source code for. If not provided, uses the default branch. |


### `list_commits`

Get a list of commits for a given repository.

Parameters:
| Name | Required | Description |
|:----------|:---------|:-----------------------------------------------------------------------------------------------------------------------|
| `repo` | yes | The name of the repository to list commits for. |
| `query` | no | Search query to filter commits by message content (case-insensitive). |
| `since` | no | Show commits more recent than this date. Supports ISO 8601 (e.g., '2024-01-01') or relative formats (e.g., '30 days ago'). |
| `until` | no | Show commits older than this date. Supports ISO 8601 (e.g., '2024-12-31') or relative formats (e.g., 'yesterday'). |
| `author` | no | Filter commits by author name or email (case-insensitive). |
| `ref` | no | Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch. |
| `page` | no | Page number for pagination (min 1, default: 1). |
| `perPage` | no | Results per page for pagination (min 1, max 100, default: 50). |


### `list_language_models`

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

Parameters:

This tool takes no parameters.


### `ask_codebase`

Fetches the source code for a given file.
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

Parameters:
| Name | Required | Description |
|:-------------|:---------|:-----------------------------------------------------------------|
| `fileName` | yes | The file to fetch the source code for. |
| `repoId` | yes | The Sourcebot repository ID. |
| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |
Comment thread
brendan-kellam marked this conversation as resolved.


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added `ask_codebase` tool that can invoke the Ask subagent to explore a set of codebases and return a summarized answer. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)
- Added `list_language_models` tool to discover available language models configured on the Sourcebot instance. [#814](https://github.com/sourcebot-dev/sourcebot/pull/814)

## [1.0.14] - 2026-01-27

### Changed
Expand Down
26 changes: 26 additions & 0 deletions packages/mcp/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -234,6 +234,32 @@ Get a list of commits for a given repository.

</details>

### list_language_models

Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling `ask_codebase`.

<details>
<summary>Parameters</summary>

This tool takes no parameters.

</details>

### ask_codebase

Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question. Returns a detailed answer in markdown format with code references, plus a link to view the full research session in the Sourcebot web UI.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| `query` | yes | The query to ask about the codebase. |
| `repos` | no | The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible. |
| `languageModel` | no | The language model to use for answering the question. Object with `provider` and `model`. If not provided, defaults to the first model in the config. Use `list_language_models` to see available options. |

Comment thread
brendan-kellam marked this conversation as resolved.
</details>


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
45 changes: 42 additions & 3 deletions packages/mcp/src/client.ts
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
import { env } from './env.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema } from './schemas.js';
import { FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { listReposResponseSchema, searchResponseSchema, fileSourceResponseSchema, listCommitsResponseSchema, askCodebaseResponseSchema, listLanguageModelsResponseSchema } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema, ListLanguageModelsResponse } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand DownExpand Up@@ -106,4 +106,43 @@ export const listCommits = async (queryParams: ListCommitsQueryParamsSchema) =>
const commits = await parseResponse(response, listCommitsResponseSchema);
const totalCount = parseInt(response.headers.get('X-Total-Count') ?? '0', 10);
return { commits, totalCount };
}
}

/**
* Asks a natural language question about the codebase using the Sourcebot AI agent.
* This is a blocking call that runs the full agent loop and returns when complete.
*
* @param request - The question and optional repo filters
* @returns The agent's answer, chat URL, sources, and metadata
*/
Comment thread
coderabbitai[bot] marked this conversation as resolved.
export const askCodebase = async (request: AskCodebaseRequest): Promise<AskCodebaseResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/chat/blocking`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
body: JSON.stringify(request),
});

return parseResponse(response, askCodebaseResponseSchema);
}

/**
* Lists the available language models configured on the Sourcebot instance.
*
* @returns Array of language model info objects
*/
export const listLanguageModels = async (): Promise<ListLanguageModelsResponse> => {
const response = await fetch(`${env.SOURCEBOT_HOST}/api/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Sourcebot-Client-Source': 'mcp',
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
},
});

return parseResponse(response, listLanguageModelsResponseSchema);
}
56 changes: 53 additions & 3 deletions packages/mcp/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,10 +6,10 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import _dedent from "dedent";
import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { getFileSource, listCommits, listRepos, search } from './client.js';
import { askCodebase, getFileSource, listCommits, listLanguageModels, listRepos, search } from './client.js';
import { env, numberSchema } from './env.js';
import { fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';
import { askCodebaseRequestSchema, fileSourceRequestSchema, listCommitsQueryParamsSchema, listReposQueryParamsSchema } from './schemas.js';
import { AskCodebaseRequest, FileSourceRequest, ListCommitsQueryParamsSchema, ListReposQueryParams, TextContent } from './types.js';

const dedent = _dedent.withOptions({ alignValues: true });

Expand DownExpand Up@@ -238,7 +238,57 @@ server.tool(
}
);

server.tool(
"list_language_models",
dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
{},
async () => {
const models = await listLanguageModels();

return {
content: [{
type: "text",
text: JSON.stringify(models),
}],
};
}
);

server.tool(
"ask_codebase",
dedent`
Ask a natural language question about the codebase. This tool uses an AI agent to autonomously search code, read files, and find symbol references/definitions to answer your question.

The agent will:
- Analyze your question and determine what context it needs
- Search the codebase using multiple strategies (code search, symbol lookup, file reading)
- Synthesize findings into a comprehensive answer with code references

Returns a detailed answer in markdown format with code references, plus a link to view the full research session (including all tool calls and reasoning) in the Sourcebot web UI.

This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase.
`,
askCodebaseRequestSchema.shape,
async (request: AskCodebaseRequest) => {
const response = await askCodebase(request);

// Format the response with the answer and a link to the chat
const formattedResponse = dedent`
${response.answer}

---
**View full research session:** ${response.chatUrl}
**Model used:** ${response.languageModel.model}
`;

return {
content: [{
type: "text",
text: formattedResponse,
}],
};
}
);

const runServer = async () => {
const transport = new StdioServerTransport();
Expand Down
42 changes: 42 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -272,3 +272,45 @@ export const listCommitsResponseSchema = z.array(z.object({
author_name: z.string(),
author_email: z.string(),
}));

export const languageModelInfoSchema = z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
});

export const listLanguageModelsResponseSchema = z.array(languageModelInfoSchema);

export const askCodebaseRequestSchema = z.object({
query: z
.string()
.describe("The query to ask about the codebase."),
repos: z
.array(z.string())
.optional()
.describe("The repositories that are accessible to the agent during the chat. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema
.omit({ displayName: true })
.optional()
.describe("The language model to use for answering the question. If not provided, defaults to the first model in the config. Use list_language_models to see available options."),
});

export const sourceSchema = z.object({
type: z.literal('file'),
repo: z.string(),
path: z.string(),
name: z.string(),
language: z.string(),
revision: z.string(),
});

export const askCodebaseResponseSchema = z.object({
answer: z.string().describe("The agent's final answer in markdown format"),
chatId: z.string().describe("ID of the persisted chat session"),
chatUrl: z.string().describe("URL to view the chat in the web UI"),
languageModel: z.object({
provider: z.string().describe("The model provider (e.g., 'anthropic', 'openai')"),
model: z.string().describe("The model ID"),
displayName: z.string().optional().describe("Optional display name for the model"),
}).describe("The language model used to generate the response"),
});
Loading