mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 12:25:22 +00:00
feat(mcp): Add pagination and filtering to list_repos tool (#614)
* feat(mcp): Add pagination and filtering to list_repos tool Fixes #566 - Add query parameter to filter repositories by name - Add pageNumber and limit parameters for pagination - Include pagination info in response when applicable - Add listReposRequestSchema for request validation - Update README with new list_repos parameters * feat(mcp): Sort repositories alphabetically for consistent pagination Fixes #566 - Updated CHANGELOG.md with pagination and filtering changes --------- Co-authored-by: Brendan Kellam <bshizzle1234@gmail.com>
This commit is contained in:
parent
e20d514569
commit
9bee8c2c59
4 changed files with 75 additions and 4 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue