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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
11 changes: 11 additions & 0 deletions docs/docs/features/mcp-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/features/chat/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
</selected_repositories>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -289,6 +290,15 @@ export const StepPartRenderer = ({ part }: { part: SBChatMessagePart }) => {
{(output) => <ListCommitsToolComponent {...output} />}
</ToolOutputGuard>
)
case 'tool-get_diff':
return (
<ToolOutputGuard
part={part}
loadingText="Comparing revisions..."
>
{(output) => <GetDiffToolComponent {...output} />}
</ToolOutputGuard>
)
case 'tool-list_tree':
return (
<ToolOutputGuard
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client';

import { Separator } from '@/components/ui/separator';
import { GetDiffMetadata, ToolResult } from '@/features/tools';

export const GetDiffToolComponent = ({ metadata }: ToolResult<GetDiffMetadata>) => {
const fileCount = metadata.files.length;

return (
<div className="flex items-center gap-2 select-none cursor-default text-sm text-muted-foreground">
<span className="flex-shrink-0">Compared</span>
<span className="text-xs font-mono bg-muted px-1.5 py-0.5 rounded text-foreground">
{metadata.base}
</span>
<span className="flex-shrink-0">to</span>
<span className="text-xs font-mono bg-muted px-1.5 py-0.5 rounded text-foreground">
{metadata.head}
</span>
<span className="flex-shrink-0">in</span>
<span className="text-xs font-mono bg-muted px-1.5 py-0.5 rounded text-foreground truncate">
{metadata.repo}
</span>
<span className="flex-1" />
<span className="text-xs flex-shrink-0">
{fileCount} changed {fileCount === 1 ? 'file' : 'files'}
</span>
<Separator orientation="vertical" className="h-3 flex-shrink-0" />
</div>
);
};
3 changes: 3 additions & 0 deletions packages/web/src/features/chat/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
readFileDefinition,
listCommitsDefinition,
listReposDefinition,
getDiffDefinition,
grepDefinition,
globDefinition,
findSymbolReferencesDefinition,
Expand All @@ -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),
Expand All @@ -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'] }>;
Expand Down
2 changes: 2 additions & 0 deletions packages/web/src/features/mcp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getConfiguredLanguageModelsInfo } from "../chat/utils.server";
import {
findSymbolDefinitionsDefinition,
findSymbolReferencesDefinition,
getDiffDefinition,
listCommitsDefinition,
listReposDefinition,
listTreeDefinition,
Expand Down Expand Up @@ -40,6 +41,7 @@ export async function createMcpServer(): Promise<McpServer> {

registerMcpTool(server, grepDefinition, toolContext);
registerMcpTool(server, globDefinition, toolContext);
registerMcpTool(server, getDiffDefinition, toolContext);
registerMcpTool(server, listCommitsDefinition, toolContext);
registerMcpTool(server, listReposDefinition, toolContext);
registerMcpTool(server, readFileDefinition, toolContext);
Expand Down
40 changes: 40 additions & 0 deletions packages/web/src/features/tools/getDiff.ts
Original file line number Diff line number Diff line change
@@ -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,
},
};
},
};
4 changes: 4 additions & 0 deletions packages/web/src/features/tools/getDiff.txt
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions packages/web/src/features/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Loading