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 packages/mcp/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 pagination and filtering to `list_repos` tool to handle large repository lists efficiently and prevent oversized responses that waste token context. [#614](https://github.com/sourcebot-dev/sourcebot/pull/614)

## [1.0.8] - 2025-11-10

### Fixed
Expand Down
13 changes: 12 additions & 1 deletion packages/mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,18 @@ Fetches code that matches the provided regex pattern in `query`.

### list_repos

Lists all repositories indexed by Sourcebot.
Lists repositories indexed by Sourcebot with optional filtering and pagination.

<details>
<summary>Parameters</summary>

| Name | Required | Description |
|:-------------|:---------|:--------------------------------------------------------------------|
| `query` | no | Filter repositories by name (case-insensitive). |
| `pageNumber` | no | Page number (1-indexed, default: 1). |
| `limit` | no | Number of repositories per page (default: 50). |

</details>

### get_file_source

Expand Down
44 changes: 41 additions & 3 deletions packages/mcp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import escapeStringRegexp from 'escape-string-regexp';
import { z } from 'zod';
import { listRepos, search, getFileSource } from './client.js';
import { env, numberSchema } from './env.js';
import { listReposRequestSchema } from './schemas.js';
import { TextContent } from './types.js';
import { isServiceError } from './utils.js';

Expand Down Expand Up @@ -165,8 +166,13 @@ server.tool(

server.tool(
"list_repos",
"Lists all repositories in the organization. If you receive an error that indicates that you're not authenticated, please inform the user to set the SOURCEBOT_API_KEY environment variable.",
async () => {
"Lists repositories in the organization with optional filtering and pagination. If you receive an error that indicates that you're not authenticated, please inform the user to set the SOURCEBOT_API_KEY environment variable.",
listReposRequestSchema.shape,
async ({ query, pageNumber = 1, limit = 50 }: {
query?: string;
pageNumber?: number;
limit?: number;
}) => {
const response = await listRepos();
if (isServiceError(response)) {
return {
Expand All @@ -177,13 +183,45 @@ server.tool(
};
}

const content: TextContent[] = response.map(repo => {
// Apply query filter if provided
let filtered = response;
if (query) {
const lowerQuery = query.toLowerCase();
filtered = response.filter(repo =>
repo.repoName.toLowerCase().includes(lowerQuery) ||
repo.repoDisplayName?.toLowerCase().includes(lowerQuery)
);
}

// Sort alphabetically for consistent pagination
filtered.sort((a, b) => a.repoName.localeCompare(b.repoName));

// Apply pagination
const startIndex = (pageNumber - 1) * limit;
const endIndex = startIndex + limit;
const paginated = filtered.slice(startIndex, endIndex);

// Format output
const content: TextContent[] = paginated.map(repo => {
return {
type: "text",
text: `id: ${repo.repoName}\nurl: ${repo.webUrl}`,
}
});

// Add pagination info
if (content.length === 0 && filtered.length > 0) {
content.push({
type: "text",
text: `No results on page ${pageNumber}. Total matching repositories: ${filtered.length}`,
});
} else if (filtered.length > endIndex) {
content.push({
type: "text",
text: `Showing ${paginated.length} repositories (page ${pageNumber}). Total matching: ${filtered.length}. Use pageNumber ${pageNumber + 1} to see more.`,
});
}

return {
content,
};
Expand Down
19 changes: 19 additions & 0 deletions packages/mcp/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,25 @@ export const repositoryQuerySchema = z.object({

export const listRepositoriesResponseSchema = repositoryQuerySchema.array();

export const listReposRequestSchema = z.object({
query: z
.string()
.describe("Filter repositories by name or displayName (case-insensitive)")
.optional(),
pageNumber: z
.number()
.int()
.positive()
.describe("Page number (1-indexed, default: 1)")
.default(1),
limit: z
.number()
.int()
.positive()
.describe("Number of repositories per page (default: 50)")
.default(50),
});

export const fileSourceRequestSchema = z.object({
fileName: z.string(),
repository: z.string(),
Expand Down
Loading