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
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Standard dev commands are documented in `CONTRIBUTING.md` and `package.json`. Ke
- **Build deps only:** `yarn build:deps` (builds shared packages: schemas, db, shared, query-language)
- **DB migrations:** `yarn dev:prisma:migrate:dev`

### Deprecated Packages

- **`packages/mcp`** - This standalone MCP package is deprecated. Do NOT modify it. MCP functionality is now handled by the web package at `packages/web/src/features/mcp/`.

### Non-obvious Caveats

- **Docker must be running** before `yarn dev`. Start it with `docker compose -f docker-compose-dev.yml up -d`. The backend will fail to connect to Redis/PostgreSQL otherwise.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed
- Increased `SOURCEBOT_CHAT_MAX_STEP_COUNT` default from 20 to 100 to allow agents to perform more autonomous steps. [#1017](https://github.com/sourcebot-dev/sourcebot/pull/1017)
- The `ask_codebase` MCP tool is now hidden when no language model providers are configured. [#1018](https://github.com/sourcebot-dev/sourcebot/pull/1018)

## [4.15.9] - 2026-03-17

Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/api/(server)/mcp/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
},
});

const mcpServer = createMcpServer();
const mcpServer = await createMcpServer();
await mcpServer.connect(transport);

return transport.handleRequest(request);
Expand Down
77 changes: 41 additions & 36 deletions packages/web/src/features/mcp/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { listRepos } from '@/app/api/(server)/repos/listReposApi';
import { getConfiguredLanguageModelsInfo } from "../chat/utils.server";
import { getConfiguredLanguageModels, getConfiguredLanguageModelsInfo } from "../chat/utils.server";
import { askCodebase } from '@/features/mcp/askCodebase';
import {
languageModelInfoSchema,
Expand Down Expand Up @@ -66,12 +66,15 @@ const TOOL_DESCRIPTIONS = {
`,
};

export function createMcpServer(): McpServer {
export async function createMcpServer(): Promise<McpServer> {
const server = new McpServer({
name: 'sourcebot-mcp-server',
version: SOURCEBOT_VERSION,
});

const configuredModels = await getConfiguredLanguageModels();
const hasLanguageModels = configuredModels.length > 0;

server.registerTool(
"search_code",
{
Expand Down Expand Up @@ -493,43 +496,45 @@ export function createMcpServer(): McpServer {
}
);

server.registerTool(
"ask_codebase",
{
description: TOOL_DESCRIPTIONS.ask_codebase,
annotations: { readOnlyHint: true },
inputSchema: z.object({
query: z.string().describe("The query to ask about the codebase."),
repos: z.array(z.string()).optional().describe("The repositories accessible to the agent. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema.optional().describe("The language model to use. If not provided, defaults to the first model in the config."),
visibility: z.enum(['PRIVATE', 'PUBLIC']).optional().describe("The visibility of the chat session. Defaults to PRIVATE for authenticated users."),
}),
},
async (request) => {
const result = await askCodebase({
query: request.query,
repos: request.repos,
languageModel: request.languageModel,
visibility: request.visibility as ChatVisibility | undefined,
source: 'mcp',
});
if (hasLanguageModels) {
server.registerTool(
"ask_codebase",
{
description: TOOL_DESCRIPTIONS.ask_codebase,
annotations: { readOnlyHint: true },
inputSchema: z.object({
query: z.string().describe("The query to ask about the codebase."),
repos: z.array(z.string()).optional().describe("The repositories accessible to the agent. If not provided, all repositories are accessible."),
languageModel: languageModelInfoSchema.optional().describe("The language model to use. If not provided, defaults to the first model in the config."),
visibility: z.enum(['PRIVATE', 'PUBLIC']).optional().describe("The visibility of the chat session. Defaults to PRIVATE for authenticated users."),
}),
},
async (request) => {
const result = await askCodebase({
query: request.query,
repos: request.repos,
languageModel: request.languageModel,
visibility: request.visibility as ChatVisibility | undefined,
source: 'mcp',
});

if (isServiceError(result)) {
return {
content: [{ type: "text", text: `Failed to ask codebase: ${result.message}` }],
};
}
if (isServiceError(result)) {
return {
content: [{ type: "text", text: `Failed to ask codebase: ${result.message}` }],
};
}

const formattedResponse = dedent`
${result.answer}
const formattedResponse = dedent`
${result.answer}

---
**View full research session:** ${result.chatUrl}
**Model used:** ${result.languageModel.model}
`;
return { content: [{ type: "text", text: formattedResponse }] };
}
);
---
**View full research session:** ${result.chatUrl}
**Model used:** ${result.languageModel.model}
`;
return { content: [{ type: "text", text: formattedResponse }] };
}
);
}

return server;
}
Loading