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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- Require explicit invokation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995)
- Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)

## [4.15.4] - 2026-03-11
Expand Down
67 changes: 43 additions & 24 deletions packages/web/src/features/mcp/server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,42 @@ const MAX_TREE_DEPTH = 10;
const DEFAULT_MAX_TREE_ENTRIES = 1000;
const MAX_MAX_TREE_ENTRIES = 10000;

const TOOL_DESCRIPTIONS = {
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.
`,
list_commits: dedent`Get a list of commits for a given repository.`,
list_repos: dedent`Lists repositories in the organization with optional filtering and pagination.`,
read_file: dedent`Reads the source code for a given file.`,
list_tree: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
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.`,
ask_codebase: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.

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.

This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.

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.

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

export function createMcpServer(): McpServer {
const server = new McpServer({
name: 'sourcebot-mcp-server',
Expand All@@ -39,8 +75,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"search_code",
{
description: 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.`,
description: TOOL_DESCRIPTIONS.search_code,
inputSchema: {
query: z
.string()
Expand DownExpand Up@@ -194,7 +229,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_commits",
{
description: dedent`Get a list of commits for a given repository.`,
description: TOOL_DESCRIPTIONS.list_commits,
inputSchema: z.object({
repo: z.string().describe("The name of the repository to list commits for."),
query: z.string().describe("Search query to filter commits by message content (case-insensitive).").optional(),
Expand DownExpand Up@@ -232,7 +267,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_repos",
{
description: dedent`Lists repositories in the organization with optional filtering and pagination.`,
description: TOOL_DESCRIPTIONS.list_repos,
inputSchema: z.object({
query: z.string().describe("Filter repositories by name (case-insensitive)").optional(),
page: z.number().int().positive().describe("Page number for pagination (min 1). Default: 1").optional().default(1),
Expand DownExpand Up@@ -272,7 +307,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"read_file",
{
description: dedent`Reads the source code for a given file.`,
description: TOOL_DESCRIPTIONS.read_file,
inputSchema: {
repo: z.string().describe("The repository name."),
path: z.string().describe("The path to the file."),
Expand DownExpand Up@@ -305,10 +340,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_tree",
{
description: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
description: TOOL_DESCRIPTIONS.list_tree,
inputSchema: {
repo: z.string().describe("The name of the repository to list files from."),
path: z.string().describe("Directory path (relative to repo root). If omitted, the repo root is used.").optional().default(''),
Expand DownExpand Up@@ -447,7 +479,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
description: TOOL_DESCRIPTIONS.list_language_models,
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
Expand All@@ -458,20 +490,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"ask_codebase",
{
description: 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.

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.
`,
description: TOOL_DESCRIPTIONS.ask_codebase,
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."),
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- Require explicit invokation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995)
- Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)

## [4.15.4] - 2026-03-11
Expand Down
67 changes: 43 additions & 24 deletions packages/web/src/features/mcp/server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,42 @@ const MAX_TREE_DEPTH = 10;
const DEFAULT_MAX_TREE_ENTRIES = 1000;
const MAX_MAX_TREE_ENTRIES = 10000;

const TOOL_DESCRIPTIONS = {
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.
`,
list_commits: dedent`Get a list of commits for a given repository.`,
list_repos: dedent`Lists repositories in the organization with optional filtering and pagination.`,
read_file: dedent`Reads the source code for a given file.`,
list_tree: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
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.`,
ask_codebase: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.

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.

This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.

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.

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

export function createMcpServer(): McpServer {
const server = new McpServer({
name: 'sourcebot-mcp-server',
Expand All@@ -39,8 +75,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"search_code",
{
description: 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.`,
description: TOOL_DESCRIPTIONS.search_code,
inputSchema: {
query: z
.string()
Expand DownExpand Up@@ -194,7 +229,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_commits",
{
description: dedent`Get a list of commits for a given repository.`,
description: TOOL_DESCRIPTIONS.list_commits,
inputSchema: z.object({
repo: z.string().describe("The name of the repository to list commits for."),
query: z.string().describe("Search query to filter commits by message content (case-insensitive).").optional(),
Expand DownExpand Up@@ -232,7 +267,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_repos",
{
description: dedent`Lists repositories in the organization with optional filtering and pagination.`,
description: TOOL_DESCRIPTIONS.list_repos,
inputSchema: z.object({
query: z.string().describe("Filter repositories by name (case-insensitive)").optional(),
page: z.number().int().positive().describe("Page number for pagination (min 1). Default: 1").optional().default(1),
Expand DownExpand Up@@ -272,7 +307,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"read_file",
{
description: dedent`Reads the source code for a given file.`,
description: TOOL_DESCRIPTIONS.read_file,
inputSchema: {
repo: z.string().describe("The repository name."),
path: z.string().describe("The path to the file."),
Expand DownExpand Up@@ -305,10 +340,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_tree",
{
description: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
description: TOOL_DESCRIPTIONS.list_tree,
inputSchema: {
repo: z.string().describe("The name of the repository to list files from."),
path: z.string().describe("Directory path (relative to repo root). If omitted, the repo root is used.").optional().default(''),
Expand DownExpand Up@@ -447,7 +479,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
description: TOOL_DESCRIPTIONS.list_language_models,
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
Expand All@@ -458,20 +490,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"ask_codebase",
{
description: 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.

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.
`,
description: TOOL_DESCRIPTIONS.ask_codebase,
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."),
Expand Down
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- Require explicit invokation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995)
- Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)

## [4.15.4] - 2026-03-11
Expand Down
67 changes: 43 additions & 24 deletions packages/web/src/features/mcp/server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,42 @@ const MAX_TREE_DEPTH = 10;
const DEFAULT_MAX_TREE_ENTRIES = 1000;
const MAX_MAX_TREE_ENTRIES = 10000;

const TOOL_DESCRIPTIONS = {
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.
`,
list_commits: dedent`Get a list of commits for a given repository.`,
list_repos: dedent`Lists repositories in the organization with optional filtering and pagination.`,
read_file: dedent`Reads the source code for a given file.`,
list_tree: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
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.`,
ask_codebase: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.

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.

This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.

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.

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

export function createMcpServer(): McpServer {
const server = new McpServer({
name: 'sourcebot-mcp-server',
Expand All@@ -39,8 +75,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"search_code",
{
description: 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.`,
description: TOOL_DESCRIPTIONS.search_code,
inputSchema: {
query: z
.string()
Expand DownExpand Up@@ -194,7 +229,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_commits",
{
description: dedent`Get a list of commits for a given repository.`,
description: TOOL_DESCRIPTIONS.list_commits,
inputSchema: z.object({
repo: z.string().describe("The name of the repository to list commits for."),
query: z.string().describe("Search query to filter commits by message content (case-insensitive).").optional(),
Expand DownExpand Up@@ -232,7 +267,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_repos",
{
description: dedent`Lists repositories in the organization with optional filtering and pagination.`,
description: TOOL_DESCRIPTIONS.list_repos,
inputSchema: z.object({
query: z.string().describe("Filter repositories by name (case-insensitive)").optional(),
page: z.number().int().positive().describe("Page number for pagination (min 1). Default: 1").optional().default(1),
Expand DownExpand Up@@ -272,7 +307,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"read_file",
{
description: dedent`Reads the source code for a given file.`,
description: TOOL_DESCRIPTIONS.read_file,
inputSchema: {
repo: z.string().describe("The repository name."),
path: z.string().describe("The path to the file."),
Expand DownExpand Up@@ -305,10 +340,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_tree",
{
description: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
description: TOOL_DESCRIPTIONS.list_tree,
inputSchema: {
repo: z.string().describe("The name of the repository to list files from."),
path: z.string().describe("Directory path (relative to repo root). If omitted, the repo root is used.").optional().default(''),
Expand DownExpand Up@@ -447,7 +479,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
description: TOOL_DESCRIPTIONS.list_language_models,
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
Expand All@@ -458,20 +490,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"ask_codebase",
{
description: 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.

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.
`,
description: TOOL_DESCRIPTIONS.ask_codebase,
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."),
Expand Down
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 \u003e 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- Require explicit invokation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995)
- Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)

## [4.15.4] - 2026-03-11
Expand Down
67 changes: 43 additions & 24 deletions packages/web/src/features/mcp/server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,42 @@ const MAX_TREE_DEPTH = 10;
const DEFAULT_MAX_TREE_ENTRIES = 1000;
const MAX_MAX_TREE_ENTRIES = 10000;

const TOOL_DESCRIPTIONS = {
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.
`,
list_commits: dedent`Get a list of commits for a given repository.`,
list_repos: dedent`Lists repositories in the organization with optional filtering and pagination.`,
read_file: dedent`Reads the source code for a given file.`,
list_tree: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
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.`,
ask_codebase: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.

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.

This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.

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.

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

export function createMcpServer(): McpServer {
const server = new McpServer({
name: 'sourcebot-mcp-server',
Expand All@@ -39,8 +75,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"search_code",
{
description: 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.`,
description: TOOL_DESCRIPTIONS.search_code,
inputSchema: {
query: z
.string()
Expand DownExpand Up@@ -194,7 +229,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_commits",
{
description: dedent`Get a list of commits for a given repository.`,
description: TOOL_DESCRIPTIONS.list_commits,
inputSchema: z.object({
repo: z.string().describe("The name of the repository to list commits for."),
query: z.string().describe("Search query to filter commits by message content (case-insensitive).").optional(),
Expand DownExpand Up@@ -232,7 +267,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_repos",
{
description: dedent`Lists repositories in the organization with optional filtering and pagination.`,
description: TOOL_DESCRIPTIONS.list_repos,
inputSchema: z.object({
query: z.string().describe("Filter repositories by name (case-insensitive)").optional(),
page: z.number().int().positive().describe("Page number for pagination (min 1). Default: 1").optional().default(1),
Expand DownExpand Up@@ -272,7 +307,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"read_file",
{
description: dedent`Reads the source code for a given file.`,
description: TOOL_DESCRIPTIONS.read_file,
inputSchema: {
repo: z.string().describe("The repository name."),
path: z.string().describe("The path to the file."),
Expand DownExpand Up@@ -305,10 +340,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_tree",
{
description: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
description: TOOL_DESCRIPTIONS.list_tree,
inputSchema: {
repo: z.string().describe("The name of the repository to list files from."),
path: z.string().describe("Directory path (relative to repo root). If omitted, the repo root is used.").optional().default(''),
Expand DownExpand Up@@ -447,7 +479,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
description: TOOL_DESCRIPTIONS.list_language_models,
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
Expand All@@ -458,20 +490,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"ask_codebase",
{
description: 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.

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.
`,
description: TOOL_DESCRIPTIONS.ask_codebase,
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."),
Expand Down
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- Require explicit invokation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995)
- Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)

## [4.15.4] - 2026-03-11
Expand Down
67 changes: 43 additions & 24 deletions packages/web/src/features/mcp/server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,42 @@ const MAX_TREE_DEPTH = 10;
const DEFAULT_MAX_TREE_ENTRIES = 1000;
const MAX_MAX_TREE_ENTRIES = 10000;

const TOOL_DESCRIPTIONS = {
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.
`,
list_commits: dedent`Get a list of commits for a given repository.`,
list_repos: dedent`Lists repositories in the organization with optional filtering and pagination.`,
read_file: dedent`Reads the source code for a given file.`,
list_tree: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
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.`,
ask_codebase: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.

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.

This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.

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.

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

export function createMcpServer(): McpServer {
const server = new McpServer({
name: 'sourcebot-mcp-server',
Expand All@@ -39,8 +75,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"search_code",
{
description: 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.`,
description: TOOL_DESCRIPTIONS.search_code,
inputSchema: {
query: z
.string()
Expand DownExpand Up@@ -194,7 +229,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_commits",
{
description: dedent`Get a list of commits for a given repository.`,
description: TOOL_DESCRIPTIONS.list_commits,
inputSchema: z.object({
repo: z.string().describe("The name of the repository to list commits for."),
query: z.string().describe("Search query to filter commits by message content (case-insensitive).").optional(),
Expand DownExpand Up@@ -232,7 +267,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_repos",
{
description: dedent`Lists repositories in the organization with optional filtering and pagination.`,
description: TOOL_DESCRIPTIONS.list_repos,
inputSchema: z.object({
query: z.string().describe("Filter repositories by name (case-insensitive)").optional(),
page: z.number().int().positive().describe("Page number for pagination (min 1). Default: 1").optional().default(1),
Expand DownExpand Up@@ -272,7 +307,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"read_file",
{
description: dedent`Reads the source code for a given file.`,
description: TOOL_DESCRIPTIONS.read_file,
inputSchema: {
repo: z.string().describe("The repository name."),
path: z.string().describe("The path to the file."),
Expand DownExpand Up@@ -305,10 +340,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_tree",
{
description: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
description: TOOL_DESCRIPTIONS.list_tree,
inputSchema: {
repo: z.string().describe("The name of the repository to list files from."),
path: z.string().describe("Directory path (relative to repo root). If omitted, the repo root is used.").optional().default(''),
Expand DownExpand Up@@ -447,7 +479,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
description: TOOL_DESCRIPTIONS.list_language_models,
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
Expand All@@ -458,20 +490,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"ask_codebase",
{
description: 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.

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.
`,
description: TOOL_DESCRIPTIONS.ask_codebase,
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."),
Expand Down
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- Require explicit invokation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995)
- Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)

## [4.15.4] - 2026-03-11
Expand Down
67 changes: 43 additions & 24 deletions packages/web/src/features/mcp/server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,42 @@ const MAX_TREE_DEPTH = 10;
const DEFAULT_MAX_TREE_ENTRIES = 1000;
const MAX_MAX_TREE_ENTRIES = 10000;

const TOOL_DESCRIPTIONS = {
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.
`,
list_commits: dedent`Get a list of commits for a given repository.`,
list_repos: dedent`Lists repositories in the organization with optional filtering and pagination.`,
read_file: dedent`Reads the source code for a given file.`,
list_tree: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
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.`,
ask_codebase: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.

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.

This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.

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.

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

export function createMcpServer(): McpServer {
const server = new McpServer({
name: 'sourcebot-mcp-server',
Expand All@@ -39,8 +75,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"search_code",
{
description: 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.`,
description: TOOL_DESCRIPTIONS.search_code,
inputSchema: {
query: z
.string()
Expand DownExpand Up@@ -194,7 +229,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_commits",
{
description: dedent`Get a list of commits for a given repository.`,
description: TOOL_DESCRIPTIONS.list_commits,
inputSchema: z.object({
repo: z.string().describe("The name of the repository to list commits for."),
query: z.string().describe("Search query to filter commits by message content (case-insensitive).").optional(),
Expand DownExpand Up@@ -232,7 +267,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_repos",
{
description: dedent`Lists repositories in the organization with optional filtering and pagination.`,
description: TOOL_DESCRIPTIONS.list_repos,
inputSchema: z.object({
query: z.string().describe("Filter repositories by name (case-insensitive)").optional(),
page: z.number().int().positive().describe("Page number for pagination (min 1). Default: 1").optional().default(1),
Expand DownExpand Up@@ -272,7 +307,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"read_file",
{
description: dedent`Reads the source code for a given file.`,
description: TOOL_DESCRIPTIONS.read_file,
inputSchema: {
repo: z.string().describe("The repository name."),
path: z.string().describe("The path to the file."),
Expand DownExpand Up@@ -305,10 +340,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_tree",
{
description: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
description: TOOL_DESCRIPTIONS.list_tree,
inputSchema: {
repo: z.string().describe("The name of the repository to list files from."),
path: z.string().describe("Directory path (relative to repo root). If omitted, the repo root is used.").optional().default(''),
Expand DownExpand Up@@ -447,7 +479,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
description: TOOL_DESCRIPTIONS.list_language_models,
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
Expand All@@ -458,20 +490,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"ask_codebase",
{
description: 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.

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.
`,
description: TOOL_DESCRIPTIONS.ask_codebase,
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."),
Expand Down
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- Require explicit invokation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995)
- Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)

## [4.15.4] - 2026-03-11
Expand Down
67 changes: 43 additions & 24 deletions packages/web/src/features/mcp/server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,42 @@ const MAX_TREE_DEPTH = 10;
const DEFAULT_MAX_TREE_ENTRIES = 1000;
const MAX_MAX_TREE_ENTRIES = 10000;

const TOOL_DESCRIPTIONS = {
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.
`,
list_commits: dedent`Get a list of commits for a given repository.`,
list_repos: dedent`Lists repositories in the organization with optional filtering and pagination.`,
read_file: dedent`Reads the source code for a given file.`,
list_tree: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
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.`,
ask_codebase: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.

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.

This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.

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.

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

export function createMcpServer(): McpServer {
const server = new McpServer({
name: 'sourcebot-mcp-server',
Expand All@@ -39,8 +75,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"search_code",
{
description: 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.`,
description: TOOL_DESCRIPTIONS.search_code,
inputSchema: {
query: z
.string()
Expand DownExpand Up@@ -194,7 +229,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_commits",
{
description: dedent`Get a list of commits for a given repository.`,
description: TOOL_DESCRIPTIONS.list_commits,
inputSchema: z.object({
repo: z.string().describe("The name of the repository to list commits for."),
query: z.string().describe("Search query to filter commits by message content (case-insensitive).").optional(),
Expand DownExpand Up@@ -232,7 +267,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_repos",
{
description: dedent`Lists repositories in the organization with optional filtering and pagination.`,
description: TOOL_DESCRIPTIONS.list_repos,
inputSchema: z.object({
query: z.string().describe("Filter repositories by name (case-insensitive)").optional(),
page: z.number().int().positive().describe("Page number for pagination (min 1). Default: 1").optional().default(1),
Expand DownExpand Up@@ -272,7 +307,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"read_file",
{
description: dedent`Reads the source code for a given file.`,
description: TOOL_DESCRIPTIONS.read_file,
inputSchema: {
repo: z.string().describe("The repository name."),
path: z.string().describe("The path to the file."),
Expand DownExpand Up@@ -305,10 +340,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_tree",
{
description: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
description: TOOL_DESCRIPTIONS.list_tree,
inputSchema: {
repo: z.string().describe("The name of the repository to list files from."),
path: z.string().describe("Directory path (relative to repo root). If omitted, the repo root is used.").optional().default(''),
Expand DownExpand Up@@ -447,7 +479,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
description: TOOL_DESCRIPTIONS.list_language_models,
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
Expand All@@ -458,20 +490,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"ask_codebase",
{
description: 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.

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.
`,
description: TOOL_DESCRIPTIONS.ask_codebase,
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."),
Expand Down
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- Require explicit invokation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995)
- Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)

## [4.15.4] - 2026-03-11
Expand Down
67 changes: 43 additions & 24 deletions packages/web/src/features/mcp/server.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,42 @@ const MAX_TREE_DEPTH = 10;
const DEFAULT_MAX_TREE_ENTRIES = 1000;
const MAX_MAX_TREE_ENTRIES = 10000;

const TOOL_DESCRIPTIONS = {
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.
`,
list_commits: dedent`Get a list of commits for a given repository.`,
list_repos: dedent`Lists repositories in the organization with optional filtering and pagination.`,
read_file: dedent`Reads the source code for a given file.`,
list_tree: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
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.`,
ask_codebase: dedent`
DO NOT USE THIS TOOL UNLESS EXPLICITLY ASKED TO. THE PROMPT MUST SPECIFICALLY ASK TO USE THE ask_codebase TOOL.

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.

This is a blocking operation that may take 60+ seconds to research the codebase, so only invoke it if the user has explicitly asked you to by specifying the ask_codebase tool call in the prompt.

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.

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

export function createMcpServer(): McpServer {
const server = new McpServer({
name: 'sourcebot-mcp-server',
Expand All@@ -39,8 +75,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"search_code",
{
description: 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.`,
description: TOOL_DESCRIPTIONS.search_code,
inputSchema: {
query: z
.string()
Expand DownExpand Up@@ -194,7 +229,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_commits",
{
description: dedent`Get a list of commits for a given repository.`,
description: TOOL_DESCRIPTIONS.list_commits,
inputSchema: z.object({
repo: z.string().describe("The name of the repository to list commits for."),
query: z.string().describe("Search query to filter commits by message content (case-insensitive).").optional(),
Expand DownExpand Up@@ -232,7 +267,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_repos",
{
description: dedent`Lists repositories in the organization with optional filtering and pagination.`,
description: TOOL_DESCRIPTIONS.list_repos,
inputSchema: z.object({
query: z.string().describe("Filter repositories by name (case-insensitive)").optional(),
page: z.number().int().positive().describe("Page number for pagination (min 1). Default: 1").optional().default(1),
Expand DownExpand Up@@ -272,7 +307,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"read_file",
{
description: dedent`Reads the source code for a given file.`,
description: TOOL_DESCRIPTIONS.read_file,
inputSchema: {
repo: z.string().describe("The repository name."),
path: z.string().describe("The path to the file."),
Expand DownExpand Up@@ -305,10 +340,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_tree",
{
description: dedent`
Lists files and directories from a repository path. This can be used as a repo tree tool or directory listing tool.
Returns a flat list of entries with path metadata and depth relative to the requested path.
`,
description: TOOL_DESCRIPTIONS.list_tree,
inputSchema: {
repo: z.string().describe("The name of the repository to list files from."),
path: z.string().describe("Directory path (relative to repo root). If omitted, the repo root is used.").optional().default(''),
Expand DownExpand Up@@ -447,7 +479,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"list_language_models",
{
description: dedent`Lists the available language models configured on the Sourcebot instance. Use this to discover which models can be specified when calling ask_codebase.`,
description: TOOL_DESCRIPTIONS.list_language_models,
},
async () => {
const models = await getConfiguredLanguageModelsInfo();
Expand All@@ -458,20 +490,7 @@ export function createMcpServer(): McpServer {
server.registerTool(
"ask_codebase",
{
description: 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.

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.
`,
description: TOOL_DESCRIPTIONS.ask_codebase,
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."),
Expand Down
Loading