From a23c35a1b1a01d5723aa273ae440683a717c87eb Mon Sep 17 00:00:00 2001 From: msukkari Date: Tue, 21 Apr 2026 10:22:49 -0700 Subject: [PATCH 1/3] feat(web): expose get_diff on MCP server Add a new get_diff MCP tool backed by the existing git diff API so agents can request structured diffs between refs. Document the tool in MCP docs with parameters aligned to the public API spec. Made-with: Cursor --- docs/docs/features/mcp-server.mdx | 11 ++++++ packages/web/src/features/mcp/server.ts | 2 ++ packages/web/src/features/tools/getDiff.ts | 40 +++++++++++++++++++++ packages/web/src/features/tools/getDiff.txt | 4 +++ packages/web/src/features/tools/index.ts | 1 + 5 files changed, 58 insertions(+) create mode 100644 packages/web/src/features/tools/getDiff.ts create mode 100644 packages/web/src/features/tools/getDiff.txt diff --git a/docs/docs/features/mcp-server.mdx b/docs/docs/features/mcp-server.mdx index 4a97b3bb2..50adc6b02 100644 --- a/docs/docs/features/mcp-server.mdx +++ b/docs/docs/features/mcp-server.mdx @@ -388,6 +388,17 @@ Parameters: | `ref` | no | Commit SHA, branch or tag name to search on. If not provided, defaults to the default branch. | | `limit` | no | Maximum number of files to return (default: 100). | +### `get_diff` + +Returns a structured diff between two refs in a repository using a two-dot comparison. + +Parameters: +| Name | Required | Description | +|:----------|:---------|:--------------------------------------------------------------------------------------------------------------| +| `repo` | yes | The fully-qualified repository name. | +| `base` | yes | The base git ref (branch, tag, or commit SHA) to diff from. | +| `head` | yes | The head git ref (branch, tag, or commit SHA) to diff to. | + ### `find_symbol_definitions` Finds where a symbol (function, class, variable, etc.) is defined in a repository. diff --git a/packages/web/src/features/mcp/server.ts b/packages/web/src/features/mcp/server.ts index ec4103f36..953953f14 100644 --- a/packages/web/src/features/mcp/server.ts +++ b/packages/web/src/features/mcp/server.ts @@ -13,6 +13,7 @@ import { getConfiguredLanguageModelsInfo } from "../chat/utils.server"; import { findSymbolDefinitionsDefinition, findSymbolReferencesDefinition, + getDiffDefinition, listCommitsDefinition, listReposDefinition, listTreeDefinition, @@ -40,6 +41,7 @@ export async function createMcpServer(): Promise { registerMcpTool(server, grepDefinition, toolContext); registerMcpTool(server, globDefinition, toolContext); + registerMcpTool(server, getDiffDefinition, toolContext); registerMcpTool(server, listCommitsDefinition, toolContext); registerMcpTool(server, listReposDefinition, toolContext); registerMcpTool(server, readFileDefinition, toolContext); diff --git a/packages/web/src/features/tools/getDiff.ts b/packages/web/src/features/tools/getDiff.ts new file mode 100644 index 000000000..803a9429d --- /dev/null +++ b/packages/web/src/features/tools/getDiff.ts @@ -0,0 +1,40 @@ +import { getDiff, GetDiffResult } from '@/features/git'; +import { getDiffRequestSchema } from '@/features/git/schemas'; +import { isServiceError } from '@/lib/utils'; +import description from './getDiff.txt'; +import { logger } from './logger'; +import { ToolDefinition } from './types'; + +export type GetDiffMetadata = GetDiffResult & { + repo: string; + base: string; + head: string; +}; + +export const getDiffDefinition: ToolDefinition<'get_diff', typeof getDiffRequestSchema.shape, GetDiffMetadata> = { + name: 'get_diff', + title: 'Get diff', + isReadOnly: true, + isIdempotent: true, + description, + inputSchema: getDiffRequestSchema, + execute: async ({ repo, base, head }, _context) => { + logger.debug('get_diff', { repo, base, head }); + + const response = await getDiff({ repo, base, head }); + + if (isServiceError(response)) { + throw new Error(response.message); + } + + return { + output: JSON.stringify(response), + metadata: { + ...response, + repo, + base, + head, + }, + }; + }, +}; diff --git a/packages/web/src/features/tools/getDiff.txt b/packages/web/src/features/tools/getDiff.txt new file mode 100644 index 000000000..84cea0153 --- /dev/null +++ b/packages/web/src/features/tools/getDiff.txt @@ -0,0 +1,4 @@ +Returns a structured git diff between two refs in a repository. + +Use this tool when you need patch details (added, removed, and context lines) between a base and head revision. +The output includes changed files and hunks with line ranges and diff body content. diff --git a/packages/web/src/features/tools/index.ts b/packages/web/src/features/tools/index.ts index 1a8f04013..04ac7014d 100644 --- a/packages/web/src/features/tools/index.ts +++ b/packages/web/src/features/tools/index.ts @@ -3,6 +3,7 @@ export * from './listCommits'; export * from './listRepos'; export * from './grep'; export * from './glob'; +export * from './getDiff'; export * from './findSymbolReferences'; export * from './findSymbolDefinitions'; export * from './listTree'; From f401c241a7ab4e6140400023da470a78fee54338 Mon Sep 17 00:00:00 2001 From: msukkari Date: Tue, 21 Apr 2026 10:24:12 -0700 Subject: [PATCH 2/3] chore: add changelog entry for MCP get_diff tool Document the new MCP get_diff capability under Unreleased using the repository changelog format and PR link. Made-with: Cursor --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3b0702e0..cfddaf95f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Added a `get_diff` MCP tool to return structured diffs between two git refs. [#1142](https://github.com/sourcebot-dev/sourcebot/pull/1142) + ### Changed - Synced `vendor/zoekt` with upstream `sourcegraph/zoekt`. [#1140](https://github.com/sourcebot-dev/sourcebot/pull/1140) From a7511d7621288488c4bb257568c12e1d13380418 Mon Sep 17 00:00:00 2001 From: msukkari Date: Tue, 21 Apr 2026 10:31:50 -0700 Subject: [PATCH 3/3] feat(chat): expose get_diff tool in Ask agent Register get_diff in the Ask agent toolset and render its output in the details panel so chat workflows can compare refs directly. Made-with: Cursor --- packages/web/src/features/chat/agent.ts | 2 +- .../components/chatThread/detailsCard.tsx | 10 +++++++ .../chatThread/tools/getDiffToolComponent.tsx | 30 +++++++++++++++++++ packages/web/src/features/chat/tools.ts | 3 ++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/features/chat/components/chatThread/tools/getDiffToolComponent.tsx diff --git a/packages/web/src/features/chat/agent.ts b/packages/web/src/features/chat/agent.ts index 784ca5344..0efb706fc 100644 --- a/packages/web/src/features/chat/agent.ts +++ b/packages/web/src/features/chat/agent.ts @@ -271,7 +271,7 @@ const createPrompt = ({ The user has explicitly selected the following repositories for analysis: ${repos.map(repo => `- ${repo}`).join('\n')} - When calling tools that accept a \`repo\` parameter (e.g. \`read_file\`, \`list_commits\`, \`list_tree\`, \`grep\`), use these repository names exactly as listed above, including the full host prefix (e.g. \`github.com/org/repo\`). + When calling tools that accept a \`repo\` parameter (e.g. \`read_file\`, \`list_commits\`, \`list_tree\`, \`get_diff\`, \`grep\`), use these repository names exactly as listed above, including the full host prefix (e.g. \`github.com/org/repo\`). When using \`grep\` to search across ALL selected repositories (e.g. "which repos have X?"), omit the \`repo\` parameter entirely — the tool will automatically search across all selected repositories in a single call. Do NOT call \`grep\` once per repository when a single broad search would suffice. diff --git a/packages/web/src/features/chat/components/chatThread/detailsCard.tsx b/packages/web/src/features/chat/components/chatThread/detailsCard.tsx index 72ec22b8c..0e2365ea6 100644 --- a/packages/web/src/features/chat/components/chatThread/detailsCard.tsx +++ b/packages/web/src/features/chat/components/chatThread/detailsCard.tsx @@ -19,6 +19,7 @@ import { FindSymbolDefinitionsToolComponent } from './tools/findSymbolDefinition import { FindSymbolReferencesToolComponent } from './tools/findSymbolReferencesToolComponent'; import { GlobToolComponent } from './tools/globToolComponent'; import { GrepToolComponent } from './tools/grepToolComponent'; +import { GetDiffToolComponent } from './tools/getDiffToolComponent'; import { ListCommitsToolComponent } from './tools/listCommitsToolComponent'; import { ListReposToolComponent } from './tools/listReposToolComponent'; import { ListTreeToolComponent } from './tools/listTreeToolComponent'; @@ -289,6 +290,15 @@ export const StepPartRenderer = ({ part }: { part: SBChatMessagePart }) => { {(output) => } ) + case 'tool-get_diff': + return ( + + {(output) => } + + ) case 'tool-list_tree': return ( ) => { + const fileCount = metadata.files.length; + + return ( +
+ Compared + + {metadata.base} + + to + + {metadata.head} + + in + + {metadata.repo} + + + + {fileCount} changed {fileCount === 1 ? 'file' : 'files'} + + +
+ ); +}; diff --git a/packages/web/src/features/chat/tools.ts b/packages/web/src/features/chat/tools.ts index 37ad7e0a8..149748bff 100644 --- a/packages/web/src/features/chat/tools.ts +++ b/packages/web/src/features/chat/tools.ts @@ -3,6 +3,7 @@ import { readFileDefinition, listCommitsDefinition, listReposDefinition, + getDiffDefinition, grepDefinition, globDefinition, findSymbolReferencesDefinition, @@ -17,6 +18,7 @@ export const createTools = (context: ToolContext) => ({ [readFileDefinition.name]: toVercelAITool(readFileDefinition, context), [listCommitsDefinition.name]: toVercelAITool(listCommitsDefinition, context), [listReposDefinition.name]: toVercelAITool(listReposDefinition, context), + [getDiffDefinition.name]: toVercelAITool(getDiffDefinition, context), [grepDefinition.name]: toVercelAITool(grepDefinition, context), [globDefinition.name]: toVercelAITool(globDefinition, context), [findSymbolReferencesDefinition.name]: toVercelAITool(findSymbolReferencesDefinition, context), @@ -27,6 +29,7 @@ export const createTools = (context: ToolContext) => ({ export type ReadFileToolUIPart = ToolUIPart<{ read_file: SBChatMessageToolTypes['read_file'] }>; export type ListCommitsToolUIPart = ToolUIPart<{ list_commits: SBChatMessageToolTypes['list_commits'] }>; export type ListReposToolUIPart = ToolUIPart<{ list_repos: SBChatMessageToolTypes['list_repos'] }>; +export type GetDiffToolUIPart = ToolUIPart<{ get_diff: SBChatMessageToolTypes['get_diff'] }>; export type GrepToolUIPart = ToolUIPart<{ grep: SBChatMessageToolTypes['grep'] }>; export type GlobToolUIPart = ToolUIPart<{ glob: SBChatMessageToolTypes['glob'] }>; export type FindSymbolReferencesToolUIPart = ToolUIPart<{ find_symbol_references: SBChatMessageToolTypes['find_symbol_references'] }>;