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 number Diff line number Diff 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 number Diff line number Diff 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. |


## Environment Variables
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp/CHANGELOG.md
Original file line number Diff line number Diff 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 number Diff line number Diff 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. |

</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 number Diff line number Diff 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 Down Expand 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
*/
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 } : {})
},
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 number Diff line number Diff 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 Down Expand 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 number Diff line number Diff 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