Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
74 changes: 58 additions & 16 deletions docs/docs/features/mcp-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -147,32 +147,74 @@ 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). |


### `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. |


## Environment Variables
Expand Down
14 changes: 14 additions & 0 deletions packages/mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,20 @@ Get a list of commits for a given repository.

</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. |

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


## Supported Code Hosts
Sourcebot supports the following code hosts:
Expand Down
27 changes: 24 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 } from './schemas.js';
import { AskCodebaseRequest, AskCodebaseResponse, FileSourceRequest, ListReposQueryParams, SearchRequest, ListCommitsQueryParamsSchema } from './types.js';
import { isServiceError, ServiceErrorException } from './utils.js';
import { z } from 'zod';

Expand Down Expand Up @@ -106,4 +106,25 @@ 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);
}
39 changes: 36 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, 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,40 @@ server.tool(
}
);

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}
`;

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

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

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."),
});

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"),
});
5 changes: 5 additions & 0 deletions packages/mcp/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
serviceErrorSchema,
listCommitsQueryParamsSchema,
listCommitsResponseSchema,
askCodebaseRequestSchema,
askCodebaseResponseSchema,
} from "./schemas.js";
import { z } from "zod";

Expand All @@ -34,3 +36,6 @@ export type ServiceError = z.infer<typeof serviceErrorSchema>;

export type ListCommitsQueryParamsSchema = z.infer<typeof listCommitsQueryParamsSchema>;
export type ListCommitsResponse = z.infer<typeof listCommitsResponseSchema>;

export type AskCodebaseRequest = z.infer<typeof askCodebaseRequestSchema>;
export type AskCodebaseResponse = z.infer<typeof askCodebaseResponseSchema>;
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
"codemirror-lang-spreadsheet": "^1.3.0",
"codemirror-lang-zig": "^0.1.0",
"date-fns": "^4.1.0",
"dedent": "^1.7.1",
"embla-carousel-auto-scroll": "^8.3.0",
"embla-carousel-react": "^8.3.0",
"escape-string-regexp": "^5.0.0",
Expand Down
Loading