From c6b4ef1ae6b8096da7df270f1526f9748d2e2c34 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 20 Mar 2026 02:20:26 +0000 Subject: [PATCH 1/4] feat(mcp): hide ask_codebase tool when no LLM providers configured - Modified web MCP server to check configured language models before registering the ask_codebase tool - Made createMcpServer() async to support awaiting the config check - Updated standalone MCP package to check /api/models before registering ask_codebase tool - Tool is now only visible when at least one language model is configured Co-authored-by: Michael Sukkarieh --- packages/mcp/src/index.ts | 82 ++++++++++--------- .../web/src/app/api/(server)/mcp/route.ts | 2 +- packages/web/src/features/mcp/server.ts | 77 +++++++++-------- 3 files changed, 86 insertions(+), 75 deletions(-) diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 866739966..51c9abe4b 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -14,14 +14,8 @@ import { buildTreeNodeIndex, joinTreePath, normalizeTreePath, sortTreeEntries } const dedent = _dedent.withOptions({ alignValues: true }); -// Create MCP server -const server = new McpServer({ - name: 'sourcebot-mcp-server', - version: '0.1.0', -}); - - -server.tool( +function registerTools(server: McpServer, hasLanguageModels: boolean) { + server.tool( "search_code", dedent` Searches for code that matches the provided search query as a substring by default, or as a regular expression if useRegex is true. Useful for exploring remote repositories by searching for exact symbols, functions, variables, or specific code patterns. To determine if a repository is indexed, use the \`list_repos\` tool. By default, searches are global and will search the default branch of all repositories. Searches can be scoped to specific repositories, languages, and branches. When referencing code outputted by this tool, always include the file's external URL as a link. This makes it easier for the user to view the file, even if they don't have it locally checked out. @@ -413,46 +407,58 @@ 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. + if (hasLanguageModels) { + 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 + 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. + 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. - When using this in shared environments (e.g., Slack), you can set the visibility parameter to 'PUBLIC' to ensure everyone can access the chat link. + When using this in shared environments (e.g., Slack), you can set the visibility parameter to 'PUBLIC' to ensure everyone can access the chat link. - This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase. - `, - askCodebaseRequestSchema.shape, - { readOnlyHint: true }, - async (request: AskCodebaseRequest) => { - const response = await askCodebase(request); + This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase. + `, + askCodebaseRequestSchema.shape, + { readOnlyHint: true }, + 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} + // 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} - `; + --- + **View full research session:** ${response.chatUrl} + **Model used:** ${response.languageModel.model} + `; - return { - content: [{ - type: "text", - text: formattedResponse, - }], - }; + return { + content: [{ + type: "text", + text: formattedResponse, + }], + }; + } + ); } -); +} const runServer = async () => { + const server = new McpServer({ + name: 'sourcebot-mcp-server', + version: '0.1.0', + }); + + const models = await listLanguageModels(); + const hasLanguageModels = models.length > 0; + registerTools(server, hasLanguageModels); + const transport = new StdioServerTransport(); await server.connect(transport); } diff --git a/packages/web/src/app/api/(server)/mcp/route.ts b/packages/web/src/app/api/(server)/mcp/route.ts index 271aff163..5db39707c 100644 --- a/packages/web/src/app/api/(server)/mcp/route.ts +++ b/packages/web/src/app/api/(server)/mcp/route.ts @@ -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); diff --git a/packages/web/src/features/mcp/server.ts b/packages/web/src/features/mcp/server.ts index 3fe3c50f7..4353de0f4 100644 --- a/packages/web/src/features/mcp/server.ts +++ b/packages/web/src/features/mcp/server.ts @@ -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, @@ -66,12 +66,15 @@ const TOOL_DESCRIPTIONS = { `, }; -export function createMcpServer(): McpServer { +export async function createMcpServer(): Promise { const server = new McpServer({ name: 'sourcebot-mcp-server', version: SOURCEBOT_VERSION, }); + const configuredModels = await getConfiguredLanguageModels(); + const hasLanguageModels = configuredModels.length > 0; + server.registerTool( "search_code", { @@ -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; } From 5aa8e8a36f40028d7da52ff27d4967e30ec4446d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 20 Mar 2026 02:21:00 +0000 Subject: [PATCH 2/4] docs: add changelog entry for ask_codebase visibility change Co-authored-by: Michael Sukkarieh --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8681921d..57f75fbd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,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 From 59e133cb38ca1bc2908060ef2fd480f82b4c04c9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 20 Mar 2026 16:32:25 +0000 Subject: [PATCH 3/4] revert: remove changes to deprecated packages/mcp The packages/mcp package is deprecated and should not be modified. Reverted the conditional ask_codebase registration changes from this package. Co-authored-by: Michael Sukkarieh --- packages/mcp/src/index.ts | 82 ++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 51c9abe4b..866739966 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -14,8 +14,14 @@ import { buildTreeNodeIndex, joinTreePath, normalizeTreePath, sortTreeEntries } const dedent = _dedent.withOptions({ alignValues: true }); -function registerTools(server: McpServer, hasLanguageModels: boolean) { - server.tool( +// Create MCP server +const server = new McpServer({ + name: 'sourcebot-mcp-server', + version: '0.1.0', +}); + + +server.tool( "search_code", dedent` Searches for code that matches the provided search query as a substring by default, or as a regular expression if useRegex is true. Useful for exploring remote repositories by searching for exact symbols, functions, variables, or specific code patterns. To determine if a repository is indexed, use the \`list_repos\` tool. By default, searches are global and will search the default branch of all repositories. Searches can be scoped to specific repositories, languages, and branches. When referencing code outputted by this tool, always include the file's external URL as a link. This makes it easier for the user to view the file, even if they don't have it locally checked out. @@ -407,58 +413,46 @@ server.tool( } ); - if (hasLanguageModels) { - 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. +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 + 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. + 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. - When using this in shared environments (e.g., Slack), you can set the visibility parameter to 'PUBLIC' to ensure everyone can access the chat link. + When using this in shared environments (e.g., Slack), you can set the visibility parameter to 'PUBLIC' to ensure everyone can access the chat link. - This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase. - `, - askCodebaseRequestSchema.shape, - { readOnlyHint: true }, - async (request: AskCodebaseRequest) => { - const response = await askCodebase(request); + This is a blocking operation that may take 30-60+ seconds for complex questions as the agent researches the codebase. + `, + askCodebaseRequestSchema.shape, + { readOnlyHint: true }, + 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} + // 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} - `; + --- + **View full research session:** ${response.chatUrl} + **Model used:** ${response.languageModel.model} + `; - return { - content: [{ - type: "text", - text: formattedResponse, - }], - }; - } - ); + return { + content: [{ + type: "text", + text: formattedResponse, + }], + }; } -} +); const runServer = async () => { - const server = new McpServer({ - name: 'sourcebot-mcp-server', - version: '0.1.0', - }); - - const models = await listLanguageModels(); - const hasLanguageModels = models.length > 0; - registerTools(server, hasLanguageModels); - const transport = new StdioServerTransport(); await server.connect(transport); } From d42d3ab37e4a2e9199b8102d79a65cd6b5fce183 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 20 Mar 2026 16:32:54 +0000 Subject: [PATCH 4/4] docs: add note about deprecated packages/mcp to AGENTS.md Co-authored-by: Michael Sukkarieh --- AGENTS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 86d978856..49fa70b32 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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.