sourcebot/packages/mcp/src/schemas.ts

195 lines
6.1 KiB
TypeScript
Raw Normal View History

2025-05-07 23:21:05 +00:00
// @NOTE : Please keep this file in sync with @sourcebot/web/src/features/search/schemas.ts
// At some point, we should move these to a shared package...
import { z } from "zod";
export const locationSchema = z.object({
// 0-based byte offset from the beginning of the file
byteOffset: z.number(),
// 1-based line number from the beginning of the file
lineNumber: z.number(),
// 1-based column number (in runes) from the beginning of line
column: z.number(),
});
export const rangeSchema = z.object({
start: locationSchema,
end: locationSchema,
});
export const symbolSchema = z.object({
symbol: z.string(),
kind: z.string(),
});
export const searchOptionsSchema = z.object({
matches: z.number(), // The number of matches to return.
contextLines: z.number().optional(), // The number of context lines to return.
whole: z.boolean().optional(), // Whether to return the whole file as part of the response.
isRegexEnabled: z.boolean().optional(), // Whether to enable regular expression search.
isCaseSensitivityEnabled: z.boolean().optional(), // Whether to enable case sensitivity.
});
2025-05-07 23:21:05 +00:00
export const searchRequestSchema = z.object({
query: z.string(), // The zoekt query to execute.
...searchOptionsSchema.shape,
2025-05-07 23:21:05 +00:00
});
export const repositoryInfoSchema = z.object({
id: z.number(),
codeHostType: z.string(),
name: z.string(),
displayName: z.string().optional(),
webUrl: z.string().optional(),
});
// Many of these fields are defined in zoekt/api.go.
export const searchStatsSchema = z.object({
// The actual number of matches returned by the search.
// This will always be less than or equal to `totalMatchCount`.
actualMatchCount: z.number(),
// The total number of matches found during the search.
totalMatchCount: z.number(),
// The duration (in nanoseconds) of the search.
duration: z.number(),
// Number of files containing a match.
fileCount: z.number(),
// Candidate files whose contents weren't examined because we
// gathered enough matches.
filesSkipped: z.number(),
// Amount of I/O for reading contents.
contentBytesLoaded: z.number(),
// Amount of I/O for reading from index.
indexBytesLoaded: z.number(),
// Number of search shards that had a crash.
crashes: z.number(),
// Number of files in shards that we considered.
shardFilesConsidered: z.number(),
// Files that we evaluated. Equivalent to files for which all
// atom matches (including negations) evaluated to true.
filesConsidered: z.number(),
// Files for which we loaded file content to verify substring matches
filesLoaded: z.number(),
// Shards that we scanned to find matches.
shardsScanned: z.number(),
// Shards that we did not process because a query was canceled.
shardsSkipped: z.number(),
// Shards that we did not process because the query was rejected by the
// ngram filter indicating it had no matches.
shardsSkippedFilter: z.number(),
// Number of candidate matches as a result of searching ngrams.
ngramMatches: z.number(),
// NgramLookups is the number of times we accessed an ngram in the index.
ngramLookups: z.number(),
// Wall clock time for queued search.
wait: z.number(),
// Aggregate wall clock time spent constructing and pruning the match tree.
// This accounts for time such as lookups in the trigram index.
matchTreeConstruction: z.number(),
// Aggregate wall clock time spent searching the match tree. This accounts
// for the bulk of search work done looking for matches.
matchTreeSearch: z.number(),
// Number of times regexp was called on files that we evaluated.
regexpsConsidered: z.number(),
// FlushReason explains why results were flushed.
flushReason: z.string(),
});
2025-05-07 23:21:05 +00:00
export const searchResponseSchema = z.object({
stats: searchStatsSchema,
2025-05-07 23:21:05 +00:00
files: z.array(z.object({
fileName: z.object({
// The name of the file
text: z.string(),
// Any matching ranges
matchRanges: z.array(rangeSchema),
}),
webUrl: z.string().optional(),
2025-05-07 23:21:05 +00:00
repository: z.string(),
repositoryId: z.number(),
2025-05-07 23:21:05 +00:00
language: z.string(),
chunks: z.array(z.object({
content: z.string(),
matchRanges: z.array(rangeSchema),
contentStart: locationSchema,
symbols: z.array(z.object({
...symbolSchema.shape,
parent: symbolSchema.optional(),
})).optional(),
})),
branches: z.array(z.string()).optional(),
// Set if `whole` is true.
content: z.string().optional(),
})),
repositoryInfo: z.array(repositoryInfoSchema),
isSearchExhaustive: z.boolean(),
2025-05-07 23:21:05 +00:00
});
export const repositoryQuerySchema = z.object({
codeHostType: z.string(),
repoId: z.number(),
repoName: z.string(),
repoDisplayName: z.string().optional(),
repoCloneUrl: z.string(),
webUrl: z.string().optional(),
imageUrl: z.string().optional(),
indexedAt: z.coerce.date().optional(),
2025-05-07 23:21:05 +00:00
});
export const listRepositoriesResponseSchema = repositoryQuerySchema.array();
2025-05-07 23:21:05 +00:00
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),
});
2025-05-07 23:21:05 +00:00
export const fileSourceRequestSchema = z.object({
fileName: z.string(),
repository: z.string(),
branch: z.string().optional(),
});
export const fileSourceResponseSchema = z.object({
source: z.string(),
language: z.string(),
});
export const serviceErrorSchema = z.object({
statusCode: z.number(),
errorCode: z.string(),
message: z.string(),
});