search_code
Locate specific code across repositories within a project. Apply filters like branch, code element, file path, or repository to refine results and include full file content or snippets.
Instructions
Search for code across repositories in a project
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filters | No | Optional filters to narrow search results | |
| includeContent | No | Whether to include full file content in results (default: true) | |
| includeSnippet | No | Whether to include code snippets in results (default: true) | |
| projectId | Yes | The ID or name of the project to search in | |
| searchText | Yes | The text to search for | |
| skip | No | Number of results to skip for pagination (default: 0) | |
| top | No | Number of results to return (default: 100, max: 1000) |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"filters": {
"additionalProperties": false,
"description": "Optional filters to narrow search results",
"properties": {
"Branch": {
"description": "Filter by branch names",
"items": {
"type": "string"
},
"type": "array"
},
"CodeElement": {
"description": "Filter by code element types (function, class, etc.)",
"items": {
"type": "string"
},
"type": "array"
},
"Path": {
"description": "Filter by file paths",
"items": {
"type": "string"
},
"type": "array"
},
"Repository": {
"description": "Filter by repository names",
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"includeContent": {
"default": true,
"description": "Whether to include full file content in results (default: true)",
"type": "boolean"
},
"includeSnippet": {
"default": true,
"description": "Whether to include code snippets in results (default: true)",
"type": "boolean"
},
"projectId": {
"description": "The ID or name of the project to search in",
"type": "string"
},
"searchText": {
"description": "The text to search for",
"type": "string"
},
"skip": {
"default": 0,
"description": "Number of results to skip for pagination (default: 0)",
"minimum": 0,
"type": "integer"
},
"top": {
"default": 100,
"description": "Number of results to return (default: 100, max: 1000)",
"maximum": 1000,
"minimum": 1,
"type": "integer"
}
},
"required": [
"searchText",
"projectId"
],
"type": "object"
}
Implementation Reference
- Core handler function that executes the code search using Azure DevOps Search API, handles authentication, makes API calls, and optionally enriches results with file contents.export async function searchCode( connection: WebApi, options: SearchCodeOptions, ): Promise<CodeSearchResponse> { try { // When includeContent is true, limit results to prevent timeouts const top = options.includeContent ? Math.min(options.top || 10, 10) : options.top; // Get the project ID (either provided or default) const projectId = options.projectId || process.env.AZURE_DEVOPS_DEFAULT_PROJECT; if (!projectId) { throw new AzureDevOpsValidationError( 'Project ID is required. Either provide a projectId or set the AZURE_DEVOPS_DEFAULT_PROJECT environment variable.', ); } // Prepare the search request const searchRequest: CodeSearchRequest = { searchText: options.searchText, $skip: options.skip, $top: top, // Use limited top value when includeContent is true filters: { Project: [projectId], ...(options.filters || {}), }, includeFacets: true, includeSnippet: options.includeSnippet, }; // Get the authorization header from the connection const authHeader = await getAuthorizationHeader(); // Extract organization from the connection URL const { organization } = extractOrgFromUrl(connection); // Make the search API request with the project ID const searchUrl = `https://almsearch.dev.azure.com/${organization}/${projectId}/_apis/search/codesearchresults?api-version=7.1`; const searchResponse = await axios.post<CodeSearchResponse>( searchUrl, searchRequest, { headers: { Authorization: authHeader, 'Content-Type': 'application/json', }, }, ); const results = searchResponse.data; // If includeContent is true, fetch the content for each result if (options.includeContent && results.results.length > 0) { await enrichResultsWithContent(connection, results.results); } return results; } catch (error) { if (error instanceof AzureDevOpsError) { throw error; } if (axios.isAxiosError(error)) { const status = error.response?.status; if (status === 404) { throw new AzureDevOpsResourceNotFoundError( 'Repository or project not found', { cause: error }, ); } if (status === 400) { throw new AzureDevOpsValidationError( 'Invalid search parameters', error.response?.data, { cause: error }, ); } if (status === 401) { throw new AzureDevOpsAuthenticationError('Authentication failed', { cause: error, }); } if (status === 403) { throw new AzureDevOpsPermissionError( 'Permission denied to access repository', { cause: error }, ); } } throw new AzureDevOpsError('Failed to search code', { cause: error }); } }
- src/features/search/schemas.ts:7-68 (schema)Zod schema for input validation of the search_code tool parameters.export const SearchCodeSchema = z .object({ searchText: z.string().describe('The text to search for'), organizationId: z .string() .optional() .describe(`The ID or name of the organization (Default: ${defaultOrg})`), projectId: z .string() .optional() .describe( `The ID or name of the project to search in (Default: ${defaultProject}). If not provided, the default project will be used.`, ), filters: z .object({ Repository: z .array(z.string()) .optional() .describe('Filter by repository names'), Path: z.array(z.string()).optional().describe('Filter by file paths'), Branch: z .array(z.string()) .optional() .describe('Filter by branch names'), CodeElement: z .array(z.string()) .optional() .describe('Filter by code element types (function, class, etc.)'), }) .optional() .describe('Optional filters to narrow search results'), top: z .number() .int() .min(1) .max(1000) .default(100) .describe('Number of results to return (default: 100, max: 1000)'), skip: z .number() .int() .min(0) .default(0) .describe('Number of results to skip for pagination (default: 0)'), includeSnippet: z .boolean() .default(true) .describe('Whether to include code snippets in results (default: true)'), includeContent: z .boolean() .default(true) .describe( 'Whether to include full file content in results (default: true)', ), }) .transform((data) => { return { ...data, organizationId: data.organizationId ?? defaultOrg, projectId: data.projectId ?? defaultProject, }; });
- src/features/search/tool-definitions.ts:13-17 (registration)Registration of the search_code tool in the tools list, including name, description, and JSON schema derived from Zod.{ name: 'search_code', description: 'Search for code across repositories in a project', inputSchema: zodToJsonSchema(SearchCodeSchema), },
- src/features/search/index.ts:44-50 (registration)Request handler dispatch for search_code tool, parsing arguments and calling the searchCode handler.case 'search_code': { const args = SearchCodeSchema.parse(request.params.arguments); const result = await searchCode(connection, args); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; }
- Helper function to enrich search results with full file contents using the Azure DevOps Git API.async function enrichResultsWithContent( connection: WebApi, results: CodeSearchResult[], ): Promise<void> { try { const gitApi = await connection.getGitApi(); // Process each result in parallel await Promise.all( results.map(async (result) => { try { // Get the file content using the Git API // Pass only the required parameters to avoid the "path" and "scopePath" conflict const contentStream = await gitApi.getItemContent( result.repository.id, result.path, result.project.name, undefined, // No version descriptor object undefined, // No recursion level undefined, // Don't include content metadata undefined, // No latest processed change false, // Don't download { version: result.versions[0]?.changeId, versionType: GitVersionType.Commit, }, // Version descriptor true, // Include content ); // Convert the stream to a string and store it in the result if (contentStream) { // Since getItemContent always returns NodeJS.ReadableStream, we need to read the stream const chunks: Buffer[] = []; // Listen for data events to collect chunks contentStream.on('data', (chunk) => { chunks.push(Buffer.from(chunk)); }); // Use a promise to wait for the stream to finish result.content = await new Promise<string>((resolve, reject) => { contentStream.on('end', () => { // Concatenate all chunks and convert to string const buffer = Buffer.concat(chunks); resolve(buffer.toString('utf8')); }); contentStream.on('error', (err) => { reject(err); }); }); } } catch (error) { // Log the error but don't fail the entire operation console.error( `Failed to fetch content for ${result.path}: ${error instanceof Error ? error.message : String(error)}`, ); } }), ); } catch (error) { // Log the error but don't fail the entire operation console.error( `Failed to enrich results with content: ${error instanceof Error ? error.message : String(error)}`, ); } }